Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1627 lines
34 KiB

/*++
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