|
|
/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
LogWindow.h
Abstract:
Implementation of the log window
Author:
Hakki T. Bostanci (hakkib) 06-Apr-2000
Revision History:
--*/
#ifndef LOGWINDOW_H
#define LOGWINDOW_H
//////////////////////////////////////////////////////////////////////////
//
//
#include <assert.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#include <commctrl.h>
#include <lmcons.h>
#include <set>
#pragma comment(lib, "user32")
#pragma comment(lib, "advapi32")
#pragma comment(lib, "comctl32")
#include "Conv.h"
#include "Wrappers.h"
#include "MyHeap.h"
//////////////////////////////////////////////////////////////////////////
//
//
#define ID_EDIT_COMMENT 1132
#define ID_COPY_MESSAGE 1133
#define ID_PAUSE_OUTPUT 1134
#define IDB_STATES 999
#ifndef LOG_LEVELS
#define TLS_LOGALL 0x0000FFFFL // Log output. Logs all the time.
#define TLS_LOG 0x00000000L // Log output. Logs all the time.
#define TLS_INFO 0x00002000L // Log information.
#define TLS_ABORT 0x00000001L // Log Abort, then kill process.
#define TLS_SEV1 0x00000002L // Log at Severity 1 level
#define TLS_SEV2 0x00000004L // Log at Severity 2 level
#define TLS_SEV3 0x00000008L // Log at Severity 3 level
#define TLS_WARN 0x00000010L // Log at Warn level
#define TLS_PASS 0x00000020L // Log at Pass level
#define TLS_BLOCK 0x00000400L // Block the variation.
#define TLS_BREAK 0x00000800L // Debugger break;
#define TLS_CALLTREE 0x00000040L // Log call-tree (function tracking).
#define TLS_SYSTEM 0x00000080L // Log System debug.
#define TLS_TESTDEBUG 0x00001000L // Debug level.
#define TLS_TEST 0x00000100L // Log Test information (user).
#define TLS_VARIATION 0x00000200L // Log testcase level.
#endif //LOG_LEVELS
//////////////////////////////////////////////////////////////////////////
//
//
class CLogWindow { private: CLogWindow(const CLogWindow &) {} CLogWindow &operator =(const CLogWindow &) { return *this; }
public: CLogWindow(); ~CLogWindow();
void Create(PCTSTR pTitle, HWND hWndParent, HICON hIcon, ULONG nMaxNumMessages = -1); void Destroy();
PCTSTR Log( int nLogLevel, PCTSTR pszFileName, int nCode, PCTSTR pszMessage, PCTSTR pszStatus ) const;
void SetTitle(PCTSTR pTitle) { m_pTitle = pTitle; SetWindowText(m_hWnd, pTitle); }
DWORD WaitForSingleObject( DWORD dwMilliseconds = INFINITE, BOOL bAlertable = FALSE ) const { return m_Closed.WaitForSingleObject(dwMilliseconds, bAlertable); }
BOOL IsClosed() const { return m_bIsClosed; }
void Close(bool bClose = true) { if (bClose) { InterlockedExchange(&m_bIsClosed, TRUE); m_Closed.Set(); } else { InterlockedExchange(&m_bIsClosed, FALSE); m_Closed.Reset(); } }
BOOL IsDirty() const { return m_bIsDirty; }
HWND GetSafeHwnd() const { return m_hWnd; }
HANDLE GetWaitHandle() const { return m_Closed; }
class CSavedData;
struct CListData { CMyStr m_strMsgNum; CMyStr m_strLogLevel; CMyStr m_strFileName; CMyStr m_strCode; CMyStr m_strMessage; CMyStr m_strComment;
CListData( PCTSTR pszMsgNum, PCTSTR pszLogLevel, PCTSTR pszFileName, PCTSTR pszCode, PCTSTR pszMessage, const CSavedData &rSavedData );
explicit CListData(PTSTR pszLine);
void * operator new(size_t, void *pPlacement) { return pPlacement; }
#if _MSC_VER >= 1200
void operator delete(void *, void *) { } #endif
void * operator new(size_t nSize) { return g_MyHeap.allocate(nSize); }
void operator delete(void *pMem) { g_MyHeap.deallocate(pMem); }
bool operator ==(const CListData &rhs) const; bool operator <(const CListData &rhs) const;
void Write(FILE *fOut) const;
static PCTSTR GetArg(PTSTR *pszStr); };
class CSavedData : public std::set<CListData, std::less<CListData>, CMyAlloc<CListData> > { public: void Read(FILE *fIn); void Write(FILE *fOut, PCTSTR pComments = 0) const;
void Merge( const CSavedData &rNewData, CSavedData &rCollisions );
CMyStr m_strUserName; };
BOOL ReadSavedData(PCTSTR pFileName); BOOL WriteSavedData(PCTSTR pFileName, PCTSTR pComments = 0); BOOL WriteModifiedData(PCTSTR pFileName, PCTSTR pComments = 0);
static BOOL FindNtLogLevel( DWORD dwLogLevel, PCTSTR *ppszText, int *piImage );
private: typedef enum { ID_FIRSTCOLUMN = 0, ID_LOGLEVEL = 0, ID_MSGNUM = 1, ID_CODE = 2, ID_FILENAME = 3, ID_MESSAGE = 4, ID_COMMENT = 5, ID_LASTCOLUMN = 5 };
static CLogWindow *This(HWND hWnd) { return (CLogWindow *) GetWindowLongPtr(hWnd, GWLP_USERDATA); }
CListData *GetItemData(int nItem) const { return (CListData *) ListView_GetItemData(m_hList, nItem); }
static DWORD WINAPI ThreadProc(PVOID pParameter);
static ATOM RegisterLogWindow(HICON hIcon);
static LRESULT CALLBACK DialogProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
LRESULT OnCreate(HWND hWnd);
LRESULT OnClose();
LRESULT OnSize(UINT nType, int nW, int nH);
LRESULT OnCopyToClipboard() const;
BOOL CopySelectedItemsToBuffer( HANDLE hMem, PDWORD pdwBufferSize ) const;
LRESULT OnEditComment() const;
LRESULT OnPauseOutput();
void PlaceEditControlOnEditedItem(HWND hEdit) const;
LRESULT OnGetDispInfo(NMLVDISPINFO *pnmv);
LRESULT OnColumnClick(LPNMLISTVIEW pnmv);
static int CALLBACK CompareFunc( LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort );
LRESULT OnBeginLabelEdit(NMLVDISPINFO *pdi);
LRESULT OnEndLabelEdit(NMLVDISPINFO *pdi);
LRESULT OnKeyDown(LPNMLVKEYDOWN pnkd);
LRESULT OnRButtonDown();
public: CSavedData m_SavedData;
private: PCTSTR m_pTitle; HWND m_hWndParent; HICON m_hIcon; Event m_InitComplete; CThread m_Thread; HWND m_hWnd; HWND m_hList; HWND m_hStatWnd; LONG m_bIsClosed; Event m_Closed; int m_SortByHistory[ID_LASTCOLUMN - ID_FIRSTCOLUMN + 1]; mutable LONG m_nMsgNum; int m_nEditedItem; int m_bIsDirty; LONG m_bIsPaused; Event m_Resume; ULONG m_nMaxNumMessages;
mutable CMySimpleCriticalSection m_cs; };
#ifdef IMPLEMENT_LOGWINDOW
//////////////////////////////////////////////////////////////////////////
//
//
//
CLogWindow::CLogWindow() : m_bIsClosed(TRUE), m_Closed(TRUE, TRUE), m_bIsPaused(FALSE), m_Resume(TRUE, TRUE) { m_hWndParent = 0; m_hWnd = 0;
m_nMsgNum = 0;
for (int i = 0; i < COUNTOF(m_SortByHistory); ++i) { m_SortByHistory[i] = 0; }
m_bIsDirty = FALSE; }
//////////////////////////////////////////////////////////////////////////
//
//
//
CLogWindow::~CLogWindow() { Destroy(); }
//////////////////////////////////////////////////////////////////////////
//
//
//
void CLogWindow::Create(PCTSTR pTitle, HWND hWndParent, HICON hIcon, ULONG nMaxNumMessages /*= -1*/) { m_pTitle = pTitle; m_hWndParent = hWndParent; m_hIcon = hIcon; m_nMaxNumMessages = nMaxNumMessages; m_InitComplete = Event(TRUE, FALSE); m_Thread = CThread(ThreadProc, this);
m_InitComplete.WaitForSingleObject(); m_InitComplete.Detach(); }
//////////////////////////////////////////////////////////////////////////
//
//
//
void CLogWindow::Destroy() { //bugbug: this should be handled in WM_DESTROY
int nCount = ListView_GetItemCount(m_hList);
for (int i = 0; i < nCount; ++i) { CListData *pData = GetItemData(i); ListView_SetItemData(m_hList, i, 0); delete pData; }
if (m_Thread.IsAttached()) { PostThreadMessage(m_Thread, WM_QUIT, 0, 0); m_Thread.WaitForSingleObject(); m_Thread.Detach(); }
m_hWnd = 0;
m_nMsgNum = 0; }
//////////////////////////////////////////////////////////////////////////
//
//
//
PCTSTR CLogWindow::Log( int nLogLevel, PCTSTR pszFileName, int nCode, PCTSTR pszMessage, PCTSTR pszStatus ) const { if (m_bIsPaused) { m_Resume.WaitForSingleObject(); }
// get a new entry number for this item
LONG nMsgNum = InterlockedIncrement(&m_nMsgNum);
if ((ULONG) nMsgNum >= m_nMaxNumMessages) { m_cs.Enter(); CListData *pData = GetItemData(0); ListView_DeleteItem(m_hList, 0); m_cs.Leave(); delete pData; }
// prepare the CListData entry for this item
PCTSTR pszLogLevelText; int nLogLevelImage;
FindNtLogLevel(nLogLevel, &pszLogLevelText, &nLogLevelImage);
TCHAR szMsgNum[48]; _itot(nMsgNum, szMsgNum, 10);
TCHAR szCode[48]; _itot(nCode, szCode, 10);
CListData *pData = new CListData( szMsgNum, pszLogLevelText, pszFileName, szCode, pszMessage, m_SavedData );
// insert this new item
LVITEM item; item.mask = LVIF_IMAGE | LVIF_PARAM | LVIF_TEXT; item.iItem = nMsgNum - 1; item.iSubItem = 0; item.iImage = nLogLevelImage; item.lParam = (LPARAM) pData; item.pszText = LPSTR_TEXTCALLBACK;
int iItem = ListView_InsertItem(m_hList, &item); ListView_EnsureVisible(m_hList, iItem, TRUE);
SendMessage(m_hStatWnd, SB_SETTEXT, (WPARAM) SB_SIMPLEID, (LPARAM) pszStatus);
return pData->m_strComment; }
//////////////////////////////////////////////////////////////////////////
//
//
//
BOOL CLogWindow::FindNtLogLevel( DWORD dwLogLevel, PCTSTR *ppszText, int *piImage ) { dwLogLevel &= ~( TLS_CALLTREE | TLS_BREAK | TLS_SYSTEM | TLS_TESTDEBUG | TLS_TEST | TLS_VARIATION );
switch (dwLogLevel) { case TLS_INFO: *ppszText = _T("Info"); *piImage = 5; return TRUE;
case TLS_ABORT: *ppszText = _T("Abort"); *piImage = 0; return TRUE;
case TLS_SEV1: *ppszText = _T("Sev1"); *piImage = 1; return TRUE;
case TLS_SEV2: *ppszText = _T("Sev2"); *piImage = 2; return TRUE;
case TLS_SEV3: *ppszText = _T("Sev3"); *piImage = 3; return TRUE;
case TLS_WARN: *ppszText = _T("Warn"); *piImage = 4; return TRUE;
case TLS_PASS: *ppszText = _T("Pass"); *piImage = 0; return TRUE;
case TLS_BLOCK: *ppszText = _T("Block"); *piImage = 0; return TRUE; }
*ppszText = 0; *piImage = 0; return FALSE; }
//////////////////////////////////////////////////////////////////////////
//
//
//
DWORD WINAPI CLogWindow::ThreadProc(PVOID pParameter) { CLogWindow *that = (CLogWindow *) pParameter;
static ATOM pClassName = RegisterLogWindow(that->m_hIcon);
CreateWindow( (PCTSTR) pClassName, that->m_pTitle, WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 785, 560, that->m_hWndParent, 0, GetModuleHandle(0), that );
MSG msg;
while (GetMessage(&msg, 0, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); }
return msg.wParam; }
//////////////////////////////////////////////////////////////////////////
//
//
//
ATOM CLogWindow::RegisterLogWindow(HICON hIcon) { WNDCLASSEX wcex = { 0 };
wcex.cbSize = sizeof(WNDCLASSEX); wcex.lpfnWndProc = DialogProc; wcex.hInstance = GetModuleHandle(0); wcex.hIcon = hIcon; wcex.hCursor = LoadCursor(0, IDC_ARROW); wcex.lpszClassName = _T("LOGWINDOW");
return RegisterClassEx(&wcex); }
//////////////////////////////////////////////////////////////////////////
//
//
//
LRESULT CALLBACK CLogWindow::DialogProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { switch (uMsg) { case WM_CREATE: return ((CLogWindow *)((LPCREATESTRUCT)lParam)->lpCreateParams)->OnCreate(hWnd);
case WM_CLOSE: return This(hWnd)->OnClose();
case WM_SIZE: return This(hWnd)->OnSize((UINT)wParam, LOWORD(lParam), HIWORD(lParam));
case WM_COMMAND: { CLogWindow *that = This(hWnd);
HWND hEdit = ListView_GetEditControl(that->m_hList); if (hEdit != 0 && hEdit == (HWND) lParam) { that->PlaceEditControlOnEditedItem(hEdit); } else { switch (LOWORD(wParam)) { case ID_COPY_MESSAGE: return that->OnCopyToClipboard(); case ID_EDIT_COMMENT: return that->OnEditComment();
case ID_PAUSE_OUTPUT: return that->OnPauseOutput(); } }
break; }
case WM_USER + 1: { CLogWindow *that = This(hWnd);
HWND hEdit = ListView_GetEditControl(that->m_hList); if (hEdit != 0) { that->PlaceEditControlOnEditedItem(hEdit); }
break; }
case WM_NOTIFY: switch (((LPNMHDR) lParam)->code) { case LVN_GETDISPINFO: return This(hWnd)->OnGetDispInfo((NMLVDISPINFO *) lParam);
case LVN_COLUMNCLICK: return This(hWnd)->OnColumnClick((LPNMLISTVIEW) lParam);
case LVN_BEGINLABELEDIT: return This(hWnd)->OnBeginLabelEdit((NMLVDISPINFO *) lParam);
case LVN_ENDLABELEDIT: return This(hWnd)->OnEndLabelEdit((NMLVDISPINFO *) lParam);
case LVN_KEYDOWN: return This(hWnd)->OnKeyDown((LPNMLVKEYDOWN) lParam);
case NM_RCLICK: return This(hWnd)->OnRButtonDown(); } break; }
return DefWindowProc(hWnd, uMsg, wParam, lParam); }
//////////////////////////////////////////////////////////////////////////
//
//
//
LRESULT CLogWindow::OnCreate(HWND hWnd) { m_hWnd = hWnd; SetWindowLongPtr(m_hWnd, GWLP_USERDATA, (LONG_PTR) this);
m_hStatWnd = CreateStatusWindow(WS_CHILD | WS_VISIBLE, 0, m_hWnd, 0);
SendMessage(m_hStatWnd, SB_SIMPLE, (WPARAM) TRUE, (LPARAM) 0);
m_hList = CreateWindow( _T("SysListView32"), 0, WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_EDITLABELS, 0, 0, 0, 0, m_hWnd, 0, GetModuleHandle(0), 0 );
ListView_SetExtendedListViewStyle( m_hList, LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP | LVS_EX_LABELTIP );
HIMAGELIST hImgList = ImageList_LoadBitmap( GetModuleHandle(0), MAKEINTRESOURCE(IDB_STATES), 16, 0, RGB(255, 255, 255) ); if (hImgList) { ListView_SetImageList(m_hList, hImgList, LVSIL_SMALL); }
ListView_InsertColumn2(m_hList, 0, _T("Type"), LVCFMT_LEFT, 60, ID_LOGLEVEL); ListView_InsertColumn2(m_hList, 1, _T("#"), LVCFMT_LEFT, 40, ID_MSGNUM); ListView_InsertColumn2(m_hList, 2, _T("Code"), LVCFMT_LEFT, 40, ID_CODE); ListView_InsertColumn2(m_hList, 3, _T("File"), LVCFMT_LEFT, 100, ID_FILENAME); ListView_InsertColumn2(m_hList, 4, _T("Message"), LVCFMT_LEFT, 420, ID_MESSAGE); ListView_InsertColumn2(m_hList, 5, _T("Comment"), LVCFMT_LEFT, 100, ID_COMMENT);
SetForegroundWindow(m_hList);
InterlockedExchange(&m_bIsClosed, FALSE); m_Closed.Reset();
m_InitComplete.Set();
return 0; }
//////////////////////////////////////////////////////////////////////////
//
//
//
LRESULT CLogWindow::OnClose() { if (!m_bIsClosed) { InterlockedExchange(&m_bIsClosed, TRUE); m_Closed.Set();
if (m_bIsPaused) { InterlockedExchange(&m_bIsPaused, FALSE); m_Resume.Set(); }
TCHAR szNewTitle[MAX_PATH]; _tcscpy(szNewTitle, m_pTitle); _tcscat(szNewTitle, _T(" <closing>"));; SetWindowText(m_hWnd, szNewTitle); }
return 0; }
//////////////////////////////////////////////////////////////////////////
//
//
//
LRESULT CLogWindow::OnSize(UINT nType, int nW, int nH) { RECT r;
GetWindowRect(m_hStatWnd, &r);
int nStatWndH = r.bottom - r.top;
MoveWindow(m_hList, 0, 0, nW, nH - nStatWndH, TRUE);
TCHAR szBuffer[1024]; // ***bugbug: figure out why the statwnd forgets its caption
SendMessage(m_hStatWnd, SB_GETTEXT, (WPARAM) SB_SIMPLEID, (LPARAM) szBuffer);
MoveWindow(m_hStatWnd, 0, nH - nStatWndH, nW, nStatWndH, TRUE);
SendMessage(m_hStatWnd, SB_SETTEXT, (WPARAM) SB_SIMPLEID, (LPARAM) szBuffer);
return 0; }
//////////////////////////////////////////////////////////////////////////
//
//
//
LRESULT CLogWindow::OnCopyToClipboard() const { HANDLE hMem = 0;
for ( DWORD dwSize = 4 * 1024; (hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, dwSize)) != 0 && !CopySelectedItemsToBuffer(hMem, &dwSize); GlobalFree(hMem) ) { // start with a 4KB buffer size
// if buffer allocation fails, exit
// if CopySelectedItemsToBuffer succeeds, exit
// otherwise, delete the buffer and retry
}
if (hMem) { BOOL bResult = FALSE;
if (OpenClipboard(m_hWnd)) { if (EmptyClipboard()) { if (SetClipboardData(CF_TEXT, hMem)) { bResult = TRUE; } }
CloseClipboard(); }
if (!bResult) { GlobalFree(hMem); } }
return 0; }
//////////////////////////////////////////////////////////////////////////
//
//
//
BOOL CLogWindow::CopySelectedItemsToBuffer( HANDLE hMem, PDWORD pdwBufferSize ) const { PSTR pMem = (PSTR) GlobalLock(hMem);
CBufferFill Buffer(pMem, *pdwBufferSize);
if (pMem) { int nItem = -1;
while ((nItem = ListView_GetNextItem(m_hList, nItem, LVNI_SELECTED)) != -1) { USES_CONVERSION;
CListData *pData = GetItemData(nItem);
if (pData->m_strFileName && *pData->m_strFileName) { Buffer.AddTop(CStrBlob(T2A(pData->m_strFileName))); Buffer.AddTop(CStrBlob(": ")); }
Buffer.AddTop(CStrBlob(T2A(pData->m_strMessage)));
if (pData->m_strComment && *pData->m_strComment) { Buffer.AddTop(CStrBlob(" (")); Buffer.AddTop(CStrBlob(T2A(pData->m_strComment))); Buffer.AddTop(CStrBlob(")")); }
Buffer.AddTop(CStrBlob("\r\n")); }
Buffer.AddTop(CSzBlob("")); // terminating NULL
GlobalUnlock(hMem); }
*pdwBufferSize -= (DWORD) Buffer.BytesLeft();
return Buffer.BytesLeft() >= 0; }
//////////////////////////////////////////////////////////////////////////
//
//
//
LRESULT CLogWindow::OnEditComment() const { if (m_nEditedItem != -1) { ListView_EditLabel(m_hList, m_nEditedItem); }
return 0; }
//////////////////////////////////////////////////////////////////////////
//
//
//
LRESULT CLogWindow::OnPauseOutput() { if (m_bIsPaused) { InterlockedExchange(&m_bIsPaused, FALSE); m_Resume.Set();
SetWindowText(m_hWnd, m_pTitle); } else { InterlockedExchange(&m_bIsPaused, TRUE); m_Resume.Reset();
TCHAR szNewTitle[MAX_PATH]; _tcscpy(szNewTitle, m_pTitle); _tcscat(szNewTitle, _T(" <paused>"));; SetWindowText(m_hWnd, szNewTitle); }
return 0; }
//////////////////////////////////////////////////////////////////////////
//
//
//
void CLogWindow::PlaceEditControlOnEditedItem(HWND hEdit) const { RECT r;
ListView_GetSubItemRect( m_hList, m_nEditedItem, ID_COMMENT, LVIR_LABEL, &r );
MoveWindow( hEdit, r.left, r.top, r.right - r.left, r.bottom - r.top, TRUE ); }
//////////////////////////////////////////////////////////////////////////
//
//
//
LRESULT CLogWindow::OnGetDispInfo(NMLVDISPINFO *pnmv) { ASSERT(pnmv->item.mask == LVIF_TEXT);
CListData *pData = (CListData *) pnmv->item.lParam;
if (pData) { switch (pnmv->item.iSubItem) { case ID_LOGLEVEL: pnmv->item.pszText = pData->m_strLogLevel; break; case ID_MSGNUM: pnmv->item.pszText = pData->m_strMsgNum; break; case ID_CODE: pnmv->item.pszText = pData->m_strCode; break; case ID_FILENAME: pnmv->item.pszText = pData->m_strFileName; break; case ID_MESSAGE: pnmv->item.pszText = pData->m_strMessage; break; case ID_COMMENT: pnmv->item.pszText = pData->m_strComment; break; default: ASSERT(FALSE); }
ASSERT(pnmv->item.pszText); }
return 0; }
//////////////////////////////////////////////////////////////////////////
//
//
//
LRESULT CLogWindow::OnColumnClick(LPNMLISTVIEW pnmv) { // convert zero based column number to one based, as
// we will use negative numbers to indicate sort direction
int nColumn = pnmv->iSubItem + 1;
if (Abs(m_SortByHistory[0]) == nColumn) { // if the user has pressed a column header twice,
// reverse the sort direction
m_SortByHistory[0] *= -1; } else { // if the user has selected a new column, slide down
// the history list and enter the new column to the top
// position. Also keep the last entry, that will eventually
// end the recursive CompareFunc in the worst case
for (int i = COUNTOF(m_SortByHistory) - 2; i >= 1; --i) { m_SortByHistory[i] = m_SortByHistory[i-1]; }
m_SortByHistory[0] = nColumn; }
// sort the items
ListView_SortItems(m_hList, CompareFunc, m_SortByHistory);
// ensure that the selected item is visible
ListView_EnsureVisible( m_hList, ListView_GetNextItem(m_hList, -1, LVNI_SELECTED), TRUE );
return 0; }
//////////////////////////////////////////////////////////////////////////
//
//
//
int CALLBACK CLogWindow::CompareFunc( LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort ) { CListData *pData1 = (CListData *) lParam1; CListData *pData2 = (CListData *) lParam2; int *SortByHistory = (int *) lParamSort;
ASSERT(pData1); ASSERT(pData2); ASSERT(SortByHistory);
int nSortDir = 1; int nColumn = SortByHistory[0];
if (nColumn < 0) { nSortDir *= -1; nColumn *= -1; }
int nResult = 0;
switch (nColumn) { case 0: nResult = -1; break;
case ID_MSGNUM + 1: nResult = Cmp(_ttoi(pData1->m_strMsgNum), _ttoi(pData2->m_strMsgNum)); break;
case ID_LOGLEVEL + 1: nResult = _tcscmp(pData1->m_strLogLevel, pData2->m_strLogLevel); break;
case ID_FILENAME + 1: nResult = _tcscmp(pData1->m_strFileName, pData2->m_strFileName); break;
case ID_CODE + 1: nResult = Cmp(_ttoi(pData1->m_strCode), _ttoi(pData2->m_strCode)); break;
case ID_MESSAGE + 1: nResult = _tcscmp(pData1->m_strMessage, pData2->m_strMessage); break;
case ID_COMMENT + 1: nResult = _tcscmp(pData1->m_strComment, pData2->m_strComment); break;
default: ASSERT(FALSE); }
// in case of equality, go on with the comparison using the next
// column in the history list
return nResult != 0 ? nSortDir * nResult : CompareFunc(lParam1, lParam2, (LPARAM) (SortByHistory + 1)); }
//////////////////////////////////////////////////////////////////////////
//
//
//
LRESULT CLogWindow::OnBeginLabelEdit(NMLVDISPINFO *pdi) { HWND hEdit = ListView_GetEditControl(m_hList);
if (hEdit) { CListData *pData = GetItemData(pdi->item.iItem);
SetWindowText(hEdit, pData->m_strComment);
m_nEditedItem = pdi->item.iItem;
PostMessage(m_hWnd, WM_USER + 1, 0, 0); }
return 0; }
//////////////////////////////////////////////////////////////////////////
//
//
//
LRESULT CLogWindow::OnEndLabelEdit(NMLVDISPINFO *pdi) { if (pdi->item.iItem != -1 && pdi->item.pszText != 0) { CListData *pData = GetItemData(pdi->item.iItem);
if (_tcscmp(pData->m_strComment, pdi->item.pszText) != 0) { // save the comment
pData->m_strComment = pdi->item.pszText;
// replace the tabs (if any) with space, we use tabs as
// the column delimiter character
for ( PTSTR pszTab = _tcschr(pData->m_strComment, _T('\t')); pszTab; pszTab = _tcschr(pszTab + 1, _T('\t')) ) { *pszTab = _T(' '); }
ListView_SetItemText(m_hList, pdi->item.iItem, ID_COMMENT, pData->m_strComment);
// save this new entry
m_SavedData.erase(*pData); m_SavedData.insert(*pData);
m_bIsDirty = TRUE; } }
return 0; }
//////////////////////////////////////////////////////////////////////////
//
//
//
LRESULT CLogWindow::OnKeyDown(LPNMLVKEYDOWN pnkd) { if (pnkd->wVKey == VK_INSERT && GetKeyState(VK_CONTROL) < 0) // CTRL+INS
{ return OnCopyToClipboard(); } else if (pnkd->wVKey == VK_RETURN) { m_nEditedItem = ListView_GetSelectionMark(m_hList);
return OnEditComment(); } else if (pnkd->wVKey == VK_PAUSE) { return OnPauseOutput(); }
return 0; }
//////////////////////////////////////////////////////////////////////////
//
//
//
LRESULT CLogWindow::OnRButtonDown() { POINT p; GetCursorPos(&p);
LVHITTESTINFO lvHitTestInfo;
lvHitTestInfo.pt = p;
ScreenToClient(m_hList, &lvHitTestInfo.pt);
ListView_HitTest(m_hList, &lvHitTestInfo);
m_nEditedItem = lvHitTestInfo.iItem;
HMENU hMenu = CreatePopupMenu();
if (hMenu) { AppendMenu(hMenu, MF_STRING, ID_EDIT_COMMENT, _T("&Edit\tEnter"));
AppendMenu(hMenu, MF_STRING, ID_COPY_MESSAGE, _T("&Copy\tCtrl+Ins"));
AppendMenu(hMenu, MF_STRING, ID_PAUSE_OUTPUT, m_bIsPaused ? _T("&Resume") : _T("&Pause"));
TrackPopupMenu( hMenu, TPM_LEFTALIGN | TPM_TOPALIGN, p.x, p.y, 0, m_hWnd, 0 );
DestroyMenu(hMenu); }
return 0; }
//////////////////////////////////////////////////////////////////////////
//
//
//
CLogWindow::CListData::CListData( PCTSTR pszMsgNum, PCTSTR pszLogLevel, PCTSTR pszFileName, PCTSTR pszCode, PCTSTR pszMessage, const CSavedData &rSavedData ) : m_strMsgNum(pszMsgNum), m_strLogLevel(pszLogLevel), m_strFileName(pszFileName), m_strCode(pszCode), m_strMessage(pszMessage) { CSavedData::const_iterator itData = rSavedData.find(*this); m_strComment = itData != rSavedData.end() ? itData->m_strComment : _T(""); }
//////////////////////////////////////////////////////////////////////////
//
//
//
CLogWindow::CListData::CListData( PTSTR pszLine ) : m_strLogLevel(GetArg(&pszLine)), m_strFileName(GetArg(&pszLine)), m_strCode(GetArg(&pszLine)), m_strMessage(GetArg(&pszLine)), m_strComment(GetArg(&pszLine)) { }
//////////////////////////////////////////////////////////////////////////
//
//
//
bool CLogWindow::CListData::operator ==(const CListData &rhs) const { return _tcscmp(m_strMessage, rhs.m_strMessage) == 0 && _tcscmp(m_strCode, rhs.m_strCode) == 0 && _tcscmp(m_strFileName, rhs.m_strFileName) == 0 && _tcscmp(m_strLogLevel, rhs.m_strLogLevel) == 0; }
//////////////////////////////////////////////////////////////////////////
//
//
//
bool CLogWindow::CListData::operator <(const CListData &rhs) const { int nCmpMessage, nCmpCode, nCmpFileName, nCmpLogLevel;
return ((nCmpMessage = _tcscmp(m_strMessage, rhs.m_strMessage)) < 0 || (nCmpMessage == 0 && ((nCmpCode = _tcscmp(m_strCode, rhs.m_strCode)) < 0 || (nCmpCode == 0 && ((nCmpFileName = _tcscmp(m_strFileName, rhs.m_strFileName)) < 0 || (nCmpFileName == 0 && ((nCmpLogLevel = _tcscmp(m_strLogLevel, rhs.m_strLogLevel)) < 0) ) ) ) ) ) ); }
//////////////////////////////////////////////////////////////////////////
//
//
//
void CLogWindow::CListData::Write(FILE *fOut) const { _ftprintf( fOut, _T("%s\t%s\t%s\t%s\t%s\r\n"), m_strLogLevel, m_strFileName, m_strCode, m_strMessage, m_strComment ); }
//////////////////////////////////////////////////////////////////////////
//
//
//
PCTSTR CLogWindow::CListData::GetArg(PTSTR *pszStr) { PTSTR pszStart = *pszStr; PTSTR pszEnd = pszStart;
while ( *pszEnd != '\t' && *pszEnd != '\n' && *pszEnd != '\r' && *pszEnd != '\0' ) { pszEnd = CharNext(pszEnd); }
*pszStr = CharNext(pszEnd);
while ( **pszStr == '\n' || **pszStr == '\r' ) { *pszStr = CharNext(*pszStr); }
*pszEnd = '\0';
return pszStart; }
//////////////////////////////////////////////////////////////////////////
//
//
//
void CLogWindow::CSavedData::Read(FILE *fIn) { TCHAR szLine[4096];
while (_fgetts(szLine, COUNTOF(szLine), fIn)) { if (szLine[0] == _T('#')) { *FindEol(szLine) = '\0'; m_strUserName = szLine + 2; } else { insert(CListData(szLine)); } } }
//////////////////////////////////////////////////////////////////////////
//
//
//
void CLogWindow::CSavedData::Write(FILE *fOut, PCTSTR pComments /*= 0*/) const { CUserName UserName; CComputerName ComputerName;
_ftprintf( fOut, pComments && *pComments ? _T("# %s@%s %s\n") : _T("# %s@%s\n"), (PCTSTR) UserName, (PCTSTR) ComputerName, pComments );
for ( const_iterator itData = begin(); itData != end(); ++itData ) { itData->Write(fOut); } }
//////////////////////////////////////////////////////////////////////////
//
//
//
void CLogWindow::CSavedData::Merge( const CSavedData &rNewData, CSavedData &rCollisions ) { rCollisions.clear();
for ( const_iterator itData = rNewData.begin(); itData != rNewData.end(); ++itData ) { const_iterator itMatch = find(*itData);
if ( itMatch != end() && _tcscmp(itMatch->m_strComment, itData->m_strComment) != 0 ) { rCollisions.insert(*itMatch); erase(itMatch); }
insert(*itData); } }
//////////////////////////////////////////////////////////////////////////
//
//
//
BOOL CLogWindow::ReadSavedData(PCTSTR pFileName) { BOOL bResult = FALSE;
try { m_SavedData.Read(CCFile(pFileName, _T("rt")));
bResult = TRUE; } catch (const CError &) { MessageBox( 0, _T("Cannot download the comments from the central database. ") _T("Previous comments will not be available on the results window."), 0, MB_ICONERROR | MB_OK ); }
return bResult; }
//////////////////////////////////////////////////////////////////////////
//
//
//
BOOL CLogWindow::WriteSavedData(PCTSTR pFileName, PCTSTR pComments /*= 0*/) { BOOL bResult = FALSE;
HANDLE hFile = CreateFile( pFileName, GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 );
if (hFile != INVALID_HANDLE_VALUE) { FILE *fFile = OpenOSHandle(hFile, _O_TEXT, _T("wt"));
m_SavedData.Write(fFile, pComments);
bResult = fclose(fFile) == 0; }
return bResult;
/*BOOL bResult = FALSE;
try { m_SavedData.Write(CCFile(pFileName, _T("wt")), PCTSTR pComments);
bResult = TRUE; } catch (const CError &) { //
}
return bResult;*/ }
//////////////////////////////////////////////////////////////////////////
//
//
//
BOOL CLogWindow::WriteModifiedData(PCTSTR pFileName, PCTSTR pComments /*= 0*/) { BOOL bResult = TRUE;
if ( IsDirty() && MessageBox( 0, _T("You have changed some of the comments on the results window. ") _T("Do you want to save these changes to the central database?"), m_pTitle, MB_ICONQUESTION | MB_YESNO ) == IDYES ) { bResult = FALSE;
do { try { m_SavedData.Write(CCFile(pFileName, _T("wt")), pComments);
MessageBox( 0, _T("Comments database updated successfully."), m_pTitle, MB_ICONINFORMATION | MB_OK );
bResult = TRUE; } catch (const CError &) { // bResult remains FALSE;
} } while ( !bResult && MessageBox( 0, _T("Cannot update the results. Do you want to try again?"), 0, MB_ICONQUESTION | MB_YESNO ) == IDYES ); }
return bResult; }
#endif //IMPLEMENT_LOGWINDOW
#endif //PROGRESSDLG_H
|