khanat-opennel-code/code/nel/tools/misc/log_analyser/ViewDialog.cpp

1210 lines
28 KiB
C++
Raw Normal View History

// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// ViewDialog.cpp : implementation file
//
#include "stdafx.h"
#include "log_analyser.h"
#include "ViewDialog.h"
#include "log_analyserDlg.h"
#include <fstream>
#include <algorithm>
using namespace std;
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
extern CString LogDateString;
static UINT WM_FINDREPLACE = ::RegisterWindowMessage(FINDMSGSTRING);
time_t CurrentTime;
void CListCtrlEx::initIt()
{
DWORD dwStyle = GetWindowLong(m_hWnd, GWL_STYLE);
SetWindowLong( m_hWnd, GWL_STYLE, dwStyle | LVS_OWNERDRAWFIXED );
}
/*
* Keyboard handler in list box
*/
afx_msg void CListCtrlEx::OnKeyDown( UINT nChar, UINT nRepCnt, UINT nFlags )
{
// Transmit to List Ctrl AND to main window
CListCtrl::OnKeyDown( nChar, nRepCnt, nFlags );
((CLog_analyserDlg*)(_ViewDialog->GetParent()))->OnKeyDown( nChar, nRepCnt, nFlags );
}
// Adapted from http://zeus.eed.usv.ro/misc/doc/prog/c/mfc/listview/sel_row.html
void CListCtrlEx::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
CRect rcItem(lpDrawItemStruct->rcItem);
int nItem = lpDrawItemStruct->itemID;
CImageList* pImageList;
// Save dc state
int nSavedDC = pDC->SaveDC();
// Get item image and state info
LV_ITEM lvi;
lvi.mask = LVIF_IMAGE | LVIF_STATE;
lvi.iItem = nItem;
lvi.iSubItem = 0;
lvi.stateMask = 0xFFFF; // get all state flags
GetItem(&lvi);
BOOL bHighlight =((lvi.state & LVIS_DROPHILITED)
|| ( (lvi.state & LVIS_SELECTED)
&& ((GetFocus() == this)
|| (GetStyle() & LVS_SHOWSELALWAYS)
)
)
);
// Get rectangles for drawing
CRect rcBounds, rcLabel, rcIcon;
GetItemRect(nItem, rcBounds, LVIR_BOUNDS);
GetItemRect(nItem, rcLabel, LVIR_LABEL);
GetItemRect(nItem, rcIcon, LVIR_ICON);
CRect rcCol( rcBounds );
CString sLabel = GetItemText( nItem, 0 );
// Labels are offset by a certain amount
// This offset is related to the width of a space character
int offset = pDC->GetTextExtent(_T(" "), 1 ).cx*2;
CRect rcHighlight;
CRect rcWnd;
GetClientRect(&rcWnd);
rcHighlight = rcBounds;
rcHighlight.left = rcLabel.left;
rcHighlight.right = rcWnd.right;
// Draw the background color
if( bHighlight )
pDC->SetBkColor(::GetSysColor(COLOR_HIGHLIGHT));
pDC->FillRect( rcHighlight, &CBrush(_ViewDialog->getBkColorForLine( nItem, bHighlight!=0 )) );
// Set clip region
rcCol.right = rcCol.left + GetColumnWidth(0);
CRgn rgn;
rgn.CreateRectRgnIndirect(&rcCol);
pDC->SelectClipRgn(&rgn);
rgn.DeleteObject();
// Draw state icon
if (lvi.state & LVIS_STATEIMAGEMASK)
{
int nImage = ((lvi.state & LVIS_STATEIMAGEMASK)>>12) - 1;
pImageList = GetImageList(LVSIL_STATE);
if (pImageList)
{
pImageList->Draw(pDC, nImage,
CPoint(rcCol.left, rcCol.top), ILD_TRANSPARENT);
}
}
// Draw normal and overlay icon
pImageList = GetImageList(LVSIL_SMALL);
if (pImageList)
{
UINT nOvlImageMask=lvi.state & LVIS_OVERLAYMASK;
pImageList->Draw(pDC, lvi.iImage,
CPoint(rcIcon.left, rcIcon.top),
ILD_TRANSPARENT | nOvlImageMask );
}
// Draw item label - Column 0
rcLabel.left += offset/2;
rcLabel.right -= offset;
pDC->SetTextColor( _ViewDialog->getTextColorForLine( nItem, bHighlight!=0 ) );
pDC->DrawText(sLabel,-1,rcLabel,DT_LEFT | DT_SINGLELINE | DT_NOPREFIX | DT_NOCLIP
| DT_VCENTER /*| DT_END_ELLIPSIS*/);
// Draw labels for remaining columns
LV_COLUMN lvc;
lvc.mask = LVCF_FMT | LVCF_WIDTH;
rcBounds.right = rcHighlight.right > rcBounds.right ? rcHighlight.right :
rcBounds.right;
rgn.CreateRectRgnIndirect(&rcBounds);
pDC->SelectClipRgn(&rgn);
for(int nColumn = 1; GetColumn(nColumn, &lvc); nColumn++)
{
rcCol.left = rcCol.right;
rcCol.right += lvc.cx;
sLabel = GetItemText(nItem, nColumn);
if (sLabel.GetLength() == 0)
continue;
// Get the text justification
UINT nJustify = DT_LEFT;
switch(lvc.fmt & LVCFMT_JUSTIFYMASK)
{
case LVCFMT_RIGHT:
nJustify = DT_RIGHT;
break;
case LVCFMT_CENTER:
nJustify = DT_CENTER;
break;
default:
break;
}
rcLabel = rcCol;
rcLabel.left += offset;
rcLabel.right -= offset;
pDC->DrawText(sLabel, -1, rcLabel, nJustify | DT_SINGLELINE |
DT_NOPREFIX | DT_VCENTER | DT_END_ELLIPSIS);
}
// Draw focus rectangle if item has focus
if (lvi.state & LVIS_FOCUSED && (GetFocus() == this))
{
pDC->DrawFocusRect(rcHighlight);
}
// Restore dc
pDC->RestoreDC( nSavedDC );
}
void CListCtrlEx::RepaintSelectedItems()
{
CRect rcBounds, rcLabel;
// Invalidate focused item so it can repaint
int nItem = GetNextItem(-1, LVNI_FOCUSED);
if(nItem != -1)
{
GetItemRect(nItem, rcBounds, LVIR_BOUNDS);
GetItemRect(nItem, rcLabel, LVIR_LABEL);
rcBounds.left = rcLabel.left;
InvalidateRect(rcBounds, FALSE);
}
// Invalidate selected items depending on LVS_SHOWSELALWAYS
if(!(GetStyle() & LVS_SHOWSELALWAYS))
{
for(nItem = GetNextItem(-1, LVNI_SELECTED);
nItem != -1; nItem = GetNextItem(nItem, LVNI_SELECTED))
{
GetItemRect(nItem, rcBounds, LVIR_BOUNDS);
GetItemRect(nItem, rcLabel, LVIR_LABEL);
rcBounds.left = rcLabel.left;
InvalidateRect(rcBounds, FALSE);
}
}
UpdateWindow();
}
/*void CListCtrlEx::OnKillFocus(CWnd* pNewWnd)
{
CListCtrl::OnKillFocus(pNewWnd);
// check if we are losing focus to label edit box
if(pNewWnd != NULL && pNewWnd->GetParent() == this)
return;
// repaint items that should change appearance
if((GetStyle() & LVS_TYPEMASK) == LVS_REPORT)
RepaintSelectedItems();
}
void CListCtrlEx::OnSetFocus(CWnd* pOldWnd)
{
CListCtrl::OnSetFocus(pOldWnd);
// check if we are getting focus from label edit box
if(pOldWnd!=NULL && pOldWnd->GetParent()==this)
return;
// repaint items that should change appearance
if((GetStyle() & LVS_TYPEMASK)==LVS_REPORT)
RepaintSelectedItems();
}*/
BEGIN_MESSAGE_MAP(CListCtrlEx, CListCtrl)
//{{AFX_MSG_MAP(CListCtrlEx)
ON_WM_KEYDOWN()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CViewDialog dialog
CViewDialog::CViewDialog(CWnd* pParent /*=NULL*/)
: CDialog(CViewDialog::IDD, pParent)
{
//{{AFX_DATA_INIT(CViewDialog)
m_Caption = _T("");
//}}AFX_DATA_INIT
}
void CViewDialog::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CViewDialog)
DDX_Control(pDX, IDC_LIST1, m_ListCtrl);
DDX_Text(pDX, IDC_Service, m_Caption);
//}}AFX_DATA_MAP
}
/*
* Load, using the current filters
*/
void CViewDialog::reload()
{
SessionDatePassed = false;
CWaitCursor wc;
2017-01-02 08:58:41 +00:00
if ( LogSessionStartDate.IsEmpty() || (LogSessionStartDate == _T("Beginning")) )
{
SessionDatePassed = true;
}
((CButton*)GetDlgItem( IDC_BUTTON1 ))->ShowWindow( SW_SHOW );
((CButton*)GetDlgItem( IDC_BUTTON2 ))->ShowWindow( SW_SHOW );
2017-01-02 08:58:41 +00:00
m_Caption.Format( _T("%s (%u file%s) %u+ %u- (%s)"), Seriesname, Filenames.size(), Filenames.size()>1?"s":"", PosFilter.size(), NegFilter.size(), LogSessionStartDate.IsEmpty()?"all":CString("session ")+LogSessionStartDate );
UpdateData( false );
clear();
setRedraw( false );
time( &CurrentTime );
// Translate bookmarks (phase 1)
CurrentBookmark = -1;
vector<int> bookmarksAbsoluteLines;
if ( ! Bookmarks.empty() )
{
getBookmarksAbsoluteLines( bookmarksAbsoluteLines );
Bookmarks.clear();
}
loadFileOrSeries( bookmarksAbsoluteLines );
commitAddedLines();
setRedraw( true );
}
/*
* Code from IDisplayer::dateToHumanString() (NeL misc)
*/
string timeToStr( const time_t& date )
{
static char cstime[25];
struct tm *tms = localtime(&date);
if (tms)
strftime (cstime, 25, "%Y/%m/%d %H:%M:%S", tms);
else
smprintf(cstime, 25, "bad date %d", (unsigned int)date);
return cstime;
}
/*
* Reload with old filter to get bookmarks absolute line numbers (not called if there's no bookmark)
*/
void CViewDialog::getBookmarksAbsoluteLines( vector<int>& bookmarksAbsoluteLines )
{
// Read the files
unsigned int currentAbsoluteLineNum = 0, currentLineNum = 0;
for ( unsigned int i=0; i!=Filenames.size(); ++i )
{
CString& filename = Filenames[i];
2017-01-02 08:58:41 +00:00
NLMISC::CIFile ifs;
if (ifs.open(tStrToUtf8(filename)))
{
char line [1024];
2017-01-02 08:58:41 +00:00
while (!ifs.eof())
{
2017-01-02 08:58:41 +00:00
ifs.getline(line, 1024);
if ( SessionDatePassed )
{
// Stop if the session is finished
2017-01-02 08:58:41 +00:00
if ( (! LogSessionStartDate.IsEmpty()) && (strstr( line, tStrToUtf8(LogDateString).c_str())) )
{
return;
}
// Test the filters
if ( passFilter( line, PreviousPosFilter, PreviousNegFilter ) )
{
// Translate bookmarks (phase 1: filtered -> absolute)
vector<int>::iterator ibl;
ibl = find( Bookmarks.begin(), Bookmarks.end(), currentLineNum );
if ( ibl != Bookmarks.end() )
{
bookmarksAbsoluteLines.push_back( currentAbsoluteLineNum );
}
++currentLineNum;
}
++currentAbsoluteLineNum;
}
else
{
// Look for the session beginning
2017-01-02 08:58:41 +00:00
if ( strstr( line, tStrToUtf8(LogSessionStartDate).c_str()) != NULL )
{
SessionDatePassed = true;
}
}
}
}
}
}
/*
* Auto-detect file corruption (version for any NeL log file)
*/
bool detectLineCorruption( const char *cLine )
{
string line = string(cLine);
if ( ! line.empty() )
{
bool corrupted = false;
string::size_type p;
// Some line may begin with no date, this is normal, we don't state them as corruption
// Search for year not at beginning. Ex: "2003/" (it does not work when the year is different from the current one!)
p = line.substr( 1 ).find( timeToStr( CurrentTime ).substr( 0, 5 ) );
if ( p != string::npos )
{
++p; // because searched from pos 1
// Search for date/time
if ( (line.size()>p+20) && (line[p+10]==' ') && (line[p+13]==':')
&& (line[p+16]==':') && (line[p+19]==' ') )
{
// Search for the five next blank characters. The fifth is followed by ": ".
// (Date Time LogType ThreadId Machine/Service SourceFile Line : User-defined log line)
unsigned int nbBlank = 0;
string::size_type sp;
for ( sp=p+20; sp!=line.size(); ++sp )
{
if ( line[sp]==' ')
++nbBlank;
if ( nbBlank==5 )
break;
}
if ( (nbBlank==5) && (line[sp+1]==':') && (line[sp+2]==' ') )
{
return true;
}
}
}
}
return false;
}
bool HasCorruptedLines;
/*
*
*/
std::string CViewDialog::corruptedLinesString( const std::vector<unsigned int>& corruptedLines )
{
string res;
if ( ! corruptedLines.empty() )
{
2017-01-02 08:58:41 +00:00
res = NLMISC::toString(" -> %u corrupted lines:", (uint)corruptedLines.size());
vector<unsigned int>::const_iterator ivc;
for ( ivc=corruptedLines.begin(); ivc!=corruptedLines.end(); ++ivc )
{
2017-01-02 08:58:41 +00:00
res += NLMISC::toString("\r\n line %u : %s...", *ivc, tStrToUtf8(Buffer[*ivc].Left(20)).c_str());
}
HasCorruptedLines = true;
}
return res;
}
/*
* Load a log file or series
*/
void CViewDialog::loadFileOrSeries( const vector<int>& bookmarksAbsoluteLines )
{
// Header for 'files loaded' display
string actualFilenames = "Files loaded";
if ( LogSessionStartDate.IsEmpty() )
actualFilenames += ":\r\n";
else
2016-12-04 16:32:34 +00:00
actualFilenames += " for Session of " + tStrToUtf8(LogSessionStartDate) + ":\r\n";
bool corruptionDetectionEnabled = (((CButton*)(((CLog_analyserDlg*)GetParent())->GetDlgItem( IDC_DetectCorruptedLines )))->GetCheck() == 1);
HasCorruptedLines = false;
vector<unsigned int> corruptedLines;
// Read the files
unsigned int currentAbsoluteLineNum = 0, currentLineNum = 0;
for ( unsigned int i=0; i!=Filenames.size(); ++i )
{
CString& filename = Filenames[i];
ifstream ifs( filename );
if ( ! ifs.fail() )
{
char line [1024];
while ( ! ifs.eof() )
{
ifs.getline( line, 1024 );
line[1023] = '\0'; // force valid end of line
if ( SessionDatePassed )
{
// Stop if the session is finished
if ( (! LogSessionStartDate.IsEmpty()) && (strstr( line, LogDateString )) )
{
actualFilenames += string(filename) + corruptedLinesString( corruptedLines ) + "\r\n";
corruptedLines.clear();
goto endOfLoading;
}
// Test the filters
if ( passFilter( line, PosFilter, NegFilter ) )
{
// Auto-detect line corruption
if ( corruptionDetectionEnabled && detectLineCorruption( line ) )
corruptedLines.push_back( currentLineNum );
// Translate bookmarks (phase 2: absolute -> filtered)
if ( ! bookmarksAbsoluteLines.empty() )
{
vector<int>::const_iterator ibal;
ibal = find( bookmarksAbsoluteLines.begin(), bookmarksAbsoluteLines.end(), currentAbsoluteLineNum );
if ( ibal != bookmarksAbsoluteLines.end() )
{
Bookmarks.push_back( currentLineNum );
}
}
// Add line to list box
addLine( line );
++currentLineNum;
}
++currentAbsoluteLineNum;
}
else
{
// Look for the session beginning
if ( strstr( line, LogSessionStartDate ) != NULL )
{
SessionDatePassed = true;
}
}
}
if ( SessionDatePassed )
{
actualFilenames += string(filename) + corruptedLinesString( corruptedLines ) + "\r\n";
corruptedLines.clear();
}
}
else
{
CString s;
s.Format( "<Cannot open file %s>\r\n", filename );
actualFilenames += s;
}
}
endOfLoading:
if ( HasCorruptedLines )
{
actualFilenames += "(When corrupted lines are present, it is possible that some other lines are missing)\r\n\
Select the line number and press Ctrl-G to scroll to the corrupted line.\r\n\
At any time, press Ctrl-L in this edit box to return to this list of files and corrupted lines.";
}
((CLog_analyserDlg*)GetParent())->displayCurrentLine( actualFilenames.c_str() );
((CLog_analyserDlg*)GetParent())->memorizeFileList( actualFilenames.c_str() );
}
/*
* Set the filters (and backup the previous ones for bookmark translation)
*/
void CViewDialog::setFilters( const std::vector<CString>& posFilter, const std::vector<CString>& negFilter )
{
PreviousPosFilter = PosFilter;
PreviousNegFilter = NegFilter;
PosFilter = posFilter;
NegFilter = negFilter;
}
/*
* Returns true if the string must be logged, according to the current filters
*/
bool CViewDialog::passFilter( const char *text, const std::vector<CString>& posFilter, const std::vector<CString>& negFilter ) const
{
bool yes = posFilter.empty();
bool found;
vector<CString>::const_iterator ilf;
// 1. Positive filter
for ( ilf=posFilter.begin(); ilf!=posFilter.end(); ++ilf )
{
found = ( strstr( text, *ilf ) != NULL );
if ( found )
{
yes = true; // positive filter passed (no need to check another one)
break;
}
// else try the next one
}
if ( ! yes )
{
return false; // positive filter not passed
}
// 2. Negative filter
for ( ilf=negFilter.begin(); ilf!=negFilter.end(); ++ilf )
{
found = ( strstr( text, *ilf ) != NULL );
if ( found )
{
return false; // negative filter not passed (no need to check another one)
}
}
return true; // negative filter passed
}
/*
* Load trace
*/
void CViewDialog::reloadTrace()
{
CWaitCursor wc;
((CButton*)GetDlgItem( IDC_BUTTON1 ))->ShowWindow( SW_HIDE );
((CButton*)GetDlgItem( IDC_BUTTON2 ))->ShowWindow( SW_HIDE );
if ( LogSessionStartDate.IsEmpty() )
{
SessionDatePassed = true;
if ( PosFilter.empty() )
m_Caption = "Trace of " + Seriesname + " (all)";
else
m_Caption = "Trace of " + PosFilter[0] + " (all)";
}
else
{
if ( LogSessionStartDate == "Beginning" )
{
SessionDatePassed = true;
}
if ( PosFilter.empty() )
m_Caption = "Trace of " + Seriesname + " (session " + LogSessionStartDate + ")" ;
else
m_Caption = "Trace of " + PosFilter[0] + " (session " + LogSessionStartDate + ")" ;
}
UpdateData( false );
clear();
ifstream ifs( Seriesname );
if ( ! ifs.fail() )
{
char line [1024];
while ( ! ifs.eof() )
{
ifs.getline( line, 1024 );
if ( SessionDatePassed )
{
// Stop if the session is finished
if ( (! LogSessionStartDate.IsEmpty()) && (strstr( line, LogDateString )) )
break;
// Read if it's a TRACE
char *pc = strstr( line, "TRACE" );
if ( pc != NULL )
{
if ( PosFilter.empty() || (strncmp( pc-PosFilter[0].GetLength(), PosFilter[0], PosFilter[0].GetLength() ) == 0) )
{
((CLog_analyserDlg*)GetParent())->insertTraceLine( Index, pc+6 );
}
}
}
else
{
// Look for the session beginning
if ( strstr( line, LogSessionStartDate ) != NULL )
{
SessionDatePassed = true;
}
}
}
addLine( "<After adding all the views" );
addLine( "you need, click Compute Traces" );
addLine( "to generate all the views>" );
}
else
{
addLine( "<Cannot open log file>" );
}
commitAddedLines();
}
BEGIN_MESSAGE_MAP(CViewDialog, CDialog)
//{{AFX_MSG_MAP(CViewDialog)
ON_BN_CLICKED(IDC_BUTTON1, OnButtonFilter)
ON_NOTIFY(LVN_GETDISPINFO, IDC_LIST1, OnGetdispinfoList1)
ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST1, OnItemchangedList1)
ON_NOTIFY(NM_SETFOCUS, IDC_LIST1, OnSetfocusList1)
ON_BN_CLICKED(IDC_BUTTON2, OnButtonFind)
ON_WM_LBUTTONDOWN()
ON_REGISTERED_MESSAGE( WM_FINDREPLACE, OnFindReplace )
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CViewDialog message handlers
/*
*
*/
void CViewDialog::OnSetfocusList1(NMHDR* pNMHDR, LRESULT* pResult)
{
// Force to display the current item when the current view changes
if ( getSelectionIndex() != -1 )
displayString();
((CLog_analyserDlg*)GetParent())->setCurrentView( Index );
m_ListCtrl.RepaintSelectedItems();
*pResult = 0;
}
/*
*
*/
void CViewDialog::OnItemchangedList1(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
// Display the current item when it changes
if ( pNMListView->iItem != -1 )
displayString();
((CLog_analyserDlg*)GetParent())->setCurrentView( Index );
m_ListCtrl.RepaintSelectedItems();
*pResult = 0;
}
/*
* Resize
*/
void CViewDialog::resizeView( int nbViews, int top, int left )
{
RECT parentRect;
GetParent()->GetClientRect( &parentRect );
int width = (int)((parentRect.right-32)*WidthR);
RECT viewRect;
viewRect.left = left;
viewRect.top = top;
viewRect.right = viewRect.left + width;
viewRect.bottom = parentRect.bottom-10;
MoveWindow( &viewRect, TRUE );
m_ListCtrl.MoveWindow( 5, 32, width-5, viewRect.bottom-top-42 );
LVCOLUMN lvc;
lvc.mask = LVCF_WIDTH;
lvc.cx = width-24;
m_ListCtrl.SetColumn( 0, &lvc );
//m_ListCtrl.SetColumnWidth( 0, LVSCW_AUTOSIZE ); // worse
GetDlgItem( IDC_DragBar )->MoveWindow( 0, 0, 32, viewRect.bottom-top );
}
/*
* Return the nb of lines
*/
int CViewDialog::getNbLines() const
{
return (int)Buffer.size();
}
/*
* Return the nb of visible lines
*/
int CViewDialog::getNbVisibleLines() const
{
return m_ListCtrl.GetCountPerPage();
}
/*
* Fill from getNbLines() to maxNbLines with blank lines
*/
void CViewDialog::fillGaps( int maxNbLines )
{
int nbLines = getNbLines();
for ( int i=0; i!=maxNbLines-nbLines; ++i )
{
addLine( "" );
}
}
/*
* Commit the lines previously added
*/
void CViewDialog::commitAddedLines()
{
m_ListCtrl.SetItemCount( (int)Buffer.size() );
m_ListCtrl.SetColumnWidth( 0, LVSCW_AUTOSIZE );
}
/*
* Scroll
*/
void CViewDialog::scrollTo( int index )
{
int deltaIndex = index - m_ListCtrl.GetTopIndex();
RECT rect;
if ( m_ListCtrl.GetItemRect( 0, &rect, LVIR_BOUNDS ) )
{
int itemH = rect.bottom-rect.top;
m_ListCtrl.Scroll( CSize( 0, deltaIndex*itemH ) );
}
//m_ListCtrl.EnsureVisible( index, false );
}
/*
* Select
*/
void CViewDialog::select( int index )
{
LVITEM itstate;
itstate.mask = LVIF_STATE;
itstate.state = 0;
int sm = getSelectionIndex();
if ( sm != -1 )
{
m_ListCtrl.SetItemState( sm, &itstate );
}
if ( index != -1 )
{
itstate.state = LVIS_SELECTED | /*LVIS_DROPHILITED |*/ LVIS_FOCUSED;
m_ListCtrl.SetItemState( index, &itstate );
m_ListCtrl.SetSelectionMark( index );
}
}
/*
* Return the index of the top of the listbox
*/
int CViewDialog::getScrollIndex() const
{
return m_ListCtrl.GetTopIndex();
}
/*
* Add the current scroll index to the bookmark list, or delete it if already inside the list
*/
void CViewDialog::addBookmark()
{
int bkIndex = getSelectionIndex();
vector<int>::iterator ibk = find( Bookmarks.begin(), Bookmarks.end(), bkIndex );
if ( ibk == Bookmarks.end() )
{
// Add
Bookmarks.push_back( bkIndex );
std::sort( Bookmarks.begin(), Bookmarks.end() ); // not very fast but not many items
((CLog_analyserDlg*)(GetParent()))->displayCurrentLine( "Bookmark set" );
}
else
{
// Remove
Bookmarks.erase( ibk );
}
// Refresh the listbox view to display the right bookmark color
((CLog_analyserDlg*)(GetParent()))->SetFocus();
m_ListCtrl.SetFocus();
}
/*
* Scroll the listbox to the next stored bookmkark
*/
void CViewDialog::recallNextBookmark()
{
if ( Bookmarks.empty() )
return;
// Precondition: the vector is sorted
int origIndex = (CurrentBookmark==-1) ? getScrollIndex() : CurrentBookmark, destIndex;
unsigned int i = 0;
while ( (i < Bookmarks.size()) && (Bookmarks[i] <= origIndex) )
++i;
if ( i == Bookmarks.size() )
{
// Origin index > all the bookmarks => go back to the first one
destIndex = Bookmarks[0];
}
else
{
// Go to the next bookmark
destIndex = Bookmarks[i];
}
CurrentBookmark = destIndex; // because scrollTo does not scroll if we are at the bottom of the list
scrollTo( destIndex );
//select( destIndex );
}
/*
* Add several lines
*/
void CViewDialog::addText( const CString& lines )
{
int pos, lineStartPos=0;
for ( pos=0; pos<lines.GetLength(); ++pos )
{
if ( lines[pos] == '\n' )
{
addLine( lines.Mid( lineStartPos, pos-lineStartPos ) );
++pos; // skip '\n'
lineStartPos = pos;
}
}
if ( lineStartPos > pos )
addLine( lines.Mid( lineStartPos, pos-lineStartPos ) );
}
/*
* Clear
*/
void CViewDialog::clear()
{
Buffer.clear();
m_ListCtrl.DeleteAllItems();
}
/*
*
*/
void CViewDialog::OnButtonFilter()
{
if ( ((CLog_analyserDlg*)GetParent())->FilterDialog.DoModal() == IDOK )
{
setFilters( ((CLog_analyserDlg*)GetParent())->FilterDialog.getPosFilter(), ((CLog_analyserDlg*)GetParent())->FilterDialog.getNegFilter() );
if ( ! ((CLog_analyserDlg*)GetParent())->Trace )
{
reload();
}
}
}
BOOL CViewDialog::OnInitDialog()
{
CDialog::OnInitDialog();
m_ListCtrl.GetHeaderCtrl()->ModifyStyle( 0, HDS_HIDDEN );
m_ListCtrl.InsertColumn( 0, "" );
m_ListCtrl.setViewDialog( this );
m_ListCtrl.initIt();
Index = -1;
BeginFindIndex = -1;
FindDialog = NULL;
FindMatchCase = false;
FindDownwards = true;
WidthR = 0.0f;
CurrentBookmark = -1;
return TRUE;
}
/*
* Return the text color
*/
COLORREF CViewDialog::getTextColorForLine( int index, bool selected )
{
if ( selected )
return ::GetSysColor(COLOR_HIGHLIGHTTEXT);
else
{
if ( Buffer[index].Find( "DBG" ) != -1 )
return RGB(0x80,0x80,0x80);
else if ( Buffer[index].Find( "WRN" ) != -1 )
return RGB(0x80,0,0);
else if ( (Buffer[index].Find( "ERR" ) != -1) || (Buffer[index].Find( "AST" ) != -1) )
return RGB(0xFF,0,0);
else // INF and others
return RGB(0,0,0);
}
}
/*
* Return the background color
*/
COLORREF CViewDialog::getBkColorForLine( int index, bool selected )
{
unsigned int i = 0;
while ( (i < Bookmarks.size()) && (Bookmarks[i]<index) )
++i;
if ( (i < Bookmarks.size()) && (index == Bookmarks[i]) )
if ( selected )
return (::GetSysColor(COLOR_HIGHLIGHT) + RGB(0xC0,0x90,0x90)) / 2; // selected bookmark
else
return RGB(0xC0,0x90,0x90); // bookmark
else
if ( selected )
return ::GetSysColor(COLOR_HIGHLIGHT); // selected
else
return GetSysColor(COLOR_WINDOW); // normal
}
/*
*
*/
void formatLogStr( CString& str, bool displayHeaders )
{
if ( ! displayHeaders )
{
int pos = str.Find( " : " );
if ( pos != -1 )
{
str.Delete( 0, pos + 3 );
}
}
}
/*
* Process string before displaying it in the view
*/
void CViewDialog::OnGetdispinfoList1(NMHDR* pNMHDR, LRESULT* pResult)
{
LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;
LV_ITEM* pItem= &(pDispInfo)->item;
int iItemIndx = pItem->iItem;
if (pItem->mask & LVIF_TEXT) //valid text buffer?
{
CString str = Buffer[iItemIndx];
formatLogStr( str, ((CButton*)(((CLog_analyserDlg*)GetParent())->GetDlgItem( IDC_DispLineHeaders )))->GetCheck() == 1 );
lstrcpy( pItem->pszText, str );
}
*pResult = 0;
}
/*
* Display string
*/
void CViewDialog::displayString()
{
// Build the string
CString s;
POSITION pos = m_ListCtrl.GetFirstSelectedItemPosition();
while ( pos != NULL )
{
int index = m_ListCtrl.GetNextSelectedItem( pos );
CString str = Buffer[index];
formatLogStr( str, ((CButton*)(((CLog_analyserDlg*)GetParent())->GetDlgItem( IDC_DispLineHeaders )))->GetCheck() == 1 );
s += str + "\r\n";
}
// Display it
((CLog_analyserDlg*)GetParent())->displayCurrentLine( s );
}
/*
* Search string
*/
void CViewDialog::OnButtonFind()
{
if ( FindDialog ) // spawn only 1 window
return;
m_ListCtrl.ModifyStyle( 0, LVS_SHOWSELALWAYS );
select( -1 );
DWORD frDown = FindDownwards ? FR_DOWN : 0;
DWORD frMatchCase = FindMatchCase ? FR_MATCHCASE : 0;
FindDialog = new CFindReplaceDialog();
FindDialog->Create( true, FindStr, NULL, frDown | frMatchCase | FR_HIDEWHOLEWORD, this );
}
bool matchString( const CString& str, const CString& substr, bool matchCase, int& matchPos )
{
if ( matchCase )
{
matchPos = str.Find( substr );
return matchPos != -1;
}
else
{
CString str2 = str, substr2 = substr;
str2.MakeUpper();
substr2.MakeUpper();
matchPos = str2.Find( substr2 );
return matchPos != -1;
}
}
/*
*
*/
afx_msg LRESULT CViewDialog::OnFindReplace(WPARAM wParam, LPARAM lParam)
{
// Test 'Cancel'
if ( FindDialog->IsTerminating() )
{
FindDialog = NULL;
m_ListCtrl.ModifyStyle( LVS_SHOWSELALWAYS, 0 );
select( -1 );
return 0;
}
// Test 'Find'
if ( FindDialog->FindNext() )
{
FindMatchCase = (FindDialog->MatchCase() != 0);
FindDownwards = (FindDialog->SearchDown() != 0);
FindStr = FindDialog->GetFindString();
int lineIndex, matchPos;
if ( FindDialog->SearchDown() )
{
BeginFindIndex = (getSelectionIndex() == -1) ? 0 : getSelectionIndex() + 1;
for ( lineIndex=BeginFindIndex; lineIndex!=(int)Buffer.size(); ++lineIndex )
{
if ( matchString( Buffer[lineIndex], FindStr, FindDialog->MatchCase()!=0, matchPos ) )
{
scrollTo( lineIndex );
select( lineIndex );
//BeginFindIndex = getSelectionIndex()+1;
//displayString();
CString s;
s.Format( "Found '%s' (downwards from line %d) at line %d:\r\n%s", FindStr, BeginFindIndex, lineIndex, Buffer[lineIndex] );
((CLog_analyserDlg*)GetParent())->displayCurrentLine( s );
((CLog_analyserDlg*)GetParent())->selectText( 1, matchPos, FindStr.GetLength() );
//BeginFindIndex = lineIndex+1;
return 1;
}
}
}
else
{
BeginFindIndex = (getSelectionIndex() == -1) ? 0 : getSelectionIndex() - 1;
for ( lineIndex=BeginFindIndex; lineIndex>=0; --lineIndex )
{
if ( matchString( Buffer[lineIndex], FindStr, FindDialog->MatchCase()!=0, matchPos ) )
{
scrollTo( lineIndex );
select( lineIndex );
//BeginFindIndex = getSelectionIndex()-1;
//displayString();
CString s;
s.Format( "Found '%s' (upwards from line %d) at line %d:\r\n%s", FindStr, BeginFindIndex, lineIndex, Buffer[lineIndex] );
((CLog_analyserDlg*)GetParent())->displayCurrentLine( s );
((CLog_analyserDlg*)GetParent())->selectText( 1, matchPos, FindStr.GetLength() );
//BeginFindIndex = lineIndex-1;
return 1;
}
}
}
CString s;
s.Format( "Not found (%s from line %d)", FindDialog->SearchDown() ? "downwards" : "upwards", BeginFindIndex );
AfxMessageBox( s );
//BeginFindIndex = 0;
return 0;
}
return 0;
}
void CViewDialog::OnLButtonDown(UINT nFlags, CPoint point)
{
if ( (Index > 0) && (ChildWindowFromPoint( point ) == GetDlgItem( IDC_DragBar )) )
{
((CLog_analyserDlg*)GetParent())->beginResizeView( Index );
}
else
{
//PostMessage(WM_NCHITTEST,HTCAPTION,MAKELPARAM(point.x,point.y));
CDialog::OnLButtonDown(nFlags, point);
}
}