1628 lines
34 KiB
C++
1628 lines
34 KiB
C++
/*++
|
|
|
|
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
|
|
|