tray.c
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 316k
Category:

Windows Kernel

Development Platform:

Visual C++

  1. #include "cabinet.h"
  2. #include "cabwnd.h"
  3. #include "rcids.h"
  4. #include <shellapi.h>
  5. #include <shlapip.h>
  6. #include "trayclok.h"
  7. #include <fsmenu.h>
  8. #include <shguidp.h>
  9. #include "traynot.h"
  10. #include <help.h>       // help ids
  11. #include <desktray.h>
  12. #if defined(FE_IME)
  13. #include <immp.h>
  14. #endif
  15. #include <trayp.h>
  16. #include <regstr.h>
  17. #ifdef WINNT
  18. // NT APM support
  19. #include <ntddapmt.h>
  20. #else
  21. // Win95 APM support
  22. #include <vmm.h>
  23. #include <bios.h>
  24. #define NOPOWERSTATUSDEFINES
  25. #include <pwrioctl.h>
  26. #include <wshioctl.h>
  27. #include <dbt.h>
  28. #include <pbt.h>
  29. #define Not_VxD
  30. #define No_CM_Calls
  31. #include <configmg.h>
  32. #endif
  33. #include "bandsite.h"
  34. #include "mmhelper.h" // Multimonitor helper functions
  35. #include <shdguid.h>
  36. #include "startmnu.h"
  37. #include "apithk.h"
  38. #include "uemapp.h"
  39. #include "deskconf.h"
  40. #define DM_FOCUS        0           // focus
  41. #define DM_SHUTDOWN     TF_TRAY     // shutdown
  42. #define DM_UEMTRACE     TF_TRAY     // timer service, other UEM stuff
  43. #define DM_MISC         0           // miscellany
  44. #define POLLINTERVAL    (15*1000)       // 15 seconds
  45. #define MINPOLLINTERVAL (3*1000)        // 3 seconds
  46. #define ERRTIMEOUT      (5*1000)        // 5 seconds
  47. #define SECOND 1000
  48. #define RUNWAITSECS 5
  49. #define DOCKSTATE_DOCKED            0
  50. #define DOCKSTATE_UNDOCKED          1
  51. #define DOCKSTATE_UNKNOWN           2
  52. #ifdef WINNT
  53. #define MAXPRINTERBUFFER (MAX_PATH+18+1)
  54. #define JOB_STATUS_ERROR_BITS (JOB_STATUS_USER_INTERVENTION|JOB_STATUS_ERROR)
  55. #else
  56. #define MAXPRINTERBUFFER 32
  57. #define JOB_STATUS_ERROR_BITS JOB_STATUS_USER_INTERVENTION
  58. #endif
  59. #define RDM_ACTIVATE                (WM_USER + 0x101)
  60. // import the WIN31 Compatibility HACKs from the shell32.dll
  61. WINSHELLAPI void WINAPI CheckWinIniForAssocs(void);
  62. HWND v_hwndDesktop = NULL;
  63. #ifdef WINNT
  64. // event to tell the services on NT5 that we are done with boot
  65. // and they can do their stuff
  66. HANDLE g_hShellReadyEvent = NULL;
  67. #endif
  68. UINT g_uStartButtonAllowPopup = WM_NULL;
  69. UINT g_uStartButtonBalloonTip = WM_NULL;
  70. // Users and Passwords must send this message to get the "real" logged on user to log off.
  71. // This is required since sometimes U&P runs in the context of a different user and logging this
  72. // other user off does no good. See extnetplwiz for the other half of this...-dsheldon.
  73. UINT g_uLogoffUser = WM_NULL;
  74. // DSA haPrinterNames structure:
  75. typedef struct _PRINTERNAME {
  76.     TCHAR szPrinterName[MAXPRINTERBUFFER]; // we know max printer name length is 32
  77.     LPITEMIDLIST pidlPrinter; // relative pidl to printer
  78.     BOOL fInErrorState;
  79. } PRINTERNAME, * LPPRINTERNAME;
  80. void RaiseDesktop();
  81. void PrintNotify_Init(HWND hwnd);
  82. void PrintNotify_AddToQueue(LPHANDLE phaPrinterNames, LPSHELLFOLDER *ppsf, LPCITEMIDLIST pidlPrinter);
  83. BOOL PrintNotify_StartThread(HWND hwnd);
  84. void PrintNotify_HandleFSNotify(HWND hwnd, LPCITEMIDLIST *ppidl, LONG lEvent);
  85. void PrintNotify_Exit(void);
  86. void PrintNotify_IconNotify(HWND hwnd, LPARAM uMsg);
  87. void RevertMenu(HMENU hmenuSub, UINT idMenu);
  88. void _PropagateMessage(HWND hwnd, UINT uMessage, WPARAM wParam, LPARAM lParam);
  89. void Tray_HandleShellServiceObject(WPARAM wParam, LPARAM lParam);
  90. STDMETHODIMP CShellTray_AddViewPropertySheetPages(DWORD dwReserved, LPFNADDPROPSHEETPAGE lpfn, LPARAM lParam);
  91. HMONITOR Tray_GetDisplayRectFromRect(LPRECT prcDisplay, LPCRECT prcIn, UINT uFlags);
  92. HMONITOR Tray_GetDisplayRectFromPoint(LPRECT prcDisplay, POINT pt, UINT uFlags);
  93. void Tray_MakeStuckRect(LPRECT prcStick, LPCRECT prcBound, SIZE size, UINT uStick);
  94. void Tray_ScreenSizeChange(HWND hwnd);
  95. void Tray_SizeWindows();
  96. void Tray_ContextMenu(DWORD dwPos, BOOL fSetTime);
  97. void Tray_DesktopMenu(DWORD dwPos);
  98. void StuckAppChange(HWND hwnd, LPCRECT prcOld, LPCRECT prcNew, BOOL bTray);
  99. void Tray_StuckTrayChange();
  100. void SetWindowStyleBit(HWND hwnd, DWORD dwBit, DWORD dwValue);
  101. void CStartDropTarget_Register();
  102. void CStartDropTarget_Revoke();
  103. void DestroySavedWindowPositions(LPWINDOWPOSITIONS pPositions);
  104. void Tray_ResetZorder();
  105. void Cabinet_InitGlobalMetrics(WPARAM, LPTSTR);
  106. void StartMenuFolder_ContextMenu(DWORD dwPos);
  107. void Tray_HandleSize();
  108. void Tray_HandleSizing(WPARAM code, LPRECT lprc, UINT uStuckPlace);
  109. void MinimizeAll(HWND hwndView);
  110. void Tray_RegisterGlobalHotkeys();
  111. void Tray_UnregisterGlobalHotkeys();
  112. void Tray_HandleGlobalHotkey(WPARAM wParam);
  113. void Tray_Unhide();
  114. void Tray_SetAutoHideTimer();
  115. void Tray_ComputeHiddenRect(LPRECT prc, UINT uStuck);
  116. UINT Tray_GetDockedRect(LPRECT prc, BOOL fMoving);
  117. void Tray_CalcClipCoords(RECT *prcClip, const RECT *prcMonitor, const RECT *prcNew);
  118. void Tray_ClipInternal(const RECT *prcClip);
  119. void Tray_ClipWindow(BOOL fEnableClipping);
  120. UINT Tray_CalcDragPlace(POINT pt);
  121. UINT Tray_RecalcStuckPos(LPRECT prc);
  122. void Tray_AutoHideCollision();
  123. void StartMenu_Build();
  124. int HotkeyList_Restore(HWND hwnd);
  125. LRESULT Tray_RegisterHotkey(HWND hwnd, int i);
  126. void RecordStartButtonSize(HWND hwndStart);
  127. LRESULT Tray_HandleMeasureItem(HWND hwnd, LPMEASUREITEMSTRUCT lpmi);
  128. void Tray_OnDesktopState(LPARAM lParam);
  129. #ifdef DESKBTN
  130. void Tray_CreateDesktopButton();
  131. HWND g_hwndDesktopTB = NULL;
  132. #else
  133. #define Tray_CreateDesktopButton()  0
  134. #define KillConfigDesktopDlg() 0
  135. #define g_hwndDesktopTB NULL
  136. #endif
  137. void ToggleDesktop();
  138. void LowerDesktop();
  139. void DoTrayProperties(INT nStartPage);
  140. void RunStartupApps();
  141. void ClearRecentDocumentsAndMRUStuff(BOOL fBroadcastChange);
  142. void WriteCleanShutdown(DWORD dwValue);
  143. void RefreshStartMenu();
  144. // Review chrisny:  this can be moved into an object easily to handle generic droptarget, dropcursor
  145. // , autoscrool, etc. . .
  146. void _DragEnter(HWND hwndTarget, const POINTL ptStart, IDataObject *pdtObject);
  147. void _DragMove(HWND hwndTarget, const POINTL ptStart);
  148. BOOL SetWindowZorder(HWND hwnd, HWND hwndInsertAfter);
  149. //
  150. // Settings UI entry point types.
  151. //
  152. typedef VOID (WINAPI *PTRAYPROPSHEETCALLBACK)(DWORD nStartPage);
  153. typedef VOID (WINAPI *PSETTINGSUIENTRY)(PTRAYPROPSHEETCALLBACK);
  154. DWORD SettingsUI_ThreadProc(void *pv);
  155. VOID WINAPI SettingsUI_TrayPropSheetCallback(DWORD nStartPage);
  156. extern void Cabinet_RefreshAll(void);
  157. #if defined(DBCS) || defined(FE_IME)
  158. // b#11258-win95d
  159. #if !defined(WINNT)
  160. DWORD WINAPI ImmGetAppIMECompatFlags(DWORD dwThreadID);
  161. #endif
  162. #endif
  163. // Shell perf automation
  164. extern DWORD g_dwShellStartTime;
  165. extern DWORD g_dwShellStopTime;
  166. // appbar stuff
  167. BOOL IAppBarSetAutoHideBar(HWND hwnd, BOOL fAutoHide, UINT uEdge);
  168. void IAppBarActivationChange(HWND hwnd, UINT uEdge);
  169. HWND AppBarGetAutoHideBar(UINT uEdge);
  170. // BOGUS: nuke this (multiple monitors...)
  171. HWND g_hwndAutoHide[ABE_MAX] = { NULL, NULL, NULL, NULL };
  172. extern BOOL g_fUseMerge;
  173. BOOL IsChildOrHWND(HWND hwnd, HWND hwndChild)
  174. {
  175.     return (hwnd == hwndChild || IsChild(hwnd, hwndChild));
  176. }
  177. void ClockCtl_HandleTrayHide(BOOL fHiding)
  178. {
  179.     g_ts.fLastHandleTrayHide = fHiding ? TRUE : FALSE;
  180.     SendMessage(g_ts.hwndNotify, TNM_TRAYHIDE, 0, fHiding);
  181. }
  182. // dyna-res change for multi-config hot/warm-doc
  183. void HandleDisplayChange(int x, int y, BOOL fCritical);
  184. #ifdef WINNT
  185. #define IsDisplayChangeSafe() TRUE
  186. #else
  187. BOOL IsDisplayChangeSafe(void);
  188. #endif
  189. DWORD GetMinDisplayRes(void);
  190. // appbar stuff
  191. void AppBarNotifyAll(HMONITOR hmon, UINT uMsg, HWND hwndExclude, LPARAM lParam);
  192. // Button subclass.
  193. WNDPROC g_ButtonProc = NULL;
  194. // timer IDs
  195. #define IDT_AUTOHIDE            2
  196. #define IDT_AUTOUNHIDE          3
  197. #ifdef DELAYWININICHANGE
  198. #define IDT_DELAYWININICHANGE   5
  199. #endif
  200. #define IDT_DESKTOP             6
  201. #define IDT_PROGRAMS            IDM_PROGRAMS
  202. #define IDT_RECENT              IDM_RECENT
  203. #define IDT_REBUILDMENU         7
  204. #define IDT_HANDLEDELAYBOOTSTUFF 8
  205. #define IDT_REVERTPROGRAMS      9
  206. #define IDT_REVERTRECENT        10
  207. #define IDT_REVERTFAVORITES     11
  208. #define IDT_STARTMENU           12
  209. #define IDT_ENDUNHIDEONTRAYNOTIFY 13
  210. #define IDT_SERVICE0            14
  211. #define IDT_SERVICE1            15
  212. #define IDT_SERVICELAST         IDT_SERVICE1
  213. #define IDT_SAVESETTINGS        17
  214. #define IDT_ENABLEUNDO          18
  215. #define RECTWIDTH(rc)   ((rc).right-(rc).left)
  216. #define RECTHEIGHT(rc)  ((rc).bottom-(rc).top)
  217. #define SZ_RECENTKEY        TEXT("Software\Microsoft\Windows\CurrentVersion\Explorer\RecentDocs\Menu")
  218. #define SZ_STARTMENUKEY     TEXT("Software\Microsoft\Windows\CurrentVersion\Explorer\Start Menu\Menu")
  219. #define REGSTR_EXPLORER_ADVANCED REGSTR_PATH_EXPLORER TEXT("\Advanced")
  220. int g_cyTrayBorders = -1; // the amount of Y difference between the window and client height;
  221. //
  222. // amount of time to show/hide the tray
  223. // to turn sliding off set these to 0
  224. //
  225. int g_dtSlideHide;
  226. int g_dtSlideShow;
  227. typedef enum
  228. {
  229.     // Sequence commands
  230.     SMCT_NONE                   = 0x0,
  231.     SMCT_PRIMARY                = 0x1,
  232.     SMCT_WININIASSOCS           = 0x2,
  233.     SMCT_DESKTOPHOTKEYS         = 0x3,
  234.     SMCT_INITPROGRAMS           = 0x4,
  235.     SMCT_INITRECENT             = 0x5,
  236.     SMCT_INITFAVORITES          = 0x6,
  237.     SMCT_PARTFILLPROGRAMS       = 0x7,
  238.     SMCT_FILLRECENT             = 0x8,
  239.     SMCT_FILLPROGRAMS           = 0x9,
  240.     SMCT_FILLFASTITEMS          = 0xa,
  241.     SMCT_FILLFAVORITES          = 0xb,
  242.     SMCT_BUILDLISTOFPATHS       = 0xc,  // Must be last item in list before SMCT_DONE
  243.     SMCT_DONE                   = 0xd,
  244.     // Single execution commands
  245.     SMCT_FILLRECENTONLY         = 0xe,
  246.     SMCT_FILLPROGRAMSONLY       = 0xf,
  247.     SMCT_DESKTOPHOTKEYSONLY     = 0x10,
  248.     SMCT_FILLFAVORITESONLY      = 0x11,
  249.     SMCT_STOP                   = 0x12,
  250.     SMCT_RESTART                = 0x13,
  251.     SMCT_STOPNOWAITBLOP         = 0x14, // Stop, but don't wait for BuildListOfPaths to complete
  252. } STARTMENUCONTROLTHREAD;
  253. // INSTRUMENTATION WARNING: If you change anything here, make sure to update instrument.c
  254. // we need to start at 500 because we're now sharing the hotkey handler
  255. // with shortcuts..  they use an index array so they need to be 0 based
  256. // NOTE, this constant is also in desktop.cpp, so that we can forward hotkeys from the desktop for
  257. // NOTE, app compatibility.
  258. #define GHID_FIRST 500
  259. enum
  260. {
  261.     GHID_RUN = GHID_FIRST,
  262.     GHID_MINIMIZEALL,
  263.     GHID_UNMINIMIZEALL,
  264.     GHID_HELP,
  265.     GHID_EXPLORER,
  266.     GHID_FINDFILES,
  267.     GHID_FINDCOMPUTER,
  268.     GHID_TASKTAB,
  269.     GHID_TASKSHIFTTAB,
  270.     GHID_SYSPROPERTIES,
  271.     GHID_DESKTOP,
  272.     GHID_MAX
  273. };
  274. const DWORD GlobalKeylist[] =
  275. { MAKELONG(TEXT('R'), MOD_WIN),
  276.       MAKELONG(TEXT('M'), MOD_WIN),
  277.       MAKELONG(TEXT('M'), MOD_SHIFT|MOD_WIN),
  278.       MAKELONG(VK_F1,MOD_WIN),
  279.       MAKELONG(TEXT('E'),MOD_WIN),
  280.       MAKELONG(TEXT('F'),MOD_WIN),
  281.       MAKELONG(TEXT('F'), MOD_CONTROL|MOD_WIN),
  282.       MAKELONG(VK_TAB, MOD_WIN),
  283.       MAKELONG(VK_TAB, MOD_WIN|MOD_SHIFT),
  284.       MAKELONG(VK_PAUSE,MOD_WIN),
  285.       MAKELONG(TEXT('D'),MOD_WIN),
  286. };
  287. void DoExitWindows(HWND hwnd);
  288. void Tray_Command(UINT idCmd);
  289. LONG Tray_SetAutoHideState(BOOL fAutoHide);
  290. LRESULT CALLBACK Tray_WndProc(HWND, UINT, WPARAM, LPARAM);
  291. //---------------------------------------------------------------------------
  292. // Global to this file only.
  293. // NB None of the tray stuff needs thread serialising because
  294. // we only ever have one tray.
  295. TRAYSTUFF g_ts = {0};
  296. // g_fDockingFlags bits
  297. #define DOCKFLAG_WARMEJECTABLENOW       (1<<0)
  298. // TVSD Flags.
  299. #define TVSD_NULL               0x0000
  300. #define TVSD_AUTOHIDE           0x0001
  301. #define TVSD_TOPMOST            0x0002
  302. #define TVSD_SMSMALLICONS       0x0004
  303. #define TVSD_HIDECLOCK          0x0008
  304. #define TVSD_SHOWDESKBTN        0x0010
  305. // old Win95 TVSD struct
  306. typedef struct _TVSD95
  307. {
  308.     DWORD   dwSize;
  309.     LONG    cxScreen;
  310.     LONG    cyScreen;
  311.     LONG    dxLeft;
  312.     LONG    dxRight;
  313.     LONG    dyTop;
  314.     LONG    dyBottom;
  315.     DWORD   uAutoHide;
  316.     RECTL   rcAutoHide;
  317.     DWORD   uStuckPlace;
  318.     DWORD   dwFlags;
  319. } TVSD95;
  320. // Nashville tray save data
  321. typedef struct _TVSD
  322. {
  323.     DWORD   dwSize;
  324.     LONG    lSignature;     // signature (must be negative)
  325.     DWORD   dwFlags;        // TVSD_ flags
  326.     DWORD   uStuckPlace;    // current stuck edge
  327.     SIZE    sStuckWidths;   // widths of stuck rects (BUGBUG: in tbd units)
  328.     RECT    rcLastStuck;    // last stuck position in pixels
  329. } TVSD;
  330. // convenient union for reading either
  331. typedef union _TVSDCOMPAT
  332. {
  333.     TVSD;           // new format
  334.     TVSD95 w95;     // old format
  335. } TVSDCOMPAT;
  336. #define TVSDSIG_CURRENT     (-1L)
  337. #define IS_CURRENT_TVSD(t)  ((t.dwSize >= sizeof(TVSD)) && (t.lSignature < 0))
  338. #define MAYBE_WIN95_TVSD(t) (t.dwSize == sizeof(TVSD95))
  339. BOOL ShouldWeShowTheStartButtonBalloon()
  340. {
  341.     DWORD dwType;
  342.     DWORD dwData = 0;
  343.     DWORD cbSize = sizeof(DWORD);
  344.     SHGetValue(HKEY_CURRENT_USER, REGSTR_EXPLORER_ADVANCED, 
  345.             TEXT("StartButtonBalloonTip"), &dwType, (BYTE*)&dwData, &cbSize);
  346.     return (dwData == 0);       // if StartButtonBalloonTip == 1, don't show
  347. }
  348. void DontShowTheStartButtonBalloonAnyMore()
  349. {
  350.     DWORD dwData = 1;
  351.     SHSetValue(HKEY_CURRENT_USER, REGSTR_EXPLORER_ADVANCED, 
  352.         TEXT("StartButtonBalloonTip"), REG_DWORD, (BYTE*)&dwData, sizeof(DWORD));
  353. }
  354. void DestroyStartButtonBalloon()
  355. {
  356.     if (g_ts.hwndStartBalloon)
  357.     {
  358.         DestroyWindow(g_ts.hwndStartBalloon);
  359.         g_ts.hwndStartBalloon = NULL;
  360.     }
  361. }
  362. void ShowStartButtonToolTip()
  363. {
  364.     if (!ShouldWeShowTheStartButtonBalloon())
  365.         return;
  366.     if (!g_ts.hwndStartBalloon)
  367.     {
  368.         g_ts.hwndStartBalloon = CreateWindow(TOOLTIPS_CLASS, NULL,
  369.                                              WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP | TTS_BALLOON,
  370.                                              CW_USEDEFAULT, CW_USEDEFAULT,
  371.                                              CW_USEDEFAULT, CW_USEDEFAULT,
  372.                                              NULL, NULL, hinstCabinet,
  373.                                              NULL);
  374.         if (g_ts.hwndStartBalloon) 
  375.         {
  376.             // set the version so we can have non buggy mouse event forwarding
  377.             SendMessage(g_ts.hwndStartBalloon, CCM_SETVERSION, COMCTL32_VERSION, 0);
  378.             SendMessage(g_ts.hwndStartBalloon, TTM_SETMAXTIPWIDTH, 0, (LPARAM)300);
  379.         }
  380.     }
  381.     if (g_ts.hwndStartBalloon)
  382.     {
  383.         TCHAR szTip[MAX_PATH];
  384.         szTip[0] = TEXT('');
  385.         LoadString(hinstCabinet, IDS_STARTMENUBALLOON_TIP, szTip, ARRAYSIZE(szTip));
  386.         if (szTip[0])
  387.         {
  388.             RECT rc;
  389.             TOOLINFO ti = {0};
  390.             ti.cbSize = SIZEOF(ti);
  391.             ti.uFlags = TTF_IDISHWND | TTF_TRACK | TTF_TRANSPARENT;
  392.             ti.hwnd = v_hwndTray;
  393.             ti.uId = (UINT_PTR)g_ts.hwndStart;
  394.             //ti.lpszText = NULL;
  395.             SendMessage(g_ts.hwndStartBalloon, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&ti);
  396.             SendMessage(g_ts.hwndStartBalloon, TTM_TRACKACTIVATE, (WPARAM)FALSE, (LPARAM)0);
  397.             ti.lpszText = szTip;
  398.             SendMessage(g_ts.hwndStartBalloon, TTM_UPDATETIPTEXT, 0, (LPARAM)&ti);
  399.             LoadString(hinstCabinet, IDS_STARTMENUBALLOON_TITLE, szTip, ARRAYSIZE(szTip));
  400.             if (szTip[0])
  401.             {
  402.                 SendMessage(g_ts.hwndStartBalloon, TTM_SETTITLE, TTI_INFO, (LPARAM)szTip);
  403.             }
  404.             GetWindowRect(g_ts.hwndStart, &rc);
  405.             SendMessage(g_ts.hwndStartBalloon, TTM_TRACKPOSITION, 0, MAKELONG((rc.left + rc.right)/2, rc.top));
  406.             SetWindowZorder(g_ts.hwndStartBalloon, HWND_TOPMOST);
  407.             SendMessage(g_ts.hwndStartBalloon, TTM_TRACKACTIVATE, (WPARAM)TRUE, (LPARAM)&ti);
  408.         }
  409.     }
  410. }
  411. // Mirror a bitmap in a DC (mainly a text object in a DC)
  412. //
  413. // [samera]
  414. //
  415. void MirrorBitmapInDC( HDC hdc , HBITMAP hbmOrig )
  416. {
  417.   HDC     hdcMem;
  418.   HBITMAP hbm;
  419.   BITMAP  bm;
  420.   if( !GetObject( hbmOrig , sizeof(BITMAP) , &bm ))
  421.     return;
  422.   hdcMem = CreateCompatibleDC( hdc );
  423.   if( !hdcMem )
  424.     return;
  425.   hbm = CreateCompatibleBitmap( hdc , bm.bmWidth , bm.bmHeight );
  426.   if( !hbm )
  427.   {
  428.     DeleteDC( hdcMem );
  429.     return;
  430.   }
  431.   //
  432.   // Flip the bitmap
  433.   //
  434.   SelectObject( hdcMem , hbm );
  435.   SET_DC_RTL_MIRRORED(hdcMem);
  436.   BitBlt( hdcMem , 0 , 0 , bm.bmWidth , bm.bmHeight ,
  437.           hdc , 0 , 0 , SRCCOPY );
  438.   SET_DC_LAYOUT(hdcMem,0);
  439.   //
  440.   // BUGBUG : The offset by 1 in hdcMem is to solve the off-by-one problem. Removed.
  441.   // [samera]
  442.   //
  443.   BitBlt( hdc , 0 , 0 , bm.bmWidth , bm.bmHeight ,
  444.           hdcMem , 0 , 0 , SRCCOPY );
  445.   DeleteDC( hdcMem );
  446.   DeleteObject( hbm );
  447.   return;
  448. }
  449. BOOL Tray_CreateClockWindow()
  450. {
  451.     g_ts.hwndNotify = TrayNotifyCreate(v_hwndTray, IDC_CLOCK, hinstCabinet);
  452.     return BOOLFROMPTR(g_ts.hwndNotify);
  453. }
  454. BOOL InitTrayClass(HINSTANCE hInstance)
  455. {
  456.     WNDCLASS wc;
  457.     ZeroMemory(&wc, SIZEOF(WNDCLASS));
  458.     wc.lpszClassName = TEXT(WNDCLASS_TRAYNOTIFY);
  459.     wc.style = CS_DBLCLKS;
  460.     wc.lpfnWndProc = Tray_WndProc;
  461.     wc.hInstance = hInstance;
  462.     wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  463.     wc.hbrBackground = (HBRUSH)(COLOR_3DFACE+1);
  464.     return RegisterClass(&wc);
  465. }
  466. HMENU LoadMenuPopup(LPCTSTR id)
  467. {
  468.     HMENU hMenuSub = NULL;
  469.     HMENU hMenu = LoadMenu(hinstCabinet, id);
  470.     if (hMenu) {
  471.         hMenuSub = GetSubMenu(hMenu, 0);
  472.         if (hMenuSub) {
  473.             RemoveMenu(hMenu, 0, MF_BYPOSITION);
  474.         }
  475.         DestroyMenu(hMenu);
  476.     }
  477.     return hMenuSub;
  478. }
  479. #define CXGAP 4
  480. //----------------------------------------------------------------------------
  481. // Compose the Start bitmap out of a flag and some text.
  482. // REVIEW UNDONE - Put the up/down arrow back in.
  483. HBITMAP CreateStartBitmap(HWND hwndTray)
  484. {
  485.     HBITMAP hbmpStart = NULL;
  486.     HBITMAP hbmpStartOld = NULL;
  487.     HICON hiconFlag = NULL;
  488.     int cx, cy, cySmIcon;
  489.     TCHAR szStart[256];
  490.     SIZE size;
  491.     HDC hdcStart;
  492.     HDC hdcScreen;
  493.     HFONT hfontStart = NULL;
  494.     HFONT hfontStartOld = NULL;
  495.     NONCLIENTMETRICS ncm;
  496.     RECT rcStart;
  497.     WORD wLang;
  498.     // DebugMsg(DM_TRACE, "c.csb: Creating start bitmap.");
  499.     hdcScreen = GetDC(NULL);
  500.     hdcStart = CreateCompatibleDC(hdcScreen);
  501.     if (hdcStart)
  502.     {
  503.         ncm.cbSize = SIZEOF(ncm);
  504.         if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, SIZEOF(ncm), &ncm, FALSE))
  505.         {
  506.             wLang = GetUserDefaultLangID();
  507.             // Select normal weight font for chinese language.
  508.             if ( PRIMARYLANGID(wLang) == LANG_CHINESE &&
  509.                ( (SUBLANGID(wLang) == SUBLANG_CHINESE_TRADITIONAL) ||
  510.                  (SUBLANGID(wLang) == SUBLANG_CHINESE_SIMPLIFIED) ))
  511.                 ncm.lfCaptionFont.lfWeight = FW_NORMAL;
  512.             else
  513.                 ncm.lfCaptionFont.lfWeight = FW_BOLD;
  514.             hfontStart = CreateFontIndirect(&ncm.lfCaptionFont);
  515.         }
  516.         else
  517.         {
  518.             DebugMsg(DM_ERROR, TEXT("c.csb: Can't create font."));
  519.         }
  520.         // Get an idea about how big we need everyhting to be.
  521.         LoadString(hinstCabinet, IDS_START, szStart, ARRAYSIZE(szStart));
  522.         hfontStartOld = SelectObject(hdcScreen, hfontStart);
  523.         GetTextExtentPoint(hdcScreen, szStart, lstrlen(szStart), &size);
  524.         SelectObject(hdcScreen, hfontStartOld);
  525. // Mimick the tray and ignore the font size for determining the height.
  526. #if 0
  527.         cy = max(g_cySize, size.cy);
  528. #else
  529.         cy = g_cySize;
  530. #endif
  531.         cySmIcon = GetSystemMetrics(SM_CYSMICON);
  532.         cx = (cy + size.cx) + CXGAP;
  533.         hbmpStart = CreateCompatibleBitmap(hdcScreen, cx, cy);
  534.         hbmpStartOld = SelectObject(hdcStart, hbmpStart);
  535.         hfontStartOld = SelectObject(hdcStart , hfontStart);
  536.         //
  537.         // Let's mirror the DC, so that text won't get mirrored
  538.         // if the window ir mirrored
  539.         //
  540.         if (IS_WINDOW_RTL_MIRRORED(hwndTray))
  541.         {
  542.             //
  543.             // Mirror the DC, so that drawing goes from the visual right
  544.             // edge. [samera]
  545.             //
  546.             SET_DC_RTL_MIRRORED(hdcStart);
  547.         }
  548.         rcStart.left = -g_cxEdge; // subtract this off because drawcaptiontemp adds it on
  549.         rcStart.top = 0;
  550.         rcStart.right = cx;
  551.         rcStart.bottom = cy;
  552.         hiconFlag = (HICON)LoadImage(NULL, MAKEINTRESOURCE(OIC_WINLOGO_DEFAULT ), IMAGE_ICON, cySmIcon, cySmIcon, 0);
  553.         // Get User to draw everything for us.
  554.         DrawCaptionTemp(hwndTray, hdcStart, &rcStart, hfontStart, hiconFlag, szStart, DC_INBUTTON | DC_TEXT | DC_ICON | DC_NOSENDMSG);
  555.         //
  556.         // Now we have the image ready to maintained by USER, let's mirror
  557.         // it so that when it is bitblt'ed to the hwndTray DC it will be mirrored
  558.         // and the bitmap image is maintained. [samera]
  559.         //
  560.         if (IS_WINDOW_RTL_MIRRORED(hwndTray))
  561.         {
  562.             MirrorBitmapInDC(hdcStart, hbmpStart);
  563.         }
  564.         // Clean up Start stuff.
  565.         SelectObject(hdcStart, hbmpStartOld);
  566.         if (hfontStart)
  567.         {
  568.             SelectObject(hdcStart, hfontStartOld);
  569.             DeleteObject(hfontStart);
  570.         }
  571.         DeleteDC(hdcStart);
  572.         DestroyIcon(hiconFlag);
  573.     }
  574.     else
  575.     {
  576.         DebugMsg(DM_ERROR, TEXT("c.csb: Can't create Start bitmap."));
  577.     }
  578.     ReleaseDC(NULL, hdcScreen);
  579.     return hbmpStart;
  580. }
  581. // Set the stuck monitor for the tray window
  582. void Tray_SetStuckMonitor()
  583. {
  584.     // use STICK_LEFT because most of the multi-monitors systems are set up
  585.     // side by side. use DEFAULTTONULL because we don't want to get the wrong one
  586.     // use the center point to call again in case we failed the first time.
  587.     g_ts.hmonStuck = MonitorFromRect(&g_ts.arStuckRects[STICK_LEFT],
  588.                                      MONITOR_DEFAULTTONULL);
  589.     if (!g_ts.hmonStuck)
  590.     {
  591.         POINT pt;
  592.         pt.x = (g_ts.arStuckRects[STICK_LEFT].left + g_ts.arStuckRects[STICK_LEFT].right)/2;
  593.         pt.y = (g_ts.arStuckRects[STICK_LEFT].top + g_ts.arStuckRects[STICK_LEFT].bottom)/2;
  594.         g_ts.hmonStuck = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
  595.     }
  596.     g_ts.hmonOld = g_ts.hmonStuck;
  597. }
  598. VOID Tray_GetSaveStateAndInitRects()
  599. {
  600.     TVSDCOMPAT tvsd;
  601.     DWORD cbData1, cbData2;
  602.     RECT rcDisplay;
  603.     DWORD dwTrayFlags;
  604.     UINT uStick;
  605.     SIZE size;
  606.     //
  607.     // first fill in the defaults
  608.     //
  609.     SetRect(&rcDisplay, 0, 0, g_cxPrimaryDisplay, g_cyPrimaryDisplay);
  610.     // size gets defaults
  611.     size.cx = g_ts.sizeStart.cx + 2 * (g_cxDlgFrame + g_cxBorder);
  612.     size.cy = g_ts.sizeStart.cy + 2 * (g_cyDlgFrame + g_cyBorder);
  613.     // sStuckWidths gets minimum
  614.     g_ts.sStuckWidths.cx = 2 * (g_cxDlgFrame + g_cxBorder);
  615.     g_ts.sStuckWidths.cy = g_ts.sizeStart.cy + 2 * (g_cyDlgFrame + g_cyBorder);
  616.     g_ts.uStuckPlace = STICK_BOTTOM;
  617.     dwTrayFlags = TVSD_TOPMOST;
  618. #ifdef WINNT
  619.     //  if we are on a remote hydra session
  620.     //  and if there is no previous saved value,
  621.     //  do not display the clock.
  622.     if (IsRemoteSession())
  623.     {
  624.         g_ts.fHideClock = TRUE;
  625.         dwTrayFlags |= TVSD_HIDECLOCK;
  626.     }
  627. #endif
  628.     g_ts.uAutoHide = 0;
  629.     //
  630.     // now try to load saved vaules
  631.     //
  632.     
  633.     // BUG : 231077
  634.     // Since Tasbar properties don't roam from NT5 to NT4, (NT4 -> NT5 yes) 
  635.     // Allow roaming from NT4 to NT5 only for the first time the User logs
  636.     // on to NT5, so that future changes to NT5 are not lost when the user
  637.     // logs on to NT4 after customizing the taskbar properties on NT5.
  638.     cbData1 = SIZEOF(tvsd);
  639.     cbData2 = SIZEOF(tvsd);
  640.     if (Reg_GetStruct(g_hkeyExplorer, TEXT("StuckRects2"), TEXT("Settings"), 
  641.         &tvsd, &cbData1) 
  642.         ||
  643.         Reg_GetStruct(g_hkeyExplorer, TEXT("StuckRects"), TEXT("Settings"),
  644.         &tvsd, &cbData2))
  645.     {
  646.         if (IS_CURRENT_TVSD(tvsd) && IsValidSTUCKPLACE(tvsd.uStuckPlace))
  647.         {
  648.             Tray_GetDisplayRectFromRect(&rcDisplay, &tvsd.rcLastStuck,
  649.                 MONITOR_DEFAULTTONEAREST);
  650.             // BUGBUG: scale sStuckWidths.cy here?
  651.             size = tvsd.sStuckWidths;
  652.             g_ts.uStuckPlace = tvsd.uStuckPlace;
  653.             dwTrayFlags = tvsd.dwFlags;
  654.         }
  655.         else if (MAYBE_WIN95_TVSD(tvsd) &&
  656.                  IsValidSTUCKPLACE(tvsd.w95.uStuckPlace))
  657.         {
  658.             g_ts.uStuckPlace = tvsd.w95.uStuckPlace;
  659.             dwTrayFlags = tvsd.w95.dwFlags;
  660.             if (tvsd.w95.uAutoHide & AH_ON)
  661.                 dwTrayFlags |= TVSD_AUTOHIDE;
  662.             switch (g_ts.uStuckPlace)
  663.             {
  664.             case STICK_LEFT:
  665.                 size.cx = tvsd.w95.dxLeft;
  666.                 break;
  667.             case STICK_RIGHT:
  668.                 size.cx = tvsd.w95.dxRight;
  669.                 break;
  670.             case STICK_BOTTOM:
  671.                 size.cy = tvsd.w95.dyBottom;
  672.                 break;
  673.             case STICK_TOP:
  674.                 size.cy = tvsd.w95.dyTop;
  675.                 break;
  676.             }
  677.         }
  678.     }
  679.     
  680.     ASSERT(IsValidSTUCKPLACE(g_ts.uStuckPlace));
  681.     //
  682.     // use the size only if it is not bogus
  683.     //
  684.     if (g_ts.sStuckWidths.cx < size.cx)
  685.         g_ts.sStuckWidths.cx = size.cx;
  686.     if (g_ts.sStuckWidths.cy < size.cy)
  687.         g_ts.sStuckWidths.cy = size.cy;
  688.     //
  689.     // set the tray flags
  690.     //
  691.     g_ts.fAlwaysOnTop  = dwTrayFlags & TVSD_TOPMOST      ? 1     : 0;
  692.     g_ts.fSMSmallIcons = dwTrayFlags & TVSD_SMSMALLICONS ? 1     : 0;
  693.     g_ts.fHideClock    = dwTrayFlags & TVSD_HIDECLOCK    ? 1     : 0;
  694.     g_ts.uAutoHide     = dwTrayFlags & TVSD_AUTOHIDE     ? (AH_ON | AH_HIDING) : 0;
  695.     g_ts.fShowDeskBtn  = dwTrayFlags & TVSD_SHOWDESKBTN  ? 1 : 0;
  696.     //
  697.     // initialize stuck rects
  698.     //
  699.     for (uStick = STICK_LEFT; uStick <= STICK_BOTTOM; uStick++)
  700.         Tray_MakeStuckRect(&g_ts.arStuckRects[uStick], &rcDisplay, g_ts.sStuckWidths, uStick);
  701.     // Determine which monitor the tray is on using its stuck rectangles
  702.     Tray_SetStuckMonitor();
  703. }
  704. extern HRESULT Tray_SaveView();
  705. void _SaveTrayStuff(void)
  706. {
  707.     TVSD tvsd;
  708.     tvsd.dwSize = SIZEOF(tvsd);
  709.     tvsd.lSignature = TVSDSIG_CURRENT;
  710.     // position
  711.     CopyRect(&tvsd.rcLastStuck, &g_ts.arStuckRects[g_ts.uStuckPlace]);
  712.     tvsd.sStuckWidths = g_ts.sStuckWidths;
  713.     tvsd.uStuckPlace = g_ts.uStuckPlace;
  714.     tvsd.dwFlags = 0;
  715.     if (g_ts.fAlwaysOnTop)      tvsd.dwFlags |= TVSD_TOPMOST;
  716.     if (g_ts.fSMSmallIcons)     tvsd.dwFlags |= TVSD_SMSMALLICONS;
  717.     if (g_ts.fHideClock)        tvsd.dwFlags |= TVSD_HIDECLOCK;
  718.     if (g_ts.uAutoHide & AH_ON) tvsd.dwFlags |= TVSD_AUTOHIDE;
  719.     if (g_ts.fShowDeskBtn)      tvsd.dwFlags |= TVSD_SHOWDESKBTN;
  720.     // Save for now in Stuck rects.
  721.     // BUGBUG: really want to save rcLastStuck per user/maching/config
  722.     Reg_SetStruct(g_hkeyExplorer, TEXT("StuckRects2"), TEXT("Settings"), &tvsd, SIZEOF(tvsd));
  723.     Tray_SaveView(g_ts.ptbs);
  724.     return;
  725. }
  726. /*------------------------------------------------------------------
  727. ** align toolbar so that buttons are flush with client area
  728. ** and make toolbar's buttons to be MENU style
  729. **------------------------------------------------------------------*/
  730. void Tray_AlignStartButton()
  731. {
  732.     HWND hwndStart = g_ts.hwndStart;
  733.     if (hwndStart)
  734.     {
  735.         RECT rcClient;
  736.         if (g_ts.sizeStart.cy == 0)
  737.         {
  738.             BITMAP bm;
  739.             HBITMAP hbm = (HBITMAP)SendMessage(hwndStart, BM_GETIMAGE, IMAGE_BITMAP, 0);
  740.             if (hbm)
  741.             {
  742.                 GetObject(hbm, SIZEOF(bm), &bm);
  743.                 g_ts.sizeStart.cx = bm.bmWidth  + 2 * g_cxEdge;
  744.                 g_ts.sizeStart.cy = bm.bmHeight + 2 * g_cyEdge;
  745.                 if (g_ts.sizeStart.cy < g_cySize + 2*g_cyEdge)
  746.                     g_ts.sizeStart.cy = g_cySize + 2*g_cyEdge;
  747.             }
  748.             else
  749.             {
  750.                 // BUGBUG: New user may have caused this to fail...
  751.                 // Setup some size for it that wont be too bad...
  752.                 g_ts.sizeStart.cx = g_cxMinimized;
  753.                 g_ts.sizeStart.cy = g_cySize + 2*g_cyEdge;
  754.             }
  755.         }
  756.         GetClientRect(g_ts.hwndMain, &rcClient);
  757.         SetWindowPos(hwndStart, NULL, 0, 0, (rcClient.right < g_ts.sizeStart.cx) ? rcClient.right : g_ts.sizeStart.cx, g_ts.sizeStart. cy, SWP_NOZORDER | SWP_NOACTIVATE);
  758.     }
  759. }
  760. //----------------------------------------------------------------------------
  761. // Allow us to do stuff on a "button-down".
  762. HWND g_hwndPrevFocus = NULL;
  763. LRESULT CALLBACK StartButtonSubclassWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  764. {
  765.     LRESULT lRet;
  766.     static UINT uDown = 0;
  767.     static BOOL fAllowUp = FALSE;       // Is the start button allowed to be in the up position?
  768.     ASSERT(g_ButtonProc)
  769.     // Is the button going down?
  770.     if (uMsg == BM_SETSTATE)
  771.     {
  772.         // Is it going Down?
  773.         if (wParam) {
  774.             // DebugMsg(DM_TRACE, "c.stswp: Set state %d", wParam);
  775.             // Yes, Is it already down?
  776.             if (!uDown)
  777.             {
  778.                 // Nope.
  779.                 INSTRUMENT_STATECHANGE(SHCNFI_STATE_START_DOWN);
  780.                 uDown = 1;
  781.                 // If we are going down, then we do not want to popup again until the Start Menu is collapsed
  782.                 fAllowUp = FALSE;
  783.                 SendMessage(g_ts.hwndTrayTips, TTM_ACTIVATE, FALSE, 0L);
  784.                 // Show the button down.
  785.                 lRet = CallWindowProc(g_ButtonProc, hwnd, uMsg, wParam, lParam );
  786.                 // Notify the parent.
  787.                 SendMessage(GetParent(hwnd), WM_COMMAND, (WPARAM)LOWORD(GetDlgCtrlID(hwnd)), (LPARAM)hwnd);
  788.                 return lRet;
  789.             }
  790.             else
  791.             {
  792.                 // Yep. Do nothing.
  793.                 // fDown = FALSE;
  794.                 return DefWindowProc(hwnd, uMsg, wParam, lParam);
  795.             }
  796.         }
  797.         else
  798.         {
  799.             // DebugMsg(DM_TRACE, "c.stswp: Set state %d", wParam);
  800.             // Nope, buttons coming up.
  801.             // Is it supposed to be down?   Is it not allowed to be up?
  802.             if (uDown == 1 || !fAllowUp)
  803.             {
  804.                 INSTRUMENT_STATECHANGE(SHCNFI_STATE_START_UP);
  805.                 // Yep, do nothing.
  806.                 uDown = 2;
  807.                 return DefWindowProc(hwnd, uMsg, wParam, lParam);
  808.             }
  809.             else
  810.             {
  811.                 SendMessage(g_ts.hwndTrayTips, TTM_ACTIVATE, TRUE, 0L);
  812.                 // Nope, Forward it on.
  813.                 uDown = 0;
  814.                 return CallWindowProc(g_ButtonProc, hwnd, uMsg, wParam, lParam );
  815.             }
  816.         }
  817.     }
  818.     else
  819.     {
  820.         switch (uMsg) {
  821.         case WM_LBUTTONDOWN:
  822.             // The button was clicked on, then we don't need no stink'n focus rect.
  823.             SendMessage(GetParent(hwnd), WM_UPDATEUISTATE, MAKEWPARAM(UIS_SET, 
  824.                 UISF_HIDEFOCUS), 0);
  825.             goto ProcessCapture;
  826.             break;
  827.         case WM_KEYDOWN:
  828.             // The user pressed enter or return or some other bogus key combination when
  829.             // the start button had keyboard focus, so show the rect....
  830.             SendMessage(GetParent(hwnd), WM_UPDATEUISTATE, MAKEWPARAM(UIS_CLEAR, 
  831.                 UISF_HIDEFOCUS), 0);
  832.             if (wParam == VK_RETURN)
  833.                 PostMessage(g_ts.hwndMain, WM_COMMAND, IDC_KBSTART, 0);
  834.             // We do not need the capture, because we do all of our button processing
  835.             // on the button down. In fact taking capture for no good reason screws with
  836.             // drag and drop into the menus. We're overriding user.
  837. ProcessCapture:
  838.             lRet = CallWindowProc(g_ButtonProc, hwnd, uMsg, wParam, lParam );
  839.             SetCapture(NULL);
  840.             return lRet;
  841.             break;
  842.         case WM_MOUSEMOVE:
  843.         {
  844.             MSG msg;
  845.             msg.lParam = lParam;
  846.             msg.wParam = wParam;
  847.             msg.message = uMsg;
  848.             msg.hwnd = hwnd;
  849.             SendMessage(g_ts.hwndTrayTips, TTM_RELAYEVENT, 0, (LPARAM)(LPMSG)& msg);
  850.             break;
  851.         }
  852.         case WM_NULL:
  853.                 break;
  854.         default:
  855.             if (uMsg == g_uStartButtonAllowPopup)
  856.             {
  857.                 fAllowUp = TRUE;
  858.             }
  859.             break;
  860.         }
  861.         return CallWindowProc(g_ButtonProc, hwnd, uMsg, wParam, lParam);
  862.     }
  863. }
  864. const TCHAR c_szButton[] = TEXT("button");
  865. /*------------------------------------------------------------------
  866. ** create the toolbar with the three buttons and align windows
  867. **------------------------------------------------------------------*/
  868. HWND Tray_CreateStartButton()
  869. {
  870.     HWND hwnd;
  871.     DWORD dwStyle = BS_BITMAP;
  872.     g_uStartButtonBalloonTip = RegisterWindowMessage(TEXT("Welcome Finished")); 
  873.     g_uLogoffUser = RegisterWindowMessage(TEXT("Logoff User"));
  874.     // BUGBUG: BS_CENTER | VS_VCENTER required, user bug?
  875.     hwnd = CreateWindowEx(0, c_szButton, NULL,
  876.         WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS |
  877.         BS_PUSHBUTTON | BS_LEFT | BS_VCENTER | dwStyle,
  878.         0, 0, 0, 0, v_hwndTray, (HMENU)IDC_START, hinstCabinet, NULL);
  879.     if (hwnd)
  880.     {
  881.         // Subclass it.
  882.         g_ts.hwndStart = hwnd;
  883.         g_ButtonProc = SubclassWindow(hwnd, StartButtonSubclassWndProc);
  884.         {
  885.             HBITMAP hbm = CreateStartBitmap(v_hwndTray);
  886.             if (hbm)
  887.             {
  888.                 SendMessage(hwnd, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbm);
  889.                 Tray_AlignStartButton();
  890.                 return hwnd;
  891.             }
  892.         }
  893.         DestroyWindow(hwnd);
  894.     }
  895.     return NULL;
  896. }
  897. void Tray_GetWindowSizes(PRECT prcClient, PRECT prcView, PRECT prcClock, PRECT prcDesktop)
  898. {
  899.     int xFSView, yFSView, cxFSView, cyFSView;
  900.     int xClock, yClock, cxClock, cyClock;
  901.     DWORD_PTR dwClockMinSize;
  902.     int cxDesktop;
  903.     int cyDesktop;
  904.     BOOL fDesktopBottom = FALSE;
  905.     RECT rcDesktop;
  906.     if (!g_hwndDesktopTB || !g_ts.fShowDeskBtn || SHRestricted(REST_CLASSICSHELL))
  907.     {
  908.         cxDesktop = cyDesktop = 0;
  909.     }
  910.     else
  911.     {
  912.         SendMessage(g_hwndDesktopTB, TB_GETITEMRECT, 0, (LPARAM)&rcDesktop);
  913.         cxDesktop = RECTWIDTH(rcDesktop) + g_cxEdge;
  914.         cyDesktop = RECTHEIGHT(rcDesktop);
  915.     }
  916.     // size to fill either horizontally or vertically
  917.     if ((prcClient->right - g_ts.sizeStart.cx) > (prcClient->bottom - g_ts.sizeStart.cy))
  918.     {
  919.         //
  920.         // Horizontal - Two cases.  One where we have room below the
  921.         // toolbar to display the clock. so display it there, else
  922.         // display it on the right hand side...
  923.         //
  924.         yFSView = 0;
  925.         cyFSView = prcClient->bottom - yFSView;
  926.         // Recalc the min size for horizontal arrangement
  927.         dwClockMinSize = SendMessage(g_ts.hwndNotify, WM_CALCMINSIZE,
  928.                                      0x7fff, prcClient->bottom);
  929.         cxClock = LOWORD(dwClockMinSize);
  930.         cyClock = HIWORD(dwClockMinSize);
  931.         // xClock = prcClient->right - cxClock - g_cxFrame;  // A little gap...
  932.         xClock = prcClient->right - cxClock - cxDesktop;
  933. #ifdef VCENTER_CLOCK
  934.         yClock = (prcClient->bottom-cyClock)/2;     // Center it vertically.
  935. #else
  936.         yClock = 0;
  937. #endif
  938.         xFSView = g_ts.sizeStart.cx + g_cxFrame + 1; // NT5 VFREEZE: one more pixel here
  939.         cxFSView = xClock - xFSView - g_cxFrame/2; // NT5 VFREEZE: we removed the right etch, so reclaim the space
  940.     }
  941.     else
  942.     {
  943.         // Vertical - Again two cases.  One where we have room to the
  944.         // right to display the clock.  Note: we want some gap here between
  945.         // the clock and toolbar.  If it does not fit, then we will center
  946.         // it at the bottom...
  947.         xFSView = 0;
  948.         cxFSView = prcClient->right - xFSView;
  949.         dwClockMinSize = SendMessage(g_ts.hwndNotify, WM_CALCMINSIZE,
  950.                 0x7fff, g_ts.sizeStart.cy);
  951.         cxClock = LOWORD(dwClockMinSize);
  952.         cyClock = HIWORD(dwClockMinSize);
  953.         if ((g_ts.sizeStart.cx + cxClock + 4 * g_cyTabSpace + cxDesktop)  < prcClient->right)
  954.         {
  955.             int cyMax = g_ts.sizeStart.cy;;
  956.             // Can fit on the same row!
  957.             xClock = prcClient->right - cxClock - (2 * g_cxEdge) - cxDesktop;  // A little gap...
  958.             yClock = 0;
  959.             if (cyClock > cyMax)
  960.                 cyMax = cyClock;
  961.             yFSView = cyMax + g_cyTabSpace;
  962.             cyFSView = prcClient->bottom - yFSView;
  963.         }
  964.         else
  965.         {
  966.             // Nope put at bottom
  967.             // Recalc the min size for vertical arrangement
  968.             dwClockMinSize = SendMessage(g_ts.hwndNotify, WM_CALCMINSIZE,
  969.                 cxFSView, 0x7fff);
  970.             if (LOWORD(dwClockMinSize) + cyDesktop >= prcClient->right)
  971.                 fDesktopBottom = TRUE;
  972.             if (fDesktopBottom) {
  973.                 cxClock = min(LOWORD(dwClockMinSize), prcClient->right);
  974.             } else {
  975.                 cxClock = LOWORD(dwClockMinSize);
  976.             }
  977.             cyClock = HIWORD(dwClockMinSize);
  978.             xClock = (prcClient->right - cxClock) / 2;
  979.             yClock = prcClient->bottom - cyClock - g_cyTabSpace;
  980.             // if we'ren ot slowing the clock, we still need to make
  981.             // room for hte desktop button
  982.             if (!cyClock)
  983.                 yClock -= (cyDesktop - g_cyEdge);
  984.             if (fDesktopBottom) {
  985.                 yClock -= cyDesktop;
  986.             } else {
  987.                 xClock -= (cxDesktop/2);
  988.             }
  989.             yFSView = g_ts.sizeStart.cy + g_cyTabSpace;
  990.             cyFSView = yClock - yFSView - g_cyTabSpace;
  991.         }
  992.     }
  993.     prcView->left = xFSView;
  994.     prcView->top = yFSView;
  995.     prcView->right = xFSView + cxFSView;
  996.     prcView->bottom = yFSView + cyFSView;
  997.     prcClock->left = xClock;
  998.     prcClock->top = yClock;
  999.     prcClock->right = xClock + cxClock;
  1000.     prcClock->bottom = yClock + cyClock;
  1001.     if (fDesktopBottom) {
  1002.         prcDesktop->top = prcClock->bottom + g_cyEdge;
  1003.         prcDesktop->left = (prcClient->right - cxDesktop)/2;
  1004.     } else {
  1005.         *prcDesktop = *prcClock;
  1006.         prcDesktop->left = prcClock->right + g_cxEdge;
  1007.     }
  1008.     prcDesktop->bottom = prcDesktop->top + cyDesktop;
  1009.     prcDesktop->right = prcDesktop->left + cxDesktop + g_cxEdge;
  1010. }
  1011. void Tray_RestoreWindowPos()
  1012. {
  1013.     WINDOWPLACEMENT wp;
  1014.     //first restore the stuck postitions
  1015.     Tray_GetSaveStateAndInitRects();
  1016.     wp.length = SIZEOF(wp);
  1017.     wp.showCmd = SW_HIDE;
  1018.     g_ts.uMoveStuckPlace = (UINT)-1;
  1019.     Tray_GetDockedRect(&wp.rcNormalPosition, FALSE);
  1020.     ClockCtl_HandleTrayHide(g_ts.fHideClock);
  1021.     SetWindowPlacement(v_hwndTray, &wp);
  1022. }
  1023. //----------------------------------------------------------------------------
  1024. // Sort of a registry equivalent of the profile API's.
  1025. BOOL Reg_GetStruct(HKEY hkey, LPCTSTR pszSubKey, LPCTSTR pszValue, void *pData, DWORD *pcbData)
  1026. {
  1027.     BOOL fRet = FALSE;
  1028.  
  1029.     if (!g_fCleanBoot)
  1030.     {
  1031.         fRet = ERROR_SUCCESS == SHGetValue(hkey, pszSubKey, pszValue, NULL, pData, pcbData);
  1032.     }
  1033.     return fRet;
  1034. }
  1035. //----------------------------------------------------------------------------
  1036. // Sort of a registry equivalent of the profile API's.
  1037. BOOL Reg_SetStruct(HKEY hkey, LPCTSTR pszSubKey, LPCTSTR pszValue, void *lpData, DWORD cbData)
  1038. {
  1039.     HKEY hkeyNew = hkey;
  1040.     BOOL fRet = FALSE;
  1041.     if (pszSubKey)
  1042.     {
  1043.         if (RegCreateKey(hkey, pszSubKey, &hkeyNew) != ERROR_SUCCESS)
  1044.         {
  1045.             return fRet;
  1046.         }
  1047.     }
  1048.     if (RegSetValueEx(hkeyNew, pszValue, 0, REG_BINARY, lpData, cbData) == ERROR_SUCCESS)
  1049.     {
  1050.         fRet = TRUE;
  1051.     }
  1052.     if (pszSubKey)
  1053.         RegCloseKey(hkeyNew);
  1054.     return fRet;
  1055. }
  1056. //----------------------------------------------------------------------------
  1057. // Get the display (monitor) rectangle from the given arbitrary point
  1058. HMONITOR Tray_GetDisplayRectFromPoint(LPRECT prcDisplay, POINT pt, UINT uFlags)
  1059. {
  1060.     HMONITOR hmon;
  1061.     RECT rcEmpty = {0};
  1062.     hmon = MonitorFromPoint(pt, uFlags);
  1063.     if (hmon && prcDisplay)
  1064.         GetMonitorRect(hmon, prcDisplay);
  1065.     else if (prcDisplay)
  1066.         *prcDisplay = rcEmpty;
  1067.     return hmon;
  1068. }
  1069. //----------------------------------------------------------------------------
  1070. // Get the display (monitor) rectangle from the given arbitrary rectangle
  1071. HMONITOR Tray_GetDisplayRectFromRect(LPRECT prcDisplay, LPCRECT prcIn,
  1072.     UINT uFlags)
  1073. {
  1074.     HMONITOR hmon;
  1075.     RECT rcEmpty = {0};
  1076.     hmon = MonitorFromRect(prcIn, uFlags);
  1077.     if (hmon && prcDisplay)
  1078.         GetMonitorRect(hmon, prcDisplay);
  1079.     else if (prcDisplay)
  1080.         *prcDisplay = rcEmpty;
  1081.     return hmon;
  1082. }
  1083. //----------------------------------------------------------------------------
  1084. // Get the display (monitor) rectangle where the taskbar is currently on,
  1085. // if that monitor is invalid, get the nearest one.
  1086. void Tray_GetStuckDisplayRect(UINT uStuckPlace, LPRECT prcDisplay)
  1087. {
  1088.     BOOL fValid;
  1089.     ASSERT(prcDisplay);
  1090.     fValid = GetMonitorRect(g_ts.hmonStuck, prcDisplay);
  1091.     if (!fValid)
  1092.         Tray_GetDisplayRectFromRect(prcDisplay, &g_ts.arStuckRects[uStuckPlace], MONITOR_DEFAULTTONEAREST);
  1093. }
  1094. //----------------------------------------------------------------------------
  1095. // Snap a StuckRect to the edge of a containing rectangle
  1096. // fClip determines whether to clip the rectangle if it's off the display or move it onto the screen
  1097. void Tray_MakeStuckRect(LPRECT prcStick, LPCRECT prcBound, SIZE size,
  1098.                         UINT uStick)
  1099. {
  1100.     CopyRect(prcStick, prcBound);
  1101.     InflateRect(prcStick, g_cxEdge, g_cyEdge);
  1102.     if (size.cx < 0) size.cx *= -1;
  1103.     if (size.cy < 0) size.cy *= -1;
  1104.     switch (uStick)
  1105.     {
  1106.     case STICK_LEFT:   prcStick->right  = (prcStick->left   + size.cx); break;
  1107.     case STICK_TOP:    prcStick->bottom = (prcStick->top    + size.cy); break;
  1108.     case STICK_RIGHT:  prcStick->left   = (prcStick->right  - size.cx); break;
  1109.     case STICK_BOTTOM: prcStick->top    = (prcStick->bottom - size.cy); break;
  1110.     }
  1111. }
  1112. /*-------------------------------------------------------------------
  1113. ** the screen size has changed, so the docked rectangles need to be
  1114. ** adjusted to the new screen.
  1115. **-------------------------------------------------------------------*/
  1116. void ResizeStuckRects(RECT *arStuckRects)
  1117. {
  1118.     UINT uStick;
  1119.     RECT rcDisplay;
  1120.     Tray_GetStuckDisplayRect(g_ts.uStuckPlace, &rcDisplay);
  1121.     for (uStick = STICK_LEFT; uStick <= STICK_BOTTOM; uStick++)
  1122.         Tray_MakeStuckRect(&arStuckRects[uStick], &rcDisplay, g_ts.sStuckWidths, uStick);
  1123. }
  1124. void Tray_UpdateDockingFlags()
  1125. {
  1126. #ifndef WINNT
  1127.     BOOL fIoSuccess;
  1128.     BIOSPARAMS bp;
  1129.     DWORD cbOut;
  1130. #endif
  1131.     static BOOL fFirstTime=TRUE;
  1132.     if ((!fFirstTime) && (g_ts.hBIOS == INVALID_HANDLE_VALUE)) {
  1133.         return;
  1134.     }
  1135. #ifdef WINNT
  1136.     g_ts.hBIOS = INVALID_HANDLE_VALUE;
  1137.     fFirstTime = FALSE;
  1138.     return;
  1139. #else
  1140.     g_ts.hBIOS = CreateFile(TEXT("\\.\BIOS"),
  1141.         GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
  1142.         NULL, OPEN_EXISTING, 0, NULL);
  1143.     if (g_ts.hBIOS == INVALID_HANDLE_VALUE) {
  1144.         fFirstTime = FALSE;
  1145.         return;
  1146.     }
  1147.     bp.bp_ret=0;
  1148.     fIoSuccess=DeviceIoControl(
  1149.         g_ts.hBIOS,
  1150.         PNPBIOS_SERVICE_GETDOCKCAPABILITIES,
  1151.         &bp,
  1152.         SIZEOF(bp),
  1153.         &bp,
  1154.         SIZEOF(bp),
  1155.         &cbOut,
  1156.         NULL);
  1157.     if (fFirstTime) {
  1158.         // first time through, if a warm dock change is not possible,
  1159.         // don't bother with the menu item.
  1160.         fFirstTime=FALSE;
  1161.         if (!fIoSuccess) {
  1162.             // problem getting the dock capabilities:
  1163.             // if problem wasn't "undocked now" or "can't identify dock"
  1164.             // then warm ejecting isn't possible.
  1165.             if ((bp.bp_ret!=PNPBIOS_ERR_SYSTEM_NOT_DOCKED) &&
  1166.                 (bp.bp_ret!=PNPBIOS_ERR_CANT_DETERMINE_DOCKING)) {
  1167.                 CloseHandle(g_ts.hBIOS);
  1168.                 g_ts.hBIOS=INVALID_HANDLE_VALUE;
  1169.                 return;
  1170.             }
  1171.         } else {
  1172.             // success getting the dock capabilities:
  1173.             // if the dock isn't capable of warm or hot docking
  1174.             // then warm ejecting isn't possible
  1175.             if (!(bp.bp_ret&PNPBIOS_DOCK_CAPABILITY_TEMPERATURE)) {
  1176.                 CloseHandle(g_ts.hBIOS);
  1177.                 g_ts.hBIOS=INVALID_HANDLE_VALUE;
  1178.                 return;
  1179.             }
  1180.         }
  1181.     }
  1182.     // on each call we update WARMEJECTABLENOW
  1183.     // depending on whether the dock is capable of warm ejecting right now
  1184.     if ((fIoSuccess) && (bp.bp_ret&PNPBIOS_DOCK_CAPABILITY_TEMPERATURE)) {
  1185.         g_ts.fDockingFlags|=DOCKFLAG_WARMEJECTABLENOW;
  1186.     } else {
  1187.         g_ts.fDockingFlags&=~DOCKFLAG_WARMEJECTABLENOW;
  1188.     }
  1189.     return;
  1190. #endif
  1191. }
  1192. /* Check if system is currently in a docking station.
  1193.  *
  1194.  * Returns: TRUE if docked, FALSE if undocked or can't tell.
  1195.  *
  1196.  */
  1197. // BUGBUG (lamadio): This should be moved into it's own file with the other
  1198. // API and ACPI routines.
  1199. BOOL GetDockedState(void)
  1200. {
  1201. #ifndef WINNT                       // BUGBUG - Fix this when NT gets docking capability
  1202.     struct _ghwpi {                 // Get_Hardware_Profile_Info parameter blk
  1203.         CMAPI   cmApi;
  1204.         ULONG   ulIndex;
  1205.         PFARHWPROFILEINFO pHWProfileInfo;
  1206.         ULONG   ulFlags;
  1207.         HWPROFILEINFO HWProfileInfo;
  1208.     } *pghwpi;
  1209.     HANDLE hCMHeap;
  1210.     UINT Result = DOCKSTATE_UNKNOWN;
  1211.     DWORD dwRecipients = BSM_VXDS;
  1212. #define HEAP_SHARED     0x04000000      /* put heap in shared memory--undoc'd */
  1213.     // Create a shared heap for CONFIGMG parameters
  1214.     if ((hCMHeap = HeapCreate(HEAP_SHARED, 1, 4096)) == NULL)
  1215.         return DOCKSTATE_UNKNOWN;
  1216. #undef HEAP_SHARED
  1217.     // Allocate parameter block in shared memory
  1218.     pghwpi = (struct _ghwpi *)HeapAlloc(hCMHeap, HEAP_ZERO_MEMORY,
  1219.                                         SIZEOF(*pghwpi));
  1220.     if (pghwpi == NULL)
  1221.     {
  1222.         HeapDestroy(hCMHeap);
  1223.         return DOCKSTATE_UNKNOWN;
  1224.     }
  1225.     pghwpi->cmApi.dwCMAPIRet     = 0;
  1226.     pghwpi->cmApi.dwCMAPIService = GetVxDServiceOrdinal(_CONFIGMG_Get_Hardware_Profile_Info);
  1227.     pghwpi->cmApi.pCMAPIStack    = (DWORD)(((LPBYTE)pghwpi) + SIZEOF(pghwpi->cmApi));
  1228.     pghwpi->ulIndex              = 0xFFFFFFFF;
  1229.     pghwpi->pHWProfileInfo       = &pghwpi->HWProfileInfo;
  1230.     pghwpi->ulFlags              = 0;
  1231.     // "Call" _CONFIGMG_Get_Hardware_Profile_Info service
  1232.     BroadcastSystemMessage(0, &dwRecipients, WM_DEVICECHANGE, DBT_CONFIGMGAPI32,
  1233.                            (LPARAM)pghwpi);
  1234.     if (pghwpi->cmApi.dwCMAPIRet == CR_SUCCESS) {
  1235.         switch (pghwpi->HWProfileInfo.HWPI_dwFlags) {
  1236.             case CM_HWPI_DOCKED:
  1237.                 Result = DOCKSTATE_DOCKED;
  1238.                 break;
  1239.             case CM_HWPI_UNDOCKED:
  1240.                 Result = DOCKSTATE_UNDOCKED;
  1241.                 break;
  1242.             default:
  1243.                 Result = DOCKSTATE_UNKNOWN;
  1244.                 break;
  1245.         }
  1246.     }
  1247.     HeapDestroy(hCMHeap);
  1248.     return Result;
  1249. #else
  1250.     return(DOCKSTATE_DOCKED);
  1251. #endif
  1252. }
  1253. int g_cHided;
  1254. #ifdef DEBUG
  1255. BOOL g_dbNoShow;
  1256. #endif
  1257. void Tray_Hide();
  1258. //***   TrayShowWindow -- temporary 'invisible' un-autohide
  1259. // DESCRIPTION
  1260. //  various tray resize routines need the tray to be un-autohide'd for
  1261. // stuff to be calculated correctly.  so we un-autohide it (invisibly...)
  1262. // here.  note the WM_SETREDRAW to prevent flicker (nt5:182340).
  1263. //  note that this is kind of a hack -- ideally the tray code would do
  1264. // stuff correctly even if hidden.
  1265. // NOTES
  1266. //  separate routine (vs. inline) to aid debugging.  turn this func off
  1267. // to 'see' resize stuff happening
  1268. //  BUGBUG what happens if rehide kicks off before we're done?
  1269. void TrayShowWindow(int nCmdShow)
  1270. {
  1271.     if (nCmdShow == SW_HIDE) {
  1272.         if (g_cHided++ == 0) {
  1273. #ifdef DEBUG
  1274.             if (!g_dbNoShow)
  1275. #endif
  1276.             SendMessage(v_hwndTray, WM_SETREDRAW, FALSE, 0);
  1277.             ShowWindow(v_hwndTray, nCmdShow);
  1278.             Tray_Unhide();
  1279.         }
  1280.     }
  1281.     else if (nCmdShow == SW_SHOWNA) {
  1282.         ASSERT(g_cHided > 0);       // must be push/pop
  1283.         if (--g_cHided == 0) {
  1284.             Tray_Hide();
  1285. #ifdef DEBUG
  1286.             if (!g_dbNoShow)
  1287. #endif
  1288.             ShowWindow(v_hwndTray, nCmdShow);
  1289.             SendMessage(v_hwndTray, WM_SETREDRAW, TRUE, 0);
  1290.         }
  1291.     }
  1292.     else {
  1293.         ASSERT(0);
  1294.     }
  1295.     return;
  1296. }
  1297. void Tray_VerifySize(BOOL fWinIni)
  1298. {
  1299.     RECT rc;
  1300.     BOOL fHiding;
  1301.     fHiding = (g_ts.uAutoHide & AH_HIDING);
  1302.     if (fHiding) {
  1303.         // force it visible so various calculations will happen relative
  1304.         // to unhidden size/position.
  1305.         //
  1306.         // fixes (e.g.) ie5:154536, where dropping a large-icon ISFBand
  1307.         // onto hidden tray didn't do size negotiation.
  1308.         //
  1309.         // BUGBUG what happens if rehide kicks off before we're done?.
  1310.         TrayShowWindow(SW_HIDE);
  1311.     }
  1312.     rc = g_ts.arStuckRects[g_ts.uStuckPlace];
  1313.     Tray_HandleSizing(0, NULL, g_ts.uStuckPlace);
  1314.     //
  1315.     // if the old view had a height, and now it won't...
  1316.     // push it up to at least one height
  1317.     //
  1318.     // do this only on win ini if we're on the top or bottom
  1319.     if (fWinIni && STUCK_HORIZONTAL(g_ts.uStuckPlace)) {
  1320.         RECT rcView;
  1321.         // stash the old view size;
  1322.         GetClientRect(g_ts.hwndView, &rcView);
  1323.         if (RECTHEIGHT(rcView) && (RECTHEIGHT(rc) == g_cyTrayBorders)) {
  1324.             int cyOneRow = g_cySize + 2 * g_cyEdge;
  1325.             if (g_ts.uStuckPlace == STICK_TOP) {
  1326.                 rc.bottom = rc.top + cyOneRow;
  1327.             } else {
  1328.                 rc.top = rc.bottom - cyOneRow;
  1329.             }
  1330.             // now snap this size.
  1331.             Tray_HandleSizing(0, NULL, g_ts.uStuckPlace);
  1332.         }
  1333.     }
  1334.     if (!EqualRect(&rc, &g_ts.arStuckRects[g_ts.uStuckPlace]))
  1335.     {
  1336.         if (fWinIni) {
  1337.             // if we're changing size or position, we need to be unhidden
  1338.             Tray_Unhide();
  1339.             Tray_SizeWindows();
  1340.         }
  1341.         rc = g_ts.arStuckRects[g_ts.uStuckPlace];
  1342.         if ((g_ts.uAutoHide & (AH_ON | AH_HIDING)) != (AH_ON | AH_HIDING))
  1343.         {
  1344.             g_ts.fSelfSizing = TRUE;
  1345.             SetWindowPos(g_ts.hwndMain, NULL,
  1346.                 rc.left, rc.top,
  1347.                 RECTWIDTH(rc),RECTHEIGHT(rc),
  1348.                 SWP_NOZORDER | SWP_NOACTIVATE);
  1349.             g_ts.fSelfSizing = FALSE;
  1350.         }
  1351.         else
  1352.             ASSERT(0);      // tmp unhide above
  1353.         Tray_StuckTrayChange();
  1354.     }
  1355.     if (fWinIni)
  1356.         Tray_SizeWindows();
  1357.     if (fHiding) {
  1358.         TrayShowWindow(SW_SHOWNA);
  1359.     }
  1360. }
  1361. //----------------------------------------------------------------------------
  1362. TCHAR const c_szCheckAssociations[] = TEXT("CheckAssociations");
  1363. //----------------------------------------------------------------------------
  1364. // Returns true if GrpConv says we should check extensions again (and then
  1365. // clears the flag).
  1366. // The assumption here is that runonce gets run before we call this (so
  1367. // GrpConv -s can set this).
  1368. BOOL Tray_CheckAssociations(void)
  1369. {
  1370.     DWORD dw = 0;
  1371.     DWORD cb = SIZEOF(dw);
  1372.     if (Reg_GetStruct(g_hkeyExplorer, NULL, c_szCheckAssociations,
  1373.         &dw, &cb) && dw)
  1374.     {
  1375.         dw = 0;
  1376.         Reg_SetStruct(g_hkeyExplorer, NULL, c_szCheckAssociations, &dw, SIZEOF(dw));
  1377.         return TRUE;
  1378.     }
  1379.     return FALSE;
  1380. }
  1381. ULONG _RegisterNotify(HWND hwnd, UINT nMsg, LPITEMIDLIST pidl, BOOL fRecursive );
  1382. //----------------------------------------------------------------------------
  1383. void Tray_RegisterDesktopNotify()
  1384. {
  1385.     LPITEMIDLIST pidl;
  1386.     TraceMsg(TF_TRAY, "c.rdn: Notify for desktop.");
  1387.     if (!g_ts.uDesktopNotify)
  1388.     {
  1389.         pidl = SHCloneSpecialIDList(NULL, CSIDL_DESKTOPDIRECTORY, TRUE);
  1390.         if (pidl)
  1391.         {
  1392.             g_ts.uDesktopNotify = _RegisterNotify(g_ts.hwndMain, WMTRAY_DESKTOPCHANGE, pidl, FALSE);
  1393.             ILFree(pidl);
  1394.         }
  1395.     }
  1396.     if (!SHRestricted(REST_NOCOMMONGROUPS) && !g_ts.uCommonDesktopNotify)
  1397.     {
  1398.         pidl = SHCloneSpecialIDList(NULL, CSIDL_COMMON_DESKTOPDIRECTORY, TRUE);
  1399.         if (pidl)
  1400.         {
  1401.             g_ts.uCommonDesktopNotify = _RegisterNotify(g_ts.hwndMain, WMTRAY_DESKTOPCHANGE, pidl, FALSE);
  1402.             ILFree(pidl);
  1403.         }
  1404.     }
  1405. }
  1406. HWND Tray_GetClockWindow(void)
  1407. {
  1408.     return (HWND)SendMessage(g_ts.hwndNotify, TNM_GETCLOCK, 0, 0L);
  1409. }
  1410. UINT Tray_GetStartIDB()
  1411. {
  1412.     UINT id;
  1413. #ifdef WINNT
  1414.     if (IsOS(OS_TERMINALCLIENT))
  1415.     {
  1416.         id = IDB_TERMINALSERVICESBKG;
  1417.     }
  1418.     else if (IsOS(OS_WIN2000DATACENTER))
  1419.     {
  1420.         id = IDB_DCSERVERSTARTBKG;
  1421.     }
  1422.     else if (IsOS(OS_SERVERAPPLIANCE))
  1423.     {
  1424.         id = IDB_SRVAPPSTARTBKG;
  1425.     }
  1426.     else if (IsOS(OS_WIN2000ADVSERVER))
  1427.     {
  1428.         id = IDB_ADVSERVERSTARTBKG;
  1429.     }
  1430.     else if (IsOS(OS_WIN2000SERVER))
  1431.     {
  1432.         id = IDB_SERVERSTARTBKG;
  1433.     }
  1434.     else if (IsOS(OS_WIN2000EMBED))
  1435.     {
  1436.         id = IDB_EMBEDDED;
  1437.     }
  1438.     else
  1439.     {
  1440.         id = IDB_STARTBKG;
  1441.     }
  1442. #else
  1443.     if (IsOS(OS_MEMPHIS_GOLD))
  1444.     {
  1445.         id = IDB_STARTBKG;
  1446.     }
  1447.     else
  1448.     {
  1449.         id = IDB_START95BK;
  1450.     }
  1451. #endif
  1452.     return id;
  1453. }
  1454. void Tray_CreateTrayTips()
  1455. {
  1456.     g_ts.hwndTrayTips = CreateWindow(TOOLTIPS_CLASS, NULL,
  1457.                                      WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
  1458.                                      CW_USEDEFAULT, CW_USEDEFAULT,
  1459.                                      CW_USEDEFAULT, CW_USEDEFAULT,
  1460.                                      NULL, NULL, hinstCabinet,
  1461.                                      NULL);
  1462.     SetWindowZorder(g_ts.hwndTrayTips, HWND_TOPMOST);
  1463.     if (g_ts.hwndTrayTips)
  1464.     {
  1465.         HWND hwndClock;
  1466.         TOOLINFO ti;
  1467.         ti.cbSize = SIZEOF(ti);
  1468.         ti.uFlags = TTF_IDISHWND;
  1469.         ti.hwnd = v_hwndTray;
  1470.         ti.uId = (UINT_PTR)g_ts.hwndStart;
  1471.         ti.lpszText = (LPTSTR)MAKEINTRESOURCE(IDS_STARTBUTTONTIP);
  1472.         ti.hinst = hinstCabinet;
  1473.         SendMessage(g_ts.hwndTrayTips, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&ti);
  1474.         if (NULL != (hwndClock = Tray_GetClockWindow()))
  1475.         {
  1476.             ti.uFlags = 0;
  1477.             ti.uId = (UINT_PTR)hwndClock;
  1478.             ti.lpszText = LPSTR_TEXTCALLBACK;
  1479.             ti.rect.left = ti.rect.top = ti.rect.bottom = ti.rect.right = 0;
  1480.             SendMessage(g_ts.hwndTrayTips, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&ti);
  1481.         }
  1482.     }
  1483. }
  1484. IUnknown*  Tray_CreateView();
  1485. LRESULT Tray_CreateWindows()
  1486. {
  1487.     if (Tray_CreateStartButton())
  1488.     {
  1489.         if (Tray_CreateClockWindow())
  1490.         {
  1491.             //
  1492.             //  We need to set the tray position, before creating
  1493.             // the view window, because it will call back our
  1494.             // GetWindowRect member functions.
  1495.             //
  1496.             Tray_RestoreWindowPos();
  1497.             Tray_CreateTrayTips();
  1498.             g_ts.ptbs = Tray_CreateView();
  1499.             SendMessage(g_ts.hwndNotify, TNM_HIDECLOCK, 0, g_ts.fHideClock);
  1500.             if (g_ts.ptbs)
  1501.             {
  1502.                 Tray_CreateDesktopButton();
  1503.                 Tray_VerifySize(FALSE);
  1504.                 Tray_SizeWindows();      // size after all windows created
  1505.                 return 1;
  1506.             }
  1507.         }
  1508.     }
  1509.     return -1;
  1510. }
  1511. LRESULT Tray_InitStartButtonEtc()
  1512. {
  1513.     // NOTE: This bitmap is used as a flag in CTaskBar::OnPosRectChangeDB to
  1514.     // tell when we are done initializing, so we don't resize prematurely
  1515.     g_ts.hbmpStartBkg = LoadBitmap(hinstCabinet, MAKEINTRESOURCE(Tray_GetStartIDB()));
  1516.     if (g_ts.hbmpStartBkg)
  1517.     {
  1518.         UpdateWindow(v_hwndTray);
  1519.         StartMenu_Build();
  1520.         CStartDropTarget_Register();
  1521.         PrintNotify_Init(v_hwndTray);
  1522.         if (Tray_CheckAssociations())
  1523.             CheckWinIniForAssocs();
  1524.         Tray_RegisterDesktopNotify();
  1525.         SendNotifyMessage(HWND_BROADCAST,
  1526.                           RegisterWindowMessage(TEXT("TaskbarCreated")), 0, 0);
  1527.         return 1;
  1528.     }
  1529.     return -1;
  1530. }
  1531. void Tray_AdjustMinimizedMetrics()
  1532. {
  1533.     MINIMIZEDMETRICS mm;
  1534.     mm.cbSize = SIZEOF(mm);
  1535.     SystemParametersInfo(SPI_GETMINIMIZEDMETRICS, SIZEOF(mm), &mm, FALSE);
  1536.     mm.iArrange |= ARW_HIDE;
  1537.     SystemParametersInfo(SPI_SETMINIMIZEDMETRICS, SIZEOF(mm), &mm, FALSE);
  1538. }
  1539. LRESULT Tray_OnCreateAsync()
  1540. {
  1541.     LRESULT lres;
  1542.     if (g_dwProfileCAP & 0x00000004)
  1543.     {
  1544.         StartCAP();
  1545.     }
  1546.     lres = Tray_InitStartButtonEtc();
  1547.     if (g_dwProfileCAP & 0x00000004)
  1548.     {
  1549.         StopCAP();
  1550.     }
  1551.     g_ts.hMainAccel = LoadAccelerators(hinstCabinet, MAKEINTRESOURCE(ACCEL_TRAY));
  1552.     Tray_RegisterGlobalHotkeys();
  1553.     HotkeyList_Restore(v_hwndTray);
  1554.     // we run the tray thread that handles Ctrl-Esc with a high priority
  1555.     // class so that it can respond even on a stressed system.
  1556.     SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
  1557.     return lres;
  1558. }
  1559. LRESULT Tray_OnCreate(HWND hwnd)
  1560. {
  1561.     LRESULT lres;
  1562.     v_hwndTray = hwnd;
  1563.     SendMessage(v_hwndTray, WM_CHANGEUISTATE, MAKEWPARAM(UIS_INITIALIZE, 0), 0);
  1564.     Tray_AdjustMinimizedMetrics();
  1565.     Tray_UpdateDockingFlags();
  1566.     lres = Tray_CreateWindows();
  1567.     return lres;
  1568. }
  1569. void Tray_DestroyShellView()
  1570. {
  1571.     DestroyWindow(g_ts.hwndView);
  1572.     g_ts.hwndView = NULL;
  1573. }
  1574. BOOL CALLBACK Tray_FullScreenEnumCallback(HMONITOR hmon, HDC hdc, LPRECT prc, LPARAM dwData)
  1575. {
  1576.     BOOL fFullScreen;   // Is there a rude app on this monitor?
  1577.     LPRECT prcRude = (LPRECT)dwData;
  1578.     if (prcRude)
  1579.     {
  1580.         RECT rc, rcMon;
  1581.         GetMonitorRect(hmon, &rcMon);
  1582.         IntersectRect(&rc, &rcMon, prcRude);
  1583.         fFullScreen = EqualRect(&rc, &rcMon);
  1584.     }
  1585.     else
  1586.     {
  1587.         fFullScreen = FALSE;
  1588.     }
  1589.     if (hmon == g_ts.hmonStuck)
  1590.     {
  1591.         g_ts.fStuckRudeApp = fFullScreen;
  1592.     }
  1593.     //
  1594.     // Tell all the appbars on the same display to get out of the way too
  1595.     //
  1596.     AppBarNotifyAll(hmon, ABN_FULLSCREENAPP, NULL, fFullScreen);
  1597.     return TRUE;
  1598. }
  1599. void Tray_HandleFullScreenApp(HWND hwnd)
  1600. {
  1601.     //
  1602.     // First check to see if something has actually changed
  1603.     //
  1604.     if (g_ts.hwndRude != hwnd)
  1605.     {
  1606.         g_ts.hwndRude = hwnd;
  1607.         //
  1608.         // Enumerate all the monitors, see if the app is rude on each, adjust
  1609.         // app bars and g_ts.fStuckRudeApp as necessary.  (Some rude apps, such
  1610.         // as the NT Logon Screen Saver, span multiple monitors.)
  1611.         //
  1612.         {
  1613.             LPRECT prc;
  1614.             RECT rc;
  1615.             if (hwnd && GetWindowRect(hwnd, &rc))
  1616.             {
  1617.                 prc = &rc;
  1618.             }
  1619.             else
  1620.             {
  1621.                 prc = NULL;
  1622.             }
  1623.             EnumDisplayMonitors(NULL, NULL, Tray_FullScreenEnumCallback, (LPARAM)prc);
  1624.         }
  1625.         //
  1626.         // Now that we've set g_ts.fStuckRudeApp, update the tray's z-order position
  1627.         //
  1628.         Tray_ResetZorder();
  1629.         //
  1630.         // stop the clock so we don't eat cycles and keep tons of code paged in
  1631.         //
  1632.         ClockCtl_HandleTrayHide(g_ts.fStuckRudeApp);
  1633.         //
  1634.         // Finally, let traynot know about whether the tray is hiding
  1635.         //
  1636.         SendMessage(g_ts.hwndNotify, TNM_RUDEAPP, g_ts.fStuckRudeApp, 0);
  1637.     }
  1638. }
  1639. BOOL Tray_IsTopmost()
  1640. {
  1641.     return BOOLIFY(GetWindowLong(v_hwndTray, GWL_EXSTYLE) & WS_EX_TOPMOST);
  1642. }
  1643. BOOL Tray_IsStartMenuVisible()
  1644. {
  1645.     HWND hwnd;
  1646.     if (SUCCEEDED(IUnknown_GetWindow((IUnknown*)g_ts._pmpStartMenu, &hwnd)))
  1647.     {
  1648.         return IsWindowVisible(hwnd);
  1649.     }
  1650.     return FALSE;
  1651. }
  1652. BOOL Tray_IsActive()
  1653. {
  1654.     //
  1655.     // We say the tray is "active" iff:
  1656.     //
  1657.     // (a) the foreground window is the tray or a window owned by the tray, or
  1658.     // (b) the start menu is showing
  1659.     //
  1660.     BOOL fActive = FALSE;
  1661.     HWND hwnd = GetForegroundWindow();
  1662.     if (hwnd != NULL &&
  1663.         (hwnd == v_hwndTray || (GetWindowOwner(hwnd) == v_hwndTray)))
  1664.     {
  1665.         fActive = TRUE;
  1666.     }
  1667.     else if (Tray_IsStartMenuVisible())
  1668.     {
  1669.         fActive = TRUE;
  1670.     }
  1671.     return fActive;
  1672. }
  1673. void Tray_ResetZorder()
  1674. {
  1675.     HWND hwndZorder, hwndZorderCurrent;
  1676.     if (g_fDesktopRaised || (g_ts.fAlwaysOnTop && !g_ts.fStuckRudeApp))
  1677.     {
  1678.         hwndZorder = HWND_TOPMOST;
  1679.     }
  1680.     else if (Tray_IsActive())
  1681.     {
  1682.         hwndZorder = HWND_TOP;
  1683.     }
  1684.     else if (g_ts.fStuckRudeApp)
  1685.     {
  1686.         hwndZorder = HWND_BOTTOM;
  1687.     }
  1688.     else
  1689.     {
  1690.         hwndZorder = HWND_NOTOPMOST;
  1691.     }
  1692.     //
  1693.     // We don't have to worry about the HWND_BOTTOM current case -- it's ok
  1694.     // to keep moving ourselves down to the bottom when there's a rude app.
  1695.     //
  1696.     // Nor do we have to worry about the HWND_TOP current case -- it's ok
  1697.     // to keep moving ourselves up to the top when we're active.
  1698.     //
  1699.     hwndZorderCurrent = Tray_IsTopmost() ? HWND_TOPMOST : HWND_NOTOPMOST;
  1700.     if (hwndZorder != hwndZorderCurrent)
  1701.     {
  1702.         // only do this if somehting has changed.
  1703.         // this keeps us from popping up over menus as desktop async
  1704.         // notifies us of it's state
  1705.         SHForceWindowZorder(v_hwndTray, hwndZorder);
  1706.     }
  1707. }
  1708. DWORD CALLBACK Tray_SyncThreadProc(void *hInst);
  1709. DWORD CALLBACK Tray_MainThreadProc(void *hInst);
  1710. void Tray_MessageLoop()
  1711. {
  1712.     for (;;)
  1713.     {
  1714.         MSG  msg;
  1715.         if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
  1716.         {
  1717.             if (msg.message == WM_QUIT)
  1718.             {
  1719.                 if (v_hwndTray && IsWindow(v_hwndTray))
  1720.                 {
  1721.                     // Tell the tray to save everything off if we got here
  1722.                     // without it being destroyed.
  1723.                     SendMessage(v_hwndTray, WM_ENDSESSION, 1, 0);
  1724.                 }
  1725.                 return;  // break all the way out of the main loop
  1726.             }
  1727.             if (g_ts._pmbStartMenu &&
  1728.                 g_ts._pmbStartMenu->lpVtbl->IsMenuMessage(g_ts._pmbStartMenu, &msg) == S_OK)
  1729.             {
  1730.                 continue;
  1731.             }
  1732.             if (g_ts.hMainAccel && TranslateAccelerator(g_ts.hwndMain, g_ts.hMainAccel, &msg))
  1733.             {
  1734.                 continue;
  1735.             }
  1736.             TranslateMessage(&msg);
  1737.             DispatchMessage(&msg);
  1738.         }
  1739.         else
  1740.         {
  1741.             WaitMessage();
  1742.         }
  1743.     }
  1744. }
  1745. BOOL InitTray(HINSTANCE hInst )
  1746. {
  1747.     // put the tray on a separate thread
  1748.     return SHCreateThread(Tray_MainThreadProc, hInst, 0, Tray_SyncThreadProc);
  1749. }
  1750. void Tray_InitBandsite()
  1751. {
  1752.     ASSERT(v_hwndTray);
  1753.     // we initilize the contents after all the infrastructure is created and sized properly
  1754.     // need to notify which side we're on.
  1755.     // nt5:211881: set mode *before* load, o.w. Update->RBAutoSize screwed up
  1756.     BandSite_SetMode(g_ts.ptbs, STUCK_HORIZONTAL(g_ts.uStuckPlace) ? 0 : DBIF_VIEWMODE_VERTICAL);
  1757.     BandSite_Load();
  1758.     // now that the mode is set, we need to force an update because we
  1759.     // explicitly avoided the update during BandSite_Load
  1760.     BandSite_Update(g_ts.ptbs);
  1761.     BandSite_UIActivateDBC(g_ts.ptbs, DBC_SHOW);
  1762. }
  1763. void Tray_KickStartAutohide()
  1764. {
  1765.     if (g_ts.uAutoHide & AH_ON)
  1766.     {
  1767.         // tray always starts out hidden on autohide
  1768.         g_ts.uAutoHide = AH_ON | AH_HIDING;
  1769.         // we and many apps rely upon us having calculated the size correctly
  1770.         Tray_Unhide();
  1771.         // register it
  1772.         if (!IAppBarSetAutoHideBar(v_hwndTray, TRUE, g_ts.uStuckPlace))
  1773.         {
  1774.             // don't bother putting up UI in this case
  1775.             // if someone is there just silently convert to normal
  1776.             // (the shell is booting who would be there anyway?)
  1777.             Tray_SetAutoHideState(FALSE);
  1778.         }
  1779.     }
  1780. }
  1781. void Tray_InitNonzeroGlobals()
  1782. {
  1783.     // initalize globals that need to be non-zero
  1784.     g_ts.wThreadCmd = SMCT_DONE;
  1785.     g_ts.hBIOS = INVALID_HANDLE_VALUE;
  1786.     if (GetSystemMetrics(SM_SLOWMACHINE))
  1787.     {
  1788.         g_dtSlideHide = 0;       // dont slide the tray out
  1789.         g_dtSlideShow = 0;
  1790.     }
  1791.     else
  1792.     {
  1793.         //BUGBUG: we should read from registry.
  1794.         g_dtSlideHide = 400;
  1795.         g_dtSlideShow = 200;
  1796.     }
  1797. }
  1798. void Tray_CreateTrayWindow(HINSTANCE hinst)
  1799. {
  1800.     DWORD dwExStyle = WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW;
  1801.     dwExStyle |= IS_BIDI_LOCALIZED_SYSTEM() ? dwExStyleRTLMirrorWnd : 0L;
  1802.     v_hwndTray = CreateWindowEx(dwExStyle,
  1803.                                 TEXT(WNDCLASS_TRAYNOTIFY), NULL,
  1804.                                 WS_CLIPCHILDREN | WS_POPUP | WS_BORDER | WS_THICKFRAME,
  1805.                                 0, 0, 0, 0, NULL, NULL, hinst, NULL);
  1806. }
  1807. DWORD CALLBACK Tray_SyncThreadProc(void *pv)
  1808. {
  1809.     MSG msg;
  1810.     if (g_dwProfileCAP & 0x00000002)
  1811.     {
  1812.         StartCAP();
  1813.     }
  1814.     // make sure the message queue has been created...
  1815.     PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE );
  1816.     OleInitialize( NULL );
  1817.     Tray_InitNonzeroGlobals();
  1818.     Tray_CreateTrayWindow((HINSTANCE)pv);
  1819.     if (!v_hwndTray || !g_ts.ptbs)
  1820.     {
  1821.         OleUninitialize();
  1822.         return FALSE;
  1823.     }
  1824.     //
  1825.     // obey the "always on top" flag
  1826.     //
  1827.     Tray_ResetZorder();
  1828.     Tray_KickStartAutohide();
  1829.     Tray_InitBandsite();
  1830.     //
  1831.     // make sure we clip the taskbar to the current monitor before showing it
  1832.     //
  1833.     Tray_ClipWindow(TRUE);
  1834.     //
  1835.     // it looks really dorky for the tray to pop up and rehide at logon
  1836.     // if we are autohide don't activate the tray when we show it
  1837.     // if we aren't autohide do what Win95 did (tray is active by default)
  1838.     //
  1839.     ShowWindow(v_hwndTray,
  1840.         ((g_ts.uAutoHide & AH_HIDING)? SW_SHOWNA : SW_SHOW));
  1841.     UpdateWindow(v_hwndTray);
  1842.     Tray_StuckTrayChange();
  1843.     SetTimer(v_hwndTray, IDT_HANDLEDELAYBOOTSTUFF, 10 * 1000, NULL);
  1844.     if (g_dwProfileCAP & 0x00020000)
  1845.     {
  1846.         StopCAP();
  1847.     }
  1848.     return FALSE;
  1849. }
  1850. // the rest of the thread proc that includes the message loop
  1851. DWORD CALLBACK Tray_MainThreadProc(void *pv)
  1852. {
  1853.     if (!v_hwndTray)
  1854.         return FALSE;
  1855.     Tray_OnCreateAsync();
  1856.     Tray_MessageLoop();
  1857.     OleUninitialize();
  1858.     return FALSE;
  1859. }
  1860. #define DM_IANELHK 0
  1861. //---------------------------------------------------------------------------
  1862. ULONG _RegisterNotify(HWND hwnd, UINT nMsg, LPITEMIDLIST pidl, BOOL fRecursive)
  1863. {
  1864.     SHChangeNotifyEntry fsne;
  1865.     ULONG lReturn;
  1866.     fsne.fRecursive = fRecursive;
  1867.     fsne.pidl = pidl;
  1868.     //
  1869.     // Don't watch for attribute changes since we just want the
  1870.     // name and icon.  For example, if a printer is paused, we don't
  1871.     // want to re-enumerate everything.
  1872.     //
  1873.     lReturn =  SHChangeNotifyRegister(hwnd, SHCNRF_NewDelivery | SHCNRF_ShellLevel | SHCNRF_InterruptLevel,
  1874.         ((SHCNE_DISKEVENTS | SHCNE_UPDATEIMAGE) & ~SHCNE_ATTRIBUTES), nMsg, 1, &fsne);
  1875.     return lReturn;
  1876. }
  1877. //---------------------------------------------------------------------------
  1878. ULONG RegisterNotify(HWND hwnd, UINT nMsg, LPITEMIDLIST pidl)
  1879. {
  1880.     return _RegisterNotify(hwnd, nMsg, pidl, TRUE);
  1881. }
  1882. //---------------------------------------------------------------------------
  1883. void UnregisterNotify(ULONG nNotify)
  1884. {
  1885.     if (nNotify)
  1886.         SHChangeNotifyDeregister(nNotify);
  1887. }
  1888. //----------------------------------------------------------------------------
  1889. #define HKIF_NULL               0
  1890. #define HKIF_CACHED             1
  1891. #define HKIF_FREEPIDLS          2
  1892. typedef struct
  1893. {
  1894.     LPITEMIDLIST pidlFolder;
  1895.     LPITEMIDLIST pidlItem;
  1896.     WORD wGHotkey;
  1897.     // BOOL fCached;
  1898.     WORD wFlags;
  1899. } HOTKEYITEM, *PHOTKEYITEM;
  1900. const TCHAR c_szSlashCLSID[] = TEXT("\CLSID");
  1901. //
  1902. // like OLE GetClassFile(), but it only works on ProgIDCLSID type registration
  1903. // not real doc files or pattern matched files
  1904. //
  1905. HRESULT _CLSIDFromExtension(LPCTSTR pszExt, CLSID *pclsid)
  1906. {
  1907.     TCHAR szProgID[80];
  1908.     ULONG cb = SIZEOF(szProgID);
  1909.     if (RegQueryValue(HKEY_CLASSES_ROOT, pszExt, szProgID, &cb) == ERROR_SUCCESS)
  1910.     {
  1911.         TCHAR szCLSID[80];
  1912.         lstrcat(szProgID, c_szSlashCLSID);
  1913.         cb = SIZEOF(szCLSID);
  1914.         if (RegQueryValue(HKEY_CLASSES_ROOT, szProgID, szCLSID, &cb) == ERROR_SUCCESS)
  1915.             return SHCLSIDFromString(szCLSID, pclsid);
  1916.     }
  1917.     return E_FAIL;
  1918. }
  1919. //----------------------------------------------------------------------------
  1920. // this gets hotkeys for files given a folder and a pidls  it is much faster
  1921. // than _GetHotkeyFromPidls since it does not need to bind to an IShellFolder
  1922. // to interrogate it.  if you have access to the item's IShellFolder, call this
  1923. // one, especially in a loop.
  1924. //
  1925. WORD _GetHotkeyFromFolderItem(LPSHELLFOLDER psf, LPCITEMIDLIST pidl)
  1926. {
  1927. //    TCHAR szPath[MAX_PATH];
  1928.     WORD wHotkey = 0;
  1929.     DWORD dwAttrs = SFGAO_LINK;
  1930.     //
  1931.     // Make sure it is an SFGAO_LINK so we don't load a big handler dll
  1932.     // just to get back E_NOINTERFACE...
  1933.     //
  1934.     if (SUCCEEDED(psf->lpVtbl->GetAttributesOf(psf, 1, &pidl, &dwAttrs)) &&
  1935.         (dwAttrs & SFGAO_LINK))
  1936.     {
  1937.         IShellLink * pLink;
  1938.         UINT rgfInOut = 0;
  1939.         if ( SUCCEEDED( psf->lpVtbl->GetUIObjectOf(psf, NULL, 1, &pidl, &IID_IShellLink, &rgfInOut, &pLink )))
  1940.         {
  1941.             pLink->lpVtbl->GetHotkey(pLink, &wHotkey);
  1942.             pLink->lpVtbl->Release(pLink);
  1943.         }
  1944.     }
  1945.     return wHotkey;
  1946. }
  1947. //----------------------------------------------------------------------------
  1948. UINT HotkeyList_GetFreeItemIndex(void)
  1949. {
  1950.     int i, cItems;
  1951.     PHOTKEYITEM phki;
  1952.     ASSERT(IS_VALID_HANDLE(g_ts.hdsaHKI, DSA));
  1953.     cItems = DSA_GetItemCount(g_ts.hdsaHKI);
  1954.     for (i=0; i<cItems; i++)
  1955.     {
  1956.         phki = DSA_GetItemPtr(g_ts.hdsaHKI, i);
  1957.         if (!phki->wGHotkey)
  1958.         {
  1959.             ASSERT(!phki->pidlFolder);
  1960.             ASSERT(!phki->pidlItem);
  1961.             break;
  1962.         }
  1963.     }
  1964.     return i;
  1965. }
  1966. //----------------------------------------------------------------------------
  1967. // Weird, Global hotkeys use different flags for modifiers than window hotkeys
  1968. // (and hotkeys returned by the hotkey control)
  1969. WORD MapHotkeyToGlobalHotkey(WORD wHotkey)
  1970. {
  1971.     UINT nVirtKey;
  1972.     UINT nMod = 0;
  1973.     // Map the modifiers.
  1974.     if (HIBYTE(wHotkey) & HOTKEYF_SHIFT)
  1975.         nMod |= MOD_SHIFT;
  1976.     if (HIBYTE(wHotkey) & HOTKEYF_CONTROL)
  1977.         nMod |= MOD_CONTROL;
  1978.     if (HIBYTE(wHotkey) & HOTKEYF_ALT)
  1979.         nMod |= MOD_ALT;
  1980.     nVirtKey = LOBYTE(wHotkey);
  1981.     return (WORD)((nMod*256) + nVirtKey);
  1982. }
  1983. //----------------------------------------------------------------------------
  1984. // NB This takes a regular window hotkey not a global hotkey (it does
  1985. // the convertion for you).
  1986. int HotkeyList_Add(WORD wHotkey, LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlItem, BOOL fClone)
  1987. {
  1988.     LPCITEMIDLIST pidl1, pidl2;
  1989.     if (wHotkey)
  1990.     {
  1991.         HOTKEYITEM hki;
  1992.         int i = HotkeyList_GetFreeItemIndex();
  1993.         ASSERT(IS_VALID_HANDLE(g_ts.hdsaHKI, DSA));
  1994.         // DebugMsg(DM_IANELHK, "c.hl_a: Hotkey %x with id %d.", wHotkey, i);
  1995.         if (fClone)
  1996.         {
  1997.             pidl1 = ILClone(pidlFolder);
  1998.             pidl2 = ILClone(pidlItem);
  1999.             hki.wFlags = HKIF_FREEPIDLS;
  2000.         }
  2001.         else
  2002.         {
  2003.             pidl1 = pidlFolder;
  2004.             pidl2 = pidlItem;
  2005.             hki.wFlags = HKIF_NULL;
  2006.         }
  2007.         hki.pidlFolder = (LPITEMIDLIST)pidl1;
  2008.         hki.pidlItem = (LPITEMIDLIST)pidl2;
  2009.         hki.wGHotkey = MapHotkeyToGlobalHotkey(wHotkey);
  2010.         DSA_SetItem(g_ts.hdsaHKI, i, &hki);
  2011.         return i;
  2012.     }
  2013.     return -1;
  2014. }
  2015. //----------------------------------------------------------------------------
  2016. // NB Cached hotkeys have their own pidls that need to be free but
  2017. // regular hotkeys just keep a pointer to pidls used by the startmenu and
  2018. // so don't.
  2019. int HotkeyList_AddCached(WORD wGHotkey, LPITEMIDLIST pidl)
  2020. {
  2021.     int i = -1;
  2022.     if (wGHotkey)
  2023.     {
  2024.         LPITEMIDLIST pidlItem = ILClone(ILFindLastID(pidl));
  2025.         ASSERT(IS_VALID_HANDLE(g_ts.hdsaHKI, DSA));
  2026.         if (pidlItem)
  2027.         {
  2028.             if (ILRemoveLastID(pidl))
  2029.             {
  2030.                 HOTKEYITEM hki;
  2031.                 i = HotkeyList_GetFreeItemIndex();
  2032.                 // DebugMsg(DM_IANELHK, "c.hl_ac: Hotkey %x with id %d.", wGHotkey, i);
  2033.                 hki.pidlFolder = pidl;
  2034.                 hki.pidlItem = pidlItem;
  2035.                 hki.wGHotkey = wGHotkey;
  2036.                 hki.wFlags = HKIF_CACHED | HKIF_FREEPIDLS;
  2037.                 DSA_SetItem(g_ts.hdsaHKI, i, &hki);
  2038.             }
  2039.         }
  2040.     }
  2041.     return i;
  2042. }
  2043. const TCHAR c_szHotkeys[] = TEXT("Hotkeys");
  2044. //----------------------------------------------------------------------------
  2045. // NB Must do this before destroying the startmenu since the hotkey list
  2046. // uses it's pidls in a lot of cases.
  2047. int HotkeyList_Save(void)
  2048. {
  2049.     int cCached = 0;
  2050.     TraceMsg(TF_TRAY, "HotkeyList_Save: Saving global hotkeys.");
  2051.     if (EVAL(g_ts.hdsaHKI))
  2052.     {
  2053.         int i, cItems;
  2054.         PHOTKEYITEM phki;
  2055.         LPITEMIDLIST pidl;
  2056.         int cbData = 0;
  2057.         TCHAR szValue[32];
  2058.         LPBYTE pData;
  2059.         ASSERT(IS_VALID_HANDLE(g_ts.hdsaHKI, DSA));
  2060.         cItems = DSA_GetItemCount(g_ts.hdsaHKI);
  2061.         for (i=0; i<cItems; i++)
  2062.         {
  2063.             phki = DSA_GetItemPtr(g_ts.hdsaHKI, i);
  2064.             // Non cached item?
  2065.             if (phki->wGHotkey && !(phki->wFlags & HKIF_CACHED))
  2066.             {
  2067.                 // Yep, save it.
  2068.                 pidl = ILCombine(phki->pidlFolder, phki->pidlItem);
  2069.                 if (pidl)
  2070.                 {
  2071.                     cbData = SIZEOF(WORD) + ILGetSize(pidl);
  2072.                     pData = LocalAlloc(GPTR, cbData);
  2073.                     if (pData)
  2074.                     {
  2075.                         // DebugMsg(DM_TRACE, "c.hl_s: Saving %x.", phki->wGHotkey );
  2076.                         *((LPWORD)pData) = phki->wGHotkey;
  2077.                         memcpy(pData+SIZEOF(WORD), pidl, cbData-SIZEOF(DWORD));
  2078.                         wsprintf(szValue, TEXT("%d"), cCached);
  2079.                         Reg_SetStruct(g_hkeyExplorer, c_szHotkeys, szValue, pData , cbData);
  2080.                         cCached++;
  2081.                         LocalFree(pData);
  2082.                     }
  2083.                     ILFree(pidl);
  2084.                 }
  2085.             }
  2086.         }
  2087.     }
  2088.     return cCached;
  2089. }
  2090. //----------------------------------------------------------------------------
  2091. HKEY Reg_EnumValueCreate(HKEY hkey, LPCTSTR pszSubkey)
  2092. {
  2093.     HKEY hkeyOut;
  2094.     if (RegOpenKeyEx(hkey, pszSubkey, 0L, KEY_ALL_ACCESS, &hkeyOut) == ERROR_SUCCESS)
  2095.     {
  2096.         return hkeyOut;
  2097.     }
  2098.     return(NULL);
  2099. }
  2100. //----------------------------------------------------------------------------
  2101. int Reg_EnumValue(HKEY hkey, int i, LPBYTE pData, int cbData)
  2102. {
  2103.     TCHAR szValue[MAX_PATH];
  2104.     DWORD cchValue;
  2105.     DWORD dw;
  2106.     DWORD dwType;
  2107.     cchValue = ARRAYSIZE(szValue);
  2108.     if (RegEnumValue(hkey, i, szValue, &cchValue, &dw, &dwType, pData,
  2109.         &cbData) == ERROR_SUCCESS)
  2110.     {
  2111.         return cbData;
  2112.     }
  2113.     return 0;
  2114. }
  2115. //----------------------------------------------------------------------------
  2116. BOOL Reg_EnumValueDestroy(HKEY hkey)
  2117. {
  2118.     return RegCloseKey(hkey) == ERROR_SUCCESS;
  2119. }
  2120. //----------------------------------------------------------------------------
  2121. int HotkeyList_Restore(HWND hwnd)
  2122. {
  2123.     int i = 0;
  2124.     HKEY hkey;
  2125.     LPBYTE pData;
  2126.     int cbData;
  2127.     WORD wGHotkey;
  2128.     int id;
  2129.     LPITEMIDLIST pidl;
  2130.     // If shdocvw fails to load for some reason, we will GP-fault if we
  2131.     // don't validate hdsaHKI.
  2132.     if (EVAL(g_ts.hdsaHKI))
  2133.     {
  2134.         TraceMsg(TF_TRAY, "HotkeyList_Restore: Restoring global hotkeys...");
  2135.         hkey = Reg_EnumValueCreate(g_hkeyExplorer, c_szHotkeys);
  2136.         if (hkey)
  2137.         {
  2138.             cbData = Reg_EnumValue(hkey, i, NULL, 0);
  2139.             while (cbData)
  2140.             {
  2141.                 pData = LocalAlloc(GPTR, cbData);
  2142.                 if (pData)
  2143.                 {
  2144.                     Reg_EnumValue(hkey, i, pData, cbData);
  2145.                     // Get the hotkey and the pidl components.
  2146.                     wGHotkey = *((LPWORD)pData);
  2147.                     pidl = ILClone((LPITEMIDLIST)(pData+SIZEOF(WORD)));
  2148.                     // DebugMsg(DM_TRACE, "c.hl_r: Restoring %x", wGHotkey);
  2149.                     id = HotkeyList_AddCached(wGHotkey, pidl);
  2150.                     if (id != -1)
  2151.                         Tray_RegisterHotkey(hwnd, id);
  2152.                     LocalFree(pData);
  2153.                 }
  2154.                 i++;
  2155.                 cbData = Reg_EnumValue(hkey, i, NULL, 0);
  2156.             }
  2157.             Reg_EnumValueDestroy(hkey);
  2158.             // Nuke the cached stuff.
  2159.             RegDeleteKey(g_hkeyExplorer, c_szHotkeys);
  2160.         }
  2161.     }
  2162.     return i;
  2163. }
  2164. //----------------------------------------------------------------------------
  2165. // NB Again, this takes window hotkey not a Global one.
  2166. // NB This doesn't delete cached hotkeys.
  2167. int HotkeyList_Remove(WORD wHotkey)
  2168. {
  2169.     if (EVAL(g_ts.hdsaHKI))
  2170.     {
  2171.         int i, cItems;
  2172.         PHOTKEYITEM phki;
  2173.         WORD wGHotkey;
  2174.         ASSERT(IS_VALID_HANDLE(g_ts.hdsaHKI, DSA));
  2175.         // DebugMsg(DM_IANELHK, "c.hl_r: Remove hotkey for %x" , wHotkey);
  2176.         // Unmap the modifiers.
  2177.         wGHotkey = MapHotkeyToGlobalHotkey(wHotkey);
  2178.         cItems = DSA_GetItemCount(g_ts.hdsaHKI);
  2179.         for (i=0; i<cItems; i++)
  2180.         {
  2181.             phki = DSA_GetItemPtr(g_ts.hdsaHKI, i);
  2182.             if (phki && !(phki->wFlags & HKIF_CACHED) && (phki->wGHotkey == wGHotkey))
  2183.             {
  2184.                 // DebugMsg(DM_IANELHK, "c.hl_r: Invalidating %d", i);
  2185.                 if (phki->wFlags & HKIF_FREEPIDLS)
  2186.                 {
  2187.                     if (phki->pidlFolder)
  2188.                         ILFree(phki->pidlFolder);
  2189.                     if (phki->pidlItem)
  2190.                         ILFree(phki->pidlItem);
  2191.                 }
  2192.                 phki->wGHotkey = 0;
  2193.                 phki->pidlFolder = NULL;
  2194.                 phki->pidlItem = NULL;
  2195.                 phki->wFlags &= ~HKIF_FREEPIDLS;
  2196.                 return i;
  2197.             }
  2198.         }
  2199.     }
  2200.     return -1;
  2201. }
  2202. //----------------------------------------------------------------------------
  2203. // NB This takes a global hotkey.
  2204. int HotkeyList_RemoveCached(WORD wGHotkey)
  2205. {
  2206.     int i, cItems;
  2207.     PHOTKEYITEM phki;
  2208.     ASSERT(IS_VALID_HANDLE(g_ts.hdsaHKI, DSA));
  2209.     // DebugMsg(DM_IANELHK, "c.hl_rc: Remove hotkey for %x" , wGHotkey);
  2210.     cItems = DSA_GetItemCount(g_ts.hdsaHKI);
  2211.     for (i=0; i<cItems; i++)
  2212.     {
  2213.         phki = DSA_GetItemPtr(g_ts.hdsaHKI, i);
  2214.         if (phki && (phki->wFlags & HKIF_CACHED) && (phki->wGHotkey == wGHotkey))
  2215.         {
  2216.             // DebugMsg(DM_IANELHK, "c.hl_r: Invalidating %d", i);
  2217.             if (phki->wFlags & HKIF_FREEPIDLS)
  2218.             {
  2219.                 if (phki->pidlFolder)
  2220.                     ILFree(phki->pidlFolder);
  2221.                 if (phki->pidlItem)
  2222.                     ILFree(phki->pidlItem);
  2223.             }
  2224.             phki->pidlFolder = NULL;
  2225.             phki->pidlItem = NULL;
  2226.             phki->wGHotkey = 0;
  2227.             phki->wFlags &= ~(HKIF_CACHED | HKIF_FREEPIDLS);
  2228.             return i;
  2229.         }
  2230.     }
  2231.     return -1;
  2232. }
  2233. //----------------------------------------------------------------------------
  2234. // NB Some (the ones not marked HKIF_FREEPIDLS) of the items in the list of hotkeys
  2235. // have pointers to idlists used by the filemenu so they are only valid for
  2236. // the lifetime of the filemenu.
  2237. BOOL HotkeyList_Create(void)
  2238. {
  2239.     if (!g_ts.hdsaHKI)
  2240.     {
  2241.         // DebugMsg(DM_TRACE, "c.hkl_c: Creating global hotkey list.");
  2242.         g_ts.hdsaHKI = DSA_Create(SIZEOF(HOTKEYITEM), 0);
  2243.     }
  2244.     if (g_ts.hdsaHKI)
  2245.         return TRUE;
  2246.     return FALSE;
  2247. }
  2248. void StartMenu_AddTask(IShellTaskScheduler* pSystemScheduler, int nFolder, int iPriority)
  2249. {
  2250.     LPITEMIDLIST pidlFolder;
  2251.     IShellHotKey * pshk;
  2252.     if (FAILED(CHotKey_Create(&pshk)))
  2253.         return;
  2254.     SHGetSpecialFolderLocation(NULL, nFolder, &pidlFolder);
  2255.     if (pidlFolder)
  2256.     {
  2257.         LPITEMIDLIST pidlParent = ILClone(pidlFolder);
  2258.         if (pidlParent)
  2259.         {
  2260.             IShellFolder* psf;
  2261.             ILRemoveLastID(pidlParent);
  2262.             psf = BindToFolder(pidlParent);
  2263.             if (psf)
  2264.             {
  2265.                 HRESULT hres;
  2266.                 IStartMenuTask * psmt;
  2267.                 BOOL bDesktop = (CSIDL_DESKTOPDIRECTORY == nFolder || CSIDL_COMMON_DESKTOPDIRECTORY == nFolder);
  2268.                 hres = CoCreateInstance(bDesktop ? &CLSID_DesktopTask : &CLSID_StartMenuTask,
  2269.                     NULL, CLSCTX_INPROC, &IID_IStartMenuTask, (void **) &psmt);
  2270.                 if (SUCCEEDED(hres))
  2271.                 {
  2272.                     DWORD  dwFlags = bDesktop ? 0 : ITSFT_RECURSE;
  2273.                     LONG   nMaxRecursion = bDesktop ? 32 : 2 ;
  2274.                     // Initialize the task.  Set task priority according to max
  2275.                     // folder depth.  We'll assume 32 is a reasonable number.
  2276.                     hres = psmt->lpVtbl->InitTaskSFT(psmt, psf, pidlFolder, nMaxRecursion, dwFlags, 32);
  2277.                     if (SUCCEEDED(hres))
  2278.                     {
  2279.                         IRunnableTask * ptask;
  2280.                         psmt->lpVtbl->InitTaskSMT(psmt, pshk, iPriority);
  2281.                         hres = psmt->lpVtbl->QueryInterface(psmt, &IID_IRunnableTask, (void **)&ptask);
  2282.                         if (SUCCEEDED(hres))
  2283.                         {
  2284.                             // Add it to the scheduler
  2285.                             hres = pSystemScheduler->lpVtbl->AddTask(pSystemScheduler, ptask,
  2286.                                                               &CLSID_StartMenuTask,
  2287.                                                               0, 32);
  2288.                             ptask->lpVtbl->Release(ptask);
  2289.                         }
  2290.                     }
  2291.                     psmt->lpVtbl->Release(psmt);
  2292.                 }
  2293.                 if (FAILED(hres))
  2294.                     TraceMsg(TF_ERROR, "StartMenu_AddTask: failed to create start menu task");
  2295.                 psf->lpVtbl->Release(psf);
  2296.             }
  2297.             ILFree(pidlParent);
  2298.         }
  2299.         ILFree(pidlFolder);
  2300.     }
  2301.     pshk->lpVtbl->Release(pshk);
  2302. }
  2303. //----------------------------------------------------------------------------
  2304. void StartMenu_Build()
  2305. {
  2306.     HRESULT hres;
  2307.     ATOMICRELEASET(g_ts._pmpStartMenu, IMenuPopup);
  2308.     ATOMICRELEASET(g_ts._pmbStartMenu, IMenuBand);
  2309.     g_uStartButtonAllowPopup = RegisterWindowMessage(TEXT("StartButtonAllowPopup"));
  2310.     hres = StartMenuHost_Create(&g_ts._pmpStartMenu, &g_ts._pmbStartMenu);
  2311.     if (SUCCEEDED(hres))
  2312.     {
  2313.         IBanneredBar* pbb;
  2314.         hres = g_ts._pmpStartMenu->lpVtbl->QueryInterface(g_ts._pmpStartMenu,
  2315.                                         &IID_IBanneredBar, (void**)&pbb);
  2316.         if (SUCCEEDED(hres))
  2317.         {
  2318.             pbb->lpVtbl->SetBitmap(pbb, g_ts.hbmpStartBkg);
  2319.             if (g_ts.fSMSmallIcons)
  2320.                 pbb->lpVtbl->SetIconSize(pbb, BMICON_SMALL);
  2321.             else
  2322.                 pbb->lpVtbl->SetIconSize(pbb, BMICON_LARGE);
  2323.             pbb->lpVtbl->Release(pbb);
  2324.         }
  2325.         HotkeyList_Create();
  2326.     }
  2327.     else
  2328.         TraceMsg(TF_ERROR, "Could not create StartMenu");
  2329. }
  2330. //----------------------------------------------------------------------------
  2331. void StartMenu_Destroy()
  2332. {
  2333.     IUnknown_SetSite((IUnknown*)g_ts._pmpStartMenu, NULL);
  2334.     ATOMICRELEASET(g_ts._pmpStartMenu, IMenuPopup);
  2335.     ATOMICRELEASET(g_ts._pmbStartMenu, IMenuBand);
  2336. }
  2337. //----------------------------------------------------------------------------
  2338. void _ForceStartButtonUp()
  2339. {
  2340.     MSG msg;
  2341.     // don't do that check message pos because it gets screwy with
  2342.     // keyboard cancel.  and besides, we always want it cleared after
  2343.     // track menu popup is done.
  2344.     // do it twice to be sure it's up due to the uDown cycling twice in
  2345.     // the subclassing stuff
  2346.     // pull off any button downs
  2347.     PeekMessage(&msg, g_ts.hwndStart, WM_LBUTTONDOWN, WM_LBUTTONDOWN, PM_REMOVE);
  2348.     SendMessage(g_ts.hwndStart, BM_SETSTATE, FALSE, 0);
  2349.     SendMessage(g_ts.hwndStart, BM_SETSTATE, FALSE, 0);
  2350.     // The user cancelled the Start menu, so a new press of the Windows key
  2351.     // should be considered "Reopen the Start Menu" instead of "Cancel the
  2352.     // Start Menu and return to the previous window."
  2353.     g_hwndPrevFocus = NULL;
  2354. }
  2355. int Tray_TrackMenu(HMENU hmenu)
  2356. {
  2357.     TPMPARAMS tpm;
  2358.     int iret;
  2359.     tpm.cbSize = SIZEOF(tpm);
  2360.     GetClientRect(g_ts.hwndStart, &tpm.rcExclude);
  2361.     MapWindowPoints(g_ts.hwndStart, NULL, (LPPOINT)&tpm.rcExclude, 2);
  2362.     SendMessage(g_ts.hwndTrayTips, TTM_ACTIVATE, FALSE, 0L);
  2363.     iret = TrackPopupMenuEx(hmenu, TPM_VERTICAL | TPM_BOTTOMALIGN | TPM_RETURNCMD,
  2364.                             tpm.rcExclude.left, tpm.rcExclude.bottom, v_hwndTray, &tpm);
  2365.     SendMessage(g_ts.hwndTrayTips, TTM_ACTIVATE, TRUE, 0L);
  2366.     return iret;
  2367. }
  2368. //----------------------------------------------------------------------------
  2369. // Keep track of the menu font name and weight so we can redo the startmenu if
  2370. // these change before getting notified via win.ini.
  2371. BOOL StartMenu_FontChange(void)
  2372. {
  2373.     NONCLIENTMETRICS ncm;
  2374.     static TCHAR lfFaceName[LF_FACESIZE] = TEXT("");
  2375.     static long lfHeight = 0;
  2376.     ncm.cbSize = SIZEOF(ncm);
  2377.     if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, SIZEOF(ncm), &ncm, FALSE ))
  2378.     {
  2379.         if (!*lfFaceName)
  2380.         {
  2381.             // DebugMsg(DM_TRACE, "sm_fc: No menu font - initing.");
  2382.             lstrcpy(lfFaceName, ncm.lfMenuFont.lfFaceName);
  2383.             lfHeight = ncm.lfMenuFont.lfHeight;
  2384.             return FALSE;
  2385.         }
  2386.         else if ((lstrcmpi(ncm.lfMenuFont.lfFaceName, lfFaceName) == 0) &&
  2387.             (ncm.lfMenuFont.lfHeight == lfHeight))
  2388.         {
  2389.             // DebugMsg(DM_TRACE, "sm_fc: Menu font unchanged.");
  2390.             return FALSE;
  2391.         }
  2392.         else
  2393.         {
  2394.             // DebugMsg(DM_TRACE, "sm_fc: Menu font changed.");
  2395.             lstrcpy(lfFaceName, ncm.lfMenuFont.lfFaceName);
  2396.             lfHeight = ncm.lfMenuFont.lfHeight;
  2397.             return TRUE;
  2398.         }
  2399.     }
  2400.     return FALSE;
  2401. }
  2402. /*------------------------------------------------------------------
  2403. ** Respond to a button's pressing by bringing up the appropriate menu.
  2404. ** Clean up the button depression when the menu is dismissed.
  2405. **------------------------------------------------------------------*/
  2406. void ToolbarMenu()
  2407. {
  2408.     RECTL    rcExclude;
  2409.     POINTL   ptPop;
  2410.     DWORD dwFlags = MPPF_KEYBOARD;      // Assume that we're popuping
  2411.                                         // up because of the keyboard
  2412.                                         // This is for the underlines on NT5
  2413.     if (g_ts.hwndStartBalloon)
  2414.     {
  2415.         DontShowTheStartButtonBalloonAnyMore();
  2416.         ShowWindow(g_ts.hwndStartBalloon, SW_HIDE);
  2417.         DestroyStartButtonBalloon();
  2418.     }
  2419.     SetActiveWindow(v_hwndTray);
  2420.     g_ts.bMainMenuInit = TRUE;
  2421.     GetClientRect(g_ts.hwndStart, (RECT *)&rcExclude);
  2422.     MapWindowRect(g_ts.hwndStart, HWND_DESKTOP, &rcExclude);
  2423.     ptPop.x = rcExclude.left;
  2424.     ptPop.y = rcExclude.top;
  2425.     // Close any Context Menus
  2426.     SendMessage(v_hwndTray, WM_CANCELMODE, 0, 0);
  2427.     // Is the "Activate" button down (If the buttons are swapped, then it's the
  2428.     // right button, otherwise the left button)
  2429.     if (GetKeyState(GetSystemMetrics(SM_SWAPBUTTON)?VK_RBUTTON:VK_LBUTTON) < 0)
  2430.     {
  2431.         dwFlags = 0;    // Then set to the default
  2432.     }
  2433.     else
  2434.     {
  2435.         // Since the user has launched the start button by Ctrl-Esc, or some other worldly
  2436.         // means, then turn the rect on.
  2437.         SendMessage(g_ts.hwndStart, WM_UPDATEUISTATE, MAKEWPARAM(UIS_CLEAR,
  2438.             UISF_HIDEFOCUS), 0);
  2439.     }
  2440.     if (g_ts._pmpStartMenu) {
  2441.         g_ts._pmpStartMenu->lpVtbl->Popup(g_ts._pmpStartMenu, &ptPop, &rcExclude, dwFlags);
  2442.         TraceMsg(DM_MISC, "e.tbm: dwFlags=%x (0=mouse 1=key)", dwFlags);
  2443.         UEMFireEvent(&UEMIID_SHELL, UEME_INSTRBROWSER, UEMF_INSTRUMENT, UIBW_UIINPUT, dwFlags ? UIBL_INPMENU : UIBL_INPMOUSE);
  2444.     }
  2445. }
  2446. //
  2447. // can't use SubtractRect sometimes because of inclusion limitations
  2448. //
  2449. void AppBarSubtractRect(PAPPBAR pab, LPRECT lprc)
  2450. {
  2451.     switch (pab->uEdge) {
  2452.         case ABE_TOP:
  2453.             if (pab->rc.bottom > lprc->top)
  2454.                 lprc->top = pab->rc.bottom;
  2455.             break;
  2456.         case ABE_LEFT:
  2457.             if (pab->rc.right > lprc->left)
  2458.                 lprc->left = pab->rc.right;
  2459.             break;
  2460.         case ABE_BOTTOM:
  2461.             if (pab->rc.top < lprc->bottom)
  2462.                 lprc->bottom = pab->rc.top;
  2463.             break;
  2464.         case ABE_RIGHT:
  2465.             if (pab->rc.left < lprc->right)
  2466.                 lprc->right = pab->rc.left;
  2467.             break;
  2468.     }
  2469. }
  2470. void AppBarSubtractRects(HMONITOR hmon, LPRECT lprc)
  2471. {
  2472.     int i;
  2473.     if (!g_ts.hdpaAppBars)
  2474.         return;
  2475.     i = DPA_GetPtrCount(g_ts.hdpaAppBars);
  2476.     while (i--)
  2477.     {
  2478.         PAPPBAR pab = (PAPPBAR)DPA_GetPtr(g_ts.hdpaAppBars, i);
  2479.         //
  2480.         // autohide bars are not in our DPA or live on the edge
  2481.         // BUGBUG: don't subtract the appbar if it is not always on top????
  2482.         // don't subtract the appbar if it's on a different display
  2483.         //
  2484.         if (hmon == MonitorFromRect(&pab->rc, MONITOR_DEFAULTTONULL))
  2485.             AppBarSubtractRect(pab, lprc);
  2486.     }
  2487. }
  2488. #define RWA_NOCHANGE      0
  2489. #define RWA_CHANGED       1
  2490. #define RWA_BOTTOMMOSTTRAY 2
  2491. // BUGBUG: (dli) This is a hack put in because bottommost tray is wierd, once
  2492. // it becomes a toolbar, this code should go away.
  2493. // In the bottommost tray case, even though the work area has not changed,
  2494. // we should notify the desktop.
  2495. int RecomputeWorkArea(HWND hwndCause, HMONITOR hmon, LPRECT prcWork)
  2496. {
  2497.     int iRet = RWA_NOCHANGE;
  2498.     MONITORINFO mi;
  2499.     //
  2500.     // tell everybody that this window changed positions _on_this_monitor_
  2501.     // note that this notify happens even if we don't change the work area
  2502.     // since it may cause another app to change the work area...
  2503.     //
  2504.     PostMessage(v_hwndTray, TM_RELAYPOSCHANGED, (WPARAM)hwndCause,
  2505.         (LPARAM)hmon);
  2506.     //
  2507.     // get the current info for this monitor
  2508.     // we subtract down from the display rectangle to build the work area
  2509.     //
  2510.     mi.cbSize = sizeof(mi);
  2511.     if (GetMonitorInfo(hmon, &mi))
  2512.     {
  2513.         //
  2514.         // don't subtract the tray if it is autohide
  2515.         // don't subtract the tray if it is not always on top
  2516.         // don't subtract the tray if it's on a different display
  2517.         //
  2518.         if (!(g_ts.uAutoHide & AH_ON) && g_ts.fAlwaysOnTop &&
  2519.             (hmon == g_ts.hmonStuck))
  2520.         {
  2521.             SubtractRect(prcWork, &mi.rcMonitor,
  2522.                          &g_ts.arStuckRects[g_ts.uStuckPlace]);
  2523.         }
  2524.         else
  2525.             *prcWork = mi.rcMonitor;
  2526.         //
  2527.         // now subtract off all the appbars on this display
  2528.         //
  2529.         AppBarSubtractRects(hmon, prcWork);
  2530.         //
  2531.         // return whether we changed anything
  2532.         //
  2533.         if (!EqualRect(prcWork, &mi.rcWork))
  2534.             iRet = RWA_CHANGED;
  2535.         else if (!(g_ts.uAutoHide & AH_ON) && (!g_ts.fAlwaysOnTop) &&
  2536.                  (!IsRectEmpty(&g_ts.arStuckRects[g_ts.uStuckPlace])))
  2537.             // NOTE: This is the bottommost case, it only applies for the tray.
  2538.             // this should be taken out when bottommost tray becomes toolbar
  2539.             iRet = RWA_BOTTOMMOSTTRAY;
  2540.     }
  2541.     else
  2542.     {
  2543.         // NOTE: This should never happen, if it does because of USER problem,
  2544.         // we just say NO CHANGE!!!!
  2545.         ASSERTMSG(FALSE, "GetMonitorInfo should never fail! We should have updated our hmonOld or hmonStuck on time");
  2546.         iRet = RWA_NOCHANGE;
  2547.     }
  2548.     
  2549.     return iRet;
  2550. }
  2551. //
  2552. // Nashville's debug USER draws silly "Monitor N CXxCYxBPP" crud on the display
  2553. //
  2554. #if !defined(WINNT) && defined(DEBUG)
  2555. #define FORCE_INVALIDATE_WORKAREA TRUE
  2556. #else
  2557. #define FORCE_INVALIDATE_WORKAREA FALSE
  2558. #endif
  2559. void RedrawDesktop(LPRECT prcWork)
  2560. {
  2561.     // This rect point should always be valid (dli)
  2562.     RIP(prcWork);
  2563.     
  2564.     if (v_hwndDesktop && (FORCE_INVALIDATE_WORKAREA || g_fCleanBoot))
  2565.     {
  2566.         MapWindowPoints(NULL, v_hwndDesktop, (LPPOINT)prcWork, 2);
  2567.         DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.sac invalidating desktop rect {%d,%d,%d,%d}"), prcWork->left, prcWork->top, prcWork->right, prcWork->bottom);
  2568.         RedrawWindow(v_hwndDesktop, prcWork, NULL,
  2569.                      RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN);
  2570.     }
  2571. }
  2572. void StuckAppChange(HWND hwndCause, LPCRECT prcOld, LPCRECT prcNew, BOOL bTray)
  2573. {
  2574.     RECT rcWork1, rcWork2;
  2575.     HMONITOR hmon1, hmon2 = 0;
  2576.     int iChange = 0;
  2577.     //
  2578.     // BUGBUG:
  2579.     // there are cases where we end up setting the work area multiple times
  2580.     // we need to keep a static array of displays that have changed and a
  2581.     //  reenter count so we can avoid pain of sending notifies to the whole
  2582.     //  planet...
  2583.     //
  2584.     DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.sac from AppBar %08X"), hwndCause);
  2585.     //
  2586.     // see if the work area changed on the display containing prcOld
  2587.     //
  2588.     if (prcOld)
  2589.     {
  2590.         if (bTray)
  2591.             hmon1 = g_ts.hmonOld;
  2592.         else
  2593.             hmon1 = MonitorFromRect(prcOld, MONITOR_DEFAULTTONEAREST);
  2594.         DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.sac old pos {%d,%d,%d,%d} on monitor %08X"), prcOld->left, prcOld->top, prcOld->right, prcOld->bottom, hmon1);
  2595.         if (hmon1)
  2596.         {
  2597.             int iret = RecomputeWorkArea(hwndCause, hmon1, &rcWork1);
  2598.             if (iret == RWA_CHANGED)
  2599.                 iChange = 1;
  2600.             if (iret == RWA_BOTTOMMOSTTRAY)
  2601.                 iChange = 4;
  2602.         }
  2603.     }
  2604.     else
  2605.         hmon1 = NULL;
  2606.     //
  2607.     // see if the work area changed on the display containing prcNew
  2608.     //
  2609.     if (prcNew)
  2610.     {
  2611.         hmon2 = MonitorFromRect(prcNew, MONITOR_DEFAULTTONULL);
  2612.         DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.sac new pos {%d,%d,%d,%d} on monitor %08X"), prcNew->left, prcNew->top, prcNew->right, prcNew->bottom, hmon2);
  2613.         if (hmon2 && (hmon2 != hmon1))
  2614.         {
  2615.             int iret = RecomputeWorkArea(hwndCause, hmon2, &rcWork2);
  2616.             if (iret == RWA_CHANGED)
  2617.                 iChange |= 2;
  2618.             else if (iret == RWA_BOTTOMMOSTTRAY && (!iChange))
  2619.                 iChange = 4;
  2620.         }
  2621.     }
  2622.     //
  2623.     // did the prcOld's display's work area change?
  2624.     //
  2625.     if (iChange & 1)
  2626.     {
  2627.         DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.sac changing work area for monitor %08X"), hmon1);
  2628.         // only send SENDWININICHANGE if the desktop has been created (otherwise
  2629.         // we will hang the explorer because the main thread is currently blocked)
  2630.         SystemParametersInfo(SPI_SETWORKAREA, TRUE, &rcWork1,
  2631.                              (iChange == 1 && v_hwndDesktop)? SPIF_SENDWININICHANGE : 0);
  2632.         RedrawDesktop(&rcWork1);
  2633.     }
  2634.     //
  2635.     // did the prcOld's display's work area change?
  2636.     //
  2637.     if (iChange & 2)
  2638.     {
  2639.         DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.sac changing work area for monitor %08X"), hmon2);
  2640.         // only send SENDWININICHANGE if the desktop has been created (otherwise
  2641.         // we will hang the explorer because the main thread is currently blocked)
  2642.         SystemParametersInfo(SPI_SETWORKAREA, TRUE, &rcWork2,
  2643.                              v_hwndDesktop ? SPIF_SENDWININICHANGE : 0);
  2644.         RedrawDesktop(&rcWork2);
  2645.     }
  2646.     // only send if the desktop has been created...
  2647.     // need to send if it's from the tray or any outside app that causes size change
  2648.     // from the tray because autohideness will affect desktop size even if it's not always on top
  2649.     if ((bTray || iChange == 4) && v_hwndDesktop )
  2650.         SendMessage(v_hwndDesktop, WM_SIZE, 0, 0);
  2651. }
  2652. void Tray_StuckTrayChange()
  2653. {
  2654.     // We used to blow off the StuckAppChange when the tray was in autohide
  2655.     // mode, since moving or resizing an autohid tray doesn't change the
  2656.     // work area.  Now we go ahead with the StuckAppChange in this case
  2657.     // too.  The reason is that we can get into a state where the work area
  2658.     // size is incorrect, and we want the taskbar to always be self-repairing
  2659.     // in this case (so that resizing or moving the taskbar will correct the
  2660.     // work area size).
  2661.     //
  2662.     // pass a NULL window here since we don't want to hand out our window and
  2663.     // the tray doesn't get these anyway (nobody cares as long as its not them)
  2664.     //
  2665.     StuckAppChange(NULL, &g_ts.rcOldTray,
  2666.         &g_ts.arStuckRects[g_ts.uStuckPlace], TRUE);
  2667.     //
  2668.     // save off the new tray position...
  2669.     //
  2670.     g_ts.rcOldTray = g_ts.arStuckRects[g_ts.uStuckPlace];
  2671. }
  2672. UINT Tray_RecalcStuckPos(LPRECT prc)
  2673. {
  2674.     RECT rcDummy;
  2675.     POINT pt;
  2676.     if (!prc)
  2677.     {
  2678.         DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_rsp no rect supplied, using window rect"));
  2679.         prc = &rcDummy;
  2680.         GetWindowRect(v_hwndTray, prc);
  2681.     }
  2682.     // use the center of the original drag rect as a staring point
  2683.     pt.x = prc->left + RECTWIDTH(*prc) / 2;
  2684.     pt.y = prc->top + RECTHEIGHT(*prc) / 2;
  2685.     DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_rsp rect is {%d, %d, %d, %d} point is {%d, %d}"), prc->left, prc->top, prc->right, prc->bottom, pt.x, pt.y);
  2686.     // reset this so the drag code won't give it preference
  2687.     g_ts.uMoveStuckPlace = (UINT)-1;
  2688.     // simulate a drag back to figure out where we originated from
  2689.     // you may be tempted to remove this.  before you do think about dragging
  2690.     // the tray across monitors and then hitting ESC...
  2691.     return Tray_CalcDragPlace(pt);
  2692. }
  2693. /*------------------------------------------------------------------
  2694. ** the position is changing in response to a move operation.
  2695. **
  2696. ** if the docking status changed, we need to get a new size and
  2697. ** maybe a new frame style.  change the WINDOWPOS to reflect
  2698. ** these changes accordingly.
  2699. **------------------------------------------------------------------*/
  2700. void Tray_DoneMoving(LPWINDOWPOS lpwp)
  2701. {
  2702.     RECT rc, *prc;
  2703.     if (g_ts.uMoveStuckPlace == (UINT)-1)
  2704.         return;
  2705.     if (g_ts.fSysSizing)
  2706.         g_ts._fDeferedPosRectChange = TRUE;
  2707.     rc.left   = lpwp->x;
  2708.     rc.top    = lpwp->y;
  2709.     rc.right  = lpwp->x + lpwp->cx;
  2710.     rc.bottom = lpwp->y + lpwp->cy;
  2711.     prc = &g_ts.arStuckRects[g_ts.uMoveStuckPlace];
  2712.     if (!EqualRect(prc, &rc))
  2713.     {
  2714.         g_ts.uMoveStuckPlace = Tray_RecalcStuckPos(&rc);
  2715.         prc = &g_ts.arStuckRects[g_ts.uMoveStuckPlace];
  2716.     }
  2717.     // Get the new hmonitor
  2718.     g_ts.hmonStuck = MonitorFromRect(prc, MONITOR_DEFAULTTONEAREST);
  2719.     if (g_ts.hwndView)
  2720.         Tray_HandleSizing(0, prc, g_ts.uMoveStuckPlace);
  2721.     lpwp->x = prc->left;
  2722.     lpwp->y = prc->top;
  2723.     lpwp->cx = RECTWIDTH(*prc);
  2724.     lpwp->cy = RECTHEIGHT(*prc);
  2725.     lpwp->flags &= ~(SWP_NOMOVE | SWP_NOSIZE);
  2726.     // if we were autohiding, we need to update our appbar autohide rect
  2727.     if (g_ts.uAutoHide & AH_ON)
  2728.     {
  2729.         // unregister us from the old side
  2730.         IAppBarSetAutoHideBar(v_hwndTray, FALSE, g_ts.uStuckPlace);
  2731.     }
  2732.     // All that work might've changed g_ts.uMoveStuckPlace (since there
  2733.     // was a lot of message traffic), so check one more time.
  2734.     // Somehow, NT Stress manages to get us in here with an invalid
  2735.     // uMoveStuckPlace.
  2736.     if (IsValidSTUCKPLACE(g_ts.uMoveStuckPlace))
  2737.     {
  2738.         // remember the new state
  2739.         g_ts.uStuckPlace = g_ts.uMoveStuckPlace;
  2740.     }
  2741.     g_ts.uMoveStuckPlace = (UINT)-1;
  2742.     BandSite_SetMode(g_ts.ptbs, STUCK_HORIZONTAL(g_ts.uStuckPlace) ? 0 : DBIF_VIEWMODE_VERTICAL);
  2743.     if ((g_ts.uAutoHide & AH_ON) &&
  2744.         !IAppBarSetAutoHideBar(v_hwndTray, TRUE, g_ts.uStuckPlace))
  2745.     {
  2746.         Tray_AutoHideCollision();
  2747.     }
  2748. }
  2749. UINT Tray_CalcDragPlace(POINT pt)
  2750. {
  2751.     UINT uPlace = g_ts.uMoveStuckPlace;
  2752.     DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_cdp starting point is {%d, %d}"), pt.x, pt.y);
  2753.     //
  2754.     // if the mouse is currently over the tray position leave it alone
  2755.     //
  2756.     if ((uPlace == (UINT)-1) || !PtInRect(&g_ts.arStuckRects[uPlace], pt))
  2757.     {
  2758.         HMONITOR hmonDrag;
  2759.         SIZE screen, error;
  2760.         UINT uHorzEdge, uVertEdge;
  2761.         RECT rcDisplay, *prcStick;
  2762.         //
  2763.         // which display is the mouse on?
  2764.         //
  2765.         hmonDrag = Tray_GetDisplayRectFromPoint(&rcDisplay, pt,
  2766.             MONITOR_DEFAULTTOPRIMARY);
  2767.         DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_cdp monitor is %08X"), hmonDrag);
  2768.         //
  2769.         // re-origin at zero to make calculations simpler
  2770.         //
  2771.         screen.cx =  RECTWIDTH(rcDisplay);
  2772.         screen.cy = RECTHEIGHT(rcDisplay);
  2773.         pt.x -= rcDisplay.left;
  2774.         pt.y -= rcDisplay.top;
  2775.         //
  2776.         // are we closer to the left or right side of this display?
  2777.         //
  2778.         if (pt.x < (screen.cx / 2))
  2779.         {
  2780.             uVertEdge = STICK_LEFT;
  2781.             error.cx = pt.x;
  2782.         }
  2783.         else
  2784.         {
  2785.             uVertEdge = STICK_RIGHT;
  2786.             error.cx = screen.cx - pt.x;
  2787.         }
  2788.         //
  2789.         // are we closer to the top or bottom side of this display?
  2790.         //
  2791.         if (pt.y < (screen.cy / 2))
  2792.         {
  2793.             uHorzEdge = STICK_TOP;
  2794.             error.cy = pt.y;
  2795.         }
  2796.         else
  2797.         {
  2798.             uHorzEdge = STICK_BOTTOM;
  2799.             error.cy = screen.cy - pt.y;
  2800.         }
  2801.         //
  2802.         // closer to a horizontal or vertical edge?
  2803.         //
  2804.         uPlace = ((error.cy * screen.cx) > (error.cx * screen.cy))?
  2805.             uVertEdge : uHorzEdge;
  2806.         // which StuckRect should we use?
  2807.         prcStick = &g_ts.arStuckRects[uPlace];
  2808.         //
  2809.         // need to recalc stuck rect for new monitor?
  2810.         //
  2811.         if ((hmonDrag != Tray_GetDisplayRectFromRect(NULL, prcStick,
  2812.             MONITOR_DEFAULTTONULL)))
  2813.         {
  2814.             DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_cdp re-snapping rect for new display"));
  2815.             Tray_MakeStuckRect(prcStick, &rcDisplay, g_ts.sStuckWidths, uPlace);
  2816.         }
  2817.     }
  2818.     DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_cdp edge is %d, rect is {%d, %d, %d, %d}"), uPlace, g_ts.arStuckRects[uPlace].left, g_ts.arStuckRects[uPlace].top, g_ts.arStuckRects[uPlace].right, g_ts.arStuckRects[uPlace].bottom);
  2819.     ASSERT(IsValidSTUCKPLACE(uPlace));
  2820.     return uPlace;
  2821. }
  2822. //------------------------------------------------------------------
  2823. void Tray_HandleMoving(WPARAM wParam, LPRECT lprc)
  2824. {
  2825.     POINT ptCursor;
  2826.     GetCursorPos(&ptCursor);
  2827.     g_ts.uMoveStuckPlace = Tray_CalcDragPlace(ptCursor);
  2828.     *lprc = g_ts.arStuckRects[g_ts.uMoveStuckPlace];
  2829.     Tray_HandleSizing(wParam, lprc, g_ts.uMoveStuckPlace);
  2830. }
  2831. //------------------------------------------------------------------
  2832. // store the tray size when dragging is finished
  2833. void Tray_SnapshotStuckRectSize(UINT uPlace)
  2834. {
  2835.     RECT rcDisplay, *prc = &g_ts.arStuckRects[uPlace];
  2836.     //
  2837.     // record the width of this stuck rect
  2838.     //
  2839.     if (STUCK_HORIZONTAL(uPlace))
  2840.         g_ts.sStuckWidths.cy = RECTHEIGHT(*prc);
  2841.     else
  2842.         g_ts.sStuckWidths.cx = RECTWIDTH(*prc);
  2843.     //
  2844.     // we only present a horizontal or vertical size to the end user
  2845.     // so update the StuckRect on the other side of the screen to match
  2846.     //
  2847.     Tray_GetStuckDisplayRect(uPlace, &rcDisplay);
  2848.     uPlace += 2;
  2849.     uPlace %= 4;
  2850.     prc = &g_ts.arStuckRects[uPlace];
  2851.     Tray_MakeStuckRect(prc, &rcDisplay, g_ts.sStuckWidths, uPlace);
  2852. }
  2853. //------------------------------------------------------------------
  2854. // Size the icon area to fill as much of the tray window as it can.
  2855. //------------------------------------------------------------------
  2856. void Tray_SizeWindows()
  2857. {
  2858.     RECT rcView, rcClock, rcClient, rcDesktop;
  2859.     int fHiding;
  2860.     if (!g_ts.hwndRebar || !g_ts.hwndMain || !g_ts.hwndNotify)
  2861.         return;
  2862.     fHiding = (g_ts.uAutoHide & AH_HIDING);
  2863.     if (fHiding)
  2864.     {
  2865.         TrayShowWindow(SW_HIDE);
  2866.     }
  2867.     // remember our current size
  2868.     Tray_SnapshotStuckRectSize(g_ts.uStuckPlace);
  2869.     GetClientRect(g_ts.hwndMain, &rcClient);
  2870.     Tray_AlignStartButton();
  2871.     Tray_GetWindowSizes(&rcClient, &rcView, &rcClock, &rcDesktop);
  2872.     InvalidateRect(g_ts.hwndStart, NULL, TRUE);
  2873.     // position the view
  2874.     SetWindowPos(g_ts.hwndRebar, NULL, rcView.left, rcView.top,
  2875.                  RECTWIDTH(rcView), RECTHEIGHT(rcView),