Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

9156 lines
271 KiB

  1. #include "cabinet.h"
  2. #include <wtsapi32.h> // for NOTIFY_FOR_THIS_SESSION
  3. #include <winsta.h> // for disconnect and reconnect messages from terminal server
  4. #include "mmsysp.h"
  5. #include "rcids.h"
  6. #include "dlg.h"
  7. #include <atlstuff.h>
  8. #include <shlapip.h>
  9. #include "trayclok.h"
  10. #include <help.h> // help ids
  11. #include <desktray.h>
  12. #include "util.h"
  13. #include "tray.h"
  14. #if defined(FE_IME)
  15. #include <immp.h>
  16. #endif
  17. #include <regstr.h>
  18. #include "bandsite.h"
  19. #include "startmnu.h"
  20. #include "uemapp.h"
  21. #include <uxthemep.h>
  22. #define NO_NOTIFYSUBCLASSWNDPROC
  23. #include "cwndproc.cpp"
  24. #include "desktop2.h"
  25. #include "mixer.h"
  26. #include "strsafe.h"
  27. #define DM_FOCUS 0 // focus
  28. #define DM_SHUTDOWN TF_TRAY // shutdown
  29. #define DM_UEMTRACE TF_TRAY // timer service, other UEM stuff
  30. #define DM_MISC 0 // miscellany
  31. const GUID CLSID_MSUTBDeskBand = {0x540d8a8b, 0x1c3f, 0x4e32, 0x81, 0x32, 0x53, 0x0f, 0x6a, 0x50, 0x20, 0x90};
  32. // From Desktop2\proglist.cpp
  33. HRESULT AddMenuItemsCacheTask(IShellTaskScheduler* pSystemScheduler, BOOL fKeepCacheWhenFinished);
  34. // import the WIN31 Compatibility HACKs from the shell32.dll
  35. STDAPI_(void) CheckWinIniForAssocs(void);
  36. // hooks to Shell32.dll
  37. STDAPI CheckDiskSpace();
  38. STDAPI CheckStagingArea();
  39. // startmnu.cpp
  40. void HandleFirstTime();
  41. HWND v_hwndDesktop = NULL;
  42. HWND v_hwndTray = NULL;
  43. HWND v_hwndStartPane = NULL;
  44. BOOL g_fDesktopRaised = FALSE;
  45. BOOL g_fInSizeMove = FALSE;
  46. UINT _uMsgEnableUserTrackedBalloonTips = 0;
  47. void ClearRecentDocumentsAndMRUStuff(BOOL fBroadcastChange);
  48. void DoTaskBarProperties(HWND hwnd, DWORD dwFlags);
  49. void ClassFactory_Start();
  50. void ClassFactory_Stop();
  51. //
  52. // Settings UI entry point types.
  53. //
  54. typedef void (WINAPI *PTRAYPROPSHEETCALLBACK)(DWORD nStartPage);
  55. typedef void (WINAPI *PSETTINGSUIENTRY)(PTRAYPROPSHEETCALLBACK);
  56. // Shell perf automation
  57. extern DWORD g_dwShellStartTime;
  58. extern DWORD g_dwShellStopTime;
  59. extern DWORD g_dwStopWatchMode;
  60. CTray c_tray;
  61. // from explorer\desktop2
  62. STDAPI DesktopV2_Create(
  63. IMenuPopup **ppmp, IMenuBand **ppmb, void **ppvStartPane);
  64. STDAPI DesktopV2_Build(void *pvStartPane);
  65. // dyna-res change for multi-config hot/warm-doc
  66. void HandleDisplayChange(int x, int y, BOOL fCritical);
  67. DWORD GetMinDisplayRes(void);
  68. // timer IDs
  69. #define IDT_AUTOHIDE 2
  70. #define IDT_AUTOUNHIDE 3
  71. #ifdef DELAYWININICHANGE
  72. #define IDT_DELAYWININICHANGE 5
  73. #endif
  74. #define IDT_DESKTOP 6
  75. #define IDT_PROGRAMS IDM_PROGRAMS
  76. #define IDT_RECENT IDM_RECENT
  77. #define IDT_REBUILDMENU 7
  78. #define IDT_HANDLEDELAYBOOTSTUFF 8
  79. #define IDT_REVERTPROGRAMS 9
  80. #define IDT_REVERTRECENT 10
  81. #define IDT_REVERTFAVORITES 11
  82. #define IDT_STARTMENU 12
  83. #define IDT_ENDUNHIDEONTRAYNOTIFY 13
  84. #define IDT_SERVICE0 14
  85. #define IDT_SERVICE1 15
  86. #define IDT_SERVICELAST IDT_SERVICE1
  87. #define IDT_SAVESETTINGS 17
  88. #define IDT_ENABLEUNDO 18
  89. #define IDT_STARTUPFAILED 19
  90. #define IDT_CHECKDISKSPACE 21
  91. #define IDT_STARTBUTTONBALLOON 22
  92. #define IDT_CHANGENOTIFY 23
  93. #define IDT_COFREEUNUSED 24
  94. #define IDT_DESKTOPCLEANUP 25
  95. #define FADEINDELAY 100
  96. #define BALLOONTIPDELAY 10000 // default balloon time copied from traynot.cpp
  97. // INSTRUMENTATION WARNING: If you change anything here, make sure to update instrument.c
  98. // we need to start at 500 because we're now sharing the hotkey handler
  99. // with shortcuts.. they use an index array so they need to be 0 based
  100. // NOTE, this constant is also in desktop.cpp, so that we can forward hotkeys from the desktop for
  101. // NOTE, app compatibility.
  102. #define GHID_FIRST 500
  103. enum
  104. {
  105. GHID_RUN = GHID_FIRST,
  106. GHID_MINIMIZEALL,
  107. GHID_UNMINIMIZEALL,
  108. GHID_HELP,
  109. GHID_EXPLORER,
  110. GHID_FINDFILES,
  111. GHID_FINDCOMPUTER,
  112. GHID_TASKTAB,
  113. GHID_TASKSHIFTTAB,
  114. GHID_SYSPROPERTIES,
  115. GHID_DESKTOP,
  116. GHID_TRAYNOTIFY,
  117. GHID_MAX
  118. };
  119. const DWORD GlobalKeylist[] =
  120. {
  121. MAKELONG(TEXT('R'), MOD_WIN),
  122. MAKELONG(TEXT('M'), MOD_WIN),
  123. MAKELONG(TEXT('M'), MOD_SHIFT|MOD_WIN),
  124. MAKELONG(VK_F1,MOD_WIN),
  125. MAKELONG(TEXT('E'),MOD_WIN),
  126. MAKELONG(TEXT('F'),MOD_WIN),
  127. MAKELONG(TEXT('F'), MOD_CONTROL|MOD_WIN),
  128. MAKELONG(VK_TAB, MOD_WIN),
  129. MAKELONG(VK_TAB, MOD_WIN|MOD_SHIFT),
  130. MAKELONG(VK_PAUSE,MOD_WIN),
  131. MAKELONG(TEXT('D'),MOD_WIN),
  132. MAKELONG(TEXT('B'),MOD_WIN),
  133. };
  134. CTray::CTray() : _fCanSizeMove(TRUE), _fIsLogoff(FALSE), _fIsDesktopConnected(TRUE)
  135. {
  136. }
  137. void CTray::ClosePopupMenus()
  138. {
  139. if (_pmpStartMenu)
  140. _pmpStartMenu->OnSelect(MPOS_FULLCANCEL);
  141. if (_pmpStartPane)
  142. _pmpStartPane->OnSelect(MPOS_FULLCANCEL);
  143. }
  144. BOOL Tray_StartPanelEnabled()
  145. {
  146. SHELLSTATE ss = {0};
  147. SHGetSetSettings(&ss, SSF_STARTPANELON, FALSE);
  148. return ss.fStartPanelOn;
  149. }
  150. //
  151. // The StartButtonBalloonTip registry value can have one of these values:
  152. //
  153. // 0 (or nonexistent): User has never clicked the Start Button.
  154. // 1: User has clicked the Start Button on a pre-Whistler system.
  155. // 2: User has clicked the Start Button on a Whistler system.
  156. //
  157. // In case 0, we always want to show the balloon tip regardless of whether
  158. // the user is running Classic or Personal.
  159. //
  160. // In case 1, we want to show the balloon tip if the user is using the
  161. // Personal Start Menu, but not if using Classic (since he's already
  162. // seen the Classic Start Menu). In the Classic case, upgrade the counter
  163. // to 2 so the user won't be annoyed when they switch from Classic to
  164. // Personal.
  165. //
  166. // In case 2, we don't want to show the balloon tip at all since the
  167. // user has seen all we have to offer.
  168. //
  169. BOOL CTray::_ShouldWeShowTheStartButtonBalloon()
  170. {
  171. DWORD dwType;
  172. DWORD dwData = 0;
  173. DWORD cbSize = sizeof(DWORD);
  174. SHGetValue(HKEY_CURRENT_USER, REGSTR_EXPLORER_ADVANCED,
  175. TEXT("StartButtonBalloonTip"), &dwType, (BYTE*)&dwData, &cbSize);
  176. if (Tray_StartPanelEnabled())
  177. {
  178. // Personal Start Menu is enabled, so show the balloon if the
  179. // user has never logged on to a Whistler machine before.
  180. return dwData < 2;
  181. }
  182. else
  183. {
  184. // Classic Start Menu is enabled.
  185. switch (dwData)
  186. {
  187. case 0:
  188. // User has never seen the Start Menu before, not even the
  189. // classic one. So show the tip.
  190. return TRUE;
  191. case 1:
  192. // User has already seen the Classic Start Menu, so don't
  193. // prompt them again. Note that this means that they aren't
  194. // prompted when they turn on the Personal Start Menu, but
  195. // that's okay, because by the time they switch to Personal,
  196. // they clearly have demonstrated that they know how the
  197. // Start Button works and don't need a tip.
  198. _DontShowTheStartButtonBalloonAnyMore();
  199. return FALSE;
  200. default:
  201. // User has seen Whistler Start menu before, so don't show tip.
  202. return FALSE;
  203. }
  204. }
  205. }
  206. //
  207. // Set the value to 2 to indicate that the user has seen a Whistler
  208. // Start Menu (either Classic or Personal).
  209. //
  210. void CTray::_DontShowTheStartButtonBalloonAnyMore()
  211. {
  212. DWORD dwData = 2;
  213. SHSetValue(HKEY_CURRENT_USER, REGSTR_EXPLORER_ADVANCED,
  214. TEXT("StartButtonBalloonTip"), REG_DWORD, (BYTE*)&dwData, sizeof(dwData));
  215. }
  216. void CTray::_DestroyStartButtonBalloon()
  217. {
  218. if (_hwndStartBalloon)
  219. {
  220. DestroyWindow(_hwndStartBalloon);
  221. _hwndStartBalloon = NULL;
  222. }
  223. KillTimer(_hwnd, IDT_STARTBUTTONBALLOON);
  224. }
  225. void CTray::CreateStartButtonBalloon(UINT idsTitle, UINT idsMessage)
  226. {
  227. if (!_hwndStartBalloon)
  228. {
  229. _hwndStartBalloon = CreateWindow(TOOLTIPS_CLASS, NULL,
  230. WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP | TTS_BALLOON,
  231. CW_USEDEFAULT, CW_USEDEFAULT,
  232. CW_USEDEFAULT, CW_USEDEFAULT,
  233. _hwnd, NULL, hinstCabinet,
  234. NULL);
  235. if (_hwndStartBalloon)
  236. {
  237. // set the version so we can have non buggy mouse event forwarding
  238. SendMessage(_hwndStartBalloon, CCM_SETVERSION, COMCTL32_VERSION, 0);
  239. SendMessage(_hwndStartBalloon, TTM_SETMAXTIPWIDTH, 0, (LPARAM)300);
  240. // taskbar windows are themed under Taskbar subapp name
  241. SendMessage(_hwndStartBalloon, TTM_SETWINDOWTHEME, 0, (LPARAM)c_wzTaskbarTheme);
  242. // Tell the Start Menu that this is a special balloon tip
  243. SetProp(_hwndStartBalloon, PROP_DV2_BALLOONTIP, DV2_BALLOONTIP_STARTBUTTON);
  244. }
  245. }
  246. if (_hwndStartBalloon)
  247. {
  248. TCHAR szTip[MAX_PATH];
  249. szTip[0] = TEXT('\0');
  250. LoadString(hinstCabinet, idsMessage, szTip, ARRAYSIZE(szTip));
  251. if (szTip[0])
  252. {
  253. RECT rc;
  254. TOOLINFO ti = {0};
  255. ti.cbSize = sizeof(ti);
  256. ti.uFlags = TTF_IDISHWND | TTF_TRACK | TTF_TRANSPARENT;
  257. ti.hwnd = _hwnd;
  258. ti.uId = (UINT_PTR)_hwndStart;
  259. //ti.lpszText = NULL;
  260. SendMessage(_hwndStartBalloon, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&ti);
  261. SendMessage(_hwndStartBalloon, TTM_TRACKACTIVATE, (WPARAM)FALSE, (LPARAM)0);
  262. ti.lpszText = szTip;
  263. SendMessage(_hwndStartBalloon, TTM_UPDATETIPTEXT, 0, (LPARAM)&ti);
  264. LoadString(hinstCabinet, idsTitle, szTip, ARRAYSIZE(szTip));
  265. if (szTip[0])
  266. {
  267. SendMessage(_hwndStartBalloon, TTM_SETTITLE, TTI_INFO, (LPARAM)szTip);
  268. }
  269. GetWindowRect(_hwndStart, &rc);
  270. SendMessage(_hwndStartBalloon, TTM_TRACKPOSITION, 0, MAKELONG((rc.left + rc.right)/2, rc.top));
  271. SetWindowZorder(_hwndStartBalloon, HWND_TOPMOST);
  272. SendMessage(_hwndStartBalloon, TTM_TRACKACTIVATE, (WPARAM)TRUE, (LPARAM)&ti);
  273. SetTimer(_hwnd, IDT_STARTBUTTONBALLOON, BALLOONTIPDELAY, NULL);
  274. }
  275. }
  276. }
  277. void CTray::_ShowStartButtonToolTip()
  278. {
  279. if (!_ShouldWeShowTheStartButtonBalloon() || SHRestricted(REST_NOSMBALLOONTIP))
  280. {
  281. PostMessage(_hwnd, TM_SHOWTRAYBALLOON, TRUE, 0);
  282. return;
  283. }
  284. if (Tray_StartPanelEnabled())
  285. {
  286. // In order to display the Start Menu, we need foreground activation
  287. // so keyboard focus will work properly.
  288. if (SetForegroundWindow(_hwnd))
  289. {
  290. // Inform the tray that start button is auto-popping, so the tray
  291. // can hold off on showing balloons.
  292. PostMessage(_hwnd, TM_SHOWTRAYBALLOON, FALSE, 0);
  293. // This pushes the start button and causes the start menu to popup.
  294. SendMessage(GetDlgItem(_hwnd, IDC_START), BM_SETSTATE, TRUE, 0);
  295. // Once successfully done once, don't do it again.
  296. _DontShowTheStartButtonBalloonAnyMore();
  297. }
  298. }
  299. else
  300. {
  301. PostMessage(_hwnd, TM_SHOWTRAYBALLOON, TRUE, 0);
  302. CreateStartButtonBalloon(IDS_STARTMENUBALLOON_TITLE, IDS_STARTMENUBALLOON_TIP);
  303. }
  304. }
  305. BOOL CTray::_CreateClockWindow()
  306. {
  307. _hwndNotify = _trayNotify.TrayNotifyCreate(_hwnd, IDC_CLOCK, hinstCabinet);
  308. SendMessage(_hwndNotify, TNM_UPDATEVERTICAL, 0, !STUCK_HORIZONTAL(_uStuckPlace));
  309. return BOOLFROMPTR(_hwndNotify);
  310. }
  311. BOOL CTray::_InitTrayClass()
  312. {
  313. WNDCLASS wc = {0};
  314. wc.lpszClassName = TEXT(WNDCLASS_TRAYNOTIFY);
  315. wc.style = CS_DBLCLKS;
  316. wc.lpfnWndProc = s_WndProc;
  317. wc.hInstance = hinstCabinet;
  318. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  319. wc.hbrBackground = (HBRUSH)(COLOR_3DFACE+1);
  320. wc.cbWndExtra = sizeof(LONG_PTR);
  321. return RegisterClass(&wc);
  322. }
  323. HFONT CTray::_CreateStartFont(HWND hwndTray)
  324. {
  325. HFONT hfontStart = NULL;
  326. NONCLIENTMETRICS ncm;
  327. ncm.cbSize = sizeof(ncm);
  328. if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, FALSE))
  329. {
  330. WORD wLang = GetUserDefaultLangID();
  331. // Select normal weight font for chinese language.
  332. if (PRIMARYLANGID(wLang) == LANG_CHINESE &&
  333. ((SUBLANGID(wLang) == SUBLANG_CHINESE_TRADITIONAL) ||
  334. (SUBLANGID(wLang) == SUBLANG_CHINESE_SIMPLIFIED)))
  335. ncm.lfCaptionFont.lfWeight = FW_NORMAL;
  336. else
  337. ncm.lfCaptionFont.lfWeight = FW_BOLD;
  338. hfontStart = CreateFontIndirect(&ncm.lfCaptionFont);
  339. }
  340. return hfontStart;
  341. }
  342. // Set the stuck monitor for the tray window
  343. void CTray::_SetStuckMonitor()
  344. {
  345. // use STICK_LEFT because most of the multi-monitors systems are set up
  346. // side by side. use DEFAULTTONULL because we don't want to get the wrong one
  347. // use the center point to call again in case we failed the first time.
  348. _hmonStuck = MonitorFromRect(&_arStuckRects[STICK_LEFT],
  349. MONITOR_DEFAULTTONULL);
  350. if (!_hmonStuck)
  351. {
  352. POINT pt;
  353. pt.x = (_arStuckRects[STICK_LEFT].left + _arStuckRects[STICK_LEFT].right)/2;
  354. pt.y = (_arStuckRects[STICK_LEFT].top + _arStuckRects[STICK_LEFT].bottom)/2;
  355. _hmonStuck = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
  356. }
  357. _hmonOld = _hmonStuck;
  358. }
  359. DWORD _GetDefaultTVSDFlags()
  360. {
  361. DWORD dwFlags = TVSD_TOPMOST;
  362. // if we are on a remote hydra session and if there is no previous saved value,
  363. // do not display the clock.
  364. if (SHGetMachineInfo(GMI_TSCLIENT))
  365. {
  366. dwFlags |= TVSD_HIDECLOCK;
  367. }
  368. return dwFlags;
  369. }
  370. void CTray::_GetSaveStateAndInitRects()
  371. {
  372. TVSDCOMPAT tvsd;
  373. RECT rcDisplay;
  374. DWORD dwTrayFlags;
  375. UINT uStick;
  376. SIZE size;
  377. // first fill in the defaults
  378. SetRect(&rcDisplay, 0, 0, g_cxPrimaryDisplay, g_cyPrimaryDisplay);
  379. // size gets defaults
  380. size.cx = _sizeStart.cx + 2 * (g_cxDlgFrame + g_cxBorder);
  381. size.cy = _sizeStart.cy + 2 * (g_cyDlgFrame + g_cyBorder);
  382. // sStuckWidths gets minimum
  383. _sStuckWidths.cx = 2 * (g_cxDlgFrame + g_cxBorder);
  384. _sStuckWidths.cy = _sizeStart.cy + 2 * (g_cyDlgFrame + g_cyBorder);
  385. _uStuckPlace = STICK_BOTTOM;
  386. dwTrayFlags = _GetDefaultTVSDFlags();
  387. _uAutoHide = 0;
  388. // now try to load saved vaules
  389. // BUG : 231077
  390. // Since Tasbar properties don't roam from NT5 to NT4, (NT4 -> NT5 yes)
  391. // Allow roaming from NT4 to NT5 only for the first time the User logs
  392. // on to NT5, so that future changes to NT5 are not lost when the user
  393. // logs on to NT4 after customizing the taskbar properties on NT5.
  394. DWORD cbData1 = sizeof(tvsd);
  395. DWORD cbData2 = sizeof(tvsd);
  396. if (Reg_GetStruct(g_hkeyExplorer, TEXT("StuckRects2"), TEXT("Settings"),
  397. &tvsd, &cbData1)
  398. ||
  399. Reg_GetStruct(g_hkeyExplorer, TEXT("StuckRects"), TEXT("Settings"),
  400. &tvsd, &cbData2))
  401. {
  402. if (IS_CURRENT_TVSD(tvsd.t) && IsValidSTUCKPLACE(tvsd.t.uStuckPlace))
  403. {
  404. _GetDisplayRectFromRect(&rcDisplay, &tvsd.t.rcLastStuck,
  405. MONITOR_DEFAULTTONEAREST);
  406. size = tvsd.t.sStuckWidths;
  407. _uStuckPlace = tvsd.t.uStuckPlace;
  408. dwTrayFlags = tvsd.t.dwFlags;
  409. }
  410. else if (MAYBE_WIN95_TVSD(tvsd.w95) &&
  411. IsValidSTUCKPLACE(tvsd.w95.uStuckPlace))
  412. {
  413. _uStuckPlace = tvsd.w95.uStuckPlace;
  414. dwTrayFlags = tvsd.w95.dwFlags;
  415. if (tvsd.w95.uAutoHide & AH_ON)
  416. dwTrayFlags |= TVSD_AUTOHIDE;
  417. switch (_uStuckPlace)
  418. {
  419. case STICK_LEFT:
  420. size.cx = tvsd.w95.dxLeft;
  421. break;
  422. case STICK_RIGHT:
  423. size.cx = tvsd.w95.dxRight;
  424. break;
  425. case STICK_BOTTOM:
  426. size.cy = tvsd.w95.dyBottom;
  427. break;
  428. case STICK_TOP:
  429. size.cy = tvsd.w95.dyTop;
  430. break;
  431. }
  432. }
  433. }
  434. ASSERT(IsValidSTUCKPLACE(_uStuckPlace));
  435. //
  436. // use the size only if it is not bogus
  437. //
  438. if (_sStuckWidths.cx < size.cx)
  439. _sStuckWidths.cx = size.cx;
  440. if (_sStuckWidths.cy < size.cy)
  441. _sStuckWidths.cy = size.cy;
  442. //
  443. // set the tray flags
  444. //
  445. _fAlwaysOnTop = BOOLIFY(dwTrayFlags & TVSD_TOPMOST);
  446. _fSMSmallIcons = BOOLIFY(dwTrayFlags & TVSD_SMSMALLICONS);
  447. _fHideClock = SHRestricted(REST_HIDECLOCK) || BOOLIFY(dwTrayFlags & TVSD_HIDECLOCK);
  448. _uAutoHide = (dwTrayFlags & TVSD_AUTOHIDE) ? AH_ON | AH_HIDING : 0;
  449. _RefreshSettings();
  450. //
  451. // initialize stuck rects
  452. //
  453. for (uStick = STICK_LEFT; uStick <= STICK_BOTTOM; uStick++)
  454. _MakeStuckRect(&_arStuckRects[uStick], &rcDisplay, _sStuckWidths, uStick);
  455. _UpdateVertical(_uStuckPlace);
  456. // Determine which monitor the tray is on using its stuck rectangles
  457. _SetStuckMonitor();
  458. }
  459. IBandSite * BandSite_CreateView();
  460. HRESULT BandSite_SaveView(IUnknown *pbs);
  461. LRESULT BandSite_OnMarshallBS(WPARAM wParam, LPARAM lParam);
  462. void CTray::_SaveTrayStuff(void)
  463. {
  464. TVSD tvsd;
  465. tvsd.dwSize = sizeof(tvsd);
  466. tvsd.lSignature = TVSDSIG_CURRENT;
  467. // position
  468. CopyRect(&tvsd.rcLastStuck, &_arStuckRects[_uStuckPlace]);
  469. tvsd.sStuckWidths = _sStuckWidths;
  470. tvsd.uStuckPlace = _uStuckPlace;
  471. tvsd.dwFlags = 0;
  472. if (_fAlwaysOnTop) tvsd.dwFlags |= TVSD_TOPMOST;
  473. if (_fSMSmallIcons) tvsd.dwFlags |= TVSD_SMSMALLICONS;
  474. if (_fHideClock && !SHRestricted(REST_HIDECLOCK)) tvsd.dwFlags |= TVSD_HIDECLOCK;
  475. if (_uAutoHide & AH_ON) tvsd.dwFlags |= TVSD_AUTOHIDE;
  476. // Save in Stuck rects.
  477. Reg_SetStruct(g_hkeyExplorer, TEXT("StuckRects2"), TEXT("Settings"), &tvsd, sizeof(tvsd));
  478. BandSite_SaveView(_ptbs);
  479. return;
  480. }
  481. // align toolbar so that buttons are flush with client area
  482. // and make toolbar's buttons to be MENU style
  483. void CTray::_AlignStartButton()
  484. {
  485. HWND hwndStart = _hwndStart;
  486. if (hwndStart)
  487. {
  488. TCHAR szStart[50];
  489. LoadString(hinstCabinet, _hTheme ? IDS_START : IDS_STARTCLASSIC, szStart, ARRAYSIZE(szStart));
  490. SetWindowText(_hwndStart, szStart);
  491. RECT rcClient;
  492. if (!_sizeStart.cx)
  493. {
  494. Button_GetIdealSize(hwndStart, &_sizeStart);
  495. }
  496. GetClientRect(_hwnd, &rcClient);
  497. if (rcClient.right < _sizeStart.cx)
  498. {
  499. SetWindowText(_hwndStart, L"");
  500. }
  501. int cyStart = _sizeStart.cy;
  502. if (_hwndTasks)
  503. {
  504. if (_hTheme)
  505. {
  506. cyStart = max(cyStart, SendMessage(_hwndTasks, TBC_BUTTONHEIGHT, 0, 0));
  507. }
  508. else
  509. {
  510. cyStart = SendMessage(_hwndTasks, TBC_BUTTONHEIGHT, 0, 0);
  511. }
  512. }
  513. SetWindowPos(hwndStart, NULL, 0, 0, min(rcClient.right, _sizeStart.cx),
  514. cyStart, SWP_NOZORDER | SWP_NOACTIVATE);
  515. }
  516. }
  517. // Helper function for CDesktopHost so clicking twice on the Start Button
  518. // treats the second click as a dismiss rather than a redisplay.
  519. //
  520. // The crazy state machine goes like this:
  521. //
  522. // SBSM_NORMAL - normal state, nothing exciting
  523. //
  524. // When user opens Start Pane, we become
  525. //
  526. // SBSM_SPACTIVE - start pane is active
  527. //
  528. // If user clicks Start Button while SBSM_SPACTIVE, then we become
  529. //
  530. // SBSM_EATING - eat mouse clicks
  531. //
  532. // Until we receive a WM_MOUSEFIRST/WM_MOUSELAST message, and then
  533. // we return to SBSM_NORMAL.
  534. //
  535. // If user dismisses Start Pane, we go straight to SBSM_NORMAL.
  536. //
  537. //
  538. // We eat the mouse clicks so that the click that the user made
  539. // to "unclick" the start button doesn't cause it to get pushed down
  540. // again (and cause the Start Menu to reopen).
  541. //
  542. #define SBSM_NORMAL 0
  543. #define SBSM_SPACTIVE 1
  544. #define SBSM_EATING 2
  545. void Tray_SetStartPaneActive(BOOL fActive)
  546. {
  547. if (fActive)
  548. { // Start Pane appearing
  549. c_tray._uStartButtonState = SBSM_SPACTIVE;
  550. }
  551. else if (c_tray._uStartButtonState != SBSM_EATING)
  552. { // Start Pane dismissing, not eating messages -> return to normal
  553. c_tray._uStartButtonState = SBSM_NORMAL;
  554. }
  555. }
  556. // Allow us to do stuff on a "button-down".
  557. LRESULT WINAPI CTray::StartButtonSubclassWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  558. {
  559. return c_tray._StartButtonSubclassWndProc(hwnd, uMsg, wParam, lParam);
  560. }
  561. LRESULT CTray::_StartButtonSubclassWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  562. {
  563. LRESULT lRet;
  564. ASSERT(_pfnButtonProc)
  565. // Is the button going down?
  566. if (uMsg == BM_SETSTATE)
  567. {
  568. // Is it going Down?
  569. if (wParam)
  570. {
  571. // DebugMsg(DM_TRACE, "c.stswp: Set state %d", wParam);
  572. // Yes - proceed if it's currently up and it's allowed to be down
  573. if (!_uDown)
  574. {
  575. // Nope.
  576. INSTRUMENT_STATECHANGE(SHCNFI_STATE_START_DOWN);
  577. _uDown = 1;
  578. // If we are going down, then we do not want to popup again until the Start Menu is collapsed
  579. _fAllowUp = FALSE;
  580. SendMessage(_hwndTrayTips, TTM_ACTIVATE, FALSE, 0L);
  581. // Show the button down.
  582. lRet = CallWindowProc(_pfnButtonProc, hwnd, uMsg, wParam, lParam);
  583. // Notify the parent.
  584. SendMessage(GetParent(hwnd), WM_COMMAND, (WPARAM)LOWORD(GetDlgCtrlID(hwnd)), (LPARAM)hwnd);
  585. _tmOpen = GetTickCount();
  586. return lRet;
  587. }
  588. else
  589. {
  590. // Yep. Do nothing.
  591. // fDown = FALSE;
  592. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  593. }
  594. }
  595. else
  596. {
  597. // DebugMsg(DM_TRACE, "c.stswp: Set state %d", wParam);
  598. // Nope, buttons coming up.
  599. // Is it supposed to be down? Is it not allowed to be up?
  600. if (_uDown == 1 || !_fAllowUp)
  601. {
  602. INSTRUMENT_STATECHANGE(SHCNFI_STATE_START_UP);
  603. // Yep, do nothing.
  604. _uDown = 2;
  605. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  606. }
  607. else
  608. {
  609. SendMessage(_hwndTrayTips, TTM_ACTIVATE, TRUE, 0L);
  610. // Nope, Forward it on.
  611. _uDown = 0;
  612. return CallWindowProc(_pfnButtonProc, hwnd, uMsg, wParam, lParam);
  613. }
  614. }
  615. }
  616. else
  617. {
  618. if (_uStartButtonState == SBSM_EATING &&
  619. uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST)
  620. {
  621. _uStartButtonState = SBSM_NORMAL;
  622. // Explicitly dismiss the Start Panel because it might be
  623. // stuck in this limbo state where it is open but not the
  624. // foreground window (_ShowStartButtonToolTip does this)
  625. // so it doesn't know that it needs to go away.
  626. ClosePopupMenus();
  627. }
  628. switch (uMsg) {
  629. case WM_LBUTTONDOWN:
  630. // The button was clicked on, then we don't need no stink'n focus rect.
  631. SendMessage(GetParent(hwnd), WM_UPDATEUISTATE, MAKEWPARAM(UIS_SET,
  632. UISF_HIDEFOCUS), 0);
  633. goto ProcessCapture;
  634. break;
  635. case WM_KEYDOWN:
  636. // The user pressed enter or return or some other bogus key combination when
  637. // the start button had keyboard focus, so show the rect....
  638. SendMessage(GetParent(hwnd), WM_UPDATEUISTATE, MAKEWPARAM(UIS_CLEAR,
  639. UISF_HIDEFOCUS), 0);
  640. if (wParam == VK_RETURN)
  641. PostMessage(_hwnd, WM_COMMAND, IDC_KBSTART, 0);
  642. // We do not need the capture, because we do all of our button processing
  643. // on the button down. In fact taking capture for no good reason screws with
  644. // drag and drop into the menus. We're overriding user.
  645. ProcessCapture:
  646. lRet = CallWindowProc(_pfnButtonProc, hwnd, uMsg, wParam, lParam);
  647. SetCapture(NULL);
  648. return lRet;
  649. break;
  650. case WM_MOUSEMOVE:
  651. {
  652. MSG msg;
  653. msg.lParam = lParam;
  654. msg.wParam = wParam;
  655. msg.message = uMsg;
  656. msg.hwnd = hwnd;
  657. SendMessage(_hwndTrayTips, TTM_RELAYEVENT, 0, (LPARAM)(LPMSG)& msg);
  658. break;
  659. }
  660. case WM_MOUSEACTIVATE:
  661. if (_uStartButtonState != SBSM_NORMAL)
  662. {
  663. _uStartButtonState = SBSM_EATING;
  664. return MA_ACTIVATEANDEAT;
  665. }
  666. break;
  667. //
  668. // Debounce the Start Button. Usability shows that lots of people
  669. // double-click the Start Button, resulting in the menu opening
  670. // and then immediately closing...
  671. //
  672. case WM_NCHITTEST:
  673. if (GetTickCount() - _tmOpen < GetDoubleClickTime())
  674. {
  675. return HTNOWHERE;
  676. }
  677. break;
  678. case WM_NULL:
  679. break;
  680. }
  681. return CallWindowProc(_pfnButtonProc, hwnd, uMsg, wParam, lParam);
  682. }
  683. }
  684. EXTERN_C const WCHAR c_wzTaskbarTheme[] = L"Taskbar";
  685. EXTERN_C const WCHAR c_wzTaskbarVertTheme[] = L"TaskbarVert";
  686. // create the toolbar with the three buttons and align windows
  687. HWND CTray::_CreateStartButton()
  688. {
  689. DWORD dwStyle = 0;//BS_BITMAP;
  690. _uStartButtonBalloonTip = RegisterWindowMessage(TEXT("Welcome Finished"));
  691. _uLogoffUser = RegisterWindowMessage(TEXT("Logoff User"));
  692. // Register for MM device changes
  693. _uWinMM_DeviceChange = RegisterWindowMessage(WINMMDEVICECHANGEMSGSTRING);
  694. HWND hwnd = CreateWindowEx(0, WC_BUTTON, TEXT("Start"),
  695. WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS |
  696. BS_PUSHBUTTON | BS_LEFT | BS_VCENTER | dwStyle,
  697. 0, 0, 0, 0, _hwnd, (HMENU)IDC_START, hinstCabinet, NULL);
  698. if (hwnd)
  699. {
  700. // taskbar windows are themed under Taskbar subapp name
  701. SetWindowTheme(hwnd, L"Start", NULL);
  702. SendMessage(hwnd, CCM_DPISCALE, TRUE, 0);
  703. // Subclass it.
  704. _hwndStart = hwnd;
  705. _pfnButtonProc = SubclassWindow(hwnd, StartButtonSubclassWndProc);
  706. _StartButtonReset();
  707. }
  708. return hwnd;
  709. }
  710. void CTray::_GetWindowSizes(UINT uStuckPlace, PRECT prcClient, PRECT prcView, PRECT prcNotify)
  711. {
  712. prcView->top = 0;
  713. prcView->left = 0;
  714. prcView->bottom = prcClient->bottom;
  715. prcView->right = prcClient->right;
  716. if (STUCK_HORIZONTAL(uStuckPlace))
  717. {
  718. DWORD_PTR dwNotifySize = SendMessage(_hwndNotify, WM_CALCMINSIZE, prcClient->right / 2, prcClient->bottom);
  719. prcNotify->top = 0;
  720. prcNotify->left = prcClient->right - LOWORD(dwNotifySize);
  721. prcNotify->bottom = HIWORD(dwNotifySize);
  722. prcNotify->right = prcClient->right;
  723. prcView->left = _sizeStart.cx + g_cxFrame + 1;
  724. prcView->right = prcNotify->left;
  725. }
  726. else
  727. {
  728. DWORD_PTR dwNotifySize = SendMessage(_hwndNotify, WM_CALCMINSIZE, prcClient->right, prcClient->bottom / 2);
  729. prcNotify->top = prcClient->bottom - HIWORD(dwNotifySize);
  730. prcNotify->left = 0;
  731. prcNotify->bottom = prcClient->bottom;
  732. prcNotify->right = LOWORD(dwNotifySize);
  733. prcView->top = _sizeStart.cy + g_cyTabSpace;
  734. prcView->bottom = prcNotify->top;
  735. }
  736. }
  737. void CTray::_RestoreWindowPos()
  738. {
  739. WINDOWPLACEMENT wp;
  740. //first restore the stuck postitions
  741. _GetSaveStateAndInitRects();
  742. wp.length = sizeof(wp);
  743. wp.showCmd = SW_HIDE;
  744. _uMoveStuckPlace = (UINT)-1;
  745. _GetDockedRect(&wp.rcNormalPosition, FALSE);
  746. SendMessage(_hwndNotify, TNM_TRAYHIDE, 0, _fHideClock);
  747. SetWindowPlacement(_hwnd, &wp);
  748. }
  749. // Get the display (monitor) rectangle from the given arbitrary point
  750. HMONITOR CTray::_GetDisplayRectFromPoint(LPRECT prcDisplay, POINT pt, UINT uFlags)
  751. {
  752. RECT rcEmpty = {0};
  753. HMONITOR hmon = MonitorFromPoint(pt, uFlags);
  754. if (hmon && prcDisplay)
  755. GetMonitorRect(hmon, prcDisplay);
  756. else if (prcDisplay)
  757. *prcDisplay = rcEmpty;
  758. return hmon;
  759. }
  760. // Get the display (monitor) rectangle from the given arbitrary rectangle
  761. HMONITOR CTray::_GetDisplayRectFromRect(LPRECT prcDisplay, LPCRECT prcIn, UINT uFlags)
  762. {
  763. RECT rcEmpty = {0};
  764. HMONITOR hmon = MonitorFromRect(prcIn, uFlags);
  765. if (hmon && prcDisplay)
  766. GetMonitorRect(hmon, prcDisplay);
  767. else if (prcDisplay)
  768. *prcDisplay = rcEmpty;
  769. return hmon;
  770. }
  771. // Get the display (monitor) rectangle where the taskbar is currently on,
  772. // if that monitor is invalid, get the nearest one.
  773. void CTray::_GetStuckDisplayRect(UINT uStuckPlace, LPRECT prcDisplay)
  774. {
  775. ASSERT(prcDisplay);
  776. BOOL fValid = GetMonitorRect(_hmonStuck, prcDisplay);
  777. if (!fValid)
  778. _GetDisplayRectFromRect(prcDisplay, &_arStuckRects[uStuckPlace], MONITOR_DEFAULTTONEAREST);
  779. }
  780. void CTray::_AdjustRectForSizingBar(UINT uStuckPlace, LPRECT prc, int iIncrement)
  781. {
  782. if (iIncrement != 0)
  783. {
  784. switch (uStuckPlace)
  785. {
  786. case STICK_BOTTOM: prc->top -= iIncrement * _sizeSizingBar.cy; break;
  787. case STICK_TOP: prc->bottom += iIncrement * _sizeSizingBar.cy; break;
  788. case STICK_LEFT: prc->right += iIncrement * _sizeSizingBar.cx; break;
  789. case STICK_RIGHT: prc->left -= iIncrement * _sizeSizingBar.cx; break;
  790. }
  791. }
  792. else
  793. {
  794. if (IS_BIDI_LOCALIZED_SYSTEM())
  795. {
  796. switch (uStuckPlace)
  797. {
  798. case STICK_BOTTOM: prc->bottom = prc->top + _sizeSizingBar.cy; break;
  799. case STICK_TOP: prc->top = prc->bottom - _sizeSizingBar.cy; break;
  800. case STICK_LEFT: prc->right = prc->left + _sizeSizingBar.cx; break;
  801. case STICK_RIGHT: prc->left = prc->right - _sizeSizingBar.cx; break;
  802. }
  803. }
  804. else
  805. {
  806. switch (uStuckPlace)
  807. {
  808. case STICK_BOTTOM: prc->bottom = prc->top + _sizeSizingBar.cy; break;
  809. case STICK_TOP: prc->top = prc->bottom - _sizeSizingBar.cy; break;
  810. case STICK_LEFT: prc->left = prc->right - _sizeSizingBar.cx; break;
  811. case STICK_RIGHT: prc->right = prc->left + _sizeSizingBar.cx; break;
  812. }
  813. }
  814. }
  815. }
  816. // Snap a StuckRect to the edge of a containing rectangle
  817. // fClip determines whether to clip the rectangle if it's off the display or move it onto the screen
  818. void CTray::_MakeStuckRect(LPRECT prcStick, LPCRECT prcBound, SIZE size, UINT uStick)
  819. {
  820. CopyRect(prcStick, prcBound);
  821. if (_hTheme && (_fCanSizeMove || _fShowSizingBarAlways))
  822. {
  823. _AdjustRectForSizingBar(uStick, prcStick, 1);
  824. }
  825. if (!_hTheme)
  826. {
  827. InflateRect(prcStick, g_cxEdge, g_cyEdge);
  828. }
  829. if (size.cx < 0) size.cx *= -1;
  830. if (size.cy < 0) size.cy *= -1;
  831. switch (uStick)
  832. {
  833. case STICK_LEFT: prcStick->right = (prcStick->left + size.cx); break;
  834. case STICK_TOP: prcStick->bottom = (prcStick->top + size.cy); break;
  835. case STICK_RIGHT: prcStick->left = (prcStick->right - size.cx); break;
  836. case STICK_BOTTOM: prcStick->top = (prcStick->bottom - size.cy); break;
  837. }
  838. }
  839. // the screen size has changed, so the docked rectangles need to be
  840. // adjusted to the new screen.
  841. void CTray::_ResizeStuckRects(RECT *arStuckRects)
  842. {
  843. RECT rcDisplay;
  844. _GetStuckDisplayRect(_uStuckPlace, &rcDisplay);
  845. for (UINT uStick = STICK_LEFT; uStick <= STICK_BOTTOM; uStick++)
  846. {
  847. _MakeStuckRect(&arStuckRects[uStick], &rcDisplay, _sStuckWidths, uStick);
  848. }
  849. }
  850. //*** CTray::InvisibleUnhide -- temporary 'invisible' un-autohide
  851. // DESCRIPTION
  852. // various tray resize routines need the tray to be un-autohide'd for
  853. // stuff to be calculated correctly. so we un-autohide it (invisibly...)
  854. // here. note the WM_SETREDRAW to prevent flicker (nt5:182340).
  855. // note that this is kind of a hack -- ideally the tray code would do
  856. // stuff correctly even if hidden.
  857. //
  858. void CTray::InvisibleUnhide(BOOL fShowWindow)
  859. {
  860. if (fShowWindow == FALSE)
  861. {
  862. if (_cHided++ == 0)
  863. {
  864. SendMessage(_hwnd, WM_SETREDRAW, FALSE, 0);
  865. ShowWindow(_hwnd, SW_HIDE);
  866. Unhide();
  867. }
  868. }
  869. else
  870. {
  871. ASSERT(_cHided > 0); // must be push/pop
  872. if (--_cHided == 0)
  873. {
  874. _Hide();
  875. ShowWindow(_hwnd, SW_SHOWNA);
  876. SendMessage(_hwnd, WM_SETREDRAW, TRUE, 0);
  877. }
  878. }
  879. }
  880. void CTray::VerifySize(BOOL fWinIni, BOOL fRoundUp /* = FALSE */)
  881. {
  882. RECT rc;
  883. BOOL fHiding;
  884. fHiding = (_uAutoHide & AH_HIDING);
  885. if (fHiding)
  886. {
  887. // force it visible so various calculations will happen relative
  888. // to unhidden size/position.
  889. //
  890. // fixes (e.g.) ie5:154536, where dropping a large-icon ISFBand
  891. // onto hidden tray didn't do size negotiation.
  892. //
  893. InvisibleUnhide(FALSE);
  894. }
  895. rc = _arStuckRects[_uStuckPlace];
  896. _HandleSizing(0, NULL, _uStuckPlace);
  897. if (!EqualRect(&rc, &_arStuckRects[_uStuckPlace]))
  898. {
  899. if (fWinIni)
  900. {
  901. // if we're changing size or position, we need to be unhidden
  902. Unhide();
  903. SizeWindows();
  904. }
  905. rc = _arStuckRects[_uStuckPlace];
  906. if (EVAL((_uAutoHide & (AH_ON | AH_HIDING)) != (AH_ON | AH_HIDING)))
  907. {
  908. _fSelfSizing = TRUE;
  909. SetWindowPos(_hwnd, NULL,
  910. rc.left, rc.top,
  911. RECTWIDTH(rc),RECTHEIGHT(rc),
  912. SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS);
  913. _fSelfSizing = FALSE;
  914. }
  915. _StuckTrayChange();
  916. }
  917. if (fWinIni)
  918. SizeWindows();
  919. if (fHiding)
  920. {
  921. InvisibleUnhide(TRUE);
  922. }
  923. }
  924. HWND CTray::_GetClockWindow(void)
  925. {
  926. return (HWND)SendMessage(_hwndNotify, TNM_GETCLOCK, 0, 0L);
  927. }
  928. UINT _GetStartIDB()
  929. {
  930. UINT id;
  931. if (IsOS(OS_TABLETPC))
  932. {
  933. id = IDB_TABLETPCSTARTBKG;
  934. }
  935. else if (IsOS(OS_EMBEDDED))
  936. {
  937. if (IsOS(OS_ANYSERVER))
  938. id = IDB_EMBEDDEDSERVER;
  939. else
  940. id = IDB_EMBEDDED;
  941. }
  942. else if (IsOS(OS_DATACENTER))
  943. {
  944. id = IDB_DCSERVERSTARTBKG;
  945. }
  946. else if (IsOS(OS_ADVSERVER))
  947. {
  948. id = IDB_ADVSERVERSTARTBKG;
  949. }
  950. else if (IsOS(OS_SERVER))
  951. {
  952. id = IDB_SERVERSTARTBKG;
  953. }
  954. else if (IsOS(OS_PERSONAL))
  955. {
  956. id = IDB_PERSONALSTARTBKG;
  957. }
  958. else if (IsOS(OS_BLADE))
  959. {
  960. id = IDB_BLADESTARTBKG;
  961. }
  962. else if (IsOS(OS_SMALLBUSINESSSERVER))
  963. {
  964. id = IDB_SMALLBUSINESSSTARTBKG;
  965. }
  966. else if (IsOS(OS_APPLIANCE))
  967. {
  968. id = IDB_APPLIANCESTARTBKG;
  969. }
  970. else
  971. {
  972. #ifdef _WIN64
  973. id = IDB_PROFESSIONAL64STARTBKG;
  974. #else
  975. id = IDB_PROFESSIONALSTARTBKG;
  976. #endif
  977. }
  978. return id;
  979. }
  980. void CTray::_CreateTrayTips()
  981. {
  982. _hwndTrayTips = CreateWindowEx(WS_EX_TRANSPARENT, TOOLTIPS_CLASS, NULL,
  983. WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
  984. CW_USEDEFAULT, CW_USEDEFAULT,
  985. CW_USEDEFAULT, CW_USEDEFAULT,
  986. _hwnd, NULL, hinstCabinet,
  987. NULL);
  988. if (_hwndTrayTips)
  989. {
  990. // taskbar windows are themed under Taskbar subapp name
  991. SendMessage(_hwndTrayTips, TTM_SETWINDOWTHEME, 0, (LPARAM)c_wzTaskbarTheme);
  992. SetWindowZorder(_hwndTrayTips, HWND_TOPMOST);
  993. TOOLINFO ti;
  994. ti.cbSize = sizeof(ti);
  995. ti.uFlags = TTF_IDISHWND | TTF_EXCLUDETOOLAREA;
  996. ti.hwnd = _hwnd;
  997. ti.uId = (UINT_PTR)_hwndStart;
  998. ti.lpszText = (LPTSTR)MAKEINTRESOURCE(IDS_STARTBUTTONTIP);
  999. ti.hinst = hinstCabinet;
  1000. SendMessage(_hwndTrayTips, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&ti);
  1001. HWND hwndClock = _GetClockWindow();
  1002. if (hwndClock)
  1003. {
  1004. ti.uFlags = TTF_EXCLUDETOOLAREA;
  1005. ti.uId = (UINT_PTR)hwndClock;
  1006. ti.lpszText = LPSTR_TEXTCALLBACK;
  1007. ti.rect.left = ti.rect.top = ti.rect.bottom = ti.rect.right = 0;
  1008. SendMessage(_hwndTrayTips, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&ti);
  1009. }
  1010. }
  1011. }
  1012. #define SHCNE_STAGINGAREANOTIFICATIONS (SHCNE_CREATE | SHCNE_MKDIR | SHCNE_UPDATEDIR | SHCNE_UPDATEITEM)
  1013. LRESULT CTray::_CreateWindows()
  1014. {
  1015. if (_CreateStartButton() && _CreateClockWindow())
  1016. {
  1017. //
  1018. // We need to set the tray position, before creating
  1019. // the view window, because it will call back our
  1020. // GetWindowRect member functions.
  1021. //
  1022. _RestoreWindowPos();
  1023. _CreateTrayTips();
  1024. SendMessage(_hwndNotify, TNM_HIDECLOCK, 0, _fHideClock);
  1025. _ptbs = BandSite_CreateView();
  1026. if (_ptbs)
  1027. {
  1028. IUnknown_GetWindow(_ptbs, &_hwndRebar);
  1029. SetWindowStyle(_hwndRebar, RBS_BANDBORDERS, FALSE);
  1030. // No need to check the disk space thing for non-privileged users, this reduces activity in the TS case
  1031. // and only admins can properly free disk space anyways.
  1032. if (IsUserAnAdmin() && !SHRestricted(REST_NOLOWDISKSPACECHECKS))
  1033. {
  1034. SetTimer(_hwnd, IDT_CHECKDISKSPACE, 60 * 1000, NULL); // 60 seconds poll
  1035. }
  1036. if (IsOS(OS_PERSONAL) || IsOS(OS_PROFESSIONAL))
  1037. {
  1038. SetTimer(_hwnd, IDT_DESKTOPCLEANUP, 24 * 60 * 60 * 1000, NULL); // 24 hour poll
  1039. }
  1040. if (!SHRestricted(REST_NOCDBURNING))
  1041. {
  1042. LPITEMIDLIST pidlStaging;
  1043. if (SUCCEEDED(SHGetFolderLocation(NULL, CSIDL_CDBURN_AREA | CSIDL_FLAG_CREATE, NULL, 0, &pidlStaging)))
  1044. {
  1045. SHChangeNotifyEntry fsne;
  1046. fsne.fRecursive = FALSE;
  1047. fsne.pidl = pidlStaging;
  1048. _uNotify = SHChangeNotifyRegister(_hwnd, SHCNRF_NewDelivery | SHCNRF_ShellLevel | SHCNRF_InterruptLevel,
  1049. SHCNE_STAGINGAREANOTIFICATIONS, TM_CHANGENOTIFY, 1, &fsne);
  1050. // start off by checking the first time.
  1051. _CheckStagingAreaOnTimer();
  1052. ILFree(pidlStaging);
  1053. }
  1054. }
  1055. return 1;
  1056. }
  1057. }
  1058. return -1;
  1059. }
  1060. LRESULT CTray::_InitStartButtonEtc()
  1061. {
  1062. // NOTE: This bitmap is used as a flag in CTaskBar::OnPosRectChangeDB to
  1063. // tell when we are done initializing, so we don't resize prematurely
  1064. _hbmpStartBkg = LoadBitmap(hinstCabinet, MAKEINTRESOURCE(_GetStartIDB()));
  1065. if (_hbmpStartBkg)
  1066. {
  1067. UpdateWindow(_hwnd);
  1068. _BuildStartMenu();
  1069. _RegisterDropTargets();
  1070. if (_CheckAssociations())
  1071. CheckWinIniForAssocs();
  1072. SendNotifyMessage(HWND_BROADCAST,
  1073. RegisterWindowMessage(TEXT("TaskbarCreated")), 0, 0);
  1074. return 1;
  1075. }
  1076. return -1;
  1077. }
  1078. void CTray::_AdjustMinimizedMetrics()
  1079. {
  1080. MINIMIZEDMETRICS mm;
  1081. mm.cbSize = sizeof(mm);
  1082. SystemParametersInfo(SPI_GETMINIMIZEDMETRICS, sizeof(mm), &mm, FALSE);
  1083. mm.iArrange |= ARW_HIDE;
  1084. SystemParametersInfo(SPI_SETMINIMIZEDMETRICS, sizeof(mm), &mm, FALSE);
  1085. }
  1086. void CTray::_UpdateBandSiteStyle()
  1087. {
  1088. if (_ptbs)
  1089. {
  1090. BANDSITEINFO bsi;
  1091. bsi.dwMask = BSIM_STYLE;
  1092. _ptbs->GetBandSiteInfo(&bsi);
  1093. BOOL fCanMoveBands = _fCanSizeMove && !SHRestricted(REST_NOMOVINGBAND);
  1094. DWORD dwStyleNew;
  1095. if (fCanMoveBands)
  1096. {
  1097. dwStyleNew = (bsi.dwStyle & ~(BSIS_NOGRIPPER | BSIS_LOCKED)) | BSIS_AUTOGRIPPER
  1098. | BSIS_PREFERNOLINEBREAK;
  1099. }
  1100. else
  1101. {
  1102. dwStyleNew = (bsi.dwStyle & ~BSIS_AUTOGRIPPER) | BSIS_NOGRIPPER | BSIS_LOCKED
  1103. | BSIS_PREFERNOLINEBREAK;
  1104. }
  1105. // only bother with refresh if something's changed
  1106. if (bsi.dwStyle ^ dwStyleNew)
  1107. {
  1108. bsi.dwStyle = dwStyleNew;
  1109. _ptbs->SetBandSiteInfo(&bsi);
  1110. IUnknown_Exec(_ptbs, &CGID_DeskBand, DBID_BANDINFOCHANGED, 0, NULL, NULL);
  1111. }
  1112. }
  1113. }
  1114. BOOL _IsSizeMoveRestricted()
  1115. {
  1116. return SHRegGetBoolUSValue(REGSTR_POLICIES_EXPLORER, TEXT("LockTaskbar"), FALSE, FALSE);
  1117. }
  1118. BOOL _IsSizeMoveEnabled()
  1119. {
  1120. BOOL fCanSizeMove;
  1121. if (_IsSizeMoveRestricted())
  1122. {
  1123. fCanSizeMove = FALSE;
  1124. }
  1125. else
  1126. {
  1127. fCanSizeMove = SHRegGetBoolUSValue(REGSTR_EXPLORER_ADVANCED, TEXT("TaskbarSizeMove"), FALSE, TRUE);
  1128. }
  1129. return fCanSizeMove;
  1130. }
  1131. void CTray::_RefreshSettings()
  1132. {
  1133. BOOL fOldCanSizeMove = _fCanSizeMove;
  1134. _fCanSizeMove = _IsSizeMoveEnabled();
  1135. BOOL fOldShowSizingBarAlways = _fShowSizingBarAlways;
  1136. _fShowSizingBarAlways = (_uAutoHide & AH_ON) ? TRUE : FALSE;
  1137. if ((fOldCanSizeMove != _fCanSizeMove) || (_fShowSizingBarAlways != fOldShowSizingBarAlways))
  1138. {
  1139. BOOL fHiding = (_uAutoHide & AH_HIDING);
  1140. if (fHiding)
  1141. {
  1142. InvisibleUnhide(FALSE);
  1143. }
  1144. RECT rc;
  1145. GetWindowRect(_hwnd, &rc);
  1146. if (_hTheme && !_fShowSizingBarAlways)
  1147. {
  1148. if (_fCanSizeMove)
  1149. {
  1150. _AdjustRectForSizingBar(_uStuckPlace, &rc, 1);
  1151. }
  1152. else
  1153. {
  1154. _AdjustRectForSizingBar(_uStuckPlace, &rc, -1);
  1155. }
  1156. }
  1157. _ClipWindow(FALSE);
  1158. _fSelfSizing = TRUE;
  1159. SetWindowPos(_hwnd, NULL, rc.left, rc.top, RECTWIDTH(rc), RECTHEIGHT(rc), SWP_NOZORDER | SWP_FRAMECHANGED);
  1160. _fSelfSizing = FALSE;
  1161. _ClipWindow(TRUE);
  1162. _arStuckRects[_uStuckPlace] = rc;
  1163. _StuckTrayChange();
  1164. if (fHiding)
  1165. {
  1166. InvisibleUnhide(TRUE);
  1167. }
  1168. if (!_fCanSizeMove)
  1169. {
  1170. SetWindowPos(_hwnd, NULL, rc.left, rc.top, RECTWIDTH(rc), RECTHEIGHT(rc), SWP_NOZORDER);
  1171. }
  1172. }
  1173. }
  1174. LRESULT CTray::_OnCreateAsync()
  1175. {
  1176. LRESULT lres;
  1177. if (g_dwProfileCAP & 0x00000004)
  1178. {
  1179. StartCAP();
  1180. }
  1181. lres = _InitStartButtonEtc();
  1182. if (g_dwProfileCAP & 0x00000004)
  1183. {
  1184. StopCAP();
  1185. }
  1186. _hMainAccel = LoadAccelerators(hinstCabinet, MAKEINTRESOURCE(ACCEL_TRAY));
  1187. _RegisterGlobalHotkeys();
  1188. // We spin a thread that will process "Load=", "Run=", CU\Run, and CU\RunOnce
  1189. RunStartupApps();
  1190. // If there were any startup failures that occurred before we were
  1191. // ready to handle them, re-raise the failure now that we're ready.
  1192. if (_fEarlyStartupFailure)
  1193. LogFailedStartupApp();
  1194. // we run the tray thread that handles Ctrl-Esc with a high priority
  1195. // class so that it can respond even on a stressed system.
  1196. SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
  1197. return lres;
  1198. }
  1199. LRESULT CTray::_OnCreate(HWND hwnd)
  1200. {
  1201. LRESULT lres = -1;
  1202. v_hwndTray = hwnd;
  1203. Mixer_SetCallbackWindow(hwnd);
  1204. SendMessage(_hwnd, WM_CHANGEUISTATE, MAKEWPARAM(UIS_INITIALIZE, 0), 0);
  1205. _AdjustMinimizedMetrics();
  1206. _hTheme = OpenThemeData(hwnd, c_wzTaskbarTheme);
  1207. _fShowSizingBarAlways = (_uAutoHide & AH_ON) ? TRUE : FALSE;
  1208. if (_hTheme)
  1209. {
  1210. GetThemeBool(_hTheme, 0, 0, TMT_ALWAYSSHOWSIZINGBAR, &_fShowSizingBarAlways);
  1211. }
  1212. SetWindowStyle(_hwnd, WS_BORDER | WS_THICKFRAME, !_hTheme);
  1213. // Force Refresh of frame
  1214. SetWindowPos(_hwnd, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE);
  1215. if (_HotkeyCreate())
  1216. {
  1217. lres = _CreateWindows();
  1218. }
  1219. return lres;
  1220. }
  1221. typedef struct tagFSEPDATA
  1222. {
  1223. LPRECT prc;
  1224. HMONITOR hmon;
  1225. CTray* ptray;
  1226. }
  1227. FSEPDATA, *PFSEPDATA;
  1228. BOOL WINAPI CTray::FullScreenEnumProc(HMONITOR hmon, HDC hdc, LPRECT prc, LPARAM dwData)
  1229. {
  1230. BOOL fFullScreen; // Is there a rude app on this monitor?
  1231. PFSEPDATA pd = (PFSEPDATA)dwData;
  1232. if (pd->hmon == hmon)
  1233. {
  1234. fFullScreen = TRUE;
  1235. }
  1236. else if (pd->prc)
  1237. {
  1238. RECT rc, rcMon;
  1239. GetMonitorRect(hmon, &rcMon);
  1240. IntersectRect(&rc, &rcMon, pd->prc);
  1241. fFullScreen = EqualRect(&rc, &rcMon);
  1242. }
  1243. else
  1244. {
  1245. fFullScreen = FALSE;
  1246. }
  1247. if (hmon == pd->ptray->_hmonStuck)
  1248. {
  1249. pd->ptray->_fStuckRudeApp = fFullScreen;
  1250. }
  1251. //
  1252. // Tell all the appbars on the same display to get out of the way too
  1253. //
  1254. pd->ptray->_AppBarNotifyAll(hmon, ABN_FULLSCREENAPP, NULL, fFullScreen);
  1255. return TRUE;
  1256. }
  1257. void CTray::HandleFullScreenApp(HWND hwnd)
  1258. {
  1259. //
  1260. // First check to see if something has actually changed
  1261. //
  1262. _hwndRude = hwnd;
  1263. //
  1264. // Enumerate all the monitors, see if the app is rude on each, adjust
  1265. // app bars and _fStuckRudeApp as necessary. (Some rude apps, such
  1266. // as the NT Logon Screen Saver, span multiple monitors.)
  1267. //
  1268. FSEPDATA d = {0};
  1269. RECT rc;
  1270. if (hwnd && GetWindowRect(hwnd, &rc))
  1271. {
  1272. d.prc = &rc;
  1273. d.hmon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONULL);
  1274. }
  1275. d.ptray = this;
  1276. EnumDisplayMonitors(NULL, NULL, FullScreenEnumProc, (LPARAM)&d);
  1277. //
  1278. // Now that we've set _fStuckRudeApp, update the tray's z-order position
  1279. //
  1280. _ResetZorder();
  1281. //
  1282. // stop the clock so we don't eat cycles and keep tons of code paged in
  1283. //
  1284. SendMessage(_hwndNotify, TNM_TRAYHIDE, 0, _fStuckRudeApp);
  1285. //
  1286. // Finally, let traynot know about whether the tray is hiding
  1287. //
  1288. SendMessage(_hwndNotify, TNM_RUDEAPP, _fStuckRudeApp, 0);
  1289. }
  1290. BOOL CTray::_IsTopmost()
  1291. {
  1292. return BOOLIFY(GetWindowLong(_hwnd, GWL_EXSTYLE) & WS_EX_TOPMOST);
  1293. }
  1294. BOOL CTray::_IsPopupMenuVisible()
  1295. {
  1296. HWND hwnd;
  1297. return ((SUCCEEDED(IUnknown_GetWindow(_pmpStartMenu, &hwnd)) && IsWindowVisible(hwnd)) ||
  1298. (SUCCEEDED(IUnknown_GetWindow(_pmpStartPane, &hwnd)) && IsWindowVisible(hwnd)) ||
  1299. (SUCCEEDED(IUnknown_GetWindow(_pmpTasks, &hwnd)) && IsWindowVisible(hwnd)));
  1300. }
  1301. BOOL CTray::_IsActive()
  1302. {
  1303. //
  1304. // We say the tray is "active" iff:
  1305. //
  1306. // (a) the foreground window is the tray or a window owned by the tray, or
  1307. // (b) the start menu is showing
  1308. //
  1309. BOOL fActive = FALSE;
  1310. HWND hwnd = GetForegroundWindow();
  1311. if (hwnd != NULL &&
  1312. (hwnd == _hwnd || (GetWindowOwner(hwnd) == _hwnd)))
  1313. {
  1314. fActive = TRUE;
  1315. }
  1316. else if (_IsPopupMenuVisible())
  1317. {
  1318. fActive = TRUE;
  1319. }
  1320. return fActive;
  1321. }
  1322. void CTray::_ResetZorder()
  1323. {
  1324. HWND hwndZorder, hwndZorderCurrent;
  1325. if (g_fDesktopRaised || _fProcessingDesktopRaise || (_fAlwaysOnTop && !_fStuckRudeApp))
  1326. {
  1327. hwndZorder = HWND_TOPMOST;
  1328. }
  1329. else if (_IsActive())
  1330. {
  1331. hwndZorder = HWND_TOP;
  1332. }
  1333. else if (_fStuckRudeApp)
  1334. {
  1335. hwndZorder = HWND_BOTTOM;
  1336. }
  1337. else
  1338. {
  1339. hwndZorder = HWND_NOTOPMOST;
  1340. }
  1341. //
  1342. // We don't have to worry about the HWND_BOTTOM current case -- it's ok
  1343. // to keep moving ourselves down to the bottom when there's a rude app.
  1344. //
  1345. // Nor do we have to worry about the HWND_TOP current case -- it's ok
  1346. // to keep moving ourselves up to the top when we're active.
  1347. //
  1348. hwndZorderCurrent = _IsTopmost() ? HWND_TOPMOST : HWND_NOTOPMOST;
  1349. if (hwndZorder != hwndZorderCurrent)
  1350. {
  1351. // only do this if somehting has changed.
  1352. // this keeps us from popping up over menus as desktop async
  1353. // notifies us of it's state
  1354. SHForceWindowZorder(_hwnd, hwndZorder);
  1355. }
  1356. }
  1357. void CTray::_MessageLoop()
  1358. {
  1359. for (;;)
  1360. {
  1361. MSG msg;
  1362. if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
  1363. {
  1364. if (msg.message == WM_QUIT)
  1365. {
  1366. if (_hwnd && IsWindow(_hwnd))
  1367. {
  1368. // Tell the tray to save everything off if we got here
  1369. // without it being destroyed.
  1370. SendMessage(_hwnd, WM_ENDSESSION, 1, 0);
  1371. }
  1372. return; // break all the way out of the main loop
  1373. }
  1374. if (_pmbTasks)
  1375. {
  1376. HRESULT hr = _pmbTasks->IsMenuMessage(&msg);
  1377. if (hr == E_FAIL)
  1378. {
  1379. if (_hwndTasks)
  1380. SendMessage(_hwndTasks, TBC_FREEPOPUPMENUS, 0, 0);
  1381. }
  1382. else if (hr == S_OK)
  1383. {
  1384. continue;
  1385. }
  1386. }
  1387. // Note that this needs to come before _pmbStartMenu since
  1388. // the start pane sometimes hosts the start menu and it needs
  1389. // to handle the start menu messages in that case.
  1390. if (_pmbStartPane &&
  1391. _pmbStartPane->IsMenuMessage(&msg) == S_OK)
  1392. {
  1393. continue;
  1394. }
  1395. if (_pmbStartMenu &&
  1396. _pmbStartMenu->IsMenuMessage(&msg) == S_OK)
  1397. {
  1398. continue;
  1399. }
  1400. if (_hMainAccel && TranslateAccelerator(_hwnd, _hMainAccel, &msg))
  1401. {
  1402. continue;
  1403. }
  1404. TranslateMessage(&msg);
  1405. DispatchMessage(&msg);
  1406. }
  1407. else
  1408. {
  1409. WaitMessage();
  1410. }
  1411. }
  1412. }
  1413. BOOL CTray::Init()
  1414. {
  1415. // use _COINIT to make sure COM is inited disabling the OLE1 support
  1416. return SHCreateThread(MainThreadProc, this, CTF_COINIT, SyncThreadProc) && (_hwnd != NULL);
  1417. }
  1418. int CTray::_GetPart(BOOL fSizingBar, UINT uStuckPlace)
  1419. {
  1420. if (fSizingBar)
  1421. {
  1422. switch (uStuckPlace)
  1423. {
  1424. case STICK_BOTTOM: return TBP_SIZINGBARBOTTOM;
  1425. case STICK_LEFT: return TBP_SIZINGBARLEFT;
  1426. case STICK_TOP: return TBP_SIZINGBARTOP;
  1427. case STICK_RIGHT: return TBP_SIZINGBARRIGHT;
  1428. }
  1429. }
  1430. else
  1431. {
  1432. switch (uStuckPlace)
  1433. {
  1434. case STICK_BOTTOM: return TBP_BACKGROUNDBOTTOM;
  1435. case STICK_LEFT: return TBP_BACKGROUNDLEFT;
  1436. case STICK_TOP: return TBP_BACKGROUNDTOP;
  1437. case STICK_RIGHT: return TBP_BACKGROUNDRIGHT;
  1438. }
  1439. }
  1440. return 0;
  1441. }
  1442. void CTray::_UpdateVertical(UINT uStuckPlace, BOOL fForce)
  1443. {
  1444. static UINT _uOldStuckPlace = STICK_MAX + 1;
  1445. if ((_uOldStuckPlace != uStuckPlace) || fForce)
  1446. {
  1447. _uOldStuckPlace = uStuckPlace;
  1448. DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_uv tray is now %s"), STUCK_HORIZONTAL(uStuckPlace) ? TEXT("HORIZONTAL") : TEXT("VERTICAL"));
  1449. if (_ptbs)
  1450. {
  1451. // This following function will cause a WINDOWPOSCHANGING which will call DoneMoving
  1452. // DoneMoving will then go a screw up all of the window sizing
  1453. _fIgnoreDoneMoving = TRUE;
  1454. BandSite_SetMode(_ptbs, STUCK_HORIZONTAL(uStuckPlace) ? 0 : DBIF_VIEWMODE_VERTICAL);
  1455. BandSite_SetWindowTheme(_ptbs, (LPWSTR)(STUCK_HORIZONTAL(uStuckPlace) ? c_wzTaskbarTheme : c_wzTaskbarVertTheme));
  1456. _fIgnoreDoneMoving = FALSE;
  1457. }
  1458. SendMessage(_hwndNotify, TNM_UPDATEVERTICAL, 0, !STUCK_HORIZONTAL(uStuckPlace));
  1459. if (_hTheme)
  1460. {
  1461. HDC hdc = GetDC(_hwnd);
  1462. GetThemePartSize(_hTheme, hdc, _GetPart(TRUE, uStuckPlace), 0, NULL, TS_TRUE, &_sizeSizingBar);
  1463. ReleaseDC(_hwnd, hdc);
  1464. }
  1465. }
  1466. }
  1467. void CTray::_InitBandsite()
  1468. {
  1469. ASSERT(_hwnd);
  1470. // we initilize the contents after all the infrastructure is created and sized properly
  1471. // need to notify which side we're on.
  1472. // nt5:211881: set mode *before* load, o.w. Update->RBAutoSize messed up
  1473. _UpdateBandSiteStyle();
  1474. BandSite_Load();
  1475. // now that the mode is set, we need to force an update because we
  1476. // explicitly avoided the update during BandSite_Load
  1477. _UpdateVertical(_uStuckPlace, TRUE);
  1478. BandSite_Update(_ptbs);
  1479. BandSite_UIActivateDBC(_ptbs, DBC_SHOW);
  1480. BandSite_FindBand(_ptbs, CLSID_TaskBand, IID_PPV_ARG(IDeskBand, &_pdbTasks), NULL, NULL);
  1481. IUnknown_GetWindow(_pdbTasks, &_hwndTasks);
  1482. // Now that bandsite is ready, set the correct size
  1483. VerifySize(FALSE, TRUE);
  1484. }
  1485. void CTray::_KickStartAutohide()
  1486. {
  1487. if (_uAutoHide & AH_ON)
  1488. {
  1489. // tray always starts out hidden on autohide
  1490. _uAutoHide = AH_ON | AH_HIDING;
  1491. // we and many apps rely upon us having calculated the size correctly
  1492. Unhide();
  1493. // register it
  1494. if (!_AppBarSetAutoHideBar2(_hwnd, TRUE, _uStuckPlace))
  1495. {
  1496. // don't bother putting up UI in this case
  1497. // if someone is there just silently convert to normal
  1498. // (the shell is booting who would be there anyway?)
  1499. _SetAutoHideState(FALSE);
  1500. }
  1501. }
  1502. }
  1503. void CTray::_InitNonzeroGlobals()
  1504. {
  1505. // initalize globals that need to be non-zero
  1506. if (GetSystemMetrics(SM_SLOWMACHINE))
  1507. {
  1508. _dtSlideHide = 0; // dont slide the tray out
  1509. _dtSlideShow = 0;
  1510. }
  1511. else
  1512. {
  1513. _dtSlideHide = 400;
  1514. _dtSlideShow = 200;
  1515. }
  1516. _RefreshSettings();
  1517. }
  1518. void CTray::_CreateTrayWindow()
  1519. {
  1520. _InitTrayClass();
  1521. _uMsgEnableUserTrackedBalloonTips = RegisterWindowMessage(ENABLE_BALLOONTIP_MESSAGE);
  1522. _fNoToolbarsOnTaskbarPolicyEnabled = (SHRestricted(REST_NOTOOLBARSONTASKBAR) != 0);
  1523. DWORD dwExStyle = WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW;
  1524. // Don't fadein because layered windows suck
  1525. // If you create a layered window on a non-active desktop then the window goes black
  1526. dwExStyle |= IS_BIDI_LOCALIZED_SYSTEM() ? dwExStyleRTLMirrorWnd : 0L;
  1527. CreateWindowEx(dwExStyle, TEXT(WNDCLASS_TRAYNOTIFY), NULL,
  1528. WS_CLIPCHILDREN | WS_POPUP,
  1529. 0, 0, 0, 0, NULL, NULL, hinstCabinet, (void*)this);
  1530. }
  1531. DWORD WINAPI CTray::SyncThreadProc(void *pv)
  1532. {
  1533. CTray* ptray = (CTray*)pv;
  1534. return ptray->_SyncThreadProc();
  1535. }
  1536. DWORD CTray::_SyncThreadProc()
  1537. {
  1538. if (g_dwStopWatchMode)
  1539. StopWatch_StartTimed(SWID_STARTUP, TEXT("_SyncThreadProc"), SPMODE_SHELL | SPMODE_DEBUGOUT, GetPerfTime());
  1540. if (g_dwProfileCAP & 0x00000002)
  1541. StartCAP();
  1542. InitializeCriticalSection(&_csHotkey);
  1543. OleInitialize(NULL); // matched in MainThreadProc()
  1544. ClassFactory_Start();
  1545. _InitNonzeroGlobals();
  1546. _ssomgr.Init();
  1547. //
  1548. // Watch the registry key that tells us which app is the default
  1549. // web browser, so we can track it in
  1550. // HKLM\Software\Clients\StartMenuInternet. We have to track it
  1551. // ourselves because downlevel browsers won't know about it.
  1552. //
  1553. // We need to do this only if we have write access to the key.
  1554. // (If we don't have write access, then we can't change it,
  1555. // so there's no point watching for it to change...)
  1556. //
  1557. // Well, okay, even if we only have read access, we have to do
  1558. // it once in case it changed while we were logged off.
  1559. //
  1560. // The order of these operations is important...
  1561. //
  1562. // 1. Migrate browser settings.
  1563. // 2. Build default MFU. (Depends on browser settings.)
  1564. // 3. Create tray window. (Relies on value MFU.)
  1565. //
  1566. _hHTTPEvent = CreateEvent(NULL, FALSE, TRUE, NULL);
  1567. if (_hHTTPEvent)
  1568. {
  1569. // Make one migration pass immediately so HandleFirstTime
  1570. // sees good information. This also kick-starts the
  1571. // registry change notification process if the current user
  1572. // has write permission.
  1573. _MigrateOldBrowserSettings();
  1574. if (RegisterWaitForSingleObject(&_hHTTPWait, _hHTTPEvent,
  1575. _MigrateOldBrowserSettingsCB, this,
  1576. INFINITE, WT_EXECUTEDEFAULT))
  1577. {
  1578. // Yay, everything is fine.
  1579. }
  1580. }
  1581. // Build the default MFU if necessary
  1582. HandleFirstTime();
  1583. _CreateTrayWindow();
  1584. if (_hwnd && _ptbs)
  1585. {
  1586. _ResetZorder(); // obey the "always on top" flag
  1587. _KickStartAutohide();
  1588. _InitBandsite();
  1589. _ClipWindow(TRUE); // make sure we clip the taskbar to the current monitor before showing it
  1590. // it looks really strange for the tray to pop up and rehide at logon
  1591. // if we are autohide don't activate the tray when we show it
  1592. // if we aren't autohide do what Win95 did (tray is active by default)
  1593. ShowWindow(_hwnd, (_uAutoHide & AH_HIDING) ? SW_SHOWNA : SW_SHOW);
  1594. UpdateWindow(_hwnd);
  1595. _StuckTrayChange();
  1596. // get the system background scheduler thread
  1597. IShellTaskScheduler* pScheduler;
  1598. if (SUCCEEDED(CoCreateInstance(CLSID_SharedTaskScheduler, NULL, CLSCTX_INPROC,
  1599. IID_PPV_ARG(IShellTaskScheduler, &pScheduler))))
  1600. {
  1601. AddMenuItemsCacheTask(pScheduler, Tray_StartPanelEnabled());
  1602. pScheduler->Release();
  1603. }
  1604. SetTimer(_hwnd, IDT_HANDLEDELAYBOOTSTUFF, 5 * 1000, NULL);
  1605. }
  1606. if (g_dwProfileCAP & 0x00020000)
  1607. StopCAP();
  1608. if (g_dwStopWatchMode)
  1609. StopWatch_StopTimed(SWID_STARTUP, TEXT("_SyncThreadProc"), SPMODE_SHELL | SPMODE_DEBUGOUT, GetPerfTime());
  1610. return FALSE;
  1611. }
  1612. // the rest of the thread proc that includes the message loop
  1613. DWORD WINAPI CTray::MainThreadProc(void *pv)
  1614. {
  1615. CTray* ptray = (CTray*)pv;
  1616. if (!ptray->_hwnd)
  1617. return FALSE;
  1618. ptray->_OnCreateAsync();
  1619. PERFSETMARK("ExplorerStartMenuReady");
  1620. ptray->_MessageLoop();
  1621. ClassFactory_Stop();
  1622. OleUninitialize(); // matched in _SyncThreadProc()
  1623. return FALSE;
  1624. }
  1625. #define DM_IANELHK 0
  1626. #define HKIF_NULL 0
  1627. #define HKIF_CACHED 1
  1628. #define HKIF_FREEPIDLS 2
  1629. typedef struct
  1630. {
  1631. LPITEMIDLIST pidlFolder;
  1632. LPITEMIDLIST pidlItem;
  1633. WORD wGHotkey;
  1634. WORD wFlags;
  1635. } HOTKEYITEM;
  1636. UINT CTray::_HotkeyGetFreeItemIndex(void)
  1637. {
  1638. int i, cItems;
  1639. HOTKEYITEM *phki;
  1640. ASSERT(IS_VALID_HANDLE(_hdsaHKI, DSA));
  1641. cItems = DSA_GetItemCount(_hdsaHKI);
  1642. for (i=0; i<cItems; i++)
  1643. {
  1644. phki = (HOTKEYITEM *)DSA_GetItemPtr(_hdsaHKI, i);
  1645. if (!phki->wGHotkey)
  1646. {
  1647. ASSERT(!phki->pidlFolder);
  1648. ASSERT(!phki->pidlItem);
  1649. break;
  1650. }
  1651. }
  1652. return i;
  1653. }
  1654. // Weird, Global hotkeys use different flags for modifiers than window hotkeys
  1655. // (and hotkeys returned by the hotkey control)
  1656. WORD _MapHotkeyToGlobalHotkey(WORD wHotkey)
  1657. {
  1658. UINT nMod = 0;
  1659. // Map the modifiers.
  1660. if (HIBYTE(wHotkey) & HOTKEYF_SHIFT)
  1661. nMod |= MOD_SHIFT;
  1662. if (HIBYTE(wHotkey) & HOTKEYF_CONTROL)
  1663. nMod |= MOD_CONTROL;
  1664. if (HIBYTE(wHotkey) & HOTKEYF_ALT)
  1665. nMod |= MOD_ALT;
  1666. UINT nVirtKey = LOBYTE(wHotkey);
  1667. return (WORD)((nMod*256) + nVirtKey);
  1668. }
  1669. // NB This takes a regular window hotkey not a global hotkey (it does
  1670. // the convertion for you).
  1671. int CTray::HotkeyAdd(WORD wHotkey, LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlItem, BOOL fClone)
  1672. {
  1673. if (wHotkey)
  1674. {
  1675. LPCITEMIDLIST pidl1, pidl2;
  1676. HOTKEYITEM hki;
  1677. EnterCriticalSection(&_csHotkey);
  1678. int i = _HotkeyGetFreeItemIndex();
  1679. ASSERT(IS_VALID_HANDLE(_hdsaHKI, DSA));
  1680. // DebugMsg(DM_IANELHK, "c.hl_a: Hotkey %x with id %d.", wHotkey, i);
  1681. if (fClone)
  1682. {
  1683. pidl1 = ILClone(pidlFolder);
  1684. pidl2 = ILClone(pidlItem);
  1685. hki.wFlags = HKIF_FREEPIDLS;
  1686. }
  1687. else
  1688. {
  1689. pidl1 = pidlFolder;
  1690. pidl2 = pidlItem;
  1691. hki.wFlags = HKIF_NULL;
  1692. }
  1693. hki.pidlFolder = (LPITEMIDLIST)pidl1;
  1694. hki.pidlItem = (LPITEMIDLIST)pidl2;
  1695. hki.wGHotkey = _MapHotkeyToGlobalHotkey(wHotkey);
  1696. DSA_SetItem(_hdsaHKI, i, &hki);
  1697. LeaveCriticalSection(&_csHotkey);
  1698. return i;
  1699. }
  1700. return -1;
  1701. }
  1702. // NB Cached hotkeys have their own pidls that need to be free but
  1703. // regular hotkeys just keep a pointer to pidls used by the startmenu and
  1704. // so don't.
  1705. int CTray::_HotkeyAddCached(WORD wGHotkey, LPITEMIDLIST pidl)
  1706. {
  1707. int i = -1;
  1708. if (wGHotkey)
  1709. {
  1710. LPITEMIDLIST pidlItem = ILClone(ILFindLastID(pidl));
  1711. ASSERT(IS_VALID_HANDLE(_hdsaHKI, DSA));
  1712. if (pidlItem)
  1713. {
  1714. if (ILRemoveLastID(pidl))
  1715. {
  1716. HOTKEYITEM hki;
  1717. EnterCriticalSection(&_csHotkey);
  1718. i = _HotkeyGetFreeItemIndex();
  1719. // DebugMsg(DM_IANELHK, "c.hl_ac: Hotkey %x with id %d.", wGHotkey, i);
  1720. hki.pidlFolder = pidl;
  1721. hki.pidlItem = pidlItem;
  1722. hki.wGHotkey = wGHotkey;
  1723. hki.wFlags = HKIF_CACHED | HKIF_FREEPIDLS;
  1724. DSA_SetItem(_hdsaHKI, i, &hki);
  1725. LeaveCriticalSection(&_csHotkey);
  1726. }
  1727. }
  1728. }
  1729. return i;
  1730. }
  1731. // NB Again, this takes window hotkey not a Global one.
  1732. // NB This doesn't delete cached hotkeys.
  1733. int CTray::_HotkeyRemove(WORD wHotkey)
  1734. {
  1735. int iRet = -1;
  1736. if (EVAL(_hdsaHKI))
  1737. {
  1738. int i, cItems;
  1739. HOTKEYITEM *phki;
  1740. WORD wGHotkey;
  1741. ASSERT(IS_VALID_HANDLE(_hdsaHKI, DSA));
  1742. // DebugMsg(DM_IANELHK, "c.hl_r: Remove hotkey for %x" , wHotkey);
  1743. // Unmap the modifiers.
  1744. wGHotkey = _MapHotkeyToGlobalHotkey(wHotkey);
  1745. EnterCriticalSection(&_csHotkey);
  1746. cItems = DSA_GetItemCount(_hdsaHKI);
  1747. for (i=0; i<cItems; i++)
  1748. {
  1749. phki = (HOTKEYITEM *)DSA_GetItemPtr(_hdsaHKI, i);
  1750. if (phki && !(phki->wFlags & HKIF_CACHED) && (phki->wGHotkey == wGHotkey))
  1751. {
  1752. // DebugMsg(DM_IANELHK, "c.hl_r: Invalidating %d", i);
  1753. if (phki->wFlags & HKIF_FREEPIDLS)
  1754. {
  1755. if (phki->pidlFolder)
  1756. ILFree(phki->pidlFolder);
  1757. if (phki->pidlItem)
  1758. ILFree(phki->pidlItem);
  1759. }
  1760. phki->wGHotkey = 0;
  1761. phki->pidlFolder = NULL;
  1762. phki->pidlItem = NULL;
  1763. phki->wFlags &= ~HKIF_FREEPIDLS;
  1764. iRet = i;
  1765. break;
  1766. }
  1767. }
  1768. LeaveCriticalSection(&_csHotkey);
  1769. }
  1770. return iRet;
  1771. }
  1772. // NB This takes a global hotkey.
  1773. int CTray::_HotkeyRemoveCached(WORD wGHotkey)
  1774. {
  1775. int iRet = -1;
  1776. int i, cItems;
  1777. HOTKEYITEM *phki;
  1778. ASSERT(IS_VALID_HANDLE(_hdsaHKI, DSA));
  1779. // DebugMsg(DM_IANELHK, "c.hl_rc: Remove hotkey for %x" , wGHotkey);
  1780. EnterCriticalSection(&_csHotkey);
  1781. cItems = DSA_GetItemCount(_hdsaHKI);
  1782. for (i=0; i<cItems; i++)
  1783. {
  1784. phki = (HOTKEYITEM *)DSA_GetItemPtr(_hdsaHKI, i);
  1785. if (phki && (phki->wFlags & HKIF_CACHED) && (phki->wGHotkey == wGHotkey))
  1786. {
  1787. // DebugMsg(DM_IANELHK, "c.hl_r: Invalidating %d", i);
  1788. if (phki->wFlags & HKIF_FREEPIDLS)
  1789. {
  1790. if (phki->pidlFolder)
  1791. ILFree(phki->pidlFolder);
  1792. if (phki->pidlItem)
  1793. ILFree(phki->pidlItem);
  1794. }
  1795. phki->pidlFolder = NULL;
  1796. phki->pidlItem = NULL;
  1797. phki->wGHotkey = 0;
  1798. phki->wFlags &= ~(HKIF_CACHED | HKIF_FREEPIDLS);
  1799. iRet = i;
  1800. break;
  1801. }
  1802. }
  1803. LeaveCriticalSection(&_csHotkey);
  1804. return iRet;
  1805. }
  1806. // NB Some (the ones not marked HKIF_FREEPIDLS) of the items in the list of hotkeys
  1807. // have pointers to idlists used by the filemenu so they are only valid for
  1808. // the lifetime of the filemenu.
  1809. BOOL CTray::_HotkeyCreate(void)
  1810. {
  1811. if (!_hdsaHKI)
  1812. {
  1813. // DebugMsg(DM_TRACE, "c.hkl_c: Creating global hotkey list.");
  1814. _hdsaHKI = DSA_Create(sizeof(HOTKEYITEM), 0);
  1815. }
  1816. if (_hdsaHKI)
  1817. return TRUE;
  1818. return FALSE;
  1819. }
  1820. void CTray::_BuildStartMenu()
  1821. {
  1822. HRESULT hr;
  1823. ClosePopupMenus();
  1824. //
  1825. // Avoid redundant rebuilds: Peek out any pending SBM_REBUILDMENU messages
  1826. // since the rebuild we're about to do will take care of it. Do this
  1827. // before destroying the Start Menu so we never yield while there isn't
  1828. // a Start Menu.
  1829. //
  1830. MSG msg;
  1831. while (PeekMessage(&msg, _hwnd, SBM_REBUILDMENU, SBM_REBUILDMENU, PM_REMOVE | PM_NOYIELD))
  1832. {
  1833. // Keep sucking them out
  1834. }
  1835. _DestroyStartMenu();
  1836. if (Tray_StartPanelEnabled())
  1837. {
  1838. hr = DesktopV2_Create(&_pmpStartPane, &_pmbStartPane, &_pvStartPane);
  1839. DesktopV2_Build(_pvStartPane);
  1840. }
  1841. else
  1842. {
  1843. hr = StartMenuHost_Create(&_pmpStartMenu, &_pmbStartMenu);
  1844. if (SUCCEEDED(hr))
  1845. {
  1846. IBanneredBar* pbb;
  1847. hr = _pmpStartMenu->QueryInterface(IID_PPV_ARG(IBanneredBar, &pbb));
  1848. if (SUCCEEDED(hr))
  1849. {
  1850. pbb->SetBitmap(_hbmpStartBkg);
  1851. if (_fSMSmallIcons)
  1852. pbb->SetIconSize(BMICON_SMALL);
  1853. else
  1854. pbb->SetIconSize(BMICON_LARGE);
  1855. pbb->Release();
  1856. }
  1857. }
  1858. }
  1859. if (FAILED(hr))
  1860. {
  1861. TraceMsg(TF_ERROR, "Could not create StartMenu");
  1862. }
  1863. }
  1864. void CTray::_DestroyStartMenu()
  1865. {
  1866. IUnknown_SetSite(_pmpStartMenu, NULL);
  1867. ATOMICRELEASET(_pmpStartMenu, IMenuPopup);
  1868. ATOMICRELEASET(_pmbStartMenu, IMenuBand);
  1869. IUnknown_SetSite(_pmpStartPane, NULL);
  1870. ATOMICRELEASET(_pmpStartPane, IMenuPopup);
  1871. ATOMICRELEASET(_pmbStartPane, IMenuBand);
  1872. ATOMICRELEASET(_pmpTasks, IMenuPopup);
  1873. ATOMICRELEASET(_pmbTasks, IMenuBand);
  1874. }
  1875. void CTray::ForceStartButtonUp()
  1876. {
  1877. MSG msg;
  1878. // don't do that check message pos because it gets screwy with
  1879. // keyboard cancel. and besides, we always want it cleared after
  1880. // track menu popup is done.
  1881. // do it twice to be sure it's up due to the _uDown cycling twice in
  1882. // the subclassing stuff
  1883. // pull off any button downs
  1884. PeekMessage(&msg, _hwndStart, WM_LBUTTONDOWN, WM_LBUTTONDOWN, PM_REMOVE);
  1885. SendMessage(_hwndStart, BM_SETSTATE, FALSE, 0);
  1886. SendMessage(_hwndStart, BM_SETSTATE, FALSE, 0);
  1887. if (_hwndTasks)
  1888. SendMessage(_hwndTasks, TBC_SETPREVFOCUS, 0, NULL);
  1889. PostMessage(_hwnd, TM_STARTMENUDISMISSED, 0, 0);
  1890. }
  1891. void Tray_OnStartMenuDismissed()
  1892. {
  1893. c_tray._bMainMenuInit = FALSE;
  1894. // Tell the Start Button that it's allowed to be in the up position now. This
  1895. // prevents the problem where the start menu is displayed but the button is
  1896. // in the up position... This happens when dialog boxes are displayed
  1897. c_tray._fAllowUp = TRUE;
  1898. // Now tell it to be in the up position
  1899. c_tray.ForceStartButtonUp();
  1900. PostMessage(v_hwndTray, TM_SHOWTRAYBALLOON, TRUE, 0);
  1901. }
  1902. int CTray::_TrackMenu(HMENU hmenu)
  1903. {
  1904. TPMPARAMS tpm;
  1905. int iret;
  1906. tpm.cbSize = sizeof(tpm);
  1907. GetClientRect(_hwndStart, &tpm.rcExclude);
  1908. RECT rcClient;
  1909. GetClientRect(_hwnd, &rcClient);
  1910. tpm.rcExclude.bottom = min(tpm.rcExclude.bottom, rcClient.bottom);
  1911. MapWindowPoints(_hwndStart, NULL, (LPPOINT)&tpm.rcExclude, 2);
  1912. SendMessage(_hwndTrayTips, TTM_ACTIVATE, FALSE, 0L);
  1913. iret = TrackPopupMenuEx(hmenu, TPM_VERTICAL | TPM_BOTTOMALIGN | TPM_RETURNCMD,
  1914. tpm.rcExclude.left, tpm.rcExclude.bottom, _hwnd, &tpm);
  1915. SendMessage(_hwndTrayTips, TTM_ACTIVATE, TRUE, 0L);
  1916. return iret;
  1917. }
  1918. /*------------------------------------------------------------------
  1919. ** Respond to a button's pressing by bringing up the appropriate menu.
  1920. ** Clean up the button depression when the menu is dismissed.
  1921. **------------------------------------------------------------------*/
  1922. void CTray::_ToolbarMenu()
  1923. {
  1924. RECTL rcExclude;
  1925. POINTL ptPop;
  1926. DWORD dwFlags = MPPF_KEYBOARD; // Assume that we're popuping
  1927. // up because of the keyboard
  1928. // This is for the underlines on NT5
  1929. if (_hwndTasks)
  1930. SendMessage(_hwndTasks, TBC_FREEPOPUPMENUS, 0, 0);
  1931. if (_hwndStartBalloon)
  1932. {
  1933. _DontShowTheStartButtonBalloonAnyMore();
  1934. ShowWindow(_hwndStartBalloon, SW_HIDE);
  1935. _DestroyStartButtonBalloon();
  1936. }
  1937. SetActiveWindow(_hwnd);
  1938. _bMainMenuInit = TRUE;
  1939. // Exclude rect is the VISIBLE portion of the Start Button.
  1940. {
  1941. RECT rcParent;
  1942. GetClientRect(_hwndStart, (RECT *)&rcExclude);
  1943. MapWindowRect(_hwndStart, HWND_DESKTOP, &rcExclude);
  1944. GetClientRect(_hwnd, &rcParent);
  1945. MapWindowRect(_hwnd, HWND_DESKTOP, &rcParent);
  1946. IntersectRect((RECT*)&rcExclude, (RECT*)&rcExclude, &rcParent);
  1947. }
  1948. ptPop.x = rcExclude.left;
  1949. ptPop.y = rcExclude.top;
  1950. // Close any Context Menus
  1951. SendMessage(_hwnd, WM_CANCELMODE, 0, 0);
  1952. // Is the "Activate" button down (If the buttons are swapped, then it's the
  1953. // right button, otherwise the left button)
  1954. if (GetKeyState(GetSystemMetrics(SM_SWAPBUTTON)?VK_RBUTTON:VK_LBUTTON) < 0)
  1955. {
  1956. dwFlags = 0; // Then set to the default
  1957. }
  1958. IMenuPopup **ppmpToDisplay = &_pmpStartMenu;
  1959. if (_pmpStartPane)
  1960. {
  1961. ppmpToDisplay = &_pmpStartPane;
  1962. }
  1963. // Close race window: The user can click on the Start Button
  1964. // before we get a chance to rebuild the Start Menu in its new
  1965. // form. In such case, rebuild it now.
  1966. if (!*ppmpToDisplay)
  1967. {
  1968. TraceMsg(TF_WARNING, "e.tbm: Rebuilding Start Menu");
  1969. _BuildStartMenu();
  1970. }
  1971. if (*ppmpToDisplay && SUCCEEDED((*ppmpToDisplay)->Popup(&ptPop, &rcExclude, dwFlags)))
  1972. {
  1973. // All is well - the menu is up
  1974. TraceMsg(DM_MISC, "e.tbm: dwFlags=%x (0=mouse 1=key)", dwFlags);
  1975. }
  1976. else
  1977. {
  1978. TraceMsg(TF_WARNING, "e.tbm: %08x->Popup failed", *ppmpToDisplay);
  1979. // Start Menu failed to display -- reset the Start Button
  1980. // so the user can click it again to try again
  1981. Tray_OnStartMenuDismissed();
  1982. }
  1983. if (dwFlags == MPPF_KEYBOARD)
  1984. {
  1985. // Since the user has launched the start button by Ctrl-Esc, or some other worldly
  1986. // means, then turn the rect on.
  1987. SendMessage(_hwndStart, WM_UPDATEUISTATE, MAKEWPARAM(UIS_CLEAR,
  1988. UISF_HIDEFOCUS), 0);
  1989. }
  1990. }
  1991. HRESULT CTray::_AppBarSetState(UINT uFlags)
  1992. {
  1993. if (uFlags & ~(ABS_AUTOHIDE | ABS_ALWAYSONTOP))
  1994. {
  1995. return E_INVALIDARG;
  1996. }
  1997. else
  1998. {
  1999. _SetAutoHideState(uFlags & ABS_AUTOHIDE);
  2000. _UpdateAlwaysOnTop(uFlags & ABS_ALWAYSONTOP);
  2001. return S_OK;
  2002. }
  2003. }
  2004. //
  2005. // can't use SubtractRect sometimes because of inclusion limitations
  2006. //
  2007. void CTray::_AppBarSubtractRect(PAPPBAR pab, LPRECT lprc)
  2008. {
  2009. switch (pab->uEdge) {
  2010. case ABE_TOP:
  2011. if (pab->rc.bottom > lprc->top)
  2012. lprc->top = pab->rc.bottom;
  2013. break;
  2014. case ABE_LEFT:
  2015. if (pab->rc.right > lprc->left)
  2016. lprc->left = pab->rc.right;
  2017. break;
  2018. case ABE_BOTTOM:
  2019. if (pab->rc.top < lprc->bottom)
  2020. lprc->bottom = pab->rc.top;
  2021. break;
  2022. case ABE_RIGHT:
  2023. if (pab->rc.left < lprc->right)
  2024. lprc->right = pab->rc.left;
  2025. break;
  2026. }
  2027. }
  2028. void CTray::_AppBarSubtractRects(HMONITOR hmon, LPRECT lprc)
  2029. {
  2030. int i;
  2031. if (!_hdpaAppBars)
  2032. return;
  2033. i = DPA_GetPtrCount(_hdpaAppBars);
  2034. while (i--)
  2035. {
  2036. PAPPBAR pab = (PAPPBAR)DPA_GetPtr(_hdpaAppBars, i);
  2037. //
  2038. // autohide bars are not in our DPA or live on the edge
  2039. // don't subtract the appbar if it's on a different display
  2040. // don't subtract the appbar if we are in a locked desktop
  2041. //
  2042. // if (hmon == MonitorFromRect(&pab->rc, MONITOR_DEFAULTTONULL))
  2043. if (hmon == MonitorFromRect(&pab->rc, MONITOR_DEFAULTTONULL) && !_fIsDesktopLocked)
  2044. _AppBarSubtractRect(pab, lprc);
  2045. }
  2046. }
  2047. #define RWA_NOCHANGE 0
  2048. #define RWA_CHANGED 1
  2049. #define RWA_BOTTOMMOSTTRAY 2
  2050. // (dli) This is a hack put in because bottommost tray is wierd, once
  2051. // it becomes a toolbar, this code should go away.
  2052. // In the bottommost tray case, even though the work area has not changed,
  2053. // we should notify the desktop.
  2054. int CTray::_RecomputeWorkArea(HWND hwndCause, HMONITOR hmon, LPRECT prcWork)
  2055. {
  2056. int iRet = RWA_NOCHANGE;
  2057. MONITORINFO mi;
  2058. mi.cbSize = sizeof(mi);
  2059. if (_fIsLogoff)
  2060. {
  2061. if (GetMonitorInfo(hmon, &mi))
  2062. {
  2063. *prcWork = mi.rcMonitor;
  2064. iRet = RWA_CHANGED;
  2065. }
  2066. return iRet;
  2067. }
  2068. ASSERT(!_fIsLogoff);
  2069. //
  2070. // tell everybody that this window changed positions _on_this_monitor_
  2071. // note that this notify happens even if we don't change the work area
  2072. // since it may cause another app to change the work area...
  2073. //
  2074. PostMessage(_hwnd, TM_RELAYPOSCHANGED, (WPARAM)hwndCause, (LPARAM)hmon);
  2075. //
  2076. // get the current info for this monitor
  2077. // we subtract down from the display rectangle to build the work area
  2078. //
  2079. if (GetMonitorInfo(hmon, &mi))
  2080. {
  2081. //
  2082. // don't subtract the tray if it is autohide
  2083. // don't subtract the tray if it is not always on top
  2084. // don't subtract the tray if it's on a different display
  2085. // don't subtract the tray if it is on a different desktop
  2086. //
  2087. if (!(_uAutoHide & AH_ON) && _fAlwaysOnTop &&
  2088. (hmon == _hmonStuck) && !_fIsDesktopLocked)
  2089. {
  2090. SubtractRect(prcWork, &mi.rcMonitor,
  2091. &_arStuckRects[_uStuckPlace]);
  2092. }
  2093. else
  2094. *prcWork = mi.rcMonitor;
  2095. //
  2096. // now subtract off all the appbars on this display
  2097. //
  2098. _AppBarSubtractRects(hmon, prcWork);
  2099. //
  2100. // return whether we changed anything
  2101. //
  2102. if (!EqualRect(prcWork, &mi.rcWork))
  2103. iRet = RWA_CHANGED;
  2104. else if (!(_uAutoHide & AH_ON) && (!_fAlwaysOnTop) &&
  2105. (!IsRectEmpty(&_arStuckRects[_uStuckPlace])))
  2106. // NOTE: This is the bottommost case, it only applies for the tray.
  2107. // this should be taken out when bottommost tray becomes toolbar
  2108. iRet = RWA_BOTTOMMOSTTRAY;
  2109. }
  2110. else
  2111. {
  2112. iRet = RWA_NOCHANGE;
  2113. }
  2114. return iRet;
  2115. }
  2116. void RedrawDesktop(RECT *prcWork)
  2117. {
  2118. // This rect point should always be valid (dli)
  2119. RIP(prcWork);
  2120. if (v_hwndDesktop && g_fCleanBoot)
  2121. {
  2122. MapWindowRect(NULL, v_hwndDesktop, prcWork);
  2123. DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.sac invalidating desktop rect {%d,%d,%d,%d}"), prcWork->left, prcWork->top, prcWork->right, prcWork->bottom);
  2124. RedrawWindow(v_hwndDesktop, prcWork, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN);
  2125. }
  2126. }
  2127. void CTray::_StuckAppChange(HWND hwndCause, LPCRECT prcOld, LPCRECT prcNew, BOOL bTray)
  2128. {
  2129. RECT rcWork1, rcWork2;
  2130. HMONITOR hmon1, hmon2 = 0;
  2131. int iChange = 0;
  2132. //
  2133. // PERF FEATURE:
  2134. // there are cases where we end up setting the work area multiple times
  2135. // we need to keep a static array of displays that have changed and a
  2136. // reenter count so we can avoid pain of sending notifies to the whole
  2137. // planet...
  2138. //
  2139. DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.sac from_AppBar %08X"), hwndCause);
  2140. //
  2141. // see if the work area changed on the display containing prcOld
  2142. //
  2143. if (prcOld)
  2144. {
  2145. if (bTray)
  2146. hmon1 = _hmonOld;
  2147. else
  2148. hmon1 = MonitorFromRect(prcOld, MONITOR_DEFAULTTONEAREST);
  2149. DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.sac old pos {%d,%d,%d,%d} on monitor %08X"), prcOld->left, prcOld->top, prcOld->right, prcOld->bottom, hmon1);
  2150. if (hmon1)
  2151. {
  2152. int iret = _RecomputeWorkArea(hwndCause, hmon1, &rcWork1);
  2153. if (iret == RWA_CHANGED)
  2154. iChange = 1;
  2155. if (iret == RWA_BOTTOMMOSTTRAY)
  2156. iChange = 4;
  2157. }
  2158. }
  2159. else
  2160. hmon1 = NULL;
  2161. //
  2162. // see if the work area changed on the display containing prcNew
  2163. //
  2164. if (prcNew)
  2165. {
  2166. hmon2 = MonitorFromRect(prcNew, MONITOR_DEFAULTTONULL);
  2167. DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.sac new pos {%d,%d,%d,%d} on monitor %08X"), prcNew->left, prcNew->top, prcNew->right, prcNew->bottom, hmon2);
  2168. if (hmon2 && (hmon2 != hmon1))
  2169. {
  2170. int iret = _RecomputeWorkArea(hwndCause, hmon2, &rcWork2);
  2171. if (iret == RWA_CHANGED)
  2172. iChange |= 2;
  2173. else if (iret == RWA_BOTTOMMOSTTRAY && (!iChange))
  2174. iChange = 4;
  2175. }
  2176. }
  2177. //
  2178. // did the prcOld's display's work area change?
  2179. //
  2180. if (iChange & 1)
  2181. {
  2182. DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.sac changing work area for monitor %08X"), hmon1);
  2183. // only send SENDWININICHANGE if the desktop has been created (otherwise
  2184. // we will hang the explorer because the main thread is currently blocked)
  2185. SystemParametersInfo(SPI_SETWORKAREA, TRUE, &rcWork1,
  2186. (iChange == 1 && v_hwndDesktop)? SPIF_SENDWININICHANGE : 0);
  2187. RedrawDesktop(&rcWork1);
  2188. }
  2189. //
  2190. // did the prcOld's display's work area change?
  2191. //
  2192. if (iChange & 2)
  2193. {
  2194. DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.sac changing work area for monitor %08X"), hmon2);
  2195. // only send SENDWININICHANGE if the desktop has been created (otherwise
  2196. // we will hang the explorer because the main thread is currently blocked)
  2197. SystemParametersInfo(SPI_SETWORKAREA, TRUE, &rcWork2,
  2198. v_hwndDesktop ? SPIF_SENDWININICHANGE : 0);
  2199. RedrawDesktop(&rcWork2);
  2200. }
  2201. // only send if the desktop has been created...
  2202. // need to send if it's from the tray or any outside app that causes size change
  2203. // from the tray because autohideness will affect desktop size even if it's not always on top
  2204. if ((bTray || iChange == 4) && v_hwndDesktop)
  2205. SendMessage(v_hwndDesktop, WM_SIZE, 0, 0);
  2206. }
  2207. void CTray::_StuckTrayChange()
  2208. {
  2209. // We used to blow off the _StuckAppChange when the tray was in autohide
  2210. // mode, since moving or resizing an autohid tray doesn't change the
  2211. // work area. Now we go ahead with the _StuckAppChange in this case
  2212. // too. The reason is that we can get into a state where the work area
  2213. // size is incorrect, and we want the taskbar to always be self-repairing
  2214. // in this case (so that resizing or moving the taskbar will correct the
  2215. // work area size).
  2216. //
  2217. // pass a NULL window here since we don't want to hand out our window and
  2218. // the tray doesn't get these anyway (nobody cares as long as its not them)
  2219. //
  2220. _StuckAppChange(NULL, &_rcOldTray,
  2221. &_arStuckRects[_uStuckPlace], TRUE);
  2222. //
  2223. // save off the new tray position...
  2224. //
  2225. _rcOldTray = _arStuckRects[_uStuckPlace];
  2226. }
  2227. UINT CTray::_RecalcStuckPos(LPRECT prc)
  2228. {
  2229. RECT rcDummy;
  2230. POINT pt;
  2231. if (!prc)
  2232. {
  2233. DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_rsp no rect supplied, using window rect"));
  2234. prc = &rcDummy;
  2235. GetWindowRect(_hwnd, prc);
  2236. }
  2237. // use the center of the original drag rect as a staring point
  2238. pt.x = prc->left + RECTWIDTH(*prc) / 2;
  2239. pt.y = prc->top + RECTHEIGHT(*prc) / 2;
  2240. 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);
  2241. // reset this so the drag code won't give it preference
  2242. _uMoveStuckPlace = (UINT)-1;
  2243. // simulate a drag back to figure out where we originated from
  2244. // you may be tempted to remove this. before you do think about dragging
  2245. // the tray across monitors and then hitting ESC...
  2246. return _CalcDragPlace(pt);
  2247. }
  2248. /*------------------------------------------------------------------
  2249. ** the position is changing in response to a move operation.
  2250. **
  2251. ** if the docking status changed, we need to get a new size and
  2252. ** maybe a new frame style. change the WINDOWPOS to reflect
  2253. ** these changes accordingly.
  2254. **------------------------------------------------------------------*/
  2255. void CTray::_DoneMoving(LPWINDOWPOS lpwp)
  2256. {
  2257. RECT rc, *prc;
  2258. if ((_uMoveStuckPlace == (UINT)-1) || (_fIgnoreDoneMoving))
  2259. return;
  2260. if (_fSysSizing)
  2261. _fDeferedPosRectChange = TRUE;
  2262. rc.left = lpwp->x;
  2263. rc.top = lpwp->y;
  2264. rc.right = lpwp->x + lpwp->cx;
  2265. rc.bottom = lpwp->y + lpwp->cy;
  2266. prc = &_arStuckRects[_uMoveStuckPlace];
  2267. if (!EqualRect(prc, &rc))
  2268. {
  2269. _uMoveStuckPlace = _RecalcStuckPos(&rc);
  2270. prc = &_arStuckRects[_uMoveStuckPlace];
  2271. }
  2272. // Get the new hmonitor
  2273. _hmonStuck = MonitorFromRect(prc, MONITOR_DEFAULTTONEAREST);
  2274. lpwp->x = prc->left;
  2275. lpwp->y = prc->top;
  2276. lpwp->cx = RECTWIDTH(*prc);
  2277. lpwp->cy = RECTHEIGHT(*prc);
  2278. lpwp->flags &= ~(SWP_NOMOVE | SWP_NOSIZE);
  2279. // if we were autohiding, we need to update our appbar autohide rect
  2280. if (_uAutoHide & AH_ON)
  2281. {
  2282. // unregister us from the old side
  2283. _AppBarSetAutoHideBar2(_hwnd, FALSE, _uStuckPlace);
  2284. }
  2285. // All that work might've changed _uMoveStuckPlace (since there
  2286. // was a lot of message traffic), so check one more time.
  2287. // Somehow, NT Stress manages to get us in here with an invalid
  2288. // uMoveStuckPlace.
  2289. if (IsValidSTUCKPLACE(_uMoveStuckPlace))
  2290. {
  2291. // remember the new state
  2292. _uStuckPlace = _uMoveStuckPlace;
  2293. }
  2294. _uMoveStuckPlace = (UINT)-1;
  2295. _UpdateVertical(_uStuckPlace);
  2296. _HandleSizing(0, prc, _uStuckPlace);
  2297. if ((_uAutoHide & AH_ON) &&
  2298. !_AppBarSetAutoHideBar2(_hwnd, TRUE, _uStuckPlace))
  2299. {
  2300. _AutoHideCollision();
  2301. }
  2302. }
  2303. UINT CTray::_CalcDragPlace(POINT pt)
  2304. {
  2305. UINT uPlace = _uMoveStuckPlace;
  2306. DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_cdp starting point is {%d, %d}"), pt.x, pt.y);
  2307. //
  2308. // if the mouse is currently over the tray position leave it alone
  2309. //
  2310. if ((uPlace == (UINT)-1) || !PtInRect(&_arStuckRects[uPlace], pt))
  2311. {
  2312. HMONITOR hmonDrag;
  2313. SIZE screen, error;
  2314. UINT uHorzEdge, uVertEdge;
  2315. RECT rcDisplay, *prcStick;
  2316. //
  2317. // which display is the mouse on?
  2318. //
  2319. hmonDrag = _GetDisplayRectFromPoint(&rcDisplay, pt,
  2320. MONITOR_DEFAULTTOPRIMARY);
  2321. DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_cdp monitor is %08X"), hmonDrag);
  2322. //
  2323. // re-origin at zero to make calculations simpler
  2324. //
  2325. screen.cx = RECTWIDTH(rcDisplay);
  2326. screen.cy = RECTHEIGHT(rcDisplay);
  2327. pt.x -= rcDisplay.left;
  2328. pt.y -= rcDisplay.top;
  2329. //
  2330. // are we closer to the left or right side of this display?
  2331. //
  2332. if (pt.x < (screen.cx / 2))
  2333. {
  2334. uVertEdge = STICK_LEFT;
  2335. error.cx = pt.x;
  2336. }
  2337. else
  2338. {
  2339. uVertEdge = STICK_RIGHT;
  2340. error.cx = screen.cx - pt.x;
  2341. }
  2342. //
  2343. // are we closer to the top or bottom side of this display?
  2344. //
  2345. if (pt.y < (screen.cy / 2))
  2346. {
  2347. uHorzEdge = STICK_TOP;
  2348. error.cy = pt.y;
  2349. }
  2350. else
  2351. {
  2352. uHorzEdge = STICK_BOTTOM;
  2353. error.cy = screen.cy - pt.y;
  2354. }
  2355. //
  2356. // closer to a horizontal or vertical edge?
  2357. //
  2358. uPlace = ((error.cy * screen.cx) > (error.cx * screen.cy))?
  2359. uVertEdge : uHorzEdge;
  2360. // which StuckRect should we use?
  2361. prcStick = &_arStuckRects[uPlace];
  2362. //
  2363. // need to recalc stuck rect for new monitor?
  2364. //
  2365. if ((hmonDrag != _GetDisplayRectFromRect(NULL, prcStick,
  2366. MONITOR_DEFAULTTONULL)))
  2367. {
  2368. DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_cdp re-snapping rect for new display"));
  2369. _MakeStuckRect(prcStick, &rcDisplay, _sStuckWidths, uPlace);
  2370. }
  2371. }
  2372. DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_cdp edge is %d, rect is {%d, %d, %d, %d}"), uPlace, _arStuckRects[uPlace].left, _arStuckRects[uPlace].top, _arStuckRects[uPlace].right, _arStuckRects[uPlace].bottom);
  2373. ASSERT(IsValidSTUCKPLACE(uPlace));
  2374. return uPlace;
  2375. }
  2376. void CTray::_HandleMoving(WPARAM wParam, LPRECT lprc)
  2377. {
  2378. POINT ptCursor;
  2379. GetCursorPos(&ptCursor);
  2380. // If the cursor is not far from its starting point, then ignore it.
  2381. // A very common problem is the user clicks near the corner of the clock,
  2382. // twitches the mouse 5 pixels, and BLAM, their taskbar is now vertical
  2383. // and they don't know what they did or how to get it back.
  2384. if (g_fInSizeMove && PtInRect(&_rcSizeMoveIgnore, ptCursor))
  2385. {
  2386. // Ignore -- user is merely twitching
  2387. _uMoveStuckPlace = _uStuckPlace;
  2388. }
  2389. else
  2390. {
  2391. _uMoveStuckPlace = _CalcDragPlace(ptCursor);
  2392. }
  2393. *lprc = _arStuckRects[_uMoveStuckPlace];
  2394. _HandleSizing(wParam, lprc, _uMoveStuckPlace);
  2395. }
  2396. // store the tray size when dragging is finished
  2397. void CTray::_SnapshotStuckRectSize(UINT uPlace)
  2398. {
  2399. RECT rcDisplay, *prc = &_arStuckRects[uPlace];
  2400. //
  2401. // record the width of this stuck rect
  2402. //
  2403. if (STUCK_HORIZONTAL(uPlace))
  2404. _sStuckWidths.cy = RECTHEIGHT(*prc);
  2405. else
  2406. _sStuckWidths.cx = RECTWIDTH(*prc);
  2407. //
  2408. // we only present a horizontal or vertical size to the end user
  2409. // so update the StuckRect on the other side of the screen to match
  2410. //
  2411. _GetStuckDisplayRect(uPlace, &rcDisplay);
  2412. uPlace += 2;
  2413. uPlace %= 4;
  2414. prc = &_arStuckRects[uPlace];
  2415. _MakeStuckRect(prc, &rcDisplay, _sStuckWidths, uPlace);
  2416. }
  2417. // Size the icon area to fill as much of the tray window as it can.
  2418. void CTray::SizeWindows()
  2419. {
  2420. RECT rcView, rcNotify, rcClient;
  2421. int fHiding;
  2422. if (!_hwndRebar || !_hwnd || !_hwndNotify)
  2423. return;
  2424. fHiding = (_uAutoHide & AH_HIDING);
  2425. if (fHiding)
  2426. {
  2427. InvisibleUnhide(FALSE);
  2428. }
  2429. // remember our current size
  2430. _SnapshotStuckRectSize(_uStuckPlace);
  2431. GetClientRect(_hwnd, &rcClient);
  2432. _AlignStartButton();
  2433. _GetWindowSizes(_uStuckPlace, &rcClient, &rcView, &rcNotify);
  2434. InvalidateRect(_hwndStart, NULL, TRUE);
  2435. InvalidateRect(_hwnd, NULL, TRUE);
  2436. // position the view
  2437. SetWindowPos(_hwndRebar, NULL, rcView.left, rcView.top,
  2438. RECTWIDTH(rcView), RECTHEIGHT(rcView),
  2439. SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS);
  2440. UpdateWindow(_hwndRebar);
  2441. // And the clock
  2442. SetWindowPos(_hwndNotify, NULL, rcNotify.left, rcNotify.top,
  2443. RECTWIDTH(rcNotify), RECTHEIGHT(rcNotify),
  2444. SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS);
  2445. {
  2446. TOOLINFO ti;
  2447. HWND hwndClock = _GetClockWindow();
  2448. ti.cbSize = sizeof(ti);
  2449. ti.uFlags = 0;
  2450. ti.hwnd = _hwnd;
  2451. ti.lpszText = LPSTR_TEXTCALLBACK;
  2452. ti.uId = (UINT_PTR)hwndClock;
  2453. GetWindowRect(hwndClock, &ti.rect);
  2454. MapWindowPoints(HWND_DESKTOP, _hwnd, (LPPOINT)&ti.rect, 2);
  2455. SendMessage(_hwndTrayTips, TTM_NEWTOOLRECT, 0, (LPARAM)((LPTOOLINFO)&ti));
  2456. }
  2457. if (fHiding)
  2458. {
  2459. InvisibleUnhide(TRUE);
  2460. }
  2461. }
  2462. void CTray::_HandleSize()
  2463. {
  2464. //
  2465. // if somehow we got minimized go ahead and un-minimize
  2466. //
  2467. if (((GetWindowLong(_hwnd, GWL_STYLE)) & WS_MINIMIZE))
  2468. {
  2469. ASSERT(FALSE);
  2470. ShowWindow(_hwnd, SW_RESTORE);
  2471. }
  2472. //
  2473. // if we are in the move/size loop and are visible then
  2474. // re-snap the current stuck rect to the new window size
  2475. //
  2476. #ifdef DEBUG
  2477. if (_fSysSizing && (_uAutoHide & AH_HIDING)) {
  2478. TraceMsg(DM_TRACE, "fSysSize && hiding");
  2479. ASSERT(0);
  2480. }
  2481. #endif
  2482. if (_fSysSizing &&
  2483. ((_uAutoHide & (AH_ON | AH_HIDING)) != (AH_ON | AH_HIDING)))
  2484. {
  2485. _uStuckPlace = _RecalcStuckPos(NULL);
  2486. _UpdateVertical(_uStuckPlace);
  2487. }
  2488. //
  2489. // if we are in fulldrag or we are not in the middle of a move/size then
  2490. // we should resize all our child windows to reflect our new size
  2491. //
  2492. if (g_fDragFullWindows || !_fSysSizing)
  2493. SizeWindows();
  2494. //
  2495. // if we are just plain resized and we are visible we may need re-dock
  2496. //
  2497. if (!_fSysSizing && !_fSelfSizing && IsWindowVisible(_hwnd))
  2498. {
  2499. if (_uAutoHide & AH_ON)
  2500. {
  2501. UINT uPlace = _uStuckPlace;
  2502. HWND hwndOther =_AppBarGetAutoHideBar(uPlace);
  2503. //
  2504. // we sometimes defer checking for this until after a move
  2505. // so as to avoid interrupting a full-window-drag in progress
  2506. // if there is a different autohide window in our slot then whimper
  2507. //
  2508. if (hwndOther?
  2509. (hwndOther != _hwnd) :
  2510. !_AppBarSetAutoHideBar2(_hwnd, TRUE, uPlace))
  2511. {
  2512. _AutoHideCollision();
  2513. }
  2514. }
  2515. _StuckTrayChange();
  2516. //
  2517. // make sure we clip to tray to the current monitor (if necessary)
  2518. //
  2519. _ClipWindow(TRUE);
  2520. }
  2521. if (_hwndStartBalloon)
  2522. {
  2523. RECT rc;
  2524. GetWindowRect(_hwndStart, &rc);
  2525. SendMessage(_hwndStartBalloon, TTM_TRACKPOSITION, 0, MAKELONG((rc.left + rc.right)/2, rc.top));
  2526. SetWindowZorder(_hwndStartBalloon, HWND_TOPMOST);
  2527. }
  2528. }
  2529. BOOL _IsSliverHeight(int cy)
  2530. {
  2531. //
  2532. // Is this height clearly bigger than the pure-border height that you
  2533. // get when you resize the taskbar as small as it will go?
  2534. //
  2535. return (cy < (3 * (g_cyDlgFrame + g_cyBorder)));
  2536. }
  2537. BOOL CTray::_HandleSizing(WPARAM code, LPRECT lprc, UINT uStuckPlace)
  2538. {
  2539. BOOL fChangedSize = FALSE;
  2540. RECT rcDisplay;
  2541. SIZE sNewWidths;
  2542. RECT rcTemp;
  2543. BOOL fHiding;
  2544. if (!lprc)
  2545. {
  2546. rcTemp = _arStuckRects[uStuckPlace];
  2547. lprc = &rcTemp;
  2548. }
  2549. fHiding = (_uAutoHide & AH_HIDING);
  2550. if (fHiding)
  2551. {
  2552. InvisibleUnhide(FALSE);
  2553. }
  2554. //
  2555. // get the a bunch of relevant dimensions
  2556. //
  2557. // (dli) need to change this funciton or get rid of it
  2558. _GetDisplayRectFromRect(&rcDisplay, lprc, MONITOR_DEFAULTTONEAREST);
  2559. if (code)
  2560. {
  2561. // if code != 0, this is the user sizing.
  2562. // make sure they clip it to the screen.
  2563. RECT rcMax = rcDisplay;
  2564. if (!_hTheme)
  2565. {
  2566. InflateRect(&rcMax, g_cxEdge, g_cyEdge);
  2567. }
  2568. // don't do intersect rect because of sizing up from the bottom
  2569. // (when taskbar docked on bottom) confuses people
  2570. switch (uStuckPlace)
  2571. {
  2572. case STICK_LEFT:
  2573. lprc->left = rcMax.left;
  2574. break;
  2575. case STICK_TOP:
  2576. lprc->top = rcMax.top;
  2577. break;
  2578. case STICK_RIGHT:
  2579. lprc->right = rcMax.right;
  2580. break;
  2581. case STICK_BOTTOM:
  2582. lprc->top += (rcMax.bottom-lprc->bottom);
  2583. lprc->bottom = rcMax.bottom;
  2584. break;
  2585. }
  2586. }
  2587. //
  2588. // compute the new widths
  2589. // don't let either be more than half the screen
  2590. //
  2591. sNewWidths.cx = min(RECTWIDTH(*lprc), RECTWIDTH(rcDisplay) / 2);
  2592. sNewWidths.cy = min(RECTHEIGHT(*lprc), RECTHEIGHT(rcDisplay) / 2);
  2593. if (_hTheme && (_fCanSizeMove || _fShowSizingBarAlways))
  2594. {
  2595. sNewWidths.cy = max(_sizeSizingBar.cy, sNewWidths.cy);
  2596. }
  2597. //
  2598. // compute an initial size
  2599. //
  2600. _MakeStuckRect(lprc, &rcDisplay, sNewWidths, uStuckPlace);
  2601. DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_hs starting rect is {%d, %d, %d, %d}"), lprc->left, lprc->top, lprc->right, lprc->bottom);
  2602. //
  2603. // negotiate the exact size with our children
  2604. //
  2605. DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_hs tray is being calculated for %s"), STUCK_HORIZONTAL(uStuckPlace) ? TEXT("HORIZONTAL") : TEXT("VERTICAL"));
  2606. _UpdateVertical(uStuckPlace);
  2607. if (_ptbs)
  2608. {
  2609. IDeskBarClient* pdbc;
  2610. if (SUCCEEDED(_ptbs->QueryInterface(IID_PPV_ARG(IDeskBarClient, &pdbc))))
  2611. {
  2612. RECT rcClient = *lprc;
  2613. RECT rcOldClient = _arStuckRects[uStuckPlace];
  2614. // Go from a Window Rect to Client Rect
  2615. if (_hTheme && (_fCanSizeMove || _fShowSizingBarAlways))
  2616. {
  2617. _AdjustRectForSizingBar(uStuckPlace, &rcClient, -1);
  2618. _AdjustRectForSizingBar(uStuckPlace, &rcOldClient, -1);
  2619. }
  2620. else if (!_hTheme)
  2621. {
  2622. InflateRect(&rcClient, -g_cxFrame, -g_cyFrame);
  2623. InflateRect(&rcOldClient, -g_cxFrame, -g_cyFrame);
  2624. }
  2625. // Make rcClient start at 0,0, Rebar only used the right and bottom values of this rect
  2626. OffsetRect(&rcClient, -rcClient.left, -rcClient.top);
  2627. OffsetRect(&rcOldClient, -rcOldClient.left, -rcOldClient.top);
  2628. DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_hs starting client rect is {%d, %d, %d, %d}"), rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
  2629. RECT rcNotify;
  2630. RECT rcView;
  2631. RECT rcOldView;
  2632. // Go from the taskbar's client rect to the rebar's client rect
  2633. _GetWindowSizes(uStuckPlace, &rcClient, &rcView, &rcNotify);
  2634. _GetWindowSizes(uStuckPlace, &rcOldClient, &rcOldView, &rcNotify);
  2635. // Make rcView start at 0,0, Rebar only used the right and bottom values of this rect
  2636. OffsetRect(&rcView, -rcView.left, -rcView.top);
  2637. OffsetRect(&rcOldView, -rcOldView.left, -rcOldView.top);
  2638. if (!_fCanSizeMove || (RECTHEIGHT(rcView) && RECTWIDTH(rcView)))
  2639. {
  2640. // This following function will cause a WINDOWPOSCHAGING which will call DoneMoving
  2641. // DoneMoving will then go a screw up all of the window sizing
  2642. _fIgnoreDoneMoving = TRUE;
  2643. pdbc->GetSize(DBC_GS_SIZEDOWN, &rcView);
  2644. _fIgnoreDoneMoving = FALSE;
  2645. }
  2646. // Go from a Client Rect to Window Rect
  2647. if (STUCK_HORIZONTAL(uStuckPlace))
  2648. {
  2649. rcClient.top = rcView.top;
  2650. rcClient.bottom = rcView.bottom;
  2651. }
  2652. else
  2653. {
  2654. rcClient.left = rcView.left;
  2655. rcClient.right = rcView.right;
  2656. }
  2657. DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_hs ending client rect is {%d, %d, %d, %d}"), rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
  2658. if (_hTheme && (_fCanSizeMove || _fShowSizingBarAlways))
  2659. {
  2660. _AdjustRectForSizingBar(uStuckPlace, &rcClient, 1);
  2661. _AdjustRectForSizingBar(uStuckPlace, &rcOldClient, 1);
  2662. }
  2663. else if (!_hTheme)
  2664. {
  2665. InflateRect(&rcClient, g_cxFrame, g_cyFrame);
  2666. InflateRect(&rcOldClient, g_cxFrame, g_cyFrame);
  2667. }
  2668. // Prevent huge growth of taskbar, caused by bugs in the rebar sizing code
  2669. if (RECTHEIGHT(rcView) && RECTHEIGHT(rcOldView) && (RECTHEIGHT(rcClient) > (3 * RECTHEIGHT(rcOldClient))))
  2670. {
  2671. rcClient = rcOldClient;
  2672. }
  2673. if (STUCK_HORIZONTAL(uStuckPlace) && sNewWidths.cy != RECTHEIGHT(rcClient))
  2674. {
  2675. sNewWidths.cy = RECTHEIGHT(rcClient);
  2676. fChangedSize = TRUE;
  2677. }
  2678. if (!STUCK_HORIZONTAL(uStuckPlace) && sNewWidths.cx != RECTWIDTH(rcClient))
  2679. {
  2680. sNewWidths.cx = RECTWIDTH(rcClient);
  2681. fChangedSize = TRUE;
  2682. }
  2683. pdbc->Release();
  2684. }
  2685. }
  2686. //
  2687. // was there a change?
  2688. //
  2689. if (fChangedSize)
  2690. {
  2691. //
  2692. // yes, update the final rectangle
  2693. //
  2694. _MakeStuckRect(lprc, &rcDisplay, sNewWidths, uStuckPlace);
  2695. }
  2696. DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_hs final rect is {%d, %d, %d, %d}"), lprc->left, lprc->top, lprc->right, lprc->bottom);
  2697. //
  2698. // store the new size in the appropriate StuckRect
  2699. //
  2700. _arStuckRects[uStuckPlace] = *lprc;
  2701. if (fHiding)
  2702. {
  2703. InvisibleUnhide(TRUE);
  2704. }
  2705. if (_hwndStartBalloon)
  2706. {
  2707. RECT rc;
  2708. GetWindowRect(_hwndStart, &rc);
  2709. SendMessage(_hwndStartBalloon, TTM_TRACKPOSITION, 0, MAKELONG((rc.left + rc.right)/2, rc.top));
  2710. SetWindowZorder(_hwndStartBalloon, HWND_TOPMOST);
  2711. }
  2712. return fChangedSize;
  2713. }
  2714. /*-------------------------------------------------------------------
  2715. ** the screen size changed, and we need to adjust some stuff, mostly
  2716. ** globals. if the tray was docked, it needs to be resized, too.
  2717. **
  2718. ** TRICKINESS: the handling of WM_WINDOWPOSCHANGING is used to
  2719. ** actually do all the real sizing work. this saves a bit of
  2720. ** extra code here.
  2721. **-------------------------------------------------------------------*/
  2722. BOOL WINAPI CTray::MonitorEnumProc(HMONITOR hMonitor, HDC hdc, LPRECT lprc, LPARAM lData)
  2723. {
  2724. CTray* ptray = (CTray*)lData;
  2725. RECT rcWork;
  2726. int iRet = ptray->_RecomputeWorkArea(NULL, hMonitor, &rcWork);
  2727. if (iRet == RWA_CHANGED)
  2728. {
  2729. // only send SENDWININICHANGE if the desktop has been created (otherwise
  2730. // we will hang the explorer because the main thread is currently blocked)
  2731. // PERF FEATURE: it will be nice to send WININICHANGE only once, but we can't
  2732. // because each time the rcWork is different, and there is no way to do it all
  2733. SystemParametersInfo(SPI_SETWORKAREA, TRUE, &rcWork, v_hwndDesktop ? SPIF_SENDWININICHANGE : 0);
  2734. RedrawDesktop(&rcWork);
  2735. }
  2736. return TRUE;
  2737. }
  2738. void CTray::_RecomputeAllWorkareas()
  2739. {
  2740. EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)this);
  2741. }
  2742. void CTray::_ScreenSizeChange(HWND hwnd)
  2743. {
  2744. // Set our new HMONITOR in case there is some change
  2745. {
  2746. MONITORINFO mi = {0};
  2747. mi.cbSize = sizeof(mi);
  2748. // Is our old HMONITOR still valid?
  2749. // NOTE: This test is used to tell whether somethings happened to the
  2750. // HMONITOR's or just the screen size changed
  2751. if (!GetMonitorInfo(_hmonStuck, &mi))
  2752. {
  2753. // No, this means the HMONITORS changed, our monitor might have gone away
  2754. _SetStuckMonitor();
  2755. _fIsLogoff = FALSE;
  2756. _RecomputeAllWorkareas();
  2757. }
  2758. }
  2759. // screen size changed, so we need to adjust globals
  2760. g_cxPrimaryDisplay = GetSystemMetrics(SM_CXSCREEN);
  2761. g_cyPrimaryDisplay = GetSystemMetrics(SM_CYSCREEN);
  2762. _ResizeStuckRects(_arStuckRects);
  2763. if (hwnd)
  2764. {
  2765. //
  2766. // set a bogus windowpos and actually repaint with the right
  2767. // shape/size in handling the WINDOWPOSCHANGING message
  2768. //
  2769. SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOACTIVATE);
  2770. }
  2771. SizeWindows();
  2772. RECT rc = _arStuckRects[_uStuckPlace];
  2773. _HandleSizing(0, &rc, _uStuckPlace);
  2774. // In the multi-monitor case, we need to turn on clipping for the dynamic
  2775. // monitor changes i.e. when the user add monitors or remove them from the
  2776. // control panel.
  2777. _ClipWindow(TRUE);
  2778. }
  2779. BOOL CTray::_UpdateAlwaysOnTop(BOOL fAlwaysOnTop)
  2780. {
  2781. BOOL fChanged = ((_fAlwaysOnTop == 0) != (fAlwaysOnTop == 0));
  2782. //
  2783. // The user clicked on the AlwaysOnTop menu item, we should now toggle
  2784. // the state and update the window accordingly...
  2785. //
  2786. _fAlwaysOnTop = fAlwaysOnTop;
  2787. _ResetZorder();
  2788. // Make sure the screen limits are update to the new state.
  2789. _StuckTrayChange();
  2790. return fChanged;
  2791. }
  2792. void CTray::_SaveTrayAndDesktop(void)
  2793. {
  2794. _SaveTray();
  2795. if (v_hwndDesktop)
  2796. SendMessage(v_hwndDesktop, DTM_SAVESTATE, 0, 0);
  2797. if (_hwndNotify)
  2798. SendMessage(_hwndNotify, TNM_SAVESTATE, 0, 0);
  2799. }
  2800. void CTray::_SlideStep(HWND hwnd, const RECT *prcMonitor, const RECT *prcOld, const RECT *prcNew)
  2801. {
  2802. SIZE sizeOld = {prcOld->right - prcOld->left, prcOld->bottom - prcOld->top};
  2803. SIZE sizeNew = {prcNew->right - prcNew->left, prcNew->bottom - prcNew->top};
  2804. BOOL fClipFirst = FALSE;
  2805. UINT flags;
  2806. DAD_ShowDragImage(FALSE); // Make sure this is off - client function must turn back on!!!
  2807. if (prcMonitor)
  2808. {
  2809. RECT rcClip, rcClipSafe, rcClipTest;
  2810. _CalcClipCoords(&rcClip, prcMonitor, prcNew);
  2811. rcClipTest = rcClip;
  2812. OffsetRect(&rcClipTest, prcOld->left, prcOld->top);
  2813. IntersectRect(&rcClipSafe, &rcClipTest, prcMonitor);
  2814. fClipFirst = EqualRect(&rcClipTest, &rcClipSafe);
  2815. if (fClipFirst)
  2816. _ClipInternal(&rcClip);
  2817. }
  2818. flags = SWP_NOZORDER|SWP_NOACTIVATE;
  2819. if ((sizeOld.cx == sizeNew.cx) && (sizeOld.cy == sizeNew.cy))
  2820. flags |= SWP_NOSIZE;
  2821. SetWindowPos(hwnd, NULL,
  2822. prcNew->left, prcNew->top, sizeNew.cx, sizeNew.cy, flags);
  2823. if (prcMonitor && !fClipFirst)
  2824. {
  2825. RECT rcClip;
  2826. _CalcClipCoords(&rcClip, prcMonitor, prcNew);
  2827. _ClipInternal(&rcClip);
  2828. }
  2829. }
  2830. void CTray::_SlideWindow(HWND hwnd, RECT *prc, BOOL fShow)
  2831. {
  2832. RECT rcLast;
  2833. RECT rcMonitor;
  2834. const RECT *prcMonitor;
  2835. DWORD dt;
  2836. BOOL fAnimate;
  2837. if (!IsWindowVisible(hwnd))
  2838. {
  2839. DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.sw window is hidden, just moving"));
  2840. MoveWindow(_hwnd, prc->left, prc->top, RECTWIDTH(*prc), RECTHEIGHT(*prc), FALSE);
  2841. return;
  2842. }
  2843. DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.sw -----------------BEGIN-----------------"));
  2844. if (GetSystemMetrics(SM_CMONITORS) > 1)
  2845. {
  2846. _GetStuckDisplayRect(_uStuckPlace, &rcMonitor);
  2847. prcMonitor = &rcMonitor;
  2848. }
  2849. else
  2850. prcMonitor = NULL;
  2851. GetWindowRect(hwnd, &rcLast);
  2852. dt = fShow? _dtSlideShow : _dtSlideHide;
  2853. // See if we can use animation effects.
  2854. SystemParametersInfo(SPI_GETMENUANIMATION, 0, &fAnimate, 0);
  2855. if (g_fDragFullWindows && fAnimate && (dt > 0))
  2856. {
  2857. RECT rcOld, rcNew, rcMove;
  2858. int dx, dy, priority;
  2859. DWORD t, t2, t0;
  2860. HANDLE me;
  2861. rcOld = rcLast;
  2862. rcNew = *prc;
  2863. dx = ((rcNew.left + rcNew.right) - (rcOld.left + rcOld.right)) / 2;
  2864. dy = ((rcNew.top + rcNew.bottom) - (rcOld.top + rcOld.bottom)) / 2;
  2865. ASSERT(dx == 0 || dy == 0);
  2866. me = GetCurrentThread();
  2867. priority = GetThreadPriority(me);
  2868. SetThreadPriority(me, THREAD_PRIORITY_HIGHEST);
  2869. t2 = t0 = GetTickCount();
  2870. rcMove = rcOld;
  2871. while ((t = GetTickCount()) - t0 < dt)
  2872. {
  2873. int dtdiff;
  2874. if (t != t2)
  2875. {
  2876. rcMove.right -= rcMove.left;
  2877. rcMove.left = rcOld.left + (dx) * (t - t0) / dt;
  2878. rcMove.right += rcMove.left;
  2879. rcMove.bottom -= rcMove.top;
  2880. rcMove.top = rcOld.top + (dy) * (t - t0) / dt;
  2881. rcMove.bottom += rcMove.top;
  2882. _SlideStep(hwnd, prcMonitor, &rcLast, &rcMove);
  2883. if (fShow)
  2884. UpdateWindow(hwnd);
  2885. rcLast = rcMove;
  2886. t2 = t;
  2887. }
  2888. // don't draw frames faster than user can see, e.g. 20ms
  2889. #define ONEFRAME 20
  2890. dtdiff = GetTickCount();
  2891. if ((dtdiff - t) < ONEFRAME)
  2892. Sleep(ONEFRAME - (dtdiff - t));
  2893. // try to give desktop a chance to update
  2894. // only do it on hide because desktop doesn't need to paint on taskbar show
  2895. if (!fShow)
  2896. {
  2897. DWORD_PTR lres;
  2898. SendMessageTimeout(v_hwndDesktop, DTM_UPDATENOW, 0, 0, SMTO_ABORTIFHUNG, 50, &lres);
  2899. }
  2900. }
  2901. SetThreadPriority(me, priority);
  2902. }
  2903. _SlideStep(hwnd, prcMonitor, &rcLast, prc);
  2904. if (fShow)
  2905. UpdateWindow(hwnd);
  2906. DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.sw ------------------END------------------"));
  2907. }
  2908. void CTray::_UnhideNow()
  2909. {
  2910. if (_uModalMode == MM_SHUTDOWN)
  2911. return;
  2912. _fSelfSizing = TRUE;
  2913. DAD_ShowDragImage(FALSE); // unlock the drag sink if we are dragging.
  2914. _SlideWindow(_hwnd, &_arStuckRects[_uStuckPlace], _dtSlideShow);
  2915. DAD_ShowDragImage(TRUE); // restore the lock state.
  2916. _fSelfSizing = FALSE;
  2917. SendMessage(_hwndNotify, TNM_TRAYHIDE, 0, FALSE);
  2918. }
  2919. void CTray::_ComputeHiddenRect(LPRECT prc, UINT uStuck)
  2920. {
  2921. int dwh;
  2922. HMONITOR hMon;
  2923. RECT rcMon;
  2924. hMon = MonitorFromRect(prc, MONITOR_DEFAULTTONULL);
  2925. if (!hMon)
  2926. return;
  2927. GetMonitorRect(hMon, &rcMon);
  2928. if (STUCK_HORIZONTAL(uStuck))
  2929. dwh = prc->bottom - prc->top;
  2930. else
  2931. dwh = prc->right - prc->left;
  2932. switch (uStuck)
  2933. {
  2934. case STICK_LEFT:
  2935. prc->right = rcMon.left + (g_cxFrame / 2);
  2936. prc->left = prc->right - dwh;
  2937. break;
  2938. case STICK_RIGHT:
  2939. prc->left = rcMon.right - (g_cxFrame / 2);
  2940. prc->right = prc->left + dwh;
  2941. break;
  2942. case STICK_TOP:
  2943. prc->bottom = rcMon.top + (g_cyFrame / 2);
  2944. prc->top = prc->bottom - dwh;
  2945. break;
  2946. case STICK_BOTTOM:
  2947. prc->top = rcMon.bottom - (g_cyFrame / 2);
  2948. prc->bottom = prc->top + dwh;
  2949. break;
  2950. }
  2951. }
  2952. UINT CTray::_GetDockedRect(LPRECT prc, BOOL fMoving)
  2953. {
  2954. UINT uPos;
  2955. if (fMoving && (_uMoveStuckPlace != (UINT)-1))
  2956. uPos = _uMoveStuckPlace;
  2957. else
  2958. uPos = _uStuckPlace;
  2959. *prc = _arStuckRects[uPos];
  2960. if ((_uAutoHide & (AH_ON | AH_HIDING)) == (AH_ON | AH_HIDING))
  2961. {
  2962. _ComputeHiddenRect(prc, uPos);
  2963. }
  2964. return uPos;
  2965. }
  2966. void CTray::_CalcClipCoords(RECT *prcClip, const RECT *prcMonitor, const RECT *prcNew)
  2967. {
  2968. RECT rcMonitor;
  2969. RECT rcWindow;
  2970. if (!prcMonitor)
  2971. {
  2972. _GetStuckDisplayRect(_uStuckPlace, &rcMonitor);
  2973. prcMonitor = &rcMonitor;
  2974. }
  2975. if (!prcNew)
  2976. {
  2977. GetWindowRect(_hwnd, &rcWindow);
  2978. prcNew = &rcWindow;
  2979. }
  2980. IntersectRect(prcClip, prcMonitor, prcNew);
  2981. OffsetRect(prcClip, -prcNew->left, -prcNew->top);
  2982. }
  2983. void CTray::_ClipInternal(const RECT *prcClip)
  2984. {
  2985. HRGN hrgnClip;
  2986. // don't worry about clipping if there's only one monitor
  2987. if (GetSystemMetrics(SM_CMONITORS) <= 1)
  2988. prcClip = NULL;
  2989. if (prcClip)
  2990. {
  2991. _fMonitorClipped = TRUE;
  2992. hrgnClip = CreateRectRgnIndirect(prcClip);
  2993. }
  2994. else
  2995. {
  2996. // SetWindowRgn is expensive, skip ones that are NOPs
  2997. if (!_fMonitorClipped)
  2998. return;
  2999. _fMonitorClipped = FALSE;
  3000. hrgnClip = NULL;
  3001. }
  3002. SetWindowRgn(_hwnd, hrgnClip, TRUE);
  3003. }
  3004. void CTray::_ClipWindow(BOOL fClipState)
  3005. {
  3006. RECT rcClip;
  3007. RECT *prcClip;
  3008. if (_fSelfSizing || _fSysSizing)
  3009. {
  3010. TraceMsg(TF_WARNING, "_ClipWindow: _fSelfSizing %x, _fSysSizing %x", _fSelfSizing, _fSysSizing);
  3011. return;
  3012. }
  3013. if ((GetSystemMetrics(SM_CMONITORS) <= 1) || _hTheme)
  3014. fClipState = FALSE;
  3015. if (fClipState)
  3016. {
  3017. prcClip = &rcClip;
  3018. _CalcClipCoords(prcClip, NULL, NULL);
  3019. }
  3020. else
  3021. prcClip = NULL;
  3022. _ClipInternal(prcClip);
  3023. }
  3024. void CTray::_Hide()
  3025. {
  3026. RECT rcNew;
  3027. // if we're in shutdown or if we're on boot up
  3028. // don't hide
  3029. if (_uModalMode == MM_SHUTDOWN)
  3030. {
  3031. TraceMsg(TF_TRAY, "e.th: suppress hide (shutdown || Notify)");
  3032. return;
  3033. }
  3034. KillTimer(_hwnd, IDT_AUTOHIDE);
  3035. _fSelfSizing = TRUE;
  3036. //
  3037. // update the flags here to prevent race conditions
  3038. //
  3039. _uAutoHide = AH_ON | AH_HIDING;
  3040. _GetDockedRect(&rcNew, FALSE);
  3041. DAD_ShowDragImage(FALSE); // unlock the drag sink if we are dragging.
  3042. _SlideWindow(_hwnd, &rcNew, _dtSlideHide);
  3043. DAD_ShowDragImage(FALSE); // Another thread could have locked while we were gone
  3044. DAD_ShowDragImage(TRUE); // restore the lock state.
  3045. SendMessage(_hwndNotify, TNM_TRAYHIDE, 0, TRUE);
  3046. _fSelfSizing = FALSE;
  3047. }
  3048. void CTray::_AutoHideCollision()
  3049. {
  3050. DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_ahc COLLISION! (posting UI request)"));
  3051. PostMessage(_hwnd, TM_WARNNOAUTOHIDE, ((_uAutoHide & AH_ON) != 0),
  3052. 0L);
  3053. }
  3054. LONG CTray::_SetAutoHideState(BOOL fAutoHide)
  3055. {
  3056. //
  3057. // make sure we have something to do
  3058. //
  3059. if ((fAutoHide != 0) == ((_uAutoHide & AH_ON) != 0))
  3060. {
  3061. DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.sahs nothing to do"));
  3062. return MAKELONG(FALSE, TRUE);
  3063. }
  3064. //
  3065. // make sure we can do it
  3066. //
  3067. if (!_AppBarSetAutoHideBar2(_hwnd, fAutoHide, _uStuckPlace))
  3068. {
  3069. _AutoHideCollision();
  3070. return MAKELONG(FALSE, FALSE);
  3071. }
  3072. //
  3073. // do it
  3074. //
  3075. if (fAutoHide)
  3076. {
  3077. _uAutoHide = AH_ON;
  3078. _RefreshSettings();
  3079. _Hide();
  3080. #ifdef DEBUG
  3081. // _Hide updates the flags for us (sanity)
  3082. if (!(_uAutoHide & AH_ON))
  3083. {
  3084. TraceMsg(DM_WARNING, "e.sahs: !AH_ON"); // ok to fail on boot/shutdown
  3085. }
  3086. #endif
  3087. }
  3088. else
  3089. {
  3090. _uAutoHide = 0;
  3091. KillTimer(_hwnd, IDT_AUTOHIDE);
  3092. _UnhideNow();
  3093. _RefreshSettings();
  3094. }
  3095. //
  3096. // brag about it
  3097. //
  3098. _StuckTrayChange();
  3099. return MAKELONG(TRUE, TRUE);
  3100. }
  3101. void CTray::_HandleEnterMenuLoop()
  3102. {
  3103. // kill the timer when we're in the menu loop so that we don't
  3104. // pop done while browsing the menus.
  3105. if (_uAutoHide & AH_ON)
  3106. {
  3107. KillTimer(_hwnd, IDT_AUTOHIDE);
  3108. }
  3109. }
  3110. void CTray::_SetAutoHideTimer()
  3111. {
  3112. if (_uAutoHide & AH_ON)
  3113. {
  3114. SetTimer(_hwnd, IDT_AUTOHIDE, 500, NULL);
  3115. }
  3116. }
  3117. void CTray::_HandleExitMenuLoop()
  3118. {
  3119. // when we leave the menu stuff, start checking again.
  3120. _SetAutoHideTimer();
  3121. }
  3122. void CTray::Unhide()
  3123. {
  3124. // handle autohide
  3125. if ((_uAutoHide & AH_ON) &&
  3126. (_uAutoHide & AH_HIDING))
  3127. {
  3128. _UnhideNow();
  3129. _uAutoHide &= ~AH_HIDING;
  3130. _SetAutoHideTimer();
  3131. if (_fShouldResize)
  3132. {
  3133. ASSERT(0);
  3134. ASSERT(!(_uAutoHide & AH_HIDING));
  3135. SizeWindows();
  3136. _fShouldResize = FALSE;
  3137. }
  3138. }
  3139. }
  3140. void CTray::_SetUnhideTimer(LONG x, LONG y)
  3141. {
  3142. // handle autohide
  3143. if ((_uAutoHide & AH_ON) &&
  3144. (_uAutoHide & AH_HIDING))
  3145. {
  3146. LONG dx = x-_ptLastHittest.x;
  3147. LONG dy = y-_ptLastHittest.y;
  3148. LONG rr = dx*dx + dy*dy;
  3149. LONG dd = GetSystemMetrics(SM_CXDOUBLECLK) * GetSystemMetrics(SM_CYDOUBLECLK);
  3150. if (rr > dd)
  3151. {
  3152. SetTimer(_hwnd, IDT_AUTOUNHIDE, 50, NULL);
  3153. _ptLastHittest.x = x;
  3154. _ptLastHittest.y = y;
  3155. }
  3156. }
  3157. }
  3158. void CTray::_StartButtonReset()
  3159. {
  3160. // Get an idea about how big we need everyhting to be.
  3161. TCHAR szStart[50];
  3162. LoadString(hinstCabinet, _hTheme ? IDS_START : IDS_STARTCLASSIC, szStart, ARRAYSIZE(szStart));
  3163. SetWindowText(_hwndStart, szStart);
  3164. if (_hFontStart)
  3165. DeleteObject(_hFontStart);
  3166. _hFontStart = _CreateStartFont(_hwndStart);
  3167. int idbStart = IDB_START16;
  3168. HDC hdc = GetDC(NULL);
  3169. if (hdc)
  3170. {
  3171. int bpp = GetDeviceCaps(hdc, BITSPIXEL) * GetDeviceCaps(hdc, PLANES);
  3172. if (bpp > 8)
  3173. {
  3174. idbStart = _hTheme ? IDB_START : IDB_STARTCLASSIC;
  3175. }
  3176. ReleaseDC(NULL, hdc);
  3177. }
  3178. HBITMAP hbmFlag = (HBITMAP)LoadImage(hinstCabinet, MAKEINTRESOURCE(idbStart), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
  3179. if (hbmFlag)
  3180. {
  3181. BITMAP bm;
  3182. if (GetObject(hbmFlag, sizeof(BITMAP), &bm))
  3183. {
  3184. BUTTON_IMAGELIST biml = {0};
  3185. if (_himlStartFlag)
  3186. ImageList_Destroy(_himlStartFlag);
  3187. DWORD dwFlags = ILC_COLOR32;
  3188. HBITMAP hbmFlagMask = NULL;
  3189. if (idbStart == IDB_START16)
  3190. {
  3191. dwFlags = ILC_COLOR8 | ILC_MASK;
  3192. hbmFlagMask = (HBITMAP)LoadImage(hinstCabinet, MAKEINTRESOURCE(IDB_START16MASK), IMAGE_BITMAP, 0, 0, LR_MONOCHROME);
  3193. }
  3194. if (IS_WINDOW_RTL_MIRRORED(_hwndStart))
  3195. {
  3196. dwFlags |= ILC_MIRROR;
  3197. }
  3198. biml.himl = _himlStartFlag = ImageList_Create(bm.bmWidth, bm.bmHeight, dwFlags, 1, 1);
  3199. ImageList_Add(_himlStartFlag, hbmFlag, hbmFlagMask);
  3200. if (hbmFlagMask)
  3201. {
  3202. DeleteObject(hbmFlagMask);
  3203. }
  3204. biml.uAlign = BUTTON_IMAGELIST_ALIGN_LEFT;
  3205. Button_SetImageList(_hwndStart, &biml);
  3206. }
  3207. DeleteObject(hbmFlag);
  3208. }
  3209. if (_hFontStart)
  3210. {
  3211. SendMessage(_hwndStart, WM_SETFONT, (WPARAM)_hFontStart, TRUE);
  3212. _sizeStart.cx = 0;
  3213. }
  3214. _AlignStartButton();
  3215. }
  3216. void CTray::_OnNewSystemSizes()
  3217. {
  3218. TraceMsg(TF_TRAY, "Handling win ini change.");
  3219. _StartButtonReset();
  3220. VerifySize(TRUE);
  3221. }
  3222. //*** CheckWindowPositions -- flag which windows actually changed
  3223. // ENTRY/EXIT
  3224. // _pPositions->hdsaWP[*]->fRestore modified
  3225. // NOTES
  3226. // in order to correctly implement 'Undo Minimize-all(Cascade/Tile)',
  3227. // we need to tell which windows were changed by the 'Do' operation.
  3228. // (nt5:183421: we used to restore *every* top-level window).
  3229. int WINAPI CTray::CheckWndPosEnumProc(void *pItem, void *pData)
  3230. {
  3231. HWNDANDPLACEMENT *pI2 = (HWNDANDPLACEMENT *)pItem;
  3232. WINDOWPLACEMENT wp;
  3233. wp.length = sizeof(wp);
  3234. pI2->fRestore = TRUE;
  3235. if (GetWindowPlacement(pI2->hwnd, &wp)) {
  3236. if (memcmp(&pI2->wp, &wp, sizeof(wp)) == 0)
  3237. pI2->fRestore = FALSE;
  3238. }
  3239. TraceMsg(TF_TRAY, "cwp: (hwnd=0x%x) fRestore=%d", pI2->hwnd, pI2->fRestore);
  3240. return 1; // 1:continue enum
  3241. }
  3242. void CTray::CheckWindowPositions()
  3243. {
  3244. ENTERCRITICAL; // i think this is needed...
  3245. if (_pPositions) {
  3246. if (_pPositions->hdsaWP) {
  3247. DSA_EnumCallback(_pPositions->hdsaWP, CheckWndPosEnumProc, NULL);
  3248. }
  3249. }
  3250. LEAVECRITICAL;
  3251. return;
  3252. }
  3253. BOOL BandSite_PermitAutoHide(IUnknown* punk)
  3254. {
  3255. OLECMD cmd = { DBID_PERMITAUTOHIDE, 0 };
  3256. if (SUCCEEDED(IUnknown_QueryStatus(punk, &IID_IDockingWindow, 1, &cmd, NULL)))
  3257. {
  3258. return !(cmd.cmdf & OLECMDF_SUPPORTED) || (cmd.cmdf & OLECMDF_ENABLED);
  3259. }
  3260. return TRUE;
  3261. }
  3262. void CTray::_HandleTimer(WPARAM wTimerID)
  3263. {
  3264. switch (wTimerID)
  3265. {
  3266. case IDT_CHECKDISKSPACE:
  3267. CheckDiskSpace();
  3268. break;
  3269. case IDT_DESKTOPCLEANUP:
  3270. _CheckDesktopCleanup();
  3271. break;
  3272. case IDT_CHANGENOTIFY:
  3273. // did somebody send another notify since we last handled one?
  3274. if (_fUseChangeNotifyTimer)
  3275. {
  3276. // yep.
  3277. // all we do is check the staging area.
  3278. CheckStagingArea();
  3279. // kill it off the next time.
  3280. _fUseChangeNotifyTimer = FALSE;
  3281. }
  3282. else
  3283. {
  3284. // nope.
  3285. KillTimer(_hwnd, IDT_CHANGENOTIFY);
  3286. _fChangeNotifyTimerRunning = FALSE;
  3287. }
  3288. break;
  3289. case IDT_HANDLEDELAYBOOTSTUFF:
  3290. KillTimer(_hwnd, IDT_HANDLEDELAYBOOTSTUFF);
  3291. PostMessage(_hwnd, TM_HANDLEDELAYBOOTSTUFF, 0, 0);
  3292. break;
  3293. case IDT_STARTMENU:
  3294. SetForegroundWindow(_hwnd);
  3295. KillTimer(_hwnd, wTimerID);
  3296. DAD_ShowDragImage(FALSE); // unlock the drag sink if we are dragging.
  3297. SendMessage(_hwndStart, BM_SETSTATE, TRUE, 0);
  3298. UpdateWindow(_hwndStart);
  3299. DAD_ShowDragImage(TRUE); // restore the lock state.
  3300. break;
  3301. case IDT_SAVESETTINGS:
  3302. KillTimer(_hwnd, IDT_SAVESETTINGS);
  3303. _SaveTray();
  3304. break;
  3305. case IDT_ENABLEUNDO:
  3306. KillTimer(_hwnd, IDT_ENABLEUNDO);
  3307. CheckWindowPositions();
  3308. _fUndoEnabled = TRUE;
  3309. break;
  3310. case IDT_AUTOHIDE:
  3311. if (!_fSysSizing && (_uAutoHide & AH_ON))
  3312. {
  3313. POINT pt;
  3314. RECT rc;
  3315. // Don't hide if we're already hiding, a balloon tip is showing, or
  3316. // (on NT5) if any apps are flashing.
  3317. //
  3318. if (!(_uAutoHide & AH_HIDING) && BandSite_PermitAutoHide(_ptbs) && !_fBalloonUp)
  3319. {
  3320. // Get the cursor position.
  3321. GetCursorPos(&pt);
  3322. // Get the tray rect and inflate it a bit.
  3323. rc = _arStuckRects[_uStuckPlace];
  3324. InflateRect(&rc, g_cxEdge * 4, g_cyEdge*4);
  3325. // Don't hide if cursor is within inflated tray rect.
  3326. if (!PtInRect(&rc, pt))
  3327. {
  3328. // Don't hide if the tray is active
  3329. if (!_IsActive() && _uStartButtonState != SBSM_SPACTIVE)
  3330. {
  3331. // Don't hide if the view has a system menu up.
  3332. if (!SendMessage(_hwndTasks, TBC_SYSMENUCOUNT, 0, 0L))
  3333. {
  3334. // Phew! We made it. Hide the tray.
  3335. _Hide();
  3336. }
  3337. }
  3338. }
  3339. }
  3340. }
  3341. break;
  3342. case IDT_AUTOUNHIDE:
  3343. if (!_fSysSizing && (_uAutoHide & AH_ON))
  3344. {
  3345. POINT pt;
  3346. RECT rc;
  3347. KillTimer(_hwnd, wTimerID);
  3348. _ptLastHittest.x = -0x0fff;
  3349. _ptLastHittest.y = -0x0fff;
  3350. GetWindowRect(_hwnd, &rc);
  3351. if (_uAutoHide & AH_HIDING)
  3352. {
  3353. GetCursorPos(&pt);
  3354. if (PtInRect(&rc, pt))
  3355. Unhide();
  3356. }
  3357. }
  3358. break;
  3359. case IDT_STARTBUTTONBALLOON:
  3360. _DestroyStartButtonBalloon();
  3361. break;
  3362. case IDT_COFREEUNUSED:
  3363. CoFreeUnusedLibraries();
  3364. KillTimer(_hwnd, IDT_COFREEUNUSED);
  3365. break;
  3366. }
  3367. }
  3368. void CTray::_CheckStagingAreaOnTimer()
  3369. {
  3370. if (_fChangeNotifyTimerRunning)
  3371. {
  3372. // we're already running the timer, so force a check the next time it comes up
  3373. _fUseChangeNotifyTimer = TRUE;
  3374. }
  3375. else
  3376. {
  3377. _fChangeNotifyTimerRunning = TRUE;
  3378. // check once immediately
  3379. CheckStagingArea();
  3380. // check again in half a minute, but only if notifies have been happening in the meantime.
  3381. SetTimer(_hwnd, IDT_CHANGENOTIFY, 30 * 1000, NULL);
  3382. }
  3383. }
  3384. void CTray::_HandleChangeNotify(WPARAM wParam, LPARAM lParam)
  3385. {
  3386. LPITEMIDLIST *ppidl;
  3387. LONG lEvent;
  3388. LPSHChangeNotificationLock pshcnl = SHChangeNotification_Lock((HANDLE)wParam, (DWORD)lParam, &ppidl, &lEvent);
  3389. if (pshcnl)
  3390. {
  3391. if (lEvent & SHCNE_STAGINGAREANOTIFICATIONS)
  3392. {
  3393. // something has changed within the staging area.
  3394. _CheckStagingAreaOnTimer();
  3395. }
  3396. SHChangeNotification_Unlock(pshcnl);
  3397. }
  3398. }
  3399. BOOL _ExecItemByPidls(HWND hwnd, LPITEMIDLIST pidlFolder, LPITEMIDLIST pidlItem)
  3400. {
  3401. BOOL fRes = FALSE;
  3402. if (pidlFolder && pidlItem)
  3403. {
  3404. IShellFolder *psf = BindToFolder(pidlFolder);
  3405. if (psf)
  3406. {
  3407. fRes = SUCCEEDED(SHInvokeDefaultCommand(hwnd, psf, pidlItem));
  3408. }
  3409. else
  3410. {
  3411. TCHAR szPath[MAX_PATH];
  3412. SHGetPathFromIDList(pidlFolder, szPath);
  3413. ShellMessageBox(hinstCabinet, hwnd, MAKEINTRESOURCE(IDS_CANTFINDSPECIALDIR),
  3414. NULL, MB_ICONEXCLAMATION, szPath);
  3415. }
  3416. }
  3417. return fRes;
  3418. }
  3419. void _DestroySavedWindowPositions(LPWINDOWPOSITIONS pPositions);
  3420. LRESULT CTray::_HandleDestroy()
  3421. {
  3422. MINIMIZEDMETRICS mm;
  3423. TraceMsg(DM_SHUTDOWN, "_HD: enter");
  3424. mm.cbSize = sizeof(mm);
  3425. SystemParametersInfo(SPI_GETMINIMIZEDMETRICS, sizeof(mm), &mm, FALSE);
  3426. mm.iArrange &= ~ARW_HIDE;
  3427. SystemParametersInfo(SPI_SETMINIMIZEDMETRICS, sizeof(mm), &mm, FALSE);
  3428. _RevokeDropTargets();
  3429. _DestroyStartMenu();
  3430. Mixer_Shutdown();
  3431. // Tell the start menu to free all its cached darwin links
  3432. SHRegisterDarwinLink(NULL, NULL, TRUE);
  3433. _DestroySavedWindowPositions(_pPositions);
  3434. _pPositions = NULL;
  3435. if (_hTheme)
  3436. {
  3437. CloseThemeData(_hTheme);
  3438. _hTheme = NULL;
  3439. }
  3440. _UnregisterGlobalHotkeys();
  3441. if (_uNotify)
  3442. {
  3443. SHChangeNotifyDeregister(_uNotify);
  3444. _uNotify = 0;
  3445. }
  3446. ATOMICRELEASE(_ptbs);
  3447. ATOMICRELEASE(_pdbTasks);
  3448. _hwndTasks = NULL;
  3449. if (_hwndTrayTips)
  3450. {
  3451. DestroyWindow(_hwndTrayTips);
  3452. _hwndTrayTips = NULL;
  3453. }
  3454. _DestroyStartButtonBalloon();
  3455. // REVIEW
  3456. PostQuitMessage(0);
  3457. if (_hbmpStartBkg)
  3458. {
  3459. DeleteBitmap(_hbmpStartBkg);
  3460. }
  3461. if (_hFontStart)
  3462. {
  3463. DeleteObject(_hFontStart);
  3464. }
  3465. if (_himlStartFlag)
  3466. {
  3467. ImageList_Destroy(_himlStartFlag);
  3468. }
  3469. // clean up service objects
  3470. _ssomgr.Destroy();
  3471. if (_hShellReadyEvent)
  3472. {
  3473. ResetEvent(_hShellReadyEvent);
  3474. CloseHandle(_hShellReadyEvent);
  3475. _hShellReadyEvent = NULL;
  3476. }
  3477. if (_fHandledDelayBootStuff)
  3478. {
  3479. TBOOL(WinStationUnRegisterConsoleNotification(SERVERNAME_CURRENT, v_hwndTray));
  3480. }
  3481. DeleteCriticalSection(&_csHotkey);
  3482. // The order in which we shut down the HTTP key monitoring is important.
  3483. //
  3484. // We must close the key before closing the event handle because
  3485. // closing the key causes the event to be signalled and we don't
  3486. // want ADVAPI32 to try to signal an event after we closed its handle...
  3487. //
  3488. // To avoid a spurious trigger when the event fires, we unregister
  3489. // the wait before closing the key.
  3490. //
  3491. if (_hHTTPWait)
  3492. {
  3493. UnregisterWait(_hHTTPWait);
  3494. _hHTTPWait = NULL;
  3495. }
  3496. if (_hkHTTP)
  3497. {
  3498. RegCloseKey(_hkHTTP);
  3499. _hkHTTP = NULL;
  3500. }
  3501. if (_hHTTPEvent)
  3502. {
  3503. CloseHandle(_hHTTPEvent);
  3504. _hHTTPEvent = NULL;
  3505. }
  3506. // End of order-sensitive operations ----------------------------------
  3507. v_hwndTray = NULL;
  3508. _hwndStart = NULL;
  3509. TraceMsg(DM_SHUTDOWN, "_HD: leave");
  3510. return 0;
  3511. }
  3512. void CTray::_SetFocus(HWND hwnd)
  3513. {
  3514. IUnknown_UIActivateIO(_ptbs, FALSE, NULL);
  3515. SetFocus(hwnd);
  3516. }
  3517. #define TRIEDTOOMANYTIMES 100
  3518. void CTray::_ActAsSwitcher()
  3519. {
  3520. if (_uModalMode)
  3521. {
  3522. if (_uModalMode != MM_SHUTDOWN)
  3523. {
  3524. SwitchToThisWindow(GetLastActivePopup(_hwnd), TRUE);
  3525. }
  3526. MessageBeep(0);
  3527. }
  3528. else
  3529. {
  3530. HWND hwndForeground;
  3531. HWND hwndActive;
  3532. static int s_iRecurse = 0;
  3533. s_iRecurse++;
  3534. ASSERT(s_iRecurse < TRIEDTOOMANYTIMES);
  3535. TraceMsg(TF_TRAY, "s_iRecurse = %d", s_iRecurse);
  3536. hwndForeground = GetForegroundWindow();
  3537. hwndActive = GetActiveWindow();
  3538. BOOL fIsTrayActive = (hwndForeground == _hwnd) && (hwndActive == _hwnd);
  3539. if (v_hwndStartPane && hwndForeground == v_hwndStartPane && hwndActive == v_hwndStartPane)
  3540. {
  3541. fIsTrayActive = TRUE;
  3542. }
  3543. // only do the button once we're the foreground dude.
  3544. if (fIsTrayActive)
  3545. {
  3546. // This code path causes the start button to do something because
  3547. // of the keyboard. So reflect that with the focus rect.
  3548. SendMessage(_hwndStart, WM_UPDATEUISTATE, MAKEWPARAM(UIS_CLEAR,
  3549. UISF_HIDEFOCUS), 0);
  3550. if (SendMessage(_hwndStart, BM_GETSTATE, 0, 0) & BST_PUSHED)
  3551. {
  3552. ClosePopupMenus();
  3553. ForceStartButtonUp();
  3554. }
  3555. else
  3556. {
  3557. // This pushes the start button and causes the start menu to popup.
  3558. SendMessage(GetDlgItem(_hwnd, IDC_START), BM_SETSTATE, TRUE, 0);
  3559. }
  3560. s_iRecurse = 0;
  3561. }
  3562. else
  3563. {
  3564. // we don't want to loop endlessly trying to become
  3565. // foreground. With NT's new SetForegroundWindow rules, it would
  3566. // be pointless to try and hopefully we won't need to anyhow.
  3567. // Randomly, I picked a quarter as many times as the debug squirty would indicate
  3568. // as the number of times to try on NT.
  3569. // Hopefully that is enough on most machines.
  3570. if (s_iRecurse > (TRIEDTOOMANYTIMES / 4))
  3571. {
  3572. s_iRecurse = 0;
  3573. return;
  3574. }
  3575. // until then, try to come forward.
  3576. HandleFullScreenApp(NULL);
  3577. if (hwndForeground == v_hwndDesktop)
  3578. {
  3579. _SetFocus(_hwndStart);
  3580. if (GetFocus() != _hwndStart)
  3581. return;
  3582. }
  3583. SwitchToThisWindow(_hwnd, TRUE);
  3584. SetForegroundWindow(_hwnd);
  3585. Sleep(20); // give some time for other async activation messages to get posted
  3586. PostMessage(_hwnd, TM_ACTASTASKSW, 0, 0);
  3587. }
  3588. }
  3589. }
  3590. void CTray::_OnWinIniChange(HWND hwnd, WPARAM wParam, LPARAM lParam)
  3591. {
  3592. Cabinet_InitGlobalMetrics(wParam, (LPTSTR)lParam);
  3593. // Reset the programs menu.
  3594. // REVIEW IANEL - We should only need to listen to the SPI_SETNONCLIENT stuff
  3595. // but deskcpl doesn't send one.
  3596. if (wParam == SPI_SETNONCLIENTMETRICS || (!wParam && (!lParam || (lstrcmpi((LPTSTR)lParam, TEXT("WindowMetrics")) == 0))))
  3597. {
  3598. #ifdef DEBUG
  3599. if (wParam == SPI_SETNONCLIENTMETRICS)
  3600. TraceMsg(TF_TRAY, "c.t_owic: Non-client metrics (probably) changed.");
  3601. else
  3602. TraceMsg(TF_TRAY, "c.t_owic: Window metrics changed.");
  3603. #endif
  3604. _OnNewSystemSizes();
  3605. }
  3606. // Handle old extensions.
  3607. if (!lParam || (lParam && (lstrcmpi((LPTSTR)lParam, TEXT("Extensions")) == 0)))
  3608. {
  3609. TraceMsg(TF_TRAY, "t_owic: Extensions section change.");
  3610. CheckWinIniForAssocs();
  3611. }
  3612. if (lParam && (0 == lstrcmpi((LPCTSTR)lParam, TEXT("TraySettings"))))
  3613. {
  3614. _Command(FCIDM_REFRESH);
  3615. }
  3616. // Tell shell32 to refresh its cache
  3617. SHSettingsChanged(wParam, lParam);
  3618. }
  3619. HWND CTray::_HotkeyInUse(WORD wHK)
  3620. {
  3621. HWND hwnd;
  3622. DWORD_PTR lrHKInUse = 0;
  3623. int nMod;
  3624. WORD wHKNew;
  3625. #ifdef DEBUG
  3626. TCHAR sz[MAX_PATH];
  3627. #endif
  3628. // Map the modifiers back.
  3629. nMod = 0;
  3630. if (HIBYTE(wHK) & MOD_SHIFT)
  3631. nMod |= HOTKEYF_SHIFT;
  3632. if (HIBYTE(wHK) & MOD_CONTROL)
  3633. nMod |= HOTKEYF_CONTROL;
  3634. if (HIBYTE(wHK) & MOD_ALT)
  3635. nMod |= HOTKEYF_ALT;
  3636. wHKNew = (WORD)((nMod*256)+LOBYTE(wHK));
  3637. DebugMsg(DM_IANELHK, TEXT("c.hkl_hiu: Checking for %x"), wHKNew);
  3638. hwnd = GetWindow(GetDesktopWindow(), GW_CHILD);
  3639. while (hwnd)
  3640. {
  3641. SendMessageTimeout(hwnd, WM_GETHOTKEY, 0, 0, SMTO_ABORTIFHUNG| SMTO_BLOCK, 3000, &lrHKInUse);
  3642. if (wHKNew == (WORD)lrHKInUse)
  3643. {
  3644. #ifdef DEBUG
  3645. GetWindowText(hwnd, sz, ARRAYSIZE(sz));
  3646. DebugMsg(DM_IANELHK, TEXT("c.hkl_hiu: %s (%x) is using %x"), sz, hwnd, lrHKInUse);
  3647. #endif
  3648. return hwnd;
  3649. }
  3650. #ifdef DEBUG
  3651. else if (lrHKInUse)
  3652. {
  3653. GetWindowText(hwnd, sz, ARRAYSIZE(sz));
  3654. DebugMsg(DM_IANELHK, TEXT("c.hkl_hiu: %s (%x) is using %x"), sz, hwnd, lrHKInUse);
  3655. }
  3656. #endif
  3657. hwnd = GetWindow(hwnd, GW_HWNDNEXT);
  3658. }
  3659. return NULL;
  3660. }
  3661. void CTray::_HandleHotKey(int nID)
  3662. {
  3663. TraceMsg(TF_TRAY, "c.hkl_hh: Handling hotkey (%d).", nID);
  3664. // Find it in the list.
  3665. ASSERT(IS_VALID_HANDLE(_hdsaHKI, DSA));
  3666. EnterCriticalSection(&_csHotkey);
  3667. HOTKEYITEM *phki = (HOTKEYITEM *)DSA_GetItemPtr(_hdsaHKI, nID);
  3668. if (phki && phki->wGHotkey)
  3669. {
  3670. TraceMsg(TF_TRAY, "c.hkl_hh: Hotkey listed.");
  3671. // Are global hotkeys enabled?
  3672. if (!_fGlobalHotkeyDisable)
  3673. {
  3674. // Yep.
  3675. HWND hwnd = _HotkeyInUse(phki->wGHotkey);
  3676. // Make sure this hotkey isn't already in use by someone.
  3677. if (hwnd)
  3678. {
  3679. TraceMsg(TF_TRAY, "c.hkl_hh: Hotkey is already in use.");
  3680. // Activate it.
  3681. SwitchToThisWindow(GetLastActivePopup(hwnd), TRUE);
  3682. }
  3683. else
  3684. {
  3685. DECLAREWAITCURSOR;
  3686. // Exec the item.
  3687. SetWaitCursor();
  3688. TraceMsg(TF_TRAY, "c.hkl_hh: Hotkey is not in use, execing item.");
  3689. ASSERT(phki->pidlFolder && phki->pidlItem);
  3690. BOOL fRes = _ExecItemByPidls(_hwnd, phki->pidlFolder, phki->pidlItem);
  3691. ResetWaitCursor();
  3692. #ifdef DEBUG
  3693. if (!fRes)
  3694. {
  3695. DebugMsg(DM_ERROR, TEXT("c.hkl_hh: Can't exec command ."));
  3696. }
  3697. #endif
  3698. }
  3699. }
  3700. else
  3701. {
  3702. DebugMsg(DM_ERROR, TEXT("c.hkl_hh: Global hotkeys have been disabled."));
  3703. }
  3704. }
  3705. else
  3706. {
  3707. DebugMsg(DM_ERROR, TEXT("c.hkl_hh: Hotkey not listed."));
  3708. }
  3709. LeaveCriticalSection(&_csHotkey);
  3710. }
  3711. LRESULT CTray::_UnregisterHotkey(HWND hwnd, int i)
  3712. {
  3713. TraceMsg(TF_TRAY, "c.t_uh: Unregistering hotkey (%d).", i);
  3714. if (!UnregisterHotKey(hwnd, i))
  3715. {
  3716. DebugMsg(DM_ERROR, TEXT("c.t_rh: Unable to unregister hotkey %d."), i);
  3717. }
  3718. return TRUE;
  3719. }
  3720. // Add hotkey to the shell's list of global hotkeys.
  3721. LRESULT CTray::_ShortcutRegisterHotkey(HWND hwnd, WORD wHotkey, ATOM atom)
  3722. {
  3723. int i;
  3724. LPITEMIDLIST pidl;
  3725. TCHAR szPath[MAX_PATH];
  3726. ASSERT(atom);
  3727. if (GlobalGetAtomName(atom, szPath, MAX_PATH))
  3728. {
  3729. TraceMsg(TF_TRAY, "c.t_srh: Hotkey %d for %s", wHotkey, szPath);
  3730. pidl = ILCreateFromPath(szPath);
  3731. if (pidl)
  3732. {
  3733. i = _HotkeyAddCached(_MapHotkeyToGlobalHotkey(wHotkey), pidl);
  3734. if (i != -1)
  3735. {
  3736. _RegisterHotkey(_hwnd, i);
  3737. }
  3738. }
  3739. return TRUE;
  3740. }
  3741. else
  3742. {
  3743. return FALSE;
  3744. }
  3745. }
  3746. // Remove hotkey from shell's list.
  3747. LRESULT CTray::_ShortcutUnregisterHotkey(HWND hwnd, WORD wHotkey)
  3748. {
  3749. // DebugMsg(DM_TRACE, "c.t_suh: Hotkey %d", wHotkey);
  3750. int i = _HotkeyRemove(wHotkey);
  3751. if (i == -1)
  3752. i = _HotkeyRemoveCached(_MapHotkeyToGlobalHotkey(wHotkey));
  3753. if (i != -1)
  3754. _UnregisterHotkey(hwnd, i);
  3755. return TRUE;
  3756. }
  3757. LRESULT CTray::_RegisterHotkey(HWND hwnd, int i)
  3758. {
  3759. HOTKEYITEM *phki;
  3760. WORD wGHotkey = 0;
  3761. ASSERT(IS_VALID_HANDLE(_hdsaHKI, DSA));
  3762. TraceMsg(TF_TRAY, "c.t_rh: Registering hotkey (%d).", i);
  3763. EnterCriticalSection(&_csHotkey);
  3764. phki = (HOTKEYITEM *)DSA_GetItemPtr(_hdsaHKI, i);
  3765. ASSERT(phki);
  3766. if (phki)
  3767. {
  3768. wGHotkey = phki->wGHotkey;
  3769. }
  3770. LeaveCriticalSection(&_csHotkey);
  3771. if (wGHotkey)
  3772. {
  3773. // Is the hotkey available?
  3774. if (RegisterHotKey(hwnd, i, HIBYTE(wGHotkey), LOBYTE(wGHotkey)))
  3775. {
  3776. // Yes.
  3777. return TRUE;
  3778. }
  3779. else
  3780. {
  3781. // Delete any cached items that might be using this
  3782. // hotkey.
  3783. int iCached = _HotkeyRemoveCached(wGHotkey);
  3784. ASSERT(iCached != i);
  3785. if (iCached != -1)
  3786. {
  3787. // Free up the hotkey for us.
  3788. _UnregisterHotkey(hwnd, iCached);
  3789. // Yep, nuked the cached item. Try again.
  3790. if (RegisterHotKey(hwnd, i, HIBYTE(wGHotkey), LOBYTE(wGHotkey)))
  3791. {
  3792. return TRUE;
  3793. }
  3794. }
  3795. }
  3796. // Can't set hotkey for this item.
  3797. DebugMsg(DM_ERROR, TEXT("c.t_rh: Unable to register hotkey %d."), i);
  3798. // Null out this item.
  3799. phki->wGHotkey = 0;
  3800. phki->pidlFolder = NULL;
  3801. phki->pidlItem = NULL;
  3802. }
  3803. else
  3804. {
  3805. DebugMsg(DM_ERROR, TEXT("c.t_rh: Hotkey item is invalid."));
  3806. }
  3807. return FALSE;
  3808. }
  3809. #define GetABDHWnd(pabd) ((HWND)ULongToPtr((pabd)->dwWnd))
  3810. void CTray::_AppBarGetTaskBarPos(PTRAYAPPBARDATA ptabd)
  3811. {
  3812. APPBARDATA3264 *pabd;
  3813. pabd = (APPBARDATA3264*)SHLockShared(UlongToPtr(ptabd->hSharedABD), ptabd->dwProcId);
  3814. if (pabd)
  3815. {
  3816. pabd->rc = _arStuckRects[_uStuckPlace];
  3817. pabd->uEdge = _uStuckPlace; // compat: new to ie4
  3818. SHUnlockShared(pabd);
  3819. }
  3820. }
  3821. void CTray::_NukeAppBar(int i)
  3822. {
  3823. LocalFree(DPA_GetPtr(_hdpaAppBars, i));
  3824. DPA_DeletePtr(_hdpaAppBars, i);
  3825. }
  3826. void CTray::_AppBarRemove(PTRAYAPPBARDATA ptabd)
  3827. {
  3828. int i;
  3829. if (!_hdpaAppBars)
  3830. return;
  3831. i = DPA_GetPtrCount(_hdpaAppBars);
  3832. while (i--)
  3833. {
  3834. PAPPBAR pab = (PAPPBAR)DPA_GetPtr(_hdpaAppBars, i);
  3835. if (GetABDHWnd(&ptabd->abd) == pab->hwnd)
  3836. {
  3837. RECT rcNuke = pab->rc;
  3838. _NukeAppBar(i);
  3839. _StuckAppChange(GetABDHWnd(&ptabd->abd), &rcNuke, NULL, FALSE);
  3840. }
  3841. }
  3842. }
  3843. PAPPBAR CTray::_FindAppBar(HWND hwnd)
  3844. {
  3845. if (_hdpaAppBars)
  3846. {
  3847. int i = DPA_GetPtrCount(_hdpaAppBars);
  3848. while (i--)
  3849. {
  3850. PAPPBAR pab = (PAPPBAR)DPA_GetPtr(_hdpaAppBars, i);
  3851. if (hwnd == pab->hwnd)
  3852. return pab;
  3853. }
  3854. }
  3855. return NULL;
  3856. }
  3857. void CTray::_AppBarNotifyAll(HMONITOR hmon, UINT uMsg, HWND hwndExclude, LPARAM lParam)
  3858. {
  3859. if (!_hdpaAppBars)
  3860. return;
  3861. int i = DPA_GetPtrCount(_hdpaAppBars);
  3862. while (i--)
  3863. {
  3864. PAPPBAR pab = (PAPPBAR)DPA_GetPtr(_hdpaAppBars, i);
  3865. // We need to check pab here as an appbar can delete other
  3866. // appbars on the callback.
  3867. if (pab && (hwndExclude != pab->hwnd))
  3868. {
  3869. if (!IsWindow(pab->hwnd))
  3870. {
  3871. _NukeAppBar(i);
  3872. continue;
  3873. }
  3874. //
  3875. // if a monitor was specified only tell appbars on that display
  3876. //
  3877. if (hmon &&
  3878. (hmon != MonitorFromWindow(pab->hwnd, MONITOR_DEFAULTTONULL)))
  3879. {
  3880. continue;
  3881. }
  3882. PostMessage(pab->hwnd, pab->uCallbackMessage, uMsg, lParam);
  3883. }
  3884. }
  3885. }
  3886. BOOL CTray::_AppBarNew(PTRAYAPPBARDATA ptabd)
  3887. {
  3888. PAPPBAR pab;
  3889. if (!_hdpaAppBars)
  3890. {
  3891. _hdpaAppBars = DPA_Create(4);
  3892. if (!_hdpaAppBars)
  3893. return FALSE;
  3894. }
  3895. else if (_FindAppBar(GetABDHWnd(&ptabd->abd)))
  3896. {
  3897. // already have this hwnd
  3898. return FALSE;
  3899. }
  3900. pab = (PAPPBAR)LocalAlloc(LPTR, sizeof(APPBAR));
  3901. if (!pab)
  3902. return FALSE;
  3903. pab->hwnd = GetABDHWnd(&ptabd->abd);
  3904. pab->uCallbackMessage = ptabd->abd.uCallbackMessage;
  3905. pab->uEdge = (UINT)-1;
  3906. if (DPA_AppendPtr(_hdpaAppBars, pab) == -1)
  3907. {
  3908. // insertion failed
  3909. LocalFree(pab);
  3910. return FALSE;
  3911. }
  3912. return TRUE;
  3913. }
  3914. BOOL CTray::_AppBarOutsideOf(PAPPBAR pabReq, PAPPBAR pab)
  3915. {
  3916. if (pabReq->uEdge == pab->uEdge)
  3917. {
  3918. switch (pab->uEdge)
  3919. {
  3920. case ABE_RIGHT:
  3921. return (pab->rc.right >= pabReq->rc.right);
  3922. case ABE_BOTTOM:
  3923. return (pab->rc.bottom >= pabReq->rc.bottom);
  3924. case ABE_TOP:
  3925. return (pab->rc.top <= pabReq->rc.top);
  3926. case ABE_LEFT:
  3927. return (pab->rc.left <= pabReq->rc.left);
  3928. }
  3929. }
  3930. return FALSE;
  3931. }
  3932. void CTray::_AppBarQueryPos(PTRAYAPPBARDATA ptabd)
  3933. {
  3934. int i;
  3935. PAPPBAR pabReq = _FindAppBar(GetABDHWnd(&ptabd->abd));
  3936. if (pabReq)
  3937. {
  3938. APPBARDATA3264 *pabd;
  3939. pabd = (APPBARDATA3264*)SHLockShared(UlongToPtr(ptabd->hSharedABD), ptabd->dwProcId);
  3940. if (pabd)
  3941. {
  3942. HMONITOR hmon;
  3943. pabd->rc = ptabd->abd.rc;
  3944. //
  3945. // default to the primary display for this call because old appbars
  3946. // sometimes pass a huge rect and let us pare it down. if they do
  3947. // something like that they don't support multiple displays anyway
  3948. // so just put them on the primary display...
  3949. //
  3950. hmon = MonitorFromRect(&pabd->rc, MONITOR_DEFAULTTOPRIMARY);
  3951. //
  3952. // always subtract off the tray if it's on the same display
  3953. //
  3954. if (!_uAutoHide && (hmon == _hmonStuck))
  3955. {
  3956. APPBAR ab;
  3957. ab.uEdge = _GetDockedRect(&ab.rc, FALSE);
  3958. _AppBarSubtractRect(&ab, &pabd->rc);
  3959. }
  3960. i = DPA_GetPtrCount(_hdpaAppBars);
  3961. while (i--)
  3962. {
  3963. PAPPBAR pab = (PAPPBAR)DPA_GetPtr(_hdpaAppBars, i);
  3964. //
  3965. // give top and bottom preference
  3966. // ||
  3967. // if we're not changing edges,
  3968. // subtract anything currently on the outside of us
  3969. // ||
  3970. // if we are changing sides,
  3971. // subtract off everything on the new side.
  3972. //
  3973. // of course ignore appbars which are not on the same display...
  3974. //
  3975. if ((((pabReq->hwnd != pab->hwnd) &&
  3976. STUCK_HORIZONTAL(pab->uEdge) &&
  3977. !STUCK_HORIZONTAL(ptabd->abd.uEdge)) ||
  3978. ((pabReq->hwnd != pab->hwnd) &&
  3979. (pabReq->uEdge == ptabd->abd.uEdge) &&
  3980. _AppBarOutsideOf(pabReq, pab)) ||
  3981. ((pabReq->hwnd != pab->hwnd) &&
  3982. (pabReq->uEdge != ptabd->abd.uEdge) &&
  3983. (pab->uEdge == ptabd->abd.uEdge))) &&
  3984. (hmon == MonitorFromRect(&pab->rc, MONITOR_DEFAULTTONULL)))
  3985. {
  3986. _AppBarSubtractRect(pab, &pabd->rc);
  3987. }
  3988. }
  3989. SHUnlockShared(pabd);
  3990. }
  3991. }
  3992. }
  3993. void CTray::_AppBarSetPos(PTRAYAPPBARDATA ptabd)
  3994. {
  3995. PAPPBAR pab = _FindAppBar(GetABDHWnd(&ptabd->abd));
  3996. if (pab)
  3997. {
  3998. RECT rcOld;
  3999. APPBARDATA3264 *pabd;
  4000. BOOL fChanged = FALSE;
  4001. _AppBarQueryPos(ptabd);
  4002. pabd = (APPBARDATA3264*)SHLockShared(UlongToPtr(ptabd->hSharedABD), ptabd->dwProcId);
  4003. if (pabd)
  4004. {
  4005. if (!EqualRect(&pab->rc, &pabd->rc)) {
  4006. rcOld = pab->rc;
  4007. pab->rc = pabd->rc;
  4008. pab->uEdge = ptabd->abd.uEdge;
  4009. fChanged = TRUE;
  4010. }
  4011. SHUnlockShared(pabd);
  4012. }
  4013. if (fChanged)
  4014. _StuckAppChange(GetABDHWnd(&ptabd->abd), &rcOld, &pab->rc, FALSE);
  4015. }
  4016. }
  4017. //
  4018. // FEATURE: need to get rid of this array-based implementation to allow autohide
  4019. // appbars on secondary display (or a/h tray on 2nd with a/h appbar on primary)
  4020. // change it to an _AppBarFindAutoHideBar that keeps flags on the appbardata...
  4021. //
  4022. HWND CTray::_AppBarGetAutoHideBar(UINT uEdge)
  4023. {
  4024. if (uEdge >= ABE_MAX)
  4025. return FALSE;
  4026. else
  4027. {
  4028. HWND hwndAutoHide = _aHwndAutoHide[uEdge];
  4029. if (!IsWindow(hwndAutoHide))
  4030. {
  4031. _aHwndAutoHide[uEdge] = NULL;
  4032. }
  4033. return _aHwndAutoHide[uEdge];
  4034. }
  4035. }
  4036. BOOL CTray::_AppBarSetAutoHideBar2(HWND hwnd, BOOL fAutoHide, UINT uEdge)
  4037. {
  4038. HWND hwndAutoHide = _aHwndAutoHide[uEdge];
  4039. if (!IsWindow(hwndAutoHide))
  4040. {
  4041. _aHwndAutoHide[uEdge] = NULL;
  4042. }
  4043. if (fAutoHide)
  4044. {
  4045. // register
  4046. if (!_aHwndAutoHide[uEdge])
  4047. {
  4048. _aHwndAutoHide[uEdge] = hwnd;
  4049. }
  4050. return _aHwndAutoHide[uEdge] == hwnd;
  4051. }
  4052. else
  4053. {
  4054. // unregister
  4055. if (_aHwndAutoHide[uEdge] == hwnd)
  4056. {
  4057. _aHwndAutoHide[uEdge] = NULL;
  4058. }
  4059. return TRUE;
  4060. }
  4061. }
  4062. BOOL CTray::_AppBarSetAutoHideBar(PTRAYAPPBARDATA ptabd)
  4063. {
  4064. UINT uEdge = ptabd->abd.uEdge;
  4065. if (uEdge >= ABE_MAX)
  4066. return FALSE;
  4067. else {
  4068. return _AppBarSetAutoHideBar2(GetABDHWnd(&ptabd->abd), BOOLFROMPTR(ptabd->abd.lParam), uEdge);
  4069. }
  4070. }
  4071. void CTray::_AppBarActivationChange2(HWND hwnd, UINT uEdge)
  4072. {
  4073. //
  4074. // FEATURE: make this multi-monitor cool
  4075. //
  4076. HWND hwndAutoHide = _AppBarGetAutoHideBar(uEdge);
  4077. if (hwndAutoHide && (hwndAutoHide != hwnd))
  4078. {
  4079. //
  4080. // the _AppBar got this notification inside a SendMessage from USER
  4081. // and is now in a SendMessage to us. don't try to do a SetWindowPos
  4082. // right now...
  4083. //
  4084. PostMessage(_hwnd, TM_BRINGTOTOP, (WPARAM)hwndAutoHide, uEdge);
  4085. }
  4086. }
  4087. void CTray::_AppBarActivationChange(PTRAYAPPBARDATA ptabd)
  4088. {
  4089. PAPPBAR pab = _FindAppBar(GetABDHWnd(&ptabd->abd));
  4090. if (pab)
  4091. {
  4092. // if this is an autohide bar and they're claiming to be on an edge not the same as their autohide edge,
  4093. // we don't do any activation of other autohides
  4094. for (UINT i = 0; i < ABE_MAX; i++)
  4095. {
  4096. if (_aHwndAutoHide[i] == GetABDHWnd(&ptabd->abd) &&
  4097. i != pab->uEdge)
  4098. return;
  4099. }
  4100. _AppBarActivationChange2(GetABDHWnd(&ptabd->abd), pab->uEdge);
  4101. }
  4102. }
  4103. LRESULT CTray::_OnAppBarMessage(PCOPYDATASTRUCT pcds)
  4104. {
  4105. PTRAYAPPBARDATA ptabd = (PTRAYAPPBARDATA)pcds->lpData;
  4106. ASSERT(pcds->cbData == sizeof(TRAYAPPBARDATA));
  4107. ASSERT(ptabd->abd.cbSize == sizeof(APPBARDATA3264));
  4108. switch (ptabd->dwMessage) {
  4109. case ABM_NEW:
  4110. return _AppBarNew(ptabd);
  4111. case ABM_REMOVE:
  4112. _AppBarRemove(ptabd);
  4113. break;
  4114. case ABM_QUERYPOS:
  4115. _AppBarQueryPos(ptabd);
  4116. break;
  4117. case ABM_SETPOS:
  4118. _AppBarSetPos(ptabd);
  4119. break;
  4120. case ABM_GETSTATE:
  4121. return _desktray.AppBarGetState();
  4122. case ABM_SETSTATE:
  4123. _AppBarSetState((UINT)ptabd->abd.lParam);
  4124. break;
  4125. case ABM_GETTASKBARPOS:
  4126. _AppBarGetTaskBarPos(ptabd);
  4127. break;
  4128. case ABM_WINDOWPOSCHANGED:
  4129. case ABM_ACTIVATE:
  4130. _AppBarActivationChange(ptabd);
  4131. break;
  4132. case ABM_GETAUTOHIDEBAR:
  4133. return (LRESULT)_AppBarGetAutoHideBar(ptabd->abd.uEdge);
  4134. case ABM_SETAUTOHIDEBAR:
  4135. return _AppBarSetAutoHideBar(ptabd);
  4136. default:
  4137. return FALSE;
  4138. }
  4139. return TRUE;
  4140. }
  4141. // EA486701-7F92-11cf-9E05-444553540000
  4142. const GUID CLSID_HIJACKINPROC = {0xEA486701, 0x7F92, 0x11cf, 0x9E, 0x05, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00};
  4143. HRESULT CTray::_LoadInProc(PCOPYDATASTRUCT pcds)
  4144. {
  4145. ASSERT(pcds->cbData == sizeof(LOADINPROCDATA));
  4146. PLOADINPROCDATA plipd = (PLOADINPROCDATA)pcds->lpData;
  4147. // Hack to allow us to kill W95 shell extensions that do reall hacky things that
  4148. // we can not support. In this case Hijack pro
  4149. if (IsEqualIID(plipd->clsid, CLSID_HIJACKINPROC))
  4150. {
  4151. return E_FAIL;
  4152. }
  4153. return _ssomgr.EnableObject(&plipd->clsid, plipd->dwFlags);
  4154. }
  4155. // Allow the trays global hotkeys to be disabled for a while.
  4156. LRESULT CTray::_SetHotkeyEnable(HWND hwnd, BOOL fEnable)
  4157. {
  4158. _fGlobalHotkeyDisable = !fEnable;
  4159. return TRUE;
  4160. }
  4161. BOOL IsPosInHwnd(LPARAM lParam, HWND hwnd)
  4162. {
  4163. RECT r1;
  4164. POINT pt;
  4165. pt.x = GET_X_LPARAM(lParam);
  4166. pt.y = GET_Y_LPARAM(lParam);
  4167. GetWindowRect(hwnd, &r1);
  4168. return PtInRect(&r1, pt);
  4169. }
  4170. void CTray::_HandleWindowPosChanging(LPWINDOWPOS lpwp)
  4171. {
  4172. DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_hwpc"));
  4173. if (_uMoveStuckPlace != (UINT)-1)
  4174. {
  4175. DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_hwpc handling pending move"));
  4176. _DoneMoving(lpwp);
  4177. }
  4178. else if (_fSysSizing || !_fSelfSizing)
  4179. {
  4180. RECT rc;
  4181. if (_fSysSizing)
  4182. {
  4183. GetWindowRect(_hwnd, &rc);
  4184. if (!(lpwp->flags & SWP_NOMOVE))
  4185. {
  4186. rc.left = lpwp->x;
  4187. rc.top = lpwp->y;
  4188. }
  4189. if (!(lpwp->flags & SWP_NOSIZE))
  4190. {
  4191. rc.right = rc.left + lpwp->cx;
  4192. rc.bottom = rc.top + lpwp->cy;
  4193. }
  4194. DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_hwpc sys sizing to rect {%d, %d, %d, %d}"), rc.left, rc.top, rc.right, rc.bottom);
  4195. _uStuckPlace = _RecalcStuckPos(&rc);
  4196. _UpdateVertical(_uStuckPlace);
  4197. }
  4198. _GetDockedRect(&rc, _fSysSizing);
  4199. DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.t_hwpc using rect {%d, %d, %d, %d}"), rc.left, rc.top, rc.right, rc.bottom);
  4200. lpwp->x = rc.left;
  4201. lpwp->y = rc.top;
  4202. lpwp->cx = RECTWIDTH(rc);
  4203. lpwp->cy = RECTHEIGHT(rc);
  4204. lpwp->flags &= ~(SWP_NOMOVE | SWP_NOSIZE);
  4205. }
  4206. lpwp->flags |= SWP_FRAMECHANGED;
  4207. }
  4208. void CTray::_HandlePowerStatus(UINT uMsg, WPARAM wParam, LPARAM lParam)
  4209. {
  4210. BOOL fResetDisplay = FALSE;
  4211. //
  4212. // always reset the display when the machine wakes up from a
  4213. // suspend. NOTE: we don't need this for a standby suspend.
  4214. //
  4215. // a critical resume does not generate a WM_POWERBROADCAST
  4216. // to windows for some reason, but it does generate an old
  4217. // WM_POWER message.
  4218. //
  4219. switch (uMsg)
  4220. {
  4221. case WM_POWER:
  4222. fResetDisplay = (wParam == PWR_CRITICALRESUME);
  4223. break;
  4224. case WM_POWERBROADCAST:
  4225. switch (wParam)
  4226. {
  4227. case PBT_APMRESUMECRITICAL:
  4228. fResetDisplay = TRUE;
  4229. break;
  4230. }
  4231. break;
  4232. }
  4233. if (fResetDisplay)
  4234. ChangeDisplaySettings(NULL, CDS_RESET);
  4235. }
  4236. //////////////////////////////////////////////////////
  4237. //
  4238. // This function checks whether we need to run the cleaner
  4239. // We will not run if user is guest, user has forced us not to, or if the requisite
  4240. // number of days have not yet elapsed
  4241. //
  4242. // We execute a great deal of code to decide whether to run or not that logically should be
  4243. // in fldrclnr.dll, but we execute it here so that we don't have to load fldrclnr.dll unless
  4244. // we absolutely have to, since we execute this path on every logon of explorer.exe
  4245. //
  4246. #define REGSTR_PATH_CLEANUPWIZ REGSTR_PATH_EXPLORER TEXT("\\Desktop\\CleanupWiz")
  4247. #define REGSTR_OEM_PATH REGSTR_PATH_SETUP TEXT("\\OemStartMenuData")
  4248. #define REGSTR_VAL_TIME TEXT("Last used time")
  4249. #define REGSTR_VAL_DELTA_DAYS TEXT("Days between clean up")
  4250. #define REGSTR_VAL_DONTRUN TEXT("NoRun")
  4251. #define REGSTR_OEM_SEVENDAY_DISABLE TEXT("OemDesktopCleanupDisable")
  4252. //
  4253. // iDays can be negative or positive, indicating time in the past or future
  4254. //
  4255. //
  4256. #define FTsPerDayOver1000 (10000*60*60*24) // we've got (1000 x 10,000) 100ns intervals per second
  4257. void CTray::_DesktopCleanup_GetFileTimeNDaysFromGivenTime(const FILETIME *pftGiven, FILETIME * pftReturn, int iDays)
  4258. {
  4259. __int64 i64 = *((__int64 *) pftGiven);
  4260. i64 += Int32x32To64(iDays*1000,FTsPerDayOver1000);
  4261. *pftReturn = *((FILETIME *) &i64);
  4262. }
  4263. //////////////////////////////////////////////////////
  4264. BOOL CTray::_DesktopCleanup_ShouldRun()
  4265. {
  4266. BOOL fRetVal = FALSE;
  4267. if (!IsOS(OS_ANYSERVER) &&
  4268. _fIsDesktopConnected &&
  4269. !SHTestTokenMembership(NULL, DOMAIN_ALIAS_RID_GUESTS) &&
  4270. !SHRestricted(REST_NODESKTOPCLEANUP))
  4271. {
  4272. fRetVal = TRUE;
  4273. FILETIME ftNow, ftLast;
  4274. SYSTEMTIME st;
  4275. GetLocalTime(&st);
  4276. SystemTimeToFileTime(&st, &ftNow);
  4277. DWORD cb = sizeof(ftLast);
  4278. DWORD dwData;
  4279. if (ERROR_SUCCESS != SHRegGetUSValue(REGSTR_PATH_CLEANUPWIZ, REGSTR_VAL_TIME,
  4280. NULL, &ftLast, &cb, FALSE, NULL, 0))
  4281. {
  4282. cb = sizeof(dwData);
  4283. if ((ERROR_SUCCESS == SHGetValue(HKEY_LOCAL_MACHINE, REGSTR_OEM_PATH, REGSTR_OEM_SEVENDAY_DISABLE, NULL, &dwData, &cb)) &&
  4284. (dwData != 0))
  4285. {
  4286. _DesktopCleanup_GetFileTimeNDaysFromGivenTime(&ftNow, &ftLast, -53); // to get the timer to kick in 7 days from now, set last to be 53 days ago
  4287. }
  4288. else
  4289. {
  4290. ftLast = ftNow;
  4291. }
  4292. SHRegSetUSValue(REGSTR_PATH_CLEANUPWIZ, REGSTR_VAL_TIME, NULL, &ftLast, sizeof(ftLast), SHREGSET_FORCE_HKCU);
  4293. }
  4294. HUSKEY hkey = NULL;
  4295. if (ERROR_SUCCESS == SHRegOpenUSKey(REGSTR_PATH_CLEANUPWIZ, KEY_READ, NULL, &hkey, FALSE))
  4296. {
  4297. //
  4298. // if we're in normal mode and the DONT RUN flag is set, we return immediately
  4299. // (the user checked the "don't run automatically" box)
  4300. //
  4301. cb = sizeof (DWORD);
  4302. if ((ERROR_SUCCESS == SHRegQueryUSValue(hkey, REGSTR_VAL_DONTRUN, NULL, &dwData, &cb, FALSE, NULL, 0)) &&
  4303. (dwData != 0))
  4304. {
  4305. fRetVal = FALSE;
  4306. }
  4307. else
  4308. {
  4309. //
  4310. // we need to figure out if if we are within the (last run time + delta days)
  4311. // time period
  4312. //
  4313. int iDays = 60;
  4314. if (ERROR_SUCCESS == (SHRegGetUSValue(REGSTR_PATH_CLEANUPWIZ, REGSTR_VAL_DELTA_DAYS,
  4315. NULL, &dwData, &cb,FALSE, NULL, 0)))
  4316. {
  4317. iDays = dwData;
  4318. }
  4319. // if (iDays == 0), run every time!
  4320. if (iDays > 0)
  4321. {
  4322. FILETIME ftRange;
  4323. _DesktopCleanup_GetFileTimeNDaysFromGivenTime(&ftLast, &ftRange, iDays);
  4324. if (!(CompareFileTime(&ftNow, &ftRange) > 0))
  4325. {
  4326. fRetVal = FALSE;
  4327. }
  4328. }
  4329. }
  4330. SHRegCloseUSKey(hkey);
  4331. }
  4332. }
  4333. return fRetVal;
  4334. }
  4335. void CTray::_CheckDesktopCleanup()
  4336. {
  4337. if (_DesktopCleanup_ShouldRun())
  4338. {
  4339. PROCESS_INFORMATION pi = {0};
  4340. TCHAR szRunDll32[MAX_PATH];
  4341. GetSystemDirectory(szRunDll32, ARRAYSIZE(szRunDll32));
  4342. PathAppend(szRunDll32, TEXT("rundll32.exe"));
  4343. if (CreateProcessWithArgs(szRunDll32, TEXT("fldrclnr.dll,Wizard_RunDLL"), NULL, &pi))
  4344. {
  4345. CloseHandle(pi.hProcess);
  4346. CloseHandle(pi.hThread);
  4347. }
  4348. }
  4349. }
  4350. //////////////////////////////////////////////////////
  4351. //
  4352. // Try tacking a 1, 2, 3 or whatever on to a file or
  4353. // directory name until it is unique. When on a file,
  4354. // stick it before the extension.
  4355. //
  4356. void _MakeBetterUniqueName(LPTSTR pszPathName, int cchPathName)
  4357. {
  4358. TCHAR szNewPath[MAX_PATH];
  4359. int i = 1;
  4360. if (PathIsDirectory(pszPathName))
  4361. {
  4362. do
  4363. {
  4364. StringCchPrintf(szNewPath, ARRAYSIZE(szNewPath), TEXT("%s%d"), pszPathName, i++);
  4365. } while (-1 != GetFileAttributes(szNewPath));
  4366. StringCchCopy(pszPathName, cchPathName, szNewPath);
  4367. }
  4368. else
  4369. {
  4370. TCHAR szExt[MAX_PATH];
  4371. LPTSTR pszExt;
  4372. pszExt = PathFindExtension(pszPathName);
  4373. if (pszExt)
  4374. {
  4375. StringCchCopy(szExt, ARRAYSIZE(szExt), pszExt);
  4376. *pszExt = 0;
  4377. do
  4378. {
  4379. StringCchPrintf(szNewPath, ARRAYSIZE(szNewPath), TEXT("%s%d%s"), pszPathName, i++,szExt);
  4380. } while (-1 != GetFileAttributes(szNewPath));
  4381. StringCchCopy(pszPathName, cchPathName, szNewPath);
  4382. }
  4383. }
  4384. }
  4385. BOOL_PTR WINAPI CTray::RogueProgramFileDlgProc(HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
  4386. {
  4387. TCHAR szBuffer[MAX_PATH*2];
  4388. TCHAR szBuffer2[MAX_PATH*2];
  4389. static TCHAR szBetterPath[MAX_PATH];
  4390. static TCHAR *pszPath = NULL;
  4391. switch (iMsg)
  4392. {
  4393. case WM_INITDIALOG:
  4394. pszPath = (TCHAR *)lParam;
  4395. StringCchCopy(szBetterPath, ARRAYSIZE(szBetterPath), pszPath);
  4396. _MakeBetterUniqueName(szBetterPath, ARRAYSIZE(szBetterPath));
  4397. SendDlgItemMessage(hWnd, IDC_MSG, WM_GETTEXT, (WPARAM)(MAX_PATH*2), (LPARAM)szBuffer);
  4398. StringCchPrintf(szBuffer2, ARRAYSIZE(szBuffer2), szBuffer, pszPath, szBetterPath);
  4399. SendDlgItemMessage(hWnd, IDC_MSG, WM_SETTEXT, (WPARAM)0, (LPARAM)szBuffer2);
  4400. return TRUE;
  4401. case WM_COMMAND:
  4402. switch(LOWORD(wParam))
  4403. {
  4404. case IDC_RENAME:
  4405. //rename and fall through
  4406. if (pszPath)
  4407. {
  4408. MoveFile(pszPath, szBetterPath);
  4409. }
  4410. EndDialog(hWnd, IDC_RENAME);
  4411. return TRUE;
  4412. case IDIGNORE:
  4413. EndDialog(hWnd, IDIGNORE);
  4414. return TRUE;
  4415. }
  4416. break;
  4417. }
  4418. return FALSE;
  4419. }
  4420. //
  4421. // Check to see if there are any files or folders that could interfere
  4422. // with the fact that Program Files has a space in it.
  4423. //
  4424. // An example would be a directory called: "C:\Program" or a file called"C:\Program.exe".
  4425. //
  4426. // This can prevent apps that dont quote strings in the registry or call CreateProcess with
  4427. // unquoted strings from working properly since CreateProcess wont know what the real exe is.
  4428. //
  4429. void CTray::_CheckForRogueProgramFile()
  4430. {
  4431. TCHAR szProgramFilesPath[MAX_PATH];
  4432. TCHAR szProgramFilesShortName[MAX_PATH];
  4433. if (SHTestTokenMembership(NULL, DOMAIN_ALIAS_RID_ADMINS)
  4434. && S_OK == SHGetFolderPath(NULL, CSIDL_PROGRAM_FILES, NULL, SHGFP_TYPE_CURRENT, szProgramFilesPath))
  4435. {
  4436. LPTSTR pszRoguePattern;
  4437. int cchRoguePattern;
  4438. pszRoguePattern = StrChr(szProgramFilesPath, TEXT(' '));
  4439. cchRoguePattern = ARRAYSIZE(szProgramFilesPath) - (pszRoguePattern - szProgramFilesPath);
  4440. if (pszRoguePattern)
  4441. {
  4442. HANDLE hFind;
  4443. WIN32_FIND_DATA wfd;
  4444. // Remember short name for folder name comparison below
  4445. *pszRoguePattern = TEXT('\0');
  4446. StringCchCopy(szProgramFilesShortName, ARRAYSIZE(szProgramFilesShortName), szProgramFilesPath);
  4447. // turn "C:\program files" into "C:\program.*"
  4448. StringCchCopy(pszRoguePattern, cchRoguePattern, TEXT(".*"));
  4449. pszRoguePattern = szProgramFilesPath;
  4450. hFind = FindFirstFile(pszRoguePattern, &wfd);
  4451. while (hFind != INVALID_HANDLE_VALUE)
  4452. {
  4453. int iRet = 0;
  4454. TCHAR szRogueFileName[MAX_PATH];
  4455. // we found a file (eg "c:\Program.txt")
  4456. StringCchCopy(szRogueFileName, ARRAYSIZE(szRogueFileName), pszRoguePattern);
  4457. PathRemoveFileSpec(szRogueFileName);
  4458. StringCchCat(szRogueFileName, ARRAYSIZE(szRogueFileName), wfd.cFileName);
  4459. // don't worry about folders unless they are called "Program"
  4460. if (!((wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
  4461. lstrcmpi(szProgramFilesShortName, szRogueFileName) != 0))
  4462. {
  4463. iRet = SHMessageBoxCheckEx(GetDesktopWindow(),
  4464. hinstCabinet,
  4465. MAKEINTRESOURCE(DLG_PROGRAMFILECONFLICT),
  4466. RogueProgramFileDlgProc,
  4467. (void *)szRogueFileName,
  4468. IDIGNORE,
  4469. TEXT("RogueProgramName"));
  4470. }
  4471. if ((iRet == IDIGNORE) || !FindNextFile(hFind, &wfd))
  4472. {
  4473. // user hit ignore or we are done, so don't keep going
  4474. break;
  4475. }
  4476. }
  4477. if (hFind != INVALID_HANDLE_VALUE)
  4478. {
  4479. FindClose(hFind);
  4480. }
  4481. }
  4482. }
  4483. }
  4484. void CTray::_OnWaitCursorNotify(LPNMHDR pnm)
  4485. {
  4486. _iWaitCount += (pnm->code == NM_STARTWAIT ? 1 :-1);
  4487. ASSERT(_iWaitCount >= 0);
  4488. // Don't let it go negative or we'll never get rid of it.
  4489. if (_iWaitCount < 0)
  4490. _iWaitCount = 0;
  4491. // what we really want is for user to simulate a mouse move/setcursor
  4492. SetCursor(LoadCursor(NULL, _iWaitCount ? IDC_APPSTARTING : IDC_ARROW));
  4493. }
  4494. void CTray::_HandlePrivateCommand(LPARAM lParam)
  4495. {
  4496. LPSTR psz = (LPSTR) lParam; // lParam always ansi.
  4497. if (!lstrcmpiA(psz, "ToggleDesktop"))
  4498. {
  4499. _RaiseDesktop(!g_fDesktopRaised, TRUE);
  4500. }
  4501. else if (!lstrcmpiA(psz, "Explorer"))
  4502. {
  4503. // Fast way to bring up explorer window on root of
  4504. // windows dir.
  4505. SHELLEXECUTEINFO shei = {0};
  4506. TCHAR szPath[MAX_PATH];
  4507. if (GetWindowsDirectory(szPath, ARRAYSIZE(szPath)) != 0)
  4508. {
  4509. PathStripToRoot(szPath);
  4510. shei.lpIDList = ILCreateFromPath(szPath);
  4511. if (shei.lpIDList)
  4512. {
  4513. shei.cbSize = sizeof(shei);
  4514. shei.fMask = SEE_MASK_IDLIST;
  4515. shei.nShow = SW_SHOWNORMAL;
  4516. shei.lpVerb = TEXT("explore");
  4517. ShellExecuteEx(&shei);
  4518. ILFree((LPITEMIDLIST)shei.lpIDList);
  4519. }
  4520. }
  4521. }
  4522. LocalFree(psz);
  4523. }
  4524. //***
  4525. //
  4526. void CTray::_OnFocusMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
  4527. {
  4528. BOOL fActivate = (BOOL) wParam;
  4529. switch (uMsg)
  4530. {
  4531. case TM_UIACTIVATEIO:
  4532. {
  4533. #ifdef DEBUG
  4534. {
  4535. int dtb = (int) lParam;
  4536. TraceMsg(DM_FOCUS, "tiois: TM_UIActIO fAct=%d dtb=%d", fActivate, dtb);
  4537. ASSERT(dtb == 1 || dtb == -1);
  4538. }
  4539. #endif
  4540. if (fActivate)
  4541. {
  4542. // Since we are tabbing into the tray, turn the focus rect on.
  4543. SendMessage(_hwnd, WM_UPDATEUISTATE, MAKEWPARAM(UIS_CLEAR,
  4544. UISF_HIDEFOCUS), 0);
  4545. SendMessage(v_hwndDesktop, DTM_ONFOCUSCHANGEIS, TRUE, (LPARAM) _hwnd);
  4546. SetForegroundWindow(_hwnd);
  4547. // fake an IUnknown_UIActivateIO(_ptbs, TRUE, &msg);
  4548. if (GetAsyncKeyState(VK_SHIFT) < 0)
  4549. {
  4550. _SetFocus(_hwndNotify);
  4551. }
  4552. else
  4553. {
  4554. _SetFocus(_hwndStart);
  4555. }
  4556. }
  4557. else
  4558. {
  4559. Ldeact:
  4560. IUnknown_UIActivateIO(_ptbs, FALSE, NULL);
  4561. SetForegroundWindow(v_hwndDesktop);
  4562. }
  4563. break;
  4564. }
  4565. case TM_ONFOCUSCHANGEIS:
  4566. {
  4567. HWND hwnd = (HWND) lParam;
  4568. TraceMsg(DM_FOCUS, "tiois: TM_OnFocChgIS hwnd=%x fAct=%d", hwnd, fActivate);
  4569. if (fActivate)
  4570. {
  4571. // someone else is activating, so we need to deactivate
  4572. goto Ldeact;
  4573. }
  4574. break;
  4575. }
  4576. default:
  4577. ASSERT(0);
  4578. break;
  4579. }
  4580. return;
  4581. }
  4582. #define TSVC_NTIMER (IDT_SERVICELAST - IDT_SERVICE0 + 1)
  4583. struct {
  4584. #ifdef DEBUG
  4585. UINT_PTR idtWin;
  4586. #endif
  4587. TIMERPROC pfnSvc;
  4588. } g_timerService[TSVC_NTIMER];
  4589. #define TSVC_IDToIndex(id) ((id) - IDT_SERVICE0)
  4590. #define TSVC_IndexToID(i) ((i) + IDT_SERVICE0)
  4591. int CTray::_OnTimerService(UINT uMsg, WPARAM wParam, LPARAM lParam)
  4592. {
  4593. int i;
  4594. UINT_PTR idt;
  4595. TIMERPROC pfn;
  4596. BOOL b;
  4597. switch (uMsg) {
  4598. case TM_SETTIMER:
  4599. TraceMsg(DM_UEMTRACE, "e.TM_SETTIMER: wP=0x%x lP=%x", wParam, lParam);
  4600. ASSERT(IS_VALID_CODE_PTR(lParam, TIMERPROC));
  4601. for (i = 0; i < TSVC_NTIMER; i++) {
  4602. if (g_timerService[i].pfnSvc == 0) {
  4603. g_timerService[i].pfnSvc = (TIMERPROC)lParam;
  4604. idt = SetTimer(_hwnd, TSVC_IndexToID(i), (UINT)wParam, 0);
  4605. if (idt == 0) {
  4606. TraceMsg(DM_UEMTRACE, "e.TM_SETTIMER: ST()=%d (!)", idt);
  4607. break;
  4608. }
  4609. ASSERT(idt == (UINT_PTR)TSVC_IndexToID(i));
  4610. DBEXEC(TRUE, (g_timerService[i].idtWin = idt));
  4611. TraceMsg(DM_UEMTRACE, "e.TM_SETTIMER: ret=0x%x", TSVC_IndexToID(i));
  4612. return TSVC_IndexToID(i); // idtWin
  4613. }
  4614. }
  4615. TraceMsg(DM_UEMTRACE, "e.TM_SETTIMER: ret=0 (!)");
  4616. return 0;
  4617. case TM_KILLTIMER: // lP=idtWin
  4618. TraceMsg(DM_UEMTRACE, "e.TM_KILLTIMER: wP=0x%x lP=%x", wParam, lParam);
  4619. if (EVAL(IDT_SERVICE0 <= lParam && lParam <= IDT_SERVICE0 + TSVC_NTIMER - 1)) {
  4620. i = (int)TSVC_IDToIndex(lParam);
  4621. if (g_timerService[i].pfnSvc) {
  4622. ASSERT(g_timerService[i].idtWin == (UINT)lParam);
  4623. b = KillTimer(_hwnd, lParam);
  4624. ASSERT(b);
  4625. g_timerService[i].pfnSvc = 0;
  4626. DBEXEC(TRUE, (g_timerService[i].idtWin = 0));
  4627. return TRUE;
  4628. }
  4629. }
  4630. return 0;
  4631. case WM_TIMER: // wP=idtWin lP=0
  4632. TraceMsg(DM_UEMTRACE, "e.TM_TIMER: wP=0x%x lP=%x", wParam, lParam);
  4633. if (EVAL(IDT_SERVICE0 <= wParam && wParam <= IDT_SERVICE0 + TSVC_NTIMER - 1)) {
  4634. i = (int)TSVC_IDToIndex(wParam);
  4635. pfn = g_timerService[i].pfnSvc;
  4636. if (EVAL(IS_VALID_CODE_PTR(pfn, TIMERPROC)))
  4637. (*pfn)(_hwnd, WM_TIMER, wParam, GetTickCount());
  4638. }
  4639. return 0;
  4640. }
  4641. ASSERT(0); /*NOTREACHED*/
  4642. return 0;
  4643. }
  4644. void CTray::RealityCheck()
  4645. {
  4646. //
  4647. // Make sure that the tray's actual z-order position agrees with what we think
  4648. // it is. We need to do this because there's a recurring bug where the tray
  4649. // gets bumped out of TOPMOST position. (Lots of things, like a tray-owned
  4650. // window moving itself to non-TOPMOST or a random app messing with the tray
  4651. // window position, can cause this.)
  4652. //
  4653. _ResetZorder();
  4654. }
  4655. #define DELAY_STARTUPTROUBLESHOOT (15 * 1000)
  4656. void CTray::LogFailedStartupApp()
  4657. {
  4658. if (_hwnd)
  4659. {
  4660. PostMessage(_hwnd, TM_HANDLESTARTUPFAILED, 0, 0);
  4661. }
  4662. else
  4663. {
  4664. _fEarlyStartupFailure = TRUE;
  4665. }
  4666. }
  4667. void WINAPI CTray::TroubleShootStartupCB(HWND hwnd, UINT uMsg, UINT_PTR idTimer, DWORD dwTime)
  4668. {
  4669. KillTimer(hwnd, idTimer);
  4670. if (!c_tray._fStartupTroubleshooterLaunched)
  4671. {
  4672. TCHAR szCmdLine[MAX_PATH];
  4673. DWORD cb;
  4674. c_tray._fStartupTroubleshooterLaunched = TRUE;
  4675. cb = sizeof(szCmdLine);
  4676. if (SHGetValue(HKEY_LOCAL_MACHINE, REGSTR_PATH_EXPLORER,
  4677. TEXT("StartupTroubleshoot"), NULL,
  4678. szCmdLine, &cb) == ERROR_SUCCESS)
  4679. {
  4680. ShellExecuteRegApp(szCmdLine, RRA_NOUI | RRA_DELETE);
  4681. }
  4682. }
  4683. }
  4684. void CTray::_OnHandleStartupFailed()
  4685. {
  4686. /*
  4687. * Don't launch the troubleshooter until we have gone
  4688. * DELAY_STARTUPTROUBLESHOOT milliseconds without a startup problem.
  4689. * This gives time for the system to settle down before starting to
  4690. * annoy the user all over again.
  4691. *
  4692. * (And, of course, don't launch it more than once.)
  4693. */
  4694. if (!_fStartupTroubleshooterLaunched)
  4695. {
  4696. SetTimer(_hwnd, IDT_STARTUPFAILED, DELAY_STARTUPTROUBLESHOOT, TroubleShootStartupCB);
  4697. }
  4698. }
  4699. void CTray::_HandleDelayBootStuff()
  4700. {
  4701. // This posted message is the last one processed by the primary
  4702. // thread (tray thread) when we boot. At this point we will
  4703. // want to load the shell services (which usually create threads)
  4704. // and resume both the background start menu thread and the fs_notfiy
  4705. // thread.
  4706. if (!_fHandledDelayBootStuff)
  4707. {
  4708. if (GetShellWindow() == NULL)
  4709. {
  4710. // The desktop browser hasn't finished navigating yet.
  4711. SetTimer(_hwnd, IDT_HANDLEDELAYBOOTSTUFF, 3 * 1000, NULL);
  4712. return;
  4713. }
  4714. _fHandledDelayBootStuff = TRUE;
  4715. if (g_dwStopWatchMode)
  4716. {
  4717. StopWatch_StartTimed(SWID_STARTUP, TEXT("_DelayedBootStuff"), SPMODE_SHELL | SPMODE_DEBUGOUT, GetPerfTime());
  4718. }
  4719. PostMessage(_hwnd, TM_SHELLSERVICEOBJECTS, 0, 0);
  4720. BandSite_HandleDelayBootStuff(_ptbs);
  4721. //check to see if there are any files or folders that could interfere
  4722. //with the fact that Program Files has a space in it. An example would
  4723. //be a folder called "C:\Program" or a file called "C:\Program.exe"
  4724. _CheckForRogueProgramFile();
  4725. // Create a named event and fire it so that the services can
  4726. // go to work, reducing contention during boot.
  4727. _hShellReadyEvent = CreateEvent(0, TRUE, TRUE, TEXT("ShellReadyEvent"));
  4728. if (_hShellReadyEvent)
  4729. {
  4730. // Set the event in case it was already created and our "create
  4731. // signaled" parameter to CreateEvent got ignored.
  4732. SetEvent(_hShellReadyEvent);
  4733. }
  4734. // Check whether we should launch Desktop Cleanup Wizard
  4735. _CheckDesktopCleanup();
  4736. TBOOL(WinStationRegisterConsoleNotification(SERVERNAME_CURRENT, _hwnd, NOTIFY_FOR_THIS_SESSION));
  4737. if (g_dwStopWatchMode)
  4738. {
  4739. StopWatch_StopTimed(SWID_STARTUP, TEXT("_DelayedBootStuff"), SPMODE_SHELL | SPMODE_DEBUGOUT, GetPerfTime());
  4740. }
  4741. }
  4742. }
  4743. LRESULT CTray::_OnDeviceChange(HWND hwnd, WPARAM wParam, LPARAM lParam)
  4744. {
  4745. switch (wParam)
  4746. {
  4747. case DBT_CONFIGCHANGED:
  4748. // We got an update. Refresh.
  4749. _RefreshStartMenu();
  4750. break;
  4751. case DBT_QUERYCHANGECONFIG:
  4752. //
  4753. // change to registry settings
  4754. //
  4755. ChangeDisplaySettings(NULL, 0);
  4756. break;
  4757. case DBT_MONITORCHANGE:
  4758. //
  4759. // handle monitor change
  4760. //
  4761. HandleDisplayChange(LOWORD(lParam), HIWORD(lParam), TRUE);
  4762. break;
  4763. case DBT_CONFIGCHANGECANCELED:
  4764. //
  4765. // if the config change was canceled go back
  4766. //
  4767. HandleDisplayChange(0, 0, FALSE);
  4768. break;
  4769. }
  4770. Mixer_DeviceChange(wParam, lParam);
  4771. return 0;
  4772. }
  4773. //
  4774. // The "resizable" edge of the taskbar is the edge adjacent to
  4775. // the desktop, i.e. opposite the stuck place.
  4776. //
  4777. // returns HTXXX if on a resizable edge, else HTBORDER
  4778. //
  4779. DWORD CTray::_PtOnResizableEdge(POINT pt, LPRECT prcClient)
  4780. {
  4781. RECT rc;
  4782. GetWindowRect(_hwnd, &rc);
  4783. DWORD dwHit = HTBORDER;
  4784. switch (_uStuckPlace)
  4785. {
  4786. case STICK_LEFT: rc.left = prcClient->right; dwHit = HTRIGHT; break;
  4787. case STICK_TOP: rc.top = prcClient->bottom; dwHit = HTBOTTOM; break;
  4788. case STICK_RIGHT: rc.right = prcClient->left; dwHit = HTLEFT; break;
  4789. case STICK_BOTTOM: rc.bottom = prcClient->top; dwHit = HTTOP; break;
  4790. }
  4791. return PtInRect(&rc, pt) ? dwHit : HTBORDER;
  4792. }
  4793. //
  4794. // _OnFactoryMessage
  4795. //
  4796. // The OPK "factory.exe" tool sends us this message to tell us that
  4797. // it has dorked some setting or other and we should refresh so the
  4798. // OEM can see the effect immediately and feel confident that it
  4799. // actually worked. This is not technically necessary but it cuts
  4800. // down on OEM support calls when they ask us why their setting didn't
  4801. // work. (It did work, they just have to log off and back on to see
  4802. // it.)
  4803. //
  4804. int CTray::_OnFactoryMessage(WPARAM wParam, LPARAM lParam)
  4805. {
  4806. switch (wParam)
  4807. {
  4808. case 0: // FACTORY_OEMLINK: factory.exe has dorked the OEM link
  4809. ClosePopupMenus();
  4810. _BuildStartMenu(); // Force a rebuild
  4811. return 1;
  4812. case 1: // FACTORY_MFU: factory.exe has written a new MFU
  4813. HandleFirstTime(); // Rebuild the default MFU
  4814. ClosePopupMenus();
  4815. _BuildStartMenu(); // Force a rebuild
  4816. return 1;
  4817. }
  4818. return 0;
  4819. }
  4820. #define CX_OFFSET g_cxEdge
  4821. #define CY_OFFSET g_cyEdge
  4822. //
  4823. // _MapNCToClient
  4824. //
  4825. // see comments in _TryForwardNCToClient
  4826. //
  4827. BOOL CTray::_MapNCToClient(LPARAM* plParam)
  4828. {
  4829. POINT pt = { GET_X_LPARAM(*plParam), GET_Y_LPARAM(*plParam) };
  4830. RECT rcClient;
  4831. GetClientRect(_hwnd, &rcClient);
  4832. MapWindowPoints(_hwnd, NULL, (LPPOINT)&rcClient, 2);
  4833. //
  4834. // point must be outside the client area and not on the
  4835. // resizable edge of the taskbar
  4836. //
  4837. if (!PtInRect(&rcClient, pt) && _PtOnResizableEdge(pt, &rcClient) == HTBORDER)
  4838. {
  4839. //
  4840. // fudge it over onto the client edge and return TRUE
  4841. //
  4842. if (pt.x < rcClient.left)
  4843. pt.x = rcClient.left + CX_OFFSET;
  4844. else if (pt.x > rcClient.right)
  4845. pt.x = rcClient.right - CX_OFFSET;
  4846. if (pt.y < rcClient.top)
  4847. pt.y = rcClient.top + CY_OFFSET;
  4848. else if (pt.y > rcClient.bottom)
  4849. pt.y = rcClient.bottom - CY_OFFSET;
  4850. *plParam = MAKELONG(pt.x, pt.y);
  4851. return TRUE;
  4852. }
  4853. //
  4854. // didn't pass the test. leave the point alone and return FALSE.
  4855. //
  4856. return FALSE;
  4857. }
  4858. HWND _TopChildWindowFromPoint(HWND hwnd, POINT pt)
  4859. {
  4860. HWND hwndLast = NULL;
  4861. hwnd = ChildWindowFromPoint(hwnd, pt);
  4862. while (hwnd && hwnd != hwndLast)
  4863. {
  4864. hwndLast = hwnd;
  4865. hwnd = ChildWindowFromPoint(hwnd, pt);
  4866. }
  4867. return hwndLast;
  4868. }
  4869. //
  4870. // _TryForwardNCToClient
  4871. //
  4872. // Hack! This exists to solve a usability problem. When you slam your
  4873. // mouse into the bottom corner of the screen and click, we want that to
  4874. // activate the start button. Similarly, when you slam your mouse below
  4875. // a Quick Launch button or task button and click, we want that to
  4876. // activate the button.
  4877. //
  4878. // We hack this by remapping the coordinate of NC mouse messages and
  4879. // manually forwarding to the appropriate window. We only do this for
  4880. // clicks on the edges non-resizable edge of the taskbar.
  4881. //
  4882. // We also warp the mouse cursor over to the new position. This is needed
  4883. // because e.g. if toolbar is the client window we're forwarding
  4884. // to, it will set capture and receive subsequent mouse messages. (And
  4885. // once it gets a mouse message outside the window, it will deselect the
  4886. // button and so the button won't get activated.)
  4887. //
  4888. // _MapNCToClient has the rules for figuring out if the click is on
  4889. // one of the edges we want and for remapping the coordinate into the
  4890. // client area.
  4891. //
  4892. BOOL CTray::_TryForwardNCToClient(UINT uMsg, LPARAM lParam)
  4893. {
  4894. if (_MapNCToClient(&lParam))
  4895. {
  4896. // see if this is over one of our windows
  4897. POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
  4898. MapWindowPoints(NULL, _hwnd, &pt, 1);
  4899. HWND hwnd = _TopChildWindowFromPoint(_hwnd, pt);
  4900. if (hwnd)
  4901. {
  4902. // warp the mouse cursor to this screen coord
  4903. SetCursorPos(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
  4904. // map to window coords
  4905. MapWindowPoints(_hwnd, hwnd, &pt, 1);
  4906. // set lparam to window coords
  4907. lParam = MAKELONG(pt.x, pt.y);
  4908. // map to client message
  4909. ASSERT(InRange(uMsg, WM_NCMOUSEFIRST, WM_NCMOUSELAST));
  4910. uMsg += (WM_LBUTTONDOWN - WM_NCLBUTTONDOWN);
  4911. // forward it
  4912. SendMessage(hwnd, uMsg, 0, lParam);
  4913. return TRUE;
  4914. }
  4915. }
  4916. return FALSE;
  4917. }
  4918. // --------------------------------------------------------------------------
  4919. // CTray::CountOfRunningPrograms
  4920. //
  4921. // Arguments: <none>
  4922. //
  4923. // Returns: DWORD
  4924. //
  4925. // Purpose: Iterates the window list. Looks for windows that are visible
  4926. // with a non-zero length window title. Gets that window process
  4927. // ID and keeps the IDs in a list. For each window iterated it
  4928. // checks against the list to see if the process is already
  4929. // tagged and if so doesn't add it again. Finally it returns the
  4930. // count of the unique processes handling open visible windows
  4931. // in the user's desktop.
  4932. //
  4933. // The list is fixed at 1000 processes (using stack space).
  4934. //
  4935. // History: 2000-06-29 vtan created
  4936. // --------------------------------------------------------------------------
  4937. static const int MAXIMUM_PROCESS_COUNT = 1000;
  4938. typedef struct
  4939. {
  4940. DWORD dwCount;
  4941. DWORD dwProcessIDs[MAXIMUM_PROCESS_COUNT];
  4942. } tProcessIDList;
  4943. bool FoundProcessID (tProcessIDList *pProcessIDList, DWORD dwProcessID)
  4944. {
  4945. bool fFound;
  4946. DWORD dwIndex;
  4947. for (fFound = false, dwIndex = 0; !fFound && (dwIndex < pProcessIDList->dwCount); ++dwIndex)
  4948. {
  4949. fFound = (pProcessIDList->dwProcessIDs[dwIndex] == dwProcessID);
  4950. }
  4951. return(fFound);
  4952. }
  4953. void AddProcessID (tProcessIDList *pProcessIDList, DWORD dwProcessID)
  4954. {
  4955. if (pProcessIDList->dwCount < MAXIMUM_PROCESS_COUNT)
  4956. {
  4957. pProcessIDList->dwProcessIDs[pProcessIDList->dwCount++] = dwProcessID;
  4958. }
  4959. }
  4960. BOOL CALLBACK CountOfRunningProgramsEnumWindowsProc (HWND hwnd, LPARAM lParam)
  4961. {
  4962. if ((GetShellWindow() != hwnd) && IsWindowVisible(hwnd))
  4963. {
  4964. DWORD dwThreadID, dwProcessID;
  4965. TCHAR szWindowTitle[256];
  4966. dwThreadID = GetWindowThreadProcessId(hwnd, &dwProcessID);
  4967. if ((InternalGetWindowText(hwnd, szWindowTitle, ARRAYSIZE(szWindowTitle)) > 0) &&
  4968. (szWindowTitle[0] != TEXT('\0')))
  4969. {
  4970. if (!FoundProcessID(reinterpret_cast<tProcessIDList*>(lParam), dwProcessID))
  4971. {
  4972. AddProcessID(reinterpret_cast<tProcessIDList*>(lParam), dwProcessID);
  4973. }
  4974. }
  4975. }
  4976. return(TRUE);
  4977. }
  4978. DWORD CTray::CountOfRunningPrograms()
  4979. {
  4980. tProcessIDList processIDList = {0};
  4981. TBOOL(EnumWindows(CountOfRunningProgramsEnumWindowsProc, reinterpret_cast<LPARAM>(&processIDList)));
  4982. return processIDList.dwCount;
  4983. }
  4984. // --------------------------------------------------------------------------
  4985. // CTray::_OnSessionChange
  4986. //
  4987. // Arguments: wParam = WTS_xxx notification.
  4988. // lParam = WTS_SESSION_NOTIFICATION struct pointer.
  4989. //
  4990. // Returns: LRESULT
  4991. //
  4992. // Purpose: Handles console/remote dis/reconnects.
  4993. //
  4994. // History: 2000-07-12 vtan created
  4995. // --------------------------------------------------------------------------
  4996. LRESULT CTray::_OnSessionChange(WPARAM wParam, LPARAM lParam)
  4997. {
  4998. ASSERTMSG(DWORD(lParam) == NtCurrentPeb()->SessionId, "Session ID mismatch in CTray::_OnSessionChange");
  4999. if ((WTS_CONSOLE_CONNECT == wParam) || (WTS_REMOTE_CONNECT == wParam) || (WTS_SESSION_UNLOCK == wParam))
  5000. {
  5001. _fIsDesktopConnected = TRUE;
  5002. }
  5003. else if ((WTS_CONSOLE_DISCONNECT == wParam) || (WTS_REMOTE_DISCONNECT == wParam) || (WTS_SESSION_LOCK == wParam))
  5004. {
  5005. _fIsDesktopConnected = FALSE;
  5006. }
  5007. if ((WTS_CONSOLE_CONNECT == wParam) || (WTS_REMOTE_CONNECT == wParam))
  5008. {
  5009. _RefreshStartMenu();
  5010. SHUpdateRecycleBinIcon();
  5011. }
  5012. else if ((WTS_SESSION_LOCK == wParam) || (WTS_SESSION_UNLOCK == wParam))
  5013. {
  5014. if (IsOS(OS_FASTUSERSWITCHING))
  5015. {
  5016. if (wParam == WTS_SESSION_LOCK)
  5017. {
  5018. ExplorerPlaySound(TEXT("WindowsLogoff"));
  5019. }
  5020. else if (wParam == WTS_SESSION_UNLOCK)
  5021. {
  5022. ExplorerPlaySound(TEXT("WindowsLogon"));
  5023. }
  5024. }
  5025. PostMessage(_hwnd, TM_WORKSTATIONLOCKED, (WTS_SESSION_LOCK == wParam), 0);
  5026. }
  5027. else if (WTS_SESSION_REMOTE_CONTROL == wParam)
  5028. {
  5029. // optimization not needed on remote sessions
  5030. if (!GetSystemMetrics(SM_REMOTESESSION)) {
  5031. _BuildStartMenu();
  5032. }
  5033. }
  5034. return 1;
  5035. }
  5036. LRESULT CTray::_NCPaint(HRGN hrgn)
  5037. {
  5038. ASSERT(_hTheme);
  5039. if (_fCanSizeMove || _fShowSizingBarAlways)
  5040. {
  5041. if ((INT_PTR)hrgn == 1)
  5042. hrgn = NULL;
  5043. HDC hdc = GetDCEx( _hwnd, hrgn, DCX_USESTYLE|DCX_WINDOW|DCX_LOCKWINDOWUPDATE|
  5044. ((hrgn != NULL) ? DCX_INTERSECTRGN|DCX_NODELETERGN : 0));
  5045. if (hdc)
  5046. {
  5047. RECT rc;
  5048. GetWindowRect(_hwnd, &rc);
  5049. OffsetRect(&rc, -rc.left, -rc.top);
  5050. _AdjustRectForSizingBar(_uStuckPlace, &rc, 0);
  5051. DrawThemeBackground(_hTheme, hdc, _GetPart(TRUE, _uStuckPlace), 0, &rc, 0);
  5052. ReleaseDC(_hwnd, hdc);
  5053. }
  5054. }
  5055. return 0;
  5056. }
  5057. LRESULT CTray::v_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  5058. {
  5059. static UINT uDDEExec = 0;
  5060. LRESULT lres = 0;
  5061. MSG msg;
  5062. msg.hwnd = hwnd;
  5063. msg.message = uMsg;
  5064. msg.wParam = wParam;
  5065. msg.lParam = lParam;
  5066. if (_pmbStartMenu &&
  5067. _pmbStartMenu->TranslateMenuMessage(&msg, &lres) == S_OK)
  5068. return lres;
  5069. if (_pmbStartPane &&
  5070. _pmbStartPane->TranslateMenuMessage(&msg, &lres) == S_OK)
  5071. return lres;
  5072. if (_pmbTasks &&
  5073. _pmbTasks->TranslateMenuMessage(&msg, &lres) == S_OK)
  5074. return lres;
  5075. wParam = msg.wParam;
  5076. lParam = msg.lParam;
  5077. INSTRUMENT_WNDPROC(SHCNFI_TRAY_WNDPROC, hwnd, uMsg, wParam, lParam);
  5078. switch (uMsg)
  5079. {
  5080. case WMTRAY_REGISTERHOTKEY:
  5081. return _RegisterHotkey(hwnd, (int)wParam);
  5082. case WMTRAY_UNREGISTERHOTKEY:
  5083. return _UnregisterHotkey(hwnd, (int)wParam);
  5084. case WMTRAY_SCREGISTERHOTKEY:
  5085. return _ShortcutRegisterHotkey(hwnd, (WORD)wParam, (ATOM)lParam);
  5086. case WMTRAY_SCUNREGISTERHOTKEY:
  5087. return _ShortcutUnregisterHotkey(hwnd, (WORD)wParam);
  5088. case WMTRAY_SETHOTKEYENABLE:
  5089. return _SetHotkeyEnable(hwnd, (BOOL)wParam);
  5090. case WMTRAY_QUERY_MENU:
  5091. return (LRESULT)_hmenuStart;
  5092. case WMTRAY_QUERY_VIEW:
  5093. return (LRESULT)_hwndTasks;
  5094. case WMTRAY_TOGGLEQL:
  5095. return _ToggleQL((int)lParam);
  5096. case WM_COPYDATA:
  5097. // Check for NULL it can happen if user runs out of selectors or memory...
  5098. if (lParam)
  5099. {
  5100. switch (((PCOPYDATASTRUCT)lParam)->dwData) {
  5101. case TCDM_NOTIFY:
  5102. {
  5103. BOOL bRefresh = FALSE;
  5104. lres = _trayNotify.TrayNotify(_hwndNotify, (HWND)wParam, (PCOPYDATASTRUCT)lParam, &bRefresh);
  5105. if (bRefresh)
  5106. {
  5107. SizeWindows();
  5108. }
  5109. return(lres);
  5110. }
  5111. case TCDM_APPBAR:
  5112. return _OnAppBarMessage((PCOPYDATASTRUCT)lParam);
  5113. case TCDM_LOADINPROC:
  5114. return (UINT)_LoadInProc((PCOPYDATASTRUCT)lParam);
  5115. }
  5116. }
  5117. return FALSE;
  5118. case WM_NCCALCSIZE:
  5119. if (_hTheme)
  5120. {
  5121. if ((_fCanSizeMove || _fShowSizingBarAlways) && lParam)
  5122. {
  5123. _AdjustRectForSizingBar(_uStuckPlace, (LPRECT)lParam, -1);
  5124. }
  5125. return 0;
  5126. }
  5127. else
  5128. {
  5129. goto L_default;
  5130. }
  5131. break;
  5132. case WM_NCLBUTTONDBLCLK:
  5133. if (!_TryForwardNCToClient(uMsg, lParam))
  5134. {
  5135. if (IsPosInHwnd(lParam, _hwndNotify))
  5136. {
  5137. _Command(IDM_SETTIME);
  5138. // Hack! If you click on the tray clock, this tells the tooltip
  5139. // "Hey, I'm using this thing; stop putting up a tip for me."
  5140. // You can get the tooltip to lose track of when it needs to
  5141. // reset the "stop it!" flag and you get stuck in "stop it!" mode.
  5142. // It's particularly easy to make happen on Terminal Server.
  5143. //
  5144. // So let's assume that the only reason people click on the
  5145. // tray clock is to change the time. when they change the time,
  5146. // kick the tooltip in the head to reset the "stop it!" flag.
  5147. SendMessage(_hwndTrayTips, TTM_POP, 0, 0);
  5148. }
  5149. }
  5150. break;
  5151. case WM_NCLBUTTONDOWN:
  5152. case WM_NCLBUTTONUP:
  5153. if (!_TryForwardNCToClient(uMsg, lParam))
  5154. {
  5155. goto L_WM_NCMOUSEMOVE;
  5156. }
  5157. break;
  5158. case WM_NCMOUSEMOVE:
  5159. L_WM_NCMOUSEMOVE:
  5160. if (IsPosInHwnd(lParam, _hwndNotify))
  5161. {
  5162. MSG msgInner;
  5163. msgInner.lParam = lParam;
  5164. msgInner.wParam = wParam;
  5165. msgInner.message = uMsg;
  5166. msgInner.hwnd = hwnd;
  5167. SendMessage(_hwndTrayTips, TTM_RELAYEVENT, 0, (LPARAM)(LPMSG)&msgInner);
  5168. if (uMsg == WM_NCLBUTTONDOWN)
  5169. _SetFocus(_hwndNotify);
  5170. }
  5171. goto DoDefault;
  5172. case WM_CREATE:
  5173. return _OnCreate(hwnd);
  5174. case WM_DESTROY:
  5175. return _HandleDestroy();
  5176. #ifdef DEBUG
  5177. case WM_QUERYENDSESSION:
  5178. TraceMsg(DM_SHUTDOWN, "Tray.wp WM_QUERYENDSESSION");
  5179. goto DoDefault;
  5180. #endif
  5181. case WM_ENDSESSION:
  5182. // save our settings if we are shutting down
  5183. if (wParam)
  5184. {
  5185. if (lParam | ENDSESSION_LOGOFF)
  5186. {
  5187. _fIsLogoff = TRUE;
  5188. _RecomputeAllWorkareas();
  5189. }
  5190. _SaveTrayAndDesktop();
  5191. ShowWindow(_hwnd, SW_HIDE);
  5192. ShowWindow(v_hwndDesktop, SW_HIDE);
  5193. DestroyWindow(_hwnd);
  5194. }
  5195. break;
  5196. case WM_PRINTCLIENT:
  5197. case WM_PAINT:
  5198. {
  5199. RECT rc;
  5200. PAINTSTRUCT ps;
  5201. HDC hdc = (HDC)wParam;
  5202. if (hdc == 0)
  5203. hdc = BeginPaint(hwnd, &ps);
  5204. GetClientRect(hwnd, &rc);
  5205. if (_hTheme)
  5206. {
  5207. RECT rcClip;
  5208. if (GetClipBox(hdc, &rcClip) == NULLREGION)
  5209. rcClip = rc;
  5210. DrawThemeBackground(_hTheme, hdc, _GetPart(FALSE, _uStuckPlace), 0, &rc, &rcClip);
  5211. }
  5212. else
  5213. {
  5214. FillRect(hdc, &rc, (HBRUSH)(COLOR_3DFACE + 1));
  5215. // draw etched line around on either side of the bandsite
  5216. MapWindowPoints(HWND_DESKTOP, hwnd, (LPPOINT)&rc, 2);
  5217. InflateRect(&rc, g_cxEdge, g_cyEdge);
  5218. DrawEdge(hdc, &rc, EDGE_ETCHED, BF_TOPLEFT);
  5219. }
  5220. if (wParam == 0)
  5221. EndPaint(hwnd, &ps);
  5222. }
  5223. break;
  5224. case WM_ERASEBKGND:
  5225. if (_hTheme)
  5226. {
  5227. if (!_fSkipErase)
  5228. {
  5229. RECT rc;
  5230. GetClientRect(hwnd, &rc);
  5231. DrawThemeBackground(_hTheme, (HDC)wParam, _GetPart(FALSE, _uStuckPlace), 0, &rc, NULL);
  5232. // Only draw the first time to prevent piece-mail taskbar painting
  5233. _fSkipErase = TRUE;
  5234. }
  5235. return 1;
  5236. }
  5237. else
  5238. {
  5239. goto DoDefault;
  5240. }
  5241. break;
  5242. case WM_NCPAINT:
  5243. if (_hTheme)
  5244. {
  5245. return _NCPaint((HRGN)wParam);
  5246. }
  5247. else
  5248. {
  5249. goto DoDefault;
  5250. }
  5251. break;
  5252. case WM_POWER:
  5253. case WM_POWERBROADCAST:
  5254. _PropagateMessage(hwnd, uMsg, wParam, lParam);
  5255. _HandlePowerStatus(uMsg, wParam, lParam);
  5256. goto DoDefault;
  5257. case WM_DEVICECHANGE:
  5258. lres = _OnDeviceChange(hwnd, wParam, lParam);
  5259. if (lres == 0)
  5260. {
  5261. goto DoDefault;
  5262. }
  5263. break;
  5264. case WM_NOTIFY:
  5265. {
  5266. NMHDR *pnm = (NMHDR*)lParam;
  5267. if (!BandSite_HandleMessage(_ptbs, hwnd, uMsg, wParam, lParam, &lres)) {
  5268. switch (pnm->code)
  5269. {
  5270. case SEN_DDEEXECUTE:
  5271. if (((LPNMHDR)lParam)->idFrom == 0)
  5272. {
  5273. LPNMVIEWFOLDER pnmPost = DDECreatePostNotify((LPNMVIEWFOLDER)pnm);
  5274. if (pnmPost)
  5275. {
  5276. PostMessage(hwnd, GetDDEExecMsg(), 0, (LPARAM)pnmPost);
  5277. return TRUE;
  5278. }
  5279. }
  5280. break;
  5281. case NM_STARTWAIT:
  5282. case NM_ENDWAIT:
  5283. _OnWaitCursorNotify((NMHDR *)lParam);
  5284. PostMessage(v_hwndDesktop, ((NMHDR*)lParam)->code == NM_STARTWAIT ? DTM_STARTWAIT : DTM_ENDWAIT,
  5285. 0, 0); // forward it along
  5286. break;
  5287. case NM_THEMECHANGED:
  5288. // Force the start button to recalc its size
  5289. _sizeStart.cx = 0;
  5290. SizeWindows();
  5291. break;
  5292. case TTN_NEEDTEXT:
  5293. //
  5294. // Make the clock manage its own tooltip.
  5295. //
  5296. return SendMessage(_GetClockWindow(), WM_NOTIFY, wParam, lParam);
  5297. case TTN_SHOW:
  5298. SetWindowZorder(_hwndTrayTips, HWND_TOP);
  5299. break;
  5300. }
  5301. }
  5302. break;
  5303. }
  5304. case WM_CLOSE:
  5305. _DoExitWindows(v_hwndDesktop);
  5306. break;
  5307. case WM_NCHITTEST:
  5308. {
  5309. RECT r1;
  5310. POINT pt;
  5311. GetClientRect(hwnd, &r1);
  5312. MapWindowPoints(hwnd, NULL, (LPPOINT)&r1, 2);
  5313. pt.x = GET_X_LPARAM(lParam);
  5314. pt.y = GET_Y_LPARAM(lParam);
  5315. _SetUnhideTimer(pt.x, pt.y);
  5316. // If the user can't size or move the taskbar, then just say
  5317. // they hit something useless
  5318. if (!_fCanSizeMove)
  5319. {
  5320. return HTBORDER;
  5321. }
  5322. else if (PtInRect(&r1, pt))
  5323. {
  5324. // allow dragging if mouse is in client area of _hwnd
  5325. return HTCAPTION;
  5326. }
  5327. else
  5328. {
  5329. return _PtOnResizableEdge(pt, &r1);
  5330. }
  5331. }
  5332. break;
  5333. case WM_WINDOWPOSCHANGING:
  5334. _HandleWindowPosChanging((LPWINDOWPOS)lParam);
  5335. break;
  5336. case WM_ENTERSIZEMOVE:
  5337. DebugMsg(DM_TRAYDOCK, TEXT("Tray -- WM_ENTERSIZEMOVE"));
  5338. g_fInSizeMove = TRUE;
  5339. GetCursorPos((LPPOINT)&_rcSizeMoveIgnore);
  5340. _rcSizeMoveIgnore.right = _rcSizeMoveIgnore.left;
  5341. _rcSizeMoveIgnore.bottom = _rcSizeMoveIgnore.top;
  5342. InflateRect(&_rcSizeMoveIgnore, GetSystemMetrics(SM_CXICON),
  5343. GetSystemMetrics(SM_CYICON));
  5344. //
  5345. // unclip the tray from the current monitor.
  5346. // keeping the tray properly clipped in the MoveSize loop is extremely
  5347. // hairy and provides almost no benefit. we'll re-clip when it lands.
  5348. //
  5349. _ClipWindow(FALSE);
  5350. // Remember the old monitor we were on
  5351. _hmonOld = _hmonStuck;
  5352. // set up for WM_MOVING/WM_SIZING messages
  5353. _uMoveStuckPlace = (UINT)-1;
  5354. _fSysSizing = TRUE;
  5355. if (!g_fDragFullWindows)
  5356. {
  5357. SendMessage(_hwndRebar, WM_SETREDRAW, FALSE, 0);
  5358. }
  5359. break;
  5360. case WM_EXITSIZEMOVE:
  5361. DebugMsg(DM_TRAYDOCK, TEXT("Tray -- WM_EXITSIZEMOVE"));
  5362. // done sizing
  5363. _fSysSizing = FALSE;
  5364. _fDeferedPosRectChange = FALSE;
  5365. if (!g_fDragFullWindows)
  5366. {
  5367. SendMessage(_hwndRebar, WM_SETREDRAW, TRUE, 0);
  5368. }
  5369. //
  5370. // kick the size code one last time after the loop is done.
  5371. // NOTE: we rely on the WM_SIZE code re-clipping the tray.
  5372. //
  5373. PostMessage(hwnd, WM_SIZE, 0, 0L);
  5374. g_fInSizeMove = FALSE;
  5375. break;
  5376. case WM_MOVING:
  5377. _HandleMoving(wParam, (LPRECT)lParam);
  5378. break;
  5379. case WM_ENTERMENULOOP:
  5380. // DebugMsg(DM_TRACE, "c.twp: Enter menu loop.");
  5381. _HandleEnterMenuLoop();
  5382. break;
  5383. case WM_EXITMENULOOP:
  5384. // DebugMsg(DM_TRACE, "c.twp: Exit menu loop.");
  5385. _HandleExitMenuLoop();
  5386. break;
  5387. case WM_TIMER:
  5388. if (IDT_SERVICE0 <= wParam && wParam <= IDT_SERVICELAST)
  5389. return _OnTimerService(uMsg, wParam, lParam);
  5390. _HandleTimer(wParam);
  5391. break;
  5392. case WM_SIZING:
  5393. _HandleSizing(wParam, (LPRECT)lParam, _uStuckPlace);
  5394. break;
  5395. case WM_SIZE:
  5396. _HandleSize();
  5397. break;
  5398. case WM_DISPLAYCHANGE:
  5399. // NOTE: we get WM_DISPLAYCHANGE in the below two situations
  5400. // 1. a display size changes (HMON will not change in USER)
  5401. // 2. a display goes away or gets added (HMON will change even if
  5402. // the monitor that went away has nothing to do with our hmonStuck)
  5403. // In the above two situations we actually need to do different things
  5404. // because in 1, we do not want to update our hmonStuck because we might
  5405. // end up on another monitor, but in 2 we do want to update hmonStuck because
  5406. // our hmon is invalid
  5407. // The way we handle this is to call GetMonitorInfo on our old HMONITOR
  5408. // and see if it's still valid, if not, we update it by calling _SetStuckMonitor
  5409. // all these code is in _ScreenSizeChange;
  5410. _ScreenSizeChange(hwnd);
  5411. // Force the Start Pane to rebuild because a change in color depth
  5412. // causes themes to run around destroying fonts (ruining the OOBE
  5413. // text) and we need to reload our bitmaps for the new color depth
  5414. // anyway.
  5415. ::PostMessage(_hwnd, SBM_REBUILDMENU, 0, 0);
  5416. break;
  5417. // Don't go to default wnd proc for this one...
  5418. case WM_INPUTLANGCHANGEREQUEST:
  5419. return(LRESULT)0L;
  5420. case WM_GETMINMAXINFO:
  5421. ((MINMAXINFO *)lParam)->ptMinTrackSize.x = g_cxFrame;
  5422. ((MINMAXINFO *)lParam)->ptMinTrackSize.y = g_cyFrame;
  5423. break;
  5424. case WM_WININICHANGE:
  5425. if (lParam && (0 == lstrcmpi((LPCTSTR)lParam, TEXT("SaveTaskbar"))))
  5426. {
  5427. _SaveTrayAndDesktop();
  5428. }
  5429. else
  5430. {
  5431. BandSite_HandleMessage(_ptbs, hwnd, uMsg, wParam, lParam, NULL);
  5432. _PropagateMessage(hwnd, uMsg, wParam, lParam);
  5433. _OnWinIniChange(hwnd, wParam, lParam);
  5434. }
  5435. if (lParam)
  5436. TraceMsg(TF_TRAY, "Tray Got: lParam=%s", (LPCSTR)lParam);
  5437. break;
  5438. case WM_TIMECHANGE:
  5439. _PropagateMessage(hwnd, uMsg, wParam, lParam);
  5440. break;
  5441. case WM_SYSCOLORCHANGE:
  5442. _OnNewSystemSizes();
  5443. BandSite_HandleMessage(_ptbs, hwnd, uMsg, wParam, lParam, NULL);
  5444. _PropagateMessage(hwnd, uMsg, wParam, lParam);
  5445. break;
  5446. case WM_SETCURSOR:
  5447. if (_iWaitCount) {
  5448. SetCursor(LoadCursor(NULL, IDC_APPSTARTING));
  5449. return TRUE;
  5450. } else
  5451. goto DoDefault;
  5452. case WM_SETFOCUS:
  5453. IUnknown_UIActivateIO(_ptbs, TRUE, NULL);
  5454. break;
  5455. case WM_SYSCHAR:
  5456. if (wParam == TEXT(' ')) {
  5457. HMENU hmenu;
  5458. int idCmd;
  5459. SHSetWindowBits(hwnd, GWL_STYLE, WS_SYSMENU, WS_SYSMENU);
  5460. hmenu = GetSystemMenu(hwnd, FALSE);
  5461. if (hmenu) {
  5462. EnableMenuItem(hmenu, SC_RESTORE, MFS_GRAYED | MF_BYCOMMAND);
  5463. EnableMenuItem(hmenu, SC_MAXIMIZE, MFS_GRAYED | MF_BYCOMMAND);
  5464. EnableMenuItem(hmenu, SC_MINIMIZE, MFS_GRAYED | MF_BYCOMMAND);
  5465. EnableMenuItem(hmenu, SC_MOVE, (_fCanSizeMove ? MFS_ENABLED : MFS_GRAYED) | MF_BYCOMMAND);
  5466. EnableMenuItem(hmenu, SC_SIZE, (_fCanSizeMove ? MFS_ENABLED : MFS_GRAYED) | MF_BYCOMMAND);
  5467. idCmd = _TrackMenu(hmenu);
  5468. if (idCmd)
  5469. SendMessage(_hwnd, WM_SYSCOMMAND, idCmd, 0L);
  5470. }
  5471. SHSetWindowBits(hwnd, GWL_STYLE, WS_SYSMENU, 0L);
  5472. }
  5473. break;
  5474. case WM_SYSCOMMAND:
  5475. // if we are sizing, make the full screen accessible
  5476. switch (wParam & 0xFFF0) {
  5477. case SC_CLOSE:
  5478. _DoExitWindows(v_hwndDesktop);
  5479. break;
  5480. default:
  5481. goto DoDefault;
  5482. }
  5483. break;
  5484. case TM_DESKTOPSTATE:
  5485. _OnDesktopState(lParam);
  5486. break;
  5487. case TM_RAISEDESKTOP:
  5488. _RaiseDesktop((BOOL)wParam, FALSE);
  5489. break;
  5490. #ifdef DEBUG
  5491. case TM_NEXTCTL:
  5492. #endif
  5493. case TM_UIACTIVATEIO:
  5494. case TM_ONFOCUSCHANGEIS:
  5495. _OnFocusMsg(uMsg, wParam, lParam);
  5496. break;
  5497. case TM_MARSHALBS: // wParam=IID lRes=pstm
  5498. return BandSite_OnMarshallBS(wParam, lParam);
  5499. case TM_SETTIMER:
  5500. case TM_KILLTIMER:
  5501. return _OnTimerService(uMsg, wParam, lParam);
  5502. break;
  5503. case TM_FACTORY:
  5504. return _OnFactoryMessage(wParam, lParam);
  5505. case TM_ACTASTASKSW:
  5506. _ActAsSwitcher();
  5507. break;
  5508. case TM_RELAYPOSCHANGED:
  5509. _AppBarNotifyAll((HMONITOR)lParam, ABN_POSCHANGED, (HWND)wParam, 0);
  5510. break;
  5511. case TM_BRINGTOTOP:
  5512. SetWindowZorder((HWND)wParam, HWND_TOP);
  5513. break;
  5514. case TM_WARNNOAUTOHIDE:
  5515. DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.twp collision UI request"));
  5516. //
  5517. // this may look a little funny but what we do is post this message all
  5518. // over the place and ignore it when we think it is a bad time to put
  5519. // up a message (like the middle of a fulldrag...)
  5520. //
  5521. // wParam tells us if we need to try to clear the state
  5522. // the lowword of _SetAutoHideState's return tells if anything changed
  5523. //
  5524. if ((!_fSysSizing || !g_fDragFullWindows) &&
  5525. (!wParam || LOWORD(_SetAutoHideState(FALSE))))
  5526. {
  5527. ShellMessageBox(hinstCabinet, hwnd,
  5528. MAKEINTRESOURCE(IDS_ALREADYAUTOHIDEBAR),
  5529. MAKEINTRESOURCE(IDS_TASKBAR), MB_OK | MB_ICONINFORMATION);
  5530. }
  5531. else
  5532. {
  5533. DebugMsg(DM_TRAYDOCK, TEXT("TRAYDOCK.twp blowing off extraneous collision UI request"));
  5534. }
  5535. break;
  5536. case TM_PRIVATECOMMAND:
  5537. _HandlePrivateCommand(lParam);
  5538. break;
  5539. case TM_HANDLEDELAYBOOTSTUFF:
  5540. _HandleDelayBootStuff();
  5541. break;
  5542. case TM_SHELLSERVICEOBJECTS:
  5543. _ssomgr.LoadRegObjects();
  5544. break;
  5545. case TM_CHANGENOTIFY:
  5546. _HandleChangeNotify(wParam, lParam);
  5547. break;
  5548. case TM_GETHMONITOR:
  5549. *((HMONITOR *)lParam) = _hmonStuck;
  5550. break;
  5551. case TM_DOTRAYPROPERTIES:
  5552. DoProperties(TPF_TASKBARPAGE);
  5553. break;
  5554. case TM_STARTUPAPPSLAUNCHED:
  5555. PostMessage(_hwndNotify, TNM_STARTUPAPPSLAUNCHED, 0, 0);
  5556. break;
  5557. case TM_LANGUAGEBAND:
  5558. return _ToggleLanguageBand(lParam);
  5559. case WM_NCRBUTTONUP:
  5560. uMsg = WM_CONTEXTMENU;
  5561. wParam = (WPARAM)_hwndTasks;
  5562. goto L_WM_CONTEXTMENU;
  5563. case WM_CONTEXTMENU:
  5564. L_WM_CONTEXTMENU:
  5565. if (!SHRestricted(REST_NOTRAYCONTEXTMENU))
  5566. {
  5567. if (((HWND)wParam) == _hwndStart)
  5568. {
  5569. // Don't display of the Start Menu is up.
  5570. if (SendMessage(_hwndStart, BM_GETSTATE, 0, 0) & BST_PUSHED)
  5571. break;
  5572. _fFromStart = TRUE;
  5573. StartMenuContextMenu(_hwnd, (DWORD)lParam);
  5574. _fFromStart = FALSE;
  5575. }
  5576. else if (IsPosInHwnd(lParam, _hwndNotify) || SHIsChildOrSelf(_hwndNotify, GetFocus()) == S_OK)
  5577. {
  5578. // if click was inthe clock, include
  5579. // the time
  5580. _ContextMenu((DWORD)lParam, TRUE);
  5581. }
  5582. else
  5583. {
  5584. BandSite_HandleMessage(_ptbs, hwnd, uMsg, wParam, lParam, &lres);
  5585. }
  5586. }
  5587. break;
  5588. case WM_INITMENUPOPUP:
  5589. case WM_MEASUREITEM:
  5590. case WM_DRAWITEM:
  5591. case WM_MENUCHAR:
  5592. // Don't call bandsite message handler when code path started via the start button context menu
  5593. if (!_fFromStart)
  5594. {
  5595. BandSite_HandleMessage(_ptbs, hwnd, uMsg, wParam, lParam, &lres);
  5596. }
  5597. break;
  5598. case TM_DOEXITWINDOWS:
  5599. _DoExitWindows(v_hwndDesktop);
  5600. break;
  5601. case TM_HANDLESTARTUPFAILED:
  5602. _OnHandleStartupFailed();
  5603. break;
  5604. case WM_HOTKEY:
  5605. if (wParam < GHID_FIRST)
  5606. {
  5607. _HandleHotKey((WORD)wParam);
  5608. }
  5609. else
  5610. {
  5611. _HandleGlobalHotkey(wParam);
  5612. }
  5613. break;
  5614. case WM_COMMAND:
  5615. if (!BandSite_HandleMessage(_ptbs, hwnd, uMsg, wParam, lParam, &lres))
  5616. _Command(GET_WM_COMMAND_ID(wParam, lParam));
  5617. break;
  5618. case SBM_CANCELMENU:
  5619. ClosePopupMenus();
  5620. break;
  5621. case SBM_REBUILDMENU:
  5622. _BuildStartMenu();
  5623. break;
  5624. case WM_WINDOWPOSCHANGED:
  5625. _AppBarActivationChange2(hwnd, _uStuckPlace);
  5626. SendMessage(_hwndNotify, TNM_TRAYPOSCHANGED, 0, 0);
  5627. goto DoDefault;
  5628. case WM_LBUTTONDOWN:
  5629. case WM_RBUTTONDOWN:
  5630. case WM_MBUTTONDOWN:
  5631. if (_hwndStartBalloon)
  5632. {
  5633. RECT rc;
  5634. POINT pt = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
  5635. GetWindowRect(_hwndStartBalloon, &rc);
  5636. MapWindowRect(HWND_DESKTOP, _hwnd, &rc);
  5637. if (PtInRect(&rc, pt))
  5638. {
  5639. ShowWindow(_hwndStartBalloon, SW_HIDE);
  5640. _DontShowTheStartButtonBalloonAnyMore();
  5641. _DestroyStartButtonBalloon();
  5642. }
  5643. }
  5644. break;
  5645. case TM_SETPUMPHOOK:
  5646. ATOMICRELEASE(_pmbTasks);
  5647. ATOMICRELEASE(_pmpTasks);
  5648. if (wParam && lParam)
  5649. {
  5650. _pmbTasks = (IMenuBand*)wParam;
  5651. _pmbTasks->AddRef();
  5652. _pmpTasks = (IMenuPopup*)lParam;
  5653. _pmpTasks->AddRef();
  5654. }
  5655. break;
  5656. case WM_ACTIVATE:
  5657. _AppBarActivationChange2(hwnd, _uStuckPlace);
  5658. if (wParam != WA_INACTIVE)
  5659. {
  5660. Unhide();
  5661. }
  5662. else
  5663. {
  5664. // When tray is deactivated, remove our keyboard cues:
  5665. //
  5666. SendMessage(hwnd, WM_CHANGEUISTATE,
  5667. MAKEWPARAM(UIS_SET, UISF_HIDEFOCUS | UISF_HIDEACCEL), 0);
  5668. IUnknown_UIActivateIO(_ptbs, FALSE, NULL);
  5669. }
  5670. //
  5671. // Tray activation is a good time to do a reality check
  5672. // (make sure "always-on-top" agrees with actual window
  5673. // position, make sure there are no ghost buttons, etc).
  5674. //
  5675. RealityCheck();
  5676. goto L_default;
  5677. case WM_WTSSESSION_CHANGE:
  5678. {
  5679. lres = _OnSessionChange(wParam, lParam);
  5680. break;
  5681. }
  5682. case WM_THEMECHANGED:
  5683. {
  5684. if (_hTheme)
  5685. {
  5686. CloseThemeData(_hTheme);
  5687. _hTheme = NULL;
  5688. }
  5689. if (wParam)
  5690. {
  5691. _hTheme = OpenThemeData(_hwnd, c_wzTaskbarTheme);
  5692. _fShowSizingBarAlways = (_uAutoHide & AH_ON) ? TRUE : FALSE;
  5693. if (_hTheme)
  5694. {
  5695. GetThemeBool(_hTheme, 0, 0, TMT_ALWAYSSHOWSIZINGBAR, &_fShowSizingBarAlways);
  5696. }
  5697. _UpdateVertical(_uStuckPlace, TRUE);
  5698. // Force Refresh of frame
  5699. SetWindowPos(_hwnd, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
  5700. }
  5701. // Force the start button to recalc its size
  5702. _sizeStart.cx = 0;
  5703. _StartButtonReset();
  5704. InvalidateRect(_hwnd, NULL, TRUE);
  5705. // Force the Start Pane to rebuild with new theme
  5706. ::PostMessage(_hwnd, SBM_REBUILDMENU, 0, 0);
  5707. SetWindowStyle(_hwnd, WS_BORDER | WS_THICKFRAME, !_hTheme);
  5708. }
  5709. break;
  5710. case TM_WORKSTATIONLOCKED:
  5711. {
  5712. // Desktop locked status changed...
  5713. BOOL fIsDesktopLocked = (BOOL) wParam;
  5714. if (_fIsDesktopLocked != fIsDesktopLocked)
  5715. {
  5716. _fIsDesktopLocked = fIsDesktopLocked;
  5717. _fIsLogoff = FALSE;
  5718. _RecomputeAllWorkareas();
  5719. PostMessage(_hwndNotify, TNM_WORKSTATIONLOCKED, wParam, 0);
  5720. }
  5721. }
  5722. break;
  5723. case TM_SHOWTRAYBALLOON:
  5724. PostMessage(_hwndNotify, TNM_SHOWTRAYBALLOON, wParam, 0);
  5725. break;
  5726. case TM_STARTMENUDISMISSED:
  5727. // 107561 - call CoFreeUnusedLibraries() peridically to free up dlls - ZekeL - 4-MAY-2001
  5728. // specifically to support MSONSEXT (webfolders) being used in RecentDocs
  5729. // after a file has been opened via webfolders. we get the icon via
  5730. // their namespace but then COM holds on to the DLL for a while (forever?)
  5731. // calling CoFreeUnusedLibraries() does the trick
  5732. SetTimer(_hwnd, IDT_COFREEUNUSED, 3 * 60 * 1000, NULL);
  5733. break;
  5734. case MM_MIXM_CONTROL_CHANGE:
  5735. Mixer_ControlChange(wParam, lParam);
  5736. break;
  5737. default:
  5738. L_default:
  5739. if (uMsg == GetDDEExecMsg())
  5740. {
  5741. ASSERT(lParam && 0 == ((LPNMHDR)lParam)->idFrom);
  5742. DDEHandleViewFolderNotify(NULL, _hwnd, (LPNMVIEWFOLDER)lParam);
  5743. LocalFree((LPNMVIEWFOLDER)lParam);
  5744. return TRUE;
  5745. }
  5746. else if (uMsg == _uStartButtonBalloonTip)
  5747. {
  5748. _ShowStartButtonToolTip();
  5749. }
  5750. else if (uMsg == _uLogoffUser)
  5751. {
  5752. // Log off the current user (message from U&P control panel)
  5753. ExitWindowsEx(EWX_LOGOFF, 0);
  5754. }
  5755. else if (uMsg == _uMsgEnableUserTrackedBalloonTips)
  5756. {
  5757. PostMessage(_hwndNotify, TNM_ENABLEUSERTRACKINGINFOTIPS, wParam, 0);
  5758. }
  5759. else if (uMsg == _uWinMM_DeviceChange)
  5760. {
  5761. Mixer_MMDeviceChange();
  5762. }
  5763. DoDefault:
  5764. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  5765. }
  5766. return lres;
  5767. }
  5768. void CTray::_DoExitWindows(HWND hwnd)
  5769. {
  5770. static BOOL s_fShellShutdown = FALSE;
  5771. if (!s_fShellShutdown)
  5772. {
  5773. if (_Restricted(hwnd, REST_NOCLOSE))
  5774. return;
  5775. {
  5776. UEMFireEvent(&UEMIID_SHELL, UEME_CTLSESSION, UEMF_XEVENT, FALSE, -1);
  5777. // really #ifdef DEBUG, but want for testing
  5778. // however can't do unconditionally due to perf
  5779. if (ERROR_SUCCESS == SHGetValue(HKEY_CURRENT_USER, REGSTR_EXPLORER_ADVANCED, TEXT("StartMenuForceRefresh"),
  5780. NULL, NULL, NULL) || GetAsyncKeyState(VK_SHIFT) < 0)
  5781. {
  5782. _RefreshStartMenu();
  5783. }
  5784. }
  5785. _SaveTrayAndDesktop();
  5786. _uModalMode = MM_SHUTDOWN;
  5787. ExitWindowsDialog(hwnd);
  5788. // NB User can have problems if the focus is forcebly changed to the desktop while
  5789. // shutting down since it tries to serialize the whole process by making windows sys-modal.
  5790. // If we hit this code at just the wrong moment (ie just after the sharing dialog appears)
  5791. // the desktop will become sys-modal so you can't switch back to the sharing dialog
  5792. // and you won't be able to shutdown.
  5793. // SetForegroundWindow(hwnd);
  5794. // SetFocus(hwnd);
  5795. _uModalMode = 0;
  5796. if ((GetKeyState(VK_SHIFT) < 0) && (GetKeyState(VK_CONTROL) < 0) && (GetKeyState(VK_MENU) < 0))
  5797. {
  5798. // User cancelled...
  5799. // The shift key means exit the tray...
  5800. // ??? - Used to destroy all cabinets...
  5801. // PostQuitMessage(0);
  5802. g_fFakeShutdown = TRUE; // Don't blow away session state; the session will survive
  5803. TraceMsg(TF_TRAY, "c.dew: Posting quit message for tid=%#08x hwndDesk=%x(IsWnd=%d) hwndTray=%x(IsWnd=%d)", GetCurrentThreadId(),
  5804. v_hwndDesktop,IsWindow(v_hwndDesktop), _hwnd,IsWindow(_hwnd));
  5805. // 1 means close all the shell windows too
  5806. PostMessage(v_hwndDesktop, WM_QUIT, 0, 1);
  5807. PostMessage(_hwnd, WM_QUIT, 0, 0);
  5808. s_fShellShutdown = TRUE;
  5809. }
  5810. }
  5811. }
  5812. void CTray::_SaveTray(void)
  5813. {
  5814. if (SHRestricted(REST_NOSAVESET))
  5815. return;
  5816. if (SHRestricted(REST_CLEARRECENTDOCSONEXIT))
  5817. ClearRecentDocumentsAndMRUStuff(FALSE);
  5818. //
  5819. // Don't persist tray stuff if in safe mode. We want this
  5820. // to be a temporary mode where the UI settings don't stick.
  5821. //
  5822. if (GetSystemMetrics(SM_CLEANBOOT) == 0)
  5823. {
  5824. _SaveTrayStuff();
  5825. }
  5826. }
  5827. DWORD WINAPI CTray::PropertiesThreadProc(void* pv)
  5828. {
  5829. return c_tray._PropertiesThreadProc(PtrToUlong(pv));
  5830. }
  5831. DWORD CTray::_PropertiesThreadProc(DWORD dwFlags)
  5832. {
  5833. HWND hwnd;
  5834. RECT rc;
  5835. DWORD dwExStyle = WS_EX_TOOLWINDOW;
  5836. GetWindowRect(_hwndStart, &rc);
  5837. dwExStyle |= IS_BIDI_LOCALIZED_SYSTEM() ? dwExStyleRTLMirrorWnd : 0L;
  5838. _hwndProp = hwnd = CreateWindowEx(dwExStyle, TEXT("static"), NULL, 0 ,
  5839. rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, hinstCabinet, NULL);
  5840. #define IDI_STTASKBR 40 // stolen from shell32\ids.h
  5841. if (_hwndProp)
  5842. {
  5843. // Get the Alt+Tab icon right
  5844. HICON hicoStub = LoadIcon(GetModuleHandle(TEXT("SHELL32")), MAKEINTRESOURCE(IDI_STTASKBR));
  5845. SendMessage(_hwndProp, WM_SETICON, ICON_BIG, (LPARAM)hicoStub);
  5846. // SwitchToThisWindow(hwnd, TRUE);
  5847. // SetForegroundWindow(hwnd);
  5848. DoTaskBarProperties(hwnd, dwFlags);
  5849. _hwndProp = NULL;
  5850. DestroyWindow(hwnd);
  5851. if (hicoStub)
  5852. DestroyIcon(hicoStub);
  5853. }
  5854. return TRUE;
  5855. }
  5856. #define RUNWAITSECS 5
  5857. void CTray::DoProperties(DWORD dwFlags)
  5858. {
  5859. if (!_Restricted(_hwnd, REST_NOSETTASKBAR))
  5860. {
  5861. int i = RUNWAITSECS;
  5862. while (_hwndProp == ((HWND)-1) &&i--)
  5863. {
  5864. // we're in the process of coming up. wait
  5865. Sleep(1000);
  5866. }
  5867. // failed! blow it off.
  5868. if (_hwndProp == (HWND)-1)
  5869. {
  5870. _hwndProp = NULL;
  5871. }
  5872. if (_hwndProp)
  5873. {
  5874. // there's a window out there... activate it
  5875. SwitchToThisWindow(GetLastActivePopup(_hwndProp), TRUE);
  5876. }
  5877. else
  5878. {
  5879. _hwndProp = (HWND)-1;
  5880. if (!SHCreateThread(PropertiesThreadProc, IntToPtr(dwFlags), CTF_COINIT, NULL))
  5881. {
  5882. _hwndProp = NULL;
  5883. }
  5884. }
  5885. }
  5886. }
  5887. BOOL CTray::TileEnumProc(HWND hwnd, LPARAM lParam)
  5888. {
  5889. CTray* ptray = (CTray*)lParam;
  5890. if (IsWindowVisible(hwnd) && !IsIconic(hwnd) &&
  5891. ((GetWindowLong(hwnd, GWL_STYLE) & WS_CAPTION) == WS_CAPTION) &&
  5892. (hwnd != ptray->_hwnd) && hwnd != v_hwndDesktop)
  5893. {
  5894. return FALSE; // we *can* tile this guy
  5895. }
  5896. return TRUE; // we *cannot* tile this guy
  5897. }
  5898. HMENU CTray::BuildContextMenu(BOOL fIncludeTime)
  5899. {
  5900. HMENU hmContext = LoadMenuPopup(MAKEINTRESOURCE(MENU_TRAYCONTEXT));
  5901. if (!hmContext)
  5902. return NULL;
  5903. if (fIncludeTime)
  5904. {
  5905. if (_trayNotify.GetIsNoTrayItemsDisplayPolicyEnabled())
  5906. {
  5907. // We know the position of IDM_NOTIFYCUST from the menu resource...
  5908. DeleteMenu(hmContext, 1, MF_BYPOSITION);
  5909. }
  5910. else
  5911. {
  5912. UINT uEnable = MF_BYCOMMAND;
  5913. if (_trayNotify.GetIsNoAutoTrayPolicyEnabled() || !_trayNotify.GetIsAutoTrayEnabledByUser())
  5914. {
  5915. uEnable |= MFS_DISABLED;
  5916. }
  5917. else
  5918. {
  5919. uEnable |= MFS_ENABLED;
  5920. }
  5921. EnableMenuItem(hmContext, IDM_NOTIFYCUST, uEnable);
  5922. }
  5923. }
  5924. else
  5925. {
  5926. INSTRUMENT_STATECHANGE(SHCNFI_STATE_TRAY_CONTEXT);
  5927. for (int i = 2; i >= 0; i--) // separator, IDM_SETTIME, IDM_NOTIFYCUST,
  5928. {
  5929. DeleteMenu(hmContext, i, MF_BYPOSITION);
  5930. }
  5931. }
  5932. CheckMenuItem(hmContext, IDM_LOCKTASKBAR,
  5933. MF_BYCOMMAND | (_fCanSizeMove ? MF_UNCHECKED : MF_CHECKED));
  5934. // Don't let users accidentally check lock the taskbar when the taskbar is zero height
  5935. RECT rc;
  5936. GetClientRect(_hwnd, &rc);
  5937. EnableMenuItem(hmContext, IDM_LOCKTASKBAR,
  5938. MF_BYCOMMAND | ((_IsSizeMoveRestricted() || (RECTHEIGHT(rc) == 0)) ? MFS_DISABLED : MFS_ENABLED));
  5939. if (!_fUndoEnabled || !_pPositions)
  5940. {
  5941. DeleteMenu(hmContext, IDM_UNDO, MF_BYCOMMAND);
  5942. }
  5943. else
  5944. {
  5945. TCHAR szTemplate[30];
  5946. TCHAR szCommand[30];
  5947. TCHAR szMenu[64];
  5948. LoadString(hinstCabinet, IDS_UNDOTEMPLATE, szTemplate, ARRAYSIZE(szTemplate));
  5949. LoadString(hinstCabinet, _pPositions->idRes, szCommand, ARRAYSIZE(szCommand));
  5950. StringCchPrintf(szMenu, ARRAYSIZE(szMenu), szTemplate, szCommand);
  5951. ModifyMenu(hmContext, IDM_UNDO, MF_BYCOMMAND | MF_STRING, IDM_UNDO, szMenu);
  5952. }
  5953. if (g_fDesktopRaised)
  5954. {
  5955. TCHAR szHideDesktop[64];
  5956. LoadString(hinstCabinet, IDS_HIDEDESKTOP, szHideDesktop, ARRAYSIZE(szHideDesktop));
  5957. ModifyMenu(hmContext, IDM_TOGGLEDESKTOP, MF_BYCOMMAND | MF_STRING, IDM_TOGGLEDESKTOP, szHideDesktop);
  5958. }
  5959. if (!_CanTileAnyWindows())
  5960. {
  5961. EnableMenuItem(hmContext, IDM_CASCADE, MFS_GRAYED | MF_BYCOMMAND);
  5962. EnableMenuItem(hmContext, IDM_HORIZTILE, MFS_GRAYED | MF_BYCOMMAND);
  5963. EnableMenuItem(hmContext, IDM_VERTTILE, MFS_GRAYED | MF_BYCOMMAND);
  5964. }
  5965. HKEY hKeyPolicy;
  5966. if (RegOpenKeyEx(HKEY_CURRENT_USER,
  5967. TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System"),
  5968. 0, KEY_READ, &hKeyPolicy) == ERROR_SUCCESS)
  5969. {
  5970. DWORD dwType, dwData = 0, dwSize = sizeof(dwData);
  5971. RegQueryValueEx(hKeyPolicy, TEXT("DisableTaskMgr"), NULL,
  5972. &dwType, (LPBYTE) &dwData, &dwSize);
  5973. RegCloseKey(hKeyPolicy);
  5974. if (dwData)
  5975. EnableMenuItem(hmContext, IDM_SHOWTASKMAN, MFS_GRAYED | MF_BYCOMMAND);
  5976. }
  5977. return hmContext;
  5978. }
  5979. void CTray::ContextMenuInvoke(int idCmd)
  5980. {
  5981. if (idCmd)
  5982. {
  5983. if (idCmd < IDM_TRAYCONTEXTFIRST)
  5984. {
  5985. BandSite_HandleMenuCommand(_ptbs, idCmd);
  5986. }
  5987. else
  5988. {
  5989. _Command(idCmd);
  5990. }
  5991. }
  5992. }
  5993. //
  5994. // CTray::AsyncSaveSettings
  5995. //
  5996. // We need to save our tray settings, but there may be a bunch
  5997. // of these calls coming, (at startup time or when dragging
  5998. // items in the task bar) so gather them up into one save that
  5999. // will happen in at least 2 seconds.
  6000. //
  6001. void CTray::AsyncSaveSettings()
  6002. {
  6003. if (!_fHandledDelayBootStuff) // no point in saving if we're not done booting
  6004. return;
  6005. KillTimer(_hwnd, IDT_SAVESETTINGS);
  6006. SetTimer(_hwnd, IDT_SAVESETTINGS, 2000, NULL);
  6007. }
  6008. void CTray::_ContextMenu(DWORD dwPos, BOOL fIncludeTime)
  6009. {
  6010. POINT pt = {LOWORD(dwPos), HIWORD(dwPos)};
  6011. SwitchToThisWindow(_hwnd, TRUE);
  6012. SetForegroundWindow(_hwnd);
  6013. SendMessage(_hwndTrayTips, TTM_ACTIVATE, FALSE, 0L);
  6014. if (dwPos != (DWORD)-1 &&
  6015. IsChildOrHWND(_hwndRebar, WindowFromPoint(pt)))
  6016. {
  6017. // if the context menu came from below us, reflect down
  6018. BandSite_HandleMessage(_ptbs, _hwnd, WM_CONTEXTMENU, 0, dwPos, NULL);
  6019. }
  6020. else
  6021. {
  6022. HMENU hmenu;
  6023. if (dwPos == (DWORD)-1)
  6024. {
  6025. HWND hwnd = GetFocus();
  6026. pt.x = pt.y = 0;
  6027. ClientToScreen(hwnd, &pt);
  6028. dwPos = MAKELONG(pt.x, pt.y);
  6029. }
  6030. hmenu = BuildContextMenu(fIncludeTime);
  6031. if (hmenu)
  6032. {
  6033. int idCmd;
  6034. BandSite_AddMenus(_ptbs, hmenu, 0, 0, IDM_TRAYCONTEXTFIRST);
  6035. idCmd = TrackPopupMenu(hmenu, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN,
  6036. GET_X_LPARAM(dwPos), GET_Y_LPARAM(dwPos), 0, _hwnd, NULL);
  6037. DestroyMenu(hmenu);
  6038. ContextMenuInvoke(idCmd);
  6039. }
  6040. }
  6041. SendMessage(_hwndTrayTips, TTM_ACTIVATE, TRUE, 0L);
  6042. }
  6043. void _RunFileDlg(HWND hwnd, UINT idIcon, LPCITEMIDLIST pidlWorkingDir, UINT idTitle, UINT idPrompt, DWORD dwFlags)
  6044. {
  6045. HICON hIcon;
  6046. LPCTSTR lpszTitle;
  6047. LPCTSTR lpszPrompt;
  6048. TCHAR szTitle[256];
  6049. TCHAR szPrompt[256];
  6050. TCHAR szWorkingDir[MAX_PATH];
  6051. dwFlags |= RFD_USEFULLPATHDIR;
  6052. szWorkingDir[0] = 0;
  6053. hIcon = idIcon ? LoadIcon(hinstCabinet, MAKEINTRESOURCE(idIcon)) : NULL;
  6054. if (!pidlWorkingDir || !SHGetPathFromIDList(pidlWorkingDir, szWorkingDir))
  6055. {
  6056. // This is either the Tray, or some non-file system folder, so
  6057. // we will "suggest" the Desktop as a working dir, but if the
  6058. // user types a full path, we will use that instead. This is
  6059. // what WIN31 Progman did (except they started in the Windows
  6060. // dir instead of the Desktop).
  6061. goto UseDesktop;
  6062. }
  6063. // if it's a removable dir, make sure it's still there
  6064. if (szWorkingDir[0])
  6065. {
  6066. int idDrive = PathGetDriveNumber(szWorkingDir);
  6067. if ((idDrive != -1))
  6068. {
  6069. UINT dtype = DriveType(idDrive);
  6070. if (((dtype == DRIVE_REMOVABLE) || (dtype == DRIVE_CDROM))
  6071. && !PathFileExists(szWorkingDir))
  6072. {
  6073. goto UseDesktop;
  6074. }
  6075. }
  6076. }
  6077. //
  6078. // Check if this is a directory. Notice that it could be a in-place
  6079. // navigated document.
  6080. //
  6081. if (PathIsDirectory(szWorkingDir)) {
  6082. goto UseWorkingDir;
  6083. }
  6084. UseDesktop:
  6085. SHGetSpecialFolderPath(hwnd, szWorkingDir, CSIDL_DESKTOPDIRECTORY, FALSE);
  6086. UseWorkingDir:
  6087. if (idTitle)
  6088. {
  6089. LoadString(hinstCabinet, idTitle, szTitle, ARRAYSIZE(szTitle));
  6090. lpszTitle = szTitle;
  6091. }
  6092. else
  6093. lpszTitle = NULL;
  6094. if (idPrompt)
  6095. {
  6096. LoadString(hinstCabinet, idPrompt, szPrompt, ARRAYSIZE(szPrompt));
  6097. lpszPrompt = szPrompt;
  6098. }
  6099. else
  6100. lpszPrompt = NULL;
  6101. RunFileDlg(hwnd, hIcon, szWorkingDir, lpszTitle, lpszPrompt, dwFlags);
  6102. }
  6103. BOOL CTray::SavePosEnumProc(HWND hwnd, LPARAM lParam)
  6104. {
  6105. // dont need to entercritical here since we are only ever
  6106. // called from SaveWindowPositions, which as already entered the critical secion
  6107. // for _pPositions
  6108. ASSERTCRITICAL;
  6109. CTray* ptray = (CTray*)lParam;
  6110. ASSERT(ptray->_pPositions);
  6111. if (IsWindowVisible(hwnd) &&
  6112. (hwnd != ptray->_hwnd) &&
  6113. (hwnd != v_hwndDesktop))
  6114. {
  6115. HWNDANDPLACEMENT hap;
  6116. hap.wp.length = sizeof(WINDOWPLACEMENT);
  6117. GetWindowPlacement(hwnd, &hap.wp);
  6118. if (hap.wp.showCmd != SW_SHOWMINIMIZED)
  6119. {
  6120. hap.hwnd = hwnd;
  6121. hap.fRestore = TRUE;
  6122. DSA_AppendItem(ptray->_pPositions->hdsaWP, &hap);
  6123. }
  6124. }
  6125. return TRUE;
  6126. }
  6127. void CTray::SaveWindowPositions(UINT idRes)
  6128. {
  6129. ENTERCRITICAL;
  6130. if (_pPositions)
  6131. {
  6132. if (_pPositions->hdsaWP)
  6133. DSA_DeleteAllItems(_pPositions->hdsaWP);
  6134. }
  6135. else
  6136. {
  6137. _pPositions = (LPWINDOWPOSITIONS)LocalAlloc(LPTR, sizeof(WINDOWPOSITIONS));
  6138. if (_pPositions)
  6139. {
  6140. _pPositions->hdsaWP = DSA_Create(sizeof(HWNDANDPLACEMENT), 4);
  6141. }
  6142. }
  6143. if (_pPositions)
  6144. {
  6145. _pPositions->idRes = idRes;
  6146. // CheckWindowPositions tested for these...
  6147. ASSERT(idRes == IDS_MINIMIZEALL || idRes == IDS_CASCADE || idRes == IDS_TILE);
  6148. EnumWindows(SavePosEnumProc, (LPARAM)this);
  6149. }
  6150. LEAVECRITICAL;
  6151. }
  6152. typedef struct
  6153. {
  6154. LPWINDOWPOSITIONS pPositions;
  6155. HWND hwndDesktop;
  6156. HWND hwndTray;
  6157. BOOL fPostLowerDesktop;
  6158. } RESTOREWNDDATA, *PRESTOREWNDDATA;
  6159. DWORD WINAPI RestoreWndPosThreadProc(void* pv)
  6160. {
  6161. PRESTOREWNDDATA pWndData = (PRESTOREWNDDATA)pv;
  6162. if (pWndData && pWndData->pPositions)
  6163. {
  6164. LPHWNDANDPLACEMENT phap;
  6165. LONG iAnimate;
  6166. ANIMATIONINFO ami;
  6167. ami.cbSize = sizeof(ANIMATIONINFO);
  6168. SystemParametersInfo(SPI_GETANIMATION, sizeof(ami), &ami, FALSE);
  6169. iAnimate = ami.iMinAnimate;
  6170. ami.iMinAnimate = FALSE;
  6171. SystemParametersInfo(SPI_SETANIMATION, sizeof(ami), &ami, FALSE);
  6172. if (pWndData->pPositions->hdsaWP)
  6173. {
  6174. for (int i = DSA_GetItemCount(pWndData->pPositions->hdsaWP) - 1 ; i >= 0; i--)
  6175. {
  6176. phap = (LPHWNDANDPLACEMENT)DSA_GetItemPtr(pWndData->pPositions->hdsaWP, i);
  6177. if (IsWindow(phap->hwnd))
  6178. {
  6179. #ifndef WPF_ASYNCWINDOWPLACEMENT
  6180. #define WPF_ASYNCWINDOWPLACEMENT 0x0004
  6181. #endif
  6182. // pass this async.
  6183. if (!IsHungAppWindow(phap->hwnd))
  6184. {
  6185. phap->wp.length = sizeof(WINDOWPLACEMENT);
  6186. phap->wp.flags |= WPF_ASYNCWINDOWPLACEMENT;
  6187. if (phap->fRestore)
  6188. {
  6189. // only restore those guys we've actually munged.
  6190. SetWindowPlacement(phap->hwnd, &phap->wp);
  6191. }
  6192. }
  6193. }
  6194. }
  6195. }
  6196. ami.iMinAnimate = iAnimate;
  6197. SystemParametersInfo(SPI_SETANIMATION, sizeof(ami), &ami, FALSE);
  6198. _DestroySavedWindowPositions(pWndData->pPositions);
  6199. if (pWndData->fPostLowerDesktop)
  6200. {
  6201. PostMessage(pWndData->hwndDesktop, DTM_RAISE, (WPARAM)pWndData->hwndTray, DTRF_LOWER);
  6202. }
  6203. delete pWndData;
  6204. }
  6205. return 1;
  6206. }
  6207. BOOL CTray::_RestoreWindowPositions(BOOL fPostLowerDesktop)
  6208. {
  6209. BOOL fRet = FALSE;
  6210. ENTERCRITICAL;
  6211. if (_pPositions)
  6212. {
  6213. PRESTOREWNDDATA pWndData = new RESTOREWNDDATA;
  6214. if (pWndData)
  6215. {
  6216. pWndData->pPositions = _pPositions;
  6217. pWndData->fPostLowerDesktop = fPostLowerDesktop;
  6218. pWndData->hwndDesktop = v_hwndDesktop;
  6219. pWndData->hwndTray = _hwnd;
  6220. if (SHCreateThread(RestoreWndPosThreadProc, pWndData, 0, NULL))
  6221. {
  6222. fRet = TRUE;
  6223. _pPositions = NULL;
  6224. }
  6225. else
  6226. {
  6227. delete pWndData;
  6228. }
  6229. }
  6230. }
  6231. LEAVECRITICAL;
  6232. return fRet;
  6233. }
  6234. void _DestroySavedWindowPositions(LPWINDOWPOSITIONS pPositions)
  6235. {
  6236. ENTERCRITICAL;
  6237. if (pPositions)
  6238. {
  6239. // free the global struct
  6240. DSA_Destroy(pPositions->hdsaWP);
  6241. LocalFree(pPositions);
  6242. }
  6243. LEAVECRITICAL;
  6244. }
  6245. void CTray::HandleWindowDestroyed(HWND hwnd)
  6246. {
  6247. // enter critical section so we dont corrupt the hdsaWP
  6248. ENTERCRITICAL;
  6249. if (_pPositions)
  6250. {
  6251. int i = DSA_GetItemCount(_pPositions->hdsaWP) - 1;
  6252. for (; i >= 0; i--) {
  6253. LPHWNDANDPLACEMENT phap = (LPHWNDANDPLACEMENT)DSA_GetItemPtr(_pPositions->hdsaWP, i);
  6254. if (phap->hwnd == hwnd || !IsWindow(phap->hwnd)) {
  6255. DSA_DeleteItem(_pPositions->hdsaWP, i);
  6256. }
  6257. }
  6258. if (!DSA_GetItemCount(_pPositions->hdsaWP))
  6259. {
  6260. _DestroySavedWindowPositions(_pPositions);
  6261. _pPositions = NULL;
  6262. }
  6263. }
  6264. LEAVECRITICAL;
  6265. }
  6266. // Allow us to bump the activation of the run dlg hidden window.
  6267. // Certain apps (Norton Desktop setup) use the active window at RunDlg time
  6268. // as the parent for their dialogs. If that window disappears then they fault.
  6269. // We don't want the tray to get the activation coz it will cause it to appeare
  6270. // if you're in auto-hide mode.
  6271. LRESULT WINAPI RunDlgStaticSubclassWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  6272. {
  6273. switch (uMsg)
  6274. {
  6275. case WM_ACTIVATE:
  6276. if (wParam == WA_ACTIVE)
  6277. {
  6278. // Bump the activation to the desktop.
  6279. if (v_hwndDesktop)
  6280. {
  6281. SetForegroundWindow(v_hwndDesktop);
  6282. return 0;
  6283. }
  6284. }
  6285. break;
  6286. case WM_NOTIFY:
  6287. // relay it to the tray
  6288. return SendMessage(v_hwndTray, uMsg, wParam, lParam);
  6289. }
  6290. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  6291. }
  6292. DWORD WINAPI CTray::RunDlgThreadProc(void *pv)
  6293. {
  6294. return c_tray._RunDlgThreadProc((HANDLE)pv);
  6295. }
  6296. BOOL _IsBrowserWindow(HWND hwnd)
  6297. {
  6298. static const TCHAR* c_szClasses[] =
  6299. {
  6300. TEXT("ExploreWClass"),
  6301. TEXT("CabinetWClass"),
  6302. TEXT("IEFrame"),
  6303. };
  6304. TCHAR szClass[32];
  6305. GetClassName(hwnd, szClass, ARRAYSIZE(szClass));
  6306. for (int i = 0; i < ARRAYSIZE(c_szClasses); i++)
  6307. {
  6308. if (lstrcmpi(szClass, c_szClasses[i]) == 0)
  6309. {
  6310. return TRUE;
  6311. }
  6312. }
  6313. return FALSE;
  6314. }
  6315. DWORD CTray::_RunDlgThreadProc(HANDLE hdata)
  6316. {
  6317. RECT rc, rcTemp;
  6318. HRESULT hrInit = SHCoInitialize();
  6319. // 99/04/12 #316424 vtan: Get the rectangle for the "Start" button.
  6320. // If this is off the screen the tray is probably in auto hide mode.
  6321. // In this case offset the rectangle into the monitor where it should
  6322. // belong. This may be up, down, left or right depending on the
  6323. // position of the tray.
  6324. // First thing to do is to establish the dimensions of the monitor on
  6325. // which the tray resides. If no monitor can be found then use the
  6326. // primary monitor.
  6327. MONITORINFO monitorInfo;
  6328. monitorInfo.cbSize = sizeof(monitorInfo);
  6329. if (GetMonitorInfo(_hmonStuck, &monitorInfo) == 0)
  6330. {
  6331. TBOOL(SystemParametersInfo(SPI_GETWORKAREA, 0, &monitorInfo.rcMonitor, 0));
  6332. }
  6333. // Get the co-ordinates of the "Start" button.
  6334. GetWindowRect(_hwndStart, &rc);
  6335. // Look for an intersection in the monitor.
  6336. if (IntersectRect(&rcTemp, &rc, &monitorInfo.rcMonitor) == 0)
  6337. {
  6338. LONG lDeltaX, lDeltaY;
  6339. // Does not exist in the monitor. Move the co-ordinates by the
  6340. // width or height of the tray so that it does.
  6341. // This bizarre arithmetic is used because _ComputeHiddenRect()
  6342. // takes into account the frame and that right/bottom of RECT
  6343. // is exclusive in GDI.
  6344. lDeltaX = _sStuckWidths.cx - g_cxFrame;
  6345. lDeltaY = _sStuckWidths.cy - g_cyFrame;
  6346. if (rc.left < monitorInfo.rcMonitor.left)
  6347. {
  6348. --lDeltaX;
  6349. lDeltaY = 0;
  6350. }
  6351. else if (rc.top < monitorInfo.rcMonitor.top)
  6352. {
  6353. lDeltaX = 0;
  6354. --lDeltaY;
  6355. }
  6356. else if (rc.right > monitorInfo.rcMonitor.right)
  6357. {
  6358. lDeltaX = -lDeltaX;
  6359. lDeltaY = 0;
  6360. }
  6361. else if (rc.bottom > monitorInfo.rcMonitor.bottom)
  6362. {
  6363. lDeltaX = 0;
  6364. lDeltaY = -lDeltaY;
  6365. }
  6366. TBOOL(OffsetRect(&rc, lDeltaX, lDeltaY));
  6367. }
  6368. HWND hwnd = CreateWindowEx(WS_EX_TOOLWINDOW, TEXT("static"), NULL, 0 ,
  6369. rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, hinstCabinet, NULL);
  6370. if (hwnd)
  6371. {
  6372. BOOL fSimple = FALSE;
  6373. HANDLE hMemWorkDir = NULL;
  6374. LPITEMIDLIST pidlWorkingDir = NULL;
  6375. // Subclass it.
  6376. SubclassWindow(hwnd, RunDlgStaticSubclassWndProc);
  6377. if (hdata)
  6378. SetProp(hwnd, TEXT("WaitingThreadID"), hdata);
  6379. if (!SHRestricted(REST_STARTRUNNOHOMEPATH))
  6380. {
  6381. // On NT, we like to start apps in the HOMEPATH directory. This
  6382. // should be the current directory for the current process.
  6383. TCHAR szDir[MAX_PATH];
  6384. TCHAR szPath[MAX_PATH];
  6385. GetEnvironmentVariable(TEXT("HOMEDRIVE"), szDir, ARRAYSIZE(szDir));
  6386. GetEnvironmentVariable(TEXT("HOMEPATH"), szPath, ARRAYSIZE(szPath));
  6387. if (PathAppend(szDir, szPath) && PathIsDirectory(szDir))
  6388. {
  6389. pidlWorkingDir = SHSimpleIDListFromPath(szDir);
  6390. if (pidlWorkingDir)
  6391. {
  6392. // free it the "simple" way...
  6393. fSimple = TRUE;
  6394. }
  6395. }
  6396. }
  6397. if (!pidlWorkingDir)
  6398. {
  6399. // If the last active window was a folder/explorer window with the
  6400. // desktop as root, use its as the current dir
  6401. if (_hwndLastActive)
  6402. {
  6403. ENTERCRITICAL;
  6404. if (_hwndLastActive && !IsMinimized(_hwndLastActive) && _IsBrowserWindow(_hwndLastActive))
  6405. {
  6406. SendMessageTimeout(_hwndLastActive, CWM_CLONEPIDL, GetCurrentProcessId(), 0, SMTO_ABORTIFHUNG | SMTO_BLOCK, 500, (DWORD_PTR*)&hMemWorkDir);
  6407. pidlWorkingDir = (LPITEMIDLIST)SHLockShared(hMemWorkDir, GetCurrentProcessId());
  6408. }
  6409. LEAVECRITICAL;
  6410. }
  6411. }
  6412. _RunFileDlg(hwnd, 0, pidlWorkingDir, 0, 0, 0);
  6413. if (pidlWorkingDir)
  6414. {
  6415. if (fSimple)
  6416. {
  6417. ILFree(pidlWorkingDir);
  6418. }
  6419. else
  6420. {
  6421. SHUnlockShared(pidlWorkingDir);
  6422. }
  6423. }
  6424. if (hMemWorkDir)
  6425. {
  6426. ASSERT(fSimple == FALSE);
  6427. SHFreeShared(hMemWorkDir, GetCurrentProcessId());
  6428. }
  6429. if (hdata)
  6430. {
  6431. RemoveProp(hwnd, TEXT("WaitingThreadID"));
  6432. }
  6433. DestroyWindow(hwnd);
  6434. }
  6435. SHCoUninitialize(hrInit);
  6436. return TRUE;
  6437. }
  6438. void CTray::_RunDlg()
  6439. {
  6440. HANDLE hEvent;
  6441. void *pvThreadParam;
  6442. if (!_Restricted(_hwnd, REST_NORUN))
  6443. {
  6444. TCHAR szRunDlgTitle[MAX_PATH];
  6445. HWND hwndOldRun;
  6446. LoadString(hinstCabinet, IDS_RUNDLGTITLE, szRunDlgTitle, ARRAYSIZE(szRunDlgTitle));
  6447. // See if there is already a run dialog up, and if so, try to activate it
  6448. hwndOldRun = FindWindow(WC_DIALOG, szRunDlgTitle);
  6449. if (hwndOldRun)
  6450. {
  6451. DWORD dwPID;
  6452. GetWindowThreadProcessId(hwndOldRun, &dwPID);
  6453. if (dwPID == GetCurrentProcessId())
  6454. {
  6455. if (IsWindowVisible(hwndOldRun))
  6456. {
  6457. SetForegroundWindow(hwndOldRun);
  6458. return;
  6459. }
  6460. }
  6461. }
  6462. // Create an event so we can wait for the run dlg to appear before
  6463. // continue - this allows it to capture any type-ahead.
  6464. hEvent = CreateEvent(NULL, TRUE, FALSE, TEXT("MSShellRunDlgReady"));
  6465. if (hEvent)
  6466. pvThreadParam = IntToPtr(GetCurrentThreadId());
  6467. else
  6468. pvThreadParam = NULL;
  6469. if (SHQueueUserWorkItem(RunDlgThreadProc, pvThreadParam, 0, 0, NULL, NULL, TPS_LONGEXECTIME | TPS_DEMANDTHREAD))
  6470. {
  6471. if (hEvent)
  6472. {
  6473. SHProcessMessagesUntilEvent(NULL, hEvent, 10 * 1000);
  6474. DebugMsg(DM_TRACE, TEXT("c.t_rd: Done waiting."));
  6475. }
  6476. }
  6477. if (hEvent)
  6478. CloseHandle(hEvent);
  6479. }
  6480. }
  6481. void CTray::_ExploreCommonStartMenu(BOOL bExplore)
  6482. {
  6483. TCHAR szPath[MAX_PATH];
  6484. TCHAR szCmdLine[MAX_PATH + 50];
  6485. //
  6486. // Get the common start menu path.
  6487. //
  6488. // we want to force the directory to exist, but not on W95 machines
  6489. if (!SHGetSpecialFolderPath(NULL, szPath, CSIDL_COMMON_STARTMENU, FALSE))
  6490. {
  6491. return;
  6492. }
  6493. //
  6494. // If we are starting in explorer view, then the command line
  6495. // has a "/e, " before the quoted diretory.
  6496. //
  6497. if (bExplore)
  6498. {
  6499. StringCchCopy(szCmdLine, ARRAYSIZE(szCmdLine), TEXT("explorer.exe /e, \""));
  6500. }
  6501. else
  6502. {
  6503. StringCchCopy(szCmdLine, ARRAYSIZE(szCmdLine), TEXT("explorer.exe \""));
  6504. }
  6505. StringCchCat(szCmdLine, ARRAYSIZE(szCmdLine), szPath);
  6506. StringCchCat(szCmdLine, ARRAYSIZE(szCmdLine), TEXT("\""));
  6507. // Initialize process startup info
  6508. STARTUPINFO si = {0};
  6509. si.cb = sizeof(si);
  6510. si.dwFlags = STARTF_USESHOWWINDOW;
  6511. si.wShowWindow = SW_SHOWNORMAL;
  6512. // Start explorer
  6513. PROCESS_INFORMATION pi = {0};
  6514. if (CreateProcess(NULL, szCmdLine, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi))
  6515. {
  6516. // Close the process and thread handles
  6517. CloseHandle(pi.hProcess);
  6518. CloseHandle(pi.hThread);
  6519. }
  6520. }
  6521. int CTray::_GetQuickLaunchID()
  6522. {
  6523. int iQLBandID = -1;
  6524. DWORD dwBandID;
  6525. for (int i = 0; (iQLBandID == -1) && SUCCEEDED(_ptbs->EnumBands(i, &dwBandID)); i++)
  6526. {
  6527. if (BandSite_TestBandCLSID(_ptbs, dwBandID, CLSID_ISFBand) == S_OK)
  6528. {
  6529. IUnknown* punk;
  6530. if (SUCCEEDED(_ptbs->GetBandObject(dwBandID, IID_PPV_ARG(IUnknown, &punk))))
  6531. {
  6532. VARIANTARG v = {0};
  6533. v.vt = VT_I4;
  6534. if (SUCCEEDED(IUnknown_Exec(punk, &CLSID_ISFBand, 1, 0, NULL, &v)))
  6535. {
  6536. if ((v.vt == VT_I4) && (CSIDL_APPDATA == (DWORD)v.lVal))
  6537. {
  6538. iQLBandID = (int)dwBandID;
  6539. }
  6540. }
  6541. punk->Release();
  6542. }
  6543. }
  6544. }
  6545. return iQLBandID;
  6546. }
  6547. int CTray::_ToggleQL(int iVisible)
  6548. {
  6549. int iQLBandID = _GetQuickLaunchID();
  6550. bool fOldVisible = (-1 != iQLBandID);
  6551. bool fNewVisible = (0 != iVisible);
  6552. if ((iVisible != -1) && (fNewVisible != fOldVisible))
  6553. {
  6554. if (fNewVisible)
  6555. {
  6556. LPITEMIDLIST pidl;
  6557. if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_APPDATA, &pidl)))
  6558. {
  6559. TCHAR szPath[MAX_PATH];
  6560. SHGetPathFromIDList(pidl, szPath);
  6561. PathCombine(szPath, szPath, L"Microsoft\\Internet Explorer\\Quick Launch");
  6562. ILFree(pidl);
  6563. pidl = ILCreateFromPath(szPath);
  6564. if (pidl)
  6565. {
  6566. IFolderBandPriv *pfbp;
  6567. // create an ISF band to show folders as hotlinks
  6568. if (SUCCEEDED(CoCreateInstance(CLSID_ISFBand, NULL, CLSCTX_INPROC, IID_PPV_ARG(IFolderBandPriv, &pfbp))))
  6569. {
  6570. IShellFolderBand* psfb;
  6571. if (SUCCEEDED(pfbp->QueryInterface(IID_PPV_ARG(IShellFolderBand, &psfb))))
  6572. {
  6573. if (SUCCEEDED(psfb->InitializeSFB(NULL, pidl)))
  6574. {
  6575. pfbp->SetNoText(TRUE);
  6576. VARIANTARG v;
  6577. v.vt = VT_I4;
  6578. v.lVal = CSIDL_APPDATA;
  6579. IUnknown_Exec(psfb, &CLSID_ISFBand, 1, 0, &v, NULL);
  6580. v.lVal = UEMIND_SHELL; // UEMIND_SHELL/BROWSER
  6581. IUnknown_Exec(psfb, &CGID_ShellDocView, SHDVID_UEMLOG, 0, &v, NULL);
  6582. IDeskBand* ptb;
  6583. if (SUCCEEDED(pfbp->QueryInterface(IID_PPV_ARG(IDeskBand, &ptb))))
  6584. {
  6585. HRESULT hr = _ptbs->AddBand(ptb);
  6586. if (SUCCEEDED(hr))
  6587. {
  6588. _ptbs->SetBandState(ShortFromResult(hr), BSSF_NOTITLE, BSSF_NOTITLE);
  6589. }
  6590. ptb->Release();
  6591. }
  6592. }
  6593. psfb->Release();
  6594. }
  6595. pfbp->Release();
  6596. }
  6597. ILFree(pidl);
  6598. }
  6599. }
  6600. }
  6601. else
  6602. {
  6603. int iBandID;
  6604. do {
  6605. iBandID = _GetQuickLaunchID();
  6606. if (iBandID != -1)
  6607. {
  6608. _ptbs->RemoveBand(iBandID);
  6609. }
  6610. } while (iBandID != -1);
  6611. }
  6612. }
  6613. return iQLBandID;
  6614. }
  6615. void CTray::StartMenuContextMenu(HWND hwnd, DWORD dwPos)
  6616. {
  6617. LPITEMIDLIST pidlStart = SHCloneSpecialIDList(hwnd, CSIDL_STARTMENU, TRUE);
  6618. INSTRUMENT_STATECHANGE(SHCNFI_STATE_TRAY_CONTEXT_START);
  6619. HandleFullScreenApp(NULL);
  6620. SetForegroundWindow(hwnd);
  6621. if (pidlStart)
  6622. {
  6623. LPITEMIDLIST pidlLast = ILClone(ILFindLastID(pidlStart));
  6624. ILRemoveLastID(pidlStart);
  6625. if (pidlLast)
  6626. {
  6627. IShellFolder *psf = BindToFolder(pidlStart);
  6628. if (psf)
  6629. {
  6630. HMENU hmenu = CreatePopupMenu();
  6631. if (hmenu)
  6632. {
  6633. IContextMenu *pcm;
  6634. HRESULT hr = psf->GetUIObjectOf(hwnd, 1, (LPCITEMIDLIST*)&pidlLast, IID_X_PPV_ARG(IContextMenu, NULL, &pcm));
  6635. if (SUCCEEDED(hr))
  6636. {
  6637. hr = pcm->QueryContextMenu(hmenu, 0, IDSYSPOPUP_FIRST, IDSYSPOPUP_LAST, CMF_VERBSONLY);
  6638. if (SUCCEEDED(hr))
  6639. {
  6640. int idCmd;
  6641. TCHAR szCommon[MAX_PATH];
  6642. //Add the menu to invoke the "Start Menu Properties"
  6643. LoadString (hinstCabinet, IDS_STARTMENUPROP, szCommon, ARRAYSIZE(szCommon));
  6644. AppendMenu (hmenu, MF_STRING, IDSYSPOPUP_STARTMENUPROP, szCommon);
  6645. if (!SHRestricted(REST_NOCOMMONGROUPS))
  6646. {
  6647. // If the user has access to the Common Start Menu, then we can add those items. If not,
  6648. // then we should not.
  6649. BOOL fAddCommon = (S_OK == SHGetFolderPath(NULL, CSIDL_COMMON_STARTMENU, NULL, 0, szCommon));
  6650. if (fAddCommon)
  6651. fAddCommon = IsUserAnAdmin();
  6652. // Since we don't show this on the start button when the user is not an admin, don't show it here... I guess...
  6653. if (fAddCommon)
  6654. {
  6655. AppendMenu (hmenu, MF_SEPARATOR, 0, NULL);
  6656. LoadString (hinstCabinet, IDS_OPENCOMMON, szCommon, ARRAYSIZE(szCommon));
  6657. AppendMenu (hmenu, MF_STRING, IDSYSPOPUP_OPENCOMMON, szCommon);
  6658. LoadString (hinstCabinet, IDS_EXPLORECOMMON, szCommon, ARRAYSIZE(szCommon));
  6659. AppendMenu (hmenu, MF_STRING, IDSYSPOPUP_EXPLORECOMMON, szCommon);
  6660. }
  6661. }
  6662. if (dwPos == (DWORD)-1)
  6663. {
  6664. idCmd = _TrackMenu(hmenu);
  6665. }
  6666. else
  6667. {
  6668. SendMessage(_hwndTrayTips, TTM_ACTIVATE, FALSE, 0L);
  6669. idCmd = TrackPopupMenu(hmenu,
  6670. TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN,
  6671. GET_X_LPARAM(dwPos), GET_Y_LPARAM(dwPos), 0, hwnd, NULL);
  6672. SendMessage(_hwndTrayTips, TTM_ACTIVATE, TRUE, 0L);
  6673. }
  6674. switch(idCmd)
  6675. {
  6676. case 0: //User did not select a menu item; so, nothing to do!
  6677. break;
  6678. case IDSYSPOPUP_OPENCOMMON:
  6679. _ExploreCommonStartMenu(FALSE);
  6680. break;
  6681. case IDSYSPOPUP_EXPLORECOMMON:
  6682. _ExploreCommonStartMenu(TRUE);
  6683. break;
  6684. case IDSYSPOPUP_STARTMENUPROP:
  6685. DoProperties(TPF_STARTMENUPAGE);
  6686. break;
  6687. default:
  6688. TCHAR szPath[MAX_PATH];
  6689. CMINVOKECOMMANDINFOEX ici = {0};
  6690. #ifdef UNICODE
  6691. CHAR szPathAnsi[MAX_PATH];
  6692. #endif
  6693. ici.cbSize = sizeof(CMINVOKECOMMANDINFOEX);
  6694. ici.hwnd = hwnd;
  6695. ici.lpVerb = (LPSTR)MAKEINTRESOURCE(idCmd - IDSYSPOPUP_FIRST);
  6696. ici.nShow = SW_NORMAL;
  6697. #ifdef UNICODE
  6698. SHGetPathFromIDListA(pidlStart, szPathAnsi);
  6699. SHGetPathFromIDList(pidlStart, szPath);
  6700. ici.lpDirectory = szPathAnsi;
  6701. ici.lpDirectoryW = szPath;
  6702. ici.fMask |= CMIC_MASK_UNICODE;
  6703. #else
  6704. SHGetPathFromIDList(pidlStart, szPath);
  6705. ici.lpDirectory = szPath;
  6706. #endif
  6707. pcm->InvokeCommand((LPCMINVOKECOMMANDINFO)&ici);
  6708. break;
  6709. } // Switch(idCmd)
  6710. }
  6711. pcm->Release();
  6712. }
  6713. DestroyMenu(hmenu);
  6714. }
  6715. psf->Release();
  6716. }
  6717. ILFree(pidlLast);
  6718. }
  6719. ILFree(pidlStart);
  6720. }
  6721. }
  6722. void GiveDesktopFocus()
  6723. {
  6724. SetForegroundWindow(v_hwndDesktop);
  6725. SendMessage(v_hwndDesktop, DTM_UIACTIVATEIO, (WPARAM) TRUE, /*dtb*/0);
  6726. }
  6727. /*----------------------------------------------------------
  6728. Purpose: loads the given resource string and executes it.
  6729. The resource string should follow this format:
  6730. "program.exe>parameters"
  6731. If there are no parameters, the format should simply be:
  6732. "program.exe"
  6733. */
  6734. void _ExecResourceCmd(UINT ids)
  6735. {
  6736. TCHAR szCmd[2*MAX_PATH];
  6737. if (LoadString(hinstCabinet, ids, szCmd, SIZECHARS(szCmd)))
  6738. {
  6739. SHELLEXECUTEINFO sei = {0};
  6740. // Find list of parameters (if any)
  6741. LPTSTR pszParam = StrChr(szCmd, TEXT('>'));
  6742. if (pszParam)
  6743. {
  6744. // Replace the '>' with a null terminator
  6745. *pszParam = 0;
  6746. pszParam++;
  6747. }
  6748. sei.cbSize = sizeof(sei);
  6749. sei.nShow = SW_SHOWNORMAL;
  6750. sei.lpFile = szCmd;
  6751. sei.lpParameters = pszParam;
  6752. ShellExecuteEx(&sei);
  6753. }
  6754. }
  6755. void CTray::_RefreshStartMenu()
  6756. {
  6757. if (_pmbStartMenu)
  6758. {
  6759. IUnknown_Exec(_pmbStartMenu, &CLSID_MenuBand, MBANDCID_REFRESH, 0, NULL, NULL);
  6760. }
  6761. else if (_pmpStartPane)
  6762. {
  6763. IUnknown_Exec(_pmpStartPane, &CLSID_MenuBand, MBANDCID_REFRESH, 0, NULL, NULL);
  6764. }
  6765. _RefreshSettings();
  6766. _UpdateBandSiteStyle();
  6767. }
  6768. BOOL CTray::_CanMinimizeAll()
  6769. {
  6770. return (_hwndTasks && SendMessage(_hwndTasks, TBC_CANMINIMIZEALL, 0, 0));
  6771. }
  6772. BOOL CTray::_MinimizeAll(BOOL fPostRaiseDesktop)
  6773. {
  6774. BOOL fRet = FALSE;
  6775. if (_hwndTasks)
  6776. {
  6777. fRet = (BOOL)SendMessage(_hwndTasks, TBC_MINIMIZEALL, (WPARAM)_hwnd, (LPARAM)fPostRaiseDesktop);
  6778. }
  6779. return fRet;
  6780. }
  6781. extern void _UpdateNotifySetting(BOOL fNotifySetting);
  6782. //
  6783. // Due to the weirdness of PnP, if the eject request occurs on a thread
  6784. // that contains windows, the eject stalls for 15 seconds. So do it
  6785. // on its own thread.
  6786. //
  6787. DWORD CALLBACK _EjectThreadProc(LPVOID lpThreadParameter)
  6788. {
  6789. CM_Request_Eject_PC();
  6790. return 0;
  6791. }
  6792. void CTray::_Command(UINT idCmd)
  6793. {
  6794. INSTRUMENT_ONCOMMAND(SHCNFI_TRAYCOMMAND, _hwnd, idCmd);
  6795. switch (idCmd) {
  6796. case IDM_CONTROLS:
  6797. case IDM_PRINTERS:
  6798. _ShowFolder(_hwnd,
  6799. idCmd == IDM_CONTROLS ? CSIDL_CONTROLS : CSIDL_PRINTERS, COF_USEOPENSETTINGS);
  6800. break;
  6801. case IDM_EJECTPC:
  6802. // Must use SHCreateThread and not a queued workitem because
  6803. // a workitem might inherit a thread that has windows on it.
  6804. // CTF_INSIST: In emergency, eject synchronously. This stalls
  6805. // for 15 seconds but it's better than nothing.
  6806. SHCreateThread(_EjectThreadProc, NULL, CTF_INSIST, NULL);
  6807. break;
  6808. case IDM_LOGOFF:
  6809. // Let the desktop get a chance to repaint to get rid of the
  6810. // start menu bits before bringing up the logoff dialog box.
  6811. UpdateWindow(_hwnd);
  6812. Sleep(100);
  6813. _SaveTrayAndDesktop();
  6814. LogoffWindowsDialog(v_hwndDesktop);
  6815. break;
  6816. case IDM_MU_DISCONNECT:
  6817. // Do the same sleep as above for the same reason.
  6818. UpdateWindow(_hwnd);
  6819. Sleep(100);
  6820. DisconnectWindowsDialog(v_hwndDesktop);
  6821. break;
  6822. case IDM_EXITWIN:
  6823. // Do the same sleep as above for the same reason.
  6824. UpdateWindow(_hwnd);
  6825. Sleep(100);
  6826. _DoExitWindows(v_hwndDesktop);
  6827. break;
  6828. case IDM_TOGGLEDESKTOP:
  6829. _RaiseDesktop(!g_fDesktopRaised, TRUE);
  6830. break;
  6831. case IDM_FILERUN:
  6832. _RunDlg();
  6833. break;
  6834. case IDM_MINIMIZEALLHOTKEY:
  6835. _HandleGlobalHotkey(GHID_MINIMIZEALL);
  6836. break;
  6837. #ifdef DEBUG
  6838. case IDM_SIZEUP:
  6839. {
  6840. RECT rcView;
  6841. GetWindowRect(_hwndRebar, &rcView);
  6842. MapWindowPoints(HWND_DESKTOP, _hwnd, (LPPOINT)&rcView, 2);
  6843. rcView.bottom -= 18;
  6844. SetWindowPos(_hwndRebar, NULL, 0, 0, RECTWIDTH(rcView), RECTHEIGHT(rcView), SWP_NOMOVE | SWP_NOZORDER);
  6845. }
  6846. break;
  6847. case IDM_SIZEDOWN:
  6848. {
  6849. RECT rcView;
  6850. GetWindowRect(_hwndRebar, &rcView);
  6851. MapWindowPoints(HWND_DESKTOP, _hwnd, (LPPOINT)&rcView, 2);
  6852. rcView.bottom += 18;
  6853. SetWindowPos(_hwndRebar, NULL, 0, 0, RECTWIDTH(rcView), RECTHEIGHT(rcView), SWP_NOMOVE | SWP_NOZORDER);
  6854. }
  6855. break;
  6856. #endif
  6857. case IDM_MINIMIZEALL:
  6858. // minimize all window
  6859. _MinimizeAll(FALSE);
  6860. _fUndoEnabled = TRUE;
  6861. break;
  6862. case IDM_UNDO:
  6863. _RestoreWindowPositions(FALSE);
  6864. break;
  6865. case IDM_SETTIME:
  6866. // run the default applet in timedate.cpl
  6867. SHRunControlPanel(TEXT("timedate.cpl"), _hwnd);
  6868. break;
  6869. case IDM_NOTIFYCUST:
  6870. DoProperties(TPF_TASKBARPAGE | TPF_INVOKECUSTOMIZE);
  6871. break;
  6872. case IDM_LOCKTASKBAR:
  6873. {
  6874. BOOL fCanSizeMove = !_fCanSizeMove; // toggle
  6875. SHRegSetUSValue(REGSTR_EXPLORER_ADVANCED, TEXT("TaskbarSizeMove"),
  6876. REG_DWORD, &fCanSizeMove , sizeof(DWORD), SHREGSET_FORCE_HKCU);
  6877. _RefreshSettings();
  6878. _UpdateBandSiteStyle();
  6879. }
  6880. break;
  6881. case IDM_SHOWTASKMAN:
  6882. RunSystemMonitor();
  6883. break;
  6884. case IDM_CASCADE:
  6885. case IDM_VERTTILE:
  6886. case IDM_HORIZTILE:
  6887. if (_CanTileAnyWindows())
  6888. {
  6889. SaveWindowPositions((idCmd == IDM_CASCADE) ? IDS_CASCADE : IDS_TILE);
  6890. _AppBarNotifyAll(NULL, ABN_WINDOWARRANGE, NULL, TRUE);
  6891. if (idCmd == IDM_CASCADE)
  6892. {
  6893. CascadeWindows(GetDesktopWindow(), 0, NULL, 0, NULL);
  6894. }
  6895. else
  6896. {
  6897. TileWindows(GetDesktopWindow(), ((idCmd == IDM_VERTTILE)?
  6898. MDITILE_VERTICAL : MDITILE_HORIZONTAL), NULL, 0, NULL);
  6899. }
  6900. // do it *before* ABN_xxx so don't get 'indirect' moves
  6901. // REVIEW or should it be after?
  6902. // CheckWindowPositions();
  6903. _fUndoEnabled = FALSE;
  6904. SetTimer(_hwnd, IDT_ENABLEUNDO, 500, NULL);
  6905. _AppBarNotifyAll(NULL, ABN_WINDOWARRANGE, NULL, FALSE);
  6906. }
  6907. break;
  6908. case IDM_TRAYPROPERTIES:
  6909. DoProperties(TPF_TASKBARPAGE);
  6910. break;
  6911. case IDM_SETTINGSASSIST:
  6912. SHCreateThread(SettingsUIThreadProc, NULL, 0, NULL);
  6913. break;
  6914. case IDM_HELPSEARCH:
  6915. _ExecResourceCmd(IDS_HELP_CMD);
  6916. break;
  6917. // NB The Alt-s comes in here.
  6918. case IDC_KBSTART:
  6919. SetForegroundWindow(_hwnd);
  6920. // This pushes the start button and causes the start menu to popup.
  6921. SendMessage(_hwndStart, BM_SETSTATE, TRUE, 0);
  6922. // This forces the button back up.
  6923. SendMessage(_hwndStart, BM_SETSTATE, FALSE, 0);
  6924. break;
  6925. case IDC_ASYNCSTART:
  6926. #if 0 // (for testing UAssist locking code)
  6927. UEMFireEvent(&UEMIID_SHELL, UEME_DBSLEEP, UEMF_XEVENT, -1, (LPARAM)10000);
  6928. #endif
  6929. #ifdef DEBUG
  6930. if (GetAsyncKeyState(VK_SHIFT) < 0)
  6931. {
  6932. UEMFireEvent(&UEMIID_SHELL, UEME_CTLSESSION, UEMF_XEVENT, TRUE, -1);
  6933. _RefreshStartMenu();
  6934. }
  6935. #endif
  6936. // Make sure the button is down.
  6937. // DebugMsg(DM_TRACE, "c.twp: IDC_START.");
  6938. // Make sure the Start button is down.
  6939. if (!_bMainMenuInit && SendMessage(_hwndStart, BM_GETSTATE, 0, 0) & BST_PUSHED)
  6940. {
  6941. // DebugMsg(DM_TRACE, "c.twp: Start button down.");
  6942. // Set the focus.
  6943. _SetFocus(_hwndStart);
  6944. _ToolbarMenu();
  6945. }
  6946. break;
  6947. // NB LButtonDown on the Start button come in here.
  6948. // Space-bar stuff also comes in here.
  6949. case IDC_START:
  6950. // User gets a bit confused with space-bar tuff (the popup ends up
  6951. // getting the key-up and beeps).
  6952. PostMessage(_hwnd, WM_COMMAND, IDC_ASYNCSTART, 0);
  6953. break;
  6954. case FCIDM_FINDFILES:
  6955. SHFindFiles(NULL, NULL);
  6956. break;
  6957. case FCIDM_FINDCOMPUTER:
  6958. SHFindComputer(NULL, NULL);
  6959. break;
  6960. case FCIDM_REFRESH:
  6961. _RefreshStartMenu();
  6962. break;
  6963. case FCIDM_NEXTCTL:
  6964. {
  6965. MSG msg = { 0, WM_KEYDOWN, VK_TAB };
  6966. HWND hwndFocus = GetFocus();
  6967. // Since we are Tab or Shift Tab we should turn the focus rect on.
  6968. //
  6969. // Note: we don't need to do this in the GiveDesktopFocus cases below,
  6970. // but in those cases we're probably already in the UIS_CLEAR UISF_HIDEFOCUS
  6971. // state so this message is cheap to send.
  6972. //
  6973. SendMessage(_hwnd, WM_UPDATEUISTATE, MAKEWPARAM(UIS_CLEAR,
  6974. UISF_HIDEFOCUS), 0);
  6975. BOOL fShift = GetAsyncKeyState(VK_SHIFT) < 0;
  6976. if (hwndFocus && (IsChildOrHWND(_hwndStart, hwndFocus)))
  6977. {
  6978. if (fShift)
  6979. {
  6980. // gotta deactivate manually
  6981. GiveDesktopFocus();
  6982. }
  6983. else
  6984. {
  6985. IUnknown_UIActivateIO(_ptbs, TRUE, &msg);
  6986. }
  6987. }
  6988. else if (hwndFocus && (IsChildOrHWND(_hwndNotify, hwndFocus)))
  6989. {
  6990. if (fShift)
  6991. {
  6992. IUnknown_UIActivateIO(_ptbs, TRUE, &msg);
  6993. }
  6994. else
  6995. {
  6996. GiveDesktopFocus();
  6997. }
  6998. }
  6999. else
  7000. {
  7001. if (IUnknown_TranslateAcceleratorIO(_ptbs, &msg) != S_OK)
  7002. {
  7003. if (fShift)
  7004. {
  7005. _SetFocus(_hwndStart);
  7006. }
  7007. else
  7008. {
  7009. // if you tab forward out of the bands, the next focus guy is the tray notify set
  7010. _SetFocus(_hwndNotify);
  7011. }
  7012. }
  7013. }
  7014. }
  7015. break;
  7016. case IDM_MU_SECURITY:
  7017. MuSecurity();
  7018. break;
  7019. }
  7020. }
  7021. //// Start menu/Tray tab as a drop target
  7022. HRESULT CStartDropTarget::_GetStartMenuDropTarget(IDropTarget** pptgt)
  7023. {
  7024. HRESULT hr = E_FAIL;
  7025. *pptgt = NULL;
  7026. LPITEMIDLIST pidlStart = SHCloneSpecialIDList(NULL, CSIDL_STARTMENU, TRUE);
  7027. if (pidlStart)
  7028. {
  7029. IShellFolder *psf = BindToFolder(pidlStart);
  7030. if (psf)
  7031. {
  7032. hr = psf->CreateViewObject(_ptray->_hwnd, IID_PPV_ARG(IDropTarget, pptgt));
  7033. psf->Release();
  7034. }
  7035. ILFree(pidlStart);
  7036. }
  7037. return hr;
  7038. }
  7039. STDMETHODIMP CDropTargetBase::QueryInterface(REFIID riid, void ** ppvObj)
  7040. {
  7041. static const QITAB qit[] =
  7042. {
  7043. QITABENT(CDropTargetBase, IDropTarget),
  7044. { 0 },
  7045. };
  7046. return QISearch(this, qit, riid, ppvObj);
  7047. }
  7048. STDMETHODIMP_(ULONG) CDropTargetBase::AddRef()
  7049. {
  7050. return 2;
  7051. }
  7052. STDMETHODIMP_(ULONG) CDropTargetBase::Release()
  7053. {
  7054. return 1;
  7055. }
  7056. STDMETHODIMP CDropTargetBase::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
  7057. {
  7058. _ptray->_SetUnhideTimer(ptl.x, ptl.y);
  7059. HWND hwndLock = _ptray->_hwnd; // no clippy
  7060. _DragEnter(hwndLock, ptl, pdtobj);
  7061. return S_OK;
  7062. }
  7063. STDMETHODIMP CDropTargetBase::DragOver(DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
  7064. {
  7065. _ptray->_SetUnhideTimer(ptl.x, ptl.y);
  7066. _DragMove(_ptray->_hwndStart, ptl);
  7067. return S_OK;
  7068. }
  7069. STDMETHODIMP CDropTargetBase::DragLeave()
  7070. {
  7071. DAD_DragLeave();
  7072. return S_OK;
  7073. }
  7074. STDMETHODIMP CDropTargetBase::Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
  7075. {
  7076. DAD_DragLeave();
  7077. return S_OK;
  7078. }
  7079. //
  7080. // There are two different policies for the Start Button depending on
  7081. // whether we are in Classic mode or Personal (New Start Pane) mode.
  7082. //
  7083. // Classic mode: Drops onto the Start Button are treated as if they
  7084. // were drops into the CSIDL_STARTMENU folder.
  7085. //
  7086. // Personal mode: Drops onto the Start Button are treated as if they
  7087. // were drops into the pin list.
  7088. //
  7089. CStartDropTarget::CStartDropTarget() : CDropTargetBase(IToClass(CTray, _dtStart, this))
  7090. {
  7091. }
  7092. CTrayDropTarget::CTrayDropTarget() : CDropTargetBase(IToClass(CTray, _dtTray, this))
  7093. {
  7094. }
  7095. STDMETHODIMP CTrayDropTarget::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
  7096. {
  7097. *pdwEffect = DROPEFFECT_NONE;
  7098. return CDropTargetBase::DragEnter(pdtobj, grfKeyState, ptl, pdwEffect);
  7099. }
  7100. STDMETHODIMP CTrayDropTarget::DragOver(DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
  7101. {
  7102. *pdwEffect = DROPEFFECT_NONE;
  7103. return CDropTargetBase::DragOver(grfKeyState, ptl, pdwEffect);
  7104. }
  7105. void CStartDropTarget::_StartAutoOpenTimer(POINTL *pptl)
  7106. {
  7107. POINT pt = { pptl->x, pptl->y };
  7108. RECT rc;
  7109. //Make sure it really is in the start menu..
  7110. GetWindowRect(_ptray->_hwndStart, &rc);
  7111. if (PtInRect(&rc,pt))
  7112. {
  7113. SetTimer(_ptray->_hwnd, IDT_STARTMENU, 1000, NULL);
  7114. }
  7115. }
  7116. STDMETHODIMP CStartDropTarget::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
  7117. {
  7118. HRESULT hr = S_OK;
  7119. // default to not allowing drops
  7120. _dwEffectsAllowed = DROPEFFECT_NONE;
  7121. if (Tray_StartPanelEnabled())
  7122. {
  7123. // if we've disabled dragging and dropping, don't do anything, but if only the pin list is restricted, then still start the timer
  7124. if (!IsRestrictedOrUserSetting(HKEY_CURRENT_USER, REST_NOCHANGESTARMENU, TEXT("Advanced"), TEXT("Start_EnableDragDrop"), ROUS_KEYALLOWS | ROUS_DEFAULTALLOW))
  7125. {
  7126. // Personal mode: Treat it as an add to the pin list.
  7127. // IsPinnable checks REST_NOSMPINNEDLIST
  7128. if (_ptray->_psmpin && _ptray->_psmpin->IsPinnable(pdtobj, SMPINNABLE_REJECTSLOWMEDIA, NULL) == S_OK)
  7129. {
  7130. _dwEffectsAllowed = DROPEFFECT_LINK;
  7131. }
  7132. *pdwEffect &= _dwEffectsAllowed;
  7133. // Always start the AutoOpen timer because once we open, the user
  7134. // can drop onto other things which may have different drop policies
  7135. // from the pin list.
  7136. _StartAutoOpenTimer(&ptl);
  7137. }
  7138. }
  7139. else
  7140. {
  7141. if (!IsRestrictedOrUserSetting(HKEY_CURRENT_USER, REST_NOCHANGESTARMENU, TEXT("Advanced"), TEXT("StartMenuChange"), ROUS_KEYALLOWS | ROUS_DEFAULTALLOW))
  7142. {
  7143. // Classic mode: Treat it as a drop on the Start Menu folder.
  7144. IDropTarget* ptgt;
  7145. _dwEffectsAllowed = DROPEFFECT_LINK;
  7146. hr = _GetStartMenuDropTarget(&ptgt);
  7147. if (SUCCEEDED(hr))
  7148. {
  7149. // Check to make sure that we're going to accept the drop before we expand the start menu.
  7150. ptgt->DragEnter(pdtobj, grfKeyState, ptl,
  7151. pdwEffect);
  7152. // DROPEFFECT_NONE means it ain't gonna work, so don't popup the Start Menu.
  7153. if (*pdwEffect != DROPEFFECT_NONE)
  7154. {
  7155. _StartAutoOpenTimer(&ptl);
  7156. }
  7157. ptgt->DragLeave();
  7158. ptgt->Release();
  7159. }
  7160. }
  7161. }
  7162. CDropTargetBase::DragEnter(pdtobj, grfKeyState, ptl, pdwEffect);
  7163. return hr;
  7164. }
  7165. STDMETHODIMP CStartDropTarget::DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  7166. {
  7167. *pdwEffect = (_dwEffectsAllowed & DROPEFFECT_LINK);
  7168. return CDropTargetBase::DragOver(grfKeyState, pt, pdwEffect);
  7169. }
  7170. STDMETHODIMP CStartDropTarget::DragLeave()
  7171. {
  7172. KillTimer(_ptray->_hwnd, IDT_STARTMENU);
  7173. return CDropTargetBase::DragLeave();
  7174. }
  7175. STDMETHODIMP CStartDropTarget::Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  7176. {
  7177. KillTimer(_ptray->_hwnd, IDT_STARTMENU);
  7178. HRESULT hr;
  7179. if (Tray_StartPanelEnabled())
  7180. {
  7181. // Personal mode: Treat it as an add to the pin list.
  7182. LPITEMIDLIST pidl;
  7183. if (_ptray->_psmpin && _ptray->_psmpin->IsPinnable(pdtobj, SMPINNABLE_REJECTSLOWMEDIA, &pidl) == S_OK)
  7184. {
  7185. // Delete it from the pin list if it's already there because
  7186. // we want to move it to the bottom.
  7187. _ptray->_psmpin->Modify(pidl, NULL);
  7188. // Now add it to the bottom.
  7189. _ptray->_psmpin->Modify(NULL, pidl);
  7190. ILFree(pidl);
  7191. hr = S_OK;
  7192. }
  7193. else
  7194. {
  7195. hr = E_FAIL;
  7196. }
  7197. }
  7198. else
  7199. {
  7200. IDropTarget* pdrop;
  7201. hr = _GetStartMenuDropTarget(&pdrop);
  7202. if (SUCCEEDED(hr))
  7203. {
  7204. if (!Tray_StartPanelEnabled())
  7205. {
  7206. POINTL ptDrop = { 0, 0 };
  7207. DWORD grfKeyStateDrop = 0;
  7208. *pdwEffect &= DROPEFFECT_LINK;
  7209. pdrop->DragEnter(pdtobj, grfKeyStateDrop, ptDrop, pdwEffect);
  7210. hr = pdrop->Drop(pdtobj, grfKeyStateDrop, ptDrop, pdwEffect);
  7211. pdrop->DragLeave();
  7212. }
  7213. pdrop->Release();
  7214. }
  7215. }
  7216. DAD_DragLeave();
  7217. return hr;
  7218. }
  7219. void CTray::_RegisterDropTargets()
  7220. {
  7221. THR(RegisterDragDrop(_hwndStart, &_dtStart));
  7222. THR(RegisterDragDrop(_hwnd, &_dtTray));
  7223. // It is not a serious error if this fails; it just means that
  7224. // drag/drop to the Start Button will not add to the pin list
  7225. CoCreateInstance(CLSID_StartMenuPin, NULL, CLSCTX_INPROC_SERVER,
  7226. IID_PPV_ARG(IStartMenuPin, &_psmpin));
  7227. }
  7228. void CTray::_RevokeDropTargets()
  7229. {
  7230. RevokeDragDrop(_hwndStart);
  7231. RevokeDragDrop(_hwnd);
  7232. ATOMICRELEASET(_psmpin, IStartMenuPin);
  7233. }
  7234. void CTray::_HandleGlobalHotkey(WPARAM wParam)
  7235. {
  7236. INSTRUMENT_HOTKEY(SHCNFI_GLOBALHOTKEY, wParam);
  7237. switch(wParam)
  7238. {
  7239. case GHID_RUN:
  7240. _RunDlg();
  7241. break;
  7242. case GHID_MINIMIZEALL:
  7243. if (_CanMinimizeAll())
  7244. _MinimizeAll(FALSE);
  7245. SetForegroundWindow(v_hwndDesktop);
  7246. break;
  7247. case GHID_UNMINIMIZEALL:
  7248. _RestoreWindowPositions(FALSE);
  7249. break;
  7250. case GHID_HELP:
  7251. _Command(IDM_HELPSEARCH);
  7252. break;
  7253. case GHID_DESKTOP:
  7254. _RaiseDesktop(!g_fDesktopRaised, TRUE);
  7255. break;
  7256. case GHID_TRAYNOTIFY:
  7257. SwitchToThisWindow(_hwnd, TRUE);
  7258. SetForegroundWindow(_hwnd);
  7259. _SetFocus(_hwndNotify);
  7260. break;
  7261. case GHID_EXPLORER:
  7262. _ShowFolder(_hwnd, CSIDL_DRIVES, COF_CREATENEWWINDOW | COF_EXPLORE);
  7263. break;
  7264. case GHID_FINDFILES:
  7265. if (!SHRestricted(REST_NOFIND))
  7266. _Command(FCIDM_FINDFILES);
  7267. break;
  7268. case GHID_FINDCOMPUTER:
  7269. if (!SHRestricted(REST_NOFIND))
  7270. _Command(FCIDM_FINDCOMPUTER);
  7271. break;
  7272. case GHID_TASKTAB:
  7273. case GHID_TASKSHIFTTAB:
  7274. if (GetForegroundWindow() != _hwnd)
  7275. SetForegroundWindow(_hwnd);
  7276. SendMessage(_hwndTasks, TBC_TASKTAB, wParam == GHID_TASKTAB ? 1 : -1, 0L);
  7277. break;
  7278. case GHID_SYSPROPERTIES:
  7279. #define IDS_SYSDMCPL 0x2334 // from shelldll
  7280. SHRunControlPanel(MAKEINTRESOURCE(IDS_SYSDMCPL), _hwnd);
  7281. break;
  7282. }
  7283. }
  7284. void CTray::_UnregisterGlobalHotkeys()
  7285. {
  7286. for (int i = GHID_FIRST; i < GHID_MAX; i++)
  7287. {
  7288. UnregisterHotKey(_hwnd, i);
  7289. }
  7290. }
  7291. void CTray::_RegisterGlobalHotkeys()
  7292. {
  7293. int i;
  7294. // Are the Windows keys restricted?
  7295. DWORD dwRestricted = SHRestricted(REST_NOWINKEYS);
  7296. for (i = GHID_FIRST ; i < GHID_MAX; i++)
  7297. {
  7298. // If the Windows Keys are Not restricted or it's not a Windows key
  7299. if (!((HIWORD(GlobalKeylist[i - GHID_FIRST]) & MOD_WIN) && dwRestricted))
  7300. {
  7301. // Then register it.
  7302. RegisterHotKey(_hwnd, i, HIWORD(GlobalKeylist[i - GHID_FIRST]), LOWORD(GlobalKeylist[i - GHID_FIRST]));
  7303. }
  7304. }
  7305. }
  7306. void CTray::_RaiseDesktop(BOOL fRaise, BOOL fRestoreWindows)
  7307. {
  7308. if (v_hwndDesktop && (fRaise == !g_fDesktopRaised) && !_fProcessingDesktopRaise)
  7309. {
  7310. _fProcessingDesktopRaise = TRUE;
  7311. BOOL fPostMessage = TRUE;
  7312. if (fRaise)
  7313. {
  7314. HWND hwndFG = GetForegroundWindow();
  7315. // If no window has focus then set focus to the tray
  7316. if (hwndFG)
  7317. {
  7318. hwndFG = _hwnd;
  7319. }
  7320. if (!_hwndFocusBeforeRaise)
  7321. {
  7322. // See if the Foreground Window had a popup window
  7323. _hwndFocusBeforeRaise = GetLastActivePopup(hwndFG);
  7324. }
  7325. if (!IsWindowVisible(_hwndFocusBeforeRaise))
  7326. {
  7327. _hwndFocusBeforeRaise = hwndFG;
  7328. }
  7329. // _MinimizeAll will save the windows positions synchronously, and will minimize the
  7330. // the windows on a background thread
  7331. _fMinimizedAllBeforeRaise = _CanMinimizeAll();
  7332. if (_fMinimizedAllBeforeRaise)
  7333. {
  7334. fPostMessage = !_MinimizeAll(TRUE);
  7335. }
  7336. }
  7337. else
  7338. {
  7339. if (fRestoreWindows)
  7340. {
  7341. HWND hwnd = _hwndFocusBeforeRaise;
  7342. if (_fMinimizedAllBeforeRaise)
  7343. {
  7344. // Since the windows are restored on a seperate thread, I want the make that the
  7345. // desktop is not raised until they are done, so the window restore thread will
  7346. // actually post the message for raising the desktop
  7347. fPostMessage = !_RestoreWindowPositions(TRUE);
  7348. }
  7349. SetForegroundWindow(hwnd);
  7350. if (hwnd == _hwnd)
  7351. {
  7352. _SetFocus(_hwndStart);
  7353. }
  7354. }
  7355. _hwndFocusBeforeRaise = NULL;
  7356. }
  7357. if (fPostMessage)
  7358. PostMessage(v_hwndDesktop, DTM_RAISE, (WPARAM)_hwnd, fRaise ? DTRF_RAISE : DTRF_LOWER);
  7359. }
  7360. }
  7361. void CTray::_OnDesktopState(LPARAM lParam)
  7362. {
  7363. g_fDesktopRaised = (!(lParam & DTRF_LOWER));
  7364. DAD_ShowDragImage(FALSE); // unlock the drag sink if we are dragging.
  7365. if (!g_fDesktopRaised)
  7366. {
  7367. HandleFullScreenApp(NULL);
  7368. }
  7369. else
  7370. {
  7371. // if the desktop is raised, we need to force the tray to be always on top
  7372. // until it's lowered again
  7373. _ResetZorder();
  7374. }
  7375. DAD_ShowDragImage(TRUE); // unlock the drag sink if we are dragging.
  7376. _fProcessingDesktopRaise = FALSE;
  7377. }
  7378. BOOL CTray::_ToggleLanguageBand(BOOL fShowIt)
  7379. {
  7380. HRESULT hr = E_FAIL;
  7381. DWORD dwBandID;
  7382. BOOL fFound = FALSE;
  7383. for (int i = 0; !fFound && SUCCEEDED(_ptbs->EnumBands(i, &dwBandID)); i++)
  7384. {
  7385. if (BandSite_TestBandCLSID(_ptbs, dwBandID, CLSID_MSUTBDeskBand) == S_OK)
  7386. {
  7387. fFound = TRUE;
  7388. }
  7389. }
  7390. BOOL fShow = fFound;
  7391. if (fShowIt && !fFound)
  7392. {
  7393. IDeskBand* pdb;
  7394. HRESULT hr = CoCreateInstance(CLSID_MSUTBDeskBand, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IDeskBand, &pdb));
  7395. if (SUCCEEDED(hr))
  7396. {
  7397. hr = _ptbs->AddBand(pdb);
  7398. fShow = TRUE;
  7399. pdb->Release();
  7400. }
  7401. }
  7402. else if (!fShowIt && fFound)
  7403. {
  7404. hr = _ptbs->RemoveBand(dwBandID);
  7405. if (SUCCEEDED(hr))
  7406. {
  7407. fShow = FALSE;
  7408. }
  7409. }
  7410. return fShow;
  7411. }
  7412. // Process the message by propagating it to all of our child windows
  7413. typedef struct
  7414. {
  7415. UINT uMsg;
  7416. WPARAM wP;
  7417. LPARAM lP;
  7418. CTray* ptray;
  7419. } CABPM;
  7420. BOOL CTray::PropagateEnumProc(HWND hwnd, LPARAM lParam)
  7421. {
  7422. CABPM *ppm = (CABPM *)lParam;
  7423. if (SHIsChildOrSelf(ppm->ptray->_hwndRebar, hwnd) == S_OK)
  7424. {
  7425. return TRUE;
  7426. }
  7427. SendMessage(hwnd, ppm->uMsg, ppm->wP, ppm->lP);
  7428. return TRUE;
  7429. }
  7430. void CTray::_PropagateMessage(HWND hwnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
  7431. {
  7432. CABPM pm = {uMessage, wParam, lParam, this};
  7433. ASSERT(hwnd != _hwndRebar);
  7434. EnumChildWindows(hwnd, PropagateEnumProc, (LPARAM)&pm);
  7435. }
  7436. //
  7437. // Called from SETTINGS.DLL when the tray property sheet needs
  7438. // to be activated. See SettingsUIThreadProc below.
  7439. //
  7440. // Also used by desktop2\deskhost.cpp to get to the tray properties.
  7441. //
  7442. void WINAPI Tray_DoProperties(DWORD dwFlags)
  7443. {
  7444. c_tray.DoProperties(dwFlags);
  7445. }
  7446. DWORD WINAPI CTray::SettingsUIThreadProc(void *pv)
  7447. {
  7448. //
  7449. // Open up the "Settings Wizards" UI.
  7450. //
  7451. HMODULE hmodSettings = LoadLibrary(TEXT("settings.dll"));
  7452. if (NULL != hmodSettings)
  7453. {
  7454. //
  7455. // Entry point in SETTINGS.DLL is ordinal 1.
  7456. // Don't want to export this entry point by name.
  7457. //
  7458. PSETTINGSUIENTRY pfDllEntry = (PSETTINGSUIENTRY)GetProcAddress(hmodSettings, (LPCSTR)1);
  7459. if (NULL != pfDllEntry)
  7460. {
  7461. //
  7462. // This call will open and run the UI.
  7463. // The thread's message loop is inside settings.dll.
  7464. // This call will not return until the settings UI has been closed.
  7465. //
  7466. (*pfDllEntry)(Tray_DoProperties);
  7467. }
  7468. FreeLibrary(hmodSettings);
  7469. }
  7470. return 0;
  7471. }
  7472. //
  7473. // This function is called whenever we detect a change to the default
  7474. // browser registration in HKCR\http\shell\open\command.
  7475. //
  7476. // For compatibility with old browsers, if the default browser (URL handler)
  7477. // is not XP-aware, then auto-generate a StartMenuInternet client
  7478. // registration and set it as the default.
  7479. //
  7480. void CTray::_MigrateOldBrowserSettings()
  7481. {
  7482. // We want only one person to do this work per machine (though it doesn't
  7483. // hurt to have more than one person do it; it's just pointless), so try
  7484. // to filter out people who clearly didn't instigate the key change.
  7485. //
  7486. if (!_fIsDesktopLocked && _fIsDesktopConnected)
  7487. {
  7488. // If the user does not have write access then we can't migrate the
  7489. // setting... (In which case there was nothing to migrate anyway
  7490. // since you need to be administrator to change the default browser...)
  7491. HKEY hkBrowser;
  7492. DWORD dwDisposition;
  7493. if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\Clients\\StartMenuInternet"),
  7494. 0, NULL, REG_OPTION_NON_VOLATILE,
  7495. KEY_READ | KEY_WRITE, NULL, &hkBrowser, &dwDisposition) == ERROR_SUCCESS)
  7496. {
  7497. TCHAR szCommand[MAX_PATH];
  7498. DWORD cch = ARRAYSIZE(szCommand);
  7499. // It is important that we use AssocQueryString to parse the
  7500. // executable, because Netscape has a habit of registering
  7501. // their path incorrectly (they forget to quote the space in
  7502. // "Program Files") but AssocQueryString has special recovery
  7503. // code to detect and repair that case...
  7504. if (SUCCEEDED(AssocQueryString(ASSOCF_NOUSERSETTINGS,
  7505. ASSOCSTR_EXECUTABLE, L"http",
  7506. L"open", szCommand, &cch)) &&
  7507. szCommand[0])
  7508. {
  7509. TCHAR szAppName[MAX_PATH];
  7510. StringCchCopy(szAppName, ARRAYSIZE(szAppName), PathFindFileName(szCommand));
  7511. // You might think that we need to special-case MSN Explorer,
  7512. // since they shipped before XP RTM'd, and convert MSN6.EXE
  7513. // to "MSN Explorer", but that's not true because
  7514. // they never take over as the default http handler, so we
  7515. // will never see them here!
  7516. // Create a registration for the new default browser if necessary.
  7517. // We keep our hands off once we see a DefaultIcon key, since that
  7518. // proves that the application is XP-aware.
  7519. // When IE Access is turned off, StartMenuInternet\IExplore.exe is
  7520. // also removed so that IE will not appear in "Customize Start Menu"
  7521. // dialog box. Do not migrate IE.
  7522. HKEY hkClient;
  7523. if ( 0 != lstrcmpi(szAppName, TEXT("IEXPLORE.EXE")) &&
  7524. RegCreateKeyEx(hkBrowser, szAppName, 0, NULL, REG_OPTION_NON_VOLATILE,
  7525. KEY_WRITE, NULL, &hkClient, &dwDisposition) == ERROR_SUCCESS)
  7526. {
  7527. if (dwDisposition == REG_CREATED_NEW_KEY)
  7528. {
  7529. TCHAR szFriendly[MAX_PATH];
  7530. cch = ARRAYSIZE(szFriendly);
  7531. if (SUCCEEDED(AssocQueryString(ASSOCF_NOUSERSETTINGS | ASSOCF_INIT_BYEXENAME | ASSOCF_VERIFY,
  7532. ASSOCSTR_FRIENDLYAPPNAME, szCommand,
  7533. NULL, szFriendly, &cch)))
  7534. {
  7535. // Set the friendly name
  7536. RegSetValueEx(hkClient, TEXT("LocalizedString"), 0, REG_SZ, (BYTE*)szFriendly, sizeof(TCHAR) * (cch + 1));
  7537. // Set the command string (properly quoted)
  7538. PathQuoteSpaces(szCommand);
  7539. SHSetValue(hkClient, TEXT("shell\\open\\command"), NULL,
  7540. REG_SZ, szCommand, sizeof(TCHAR) * (1 + lstrlen(szCommand)));
  7541. }
  7542. }
  7543. LONG l = 0;
  7544. if (RegQueryValue(hkClient, TEXT("DefaultIcon"), NULL, &l) == ERROR_FILE_NOT_FOUND)
  7545. {
  7546. // Set it as the system default
  7547. RegSetValueEx(hkBrowser, NULL, 0, REG_SZ, (BYTE*)szAppName, sizeof(TCHAR) * (lstrlen(szAppName) + 1));
  7548. // Now tell everybody about the change
  7549. SHSendMessageBroadcast(WM_SETTINGCHANGE, 0, (LPARAM)TEXT("Software\\Clients\\StartMenuInternet"));
  7550. }
  7551. RegCloseKey(hkClient);
  7552. }
  7553. }
  7554. RegCloseKey(hkBrowser);
  7555. }
  7556. }
  7557. // Restart the monitoring of the registry...
  7558. // (RegNotifyChangeKeyValue is good for only one shot.)
  7559. // Some apps (like Opera) delete the key as part of their registration,
  7560. // which causes our HKEY to go bad, so close it and make a new one.
  7561. if (_hkHTTP)
  7562. {
  7563. RegCloseKey(_hkHTTP);
  7564. _hkHTTP = NULL;
  7565. }
  7566. //
  7567. // Note! We have to register on HKCR\http\shell recursively
  7568. // even though we only care about HKCR\http\shell\open\command.
  7569. // The reason is that shell\open\command might not exist (IE
  7570. // deletes it as part of its uninstall) and you can't register
  7571. // a wait on a key that doesn't exist. We don't want to create
  7572. // a blank key on our own, because that means "To launch a web
  7573. // browser, run the null string as a command," which doesn't work
  7574. // too great.
  7575. //
  7576. if (RegCreateKeyEx(HKEY_CLASSES_ROOT, TEXT("http\\shell"),
  7577. 0, NULL, REG_OPTION_NON_VOLATILE,
  7578. KEY_ENUMERATE_SUB_KEYS |
  7579. KEY_QUERY_VALUE | KEY_SET_VALUE | KEY_NOTIFY,
  7580. NULL, &_hkHTTP, NULL) == ERROR_SUCCESS)
  7581. {
  7582. RegNotifyChangeKeyValue(_hkHTTP, TRUE,
  7583. REG_NOTIFY_CHANGE_NAME |
  7584. REG_NOTIFY_CHANGE_LAST_SET,
  7585. _hHTTPEvent, TRUE);
  7586. }
  7587. }
  7588. void CTray::_MigrateOldBrowserSettingsCB(PVOID lpParameter, BOOLEAN)
  7589. {
  7590. //
  7591. // Sleep a little while so the app can finish installing all the
  7592. // registry keys it wants before we start cleaning up behind it.
  7593. //
  7594. Sleep(1000);
  7595. CTray *self = (CTray *)lpParameter;
  7596. self->_MigrateOldBrowserSettings();
  7597. }
  7598. //
  7599. // *** WARNING ***
  7600. //
  7601. // This is a private interface EXPLORER.EXE exposes to SHDOCVW, which
  7602. // allows SHDOCVW (mostly desktop) to access tray. All member must be
  7603. // thread safe!
  7604. //
  7605. CDeskTray::CDeskTray()
  7606. {
  7607. _ptray = IToClass(CTray, _desktray, this);
  7608. }
  7609. HRESULT CDeskTray::QueryInterface(REFIID riid, void ** ppvObj)
  7610. {
  7611. #if 0 // no IID_IDeskTray yet defined
  7612. static const QITAB qit[] =
  7613. {
  7614. QITABENT(CDeskTray, IDeskTray),
  7615. { 0 },
  7616. };
  7617. return QISearch(this, qit, riid, ppvObj);
  7618. #else
  7619. return E_NOTIMPL;
  7620. #endif
  7621. }
  7622. ULONG CDeskTray::AddRef()
  7623. {
  7624. return 2;
  7625. }
  7626. ULONG CDeskTray::Release()
  7627. {
  7628. return 1;
  7629. }
  7630. HRESULT CDeskTray::GetTrayWindow(HWND* phwndTray)
  7631. {
  7632. ASSERT(_ptray->_hwnd);
  7633. *phwndTray = _ptray->_hwnd;
  7634. return S_OK;
  7635. }
  7636. HRESULT CDeskTray::SetDesktopWindow(HWND hwndDesktop)
  7637. {
  7638. ASSERT(v_hwndDesktop == NULL);
  7639. v_hwndDesktop = hwndDesktop;
  7640. return S_OK;
  7641. }
  7642. UINT CDeskTray::AppBarGetState()
  7643. {
  7644. return (_ptray->_uAutoHide ? ABS_AUTOHIDE : 0) |
  7645. (_ptray->_fAlwaysOnTop ? ABS_ALWAYSONTOP : 0);
  7646. }
  7647. //*** CDeskTray::SetVar -- set an explorer variable (var#i := value)
  7648. // ENTRY/EXIT
  7649. // var id# of variable to be changed
  7650. // value value to be assigned
  7651. // NOTES
  7652. // WARNING: thread safety is up to caller!
  7653. // notes: currently only called in 1 place, but extra generality is cheap
  7654. // minimal cost
  7655. HRESULT CDeskTray::SetVar(int var, DWORD value)
  7656. {
  7657. extern BOOL g_fExitExplorer;
  7658. TraceMsg(DM_TRACE, "c.cdt_sv: set var(%d):=%d", var, value);
  7659. switch (var) {
  7660. case SVTRAY_EXITEXPLORER:
  7661. TraceMsg(DM_TRACE, "c.cdt_sv: set g_fExitExplorer:=%d", value);
  7662. g_fExitExplorer = value;
  7663. WriteCleanShutdown(1);
  7664. break;
  7665. default:
  7666. ASSERT(0);
  7667. return S_FALSE;
  7668. }
  7669. return S_OK;
  7670. }