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

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. LogWindow.h
  5. Abstract:
  6. Implementation of the log window
  7. Author:
  8. Hakki T. Bostanci (hakkib) 06-Apr-2000
  9. Revision History:
  10. --*/
  11. #ifndef LOGWINDOW_H
  12. #define LOGWINDOW_H
  13. //////////////////////////////////////////////////////////////////////////
  14. //
  15. //
  16. #include <assert.h>
  17. #include <stdio.h>
  18. #include <fcntl.h>
  19. #include <io.h>
  20. #include <commctrl.h>
  21. #include <lmcons.h>
  22. #include <set>
  23. #pragma comment(lib, "user32")
  24. #pragma comment(lib, "advapi32")
  25. #pragma comment(lib, "comctl32")
  26. #include "Conv.h"
  27. #include "Wrappers.h"
  28. #include "MyHeap.h"
  29. //////////////////////////////////////////////////////////////////////////
  30. //
  31. //
  32. #define ID_EDIT_COMMENT 1132
  33. #define ID_COPY_MESSAGE 1133
  34. #define ID_PAUSE_OUTPUT 1134
  35. #define IDB_STATES 999
  36. #ifndef LOG_LEVELS
  37. #define TLS_LOGALL 0x0000FFFFL // Log output. Logs all the time.
  38. #define TLS_LOG 0x00000000L // Log output. Logs all the time.
  39. #define TLS_INFO 0x00002000L // Log information.
  40. #define TLS_ABORT 0x00000001L // Log Abort, then kill process.
  41. #define TLS_SEV1 0x00000002L // Log at Severity 1 level
  42. #define TLS_SEV2 0x00000004L // Log at Severity 2 level
  43. #define TLS_SEV3 0x00000008L // Log at Severity 3 level
  44. #define TLS_WARN 0x00000010L // Log at Warn level
  45. #define TLS_PASS 0x00000020L // Log at Pass level
  46. #define TLS_BLOCK 0x00000400L // Block the variation.
  47. #define TLS_BREAK 0x00000800L // Debugger break;
  48. #define TLS_CALLTREE 0x00000040L // Log call-tree (function tracking).
  49. #define TLS_SYSTEM 0x00000080L // Log System debug.
  50. #define TLS_TESTDEBUG 0x00001000L // Debug level.
  51. #define TLS_TEST 0x00000100L // Log Test information (user).
  52. #define TLS_VARIATION 0x00000200L // Log testcase level.
  53. #endif //LOG_LEVELS
  54. //////////////////////////////////////////////////////////////////////////
  55. //
  56. //
  57. class CLogWindow
  58. {
  59. private:
  60. CLogWindow(const CLogWindow &) {}
  61. CLogWindow &operator =(const CLogWindow &) { return *this; }
  62. public:
  63. CLogWindow();
  64. ~CLogWindow();
  65. void Create(PCTSTR pTitle, HWND hWndParent, HICON hIcon, ULONG nMaxNumMessages = -1);
  66. void Destroy();
  67. PCTSTR
  68. Log(
  69. int nLogLevel,
  70. PCTSTR pszFileName,
  71. int nCode,
  72. PCTSTR pszMessage,
  73. PCTSTR pszStatus
  74. ) const;
  75. void SetTitle(PCTSTR pTitle)
  76. {
  77. m_pTitle = pTitle;
  78. SetWindowText(m_hWnd, pTitle);
  79. }
  80. DWORD
  81. WaitForSingleObject(
  82. DWORD dwMilliseconds = INFINITE,
  83. BOOL bAlertable = FALSE
  84. ) const
  85. {
  86. return m_Closed.WaitForSingleObject(dwMilliseconds, bAlertable);
  87. }
  88. BOOL IsClosed() const
  89. {
  90. return m_bIsClosed;
  91. }
  92. void Close(bool bClose = true)
  93. {
  94. if (bClose)
  95. {
  96. InterlockedExchange(&m_bIsClosed, TRUE);
  97. m_Closed.Set();
  98. }
  99. else
  100. {
  101. InterlockedExchange(&m_bIsClosed, FALSE);
  102. m_Closed.Reset();
  103. }
  104. }
  105. BOOL IsDirty() const
  106. {
  107. return m_bIsDirty;
  108. }
  109. HWND GetSafeHwnd() const
  110. {
  111. return m_hWnd;
  112. }
  113. HANDLE GetWaitHandle() const
  114. {
  115. return m_Closed;
  116. }
  117. class CSavedData;
  118. struct CListData
  119. {
  120. CMyStr m_strMsgNum;
  121. CMyStr m_strLogLevel;
  122. CMyStr m_strFileName;
  123. CMyStr m_strCode;
  124. CMyStr m_strMessage;
  125. CMyStr m_strComment;
  126. CListData(
  127. PCTSTR pszMsgNum,
  128. PCTSTR pszLogLevel,
  129. PCTSTR pszFileName,
  130. PCTSTR pszCode,
  131. PCTSTR pszMessage,
  132. const CSavedData &rSavedData
  133. );
  134. explicit CListData(PTSTR pszLine);
  135. void * operator new(size_t, void *pPlacement)
  136. {
  137. return pPlacement;
  138. }
  139. #if _MSC_VER >= 1200
  140. void operator delete(void *, void *)
  141. {
  142. }
  143. #endif
  144. void * operator new(size_t nSize)
  145. {
  146. return g_MyHeap.allocate(nSize);
  147. }
  148. void operator delete(void *pMem)
  149. {
  150. g_MyHeap.deallocate(pMem);
  151. }
  152. bool operator ==(const CListData &rhs) const;
  153. bool operator <(const CListData &rhs) const;
  154. void Write(FILE *fOut) const;
  155. static PCTSTR GetArg(PTSTR *pszStr);
  156. };
  157. class CSavedData : public std::set<CListData, std::less<CListData>, CMyAlloc<CListData> >
  158. {
  159. public:
  160. void Read(FILE *fIn);
  161. void Write(FILE *fOut, PCTSTR pComments = 0) const;
  162. void
  163. Merge(
  164. const CSavedData &rNewData,
  165. CSavedData &rCollisions
  166. );
  167. CMyStr m_strUserName;
  168. };
  169. BOOL ReadSavedData(PCTSTR pFileName);
  170. BOOL WriteSavedData(PCTSTR pFileName, PCTSTR pComments = 0);
  171. BOOL WriteModifiedData(PCTSTR pFileName, PCTSTR pComments = 0);
  172. static
  173. BOOL
  174. FindNtLogLevel(
  175. DWORD dwLogLevel,
  176. PCTSTR *ppszText,
  177. int *piImage
  178. );
  179. private:
  180. typedef enum
  181. {
  182. ID_FIRSTCOLUMN = 0,
  183. ID_LOGLEVEL = 0,
  184. ID_MSGNUM = 1,
  185. ID_CODE = 2,
  186. ID_FILENAME = 3,
  187. ID_MESSAGE = 4,
  188. ID_COMMENT = 5,
  189. ID_LASTCOLUMN = 5
  190. };
  191. static CLogWindow *This(HWND hWnd)
  192. {
  193. return (CLogWindow *) GetWindowLongPtr(hWnd, GWLP_USERDATA);
  194. }
  195. CListData *GetItemData(int nItem) const
  196. {
  197. return (CListData *) ListView_GetItemData(m_hList, nItem);
  198. }
  199. static DWORD WINAPI ThreadProc(PVOID pParameter);
  200. static ATOM RegisterLogWindow(HICON hIcon);
  201. static
  202. LRESULT
  203. CALLBACK
  204. DialogProc(
  205. HWND hWnd,
  206. UINT uMsg,
  207. WPARAM wParam,
  208. LPARAM lParam
  209. );
  210. LRESULT OnCreate(HWND hWnd);
  211. LRESULT OnClose();
  212. LRESULT OnSize(UINT nType, int nW, int nH);
  213. LRESULT OnCopyToClipboard() const;
  214. BOOL
  215. CopySelectedItemsToBuffer(
  216. HANDLE hMem,
  217. PDWORD pdwBufferSize
  218. ) const;
  219. LRESULT OnEditComment() const;
  220. LRESULT OnPauseOutput();
  221. void PlaceEditControlOnEditedItem(HWND hEdit) const;
  222. LRESULT OnGetDispInfo(NMLVDISPINFO *pnmv);
  223. LRESULT OnColumnClick(LPNMLISTVIEW pnmv);
  224. static
  225. int
  226. CALLBACK
  227. CompareFunc(
  228. LPARAM lParam1,
  229. LPARAM lParam2,
  230. LPARAM lParamSort
  231. );
  232. LRESULT OnBeginLabelEdit(NMLVDISPINFO *pdi);
  233. LRESULT OnEndLabelEdit(NMLVDISPINFO *pdi);
  234. LRESULT OnKeyDown(LPNMLVKEYDOWN pnkd);
  235. LRESULT OnRButtonDown();
  236. public:
  237. CSavedData m_SavedData;
  238. private:
  239. PCTSTR m_pTitle;
  240. HWND m_hWndParent;
  241. HICON m_hIcon;
  242. Event m_InitComplete;
  243. CThread m_Thread;
  244. HWND m_hWnd;
  245. HWND m_hList;
  246. HWND m_hStatWnd;
  247. LONG m_bIsClosed;
  248. Event m_Closed;
  249. int m_SortByHistory[ID_LASTCOLUMN - ID_FIRSTCOLUMN + 1];
  250. mutable LONG m_nMsgNum;
  251. int m_nEditedItem;
  252. int m_bIsDirty;
  253. LONG m_bIsPaused;
  254. Event m_Resume;
  255. ULONG m_nMaxNumMessages;
  256. mutable CMySimpleCriticalSection m_cs;
  257. };
  258. #ifdef IMPLEMENT_LOGWINDOW
  259. //////////////////////////////////////////////////////////////////////////
  260. //
  261. //
  262. //
  263. CLogWindow::CLogWindow() :
  264. m_bIsClosed(TRUE),
  265. m_Closed(TRUE, TRUE),
  266. m_bIsPaused(FALSE),
  267. m_Resume(TRUE, TRUE)
  268. {
  269. m_hWndParent = 0;
  270. m_hWnd = 0;
  271. m_nMsgNum = 0;
  272. for (int i = 0; i < COUNTOF(m_SortByHistory); ++i)
  273. {
  274. m_SortByHistory[i] = 0;
  275. }
  276. m_bIsDirty = FALSE;
  277. }
  278. //////////////////////////////////////////////////////////////////////////
  279. //
  280. //
  281. //
  282. CLogWindow::~CLogWindow()
  283. {
  284. Destroy();
  285. }
  286. //////////////////////////////////////////////////////////////////////////
  287. //
  288. //
  289. //
  290. void CLogWindow::Create(PCTSTR pTitle, HWND hWndParent, HICON hIcon, ULONG nMaxNumMessages /*= -1*/)
  291. {
  292. m_pTitle = pTitle;
  293. m_hWndParent = hWndParent;
  294. m_hIcon = hIcon;
  295. m_nMaxNumMessages = nMaxNumMessages;
  296. m_InitComplete = Event(TRUE, FALSE);
  297. m_Thread = CThread(ThreadProc, this);
  298. m_InitComplete.WaitForSingleObject();
  299. m_InitComplete.Detach();
  300. }
  301. //////////////////////////////////////////////////////////////////////////
  302. //
  303. //
  304. //
  305. void CLogWindow::Destroy()
  306. {
  307. //bugbug: this should be handled in WM_DESTROY
  308. int nCount = ListView_GetItemCount(m_hList);
  309. for (int i = 0; i < nCount; ++i)
  310. {
  311. CListData *pData = GetItemData(i);
  312. ListView_SetItemData(m_hList, i, 0);
  313. delete pData;
  314. }
  315. if (m_Thread.IsAttached())
  316. {
  317. PostThreadMessage(m_Thread, WM_QUIT, 0, 0);
  318. m_Thread.WaitForSingleObject();
  319. m_Thread.Detach();
  320. }
  321. m_hWnd = 0;
  322. m_nMsgNum = 0;
  323. }
  324. //////////////////////////////////////////////////////////////////////////
  325. //
  326. //
  327. //
  328. PCTSTR
  329. CLogWindow::Log(
  330. int nLogLevel,
  331. PCTSTR pszFileName,
  332. int nCode,
  333. PCTSTR pszMessage,
  334. PCTSTR pszStatus
  335. ) const
  336. {
  337. if (m_bIsPaused)
  338. {
  339. m_Resume.WaitForSingleObject();
  340. }
  341. // get a new entry number for this item
  342. LONG nMsgNum = InterlockedIncrement(&m_nMsgNum);
  343. if ((ULONG) nMsgNum >= m_nMaxNumMessages)
  344. {
  345. m_cs.Enter();
  346. CListData *pData = GetItemData(0);
  347. ListView_DeleteItem(m_hList, 0);
  348. m_cs.Leave();
  349. delete pData;
  350. }
  351. // prepare the CListData entry for this item
  352. PCTSTR pszLogLevelText;
  353. int nLogLevelImage;
  354. FindNtLogLevel(nLogLevel, &pszLogLevelText, &nLogLevelImage);
  355. TCHAR szMsgNum[48];
  356. _itot(nMsgNum, szMsgNum, 10);
  357. TCHAR szCode[48];
  358. _itot(nCode, szCode, 10);
  359. CListData *pData = new CListData(
  360. szMsgNum,
  361. pszLogLevelText,
  362. pszFileName,
  363. szCode,
  364. pszMessage,
  365. m_SavedData
  366. );
  367. // insert this new item
  368. LVITEM item;
  369. item.mask = LVIF_IMAGE | LVIF_PARAM | LVIF_TEXT;
  370. item.iItem = nMsgNum - 1;
  371. item.iSubItem = 0;
  372. item.iImage = nLogLevelImage;
  373. item.lParam = (LPARAM) pData;
  374. item.pszText = LPSTR_TEXTCALLBACK;
  375. int iItem = ListView_InsertItem(m_hList, &item);
  376. ListView_EnsureVisible(m_hList, iItem, TRUE);
  377. SendMessage(m_hStatWnd, SB_SETTEXT, (WPARAM) SB_SIMPLEID, (LPARAM) pszStatus);
  378. return pData->m_strComment;
  379. }
  380. //////////////////////////////////////////////////////////////////////////
  381. //
  382. //
  383. //
  384. BOOL
  385. CLogWindow::FindNtLogLevel(
  386. DWORD dwLogLevel,
  387. PCTSTR *ppszText,
  388. int *piImage
  389. )
  390. {
  391. dwLogLevel &= ~(
  392. TLS_CALLTREE | TLS_BREAK | TLS_SYSTEM | TLS_TESTDEBUG |
  393. TLS_TEST | TLS_VARIATION
  394. );
  395. switch (dwLogLevel)
  396. {
  397. case TLS_INFO:
  398. *ppszText = _T("Info");
  399. *piImage = 5;
  400. return TRUE;
  401. case TLS_ABORT:
  402. *ppszText = _T("Abort");
  403. *piImage = 0;
  404. return TRUE;
  405. case TLS_SEV1:
  406. *ppszText = _T("Sev1");
  407. *piImage = 1;
  408. return TRUE;
  409. case TLS_SEV2:
  410. *ppszText = _T("Sev2");
  411. *piImage = 2;
  412. return TRUE;
  413. case TLS_SEV3:
  414. *ppszText = _T("Sev3");
  415. *piImage = 3;
  416. return TRUE;
  417. case TLS_WARN:
  418. *ppszText = _T("Warn");
  419. *piImage = 4;
  420. return TRUE;
  421. case TLS_PASS:
  422. *ppszText = _T("Pass");
  423. *piImage = 0;
  424. return TRUE;
  425. case TLS_BLOCK:
  426. *ppszText = _T("Block");
  427. *piImage = 0;
  428. return TRUE;
  429. }
  430. *ppszText = 0;
  431. *piImage = 0;
  432. return FALSE;
  433. }
  434. //////////////////////////////////////////////////////////////////////////
  435. //
  436. //
  437. //
  438. DWORD WINAPI CLogWindow::ThreadProc(PVOID pParameter)
  439. {
  440. CLogWindow *that = (CLogWindow *) pParameter;
  441. static ATOM pClassName = RegisterLogWindow(that->m_hIcon);
  442. CreateWindow(
  443. (PCTSTR) pClassName,
  444. that->m_pTitle,
  445. WS_OVERLAPPEDWINDOW | WS_VISIBLE,
  446. CW_USEDEFAULT,
  447. CW_USEDEFAULT,
  448. 785,
  449. 560,
  450. that->m_hWndParent,
  451. 0,
  452. GetModuleHandle(0),
  453. that
  454. );
  455. MSG msg;
  456. while (GetMessage(&msg, 0, 0, 0))
  457. {
  458. TranslateMessage(&msg);
  459. DispatchMessage(&msg);
  460. }
  461. return msg.wParam;
  462. }
  463. //////////////////////////////////////////////////////////////////////////
  464. //
  465. //
  466. //
  467. ATOM CLogWindow::RegisterLogWindow(HICON hIcon)
  468. {
  469. WNDCLASSEX wcex = { 0 };
  470. wcex.cbSize = sizeof(WNDCLASSEX);
  471. wcex.lpfnWndProc = DialogProc;
  472. wcex.hInstance = GetModuleHandle(0);
  473. wcex.hIcon = hIcon;
  474. wcex.hCursor = LoadCursor(0, IDC_ARROW);
  475. wcex.lpszClassName = _T("LOGWINDOW");
  476. return RegisterClassEx(&wcex);
  477. }
  478. //////////////////////////////////////////////////////////////////////////
  479. //
  480. //
  481. //
  482. LRESULT
  483. CALLBACK
  484. CLogWindow::DialogProc(
  485. HWND hWnd,
  486. UINT uMsg,
  487. WPARAM wParam,
  488. LPARAM lParam
  489. )
  490. {
  491. switch (uMsg)
  492. {
  493. case WM_CREATE:
  494. return ((CLogWindow *)((LPCREATESTRUCT)lParam)->lpCreateParams)->OnCreate(hWnd);
  495. case WM_CLOSE:
  496. return This(hWnd)->OnClose();
  497. case WM_SIZE:
  498. return This(hWnd)->OnSize((UINT)wParam, LOWORD(lParam), HIWORD(lParam));
  499. case WM_COMMAND:
  500. {
  501. CLogWindow *that = This(hWnd);
  502. HWND hEdit = ListView_GetEditControl(that->m_hList);
  503. if (hEdit != 0 && hEdit == (HWND) lParam)
  504. {
  505. that->PlaceEditControlOnEditedItem(hEdit);
  506. }
  507. else
  508. {
  509. switch (LOWORD(wParam))
  510. {
  511. case ID_COPY_MESSAGE:
  512. return that->OnCopyToClipboard();
  513. case ID_EDIT_COMMENT:
  514. return that->OnEditComment();
  515. case ID_PAUSE_OUTPUT:
  516. return that->OnPauseOutput();
  517. }
  518. }
  519. break;
  520. }
  521. case WM_USER + 1:
  522. {
  523. CLogWindow *that = This(hWnd);
  524. HWND hEdit = ListView_GetEditControl(that->m_hList);
  525. if (hEdit != 0)
  526. {
  527. that->PlaceEditControlOnEditedItem(hEdit);
  528. }
  529. break;
  530. }
  531. case WM_NOTIFY:
  532. switch (((LPNMHDR) lParam)->code)
  533. {
  534. case LVN_GETDISPINFO:
  535. return This(hWnd)->OnGetDispInfo((NMLVDISPINFO *) lParam);
  536. case LVN_COLUMNCLICK:
  537. return This(hWnd)->OnColumnClick((LPNMLISTVIEW) lParam);
  538. case LVN_BEGINLABELEDIT:
  539. return This(hWnd)->OnBeginLabelEdit((NMLVDISPINFO *) lParam);
  540. case LVN_ENDLABELEDIT:
  541. return This(hWnd)->OnEndLabelEdit((NMLVDISPINFO *) lParam);
  542. case LVN_KEYDOWN:
  543. return This(hWnd)->OnKeyDown((LPNMLVKEYDOWN) lParam);
  544. case NM_RCLICK:
  545. return This(hWnd)->OnRButtonDown();
  546. }
  547. break;
  548. }
  549. return DefWindowProc(hWnd, uMsg, wParam, lParam);
  550. }
  551. //////////////////////////////////////////////////////////////////////////
  552. //
  553. //
  554. //
  555. LRESULT CLogWindow::OnCreate(HWND hWnd)
  556. {
  557. m_hWnd = hWnd;
  558. SetWindowLongPtr(m_hWnd, GWLP_USERDATA, (LONG_PTR) this);
  559. m_hStatWnd = CreateStatusWindow(WS_CHILD | WS_VISIBLE, 0, m_hWnd, 0);
  560. SendMessage(m_hStatWnd, SB_SIMPLE, (WPARAM) TRUE, (LPARAM) 0);
  561. m_hList = CreateWindow(
  562. _T("SysListView32"),
  563. 0,
  564. WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_EDITLABELS,
  565. 0,
  566. 0,
  567. 0,
  568. 0,
  569. m_hWnd,
  570. 0,
  571. GetModuleHandle(0),
  572. 0
  573. );
  574. ListView_SetExtendedListViewStyle(
  575. m_hList,
  576. LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP | LVS_EX_LABELTIP
  577. );
  578. HIMAGELIST hImgList = ImageList_LoadBitmap(
  579. GetModuleHandle(0),
  580. MAKEINTRESOURCE(IDB_STATES),
  581. 16,
  582. 0,
  583. RGB(255, 255, 255)
  584. );
  585. if (hImgList)
  586. {
  587. ListView_SetImageList(m_hList, hImgList, LVSIL_SMALL);
  588. }
  589. ListView_InsertColumn2(m_hList, 0, _T("Type"), LVCFMT_LEFT, 60, ID_LOGLEVEL);
  590. ListView_InsertColumn2(m_hList, 1, _T("#"), LVCFMT_LEFT, 40, ID_MSGNUM);
  591. ListView_InsertColumn2(m_hList, 2, _T("Code"), LVCFMT_LEFT, 40, ID_CODE);
  592. ListView_InsertColumn2(m_hList, 3, _T("File"), LVCFMT_LEFT, 100, ID_FILENAME);
  593. ListView_InsertColumn2(m_hList, 4, _T("Message"), LVCFMT_LEFT, 420, ID_MESSAGE);
  594. ListView_InsertColumn2(m_hList, 5, _T("Comment"), LVCFMT_LEFT, 100, ID_COMMENT);
  595. SetForegroundWindow(m_hList);
  596. InterlockedExchange(&m_bIsClosed, FALSE);
  597. m_Closed.Reset();
  598. m_InitComplete.Set();
  599. return 0;
  600. }
  601. //////////////////////////////////////////////////////////////////////////
  602. //
  603. //
  604. //
  605. LRESULT CLogWindow::OnClose()
  606. {
  607. if (!m_bIsClosed)
  608. {
  609. InterlockedExchange(&m_bIsClosed, TRUE);
  610. m_Closed.Set();
  611. if (m_bIsPaused)
  612. {
  613. InterlockedExchange(&m_bIsPaused, FALSE);
  614. m_Resume.Set();
  615. }
  616. TCHAR szNewTitle[MAX_PATH];
  617. _tcscpy(szNewTitle, m_pTitle);
  618. _tcscat(szNewTitle, _T(" <closing>"));;
  619. SetWindowText(m_hWnd, szNewTitle);
  620. }
  621. return 0;
  622. }
  623. //////////////////////////////////////////////////////////////////////////
  624. //
  625. //
  626. //
  627. LRESULT CLogWindow::OnSize(UINT nType, int nW, int nH)
  628. {
  629. RECT r;
  630. GetWindowRect(m_hStatWnd, &r);
  631. int nStatWndH = r.bottom - r.top;
  632. MoveWindow(m_hList, 0, 0, nW, nH - nStatWndH, TRUE);
  633. TCHAR szBuffer[1024]; // ***bugbug: figure out why the statwnd forgets its caption
  634. SendMessage(m_hStatWnd, SB_GETTEXT, (WPARAM) SB_SIMPLEID, (LPARAM) szBuffer);
  635. MoveWindow(m_hStatWnd, 0, nH - nStatWndH, nW, nStatWndH, TRUE);
  636. SendMessage(m_hStatWnd, SB_SETTEXT, (WPARAM) SB_SIMPLEID, (LPARAM) szBuffer);
  637. return 0;
  638. }
  639. //////////////////////////////////////////////////////////////////////////
  640. //
  641. //
  642. //
  643. LRESULT CLogWindow::OnCopyToClipboard() const
  644. {
  645. HANDLE hMem = 0;
  646. for (
  647. DWORD dwSize = 4 * 1024;
  648. (hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, dwSize)) != 0 &&
  649. !CopySelectedItemsToBuffer(hMem, &dwSize);
  650. GlobalFree(hMem)
  651. )
  652. {
  653. // start with a 4KB buffer size
  654. // if buffer allocation fails, exit
  655. // if CopySelectedItemsToBuffer succeeds, exit
  656. // otherwise, delete the buffer and retry
  657. }
  658. if (hMem)
  659. {
  660. BOOL bResult = FALSE;
  661. if (OpenClipboard(m_hWnd))
  662. {
  663. if (EmptyClipboard())
  664. {
  665. if (SetClipboardData(CF_TEXT, hMem))
  666. {
  667. bResult = TRUE;
  668. }
  669. }
  670. CloseClipboard();
  671. }
  672. if (!bResult)
  673. {
  674. GlobalFree(hMem);
  675. }
  676. }
  677. return 0;
  678. }
  679. //////////////////////////////////////////////////////////////////////////
  680. //
  681. //
  682. //
  683. BOOL
  684. CLogWindow::CopySelectedItemsToBuffer(
  685. HANDLE hMem,
  686. PDWORD pdwBufferSize
  687. ) const
  688. {
  689. PSTR pMem = (PSTR) GlobalLock(hMem);
  690. CBufferFill Buffer(pMem, *pdwBufferSize);
  691. if (pMem)
  692. {
  693. int nItem = -1;
  694. while ((nItem = ListView_GetNextItem(m_hList, nItem, LVNI_SELECTED)) != -1)
  695. {
  696. USES_CONVERSION;
  697. CListData *pData = GetItemData(nItem);
  698. if (pData->m_strFileName && *pData->m_strFileName)
  699. {
  700. Buffer.AddTop(CStrBlob(T2A(pData->m_strFileName)));
  701. Buffer.AddTop(CStrBlob(": "));
  702. }
  703. Buffer.AddTop(CStrBlob(T2A(pData->m_strMessage)));
  704. if (pData->m_strComment && *pData->m_strComment)
  705. {
  706. Buffer.AddTop(CStrBlob(" ("));
  707. Buffer.AddTop(CStrBlob(T2A(pData->m_strComment)));
  708. Buffer.AddTop(CStrBlob(")"));
  709. }
  710. Buffer.AddTop(CStrBlob("\r\n"));
  711. }
  712. Buffer.AddTop(CSzBlob("")); // terminating NULL
  713. GlobalUnlock(hMem);
  714. }
  715. *pdwBufferSize -= (DWORD) Buffer.BytesLeft();
  716. return Buffer.BytesLeft() >= 0;
  717. }
  718. //////////////////////////////////////////////////////////////////////////
  719. //
  720. //
  721. //
  722. LRESULT CLogWindow::OnEditComment() const
  723. {
  724. if (m_nEditedItem != -1)
  725. {
  726. ListView_EditLabel(m_hList, m_nEditedItem);
  727. }
  728. return 0;
  729. }
  730. //////////////////////////////////////////////////////////////////////////
  731. //
  732. //
  733. //
  734. LRESULT CLogWindow::OnPauseOutput()
  735. {
  736. if (m_bIsPaused)
  737. {
  738. InterlockedExchange(&m_bIsPaused, FALSE);
  739. m_Resume.Set();
  740. SetWindowText(m_hWnd, m_pTitle);
  741. }
  742. else
  743. {
  744. InterlockedExchange(&m_bIsPaused, TRUE);
  745. m_Resume.Reset();
  746. TCHAR szNewTitle[MAX_PATH];
  747. _tcscpy(szNewTitle, m_pTitle);
  748. _tcscat(szNewTitle, _T(" <paused>"));;
  749. SetWindowText(m_hWnd, szNewTitle);
  750. }
  751. return 0;
  752. }
  753. //////////////////////////////////////////////////////////////////////////
  754. //
  755. //
  756. //
  757. void CLogWindow::PlaceEditControlOnEditedItem(HWND hEdit) const
  758. {
  759. RECT r;
  760. ListView_GetSubItemRect(
  761. m_hList,
  762. m_nEditedItem,
  763. ID_COMMENT,
  764. LVIR_LABEL,
  765. &r
  766. );
  767. MoveWindow(
  768. hEdit,
  769. r.left,
  770. r.top,
  771. r.right - r.left,
  772. r.bottom - r.top,
  773. TRUE
  774. );
  775. }
  776. //////////////////////////////////////////////////////////////////////////
  777. //
  778. //
  779. //
  780. LRESULT CLogWindow::OnGetDispInfo(NMLVDISPINFO *pnmv)
  781. {
  782. ASSERT(pnmv->item.mask == LVIF_TEXT);
  783. CListData *pData = (CListData *) pnmv->item.lParam;
  784. if (pData)
  785. {
  786. switch (pnmv->item.iSubItem)
  787. {
  788. case ID_LOGLEVEL: pnmv->item.pszText = pData->m_strLogLevel; break;
  789. case ID_MSGNUM: pnmv->item.pszText = pData->m_strMsgNum; break;
  790. case ID_CODE: pnmv->item.pszText = pData->m_strCode; break;
  791. case ID_FILENAME: pnmv->item.pszText = pData->m_strFileName; break;
  792. case ID_MESSAGE: pnmv->item.pszText = pData->m_strMessage; break;
  793. case ID_COMMENT: pnmv->item.pszText = pData->m_strComment; break;
  794. default: ASSERT(FALSE);
  795. }
  796. ASSERT(pnmv->item.pszText);
  797. }
  798. return 0;
  799. }
  800. //////////////////////////////////////////////////////////////////////////
  801. //
  802. //
  803. //
  804. LRESULT CLogWindow::OnColumnClick(LPNMLISTVIEW pnmv)
  805. {
  806. // convert zero based column number to one based, as
  807. // we will use negative numbers to indicate sort direction
  808. int nColumn = pnmv->iSubItem + 1;
  809. if (Abs(m_SortByHistory[0]) == nColumn)
  810. {
  811. // if the user has pressed a column header twice,
  812. // reverse the sort direction
  813. m_SortByHistory[0] *= -1;
  814. }
  815. else
  816. {
  817. // if the user has selected a new column, slide down
  818. // the history list and enter the new column to the top
  819. // position. Also keep the last entry, that will eventually
  820. // end the recursive CompareFunc in the worst case
  821. for (int i = COUNTOF(m_SortByHistory) - 2; i >= 1; --i)
  822. {
  823. m_SortByHistory[i] = m_SortByHistory[i-1];
  824. }
  825. m_SortByHistory[0] = nColumn;
  826. }
  827. // sort the items
  828. ListView_SortItems(m_hList, CompareFunc, m_SortByHistory);
  829. // ensure that the selected item is visible
  830. ListView_EnsureVisible(
  831. m_hList,
  832. ListView_GetNextItem(m_hList, -1, LVNI_SELECTED),
  833. TRUE
  834. );
  835. return 0;
  836. }
  837. //////////////////////////////////////////////////////////////////////////
  838. //
  839. //
  840. //
  841. int
  842. CALLBACK
  843. CLogWindow::CompareFunc(
  844. LPARAM lParam1,
  845. LPARAM lParam2,
  846. LPARAM lParamSort
  847. )
  848. {
  849. CListData *pData1 = (CListData *) lParam1;
  850. CListData *pData2 = (CListData *) lParam2;
  851. int *SortByHistory = (int *) lParamSort;
  852. ASSERT(pData1);
  853. ASSERT(pData2);
  854. ASSERT(SortByHistory);
  855. int nSortDir = 1;
  856. int nColumn = SortByHistory[0];
  857. if (nColumn < 0)
  858. {
  859. nSortDir *= -1;
  860. nColumn *= -1;
  861. }
  862. int nResult = 0;
  863. switch (nColumn)
  864. {
  865. case 0:
  866. nResult = -1;
  867. break;
  868. case ID_MSGNUM + 1:
  869. nResult = Cmp(_ttoi(pData1->m_strMsgNum), _ttoi(pData2->m_strMsgNum));
  870. break;
  871. case ID_LOGLEVEL + 1:
  872. nResult = _tcscmp(pData1->m_strLogLevel, pData2->m_strLogLevel);
  873. break;
  874. case ID_FILENAME + 1:
  875. nResult = _tcscmp(pData1->m_strFileName, pData2->m_strFileName);
  876. break;
  877. case ID_CODE + 1:
  878. nResult = Cmp(_ttoi(pData1->m_strCode), _ttoi(pData2->m_strCode));
  879. break;
  880. case ID_MESSAGE + 1:
  881. nResult = _tcscmp(pData1->m_strMessage, pData2->m_strMessage);
  882. break;
  883. case ID_COMMENT + 1:
  884. nResult = _tcscmp(pData1->m_strComment, pData2->m_strComment);
  885. break;
  886. default:
  887. ASSERT(FALSE);
  888. }
  889. // in case of equality, go on with the comparison using the next
  890. // column in the history list
  891. return nResult != 0 ?
  892. nSortDir * nResult :
  893. CompareFunc(lParam1, lParam2, (LPARAM) (SortByHistory + 1));
  894. }
  895. //////////////////////////////////////////////////////////////////////////
  896. //
  897. //
  898. //
  899. LRESULT CLogWindow::OnBeginLabelEdit(NMLVDISPINFO *pdi)
  900. {
  901. HWND hEdit = ListView_GetEditControl(m_hList);
  902. if (hEdit)
  903. {
  904. CListData *pData = GetItemData(pdi->item.iItem);
  905. SetWindowText(hEdit, pData->m_strComment);
  906. m_nEditedItem = pdi->item.iItem;
  907. PostMessage(m_hWnd, WM_USER + 1, 0, 0);
  908. }
  909. return 0;
  910. }
  911. //////////////////////////////////////////////////////////////////////////
  912. //
  913. //
  914. //
  915. LRESULT CLogWindow::OnEndLabelEdit(NMLVDISPINFO *pdi)
  916. {
  917. if (pdi->item.iItem != -1 && pdi->item.pszText != 0)
  918. {
  919. CListData *pData = GetItemData(pdi->item.iItem);
  920. if (_tcscmp(pData->m_strComment, pdi->item.pszText) != 0)
  921. {
  922. // save the comment
  923. pData->m_strComment = pdi->item.pszText;
  924. // replace the tabs (if any) with space, we use tabs as
  925. // the column delimiter character
  926. for (
  927. PTSTR pszTab = _tcschr(pData->m_strComment, _T('\t'));
  928. pszTab;
  929. pszTab = _tcschr(pszTab + 1, _T('\t'))
  930. )
  931. {
  932. *pszTab = _T(' ');
  933. }
  934. ListView_SetItemText(m_hList, pdi->item.iItem, ID_COMMENT, pData->m_strComment);
  935. // save this new entry
  936. m_SavedData.erase(*pData);
  937. m_SavedData.insert(*pData);
  938. m_bIsDirty = TRUE;
  939. }
  940. }
  941. return 0;
  942. }
  943. //////////////////////////////////////////////////////////////////////////
  944. //
  945. //
  946. //
  947. LRESULT CLogWindow::OnKeyDown(LPNMLVKEYDOWN pnkd)
  948. {
  949. if (pnkd->wVKey == VK_INSERT && GetKeyState(VK_CONTROL) < 0) // CTRL+INS
  950. {
  951. return OnCopyToClipboard();
  952. }
  953. else if (pnkd->wVKey == VK_RETURN)
  954. {
  955. m_nEditedItem = ListView_GetSelectionMark(m_hList);
  956. return OnEditComment();
  957. }
  958. else if (pnkd->wVKey == VK_PAUSE)
  959. {
  960. return OnPauseOutput();
  961. }
  962. return 0;
  963. }
  964. //////////////////////////////////////////////////////////////////////////
  965. //
  966. //
  967. //
  968. LRESULT CLogWindow::OnRButtonDown()
  969. {
  970. POINT p;
  971. GetCursorPos(&p);
  972. LVHITTESTINFO lvHitTestInfo;
  973. lvHitTestInfo.pt = p;
  974. ScreenToClient(m_hList, &lvHitTestInfo.pt);
  975. ListView_HitTest(m_hList, &lvHitTestInfo);
  976. m_nEditedItem = lvHitTestInfo.iItem;
  977. HMENU hMenu = CreatePopupMenu();
  978. if (hMenu)
  979. {
  980. AppendMenu(hMenu, MF_STRING, ID_EDIT_COMMENT, _T("&Edit\tEnter"));
  981. AppendMenu(hMenu, MF_STRING, ID_COPY_MESSAGE, _T("&Copy\tCtrl+Ins"));
  982. AppendMenu(hMenu, MF_STRING, ID_PAUSE_OUTPUT, m_bIsPaused ? _T("&Resume") : _T("&Pause"));
  983. TrackPopupMenu(
  984. hMenu,
  985. TPM_LEFTALIGN | TPM_TOPALIGN,
  986. p.x,
  987. p.y,
  988. 0,
  989. m_hWnd,
  990. 0
  991. );
  992. DestroyMenu(hMenu);
  993. }
  994. return 0;
  995. }
  996. //////////////////////////////////////////////////////////////////////////
  997. //
  998. //
  999. //
  1000. CLogWindow::CListData::CListData(
  1001. PCTSTR pszMsgNum,
  1002. PCTSTR pszLogLevel,
  1003. PCTSTR pszFileName,
  1004. PCTSTR pszCode,
  1005. PCTSTR pszMessage,
  1006. const CSavedData &rSavedData
  1007. ) :
  1008. m_strMsgNum(pszMsgNum),
  1009. m_strLogLevel(pszLogLevel),
  1010. m_strFileName(pszFileName),
  1011. m_strCode(pszCode),
  1012. m_strMessage(pszMessage)
  1013. {
  1014. CSavedData::const_iterator itData = rSavedData.find(*this);
  1015. m_strComment = itData != rSavedData.end() ? itData->m_strComment : _T("");
  1016. }
  1017. //////////////////////////////////////////////////////////////////////////
  1018. //
  1019. //
  1020. //
  1021. CLogWindow::CListData::CListData(
  1022. PTSTR pszLine
  1023. ) :
  1024. m_strLogLevel(GetArg(&pszLine)),
  1025. m_strFileName(GetArg(&pszLine)),
  1026. m_strCode(GetArg(&pszLine)),
  1027. m_strMessage(GetArg(&pszLine)),
  1028. m_strComment(GetArg(&pszLine))
  1029. {
  1030. }
  1031. //////////////////////////////////////////////////////////////////////////
  1032. //
  1033. //
  1034. //
  1035. bool CLogWindow::CListData::operator ==(const CListData &rhs) const
  1036. {
  1037. return
  1038. _tcscmp(m_strMessage, rhs.m_strMessage) == 0 &&
  1039. _tcscmp(m_strCode, rhs.m_strCode) == 0 &&
  1040. _tcscmp(m_strFileName, rhs.m_strFileName) == 0 &&
  1041. _tcscmp(m_strLogLevel, rhs.m_strLogLevel) == 0;
  1042. }
  1043. //////////////////////////////////////////////////////////////////////////
  1044. //
  1045. //
  1046. //
  1047. bool CLogWindow::CListData::operator <(const CListData &rhs) const
  1048. {
  1049. int nCmpMessage, nCmpCode, nCmpFileName, nCmpLogLevel;
  1050. return
  1051. ((nCmpMessage = _tcscmp(m_strMessage, rhs.m_strMessage)) < 0 ||
  1052. (nCmpMessage == 0 &&
  1053. ((nCmpCode = _tcscmp(m_strCode, rhs.m_strCode)) < 0 ||
  1054. (nCmpCode == 0 &&
  1055. ((nCmpFileName = _tcscmp(m_strFileName, rhs.m_strFileName)) < 0 ||
  1056. (nCmpFileName == 0 &&
  1057. ((nCmpLogLevel = _tcscmp(m_strLogLevel, rhs.m_strLogLevel)) < 0)
  1058. )
  1059. )
  1060. )
  1061. )
  1062. )
  1063. );
  1064. }
  1065. //////////////////////////////////////////////////////////////////////////
  1066. //
  1067. //
  1068. //
  1069. void CLogWindow::CListData::Write(FILE *fOut) const
  1070. {
  1071. _ftprintf(
  1072. fOut,
  1073. _T("%s\t%s\t%s\t%s\t%s\r\n"),
  1074. m_strLogLevel,
  1075. m_strFileName,
  1076. m_strCode,
  1077. m_strMessage,
  1078. m_strComment
  1079. );
  1080. }
  1081. //////////////////////////////////////////////////////////////////////////
  1082. //
  1083. //
  1084. //
  1085. PCTSTR CLogWindow::CListData::GetArg(PTSTR *pszStr)
  1086. {
  1087. PTSTR pszStart = *pszStr;
  1088. PTSTR pszEnd = pszStart;
  1089. while (
  1090. *pszEnd != '\t' &&
  1091. *pszEnd != '\n' &&
  1092. *pszEnd != '\r' &&
  1093. *pszEnd != '\0'
  1094. )
  1095. {
  1096. pszEnd = CharNext(pszEnd);
  1097. }
  1098. *pszStr = CharNext(pszEnd);
  1099. while (
  1100. **pszStr == '\n' ||
  1101. **pszStr == '\r'
  1102. )
  1103. {
  1104. *pszStr = CharNext(*pszStr);
  1105. }
  1106. *pszEnd = '\0';
  1107. return pszStart;
  1108. }
  1109. //////////////////////////////////////////////////////////////////////////
  1110. //
  1111. //
  1112. //
  1113. void CLogWindow::CSavedData::Read(FILE *fIn)
  1114. {
  1115. TCHAR szLine[4096];
  1116. while (_fgetts(szLine, COUNTOF(szLine), fIn))
  1117. {
  1118. if (szLine[0] == _T('#'))
  1119. {
  1120. *FindEol(szLine) = '\0';
  1121. m_strUserName = szLine + 2;
  1122. }
  1123. else
  1124. {
  1125. insert(CListData(szLine));
  1126. }
  1127. }
  1128. }
  1129. //////////////////////////////////////////////////////////////////////////
  1130. //
  1131. //
  1132. //
  1133. void CLogWindow::CSavedData::Write(FILE *fOut, PCTSTR pComments /*= 0*/) const
  1134. {
  1135. CUserName UserName;
  1136. CComputerName ComputerName;
  1137. _ftprintf(
  1138. fOut,
  1139. pComments && *pComments ? _T("# %s@%s %s\n") : _T("# %s@%s\n"),
  1140. (PCTSTR) UserName,
  1141. (PCTSTR) ComputerName,
  1142. pComments
  1143. );
  1144. for (
  1145. const_iterator itData = begin();
  1146. itData != end();
  1147. ++itData
  1148. )
  1149. {
  1150. itData->Write(fOut);
  1151. }
  1152. }
  1153. //////////////////////////////////////////////////////////////////////////
  1154. //
  1155. //
  1156. //
  1157. void
  1158. CLogWindow::CSavedData::Merge(
  1159. const CSavedData &rNewData,
  1160. CSavedData &rCollisions
  1161. )
  1162. {
  1163. rCollisions.clear();
  1164. for (
  1165. const_iterator itData = rNewData.begin();
  1166. itData != rNewData.end();
  1167. ++itData
  1168. )
  1169. {
  1170. const_iterator itMatch = find(*itData);
  1171. if (
  1172. itMatch != end() &&
  1173. _tcscmp(itMatch->m_strComment, itData->m_strComment) != 0
  1174. )
  1175. {
  1176. rCollisions.insert(*itMatch);
  1177. erase(itMatch);
  1178. }
  1179. insert(*itData);
  1180. }
  1181. }
  1182. //////////////////////////////////////////////////////////////////////////
  1183. //
  1184. //
  1185. //
  1186. BOOL CLogWindow::ReadSavedData(PCTSTR pFileName)
  1187. {
  1188. BOOL bResult = FALSE;
  1189. try
  1190. {
  1191. m_SavedData.Read(CCFile(pFileName, _T("rt")));
  1192. bResult = TRUE;
  1193. }
  1194. catch (const CError &)
  1195. {
  1196. MessageBox(
  1197. 0,
  1198. _T("Cannot download the comments from the central database. ")
  1199. _T("Previous comments will not be available on the results window."),
  1200. 0,
  1201. MB_ICONERROR | MB_OK
  1202. );
  1203. }
  1204. return bResult;
  1205. }
  1206. //////////////////////////////////////////////////////////////////////////
  1207. //
  1208. //
  1209. //
  1210. BOOL CLogWindow::WriteSavedData(PCTSTR pFileName, PCTSTR pComments /*= 0*/)
  1211. {
  1212. BOOL bResult = FALSE;
  1213. HANDLE hFile = CreateFile(
  1214. pFileName,
  1215. GENERIC_WRITE,
  1216. FILE_SHARE_READ,
  1217. 0,
  1218. OPEN_EXISTING,
  1219. FILE_ATTRIBUTE_NORMAL,
  1220. 0
  1221. );
  1222. if (hFile != INVALID_HANDLE_VALUE)
  1223. {
  1224. FILE *fFile = OpenOSHandle(hFile, _O_TEXT, _T("wt"));
  1225. m_SavedData.Write(fFile, pComments);
  1226. bResult = fclose(fFile) == 0;
  1227. }
  1228. return bResult;
  1229. /*BOOL bResult = FALSE;
  1230. try
  1231. {
  1232. m_SavedData.Write(CCFile(pFileName, _T("wt")), PCTSTR pComments);
  1233. bResult = TRUE;
  1234. }
  1235. catch (const CError &)
  1236. {
  1237. //
  1238. }
  1239. return bResult;*/
  1240. }
  1241. //////////////////////////////////////////////////////////////////////////
  1242. //
  1243. //
  1244. //
  1245. BOOL CLogWindow::WriteModifiedData(PCTSTR pFileName, PCTSTR pComments /*= 0*/)
  1246. {
  1247. BOOL bResult = TRUE;
  1248. if (
  1249. IsDirty() &&
  1250. MessageBox(
  1251. 0,
  1252. _T("You have changed some of the comments on the results window. ")
  1253. _T("Do you want to save these changes to the central database?"),
  1254. m_pTitle,
  1255. MB_ICONQUESTION | MB_YESNO
  1256. ) == IDYES
  1257. )
  1258. {
  1259. bResult = FALSE;
  1260. do
  1261. {
  1262. try
  1263. {
  1264. m_SavedData.Write(CCFile(pFileName, _T("wt")), pComments);
  1265. MessageBox(
  1266. 0,
  1267. _T("Comments database updated successfully."),
  1268. m_pTitle,
  1269. MB_ICONINFORMATION | MB_OK
  1270. );
  1271. bResult = TRUE;
  1272. }
  1273. catch (const CError &)
  1274. {
  1275. // bResult remains FALSE;
  1276. }
  1277. }
  1278. while (
  1279. !bResult &&
  1280. MessageBox(
  1281. 0,
  1282. _T("Cannot update the results. Do you want to try again?"),
  1283. 0,
  1284. MB_ICONQUESTION | MB_YESNO
  1285. ) == IDYES
  1286. );
  1287. }
  1288. return bResult;
  1289. }
  1290. #endif //IMPLEMENT_LOGWINDOW
  1291. #endif //PROGRESSDLG_H