main.cpp
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 51k
Category:

Windows Kernel

Development Platform:

Visual C++

  1. #include "precomp.hxx"
  2. #pragma hdrstop
  3. #include "gdiobj.h"
  4. #include "resource.h"
  5. #include "folder.h"
  6. //
  7. // Global constants -----------------------------------------------------------
  8. //
  9. //
  10. // Width of the image graphic's shadow in dialog units.
  11. //
  12. const INT IMAGE_SHADOW_WIDTH = 2;
  13. //
  14. // Vertical space between topic menu items in dialog units.
  15. //
  16. const INT INTER_TOPIC_GAP = 4;
  17. //
  18. // Banner title offset from left edge of banner.
  19. //
  20. const INT BANNER_TITLE_INDENT = 14;
  21. //
  22. // Banner title font size in points.
  23. //
  24. const INT BANNER_FONT_SIZE = 22;
  25. //
  26. // Face name of banner title font.
  27. //
  28. const TCHAR c_szBannerFontFace[] = TEXT("Times New Roman");
  29. //
  30. // Default mouse hover time (400 ms) is too slow.
  31. // Speed it up.
  32. //
  33. const DWORD MOUSE_HOVER_TIME = 100;
  34. //
  35. // Name of the system-wide named mutex for this app.
  36. //
  37. const TCHAR c_szAppMutex[] = TEXT("Settings$Dll$Mutex");
  38. //
  39. // Enumeration for "SPOTSTATE" argument to function DrawTopicSpot().
  40. //
  41. const enum SPOTSTATE { SPOT_NORMAL = 0, SPOT_FOCUS };
  42. //
  43. // Enumeration for eAppearance argument to function GetDialogFont().
  44. //
  45. const enum GDF_APPEARANCE { GDF_NORMAL = 0, GDF_HIGHLIGHT, GDF_TITLE };
  46. //
  47. // Topic spot bitmap transparency color.
  48. //
  49. const COLORREF COLOR_TOPICSPOT_MASK = RGB(0, 128, 128);
  50. //
  51. // Module function declarations -----------------------------------------------
  52. //
  53. INT_PTR CALLBACK DlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  54. LRESULT OnInitDialog(HWND hwnd, WPARAM wParam, LPARAM lParam);
  55. LRESULT OnNotify(HWND hwnd, WPARAM wParam, LPARAM lParam);
  56. LRESULT OnCommand(HWND hwnd, WPARAM wParam, LPARAM lParam);
  57. LRESULT OnDestroy(HWND hwnd);
  58. LRESULT OnClose(HWND hwnd);
  59. LRESULT OnDrawItem(HWND hwnd, UINT idCtl, LPDRAWITEMSTRUCT pdis);
  60. LRESULT OnMeasureItem(HWND hwnd, UINT idCtl, LPMEASUREITEMSTRUCT pmis);
  61. LRESULT OnPaletteChanged(HWND hwnd, WPARAM wParam, LPARAM lParam);
  62. LRESULT OnQueryNewPalette(HWND hwnd, WPARAM wParam, LPARAM lParam);
  63. LRESULT OnSetCursor(HWND hwnd, WPARAM wParam, LPARAM lParam);
  64. LRESULT OnSystemSettingChange(HWND hwnd, WPARAM wParam, LPARAM lParam);
  65. LRESULT OnDisplayChange(VOID);
  66. INT InitializeAndRunApplication(HWND hwndParent);
  67. BOOL InitializeApplication(VOID);
  68. BOOL ActivateExistingInstance(LPHANDLE phMutex);
  69. BOOL InitializeListView(HWND hwndListview);
  70. INT  RunApplication(HWND hwndParent);
  71. BOOL LoadTopicsIntoList(HWND hwndListview);
  72. LONG CalcListviewItemHeight(HWND hwndDlg);
  73. VOID SetListviewClickMode(VOID);
  74. VOID DrawListviewItem(HWND hwnd,UINT idCtl,LPDRAWITEMSTRUCT pdis);
  75. VOID DrawTopicSpot(HDC hdc, LPRECT prc, SPOTSTATE spot);
  76. VOID DrawTopicImage(HWND hwnd, HDC hdc, PSETTINGS_FOLDER_TOPIC pITopic);
  77. VOID DrawBanner(HWND hwnd, HDC hdc);
  78. VOID DrawBannerTitle(HWND hwnd, HDC hdc);
  79. BOOL NotifyTopicsOfPaletteChange(HWND hwnd);
  80. VOID GetDialogFont(CFont& font, HWND hwndDlg, GDF_APPEARANCE eAppearance);
  81. VOID FocusOnListview(VOID);
  82. INT ListViewItemHit(INT xPos, INT yPos);
  83. INT ListViewItemHit(VOID);
  84. BOOL CreatePerThreadStorage(VOID);
  85. VOID DestroyPerThreadStorage(VOID);
  86. HRESULT OnProcessAttach(HINSTANCE hInstDll);
  87. HRESULT OnProcessDetach(VOID);
  88. VOID WINAPI SETTINGS_RUNDLLA(HWND hwnd, HINSTANCE hInstance, LPSTR pszCmdLineA, INT nCmdShow);
  89. VOID WINAPI SETTINGS_RUNDLLW(HWND hwnd, HINSTANCE hInstance, LPWSTR pszCmdLineW, INT nCmdShow);
  90. VOID WINAPI settings_rundllA(HWND hwnd, HINSTANCE hInstance, LPSTR pszCmdLineA,  INT nCmdShow);
  91. VOID WINAPI settings_rundllW(HWND hwnd, HINSTANCE hInstance, LPWSTR pszCmdLineW, INT nCmdShow);
  92. VOID WINAPI OpenSettingsUI(PTRAYPROPSHEETCALLBACK pfnCallback);
  93. //
  94. // Inline functions -----------------------------------------------------------
  95. //
  96. //
  97. //
  98. // Simple MIN/MAX template functions.
  99. //
  100. template <class T>
  101. inline MAX(T a, T b)
  102.     { return a > b ? a : b; }
  103. template <class T>
  104. inline MIN(T a, T b)
  105.     { return a < b ? a : b; }
  106. //
  107. // Dialog Unit <--> Pixel conversion  (X and Y units).
  108. //
  109. inline INT PIXELSX(INT DlgUnits)
  110.     { return (PTG.DialogBaseUnitsX * DlgUnits) / 4; }
  111. inline INT PIXELSY(INT DlgUnits)
  112.     { return (PTG.DialogBaseUnitsY * DlgUnits) / 8; }
  113. inline INT DLGUNITSX(INT pixels)
  114.     { return (pixels * 4) / PTG.DialogBaseUnitsX; }
  115. inline INT DLGUNITSY(INT pixels)
  116.     { return (pixels * 8) / PTG.DialogBaseUnitsY; }
  117. //
  118. // Left margin of topic list makes room for topic spot bitmaps.
  119. // Right margin makes room for a vertical scroll bar if localization
  120. // extends any of the topic titles beyond a single line.
  121. //
  122. inline INT TOPIC_LIST_LMARGIN(VOID)
  123.     { return PTG.cxSmallIcon * 3 / 2; }
  124. inline INT TOPIC_LIST_RMARGIN(VOID)
  125.     { return PTG.cxVertScrollBar; }
  126. //
  127. // Convert a font point size to a value suitable for use in
  128. // the LOGFONT structure member lfHeight.
  129. //
  130. inline INT FONTPTS_TO_LFHEIGHT(HDC hdc, INT pts)
  131.     { return -MulDiv(pts, GetDeviceCaps(hdc, LOGPIXELSY), 72); }
  132. BOOL WINAPI
  133. DllMain(
  134.     HINSTANCE hInstDll,
  135.     DWORD fdwReason,
  136.     LPVOID lpvReserved
  137.     )
  138. {
  139.     BOOL bResult = TRUE;
  140.     switch(fdwReason)
  141.     {
  142.         case DLL_PROCESS_ATTACH:
  143.             bResult = SUCCEEDED(OnProcessAttach(hInstDll));
  144.             break;
  145.         case DLL_PROCESS_DETACH:
  146.             bResult = SUCCEEDED(OnProcessDetach());
  147.             break;
  148.     }
  149.     return bResult;
  150. }
  151. HRESULT
  152. OnProcessAttach(
  153.     HINSTANCE hInstDll
  154.     )
  155. {
  156.     HRESULT hResult = NO_ERROR;
  157. #ifdef DEBUG
  158.     //
  159.     // Default is DM_NONE.
  160.     //
  161.     SetDebugMask(DM_ASSERT | DM_ERROR);
  162. #endif
  163.     g_hInstance = hInstDll;
  164.     DisableThreadLibraryCalls(g_hInstance);
  165.     //
  166.     // Allocate storage for per-thread global data.
  167.     //
  168.     g_dwTlsIndex = TlsAlloc();
  169.     if (TLS_OUT_OF_INDEXES == g_dwTlsIndex)
  170.     {
  171.         hResult = E_FAIL;
  172.     }
  173.     return hResult;
  174. }
  175. HRESULT
  176. OnProcessDetach(
  177.     VOID
  178.     )
  179. {
  180.     if (TLS_OUT_OF_INDEXES != g_dwTlsIndex)
  181.     {
  182.         TlsFree(g_dwTlsIndex);
  183.         g_dwTlsIndex = TLS_OUT_OF_INDEXES;
  184.     }
  185.     #ifdef DEBUG_ALLOC
  186.         //
  187.         // If the DM_ALLOCSUM flag is set, dump out the final statistics for the
  188.         // allocator.  If any blocks are still allocated, this will list them.
  189.         //
  190.         if (DM_ALLOCSUM & GetDebugMask())
  191.         {
  192.             DebugMsg(DM_ALLOCSUM, TEXT("-----------------------------------------"));
  193.             DebugMsg(DM_ALLOCSUM, TEXT("ALLOC - Heap Allocation Summary."));
  194.             DebugMsg(DM_ALLOCSUM, TEXT("-----------------------------------------"));
  195.             g_FreeStore.DumpStatistics();
  196.         }
  197. #endif
  198.     return NO_ERROR;
  199. }
  200. //
  201. // BUGBUG:  I should probably eliminate the Tls and just create a 
  202. //          "MainWindow" class.
  203. //
  204. BOOL
  205. CreatePerThreadStorage(
  206.     VOID
  207.     )
  208. {
  209.     BOOL bResult = FALSE;
  210.     if (TLS_OUT_OF_INDEXES != g_dwTlsIndex)
  211.     {
  212.         if (NULL == TlsGetValue(g_dwTlsIndex))
  213.         {
  214.             try
  215.             {
  216.                 PerThreadGlobals *pPTG = new PerThreadGlobals;
  217.                 if (TlsSetValue(g_dwTlsIndex, (LPVOID)pPTG))
  218.                 {
  219.                     bResult = TRUE;
  220.                 }
  221.                 else
  222.                 {
  223.                     //
  224.                     // Created data block but failed to store in Tls.
  225.                     // Don't know why this might happen but just in case...
  226.                     //
  227.                     delete pPTG;
  228.                 }
  229.             }
  230.             catch(...)
  231.             {
  232.                 //
  233.                 // Catch any exception.  bResult remains FALSE so
  234.                 // we return "failure".
  235.                 //
  236.             }
  237.         }
  238.         else
  239.         {
  240.             //
  241.             // A process' primary thread allocates per-thread storage
  242.             // in OnProcessAttach.  So... It's already been created.
  243.             //
  244.             bResult = TRUE;
  245.         }
  246.     }
  247.     return bResult;
  248. }
  249. VOID
  250. DestroyPerThreadStorage(
  251.     VOID
  252.     )
  253. {
  254.     if (TLS_OUT_OF_INDEXES != g_dwTlsIndex)
  255.     {
  256.         //
  257.         // Free up the per-thread-data.
  258.         //
  259.         PerThreadGlobals *pPTG = (PerThreadGlobals *)TlsGetValue(g_dwTlsIndex);
  260.         delete pPTG;
  261.         TlsSetValue(g_dwTlsIndex, NULL);
  262.     }
  263. }
  264. VOID WINAPI
  265. SETTINGS_RUNDLLA(
  266.     HWND hwnd,
  267.     HINSTANCE hInstance,
  268.     LPSTR pszCmdLineA,
  269.     INT nCmdShow
  270.     )
  271. {
  272.     if (NULL != pszCmdLineA)
  273.     {
  274.         LPWSTR pszCmdLineW = NULL;
  275.         INT cchCmdLine = MultiByteToWideChar(CP_ACP,
  276.                                              0,
  277.                                              pszCmdLineA,
  278.                                              -1,
  279.                                              NULL,
  280.                                              0);
  281.         pszCmdLineW = new WCHAR[cchCmdLine];
  282.         if (NULL != pszCmdLineW)
  283.         {
  284.             MultiByteToWideChar(CP_ACP,
  285.                                 0,
  286.                                 pszCmdLineA,
  287.                                 -1,
  288.                                 pszCmdLineW,
  289.                                 cchCmdLine);
  290.             SETTINGS_RUNDLLW(hwnd, hInstance, pszCmdLineW, nCmdShow);
  291.             delete[] pszCmdLineW;
  292.         }
  293.         else
  294.         {
  295.             //
  296.             // Insufficient memory to convert ANSI arg string to UNICODE.
  297.             // No use continuing.
  298.             //
  299.             SettingsMsgBox(
  300.                 hwnd,
  301.                 MSG_OUTOFMEMORY,
  302.                 MSG_MAINWINDOW_TITLE,
  303.                 MB_ICONSTOP | MB_OK);
  304.         }
  305.     }
  306. }
  307. VOID WINAPI
  308. SETTINGS_RUNDLLW(
  309.     HWND hwnd,
  310.     HINSTANCE hInstance,
  311.     LPWSTR pszCmdLineW,
  312.     INT nCmdShow
  313.     )
  314. {
  315.     CreatePerThreadStorage();
  316.     InitializeAndRunApplication(hwnd);
  317.     DestroyPerThreadStorage();
  318. }
  319. //
  320. // Lower case versions of entry rundll32.exe entry points.
  321. //
  322. VOID WINAPI
  323. settings_rundllA(
  324.     HWND hwnd,
  325.     HINSTANCE hInstance,
  326.     LPSTR pszCmdLineA,
  327.     INT nCmdShow
  328.     )
  329. {
  330.     SETTINGS_RUNDLLA(hwnd, hInstance, pszCmdLineA, nCmdShow);
  331. }
  332. VOID WINAPI
  333. settings_rundllW(
  334.     HWND hwnd,
  335.     HINSTANCE hInstance,
  336.     LPWSTR pszCmdLineW,
  337.     INT nCmdShow
  338.     )
  339. {
  340.     SETTINGS_RUNDLLW(hwnd, hInstance, pszCmdLineW, nCmdShow);
  341. }
  342. //
  343. // Exported entry point for clients like explorer.
  344. // BUGBUG: This API should include a parent window handle.
  345. //         Only use the desktop window as a default.
  346. //
  347. VOID WINAPI
  348. OpenSettingsUI(
  349.     PTRAYPROPSHEETCALLBACK pfnCallback
  350.     )
  351. {
  352.     CreatePerThreadStorage();
  353.     PTG.pfnTaskbarPropSheetCallback = pfnCallback;
  354.     InitializeAndRunApplication(GetDesktopWindow());
  355.     DestroyPerThreadStorage();
  356. }
  357. INT
  358. InitializeAndRunApplication(
  359.     HWND hwndParent
  360.     )
  361. {
  362.     INT iReturn = -1;
  363.     HANDLE hMutexApp = NULL;
  364.     try
  365.     {
  366.         if (!SHRestricted(REST_NOSETTINGSASSIST))
  367.         {
  368.             if (!ActivateExistingInstance(&hMutexApp))
  369.             {
  370.                 //
  371.                 // Perform initialization of global stuff.
  372.                 //
  373.                 if (InitializeApplication())
  374.                 {
  375.                     //
  376.                     // Run the app.
  377.                     //
  378.                     iReturn = RunApplication(hwndParent);
  379.                 }
  380.                 if (0 != iReturn)
  381.                 {
  382.                     //
  383.                     // Something failed during initialization or trying
  384.                     // to run the app.  There's nothing here that should be a
  385.                     // run-time error that is not a programming or out-of-memory
  386.                     // error.  It's sufficient to just say "failed initialization".
  387.                     //
  388.                     SettingsMsgBox(
  389.                         hwndParent,
  390.                         MSG_ERROR_INITIALIZATION,
  391.                         MSG_MAINWINDOW_TITLE,
  392.                         MB_ICONSTOP | MB_OK);
  393.                 }
  394.             }
  395.         }
  396.         else
  397.         {
  398.             SettingsMsgBox(
  399.                 hwndParent,
  400.                 MSG_RESTRICTED_NOSETTINGSASSIST,
  401.                 MSG_MAINWINDOW_TITLE,
  402.                 MB_ICONSTOP | MB_OK);
  403.         }
  404.     }
  405.     catch(OutOfMemory)
  406.     {
  407.         SettingsMsgBox(
  408.             hwndParent,
  409.             MSG_OUTOFMEMORY,
  410.             MSG_MAINWINDOW_TITLE,
  411.             MB_ICONSTOP | MB_OK);
  412.     }
  413.     catch(...)
  414.     {
  415.         //
  416.         // Some unknown exception caught.
  417.         //
  418.         SettingsMsgBox(
  419.             hwndParent,
  420.             MSG_ERROR_UNKNOWN_ABORT,
  421.             MSG_MAINWINDOW_TITLE,
  422.             MB_ICONSTOP | MB_OK);
  423.     }
  424.     if (NULL != hMutexApp)
  425.         CloseHandle(hMutexApp);
  426.     return iReturn;
  427. }
  428. BOOL
  429. ActivateExistingInstance(
  430.     LPHANDLE phMutex
  431.     )
  432. {
  433.     Assert(NULL != phMutex);
  434.     BOOL bResult = FALSE;
  435.     //
  436.     // We use a named mutex to determine if there's an existing
  437.     // instance of this exe already running.
  438.     // If we create the mutex and GetLastError() returns
  439.     // ERROR_ALREADY_EXISTS, that means that there's an existing
  440.     // instance.  Otherwise, there isn't.
  441.     // If there is an existing instance, we find it's window and
  442.     // promote it to the foreground.
  443.     //
  444.     try
  445.     {
  446.         *phMutex = CreateMutex(NULL, FALSE, c_szAppMutex);
  447.         if (NULL != *phMutex &&
  448.             ERROR_ALREADY_EXISTS == GetLastError())
  449.         {
  450.             LPTSTR pszAppWndTitle = FmtMsgSprintf(MSG_MAINWINDOW_TITLE);
  451.             if (NULL != pszAppWndTitle)
  452.             {
  453.                 HWND hwndApp = FindWindowEx(NULL,
  454.                                             NULL,
  455.                                             WC_DIALOG,
  456.                                             pszAppWndTitle);
  457.                 if (NULL != hwndApp)
  458.                 {
  459.                     //
  460.                     // Bring an existing app to the foreground.
  461.                     //
  462.                     ShowWindow(hwndApp, SW_SHOWNORMAL);
  463.                     bResult = SetForegroundWindow(hwndApp);
  464.                 }
  465.                 LocalFree(pszAppWndTitle);
  466.             }
  467.         }
  468.     }
  469.     catch(...)
  470.     {
  471.         //
  472.         // If we catch an exception, we want the caller to think
  473.         // that an existing instance was not found.  That way,
  474.         // they'll open up a new settings UI window.  This ensures that for
  475.         // whatever reason, the user gets a settings UI.
  476.         //
  477.         bResult = FALSE;
  478.     }
  479.     return bResult;
  480. }
  481. BOOL
  482. InitializeApplication(
  483.     VOID
  484.     ) throw(OutOfMemory)
  485. {
  486.     INITCOMMONCONTROLSEX iccex;
  487.     Assert(NULL != g_hInstance);
  488.     //
  489.     // Init common controls.
  490.     //
  491.     iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
  492.     iccex.dwICC = ICC_LISTVIEW_CLASSES;
  493.     InitCommonControlsEx(&iccex);
  494.     //
  495.     // Initialize the folder object and set the "current" topic.
  496.     //
  497.     if (PTG.Folder.Initialize() && 0 < PTG.Folder.GetTopicCount())
  498.     {
  499.         PTG.pICurrentTopic = PTG.Folder.GetTopic(0);
  500.         //
  501.         // No need to keep additional ref counts on topic objects.
  502.         // Lifetime scope of the global folder object ensures correct
  503.         // existance of topic objects.
  504.         //
  505.         if (NULL != PTG.pICurrentTopic)
  506.             PTG.pICurrentTopic->Release();
  507.     }
  508.     return NULL != PTG.pICurrentTopic;
  509. }
  510. INT
  511. RunApplication(
  512.     HWND hwndParent
  513.     )
  514. {
  515.     HWND hwndDialog;
  516.     INT iReturn = -1;  // Returns -1 on failure.  0 on success.
  517.     Assert(NULL != g_hInstance);
  518.     hwndDialog = CreateDialog(
  519.         g_hInstance,
  520.         MAKEINTRESOURCE(IDD_SETTINGS),
  521.         hwndParent,
  522.         DlgProc);
  523.     if (NULL != hwndDialog)
  524.     {
  525.         //
  526.         // This isn't very polite.  I need to change the 
  527.         // exported interface to include a parent window instead of 
  528.         // using the desktop window.  Until I'm allowed to make changes
  529.         // to explorer.exe again (Memphis Beta 1 freeze in effect), this 
  530.         // will have to do.
  531.         //
  532.         SetForegroundWindow(hwndDialog);
  533.         ShowWindow(hwndDialog, SW_SHOWNORMAL);
  534.         UpdateWindow(hwndDialog);
  535.         MSG msg;
  536.         HACCEL haccel = PTG.haccelKeyboard;
  537.         while(TRUE == GetMessage(&msg, NULL, 0, 0))
  538.         {
  539.             if (NULL == hwndDialog || !IsDialogMessage(hwndDialog, &msg))
  540.             {
  541.                 if (NULL == haccel ||
  542.                    !TranslateAccelerator(hwndDialog,
  543.                                          haccel,
  544.                                          &msg))
  545.                 {
  546.                     {
  547.                         TranslateMessage(&msg);
  548.                         DispatchMessage(&msg);
  549.                     }
  550.                 }
  551.             }
  552.         }
  553.         iReturn = (int)msg.wParam;
  554.     }
  555.     return iReturn;
  556. }
  557. INT_PTR CALLBACK
  558. DlgProc(
  559.     HWND hwnd,
  560.     UINT uMsg,
  561.     WPARAM wParam,
  562.     LPARAM lParam
  563.    )
  564. {
  565.     LRESULT lResult = 1;
  566.     try
  567.     {
  568.         switch(uMsg)
  569.         {
  570.             case WM_INITDIALOG:
  571.                 lResult = OnInitDialog(hwnd, wParam, lParam);
  572.                 SetWindowLongPtr(hwnd, DWLP_MSGRESULT, lResult);
  573.                 break;
  574.             case WM_NOTIFY:
  575.                 lResult = OnNotify(hwnd, wParam, lParam);
  576.                 SetWindowLongPtr(hwnd, DWLP_MSGRESULT, lResult);
  577.                 break;
  578.             case WM_COMMAND:
  579.                 lResult = OnCommand(hwnd, wParam, lParam);
  580.                 SetWindowLongPtr(hwnd, DWLP_MSGRESULT, lResult);
  581.                 break;
  582.             case WM_CLOSE:
  583.                 lResult = OnClose(hwnd);
  584.                 break;
  585.             case WM_ENDSESSION:
  586.             case WM_DESTROY:
  587.                 lResult = OnDestroy(hwnd);
  588.                 break;
  589.             case WM_DRAWITEM:
  590.                 lResult = OnDrawItem(hwnd, (UINT)wParam, (LPDRAWITEMSTRUCT)lParam);
  591.                 SetWindowLongPtr(hwnd, DWLP_MSGRESULT, lResult);
  592.                 break;
  593.             case WM_MEASUREITEM:
  594.                 lResult = OnMeasureItem(hwnd, (UINT)wParam, (LPMEASUREITEMSTRUCT)lParam);
  595.                 SetWindowLongPtr(hwnd, DWLP_MSGRESULT, lResult);
  596.                 break;
  597.             case WM_SETCURSOR:
  598.                 lResult = OnSetCursor(hwnd, wParam, lParam);
  599.                 SetWindowLongPtr(hwnd, DWLP_MSGRESULT, lResult);
  600.                 break;
  601.         
  602.             case WM_QUERYNEWPALETTE:
  603.                 lResult = OnQueryNewPalette(hwnd, wParam, lParam);
  604.                 SetWindowLongPtr(hwnd, DWLP_MSGRESULT, lResult);
  605.                 break;
  606.             case WM_SYSCOLORCHANGE:
  607.                 PTG.OnSysColorChange();
  608.                 break;
  609.             case WM_DISPLAYCHANGE:
  610.                 OnDisplayChange();
  611.                 break;
  612.             case WM_PALETTECHANGED:
  613.                 lResult = OnPaletteChanged(hwnd, wParam, lParam);
  614.                 SetWindowLongPtr(hwnd, DWLP_MSGRESULT, lResult);
  615.                 break;
  616.             case WM_SETTINGCHANGE:
  617.                 lResult = OnSystemSettingChange(hwnd, wParam, lParam);
  618.                 SetWindowLongPtr(hwnd, DWLP_MSGRESULT, lResult);
  619.                 break;
  620.             default:
  621.                 lResult = 0;
  622.                 break;
  623.         }
  624.     }
  625.     catch(OutOfMemory)
  626.     {
  627.         SettingsMsgBox(
  628.             hwnd,
  629.             MSG_OUTOFMEMORY,
  630.             MSG_MAINWINDOW_TITLE,
  631.             MB_ICONSTOP | MB_OK);
  632.         DestroyWindow(hwnd);
  633.         lResult = 0;
  634.     }
  635.     catch(...)
  636.     {
  637.         //
  638.         // Some unknown exception caught.
  639.         //
  640.         SettingsMsgBox(
  641.             hwnd,
  642.             MSG_ERROR_UNKNOWN,
  643.             MSG_MAINWINDOW_TITLE,
  644.             MB_ICONSTOP | MB_OK);
  645.         DestroyWindow(hwnd);
  646.         lResult = 0;
  647.     }
  648.     return lResult;
  649. }
  650. //
  651. // Fill the topic title's list with title strings.
  652. // The lParam of each listview item contains the address of
  653. // a SETTINGS_FOLDER_TOPIC object.
  654. //
  655. BOOL
  656. LoadTopicsIntoList(
  657.     HWND hwndList
  658.     )
  659. {
  660.     Assert(NULL != hwndList);
  661.     INT cTopics = PTG.Folder.GetTopicCount();
  662.     LV_ITEM item;
  663.     item.mask       = LVIF_PARAM;
  664.     item.iSubItem   = 0;
  665.     for (INT i = 0; i < cTopics; i++)
  666.     {
  667.         PSETTINGS_FOLDER_TOPIC pITopic = PTG.Folder.GetTopic(i);
  668.         if (NULL != pITopic)
  669.         {
  670.             item.iItem  = i;
  671.             item.lParam = (LPARAM)pITopic;
  672.             ListView_InsertItem(hwndList, &item);
  673.             //
  674.             // The folder object holds a reference to each topic object.
  675.             // Since the folder's lifetime encloses the lifetime of the
  676.             // listview, there is no need for the listview to maintain a
  677.             // separate reference count on the topic objects.
  678.             //
  679.             pITopic->Release();
  680.         }
  681.     }
  682.     //
  683.     // Success only if all topics were added.
  684.     //
  685.     return ListView_GetItemCount(hwndList) == cTopics;
  686. }
  687. VOID
  688. GetDialogFont(
  689.     CFont& font,
  690.     HWND hwndDlg,
  691.     GDF_APPEARANCE eAppearance
  692.     )
  693. {
  694.     Assert(NULL != hwndDlg);
  695.     LOGFONT lf;
  696.     HFONT hfont;
  697.     hfont = (HFONT)SendMessage(GetDlgItem(hwndDlg, IDC_TXT_IMAGE), WM_GETFONT, 0, 0);
  698.     GetObject(hfont, sizeof(lf), &lf);
  699.     switch(eAppearance)
  700.     {
  701.         case GDF_HIGHLIGHT:
  702.             lf.lfUnderline = TRUE;
  703.             break;
  704.         case GDF_TITLE:
  705.             {
  706.                 //
  707.                 // Override the dialog font with a serifed font for
  708.                 // the title.  It looks better.
  709.                 //
  710.                 CDC dc(hwndDlg);
  711.                 ZeroMemory(&lf, sizeof(lf));
  712.                 lstrcpyn(lf.lfFaceName, c_szBannerFontFace, LF_FACESIZE);
  713.                 lf.lfWeight = FW_BOLD;
  714.                 lf.lfHeight = FONTPTS_TO_LFHEIGHT(dc, BANNER_FONT_SIZE);
  715.             }
  716.             break;
  717.         default:
  718.             //
  719.             // Use the font obtained from the dialog.
  720.             //
  721.             break;
  722.     }
  723.     font = lf;
  724. }
  725. BOOL
  726. InitializeListView(
  727.     HWND hwndList
  728.     )
  729. {
  730.     Assert(NULL != hwndList);
  731.     BOOL bResult = FALSE;
  732.     LV_COLUMN lvc;
  733.     RECT rc;
  734.     GetClientRect(hwndList, &rc);
  735.     //
  736.     // Add our single column.
  737.     // Note that we bring in the right margin to allow for a vertical
  738.     // scroll bar.  If topic title lengths are kept to one line, no
  739.     // scroll bar is needed nor will one appear.  However, if some
  740.     // operation like localization increases any of the topic titles
  741.     // to more than one line, all of the items will grow in height,
  742.     // requiring the automatic addition of the scroll bar.  By bringing
  743.     // in the right margin, we prevent the automatic addition of the
  744.     // horizontal scroll bar.
  745.     //
  746.     lvc.mask     = LVCF_FMT | LVCF_WIDTH | LVCF_SUBITEM;
  747.     lvc.fmt      = LVCFMT_LEFT;
  748.     lvc.cx       = rc.right - TOPIC_LIST_RMARGIN();
  749.     lvc.iSubItem = 0;
  750.     if (-1 != ListView_InsertColumn(hwndList, 0, &lvc))
  751.     {
  752.         //
  753.         // Make listview transparent so any background watermark
  754.         // shows through.
  755.         //
  756.         ListView_SetBkColor(hwndList, CLR_NONE);
  757.         //
  758.         // Enable mouse tracking (hover select).
  759.         //
  760.         SendMessage(hwndList, LVM_SETEXTENDEDLISTVIEWSTYLE,
  761.                               LVS_EX_TRACKSELECT | LVS_EX_ONECLICKACTIVATE,
  762.                               LVS_EX_TRACKSELECT | LVS_EX_ONECLICKACTIVATE);
  763.         //
  764.         // Set the listview's selection mode (single vs. double click)
  765.         // based on the user's preference as set in the shell.
  766.         //
  767.         SetListviewClickMode();
  768.         //
  769.         // Set a custom hover time.  Default is too slow.
  770.         //
  771.         ListView_SetHoverTime(hwndList, MOUSE_HOVER_TIME);
  772.         bResult = TRUE;
  773.     }
  774.     return bResult;
  775. };
  776. LRESULT
  777. OnInitDialog(
  778.     HWND hwnd,
  779.     WPARAM wParam,
  780.     LPARAM lParam
  781.     )
  782. {
  783.     Assert(NULL != hwnd);
  784.     LRESULT lResult = 0;
  785.     try
  786.     {
  787.         //
  788.         // Save hwnd for topic list.  We'll be using it a lot.
  789.         //
  790.         PTG.hwndTopicList = GetDlgItem(hwnd, IDC_LIST_TOPICS);
  791.         //
  792.         // The control IDC_BMP_IMAGE is provided in the dialog template
  793.         // as a place to draw the topic image and it's shadow border.
  794.         // Convert it to OWNERDRAW so we control the painting.
  795.         //
  796.         HWND hwndImage = GetDlgItem(hwnd, IDC_BMP_IMAGE);
  797.         LONG lStyle;
  798.         lStyle = GetWindowLong(hwndImage, GWL_STYLE);
  799.         lStyle &= ~SS_BITMAP;
  800.         lStyle |= SS_OWNERDRAW;
  801.         SetWindowLong(hwndImage, GWL_STYLE, lStyle);
  802.         //
  803.         // The control IDC_BMP_BANNER is provided in the dialog template
  804.         // as a place to draw the banner watermark.
  805.         // Convert it to OWNERDRAW so we control the painting.
  806.         //
  807.         HWND hwndBanner = GetDlgItem(hwnd, IDC_BMP_BANNER);
  808.         lStyle = GetWindowLong(hwndBanner, GWL_STYLE);
  809.         lStyle &= ~SS_BITMAP;
  810.         lStyle |= SS_OWNERDRAW;
  811.         SetWindowLong(hwndBanner, GWL_STYLE, lStyle);
  812.         //
  813.         // Text fonts.
  814.         // Use the dialog's font defined in the dialog template.
  815.         //
  816.         GetDialogFont(PTG.hfontTextNormal, hwnd, GDF_NORMAL);
  817.         GetDialogFont(PTG.hfontTextHighlight, hwnd, GDF_HIGHLIGHT);
  818.         GetDialogFont(PTG.hfontBanner, hwnd, GDF_TITLE);
  819.         //
  820.         // Give the window a title.
  821.         // Use the string from MSG_MAINWINDOW_TITLE so we are assured
  822.         // that it is the same string used to find an existing window
  823.         // when we first launch the app (prevent multiple instances).
  824.         //
  825.         LPTSTR pszTitle = FmtMsgSprintf(MSG_MAINWINDOW_TITLE);
  826.         SetWindowText(hwnd, pszTitle);
  827.         LocalFree(pszTitle);
  828.         //
  829.         // Set the window's icon.  Can't set it for the class because
  830.         // we're using a dialog as the main window.
  831.         //
  832.         SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)PTG.hiconApplicationSm);
  833.         SendMessage(hwnd, WM_SETICON, ICON_BIG,   (LPARAM)PTG.hiconApplicationLg);
  834.         //
  835.         // Set some of our custom styles for the listview and
  836.         // load the topic objects into the list.
  837.         //
  838.         if (InitializeListView(PTG.hwndTopicList) &&
  839.             LoadTopicsIntoList(PTG.hwndTopicList))
  840.         {
  841.             //
  842.             // Set the focus to the listview.
  843.             //
  844.             FocusOnListview();
  845.             lResult = 1;
  846.         }
  847.         else
  848.         {
  849.             SettingsMsgBox(NULL,
  850.                            MSG_ERROR_INITIALIZATION,
  851.                            MSG_MAINWINDOW_TITLE,
  852.                            MB_ICONSTOP | MB_OK);
  853.         }
  854.     }
  855.     //
  856.     // Catch any exceptions that occur during the dialog
  857.     // initialization process.  These must be handled differently
  858.     // than those caught in DlgProc.  If an exception occurs here,
  859.     // we assume the dialog is incomplete so we destroy it.
  860.     //
  861.     catch(OutOfMemory)
  862.     {
  863.         SettingsMsgBox(NULL,
  864.                        MSG_OUTOFMEMORY,
  865.                        MSG_MAINWINDOW_TITLE,
  866.                        MB_ICONSTOP | MB_OK);
  867.     }
  868.     catch(...)
  869.     {
  870.         SettingsMsgBox(NULL,
  871.                        MSG_ERROR_UNKNOWN,
  872.                        MSG_MAINWINDOW_TITLE,
  873.                        MB_ICONSTOP | MB_OK);
  874.     }
  875.     if (1 != lResult)
  876.     {
  877.         DestroyWindow(hwnd);
  878.     }
  879.     return lResult;
  880. }
  881. LRESULT
  882. OnDestroy(
  883.     HWND hwnd
  884.     )
  885. {
  886.     Assert(NULL != hwnd);
  887.     //
  888.     // Empty out the listview object.
  889.     // Remember, the listview doesn't hold a reference count for the
  890.     // topic objects.  Don't need to call Release() on the topics.
  891.     // Topic objects are released when the Folder object is destroyed.
  892.     //
  893.     ListView_DeleteAllItems(PTG.hwndTopicList);
  894.     PostQuitMessage(0);
  895.     return 0;
  896. }
  897. LRESULT
  898. OnClose(
  899.     HWND hwnd
  900.     )
  901. {
  902.     Assert(NULL != hwnd);
  903.     DestroyWindow(hwnd);
  904.     return 0;
  905. }
  906. LRESULT
  907. OnCommand(
  908.     HWND hwnd,
  909.     WPARAM wParam,
  910.     LPARAM lParam
  911.     )
  912. {
  913.     Assert(NULL != hwnd);
  914.     LRESULT lResult  = 1;
  915.     switch(LOWORD(wParam))
  916.     {
  917.         case IDM_CLOSE:
  918.         case IDM_ESCAPE:
  919.         case IDC_BTN_CLOSE:
  920.             PostMessage(hwnd, WM_CLOSE, 0, 0);
  921.             break;
  922.         case IDC_LIST_TOPICS:
  923.             PTG.bWaitCursor = TRUE;            
  924.             SendMessage(hwnd, WM_SETCURSOR, 0, 0);
  925.             PTG.pICurrentTopic->TopicSelected();
  926.             PTG.bWaitCursor = FALSE;
  927.             break;
  928.         default:
  929.             lResult = 0;
  930.             break;
  931.     }
  932.     return lResult;
  933. }
  934. LRESULT
  935. OnDisplayChange(
  936.     VOID
  937.     )
  938. {
  939.     INT cTopics = PTG.Folder.GetTopicCount();
  940.     //
  941.     // Re-load the header and topic spot bitmaps.
  942.     //
  943.     PTG.OnDisplayChange();
  944.     //
  945.     // Tell each topic to re-load it's bitmap.
  946.     //
  947.     for (INT i = 0; i < cTopics; i++)
  948.     {
  949.         PSETTINGS_FOLDER_TOPIC pITopic = PTG.Folder.GetTopic(i);
  950.         if (NULL != pITopic)
  951.         {
  952.             pITopic->DisplayChanged();
  953.             pITopic->Release();
  954.         }
  955.     }
  956.     return 1;
  957. }
  958.         
  959. LRESULT
  960. OnSystemSettingChange(
  961.     HWND hwnd,
  962.     WPARAM wParam,
  963.     LPARAM lParam
  964.     )
  965. {
  966.     SetListviewClickMode();
  967.     return 0;
  968. }
  969.  
  970. LRESULT
  971. OnMeasureItem(
  972.     HWND hwnd,
  973.     UINT idCtl,
  974.     LPMEASUREITEMSTRUCT pmis
  975.     )
  976. {
  977.     Assert(NULL != hwnd);
  978.     Assert(NULL != pmis);
  979.     if (ODT_LISTVIEW == pmis->CtlType)
  980.     {
  981.         //
  982.         // Height of each item in the listview is set to
  983.         // contain the longest topic title accounting for
  984.         // text wrap.
  985.         //
  986.         pmis->itemHeight = CalcListviewItemHeight(hwnd);
  987.     }
  988.     return 1;
  989. }
  990. //
  991. // Calculate the maximum height that will be required to display
  992. // a topic title in the list.  This is called in response to the
  993. // WM_MEASUREITEM that is received when the listview object
  994. // needs to determine how tall each item is.
  995. //
  996. LONG
  997. CalcListviewItemHeight(
  998.     HWND hwndDlg
  999.     )
  1000. {
  1001.     Assert(NULL != hwndDlg);
  1002.     INT cTopics = PTG.Folder.GetTopicCount();
  1003.     LONG cyMax  = 0;
  1004.     CDC dc(hwndDlg);
  1005.     RECT rcListView, rc;
  1006.     //
  1007.     // Need to select the dialog's font into the DC so that we get an
  1008.     // accurate calculation of the required rectangle height for the
  1009.     // listview items.
  1010.     //
  1011.     HFONT hfont = (HFONT)SendMessage(hwndDlg, WM_GETFONT, 0, 0);
  1012.     HFONT hfontOld = (HFONT)SelectObject(dc, hfont);
  1013.     //
  1014.     // Can't use PTG.hwndTopicList yet because WM_INITDIALOG hasn't
  1015.     // been processed.
  1016.     //
  1017.     GetWindowRect(GetDlgItem(hwndDlg, IDC_LIST_TOPICS), &rcListView);
  1018.     for (INT i = 0; i < cTopics; i++)
  1019.     {
  1020.         PSETTINGS_FOLDER_TOPIC pITopic = PTG.Folder.GetTopic(i);
  1021.         if (NULL != pITopic)
  1022.         {
  1023.             LPCTSTR pszTitle = NULL;
  1024.             pITopic->GetTitle(pszTitle);
  1025.             Assert(NULL != pszTitle);
  1026.             rc        = rcListView;
  1027.             rc.left  += TOPIC_LIST_LMARGIN();
  1028.             rc.right -= TOPIC_LIST_RMARGIN();
  1029.             rc.bottom = rc.top + 1;            // DrawText will extend this.
  1030.             DrawText(dc,
  1031.                      pszTitle,
  1032.                      lstrlen(pszTitle),
  1033.                      &rc,
  1034.                      DT_WORDBREAK | DT_CALCRECT);
  1035.             cyMax = MAX(cyMax, rc.bottom - rc.top);
  1036.             pITopic->Release();
  1037.         }
  1038.     }
  1039.     SelectObject(dc, hfontOld);
  1040.     //
  1041.     // Without INTER_TOPIC_GAP, single-line list items are too scrunched
  1042.     // together vertically.  Doesn't look good for this type of stylized UI.
  1043.     //
  1044.     return cyMax + PIXELSY(INTER_TOPIC_GAP);
  1045. }
  1046. VOID 
  1047. SetListviewClickMode(
  1048.     VOID
  1049.     )
  1050. {
  1051.     SHELLSTATE ss;
  1052.     DWORD dwStyle;
  1053.     //
  1054.     // Get the current double-click setting in the shell (user pref).
  1055.     //
  1056.     SHGetSetSettings(&ss, SSF_WIN95CLASSIC | SSF_WEBVIEW | SSF_DOUBLECLICKINWEBVIEW, FALSE);
  1057.     //
  1058.     // Get the current listview style bits.
  1059.     //
  1060.     dwStyle = ListView_GetExtendedListViewStyle(PTG.hwndTopicList);
  1061.     //
  1062.     // We get single-click if user wants no web view or single-click in web view.
  1063.     // SINGLECLICK = WEBVIEW && !DBLCLICKINWEBVIEW
  1064.     //
  1065.     PTG.bListviewSingleClick = !ss.fWin95Classic && ss.fWebView && !ss.fDoubleClickInWebView;
  1066.     if (PTG.bListviewSingleClick)
  1067.     {
  1068.         if (0 == (dwStyle & LVS_EX_ONECLICKACTIVATE))
  1069.         {
  1070.             //
  1071.             // User wants single click but list is double click..
  1072.             // Set listview to single-click mode.
  1073.             //
  1074.             SendMessage(PTG.hwndTopicList, 
  1075.                         LVM_SETEXTENDEDLISTVIEWSTYLE,
  1076.                         LVS_EX_ONECLICKACTIVATE,
  1077.                         LVS_EX_ONECLICKACTIVATE);
  1078.         }
  1079.     }
  1080.     else if (dwStyle & LVS_EX_ONECLICKACTIVATE)
  1081.     {
  1082.         //
  1083.         // User wants double click but list is single click.
  1084.         // Set listview to double click mode.
  1085.         //
  1086.         SendMessage(PTG.hwndTopicList, 
  1087.                     LVM_SETEXTENDEDLISTVIEWSTYLE,
  1088.                     LVS_EX_ONECLICKACTIVATE,
  1089.                     0);
  1090.     }
  1091. }
  1092. VOID
  1093. FocusOnListview(
  1094.     VOID
  1095.     )
  1096. {
  1097.     INT iFocus = ListView_GetNextItem(PTG.hwndTopicList, -1, LVNI_FOCUSED);
  1098.     if (-1 == iFocus)
  1099.         iFocus = 0;
  1100.     ListView_SetItemState(PTG.hwndTopicList, iFocus, LVIS_FOCUSED | LVIS_SELECTED,
  1101.                                                      LVIS_FOCUSED | LVIS_SELECTED);
  1102. }
  1103. //
  1104. // Draw the "spot" bitmap to the left of the topic title.
  1105. // The bitmaps use the color RGB(0, 128, 128) to indicate transparency.
  1106. // That's sort of a dull teal green.
  1107. // This code was taken from MSDN KB article Q79212.
  1108. //
  1109. VOID
  1110. DrawTopicSpot(
  1111.     HDC hdc,
  1112.     LPRECT prc,
  1113.     SPOTSTATE spot
  1114.     )
  1115. {
  1116.     Assert(NULL != hdc);
  1117.     Assert(NULL != prc);
  1118.     COLORREF cColor;
  1119.     HBITMAP bmAndBack, bmAndObject, bmAndMem, bmSave;
  1120.     HBITMAP bmBackOld, bmObjectOld, bmMemOld, bmSaveOld, bmTempOld;
  1121.     POINT ptSize;
  1122.     RECT rcBitmap;
  1123.     HPALETTE hOldPalette[2];
  1124.     CDIB& refdibTopicSpot = PTG.rgdibTopicSpot[spot];
  1125.     //
  1126.     // Select the bitmap's palette into the window DC and
  1127.     // memory DC and realize it.
  1128.     //
  1129.     hOldPalette[0] = SelectPalette(hdc, (HPALETTE)refdibTopicSpot, TRUE);
  1130.     RealizePalette(hdc);
  1131.     hOldPalette[1] = SelectPalette(PTG.dcMem, (HPALETTE)refdibTopicSpot, TRUE);
  1132.     RealizePalette(PTG.dcMem);
  1133.     //
  1134.     // Select the bitmap into a temp DC.
  1135.     //
  1136.     CDC dcTemp(hdc);
  1137.     bmTempOld = (HBITMAP)SelectObject(dcTemp, (HBITMAP)refdibTopicSpot);
  1138.     //
  1139.     // Get bitmap dimensions and convert to logical coordinates.
  1140.     //
  1141.     refdibTopicSpot.GetRect(&rcBitmap);
  1142.     ptSize.x = rcBitmap.right;
  1143.     ptSize.y = rcBitmap.bottom;
  1144.     DPtoLP(dcTemp, &ptSize, 1);
  1145.     //
  1146.     // Create some DCs to hold temporary data.
  1147.     //
  1148.     CDC dcBack(hdc);
  1149.     CDC dcObject(hdc);
  1150.     CDC dcSave(hdc);
  1151.     //
  1152.     // Create a bitmap for each DC.
  1153.     //
  1154.     bmAndBack   = CreateBitmap(ptSize.x, ptSize.y, 1, 1, NULL);
  1155.     bmAndObject = CreateBitmap(ptSize.x, ptSize.y, 1, 1, NULL);
  1156.     bmAndMem    = CreateCompatibleBitmap(hdc, ptSize.x, ptSize.y);
  1157.     bmSave      = CreateCompatibleBitmap(hdc, ptSize.x, ptSize.y);
  1158.     //
  1159.     // Each DC must select a bitmap object to store pixel data.
  1160.     //
  1161.     bmBackOld   = (HBITMAP)SelectObject(dcBack,   bmAndBack);
  1162.     bmObjectOld = (HBITMAP)SelectObject(dcObject, bmAndObject);
  1163.     bmMemOld    = (HBITMAP)SelectObject(PTG.dcMem,  bmAndMem);
  1164.     bmSaveOld   = (HBITMAP)SelectObject(dcSave,   bmSave);
  1165.     //
  1166.     // Set the proper mapping mode.
  1167.     //
  1168.     SetMapMode(dcTemp, GetMapMode(hdc));
  1169.     //
  1170.     // Save the bitmap sent here because it will be overwritten.
  1171.     //
  1172.     BitBlt(dcSave, 0, 0, ptSize.x, ptSize.y, dcTemp, 0, 0, SRCCOPY);
  1173.     //
  1174.     // Set the background color of the source DC to the color
  1175.     // contained in the parts of the bitmap that should be transparent.
  1176.     //
  1177.     cColor = SetBkColor(dcTemp, COLOR_TOPICSPOT_MASK);
  1178.     //
  1179.     // Create the object mask for the bitmap by performing a BitBlt
  1180.     // from the source bitmap to a monochrome bitmap.
  1181.     //
  1182.     BitBlt(dcObject, 0, 0, ptSize.x, ptSize.y, dcTemp, 0, 0, SRCCOPY);
  1183.     //
  1184.     // Set the background color of the source DC back to the original color.
  1185.     //
  1186.     SetBkColor(dcTemp, cColor);
  1187.     //
  1188.     // Create the inverse of the object mask.
  1189.     //
  1190.     BitBlt(dcBack, 0, 0, ptSize.x, ptSize.y, dcObject, 0, 0, NOTSRCCOPY);
  1191.     //
  1192.     // Copy the background of the main DC to the destination.
  1193.     //
  1194.     BitBlt(PTG.dcMem, 0, 0, ptSize.x, ptSize.y, hdc, prc->left, prc->top, SRCCOPY);
  1195.     //
  1196.     // Mask out the places where the bitmap will be placed.
  1197.     //
  1198.     BitBlt(PTG.dcMem, 0, 0, ptSize.x, ptSize.y, dcObject, 0, 0, SRCAND);
  1199.     //
  1200.     // Mask out the transparent colored pixels on the bitmap.
  1201.     //
  1202.     BitBlt(dcTemp, 0, 0, ptSize.x, ptSize.y, dcBack, 0, 0, SRCAND);
  1203.     //
  1204.     // XOR the bitmap with the background on the destination DC.
  1205.     //
  1206.     BitBlt(PTG.dcMem, 0, 0, ptSize.x, ptSize.y, dcTemp, 0, 0, SRCPAINT);
  1207.     //
  1208.     // Copy the destination to the screen.
  1209.     //
  1210.     BitBlt(hdc, prc->left, prc->top, ptSize.x, ptSize.y, PTG.dcMem, 0, 0, SRCCOPY);
  1211.     //
  1212.     // Place the original bitmap back into the bitmap sent here.
  1213.     //
  1214.     BitBlt(dcTemp, 0, 0, ptSize.x, ptSize.y, dcSave, 0, 0, SRCCOPY);
  1215.     //
  1216.     // Delete the memory bitmaps.
  1217.     //
  1218.     DeleteObject(SelectObject(dcBack, bmBackOld));
  1219.     DeleteObject(SelectObject(dcObject, bmObjectOld));
  1220.     DeleteObject(SelectObject(PTG.dcMem, bmMemOld));
  1221.     DeleteObject(SelectObject(dcSave, bmSaveOld));
  1222.     SelectObject(dcTemp, bmTempOld);
  1223.     //
  1224.     // Restore the palette.
  1225.     //
  1226.     SelectPalette(hdc, hOldPalette[0], TRUE);
  1227.     RealizePalette(hdc);
  1228.     SelectPalette(PTG.dcMem, hOldPalette[1], TRUE);
  1229. }
  1230. VOID
  1231. DrawBanner(
  1232.     HWND hwnd,
  1233.     HDC hdc
  1234.     )
  1235. {
  1236.     Assert(NULL != hwnd);
  1237.     HPALETTE hOldPalette[2];
  1238.     HBITMAP hOldBitmap;
  1239.     RECT rc, rcBitmap;
  1240.     INT iStretchModeOld;
  1241.     GetClientRect(hwnd, &rc);
  1242.     //
  1243.     // We need the bitmap size for StretchBlt.
  1244.     //
  1245.     PTG.dibBanner.GetRect(&rcBitmap);
  1246.     hOldPalette[0] = SelectPalette(hdc, (HPALETTE)PTG.dibBanner, TRUE);
  1247.     RealizePalette(hdc);
  1248.     hOldPalette[1] = SelectPalette(PTG.dcMem, (HPALETTE)PTG.dibBanner, TRUE);
  1249.     RealizePalette(PTG.dcMem);
  1250.     hOldBitmap = (HBITMAP)SelectObject(PTG.dcMem, (HBITMAP)PTG.dibBanner);
  1251.     iStretchModeOld = SetStretchBltMode(hdc, COLORONCOLOR);
  1252.     StretchBlt(hdc,
  1253.                rc.left,
  1254.                rc.top,
  1255.                rc.right - rc.left,
  1256.                rc.bottom - rc.top,
  1257.                PTG.dcMem,
  1258.                0,
  1259.                0,
  1260.                rcBitmap.right,
  1261.                rcBitmap.bottom,
  1262.                SRCCOPY);
  1263.     SelectPalette(hdc, hOldPalette[0], TRUE);
  1264.     RealizePalette(hdc);
  1265.     SetStretchBltMode(hdc, iStretchModeOld);
  1266.     SelectPalette(PTG.dcMem, hOldPalette[1], TRUE);
  1267.     SelectObject(PTG.dcMem, hOldBitmap);
  1268.     DrawBannerTitle(hwnd, hdc);
  1269. }
  1270. VOID
  1271. DrawBannerTitle(
  1272.     HWND hwnd,
  1273.     HDC hdc
  1274.     )
  1275. {
  1276.     Assert(NULL != hwnd);
  1277.     LPTSTR pszTitle = FmtMsgSprintf(MSG_BANNER_TITLE);
  1278.     INT iBkModeOld  = SetBkMode(hdc, TRANSPARENT);
  1279.     HFONT hfontOld  = (HFONT)SelectObject(hdc, PTG.hfontBanner);
  1280.     RECT rc;
  1281.     GetClientRect(hwnd, &rc);
  1282.     rc.left += PIXELSX(BANNER_TITLE_INDENT);
  1283.     DrawText(hdc,
  1284.              pszTitle,
  1285.              lstrlen(pszTitle),
  1286.              &rc,
  1287.              DT_SINGLELINE | DT_VCENTER);
  1288.     LocalFree(pszTitle);
  1289.     SelectObject(hdc, hfontOld);
  1290.     SetBkMode(hdc, iBkModeOld);
  1291. }
  1292. //
  1293. // Draw the topic image and drop shadow.
  1294. //
  1295. VOID
  1296. DrawTopicImage(
  1297.     HWND hwnd,
  1298.     HDC hdc,
  1299.     PSETTINGS_FOLDER_TOPIC pITopic
  1300.     )
  1301. {
  1302.     Assert(NULL != hwnd);
  1303.     Assert(NULL != hdc);
  1304.     Assert(NULL != pITopic);
  1305.     RECT rc, rcImage;
  1306.     INT cxImageShadow = PIXELSX(IMAGE_SHADOW_WIDTH);
  1307.     INT cyImageShadow = PIXELSY(IMAGE_SHADOW_WIDTH);
  1308.     //
  1309.     // Draw the image.
  1310.     //
  1311.     GetClientRect(hwnd, &rc);
  1312.     rcImage = rc;
  1313.     rcImage.right  -= cxImageShadow;
  1314.     rcImage.bottom -= cyImageShadow;
  1315.     pITopic->DrawImage(hdc, &rcImage);
  1316.     //
  1317.     // Draw the bottom shadow.
  1318.     //
  1319.     rc.left = cxImageShadow;
  1320.     rc.top  = rc.bottom - cyImageShadow;
  1321.     FillRect(hdc, &rc, (HBRUSH)GetStockObject(GRAY_BRUSH));
  1322.     //
  1323.     // Draw the right-edge shadow.
  1324.     //
  1325.     rc.left   = rc.right - cxImageShadow;
  1326.     rc.top    = cyImageShadow;
  1327.     FillRect(hdc, &rc, (HBRUSH)GetStockObject(GRAY_BRUSH));
  1328.     //
  1329.     // Draw a thin black frame.
  1330.     //
  1331.     FrameRect(hdc, &rcImage, (HBRUSH)GetStockObject(BLACK_BRUSH));
  1332. }
  1333. //
  1334. // Draw an item in the topic list.
  1335. //
  1336. LRESULT
  1337. OnDrawItem(
  1338.     HWND hwnd,
  1339.     UINT idCtl,
  1340.     LPDRAWITEMSTRUCT pdis
  1341.     )
  1342. {
  1343.     Assert(NULL != hwnd);
  1344.     Assert(NULL != pdis);
  1345.     switch(pdis->CtlType)
  1346.     {
  1347.         case ODT_LISTVIEW:
  1348.             DrawListviewItem(hwnd, idCtl, pdis);
  1349.             break;
  1350.         case ODT_STATIC:
  1351.             switch(pdis->CtlID)
  1352.             {
  1353.                 case IDC_BMP_IMAGE:
  1354.                     DrawTopicImage(pdis->hwndItem, pdis->hDC, PTG.pICurrentTopic);
  1355.                     break;
  1356.                 case IDC_BMP_BANNER:
  1357.                     DrawBanner(pdis->hwndItem, pdis->hDC);
  1358.                     break;
  1359.                 default:
  1360.                     break;
  1361.             }
  1362.             break;
  1363.         default:
  1364.             break;
  1365.     }
  1366.     return 1;
  1367. }
  1368. VOID
  1369. DrawListviewItem(
  1370.     HWND hwnd,
  1371.     UINT idCtl,
  1372.     LPDRAWITEMSTRUCT pdis
  1373.     )
  1374. {
  1375.     COLORREF clrText = PTG.clrTopicTextNormal;
  1376.     COLORREF clrOld, clrBkOld;
  1377.     INT iBkModeOld;
  1378.     HFONT hfontText = (HFONT)PTG.hfontTextNormal;
  1379.     HFONT hfontOld;
  1380.     SPOTSTATE spot = SPOT_NORMAL;
  1381.     PSETTINGS_FOLDER_TOPIC pITopic = (PSETTINGS_FOLDER_TOPIC)pdis->itemData;
  1382.     //
  1383.     // ODA_DRAWENTIRE is the only ownerdraw action sent by the listview
  1384.     // control.  No need to test for the action.
  1385.     //
  1386.     if (pdis->itemState & (ODS_FOCUS | ODS_SELECTED))
  1387.     {
  1388.         if (pdis->itemID == (DWORD)PTG.iLastItemHit)
  1389.         {
  1390.             //
  1391.             // Drawing the current "hit" item.  Use highlight
  1392.             // font and text color.
  1393.             //
  1394.             clrText   = PTG.clrTopicTextHighlight;
  1395.             hfontText = (HFONT)PTG.hfontTextHighlight;
  1396.         }
  1397.         spot = SPOT_FOCUS;
  1398.         //
  1399.         // Tell the dialog that the listview is the "default"
  1400.         // control.  If the user presses [Enter] while the listview
  1401.         // has focus, the current topic is launched.
  1402.         //
  1403.         SendMessage(hwnd, DM_SETDEFID, IDC_LIST_TOPICS, 0);
  1404.     }
  1405.     else
  1406.     {
  1407.         //
  1408.         // Not FOCUSED and not SELECTED
  1409.         //
  1410.         if (pdis->itemID == (DWORD)PTG.iLastItemHit &&
  1411.             PTG.bListviewSingleClick)
  1412.         {
  1413.             //
  1414.             // Drawing the current "hit" item but it is not the
  1415.             // currently selected topic.  Still use hightlight font
  1416.             // and text color.  This is what makes the highlighting
  1417.             // follow the mouse cursor.  Note that this doesn't happen
  1418.             // when we're in double-click mode.
  1419.             //
  1420.             clrText   = PTG.clrTopicTextHighlight;
  1421.             hfontText = (HFONT)PTG.hfontTextHighlight;
  1422.         }
  1423.     }
  1424.     //
  1425.     // Draw the "spot" to the left of the topic title.
  1426.     //
  1427.     DrawTopicSpot(pdis->hDC, &pdis->rcItem, spot);
  1428.     //
  1429.     // Get the topic title from the topic object.
  1430.     //
  1431.     LPCTSTR pszTitle;
  1432.     pITopic->GetTitle(pszTitle);
  1433.     Assert(NULL != pszTitle);
  1434.     //
  1435.     // Need OPAQUE drawing so we erase the underline when
  1436.     // updating a previously underlined item with a non-underlined
  1437.     // font.
  1438.     //
  1439.     iBkModeOld = SetBkMode(pdis->hDC, OPAQUE);
  1440.     clrBkOld   = SetBkColor(pdis->hDC, PTG.clrTopicTextBackground);
  1441.     clrOld     = SetTextColor(pdis->hDC, clrText);
  1442.     hfontOld   = (HFONT)SelectObject(pdis->hDC, hfontText);
  1443.     //
  1444.     // Draw the title string.
  1445.     // Note we leave space to the left for the "spot".
  1446.     // Also leave space on the right in case a vertical scroll bar is
  1447.     // displayed.  This could happen if localizers don't keep their
  1448.     // topic title strings short.
  1449.     //
  1450.     pdis->rcItem.left  += TOPIC_LIST_LMARGIN();
  1451.     pdis->rcItem.right -= TOPIC_LIST_RMARGIN();
  1452.     DrawText(pdis->hDC,
  1453.              pszTitle,
  1454.              lstrlen(pszTitle),
  1455.              &pdis->rcItem,
  1456.              DT_NOCLIP | DT_WORDBREAK);
  1457.     SetTextColor(pdis->hDC, clrOld);
  1458.     SetBkColor(pdis->hDC, clrBkOld);
  1459.     SetBkMode(pdis->hDC, iBkModeOld);
  1460.     SelectObject(pdis->hDC, hfontOld);
  1461. }
  1462. //
  1463. // Determine if the mouse pointer is currently over a listview item.
  1464. //
  1465. INT
  1466. ListViewItemHit(
  1467.     VOID
  1468.     )
  1469. {
  1470.     INT iItem = -1;
  1471.     POINT ptMouse;
  1472.     if (GetCursorPos(&ptMouse) && ScreenToClient(PTG.hwndTopicList, &ptMouse))
  1473.     {
  1474.         iItem = ListViewItemHit(ptMouse.x, ptMouse.y);
  1475.     }
  1476.     return iItem;
  1477. }
  1478. //
  1479. // Determine if a given x,y coordinate is over a listview item.
  1480. //
  1481. INT
  1482. ListViewItemHit(
  1483.     INT xPos,
  1484.     INT yPos
  1485.     )
  1486. {
  1487.     INT iItem = -1;
  1488.     LV_HITTESTINFO hti;
  1489.     hti.pt.x = xPos;
  1490.     hti.pt.y = yPos;
  1491.     iItem = ListView_HitTest(PTG.hwndTopicList, &hti);
  1492.     if (!(LVHT_ONITEM & hti.flags))
  1493.         iItem = -1;
  1494.     return iItem;
  1495. }
  1496. LRESULT
  1497. OnNotify(
  1498.     HWND hwnd,
  1499.     WPARAM wParam,
  1500.     LPARAM lParam
  1501.     )
  1502. {
  1503.     LPNMLISTVIEW pnmlv = (LPNMLISTVIEW)lParam;
  1504.     switch(pnmlv->hdr.code)
  1505.     {
  1506.         case LVN_ITEMCHANGED:
  1507.             if (LVIS_FOCUSED & pnmlv->uNewState)
  1508.             {
  1509.                 //
  1510.                 // New item has focus.
  1511.                 // Set the global "current" topic pointer and force and update
  1512.                 // of the topic image and image caption to pick up the new
  1513.                 // image and text.
  1514.                 //
  1515.                 PTG.pICurrentTopic = (PSETTINGS_FOLDER_TOPIC)pnmlv->lParam;
  1516.                 InvalidateRect(GetDlgItem(hwnd, IDC_BMP_IMAGE), NULL, TRUE);
  1517.                 UpdateWindow(GetDlgItem(hwnd, IDC_BMP_IMAGE));
  1518.                 //
  1519.                 // Set the new topic image caption.
  1520.                 //
  1521.                 LPCTSTR pszText;
  1522.                 PTG.pICurrentTopic->GetCaption(pszText);
  1523.                 SetWindowText(GetDlgItem(hwnd, IDC_TXT_IMAGE), pszText);
  1524.             }
  1525.             break;
  1526.         case LVN_ITEMACTIVATE:
  1527.             //
  1528.             // Item was selected in the listview.
  1529.             // Tell the current topic to do it's thing.
  1530.             //
  1531.             PTG.bWaitCursor = TRUE;
  1532.             SendMessage(hwnd, WM_SETCURSOR, 0, 0);
  1533.             PTG.pICurrentTopic->TopicSelected();
  1534.             PTG.bWaitCursor = FALSE;
  1535.             break;
  1536.         default:
  1537.             break;
  1538.     }
  1539.     return 0;
  1540. }
  1541. LRESULT 
  1542. OnSetCursor(
  1543.     HWND hwnd, 
  1544.     WPARAM wParam, 
  1545.     LPARAM lParam
  1546.     )
  1547. {
  1548.     LRESULT lResult  = 0;
  1549.     INT iItemHit     = ListViewItemHit();
  1550.     INT iLastItemHit = PTG.iLastItemHit;
  1551.     if (iItemHit != iLastItemHit)
  1552.     {
  1553.         //
  1554.         // Mouse is over a new item or has left the list view.
  1555.         // Set the new "last item hit" and update both items.
  1556.         // Order of statement execution is important here.
  1557.         //
  1558.         HWND hwndLV = PTG.hwndTopicList;
  1559.         RECT rc;
  1560.         ListView_GetItemRect(hwndLV, iLastItemHit, &rc, LVIR_BOUNDS);
  1561.         PTG.iLastItemHit = iItemHit;
  1562.         InvalidateRect(hwndLV, &rc, FALSE);
  1563.         if (-1 != iItemHit)
  1564.         {
  1565.             //
  1566.             // Currently over an item, update it to display the underline
  1567.             // font.
  1568.             //
  1569.             ListView_GetItemRect(hwndLV, iItemHit, &rc, LVIR_BOUNDS);
  1570.             InvalidateRect(hwndLV, &rc, FALSE);
  1571.         }
  1572.     }
  1573.     if (PTG.bWaitCursor)
  1574.     {
  1575.         SetCursor(PTG.hcursorWait);
  1576.         lResult = 1;
  1577.     }
  1578.     else if (-1 != iItemHit && PTG.bListviewSingleClick)
  1579.     {
  1580.         SetCursor(PTG.hcursorHand);
  1581.         lResult = 1;
  1582.     }
  1583.     return lResult;
  1584. }
  1585. LRESULT
  1586. OnQueryNewPalette(
  1587.     HWND hwnd,
  1588.     WPARAM wParam,
  1589.     LPARAM lParam
  1590.     )
  1591. {
  1592.     HPALETTE hOldPal;
  1593.     INT i = 0;
  1594.     //
  1595.     // Re-realize the watermark palette in the foreground and the
  1596.     // the old palette in the background.
  1597.     //
  1598.     CDC dc(hwnd);
  1599.     hOldPal = SelectPalette(dc, (HPALETTE)PTG.dibBanner, FALSE);
  1600.     i       = RealizePalette(dc);
  1601.     SelectPalette(dc, hOldPal, TRUE);
  1602.     RealizePalette(dc);
  1603.     if (0 == i)
  1604.         SendMessage(hwnd, WM_PALETTECHANGED, (WPARAM)hwnd, NULL);
  1605.     else
  1606.     {
  1607.         InvalidateRect(GetDlgItem(hwnd, IDC_BMP_IMAGE), NULL, TRUE);
  1608.         InvalidateRect(GetDlgItem(hwnd, IDC_BMP_BANNER), NULL, TRUE);
  1609.     }
  1610.     return i;
  1611. }
  1612. LRESULT
  1613. OnPaletteChanged(
  1614.     HWND hwnd,
  1615.     WPARAM wParam,
  1616.     LPARAM lParam
  1617.     )
  1618. {
  1619.     Assert(NULL != hwnd);
  1620.     HPALETTE hOldPal = NULL;
  1621.     CDC dc(hwnd);
  1622.     INT i;
  1623.     if ((HWND)wParam != hwnd)
  1624.     {
  1625.         //
  1626.         // Our window didn't cause the change.  OK to realize the
  1627.         // watermark palette.
  1628.         //
  1629.         hOldPal = SelectPalette(dc, (HPALETTE)PTG.dibBanner, TRUE);
  1630.         RealizePalette(dc);
  1631.     }
  1632.     //
  1633.     // Realize the topic menu spot palettes.
  1634.     //
  1635.     HPALETTE hPalTemp = SelectPalette(dc, (HPALETTE)PTG.rgdibTopicSpot[0], TRUE);
  1636.     if (NULL == hOldPal)
  1637.         hOldPal = hPalTemp;
  1638.     i = RealizePalette(dc);
  1639.     if (0 < i)
  1640.         InvalidateRect(PTG.hwndTopicList, NULL, TRUE);
  1641.     //
  1642.     // Notify all of the topic objects about the palette change.
  1643.     // They'll need to realize any palettes they use.
  1644.     //
  1645.     if (NotifyTopicsOfPaletteChange(hwnd))
  1646.     {
  1647.         //
  1648.         // System palette changed while realizing topic image palettes.
  1649.         // Repaint the image.
  1650.         //
  1651.         InvalidateRect(GetDlgItem(hwnd, IDC_BMP_BANNER), NULL, TRUE);
  1652.         InvalidateRect(GetDlgItem(hwnd, IDC_BMP_IMAGE), NULL, TRUE);
  1653.     }
  1654.     //
  1655.     // Update any areas invalidated by palette realization.
  1656.     //
  1657.     UpdateWindow(hwnd);
  1658.     if (NULL != hOldPal)
  1659.     {
  1660.         //
  1661.         // Restore original palette in DC.
  1662.         //
  1663.         SelectPalette(dc, hOldPal, TRUE);
  1664.         RealizePalette(dc);
  1665.     }
  1666.     return 0;
  1667. }
  1668. BOOL
  1669. NotifyTopicsOfPaletteChange(
  1670.     HWND hwnd
  1671.     )
  1672. {
  1673.     INT cTopics;
  1674.     PSETTINGS_FOLDER_TOPIC pITopic = NULL;
  1675.     BOOL fSysPalChanged = FALSE;
  1676.     cTopics = PTG.Folder.GetTopicCount();
  1677.     //
  1678.     // Now notify and release the topic objects.
  1679.     //
  1680.     for (INT i = 0; i < cTopics; i++)
  1681.     {
  1682.         PSETTINGS_FOLDER_TOPIC pITopic = PTG.Folder.GetTopic(i);
  1683.         if (NULL != pITopic)
  1684.         {
  1685.             HRESULT hResult = pITopic->PaletteChanged(hwnd);
  1686.             if (!fSysPalChanged && S_OK == hResult)
  1687.                  fSysPalChanged = TRUE;
  1688.             pITopic->Release();
  1689.         }
  1690.     }
  1691.     //
  1692.     // Tell the caller if the system palette changed.
  1693.     //
  1694.     return fSysPalChanged;
  1695. }