xref: /reactos/base/shell/explorer/traywnd.cpp (revision d2c667c6)
1 /*
2  * ReactOS Explorer
3  *
4  * Copyright 2006 - 2007 Thomas Weidenmueller <w3seek@reactos.org>
5  * Copyright 2018 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
6  *
7  * this library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * this library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21 
22 #include "precomp.h"
23 #include <commoncontrols.h>
24 
25 HRESULT TrayWindowCtxMenuCreator(ITrayWindow * TrayWnd, IN HWND hWndOwner, IContextMenu ** ppCtxMenu);
26 LRESULT appbar_message(COPYDATASTRUCT* cds);
27 void appbar_notify_all(HMONITOR hMon, UINT uMsg, HWND hwndExclude, LPARAM lParam);
28 
29 #define WM_APP_TRAYDESTROY  (WM_APP + 0x100)
30 
31 #define TIMER_ID_AUTOHIDE 1
32 #define TIMER_ID_MOUSETRACK 2
33 #define MOUSETRACK_INTERVAL 100
34 #define AUTOHIDE_DELAY_HIDE 2000
35 #define AUTOHIDE_DELAY_SHOW 50
36 #define AUTOHIDE_INTERVAL_ANIMATING 10
37 
38 #define AUTOHIDE_SPEED_SHOW 10
39 #define AUTOHIDE_SPEED_HIDE 1
40 
41 #define AUTOHIDE_HIDDEN 0
42 #define AUTOHIDE_SHOWING 1
43 #define AUTOHIDE_SHOWN 2
44 #define AUTOHIDE_HIDING 3
45 
46 #define IDHK_RUN 0x1f4
47 #define IDHK_MINIMIZE_ALL 0x1f5
48 #define IDHK_RESTORE_ALL 0x1f6
49 #define IDHK_HELP 0x1f7
50 #define IDHK_EXPLORE 0x1f8
51 #define IDHK_FIND 0x1f9
52 #define IDHK_FIND_COMPUTER 0x1fa
53 #define IDHK_NEXT_TASK 0x1fb
54 #define IDHK_PREV_TASK 0x1fc
55 #define IDHK_SYS_PROPERTIES 0x1fd
56 #define IDHK_DESKTOP 0x1fe
57 #define IDHK_PAGER 0x1ff
58 
59 static const WCHAR szTrayWndClass[] = L"Shell_TrayWnd";
60 
61 struct EFFECTIVE_INFO
62 {
63     HWND hwndFound;
64     HWND hwndDesktop;
65     HWND hwndProgman;
66     HWND hTrayWnd;
67     BOOL bMustBeInMonitor;
68 };
69 
70 static BOOL CALLBACK
71 FindEffectiveProc(HWND hwnd, LPARAM lParam)
72 {
73     EFFECTIVE_INFO *pei = (EFFECTIVE_INFO *)lParam;
74 
75     if (!IsWindowVisible(hwnd) || IsIconic(hwnd))
76         return TRUE;    // continue
77 
78     if (pei->hTrayWnd == hwnd || pei->hwndDesktop == hwnd ||
79         pei->hwndProgman == hwnd)
80     {
81         return TRUE;    // continue
82     }
83 
84     if (pei->bMustBeInMonitor)
85     {
86         // is the window in the nearest monitor?
87         HMONITOR hMon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
88         if (hMon)
89         {
90             MONITORINFO info;
91             ZeroMemory(&info, sizeof(info));
92             info.cbSize = sizeof(info);
93             if (GetMonitorInfoW(hMon, &info))
94             {
95                 RECT rcWindow, rcMonitor, rcIntersect;
96                 rcMonitor = info.rcMonitor;
97 
98                 GetWindowRect(hwnd, &rcWindow);
99 
100                 if (!IntersectRect(&rcIntersect, &rcMonitor, &rcWindow))
101                     return TRUE;    // continue
102             }
103         }
104     }
105 
106     pei->hwndFound = hwnd;
107     return FALSE;   // stop if found
108 }
109 
110 static BOOL
111 IsThereAnyEffectiveWindow(BOOL bMustBeInMonitor)
112 {
113     EFFECTIVE_INFO ei;
114     ei.hwndFound = NULL;
115     ei.hwndDesktop = GetDesktopWindow();
116     ei.hTrayWnd = FindWindowW(L"Shell_TrayWnd", NULL);
117     ei.hwndProgman = FindWindowW(L"Progman", NULL);
118     ei.bMustBeInMonitor = bMustBeInMonitor;
119 
120     EnumWindows(FindEffectiveProc, (LPARAM)&ei);
121     if (ei.hwndFound && FALSE)
122     {
123         WCHAR szClass[64], szText[64];
124         GetClassNameW(ei.hwndFound, szClass, _countof(szClass));
125         GetWindowTextW(ei.hwndFound, szText, _countof(szText));
126         MessageBoxW(NULL, szText, szClass, 0);
127     }
128     return ei.hwndFound != NULL;
129 }
130 
131 CSimpleArray<HWND>  g_MinimizedAll;
132 
133 /*
134  * ITrayWindow
135  */
136 
137 const GUID IID_IShellDesktopTray = { 0x213e2df9, 0x9a14, 0x4328, { 0x99, 0xb1, 0x69, 0x61, 0xf9, 0x14, 0x3c, 0xe9 } };
138 
139 class CStartButton
140     : public CWindowImpl<CStartButton>
141 {
142     HIMAGELIST m_ImageList;
143     SIZE       m_Size;
144     HFONT      m_Font;
145 
146 public:
147     CStartButton()
148         : m_ImageList(NULL),
149           m_Font(NULL)
150     {
151         m_Size.cx = 0;
152         m_Size.cy = 0;
153     }
154 
155     virtual ~CStartButton()
156     {
157         if (m_ImageList != NULL)
158             ImageList_Destroy(m_ImageList);
159 
160         if (m_Font != NULL)
161             DeleteObject(m_Font);
162     }
163 
164     SIZE GetSize()
165     {
166         return m_Size;
167     }
168 
169     VOID UpdateSize()
170     {
171         SIZE Size = { 0, 0 };
172 
173         if (m_ImageList == NULL ||
174             !SendMessageW(BCM_GETIDEALSIZE, 0, (LPARAM) &Size))
175         {
176             Size.cx = 2 * GetSystemMetrics(SM_CXEDGE) + GetSystemMetrics(SM_CYCAPTION) * 3;
177         }
178 
179         Size.cy = max(Size.cy, GetSystemMetrics(SM_CYCAPTION));
180 
181         /* Save the size of the start button */
182         m_Size = Size;
183     }
184 
185     VOID UpdateFont()
186     {
187         /* Get the system fonts, we use the caption font, always bold, though. */
188         NONCLIENTMETRICS ncm = {sizeof(ncm)};
189         if (!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, FALSE))
190             return;
191 
192         if (m_Font)
193             DeleteObject(m_Font);
194 
195         ncm.lfCaptionFont.lfWeight = FW_BOLD;
196         m_Font = CreateFontIndirect(&ncm.lfCaptionFont);
197 
198         SetFont(m_Font, FALSE);
199     }
200 
201     VOID Initialize()
202     {
203         // HACK & FIXME: CORE-18016
204         HWND hWnd = m_hWnd;
205         m_hWnd = NULL;
206         SubclassWindow(hWnd);
207 
208         SetWindowTheme(m_hWnd, L"Start", NULL);
209 
210         m_ImageList = ImageList_LoadImageW(hExplorerInstance,
211                                            MAKEINTRESOURCEW(IDB_START),
212                                            0, 0, 0,
213                                            IMAGE_BITMAP,
214                                            LR_LOADTRANSPARENT | LR_CREATEDIBSECTION);
215 
216         BUTTON_IMAGELIST bil = {m_ImageList, {1,1,1,1}, BUTTON_IMAGELIST_ALIGN_LEFT};
217         SendMessageW(BCM_SETIMAGELIST, 0, (LPARAM) &bil);
218         UpdateSize();
219     }
220 
221     HWND Create(HWND hwndParent)
222     {
223         WCHAR szStartCaption[32];
224         if (!LoadStringW(hExplorerInstance,
225                          IDS_START,
226                          szStartCaption,
227                          _countof(szStartCaption)))
228         {
229             wcscpy(szStartCaption, L"Start");
230         }
231 
232         DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | BS_PUSHBUTTON | BS_LEFT | BS_VCENTER;
233 
234         // HACK & FIXME: CORE-18016
235         m_hWnd = CreateWindowEx(
236             0,
237             WC_BUTTON,
238             szStartCaption,
239             dwStyle,
240             0, 0, 0, 0,
241             hwndParent,
242             (HMENU) IDC_STARTBTN,
243             hExplorerInstance,
244             NULL);
245 
246         if (m_hWnd)
247             Initialize();
248 
249         return m_hWnd;
250     }
251 
252     LRESULT OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
253     {
254         if (uMsg == WM_KEYUP && wParam != VK_SPACE)
255             return 0;
256 
257         GetParent().PostMessage(TWM_OPENSTARTMENU);
258         return 0;
259     }
260 
261     BEGIN_MSG_MAP(CStartButton)
262         MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
263     END_MSG_MAP()
264 
265 };
266 
267 class CTrayWindow :
268     public CComCoClass<CTrayWindow>,
269     public CComObjectRootEx<CComMultiThreadModelNoCS>,
270     public CWindowImpl < CTrayWindow, CWindow, CControlWinTraits >,
271     public ITrayWindow,
272     public IShellDesktopTray,
273     public IOleWindow,
274     public IContextMenu
275 {
276     CStartButton m_StartButton;
277 
278     CComPtr<IMenuBand>  m_StartMenuBand;
279     CComPtr<IMenuPopup> m_StartMenuPopup;
280 
281     CComPtr<IDeskBand> m_TaskBand;
282     CComPtr<IContextMenu> m_ContextMenu;
283     HTHEME m_Theme;
284 
285     HFONT m_Font;
286 
287     HWND m_DesktopWnd;
288     HWND m_Rebar;
289     HWND m_TaskSwitch;
290     HWND m_TrayNotify;
291 
292     CComPtr<IUnknown> m_TrayNotifyInstance;
293 
294     DWORD    m_Position;
295     HMONITOR m_Monitor;
296     HMONITOR m_PreviousMonitor;
297     DWORD    m_DraggingPosition;
298     HMONITOR m_DraggingMonitor;
299 
300     RECT m_TrayRects[4];
301     SIZE m_TraySize;
302 
303     HWND m_TrayPropertiesOwner;
304     HWND m_RunFileDlgOwner;
305 
306     UINT m_AutoHideState;
307     SIZE m_AutoHideOffset;
308     TRACKMOUSEEVENT m_MouseTrackingInfo;
309 
310     HDPA m_ShellServices;
311 
312 public:
313     CComPtr<ITrayBandSite> m_TrayBandSite;
314 
315     union
316     {
317         DWORD Flags;
318         struct
319         {
320             /* UI Status */
321             DWORD InSizeMove : 1;
322             DWORD IsDragging : 1;
323             DWORD NewPosSize : 1;
324         };
325     };
326 
327 public:
328     CTrayWindow() :
329         m_StartButton(),
330         m_Theme(NULL),
331         m_Font(NULL),
332         m_DesktopWnd(NULL),
333         m_Rebar(NULL),
334         m_TaskSwitch(NULL),
335         m_TrayNotify(NULL),
336         m_Position(0),
337         m_Monitor(NULL),
338         m_PreviousMonitor(NULL),
339         m_DraggingPosition(0),
340         m_DraggingMonitor(NULL),
341         m_TrayPropertiesOwner(NULL),
342         m_RunFileDlgOwner(NULL),
343         m_AutoHideState(NULL),
344         m_ShellServices(NULL),
345         Flags(0)
346     {
347         ZeroMemory(&m_TrayRects, sizeof(m_TrayRects));
348         ZeroMemory(&m_TraySize, sizeof(m_TraySize));
349         ZeroMemory(&m_AutoHideOffset, sizeof(m_AutoHideOffset));
350         ZeroMemory(&m_MouseTrackingInfo, sizeof(m_MouseTrackingInfo));
351     }
352 
353     virtual ~CTrayWindow()
354     {
355         if (m_ShellServices != NULL)
356         {
357             ShutdownShellServices(m_ShellServices);
358             m_ShellServices = NULL;
359         }
360 
361         if (m_Font != NULL)
362         {
363             DeleteObject(m_Font);
364             m_Font = NULL;
365         }
366 
367         if (m_Theme)
368         {
369             CloseThemeData(m_Theme);
370             m_Theme = NULL;
371         }
372 
373         PostQuitMessage(0);
374     }
375 
376 
377 
378 
379 
380     /**********************************************************
381      *    ##### command handling #####
382      */
383 
384     HRESULT ExecResourceCmd(int id)
385     {
386         WCHAR szCommand[256];
387         WCHAR *pszParameters;
388 
389         if (!LoadStringW(hExplorerInstance,
390                          id,
391                          szCommand,
392                          _countof(szCommand)))
393         {
394             return E_FAIL;
395         }
396 
397         pszParameters = wcschr(szCommand, L'>');
398         if (pszParameters)
399         {
400             *pszParameters = 0;
401             pszParameters++;
402         }
403 
404         ShellExecuteW(m_hWnd, NULL, szCommand, pszParameters, NULL, SW_SHOWNORMAL);
405         return S_OK;
406     }
407 
408     LRESULT DoExitWindows()
409     {
410         /* Display the ReactOS Shutdown Dialog */
411         ExitWindowsDialog(m_hWnd);
412 
413         /*
414          * If the user presses CTRL+ALT+SHIFT while exiting
415          * the shutdown dialog, exit the shell cleanly.
416          */
417         if ((GetKeyState(VK_CONTROL) & 0x8000) &&
418             (GetKeyState(VK_SHIFT)   & 0x8000) &&
419             (GetKeyState(VK_MENU)    & 0x8000))
420         {
421             PostMessage(WM_QUIT, 0, 0);
422         }
423         return 0;
424     }
425 
426     DWORD WINAPI RunFileDlgThread()
427     {
428         HWND hwnd;
429         RECT posRect;
430 
431         m_StartButton.GetWindowRect(&posRect);
432 
433         hwnd = CreateWindowEx(0,
434                               WC_STATIC,
435                               NULL,
436                               WS_OVERLAPPED | WS_DISABLED | WS_CLIPSIBLINGS | WS_BORDER | SS_LEFT,
437                               posRect.left,
438                               posRect.top,
439                               posRect.right - posRect.left,
440                               posRect.bottom - posRect.top,
441                               NULL,
442                               NULL,
443                               NULL,
444                               NULL);
445 
446         m_RunFileDlgOwner = hwnd;
447 
448         // build the default directory from two environment variables
449         CStringW strDefaultDir, strHomePath;
450         strDefaultDir.GetEnvironmentVariable(L"HOMEDRIVE");
451         strHomePath.GetEnvironmentVariable(L"HOMEPATH");
452         strDefaultDir += strHomePath;
453 
454         RunFileDlg(hwnd, NULL, (LPCWSTR)strDefaultDir, NULL, NULL, RFF_CALCDIRECTORY);
455 
456         m_RunFileDlgOwner = NULL;
457         ::DestroyWindow(hwnd);
458 
459         return 0;
460     }
461 
462     static DWORD WINAPI s_RunFileDlgThread(IN OUT PVOID pParam)
463     {
464         CTrayWindow * This = (CTrayWindow*) pParam;
465         return This->RunFileDlgThread();
466     }
467 
468     void DisplayRunFileDlg()
469     {
470         HWND hRunDlg;
471         if (m_RunFileDlgOwner)
472         {
473             hRunDlg = ::GetLastActivePopup(m_RunFileDlgOwner);
474             if (hRunDlg != NULL &&
475                 hRunDlg != m_RunFileDlgOwner)
476             {
477                 SetForegroundWindow(hRunDlg);
478                 return;
479             }
480         }
481 
482         CloseHandle(CreateThread(NULL, 0, s_RunFileDlgThread, this, 0, NULL));
483     }
484 
485     DWORD WINAPI TrayPropertiesThread()
486     {
487         HWND hwnd;
488         RECT posRect;
489 
490         m_StartButton.GetWindowRect(&posRect);
491         hwnd = CreateWindowEx(0,
492                               WC_STATIC,
493                               NULL,
494                               WS_OVERLAPPED | WS_DISABLED | WS_CLIPSIBLINGS | WS_BORDER | SS_LEFT,
495                               posRect.left,
496                               posRect.top,
497                               posRect.right - posRect.left,
498                               posRect.bottom - posRect.top,
499                               NULL,
500                               NULL,
501                               NULL,
502                               NULL);
503 
504         m_TrayPropertiesOwner = hwnd;
505 
506         DisplayTrayProperties(hwnd, m_hWnd);
507 
508         m_TrayPropertiesOwner = NULL;
509         ::DestroyWindow(hwnd);
510 
511         return 0;
512     }
513 
514     static DWORD WINAPI s_TrayPropertiesThread(IN OUT PVOID pParam)
515     {
516         CTrayWindow *This = (CTrayWindow*) pParam;
517 
518         return This->TrayPropertiesThread();
519     }
520 
521     HWND STDMETHODCALLTYPE DisplayProperties()
522     {
523         HWND hTrayProp;
524 
525         if (m_TrayPropertiesOwner)
526         {
527             hTrayProp = ::GetLastActivePopup(m_TrayPropertiesOwner);
528             if (hTrayProp != NULL &&
529                 hTrayProp != m_TrayPropertiesOwner)
530             {
531                 SetForegroundWindow(hTrayProp);
532                 return NULL;
533             }
534         }
535 
536         CloseHandle(CreateThread(NULL, 0, s_TrayPropertiesThread, this, 0, NULL));
537         return NULL;
538     }
539 
540     VOID OpenCommonStartMenuDirectory(IN HWND hWndOwner, IN LPCTSTR lpOperation)
541     {
542         WCHAR szDir[MAX_PATH];
543 
544         if (SHGetSpecialFolderPath(hWndOwner,
545             szDir,
546             CSIDL_COMMON_STARTMENU,
547             FALSE))
548         {
549             ShellExecute(hWndOwner,
550                          lpOperation,
551                          szDir,
552                          NULL,
553                          NULL,
554                          SW_SHOWNORMAL);
555         }
556     }
557 
558     VOID OpenTaskManager(IN HWND hWndOwner)
559     {
560         ShellExecute(hWndOwner,
561                      TEXT("open"),
562                      TEXT("taskmgr.exe"),
563                      NULL,
564                      NULL,
565                      SW_SHOWNORMAL);
566     }
567 
568     VOID ToggleDesktop()
569     {
570         if (::IsThereAnyEffectiveWindow(TRUE))
571         {
572             ShowDesktop();
573         }
574         else
575         {
576             RestoreAll();
577         }
578     }
579 
580     BOOL STDMETHODCALLTYPE ExecContextMenuCmd(IN UINT uiCmd)
581     {
582         switch (uiCmd)
583         {
584         case ID_SHELL_CMD_PROPERTIES:
585             DisplayProperties();
586             break;
587 
588         case ID_SHELL_CMD_OPEN_ALL_USERS:
589             OpenCommonStartMenuDirectory(m_hWnd,
590                                          TEXT("open"));
591             break;
592 
593         case ID_SHELL_CMD_EXPLORE_ALL_USERS:
594             OpenCommonStartMenuDirectory(m_hWnd,
595                                          TEXT("explore"));
596             break;
597 
598         case ID_LOCKTASKBAR:
599             if (SHRestricted(REST_CLASSICSHELL) == 0)
600             {
601                 Lock(!g_TaskbarSettings.bLock);
602             }
603             break;
604 
605         case ID_SHELL_CMD_OPEN_TASKMGR:
606             OpenTaskManager(m_hWnd);
607             break;
608 
609         case ID_SHELL_CMD_UNDO_ACTION:
610             break;
611 
612         case ID_SHELL_CMD_SHOW_DESKTOP:
613             ShowDesktop();
614             break;
615 
616         case ID_SHELL_CMD_TILE_WND_H:
617             appbar_notify_all(NULL, ABN_WINDOWARRANGE, NULL, TRUE);
618             TileWindows(NULL, MDITILE_HORIZONTAL, NULL, 0, NULL);
619             appbar_notify_all(NULL, ABN_WINDOWARRANGE, NULL, FALSE);
620             break;
621 
622         case ID_SHELL_CMD_TILE_WND_V:
623             appbar_notify_all(NULL, ABN_WINDOWARRANGE, NULL, TRUE);
624             TileWindows(NULL, MDITILE_VERTICAL, NULL, 0, NULL);
625             appbar_notify_all(NULL, ABN_WINDOWARRANGE, NULL, FALSE);
626             break;
627 
628         case ID_SHELL_CMD_CASCADE_WND:
629             appbar_notify_all(NULL, ABN_WINDOWARRANGE, NULL, TRUE);
630             CascadeWindows(NULL, MDITILE_SKIPDISABLED, NULL, 0, NULL);
631             appbar_notify_all(NULL, ABN_WINDOWARRANGE, NULL, FALSE);
632             break;
633 
634         case ID_SHELL_CMD_CUST_NOTIF:
635             ShowCustomizeNotifyIcons(hExplorerInstance, m_hWnd);
636             break;
637 
638         case ID_SHELL_CMD_ADJUST_DAT:
639             //FIXME: Use SHRunControlPanel
640             ShellExecuteW(m_hWnd, NULL, L"timedate.cpl", NULL, NULL, SW_NORMAL);
641             break;
642 
643         case ID_SHELL_CMD_RESTORE_ALL:
644             RestoreAll();
645             break;
646 
647         default:
648             TRACE("ITrayWindow::ExecContextMenuCmd(%u): Unhandled Command ID!\n", uiCmd);
649             return FALSE;
650         }
651 
652         return TRUE;
653     }
654 
655     LRESULT HandleHotKey(DWORD id)
656     {
657         switch (id)
658         {
659         case IDHK_RUN:
660             DisplayRunFileDlg();
661             break;
662         case IDHK_HELP:
663             ExecResourceCmd(IDS_HELP_COMMAND);
664             break;
665         case IDHK_EXPLORE:
666             //FIXME: We don't support this yet:
667             //ShellExecuteW(0, L"explore", NULL, NULL, NULL, 1);
668             ShellExecuteW(0, NULL, L"explorer.exe", L"/e ,", NULL, 1);
669             break;
670         case IDHK_FIND:
671             SHFindFiles(NULL, NULL);
672             break;
673         case IDHK_FIND_COMPUTER:
674             SHFindComputer(NULL, NULL);
675             break;
676         case IDHK_SYS_PROPERTIES:
677             //FIXME: Use SHRunControlPanel
678             ShellExecuteW(m_hWnd, NULL, L"sysdm.cpl", NULL, NULL, SW_NORMAL);
679             break;
680         case IDHK_NEXT_TASK:
681             break;
682         case IDHK_PREV_TASK:
683             break;
684         case IDHK_MINIMIZE_ALL:
685             MinimizeAll();
686             break;
687         case IDHK_RESTORE_ALL:
688             RestoreAll();
689             break;
690         case IDHK_DESKTOP:
691             ToggleDesktop();
692             break;
693         case IDHK_PAGER:
694             break;
695         }
696 
697         return 0;
698     }
699 
700     LRESULT HandleCommand(UINT uCommand)
701     {
702         switch (uCommand)
703         {
704             case TRAYCMD_STARTMENU:
705                 // TODO:
706                 break;
707             case TRAYCMD_RUN_DIALOG:
708                 DisplayRunFileDlg();
709                 break;
710             case TRAYCMD_LOGOFF_DIALOG:
711                 LogoffWindowsDialog(m_hWnd); // FIXME: Maybe handle it in a similar way as DoExitWindows?
712                 break;
713             case TRAYCMD_CASCADE:
714                 CascadeWindows(NULL, MDITILE_SKIPDISABLED, NULL, 0, NULL);
715                 break;
716             case TRAYCMD_TILE_H:
717                 TileWindows(NULL, MDITILE_HORIZONTAL, NULL, 0, NULL);
718                 break;
719             case TRAYCMD_TILE_V:
720                 TileWindows(NULL, MDITILE_VERTICAL, NULL, 0, NULL);
721                 break;
722             case TRAYCMD_TOGGLE_DESKTOP:
723                 ToggleDesktop();
724                 break;
725             case TRAYCMD_DATE_AND_TIME:
726                 ShellExecuteW(m_hWnd, NULL, L"timedate.cpl", NULL, NULL, SW_NORMAL);
727                 break;
728             case TRAYCMD_TASKBAR_PROPERTIES:
729                 DisplayProperties();
730                 break;
731             case TRAYCMD_MINIMIZE_ALL:
732                 MinimizeAll();
733                 break;
734             case TRAYCMD_RESTORE_ALL:
735                 RestoreAll();
736                 break;
737             case TRAYCMD_SHOW_DESKTOP:
738                 ShowDesktop();
739                 break;
740             case TRAYCMD_SHOW_TASK_MGR:
741                 OpenTaskManager(m_hWnd);
742                 break;
743             case TRAYCMD_CUSTOMIZE_TASKBAR:
744                 break;
745             case TRAYCMD_LOCK_TASKBAR:
746                 if (SHRestricted(REST_CLASSICSHELL) == 0)
747                 {
748                     Lock(!g_TaskbarSettings.bLock);
749                 }
750                 break;
751             case TRAYCMD_HELP_AND_SUPPORT:
752                 ExecResourceCmd(IDS_HELP_COMMAND);
753                 break;
754             case TRAYCMD_CONTROL_PANEL:
755                 // TODO:
756                 break;
757             case TRAYCMD_SHUTDOWN_DIALOG:
758                 DoExitWindows();
759                 break;
760             case TRAYCMD_PRINTERS_AND_FAXES:
761                 // TODO:
762                 break;
763             case TRAYCMD_LOCK_DESKTOP:
764                 // TODO:
765                 break;
766             case TRAYCMD_SWITCH_USER_DIALOG:
767                 // TODO:
768                 break;
769             case IDM_SEARCH:
770             case TRAYCMD_SEARCH_FILES:
771                 SHFindFiles(NULL, NULL);
772                 break;
773             case TRAYCMD_SEARCH_COMPUTERS:
774                 SHFindComputer(NULL, NULL);
775                 break;
776 
777             default:
778                 break;
779         }
780 
781         return FALSE;
782     }
783 
784 
785     UINT TrackMenu(
786         IN HMENU hMenu,
787         IN POINT *ppt OPTIONAL,
788         IN HWND hwndExclude OPTIONAL,
789         IN BOOL TrackUp,
790         IN BOOL IsContextMenu)
791     {
792         TPMPARAMS tmp, *ptmp = NULL;
793         POINT pt;
794         UINT cmdId;
795         UINT fuFlags;
796 
797         if (hwndExclude != NULL)
798         {
799             /* Get the client rectangle and map it to screen coordinates */
800             if (::GetClientRect(hwndExclude,
801                 &tmp.rcExclude) &&
802                 ::MapWindowPoints(hwndExclude,
803                 NULL,
804                 (LPPOINT) &tmp.rcExclude,
805                 2) != 0)
806             {
807                 ptmp = &tmp;
808             }
809         }
810 
811         if (ppt == NULL)
812         {
813             if (ptmp == NULL &&
814                 GetClientRect(&tmp.rcExclude) &&
815                 MapWindowPoints(
816                 NULL,
817                 (LPPOINT) &tmp.rcExclude,
818                 2) != 0)
819             {
820                 ptmp = &tmp;
821             }
822 
823             if (ptmp != NULL)
824             {
825                 /* NOTE: TrackPopupMenuEx will eventually align the track position
826                          for us, no need to take care of it here as long as the
827                          coordinates are somewhere within the exclusion rectangle */
828                 pt.x = ptmp->rcExclude.left;
829                 pt.y = ptmp->rcExclude.top;
830             }
831             else
832                 pt.x = pt.y = 0;
833         }
834         else
835             pt = *ppt;
836 
837         tmp.cbSize = sizeof(tmp);
838 
839         fuFlags = TPM_RETURNCMD | TPM_VERTICAL;
840         fuFlags |= (TrackUp ? TPM_BOTTOMALIGN : TPM_TOPALIGN);
841         if (IsContextMenu)
842             fuFlags |= TPM_RIGHTBUTTON;
843         else
844             fuFlags |= (TrackUp ? TPM_VERNEGANIMATION : TPM_VERPOSANIMATION);
845 
846         cmdId = TrackPopupMenuEx(hMenu,
847                                  fuFlags,
848                                  pt.x,
849                                  pt.y,
850                                  m_hWnd,
851                                  ptmp);
852 
853         return cmdId;
854     }
855 
856     HRESULT TrackCtxMenu(
857         IN IContextMenu * contextMenu,
858         IN POINT *ppt OPTIONAL,
859         IN HWND hwndExclude OPTIONAL,
860         IN BOOL TrackUp,
861         IN PVOID Context OPTIONAL)
862     {
863         POINT pt;
864         TPMPARAMS params;
865         RECT rc;
866         HRESULT hr;
867         UINT uCommand;
868         HMENU popup = CreatePopupMenu();
869 
870         if (popup == NULL)
871             return E_FAIL;
872 
873         if (ppt)
874         {
875             pt = *ppt;
876         }
877         else
878         {
879             ::GetWindowRect(m_hWnd, &rc);
880             pt.x = rc.left;
881             pt.y = rc.top;
882         }
883 
884         TRACE("Before Query\n");
885         hr = contextMenu->QueryContextMenu(popup, 0, 0, UINT_MAX, CMF_NORMAL);
886         if (FAILED_UNEXPECTEDLY(hr))
887         {
888             TRACE("Query failed\n");
889             DestroyMenu(popup);
890             return hr;
891         }
892 
893         TRACE("Before Tracking\n");
894         ::SetForegroundWindow(m_hWnd);
895         if (hwndExclude)
896         {
897             ::GetWindowRect(hwndExclude, &rc);
898             ZeroMemory(&params, sizeof(params));
899             params.cbSize = sizeof(params);
900             params.rcExclude = rc;
901             uCommand = ::TrackPopupMenuEx(popup, TPM_RETURNCMD, pt.x, pt.y, m_hWnd, &params);
902         }
903         else
904         {
905             uCommand = ::TrackPopupMenuEx(popup, TPM_RETURNCMD, pt.x, pt.y, m_hWnd, NULL);
906         }
907         ::PostMessage(m_hWnd, WM_NULL, 0, 0);
908 
909         if (uCommand != 0)
910         {
911             TRACE("Before InvokeCommand\n");
912             CMINVOKECOMMANDINFO cmi = { 0 };
913             cmi.cbSize = sizeof(cmi);
914             cmi.lpVerb = MAKEINTRESOURCEA(uCommand);
915             cmi.hwnd = m_hWnd;
916             hr = contextMenu->InvokeCommand(&cmi);
917         }
918         else
919         {
920             TRACE("TrackPopupMenu failed. Code=%d, LastError=%d\n", uCommand, GetLastError());
921             hr = S_FALSE;
922         }
923 
924         DestroyMenu(popup);
925         return hr;
926     }
927 
928 
929 
930 
931 
932     /**********************************************************
933      *    ##### moving and sizing handling #####
934      */
935 
936     void UpdateFonts()
937     {
938         /* There is nothing to do if themes are enabled */
939         if (m_Theme)
940             return;
941 
942         m_StartButton.UpdateFont();
943 
944         NONCLIENTMETRICS ncm = {sizeof(ncm)};
945         if (!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, FALSE))
946         {
947             ERR("SPI_GETNONCLIENTMETRICS failed\n");
948             return;
949         }
950 
951         if (m_Font != NULL)
952             DeleteObject(m_Font);
953 
954         ncm.lfCaptionFont.lfWeight = FW_NORMAL;
955         m_Font = CreateFontIndirect(&ncm.lfCaptionFont);
956         if (!m_Font)
957         {
958             ERR("CreateFontIndirect failed\n");
959             return;
960         }
961 
962         SendMessage(m_Rebar, WM_SETFONT, (WPARAM) m_Font, TRUE);
963         SendMessage(m_TaskSwitch, WM_SETFONT, (WPARAM) m_Font, TRUE);
964         SendMessage(m_TrayNotify, WM_SETFONT, (WPARAM) m_Font, TRUE);
965     }
966 
967     HMONITOR GetScreenRectFromRect(
968         IN OUT RECT *pRect,
969         IN DWORD dwFlags)
970     {
971         MONITORINFO mi;
972         HMONITOR hMon;
973 
974         mi.cbSize = sizeof(mi);
975         hMon = MonitorFromRect(pRect, dwFlags);
976         if (hMon != NULL &&
977             GetMonitorInfo(hMon, &mi))
978         {
979             *pRect = mi.rcMonitor;
980         }
981         else
982         {
983             pRect->left = 0;
984             pRect->top = 0;
985             pRect->right = GetSystemMetrics(SM_CXSCREEN);
986             pRect->bottom = GetSystemMetrics(SM_CYSCREEN);
987 
988             hMon = NULL;
989         }
990 
991         return hMon;
992     }
993 
994     HMONITOR GetMonitorFromRect(
995         IN const RECT *pRect)
996     {
997         HMONITOR hMon;
998 
999         /* In case the monitor sizes or saved sizes differ a bit (probably
1000            not a lot, only so the tray window overlaps into another monitor
1001            now), minimize the risk that we determine a wrong monitor by
1002            using the center point of the tray window if we can't determine
1003            it using the rectangle. */
1004         hMon = MonitorFromRect(pRect, MONITOR_DEFAULTTONULL);
1005         if (hMon == NULL)
1006         {
1007             POINT pt;
1008 
1009             pt.x = pRect->left + ((pRect->right - pRect->left) / 2);
1010             pt.y = pRect->top + ((pRect->bottom - pRect->top) / 2);
1011 
1012             /* be less error-prone, find the nearest monitor */
1013             hMon = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
1014         }
1015 
1016         return hMon;
1017     }
1018 
1019     HMONITOR GetScreenRect(
1020         IN HMONITOR hMonitor,
1021         IN OUT RECT *pRect)
1022     {
1023         HMONITOR hMon = NULL;
1024 
1025         if (hMonitor != NULL)
1026         {
1027             MONITORINFO mi;
1028 
1029             mi.cbSize = sizeof(mi);
1030             if (!GetMonitorInfo(hMonitor, &mi))
1031             {
1032                 /* Hm, the monitor is gone? Try to find a monitor where it
1033                    could be located now */
1034                 hMon = GetMonitorFromRect(pRect);
1035                 if (hMon == NULL ||
1036                     !GetMonitorInfo(hMon, &mi))
1037                 {
1038                     hMon = NULL;
1039                     goto GetPrimaryRect;
1040                 }
1041             }
1042 
1043             *pRect = mi.rcMonitor;
1044         }
1045         else
1046         {
1047 GetPrimaryRect:
1048             pRect->left = 0;
1049             pRect->top = 0;
1050             pRect->right = GetSystemMetrics(SM_CXSCREEN);
1051             pRect->bottom = GetSystemMetrics(SM_CYSCREEN);
1052         }
1053 
1054         return hMon;
1055     }
1056 
1057     VOID AdjustSizerRect(RECT *rc, DWORD pos)
1058     {
1059         int iSizerPart[4] = {TBP_SIZINGBARLEFT, TBP_SIZINGBARTOP, TBP_SIZINGBARRIGHT, TBP_SIZINGBARBOTTOM};
1060         SIZE size;
1061 
1062         if (pos > ABE_BOTTOM)
1063             pos = ABE_BOTTOM;
1064 
1065         HRESULT hr = GetThemePartSize(m_Theme, NULL, iSizerPart[pos], 0, NULL, TS_TRUE, &size);
1066         if (FAILED_UNEXPECTEDLY(hr))
1067             return;
1068 
1069         switch (pos)
1070         {
1071             case ABE_TOP:
1072                 rc->bottom -= size.cy;
1073                 break;
1074             case ABE_BOTTOM:
1075                 rc->top += size.cy;
1076                 break;
1077             case ABE_LEFT:
1078                 rc->right -= size.cx;
1079                 break;
1080             case ABE_RIGHT:
1081                 rc->left += size.cx;
1082                 break;
1083         }
1084     }
1085 
1086     VOID MakeTrayRectWithSize(IN DWORD Position,
1087                               IN const SIZE *pTraySize,
1088                               IN OUT RECT *pRect)
1089     {
1090         switch (Position)
1091         {
1092         case ABE_LEFT:
1093             pRect->right = pRect->left + pTraySize->cx;
1094             break;
1095 
1096         case ABE_TOP:
1097             pRect->bottom = pRect->top + pTraySize->cy;
1098             break;
1099 
1100         case ABE_RIGHT:
1101             pRect->left = pRect->right - pTraySize->cx;
1102             break;
1103 
1104         case ABE_BOTTOM:
1105         default:
1106             pRect->top = pRect->bottom - pTraySize->cy;
1107             break;
1108         }
1109     }
1110 
1111     VOID GetTrayRectFromScreenRect(IN DWORD Position,
1112                                    IN const RECT *pScreen,
1113                                    IN const SIZE *pTraySize OPTIONAL,
1114                                    OUT RECT *pRect)
1115     {
1116         if (pTraySize == NULL)
1117             pTraySize = &m_TraySize;
1118 
1119         *pRect = *pScreen;
1120 
1121         if(!m_Theme)
1122         {
1123             /* Move the border outside of the screen */
1124             InflateRect(pRect,
1125                         GetSystemMetrics(SM_CXEDGE),
1126                         GetSystemMetrics(SM_CYEDGE));
1127         }
1128 
1129         MakeTrayRectWithSize(Position, pTraySize, pRect);
1130     }
1131 
1132     BOOL IsPosHorizontal()
1133     {
1134         return m_Position == ABE_TOP || m_Position == ABE_BOTTOM;
1135     }
1136 
1137     HMONITOR CalculateValidSize(
1138         IN DWORD Position,
1139         IN OUT RECT *pRect)
1140     {
1141         RECT rcScreen;
1142         //BOOL Horizontal;
1143         HMONITOR hMon;
1144         SIZE szMax, szWnd;
1145 
1146         //Horizontal = IsPosHorizontal();
1147 
1148         szWnd.cx = pRect->right - pRect->left;
1149         szWnd.cy = pRect->bottom - pRect->top;
1150 
1151         rcScreen = *pRect;
1152         hMon = GetScreenRectFromRect(
1153             &rcScreen,
1154             MONITOR_DEFAULTTONEAREST);
1155 
1156         /* Calculate the maximum size of the tray window and limit the window
1157            size to half of the screen's size. */
1158         szMax.cx = (rcScreen.right - rcScreen.left) / 2;
1159         szMax.cy = (rcScreen.bottom - rcScreen.top) / 2;
1160         if (szWnd.cx > szMax.cx)
1161             szWnd.cx = szMax.cx;
1162         if (szWnd.cy > szMax.cy)
1163             szWnd.cy = szMax.cy;
1164 
1165         /* FIXME - calculate */
1166 
1167         GetTrayRectFromScreenRect(Position,
1168                                   &rcScreen,
1169                                   &szWnd,
1170                                   pRect);
1171 
1172         return hMon;
1173     }
1174 
1175 #if 0
1176     VOID
1177         GetMinimumWindowSize(
1178         OUT RECT *pRect)
1179     {
1180         RECT rcMin = {0};
1181 
1182         AdjustWindowRectEx(&rcMin,
1183                            GetWindowLong(m_hWnd,
1184                            GWL_STYLE),
1185                            FALSE,
1186                            GetWindowLong(m_hWnd,
1187                            GWL_EXSTYLE));
1188 
1189         *pRect = rcMin;
1190     }
1191 #endif
1192 
1193 
1194     DWORD GetDraggingRectFromPt(
1195         IN POINT pt,
1196         OUT RECT *pRect,
1197         OUT HMONITOR *phMonitor)
1198     {
1199         HMONITOR hMon, hMonNew;
1200         DWORD PosH, PosV, Pos;
1201         SIZE DeltaPt, ScreenOffset;
1202         RECT rcScreen;
1203 
1204         rcScreen.left = 0;
1205         rcScreen.top = 0;
1206 
1207         /* Determine the screen rectangle */
1208         hMon = MonitorFromPoint(pt, MONITOR_DEFAULTTONULL);
1209         if (hMon != NULL)
1210         {
1211             MONITORINFO mi;
1212 
1213             mi.cbSize = sizeof(mi);
1214             if (!GetMonitorInfo(hMon, &mi))
1215             {
1216                 hMon = NULL;
1217                 goto GetPrimaryScreenRect;
1218             }
1219 
1220             /* make left top corner of the screen zero based to
1221                make calculations easier */
1222             pt.x -= mi.rcMonitor.left;
1223             pt.y -= mi.rcMonitor.top;
1224 
1225             ScreenOffset.cx = mi.rcMonitor.left;
1226             ScreenOffset.cy = mi.rcMonitor.top;
1227             rcScreen.right = mi.rcMonitor.right - mi.rcMonitor.left;
1228             rcScreen.bottom = mi.rcMonitor.bottom - mi.rcMonitor.top;
1229         }
1230         else
1231         {
1232 GetPrimaryScreenRect:
1233             ScreenOffset.cx = 0;
1234             ScreenOffset.cy = 0;
1235             rcScreen.right = GetSystemMetrics(SM_CXSCREEN);
1236             rcScreen.bottom = GetSystemMetrics(SM_CYSCREEN);
1237         }
1238 
1239         /* Calculate the nearest screen border */
1240         if (pt.x < rcScreen.right / 2)
1241         {
1242             DeltaPt.cx = pt.x;
1243             PosH = ABE_LEFT;
1244         }
1245         else
1246         {
1247             DeltaPt.cx = rcScreen.right - pt.x;
1248             PosH = ABE_RIGHT;
1249         }
1250 
1251         if (pt.y < rcScreen.bottom / 2)
1252         {
1253             DeltaPt.cy = pt.y;
1254             PosV = ABE_TOP;
1255         }
1256         else
1257         {
1258             DeltaPt.cy = rcScreen.bottom - pt.y;
1259             PosV = ABE_BOTTOM;
1260         }
1261 
1262         Pos = (DeltaPt.cx * rcScreen.bottom < DeltaPt.cy * rcScreen.right) ? PosH : PosV;
1263 
1264         /* Fix the screen origin to be relative to the primary monitor again */
1265         OffsetRect(&rcScreen,
1266                    ScreenOffset.cx,
1267                    ScreenOffset.cy);
1268 
1269         RECT rcPos = m_TrayRects[Pos];
1270 
1271         hMonNew = GetMonitorFromRect(&rcPos);
1272         if (hMon != hMonNew)
1273         {
1274             SIZE szTray;
1275 
1276             /* Recalculate the rectangle, we're dragging to another monitor.
1277                We don't need to recalculate the rect on single monitor systems. */
1278             szTray.cx = rcPos.right - rcPos.left;
1279             szTray.cy = rcPos.bottom - rcPos.top;
1280 
1281             GetTrayRectFromScreenRect(Pos, &rcScreen, &szTray, pRect);
1282             hMon = hMonNew;
1283         }
1284         else
1285         {
1286             /* The user is dragging the tray window on the same monitor. We don't need
1287                to recalculate the rectangle */
1288             *pRect = rcPos;
1289         }
1290 
1291         *phMonitor = hMon;
1292 
1293         return Pos;
1294     }
1295 
1296     DWORD GetDraggingRectFromRect(
1297         IN OUT RECT *pRect,
1298         OUT HMONITOR *phMonitor)
1299     {
1300         POINT pt;
1301 
1302         /* Calculate the center of the rectangle. We call
1303            GetDraggingRectFromPt to calculate a valid
1304            dragging rectangle */
1305         pt.x = pRect->left + ((pRect->right - pRect->left) / 2);
1306         pt.y = pRect->top + ((pRect->bottom - pRect->top) / 2);
1307 
1308         return GetDraggingRectFromPt(
1309             pt,
1310             pRect,
1311             phMonitor);
1312     }
1313 
1314     VOID ChangingWinPos(IN OUT LPWINDOWPOS pwp)
1315     {
1316         RECT rcTray;
1317 
1318         if (IsDragging)
1319         {
1320             rcTray.left = pwp->x;
1321             rcTray.top = pwp->y;
1322             rcTray.right = rcTray.left + pwp->cx;
1323             rcTray.bottom = rcTray.top + pwp->cy;
1324 
1325             if (!EqualRect(&rcTray,
1326                 &m_TrayRects[m_DraggingPosition]))
1327             {
1328                 /* Recalculate the rectangle, the user dragged the tray
1329                    window to another monitor or the window was somehow else
1330                    moved or resized */
1331                 m_DraggingPosition = GetDraggingRectFromRect(
1332                     &rcTray,
1333                     &m_DraggingMonitor);
1334                 //m_TrayRects[DraggingPosition] = rcTray;
1335             }
1336 
1337             //Monitor = CalculateValidSize(DraggingPosition,
1338             //                             &rcTray);
1339 
1340             m_Monitor = m_DraggingMonitor;
1341             m_Position = m_DraggingPosition;
1342             IsDragging = FALSE;
1343 
1344             m_TrayRects[m_Position] = rcTray;
1345             goto ChangePos;
1346         }
1347         else if (GetWindowRect(&rcTray))
1348         {
1349             if (InSizeMove)
1350             {
1351                 if (!(pwp->flags & SWP_NOMOVE))
1352                 {
1353                     rcTray.left = pwp->x;
1354                     rcTray.top = pwp->y;
1355                 }
1356 
1357                 if (!(pwp->flags & SWP_NOSIZE))
1358                 {
1359                     rcTray.right = rcTray.left + pwp->cx;
1360                     rcTray.bottom = rcTray.top + pwp->cy;
1361                 }
1362 
1363                 m_Position = GetDraggingRectFromRect(&rcTray, &m_Monitor);
1364 
1365                 if (!(pwp->flags & (SWP_NOMOVE | SWP_NOSIZE)))
1366                 {
1367                     SIZE szWnd;
1368 
1369                     szWnd.cx = pwp->cx;
1370                     szWnd.cy = pwp->cy;
1371 
1372                     MakeTrayRectWithSize(m_Position, &szWnd, &rcTray);
1373                 }
1374 
1375                 m_TrayRects[m_Position] = rcTray;
1376             }
1377             else if (m_Position != (DWORD)-1)
1378             {
1379                 /* If the user isn't resizing the tray window we need to make sure the
1380                    new size or position is valid. this is to prevent changes to the window
1381                    without user interaction. */
1382                 rcTray = m_TrayRects[m_Position];
1383 
1384                 if (g_TaskbarSettings.sr.AutoHide)
1385                 {
1386                     rcTray.left += m_AutoHideOffset.cx;
1387                     rcTray.right += m_AutoHideOffset.cx;
1388                     rcTray.top += m_AutoHideOffset.cy;
1389                     rcTray.bottom += m_AutoHideOffset.cy;
1390                 }
1391 
1392             }
1393 
1394 ChangePos:
1395             m_TraySize.cx = rcTray.right - rcTray.left;
1396             m_TraySize.cy = rcTray.bottom - rcTray.top;
1397 
1398             pwp->flags &= ~(SWP_NOMOVE | SWP_NOSIZE);
1399             pwp->x = rcTray.left;
1400             pwp->y = rcTray.top;
1401             pwp->cx = m_TraySize.cx;
1402             pwp->cy = m_TraySize.cy;
1403         }
1404     }
1405 
1406     VOID ApplyClipping(IN BOOL Clip)
1407     {
1408         RECT rcClip, rcWindow;
1409         HRGN hClipRgn;
1410 
1411         if (GetWindowRect(&rcWindow))
1412         {
1413             /* Disable clipping on systems with only one monitor */
1414             if (GetSystemMetrics(SM_CMONITORS) <= 1)
1415                 Clip = FALSE;
1416 
1417             if (Clip)
1418             {
1419                 rcClip = rcWindow;
1420 
1421                 GetScreenRect(m_Monitor, &rcClip);
1422 
1423                 if (!IntersectRect(&rcClip, &rcClip, &rcWindow))
1424                 {
1425                     rcClip = rcWindow;
1426                 }
1427 
1428                 OffsetRect(&rcClip,
1429                            -rcWindow.left,
1430                            -rcWindow.top);
1431 
1432                 hClipRgn = CreateRectRgnIndirect(&rcClip);
1433             }
1434             else
1435                 hClipRgn = NULL;
1436 
1437             /* Set the clipping region or make sure the window isn't clipped
1438                by disabling it explicitly. */
1439             SetWindowRgn(hClipRgn, TRUE);
1440         }
1441     }
1442 
1443     VOID ResizeWorkArea()
1444     {
1445 #if !WIN7_DEBUG_MODE
1446         RECT rcTray, rcWorkArea;
1447 
1448         /* If monitor has changed then fix the previous monitors work area */
1449         if (m_PreviousMonitor != m_Monitor)
1450         {
1451             GetScreenRect(m_PreviousMonitor, &rcWorkArea);
1452             SystemParametersInfoW(SPI_SETWORKAREA,
1453                                   1,
1454                                   &rcWorkArea,
1455                                   SPIF_SENDCHANGE);
1456         }
1457 
1458         rcTray = m_TrayRects[m_Position];
1459 
1460         GetScreenRect(m_Monitor, &rcWorkArea);
1461         m_PreviousMonitor = m_Monitor;
1462 
1463         /* If AutoHide is false then change the workarea to exclude
1464            the area that the taskbar covers. */
1465         if (!g_TaskbarSettings.sr.AutoHide)
1466         {
1467             switch (m_Position)
1468             {
1469             case ABE_TOP:
1470                 rcWorkArea.top = rcTray.bottom;
1471                 break;
1472             case ABE_LEFT:
1473                 rcWorkArea.left = rcTray.right;
1474                 break;
1475             case ABE_RIGHT:
1476                 rcWorkArea.right = rcTray.left;
1477                 break;
1478             case ABE_BOTTOM:
1479                 rcWorkArea.bottom = rcTray.top;
1480                 break;
1481             }
1482         }
1483 
1484         /*
1485          * Resize the current monitor work area. Win32k will also send
1486          * a WM_SIZE message to automatically resize the desktop.
1487          */
1488         SystemParametersInfoW(SPI_SETWORKAREA,
1489                               1,
1490                               &rcWorkArea,
1491                               SPIF_SENDCHANGE);
1492 #endif
1493     }
1494 
1495     VOID CheckTrayWndPosition()
1496     {
1497         /* Force the rebar bands to resize */
1498         IUnknown_Exec(m_TrayBandSite,
1499                       IID_IDeskBand,
1500                       DBID_BANDINFOCHANGED,
1501                       0,
1502                       NULL,
1503                       NULL);
1504 
1505         /* Calculate the size of the taskbar based on the rebar */
1506         FitToRebar(&m_TrayRects[m_Position]);
1507 
1508         /* Move the tray window */
1509         /* The handler of WM_WINDOWPOSCHANGING will override whatever size
1510          * and position we use here with m_TrayRects */
1511         SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOACTIVATE);
1512         ResizeWorkArea();
1513         ApplyClipping(TRUE);
1514     }
1515 
1516     VOID RegLoadSettings()
1517     {
1518         DWORD Pos;
1519         RECT rcScreen;
1520         SIZE WndSize, EdgeSize, DlgFrameSize;
1521         SIZE StartBtnSize = m_StartButton.GetSize();
1522 
1523         EdgeSize.cx = GetSystemMetrics(SM_CXEDGE);
1524         EdgeSize.cy = GetSystemMetrics(SM_CYEDGE);
1525         DlgFrameSize.cx = GetSystemMetrics(SM_CXDLGFRAME);
1526         DlgFrameSize.cy = GetSystemMetrics(SM_CYDLGFRAME);
1527 
1528         m_Position = g_TaskbarSettings.sr.Position;
1529         rcScreen = g_TaskbarSettings.sr.Rect;
1530         GetScreenRectFromRect(&rcScreen, MONITOR_DEFAULTTONEAREST);
1531 
1532         if (!g_TaskbarSettings.sr.Size.cx || !g_TaskbarSettings.sr.Size.cy)
1533         {
1534             /* Use the minimum size of the taskbar, we'll use the start
1535                button as a minimum for now. Make sure we calculate the
1536                entire window size, not just the client size. However, we
1537                use a thinner border than a standard thick border, so that
1538                the start button and bands are not stuck to the screen border. */
1539             if(!m_Theme)
1540             {
1541                 g_TaskbarSettings.sr.Size.cx = StartBtnSize.cx + (2 * (EdgeSize.cx + DlgFrameSize.cx));
1542                 g_TaskbarSettings.sr.Size.cy = StartBtnSize.cy + (2 * (EdgeSize.cy + DlgFrameSize.cy));
1543             }
1544             else
1545             {
1546                 g_TaskbarSettings.sr.Size.cx = StartBtnSize.cx - EdgeSize.cx;
1547                 g_TaskbarSettings.sr.Size.cy = StartBtnSize.cy - EdgeSize.cy;
1548                 if(!g_TaskbarSettings.bLock)
1549                     g_TaskbarSettings.sr.Size.cy += GetSystemMetrics(SM_CYSIZEFRAME);
1550             }
1551         }
1552         /* Determine a minimum tray window rectangle. The "client" height is
1553            zero here since we cannot determine an optimal minimum width when
1554            loaded as a vertical tray window. We just need to make sure the values
1555            loaded from the registry are at least. The windows explorer behaves
1556            the same way, it allows the user to save a zero width vertical tray
1557            window, but not a zero height horizontal tray window. */
1558         if(!m_Theme)
1559         {
1560             WndSize.cx = 2 * (EdgeSize.cx + DlgFrameSize.cx);
1561             WndSize.cy = StartBtnSize.cy + (2 * (EdgeSize.cy + DlgFrameSize.cy));
1562         }
1563         else
1564         {
1565             WndSize.cx = StartBtnSize.cx;
1566             WndSize.cy = StartBtnSize.cy - EdgeSize.cy;
1567         }
1568 
1569         if (WndSize.cx < g_TaskbarSettings.sr.Size.cx)
1570             WndSize.cx = g_TaskbarSettings.sr.Size.cx;
1571         if (WndSize.cy < g_TaskbarSettings.sr.Size.cy)
1572             WndSize.cy = g_TaskbarSettings.sr.Size.cy;
1573 
1574         /* Save the calculated size */
1575         m_TraySize = WndSize;
1576 
1577         /* Calculate all docking rectangles. We need to do this here so they're
1578            initialized and dragging the tray window to another position gives
1579            usable results */
1580         for (Pos = ABE_LEFT; Pos <= ABE_BOTTOM; Pos++)
1581         {
1582             GetTrayRectFromScreenRect(Pos,
1583                                       &rcScreen,
1584                                       &m_TraySize,
1585                                       &m_TrayRects[Pos]);
1586             // TRACE("m_TrayRects[%d(%d)]: %d,%d,%d,%d\n", Pos, Position, m_TrayRects[Pos].left, m_TrayRects[Pos].top, m_TrayRects[Pos].right, m_TrayRects[Pos].bottom);
1587         }
1588 
1589         /* Determine which monitor we are on. It shouldn't matter which docked
1590            position rectangle we use */
1591         m_Monitor = GetMonitorFromRect(&m_TrayRects[ABE_LEFT]);
1592     }
1593 
1594     VOID AlignControls(IN PRECT prcClient OPTIONAL)
1595     {
1596         RECT rcClient;
1597         SIZE TraySize, StartSize;
1598         POINT ptTrayNotify = { 0, 0 };
1599         BOOL Horizontal;
1600         HDWP dwp;
1601 
1602         m_StartButton.UpdateSize();
1603         if (prcClient != NULL)
1604         {
1605             rcClient = *prcClient;
1606         }
1607         else
1608         {
1609             if (!GetClientRect(&rcClient))
1610             {
1611                 ERR("Could not get client rect lastErr=%d\n", GetLastError());
1612                 return;
1613             }
1614         }
1615 
1616         Horizontal = IsPosHorizontal();
1617 
1618         /* We're about to resize/move the start button, the rebar control and
1619            the tray notification control */
1620         dwp = BeginDeferWindowPos(3);
1621         if (dwp == NULL)
1622         {
1623             ERR("BeginDeferWindowPos failed. lastErr=%d\n", GetLastError());
1624             return;
1625         }
1626 
1627         /* Limit the Start button width to the client width, if necessary */
1628         StartSize = m_StartButton.GetSize();
1629         if (StartSize.cx > rcClient.right)
1630             StartSize.cx = rcClient.right;
1631 
1632         HWND hwndTaskToolbar = ::GetWindow(m_TaskSwitch, GW_CHILD);
1633         if (hwndTaskToolbar)
1634         {
1635             DWORD size = SendMessageW(hwndTaskToolbar, TB_GETBUTTONSIZE, 0, 0);
1636 
1637             /* Themed button covers Edge area as well */
1638             StartSize.cy = HIWORD(size) + (m_Theme ? GetSystemMetrics(SM_CYEDGE) : 0);
1639         }
1640 
1641         if (m_StartButton.m_hWnd != NULL)
1642         {
1643             /* Resize and reposition the button */
1644             dwp = m_StartButton.DeferWindowPos(dwp,
1645                                                NULL,
1646                                                0,
1647                                                0,
1648                                                StartSize.cx,
1649                                                StartSize.cy,
1650                                                SWP_NOZORDER | SWP_NOACTIVATE);
1651             if (dwp == NULL)
1652             {
1653                 ERR("DeferWindowPos for start button failed. lastErr=%d\n", GetLastError());
1654                 return;
1655             }
1656         }
1657 
1658         /* Determine the size that the tray notification window needs */
1659         if (Horizontal)
1660         {
1661             TraySize.cx = 0;
1662             TraySize.cy = rcClient.bottom;
1663         }
1664         else
1665         {
1666             TraySize.cx = rcClient.right;
1667             TraySize.cy = 0;
1668         }
1669 
1670         if (m_TrayNotify != NULL &&
1671             SendMessage(m_TrayNotify,
1672                         TNWM_GETMINIMUMSIZE,
1673                         (WPARAM)Horizontal,
1674                         (LPARAM)&TraySize))
1675         {
1676             /* Move the tray notification window to the desired location */
1677             if (Horizontal)
1678                 ptTrayNotify.x = rcClient.right - TraySize.cx;
1679             else
1680                 ptTrayNotify.y = rcClient.bottom - TraySize.cy;
1681 
1682             dwp = ::DeferWindowPos(dwp,
1683                                    m_TrayNotify,
1684                                    NULL,
1685                                    ptTrayNotify.x,
1686                                    ptTrayNotify.y,
1687                                    TraySize.cx,
1688                                    TraySize.cy,
1689                                    SWP_NOZORDER | SWP_NOACTIVATE);
1690             if (dwp == NULL)
1691             {
1692                 ERR("DeferWindowPos for notification area failed. lastErr=%d\n", GetLastError());
1693                 return;
1694             }
1695         }
1696 
1697         /* Resize/Move the rebar control */
1698         if (m_Rebar != NULL)
1699         {
1700             POINT ptRebar = { 0, 0 };
1701             SIZE szRebar;
1702 
1703             SetWindowStyle(m_Rebar, CCS_VERT, Horizontal ? 0 : CCS_VERT);
1704 
1705             if (Horizontal)
1706             {
1707                 ptRebar.x = StartSize.cx + GetSystemMetrics(SM_CXSIZEFRAME);
1708                 szRebar.cx = ptTrayNotify.x - ptRebar.x;
1709                 szRebar.cy = rcClient.bottom;
1710             }
1711             else
1712             {
1713                 ptRebar.y = StartSize.cy + GetSystemMetrics(SM_CYSIZEFRAME);
1714                 szRebar.cx = rcClient.right;
1715                 szRebar.cy = ptTrayNotify.y - ptRebar.y;
1716             }
1717 
1718             dwp = ::DeferWindowPos(dwp,
1719                                    m_Rebar,
1720                                    NULL,
1721                                    ptRebar.x,
1722                                    ptRebar.y,
1723                                    szRebar.cx,
1724                                    szRebar.cy,
1725                                    SWP_NOZORDER | SWP_NOACTIVATE);
1726         }
1727 
1728         if (dwp != NULL)
1729             EndDeferWindowPos(dwp);
1730 
1731         if (m_TaskSwitch != NULL)
1732         {
1733             /* Update the task switch window configuration */
1734             SendMessage(m_TaskSwitch, TSWM_UPDATETASKBARPOS, 0, 0);
1735         }
1736     }
1737 
1738     void FitToRebar(PRECT pRect)
1739     {
1740         /* Get the rect of the rebar */
1741         RECT rebarRect, taskbarRect, clientRect;
1742         ::GetWindowRect(m_Rebar, &rebarRect);
1743         ::GetWindowRect(m_hWnd, &taskbarRect);
1744         ::GetClientRect(m_hWnd, &clientRect);
1745         OffsetRect(&rebarRect, -taskbarRect.left, -taskbarRect.top);
1746 
1747         /* Calculate the difference of size of the taskbar and the rebar */
1748         SIZE margins;
1749         margins.cx = taskbarRect.right - taskbarRect.left - clientRect.right + clientRect.left;
1750         margins.cy = taskbarRect.bottom - taskbarRect.top - clientRect.bottom + clientRect.top;
1751 
1752         /* Calculate the new size of the rebar and make it resize, then change the new taskbar size */
1753         switch (m_Position)
1754         {
1755         case ABE_TOP:
1756             rebarRect.bottom = rebarRect.top + pRect->bottom - pRect->top - margins.cy;
1757             ::SendMessageW(m_Rebar, RB_SIZETORECT, RBSTR_CHANGERECT,  (LPARAM)&rebarRect);
1758             pRect->bottom = pRect->top + rebarRect.bottom - rebarRect.top + margins.cy;
1759             break;
1760         case ABE_BOTTOM:
1761             rebarRect.top = rebarRect.bottom - (pRect->bottom - pRect->top - margins.cy);
1762             ::SendMessageW(m_Rebar, RB_SIZETORECT, RBSTR_CHANGERECT,  (LPARAM)&rebarRect);
1763             pRect->top = pRect->bottom - (rebarRect.bottom - rebarRect.top + margins.cy);
1764             break;
1765         case ABE_LEFT:
1766             rebarRect.right = rebarRect.left + (pRect->right - pRect->left - margins.cx);
1767             ::SendMessageW(m_Rebar, RB_SIZETORECT, RBSTR_CHANGERECT,  (LPARAM)&rebarRect);
1768             pRect->right = pRect->left + (rebarRect.right - rebarRect.left + margins.cx);
1769             break;
1770         case ABE_RIGHT:
1771             rebarRect.left = rebarRect.right - (pRect->right - pRect->left - margins.cx);
1772             ::SendMessageW(m_Rebar, RB_SIZETORECT, RBSTR_CHANGERECT,  (LPARAM)&rebarRect);
1773             pRect->left = pRect->right - (rebarRect.right - rebarRect.left + margins.cx);
1774             break;
1775         }
1776 
1777         CalculateValidSize(m_Position, pRect);
1778     }
1779 
1780     void PopupStartMenu()
1781     {
1782         if (m_StartMenuPopup != NULL)
1783         {
1784             POINTL pt;
1785             RECTL rcExclude;
1786             DWORD dwFlags = 0;
1787 
1788             if (m_StartButton.GetWindowRect((RECT*) &rcExclude))
1789             {
1790                 switch (m_Position)
1791                 {
1792                 case ABE_BOTTOM:
1793                     pt.x = rcExclude.left;
1794                     pt.y = rcExclude.top;
1795                     dwFlags |= MPPF_TOP;
1796                     break;
1797                 case ABE_TOP:
1798                     pt.x = rcExclude.left;
1799                     pt.y = rcExclude.bottom;
1800                     dwFlags |= MPPF_BOTTOM;
1801                     break;
1802                 case ABE_LEFT:
1803                     pt.x = rcExclude.right;
1804                     pt.y = rcExclude.top;
1805                     dwFlags |= MPPF_RIGHT;
1806                     break;
1807                 case ABE_RIGHT:
1808                     pt.x = rcExclude.left;
1809                     pt.y = rcExclude.top;
1810                     dwFlags |= MPPF_LEFT;
1811                     break;
1812                 }
1813 
1814                 m_StartMenuPopup->Popup(&pt, &rcExclude, dwFlags);
1815 
1816                 m_StartButton.SendMessageW(BM_SETSTATE, TRUE, 0);
1817             }
1818         }
1819     }
1820 
1821     void ProcessMouseTracking()
1822     {
1823         RECT rcCurrent;
1824         POINT pt;
1825         BOOL over;
1826         UINT state = m_AutoHideState;
1827 
1828         GetCursorPos(&pt);
1829         GetWindowRect(&rcCurrent);
1830         over = PtInRect(&rcCurrent, pt);
1831 
1832         if (m_StartButton.SendMessage( BM_GETSTATE, 0, 0) != BST_UNCHECKED)
1833         {
1834             over = TRUE;
1835         }
1836 
1837         if (over)
1838         {
1839             if (state == AUTOHIDE_HIDING)
1840             {
1841                 TRACE("AutoHide cancelling hide.\n");
1842                 m_AutoHideState = AUTOHIDE_SHOWING;
1843                 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_INTERVAL_ANIMATING, NULL);
1844             }
1845             else if (state == AUTOHIDE_HIDDEN)
1846             {
1847                 TRACE("AutoHide starting show.\n");
1848                 m_AutoHideState = AUTOHIDE_SHOWING;
1849                 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_DELAY_SHOW, NULL);
1850             }
1851         }
1852         else
1853         {
1854             if (state == AUTOHIDE_SHOWING)
1855             {
1856                 TRACE("AutoHide cancelling show.\n");
1857                 m_AutoHideState = AUTOHIDE_HIDING;
1858                 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_INTERVAL_ANIMATING, NULL);
1859             }
1860             else if (state == AUTOHIDE_SHOWN)
1861             {
1862                 TRACE("AutoHide starting hide.\n");
1863                 m_AutoHideState = AUTOHIDE_HIDING;
1864                 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_DELAY_HIDE, NULL);
1865             }
1866 
1867             KillTimer(TIMER_ID_MOUSETRACK);
1868         }
1869     }
1870 
1871     void ProcessAutoHide()
1872     {
1873         INT w = m_TraySize.cx - GetSystemMetrics(SM_CXSIZEFRAME);
1874         INT h = m_TraySize.cy - GetSystemMetrics(SM_CYSIZEFRAME);
1875 
1876         switch (m_AutoHideState)
1877         {
1878         case AUTOHIDE_HIDING:
1879             switch (m_Position)
1880             {
1881             case ABE_LEFT:
1882                 m_AutoHideOffset.cy = 0;
1883                 m_AutoHideOffset.cx -= AUTOHIDE_SPEED_HIDE;
1884                 if (m_AutoHideOffset.cx < -w)
1885                     m_AutoHideOffset.cx = w;
1886                 break;
1887             case ABE_TOP:
1888                 m_AutoHideOffset.cx = 0;
1889                 m_AutoHideOffset.cy -= AUTOHIDE_SPEED_HIDE;
1890                 if (m_AutoHideOffset.cy < -h)
1891                     m_AutoHideOffset.cy = h;
1892                 break;
1893             case ABE_RIGHT:
1894                 m_AutoHideOffset.cy = 0;
1895                 m_AutoHideOffset.cx += AUTOHIDE_SPEED_HIDE;
1896                 if (m_AutoHideOffset.cx > w)
1897                     m_AutoHideOffset.cx = w;
1898                 break;
1899             case ABE_BOTTOM:
1900                 m_AutoHideOffset.cx = 0;
1901                 m_AutoHideOffset.cy += AUTOHIDE_SPEED_HIDE;
1902                 if (m_AutoHideOffset.cy > h)
1903                     m_AutoHideOffset.cy = h;
1904                 break;
1905             }
1906 
1907             if (m_AutoHideOffset.cx != w && m_AutoHideOffset.cy != h)
1908             {
1909                 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_INTERVAL_ANIMATING, NULL);
1910                 break;
1911             }
1912 
1913             /* fallthrough */
1914         case AUTOHIDE_HIDDEN:
1915 
1916             switch (m_Position)
1917             {
1918             case ABE_LEFT:
1919                 m_AutoHideOffset.cx = -w;
1920                 m_AutoHideOffset.cy = 0;
1921                 break;
1922             case ABE_TOP:
1923                 m_AutoHideOffset.cx = 0;
1924                 m_AutoHideOffset.cy = -h;
1925                 break;
1926             case ABE_RIGHT:
1927                 m_AutoHideOffset.cx = w;
1928                 m_AutoHideOffset.cy = 0;
1929                 break;
1930             case ABE_BOTTOM:
1931                 m_AutoHideOffset.cx = 0;
1932                 m_AutoHideOffset.cy = h;
1933                 break;
1934             }
1935 
1936             KillTimer(TIMER_ID_AUTOHIDE);
1937             m_AutoHideState = AUTOHIDE_HIDDEN;
1938             break;
1939 
1940         case AUTOHIDE_SHOWING:
1941             if (m_AutoHideOffset.cx >= AUTOHIDE_SPEED_SHOW)
1942             {
1943                 m_AutoHideOffset.cx -= AUTOHIDE_SPEED_SHOW;
1944             }
1945             else if (m_AutoHideOffset.cx <= -AUTOHIDE_SPEED_SHOW)
1946             {
1947                 m_AutoHideOffset.cx += AUTOHIDE_SPEED_SHOW;
1948             }
1949             else
1950             {
1951                 m_AutoHideOffset.cx = 0;
1952             }
1953 
1954             if (m_AutoHideOffset.cy >= AUTOHIDE_SPEED_SHOW)
1955             {
1956                 m_AutoHideOffset.cy -= AUTOHIDE_SPEED_SHOW;
1957             }
1958             else if (m_AutoHideOffset.cy <= -AUTOHIDE_SPEED_SHOW)
1959             {
1960                 m_AutoHideOffset.cy += AUTOHIDE_SPEED_SHOW;
1961             }
1962             else
1963             {
1964                 m_AutoHideOffset.cy = 0;
1965             }
1966 
1967             if (m_AutoHideOffset.cx != 0 || m_AutoHideOffset.cy != 0)
1968             {
1969                 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_INTERVAL_ANIMATING, NULL);
1970                 break;
1971             }
1972 
1973             /* fallthrough */
1974         case AUTOHIDE_SHOWN:
1975 
1976             KillTimer(TIMER_ID_AUTOHIDE);
1977             m_AutoHideState = AUTOHIDE_SHOWN;
1978             break;
1979         }
1980 
1981         SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER);
1982     }
1983 
1984 
1985 
1986 
1987 
1988     /**********************************************************
1989      *    ##### taskbar drawing #####
1990      */
1991 
1992     LRESULT EraseBackgroundWithTheme(HDC hdc)
1993     {
1994         RECT rect;
1995         int iSBkgndPart[4] = {TBP_BACKGROUNDLEFT, TBP_BACKGROUNDTOP, TBP_BACKGROUNDRIGHT, TBP_BACKGROUNDBOTTOM};
1996 
1997         ASSERT(m_Position <= ABE_BOTTOM);
1998 
1999         if (m_Theme)
2000         {
2001             GetClientRect(&rect);
2002             DrawThemeBackground(m_Theme, hdc, iSBkgndPart[m_Position], 0, &rect, 0);
2003         }
2004 
2005         return 0;
2006     }
2007 
2008     int DrawSizerWithTheme(IN HRGN hRgn)
2009     {
2010         HDC hdc;
2011         RECT rect;
2012         int iSizerPart[4] = {TBP_SIZINGBARLEFT, TBP_SIZINGBARTOP, TBP_SIZINGBARRIGHT, TBP_SIZINGBARBOTTOM};
2013         SIZE size;
2014 
2015         ASSERT(m_Position <= ABE_BOTTOM);
2016 
2017         HRESULT hr = GetThemePartSize(m_Theme, NULL, iSizerPart[m_Position], 0, NULL, TS_TRUE, &size);
2018         if (FAILED_UNEXPECTEDLY(hr))
2019             return 0;
2020 
2021         GetWindowRect(&rect);
2022         OffsetRect(&rect, -rect.left, -rect.top);
2023 
2024         hdc = GetWindowDC();
2025 
2026         switch (m_Position)
2027         {
2028         case ABE_LEFT:
2029             rect.left = rect.right - size.cx;
2030             break;
2031         case ABE_TOP:
2032             rect.top = rect.bottom - size.cy;
2033             break;
2034         case ABE_RIGHT:
2035             rect.right = rect.left + size.cx;
2036             break;
2037         case ABE_BOTTOM:
2038         default:
2039             rect.bottom = rect.top + size.cy;
2040             break;
2041         }
2042 
2043         DrawThemeBackground(m_Theme, hdc, iSizerPart[m_Position], 0, &rect, 0);
2044 
2045         ReleaseDC(hdc);
2046         return 0;
2047     }
2048 
2049 
2050 
2051 
2052 
2053     /*
2054      * ITrayWindow
2055      */
2056     HRESULT STDMETHODCALLTYPE Open()
2057     {
2058         RECT rcWnd;
2059 
2060         /* Check if there's already a window created and try to show it.
2061            If it was somehow destroyed just create a new tray window. */
2062         if (m_hWnd != NULL && IsWindow())
2063         {
2064             return S_OK;
2065         }
2066 
2067         DWORD dwExStyle = WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE;
2068         if (g_TaskbarSettings.sr.AlwaysOnTop)
2069             dwExStyle |= WS_EX_TOPMOST;
2070 
2071         DWORD dwStyle = WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
2072         if(!m_Theme)
2073         {
2074             dwStyle |= WS_THICKFRAME | WS_BORDER;
2075         }
2076 
2077         ZeroMemory(&rcWnd, sizeof(rcWnd));
2078         if (m_Position != (DWORD) -1)
2079             rcWnd = m_TrayRects[m_Position];
2080 
2081         if (!Create(NULL, rcWnd, NULL, dwStyle, dwExStyle))
2082             return E_FAIL;
2083 
2084         /* Align all controls on the tray window */
2085         AlignControls(NULL);
2086 
2087         /* Move the tray window to the right position and resize it if necessary */
2088         CheckTrayWndPosition();
2089 
2090         return S_OK;
2091     }
2092 
2093     HRESULT STDMETHODCALLTYPE Close()
2094     {
2095         if (m_hWnd != NULL)
2096         {
2097             SendMessage(m_hWnd,
2098                         WM_APP_TRAYDESTROY,
2099                         0,
2100                         0);
2101         }
2102 
2103         return S_OK;
2104     }
2105 
2106     HWND STDMETHODCALLTYPE GetHWND()
2107     {
2108         return m_hWnd;
2109     }
2110 
2111     BOOL STDMETHODCALLTYPE IsSpecialHWND(IN HWND hWnd)
2112     {
2113         return (m_hWnd == hWnd ||
2114                 (m_DesktopWnd != NULL && m_hWnd == m_DesktopWnd));
2115     }
2116 
2117     BOOL STDMETHODCALLTYPE IsHorizontal()
2118     {
2119         return IsPosHorizontal();
2120     }
2121 
2122     BOOL STDMETHODCALLTYPE Lock(IN BOOL bLock)
2123     {
2124         BOOL bPrevLock = g_TaskbarSettings.bLock;
2125 
2126         if (g_TaskbarSettings.bLock != bLock)
2127         {
2128             g_TaskbarSettings.bLock = bLock;
2129 
2130             if (m_TrayBandSite != NULL)
2131             {
2132                 if (!SUCCEEDED(m_TrayBandSite->Lock(bLock)))
2133                 {
2134                     /* Reset?? */
2135                     g_TaskbarSettings.bLock = bPrevLock;
2136                     return bPrevLock;
2137                 }
2138             }
2139 
2140             if (m_Theme)
2141             {
2142                 /* Update cached tray sizes */
2143                 for(DWORD Pos = ABE_LEFT; Pos <= ABE_BOTTOM; Pos++)
2144                 {
2145                     RECT rcGripper = {0};
2146                     AdjustSizerRect(&rcGripper, Pos);
2147 
2148                     if(g_TaskbarSettings.bLock)
2149                     {
2150                         m_TrayRects[Pos].top += rcGripper.top;
2151                         m_TrayRects[Pos].left += rcGripper.left;
2152                         m_TrayRects[Pos].bottom += rcGripper.bottom;
2153                         m_TrayRects[Pos].right += rcGripper.right;
2154                     }
2155                     else
2156                     {
2157                         m_TrayRects[Pos].top -= rcGripper.top;
2158                         m_TrayRects[Pos].left -= rcGripper.left;
2159                         m_TrayRects[Pos].bottom -= rcGripper.bottom;
2160                         m_TrayRects[Pos].right -= rcGripper.right;
2161                     }
2162                 }
2163             }
2164             SetWindowPos(NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER);
2165             ResizeWorkArea();
2166             ApplyClipping(TRUE);
2167         }
2168 
2169         return bPrevLock;
2170     }
2171 
2172 
2173     /*
2174      *  IContextMenu
2175      */
2176     HRESULT STDMETHODCALLTYPE QueryContextMenu(HMENU hPopup,
2177                                                UINT indexMenu,
2178                                                UINT idCmdFirst,
2179                                                UINT idCmdLast,
2180                                                UINT uFlags)
2181     {
2182         if (!m_ContextMenu)
2183         {
2184             HRESULT hr = TrayWindowCtxMenuCreator(this, m_hWnd, &m_ContextMenu);
2185             if (FAILED_UNEXPECTEDLY(hr))
2186                 return hr;
2187         }
2188 
2189         return m_ContextMenu->QueryContextMenu(hPopup, indexMenu, idCmdFirst, idCmdLast, uFlags);
2190     }
2191 
2192     HRESULT STDMETHODCALLTYPE InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
2193     {
2194         if (!m_ContextMenu)
2195             return E_INVALIDARG;
2196 
2197         return m_ContextMenu->InvokeCommand(lpici);
2198     }
2199 
2200     HRESULT STDMETHODCALLTYPE GetCommandString(UINT_PTR idCmd,
2201                                                UINT uType,
2202                                                UINT *pwReserved,
2203                                                LPSTR pszName,
2204                                                UINT cchMax)
2205     {
2206         if (!m_ContextMenu)
2207             return E_INVALIDARG;
2208 
2209         return m_ContextMenu->GetCommandString(idCmd, uType, pwReserved, pszName, cchMax);
2210     }
2211 
2212     /**********************************************************
2213      *    ##### message handling #####
2214      */
2215 
2216     LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2217     {
2218         HRESULT hRet;
2219 
2220         ((ITrayWindow*)this)->AddRef();
2221 
2222         SetWindowTheme(m_hWnd, L"TaskBar", NULL);
2223 
2224         /* Create the Start button */
2225         m_StartButton.Create(m_hWnd);
2226 
2227         /* Load the saved tray window settings */
2228         RegLoadSettings();
2229 
2230         /* Create and initialize the start menu */
2231         HBITMAP hbmBanner = LoadBitmapW(hExplorerInstance, MAKEINTRESOURCEW(IDB_STARTMENU));
2232         m_StartMenuPopup = CreateStartMenu(this, &m_StartMenuBand, hbmBanner, 0);
2233 
2234         /* Create the task band */
2235         hRet = CTaskBand_CreateInstance(this, m_StartButton.m_hWnd, IID_PPV_ARG(IDeskBand, &m_TaskBand));
2236         if (FAILED_UNEXPECTEDLY(hRet))
2237             return FALSE;
2238 
2239         /* Create the rebar band site. This actually creates the rebar and the tasks toolbar. */
2240         hRet = CTrayBandSite_CreateInstance(this, m_TaskBand, &m_TrayBandSite);
2241         if (FAILED_UNEXPECTEDLY(hRet))
2242             return FALSE;
2243 
2244         /* Create the tray notification window */
2245         hRet = CTrayNotifyWnd_CreateInstance(m_hWnd, IID_PPV_ARG(IUnknown, &m_TrayNotifyInstance));
2246         if (FAILED_UNEXPECTEDLY(hRet))
2247             return FALSE;
2248 
2249         /* Get the hwnd of the rebar */
2250         hRet = IUnknown_GetWindow(m_TrayBandSite, &m_Rebar);
2251         if (FAILED_UNEXPECTEDLY(hRet))
2252             return FALSE;
2253 
2254         /* Get the hwnd of the tasks toolbar */
2255         hRet = IUnknown_GetWindow(m_TaskBand, &m_TaskSwitch);
2256         if (FAILED_UNEXPECTEDLY(hRet))
2257             return FALSE;
2258 
2259         /* Get the hwnd of the tray notification window */
2260         hRet = IUnknown_GetWindow(m_TrayNotifyInstance, &m_TrayNotify);
2261         if (FAILED_UNEXPECTEDLY(hRet))
2262             return FALSE;
2263 
2264         SetWindowTheme(m_Rebar, L"TaskBar", NULL);
2265 
2266         UpdateFonts();
2267 
2268         InitShellServices(&m_ShellServices);
2269 
2270         if (g_TaskbarSettings.sr.AutoHide)
2271         {
2272             m_AutoHideState = AUTOHIDE_HIDING;
2273             SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_DELAY_HIDE, NULL);
2274         }
2275 
2276         /* Set the initial lock state in the band site */
2277         m_TrayBandSite->Lock(g_TaskbarSettings.bLock);
2278 
2279         RegisterHotKey(m_hWnd, IDHK_RUN, MOD_WIN, 'R');
2280         RegisterHotKey(m_hWnd, IDHK_MINIMIZE_ALL, MOD_WIN, 'M');
2281         RegisterHotKey(m_hWnd, IDHK_RESTORE_ALL, MOD_WIN|MOD_SHIFT, 'M');
2282         RegisterHotKey(m_hWnd, IDHK_HELP, MOD_WIN, VK_F1);
2283         RegisterHotKey(m_hWnd, IDHK_EXPLORE, MOD_WIN, 'E');
2284         RegisterHotKey(m_hWnd, IDHK_FIND, MOD_WIN, 'F');
2285         RegisterHotKey(m_hWnd, IDHK_FIND_COMPUTER, MOD_WIN|MOD_CONTROL, 'F');
2286         RegisterHotKey(m_hWnd, IDHK_NEXT_TASK, MOD_WIN, VK_TAB);
2287         RegisterHotKey(m_hWnd, IDHK_PREV_TASK, MOD_WIN|MOD_SHIFT, VK_TAB);
2288         RegisterHotKey(m_hWnd, IDHK_SYS_PROPERTIES, MOD_WIN, VK_PAUSE);
2289         RegisterHotKey(m_hWnd, IDHK_DESKTOP, MOD_WIN, 'D');
2290         RegisterHotKey(m_hWnd, IDHK_PAGER, MOD_WIN, 'B');
2291 
2292         return TRUE;
2293     }
2294 
2295     LRESULT OnThemeChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2296     {
2297         if (m_Theme)
2298             CloseThemeData(m_Theme);
2299 
2300         m_Theme = OpenThemeData(m_hWnd, L"TaskBar");
2301 
2302         if (m_Theme)
2303         {
2304             SetWindowStyle(m_hWnd, WS_THICKFRAME | WS_BORDER, 0);
2305         }
2306         else
2307         {
2308             SetWindowStyle(m_hWnd, WS_THICKFRAME | WS_BORDER, WS_THICKFRAME | WS_BORDER);
2309         }
2310         SetWindowPos(NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
2311 
2312         return TRUE;
2313     }
2314 
2315     LRESULT OnSettingChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2316     {
2317         if (wParam == SPI_SETNONCLIENTMETRICS)
2318         {
2319             SendMessage(m_TrayNotify, uMsg, wParam, lParam);
2320             SendMessage(m_TaskSwitch, uMsg, wParam, lParam);
2321             UpdateFonts();
2322             AlignControls(NULL);
2323             CheckTrayWndPosition();
2324         }
2325 
2326         return 0;
2327     }
2328 
2329     LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2330     {
2331         HDC hdc = (HDC) wParam;
2332 
2333         if (!m_Theme)
2334         {
2335             bHandled = FALSE;
2336             return 0;
2337         }
2338 
2339         return EraseBackgroundWithTheme(hdc);
2340     }
2341 
2342     LRESULT OnDisplayChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2343     {
2344         /* Load the saved tray window settings */
2345         RegLoadSettings();
2346 
2347         /* Move the tray window to the right position and resize it if necessary */
2348         CheckTrayWndPosition();
2349 
2350         return TRUE;
2351     }
2352 
2353     LRESULT OnCopyData(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2354     {
2355         COPYDATASTRUCT *pCopyData = reinterpret_cast<COPYDATASTRUCT *>(lParam);
2356         switch (pCopyData->dwData)
2357         {
2358             case TABDMC_APPBAR:
2359                 return appbar_message(pCopyData);
2360             case TABDMC_NOTIFY:
2361             case TABDMC_LOADINPROC:
2362                 return ::SendMessageW(m_TrayNotify, uMsg, wParam, lParam);
2363         }
2364         return FALSE;
2365     }
2366 
2367     LRESULT OnNcPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2368     {
2369         if (!m_Theme)
2370         {
2371             bHandled = FALSE;
2372             return 0;
2373         }
2374         else if (g_TaskbarSettings.bLock)
2375         {
2376             return 0;
2377         }
2378 
2379         return DrawSizerWithTheme((HRGN) wParam);
2380     }
2381 
2382     LRESULT OnCtlColorBtn(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2383     {
2384         SetBkMode((HDC) wParam, TRANSPARENT);
2385         return (LRESULT) GetStockObject(HOLLOW_BRUSH);
2386     }
2387 
2388     LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2389     {
2390         RECT rcClient;
2391         POINT pt;
2392 
2393         if (g_TaskbarSettings.bLock)
2394         {
2395             /* The user may not be able to resize the tray window.
2396             Pretend like the window is not sizeable when the user
2397             clicks on the border. */
2398             return HTBORDER;
2399         }
2400 
2401         SetLastError(ERROR_SUCCESS);
2402         if (GetClientRect(&rcClient) &&
2403             (MapWindowPoints(NULL, (LPPOINT) &rcClient, 2) != 0 || GetLastError() == ERROR_SUCCESS))
2404         {
2405             pt.x = (SHORT) LOWORD(lParam);
2406             pt.y = (SHORT) HIWORD(lParam);
2407 
2408             if (PtInRect(&rcClient, pt))
2409             {
2410                 /* The user is trying to drag the tray window */
2411                 return HTCAPTION;
2412             }
2413 
2414             /* Depending on the position of the tray window, allow only
2415             changing the border next to the monitor working area */
2416             switch (m_Position)
2417             {
2418             case ABE_TOP:
2419                 if (pt.y > rcClient.bottom)
2420                     return HTBOTTOM;
2421                 break;
2422             case ABE_LEFT:
2423                 if (pt.x > rcClient.right)
2424                     return HTRIGHT;
2425                 break;
2426             case ABE_RIGHT:
2427                 if (pt.x < rcClient.left)
2428                     return HTLEFT;
2429                 break;
2430             case ABE_BOTTOM:
2431             default:
2432                 if (pt.y < rcClient.top)
2433                     return HTTOP;
2434                 break;
2435             }
2436         }
2437         return HTBORDER;
2438     }
2439 
2440     LRESULT OnMoving(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2441     {
2442         POINT ptCursor;
2443         PRECT pRect = (PRECT) lParam;
2444 
2445         /* We need to ensure that an application can not accidently
2446         move the tray window (using SetWindowPos). However, we still
2447         need to be able to move the window in case the user wants to
2448         drag the tray window to another position or in case the user
2449         wants to resize the tray window. */
2450         if (!g_TaskbarSettings.bLock && GetCursorPos(&ptCursor))
2451         {
2452             IsDragging = TRUE;
2453             m_DraggingPosition = GetDraggingRectFromPt(ptCursor, pRect, &m_DraggingMonitor);
2454         }
2455         else
2456         {
2457             *pRect = m_TrayRects[m_Position];
2458         }
2459         return TRUE;
2460     }
2461 
2462     LRESULT OnSizing(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2463     {
2464         PRECT pRect = (PRECT) lParam;
2465 
2466         if (!g_TaskbarSettings.bLock)
2467         {
2468             FitToRebar(pRect);
2469         }
2470         else
2471         {
2472             *pRect = m_TrayRects[m_Position];
2473         }
2474         return TRUE;
2475     }
2476 
2477     LRESULT OnWindowPosChanging(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2478     {
2479         ChangingWinPos((LPWINDOWPOS) lParam);
2480         return TRUE;
2481     }
2482 
2483     LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2484     {
2485         RECT rcClient;
2486         if (wParam == SIZE_RESTORED && lParam == 0)
2487         {
2488             ResizeWorkArea();
2489             /* Clip the tray window on multi monitor systems so the edges can't
2490             overlap into another monitor */
2491             ApplyClipping(TRUE);
2492 
2493             if (!GetClientRect(&rcClient))
2494             {
2495                 return FALSE;
2496             }
2497         }
2498         else
2499         {
2500             rcClient.left = rcClient.top = 0;
2501             rcClient.right = LOWORD(lParam);
2502             rcClient.bottom = HIWORD(lParam);
2503         }
2504 
2505         AlignControls(&rcClient);
2506         return TRUE;
2507     }
2508 
2509     LRESULT OnEnterSizeMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2510     {
2511         InSizeMove = TRUE;
2512         IsDragging = FALSE;
2513         if (!g_TaskbarSettings.bLock)
2514         {
2515             /* Remove the clipping on multi monitor systems while dragging around */
2516             ApplyClipping(FALSE);
2517         }
2518         return TRUE;
2519     }
2520 
2521     LRESULT OnExitSizeMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2522     {
2523         InSizeMove = FALSE;
2524         if (!g_TaskbarSettings.bLock)
2525         {
2526             FitToRebar(&m_TrayRects[m_Position]);
2527 
2528             /* Apply clipping */
2529             PostMessage(WM_SIZE, SIZE_RESTORED, 0);
2530         }
2531         return TRUE;
2532     }
2533 
2534     LRESULT OnSysChar(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2535     {
2536         switch (wParam)
2537         {
2538         case TEXT(' '):
2539         {
2540             /* The user pressed Alt+Space, this usually brings up the system menu of a window.
2541             The tray window needs to handle this specially, since it normally doesn't have
2542             a system menu. */
2543 
2544             static const UINT uidDisableItem [] = {
2545                 SC_RESTORE,
2546                 SC_MOVE,
2547                 SC_SIZE,
2548                 SC_MAXIMIZE,
2549                 SC_MINIMIZE,
2550             };
2551             HMENU hSysMenu;
2552             UINT i, uId;
2553 
2554             /* temporarily enable the system menu */
2555             SetWindowStyle(m_hWnd, WS_SYSMENU, WS_SYSMENU);
2556 
2557             hSysMenu = GetSystemMenu(FALSE);
2558             if (hSysMenu != NULL)
2559             {
2560                 /* Disable all items that are not relevant */
2561                 for (i = 0; i < _countof(uidDisableItem); i++)
2562                 {
2563                     EnableMenuItem(hSysMenu,
2564                                    uidDisableItem[i],
2565                                    MF_BYCOMMAND | MF_GRAYED);
2566                 }
2567 
2568                 EnableMenuItem(hSysMenu,
2569                                SC_CLOSE,
2570                                MF_BYCOMMAND |
2571                                (SHRestricted(REST_NOCLOSE) ? MF_GRAYED : MF_ENABLED));
2572 
2573                 /* Display the system menu */
2574                 uId = TrackMenu(
2575                     hSysMenu,
2576                     NULL,
2577                     m_StartButton.m_hWnd,
2578                     m_Position != ABE_TOP,
2579                     FALSE);
2580                 if (uId != 0)
2581                 {
2582                     SendMessage(m_hWnd, WM_SYSCOMMAND, (WPARAM) uId, 0);
2583                 }
2584             }
2585 
2586             /* revert the system menu window style */
2587             SetWindowStyle(m_hWnd, WS_SYSMENU, 0);
2588             break;
2589         }
2590 
2591         default:
2592             bHandled = FALSE;
2593         }
2594         return TRUE;
2595     }
2596 
2597     LRESULT OnNcLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2598     {
2599         /* This handler implements the trick that makes  the start button to
2600            get pressed when the user clicked left or below the button */
2601 
2602         POINT pt = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
2603         WINDOWINFO wi = {sizeof(WINDOWINFO)};
2604         RECT rcStartBtn;
2605 
2606         bHandled = FALSE;
2607 
2608         m_StartButton.GetWindowRect(&rcStartBtn);
2609         GetWindowInfo(m_hWnd, &wi);
2610 
2611         switch (m_Position)
2612         {
2613             case ABE_TOP:
2614             case ABE_LEFT:
2615             {
2616                 if (pt.x > rcStartBtn.right || pt.y > rcStartBtn.bottom)
2617                     return 0;
2618                 break;
2619             }
2620             case ABE_RIGHT:
2621             {
2622                 if (pt.x < rcStartBtn.left || pt.y > rcStartBtn.bottom)
2623                     return 0;
2624 
2625                 if (rcStartBtn.right + (int)wi.cxWindowBorders * 2 + 1 < wi.rcWindow.right &&
2626                     pt.x > rcStartBtn.right)
2627                 {
2628                     return 0;
2629                 }
2630                 break;
2631             }
2632             case ABE_BOTTOM:
2633             {
2634                 if (pt.x > rcStartBtn.right || pt.y < rcStartBtn.top)
2635                 {
2636                     return 0;
2637                 }
2638 
2639                 if (rcStartBtn.bottom + (int)wi.cyWindowBorders * 2 + 1 < wi.rcWindow.bottom &&
2640                     pt.y > rcStartBtn.bottom)
2641                 {
2642                     return 0;
2643                 }
2644 
2645                 break;
2646             }
2647         }
2648 
2649         bHandled = TRUE;
2650         PopupStartMenu();
2651         return 0;
2652     }
2653 
2654     LRESULT OnNcRButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2655     {
2656         /* We want the user to be able to get a context menu even on the nonclient
2657         area (including the sizing border)! */
2658         uMsg = WM_CONTEXTMENU;
2659         wParam = (WPARAM) m_hWnd;
2660 
2661         return OnContextMenu(uMsg, wParam, lParam, bHandled);
2662     }
2663 
2664     LRESULT OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2665     {
2666         LRESULT Ret = FALSE;
2667         POINT pt, *ppt = NULL;
2668         HWND hWndExclude = NULL;
2669 
2670         /* Check if the administrator has forbidden access to context menus */
2671         if (SHRestricted(REST_NOTRAYCONTEXTMENU))
2672             return FALSE;
2673 
2674         pt.x = (SHORT) LOWORD(lParam);
2675         pt.y = (SHORT) HIWORD(lParam);
2676 
2677         if (pt.x != -1 || pt.y != -1)
2678             ppt = &pt;
2679         else
2680             hWndExclude = m_StartButton.m_hWnd;
2681 
2682         if ((HWND) wParam == m_StartButton.m_hWnd)
2683         {
2684             /* Make sure we can't track the context menu if the start
2685             menu is currently being shown */
2686             if (!(m_StartButton.SendMessage(BM_GETSTATE, 0, 0) & BST_PUSHED))
2687             {
2688                 CComPtr<IContextMenu> ctxMenu;
2689                 CStartMenuBtnCtxMenu_CreateInstance(this, m_hWnd, &ctxMenu);
2690                 TrackCtxMenu(ctxMenu, ppt, hWndExclude, m_Position == ABE_BOTTOM, this);
2691             }
2692         }
2693         else
2694         {
2695             /* See if the context menu should be handled by the task band site */
2696             if (ppt != NULL && m_TrayBandSite != NULL)
2697             {
2698                 HWND hWndAtPt;
2699                 POINT ptClient = *ppt;
2700 
2701                 /* Convert the coordinates to client-coordinates */
2702                 ::MapWindowPoints(NULL, m_hWnd, &ptClient, 1);
2703 
2704                 hWndAtPt = ChildWindowFromPoint(ptClient);
2705                 if (hWndAtPt != NULL &&
2706                     (hWndAtPt == m_Rebar || ::IsChild(m_Rebar, hWndAtPt)))
2707                 {
2708                     /* Check if the user clicked on the task switch window */
2709                     ptClient = *ppt;
2710                     ::MapWindowPoints(NULL, m_Rebar, &ptClient, 1);
2711 
2712                     hWndAtPt = ::ChildWindowFromPointEx(m_Rebar, ptClient, CWP_SKIPINVISIBLE | CWP_SKIPDISABLED);
2713                     if (hWndAtPt == m_TaskSwitch)
2714                         goto HandleTrayContextMenu;
2715 
2716                     /* Forward the message to the task band site */
2717                     m_TrayBandSite->ProcessMessage(m_hWnd, uMsg, wParam, lParam, &Ret);
2718                 }
2719                 else
2720                     goto HandleTrayContextMenu;
2721             }
2722             else
2723             {
2724 HandleTrayContextMenu:
2725                 /* Tray the default tray window context menu */
2726                 TrackCtxMenu(this, ppt, NULL, FALSE, this);
2727             }
2728         }
2729         return Ret;
2730     }
2731 
2732     LRESULT OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2733     {
2734         LRESULT Ret = FALSE;
2735         /* FIXME: We can't check with IsChild whether the hwnd is somewhere inside
2736         the rebar control! But we shouldn't forward messages that the band
2737         site doesn't handle, such as other controls (start button, tray window */
2738 
2739         HRESULT hr = E_FAIL;
2740 
2741         if (m_TrayBandSite)
2742         {
2743             hr = m_TrayBandSite->ProcessMessage(m_hWnd, uMsg, wParam, lParam, &Ret);
2744             if (SUCCEEDED(hr))
2745                 return Ret;
2746         }
2747 
2748         if (m_TrayBandSite == NULL || FAILED(hr))
2749         {
2750             const NMHDR *nmh = (const NMHDR *) lParam;
2751 
2752             if (nmh->hwndFrom == m_TrayNotify)
2753             {
2754                 switch (nmh->code)
2755                 {
2756                 case NTNWM_REALIGN:
2757                     /* Cause all controls to be aligned */
2758                     PostMessage(WM_SIZE, SIZE_RESTORED, 0);
2759                     break;
2760                 }
2761             }
2762         }
2763         return Ret;
2764     }
2765 
2766     LRESULT OnNcLButtonDblClick(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2767     {
2768         /* Let the clock handle the double click */
2769         ::SendMessageW(m_TrayNotify, uMsg, wParam, lParam);
2770 
2771         /* We "handle" this message so users can't cause a weird maximize/restore
2772         window animation when double-clicking the tray window! */
2773         return TRUE;
2774     }
2775 
2776     LRESULT OnAppTrayDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2777     {
2778         DestroyWindow();
2779         return TRUE;
2780     }
2781 
2782     LRESULT OnOpenStartMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2783     {
2784         HWND hwndStartMenu;
2785         HRESULT hr = IUnknown_GetWindow(m_StartMenuPopup, &hwndStartMenu);
2786         if (FAILED_UNEXPECTEDLY(hr))
2787             return FALSE;
2788 
2789         if (::IsWindowVisible(hwndStartMenu))
2790         {
2791             m_StartMenuPopup->OnSelect(MPOS_CANCELLEVEL);
2792         }
2793         else
2794         {
2795             PopupStartMenu();
2796         }
2797 
2798         return TRUE;
2799     }
2800 
2801     LRESULT OnDoExitWindows(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2802     {
2803         /*
2804          * TWM_DOEXITWINDOWS is send by the CDesktopBrowser to us
2805          * to show the shutdown dialog. Also a WM_CLOSE message sent
2806          * by apps should show the dialog.
2807          */
2808         return DoExitWindows();
2809     }
2810 
2811     LRESULT OnSysCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2812     {
2813         if (wParam == SC_CLOSE)
2814         {
2815             return DoExitWindows();
2816         }
2817 
2818         bHandled = FALSE;
2819         return TRUE;
2820     }
2821 
2822     LRESULT OnGetTaskSwitch(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2823     {
2824         bHandled = TRUE;
2825         return (LRESULT)m_TaskSwitch;
2826     }
2827 
2828     LRESULT OnHotkey(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2829     {
2830         return HandleHotKey(wParam);
2831     }
2832 
2833     struct MINIMIZE_INFO
2834     {
2835         HWND hwndDesktop;
2836         HWND hTrayWnd;
2837         HWND hwndProgman;
2838         BOOL bRet;
2839         CSimpleArray<HWND> *pMinimizedAll;
2840         BOOL bShowDesktop;
2841     };
2842 
2843     static BOOL IsDialog(HWND hwnd)
2844     {
2845         WCHAR szClass[32];
2846         GetClassNameW(hwnd, szClass, _countof(szClass));
2847         return wcscmp(szClass, L"#32770") == 0;
2848     }
2849 
2850     static BOOL CALLBACK MinimizeWindowsProc(HWND hwnd, LPARAM lParam)
2851     {
2852         MINIMIZE_INFO *info = (MINIMIZE_INFO *)lParam;
2853         if (hwnd == info->hwndDesktop || hwnd == info->hTrayWnd ||
2854             hwnd == info->hwndProgman)
2855         {
2856             return TRUE;
2857         }
2858         if (!info->bShowDesktop)
2859         {
2860             if (!::IsWindowEnabled(hwnd) || IsDialog(hwnd))
2861                 return TRUE;
2862             HWND hwndOwner = ::GetWindow(hwnd, GW_OWNER);
2863             if (hwndOwner && !::IsWindowEnabled(hwndOwner))
2864                 return TRUE;
2865         }
2866         if (::IsWindowVisible(hwnd) && !::IsIconic(hwnd))
2867         {
2868             ::ShowWindowAsync(hwnd, SW_MINIMIZE);
2869             info->bRet = TRUE;
2870             info->pMinimizedAll->Add(hwnd);
2871         }
2872         return TRUE;
2873     }
2874 
2875     VOID MinimizeAll(BOOL bShowDesktop = FALSE)
2876     {
2877         MINIMIZE_INFO info;
2878         info.hwndDesktop = GetDesktopWindow();;
2879         info.hTrayWnd = FindWindowW(L"Shell_TrayWnd", NULL);
2880         info.hwndProgman = FindWindowW(L"Progman", NULL);
2881         info.bRet = FALSE;
2882         info.pMinimizedAll = &g_MinimizedAll;
2883         info.bShowDesktop = bShowDesktop;
2884         EnumWindows(MinimizeWindowsProc, (LPARAM)&info);
2885 
2886         // invalid handles should be cleared to avoid mismatch of handles
2887         for (INT i = 0; i < g_MinimizedAll.GetSize(); ++i)
2888         {
2889             if (!::IsWindow(g_MinimizedAll[i]))
2890                 g_MinimizedAll[i] = NULL;
2891         }
2892 
2893         ::SetForegroundWindow(m_DesktopWnd);
2894         ::SetFocus(m_DesktopWnd);
2895     }
2896 
2897     VOID ShowDesktop()
2898     {
2899         MinimizeAll(TRUE);
2900     }
2901 
2902     VOID RestoreAll()
2903     {
2904         for (INT i = g_MinimizedAll.GetSize() - 1; i >= 0; --i)
2905         {
2906             HWND hwnd = g_MinimizedAll[i];
2907             if (::IsWindowVisible(hwnd) && ::IsIconic(hwnd))
2908             {
2909                 ::ShowWindowAsync(hwnd, SW_RESTORE);
2910             }
2911         }
2912         g_MinimizedAll.RemoveAll();
2913     }
2914 
2915     LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2916     {
2917         LRESULT Ret = FALSE;
2918 
2919         if ((HWND) lParam == m_StartButton.m_hWnd)
2920         {
2921             return FALSE;
2922         }
2923 
2924         if (m_TrayBandSite == NULL || FAILED_UNEXPECTEDLY(m_TrayBandSite->ProcessMessage(m_hWnd, uMsg, wParam, lParam, &Ret)))
2925         {
2926             return HandleCommand(LOWORD(wParam));
2927         }
2928         return Ret;
2929     }
2930 
2931     LRESULT OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2932     {
2933         if (g_TaskbarSettings.sr.AutoHide)
2934         {
2935             SetTimer(TIMER_ID_MOUSETRACK, MOUSETRACK_INTERVAL, NULL);
2936         }
2937 
2938         return TRUE;
2939     }
2940 
2941     LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2942     {
2943         if (wParam == TIMER_ID_MOUSETRACK)
2944         {
2945             ProcessMouseTracking();
2946         }
2947         else if (wParam == TIMER_ID_AUTOHIDE)
2948         {
2949             ProcessAutoHide();
2950         }
2951 
2952         bHandled = FALSE;
2953         return TRUE;
2954     }
2955 
2956     LRESULT OnNcCalcSize(INT code, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2957     {
2958         RECT *rc = NULL;
2959         /* Ignore WM_NCCALCSIZE if we are not themed or locked */
2960         if(!m_Theme || g_TaskbarSettings.bLock)
2961         {
2962             bHandled = FALSE;
2963             return 0;
2964         }
2965         if(!wParam)
2966         {
2967             rc = (RECT*)wParam;
2968         }
2969         else
2970         {
2971             NCCALCSIZE_PARAMS *prms = (NCCALCSIZE_PARAMS*)lParam;
2972             if(prms->lppos->flags & SWP_NOSENDCHANGING)
2973             {
2974                 bHandled = FALSE;
2975                 return 0;
2976             }
2977             rc = &prms->rgrc[0];
2978         }
2979 
2980         AdjustSizerRect(rc, m_Position);
2981 
2982         return 0;
2983     }
2984 
2985     LRESULT OnInitMenuPopup(INT code, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2986     {
2987         HMENU hMenu = (HMENU)wParam;
2988         if (::IsThereAnyEffectiveWindow(FALSE))
2989         {
2990             ::EnableMenuItem(hMenu, ID_SHELL_CMD_CASCADE_WND, MF_BYCOMMAND | MF_ENABLED);
2991             ::EnableMenuItem(hMenu, ID_SHELL_CMD_TILE_WND_H, MF_BYCOMMAND | MF_ENABLED);
2992             ::EnableMenuItem(hMenu, ID_SHELL_CMD_TILE_WND_V, MF_BYCOMMAND | MF_ENABLED);
2993         }
2994         else
2995         {
2996             ::EnableMenuItem(hMenu, ID_SHELL_CMD_CASCADE_WND, MF_BYCOMMAND | MF_GRAYED);
2997             ::EnableMenuItem(hMenu, ID_SHELL_CMD_TILE_WND_H, MF_BYCOMMAND | MF_GRAYED);
2998             ::EnableMenuItem(hMenu, ID_SHELL_CMD_TILE_WND_V, MF_BYCOMMAND | MF_GRAYED);
2999         }
3000         return 0;
3001     }
3002 
3003     LRESULT OnRebarAutoSize(INT code, LPNMHDR nmhdr, BOOL& bHandled)
3004     {
3005 #if 0
3006         LPNMRBAUTOSIZE as = (LPNMRBAUTOSIZE) nmhdr;
3007 
3008         if (!as->fChanged)
3009             return 0;
3010 
3011         RECT rc;
3012         ::GetWindowRect(m_hWnd, &rc);
3013 
3014         SIZE szWindow = {
3015             rc.right - rc.left,
3016             rc.bottom - rc.top };
3017         SIZE szTarget = {
3018             as->rcTarget.right - as->rcTarget.left,
3019             as->rcTarget.bottom - as->rcTarget.top };
3020         SIZE szActual = {
3021             as->rcActual.right - as->rcActual.left,
3022             as->rcActual.bottom - as->rcActual.top };
3023 
3024         SIZE borders = {
3025             szWindow.cx - szTarget.cx,
3026             szWindow.cy - szTarget.cx,
3027         };
3028 
3029         switch (m_Position)
3030         {
3031         case ABE_LEFT:
3032             szWindow.cx = szActual.cx + borders.cx;
3033             break;
3034         case ABE_TOP:
3035             szWindow.cy = szActual.cy + borders.cy;
3036             break;
3037         case ABE_RIGHT:
3038             szWindow.cx = szActual.cx + borders.cx;
3039             rc.left = rc.right - szWindow.cy;
3040             break;
3041         case ABE_BOTTOM:
3042             szWindow.cy = szActual.cy + borders.cy;
3043             rc.top = rc.bottom - szWindow.cy;
3044             break;
3045         }
3046 
3047         SetWindowPos(NULL, rc.left, rc.top, szWindow.cx, szWindow.cy, SWP_NOACTIVATE | SWP_NOZORDER);
3048 #else
3049         bHandled = FALSE;
3050 #endif
3051         return 0;
3052     }
3053 
3054     LRESULT OnTaskbarSettingsChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
3055     {
3056         TaskbarSettings* newSettings = (TaskbarSettings*)lParam;
3057 
3058         /* Propagate the new settings to the children */
3059         ::SendMessageW(m_TaskSwitch, uMsg, wParam, lParam);
3060         ::SendMessageW(m_TrayNotify, uMsg, wParam, lParam);
3061 
3062         /* Toggle autohide */
3063         if (newSettings->sr.AutoHide != g_TaskbarSettings.sr.AutoHide)
3064         {
3065             g_TaskbarSettings.sr.AutoHide = newSettings->sr.AutoHide;
3066             memset(&m_AutoHideOffset, 0, sizeof(m_AutoHideOffset));
3067             m_AutoHideState = AUTOHIDE_SHOWN;
3068             if (!newSettings->sr.AutoHide)
3069                 SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER);
3070             else
3071                 SetTimer(TIMER_ID_MOUSETRACK, MOUSETRACK_INTERVAL, NULL);
3072         }
3073 
3074         /* Toggle lock state */
3075         Lock(newSettings->bLock);
3076 
3077         /* Toggle OnTop state */
3078         if (newSettings->sr.AlwaysOnTop != g_TaskbarSettings.sr.AlwaysOnTop)
3079         {
3080             g_TaskbarSettings.sr.AlwaysOnTop = newSettings->sr.AlwaysOnTop;
3081             HWND hWndInsertAfter = newSettings->sr.AlwaysOnTop ? HWND_TOPMOST : HWND_BOTTOM;
3082             SetWindowPos(hWndInsertAfter, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
3083         }
3084 
3085         g_TaskbarSettings.Save();
3086         return 0;
3087     }
3088 
3089     DECLARE_WND_CLASS_EX(szTrayWndClass, CS_DBLCLKS, COLOR_3DFACE)
3090 
3091     BEGIN_MSG_MAP(CTrayWindow)
3092         if (m_StartMenuBand != NULL)
3093         {
3094             MSG Msg;
3095             LRESULT lRet;
3096 
3097             Msg.hwnd = m_hWnd;
3098             Msg.message = uMsg;
3099             Msg.wParam = wParam;
3100             Msg.lParam = lParam;
3101 
3102             if (m_StartMenuBand->TranslateMenuMessage(&Msg, &lRet) == S_OK)
3103             {
3104                 return lRet;
3105             }
3106 
3107             wParam = Msg.wParam;
3108             lParam = Msg.lParam;
3109         }
3110         MESSAGE_HANDLER(WM_THEMECHANGED, OnThemeChanged)
3111         MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChanged)
3112         NOTIFY_CODE_HANDLER(RBN_AUTOSIZE, OnRebarAutoSize) // Doesn't quite work ;P
3113         MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
3114         MESSAGE_HANDLER(WM_SIZE, OnSize)
3115         MESSAGE_HANDLER(WM_CREATE, OnCreate)
3116         /*MESSAGE_HANDLER(WM_DESTROY, OnDestroy)*/
3117         MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTest)
3118         MESSAGE_HANDLER(WM_COMMAND, OnCommand)
3119         MESSAGE_HANDLER(WM_SYSCOMMAND, OnSysCommand)
3120         MESSAGE_HANDLER(WM_NOTIFY, OnNotify)
3121         MESSAGE_HANDLER(WM_CONTEXTMENU, OnContextMenu)
3122         MESSAGE_HANDLER(WM_TIMER, OnTimer)
3123         MESSAGE_HANDLER(WM_DISPLAYCHANGE, OnDisplayChange)
3124         MESSAGE_HANDLER(WM_COPYDATA, OnCopyData)
3125         MESSAGE_HANDLER(WM_NCPAINT, OnNcPaint)
3126         MESSAGE_HANDLER(WM_CTLCOLORBTN, OnCtlColorBtn)
3127         MESSAGE_HANDLER(WM_MOVING, OnMoving)
3128         MESSAGE_HANDLER(WM_SIZING, OnSizing)
3129         MESSAGE_HANDLER(WM_WINDOWPOSCHANGING, OnWindowPosChanging)
3130         MESSAGE_HANDLER(WM_ENTERSIZEMOVE, OnEnterSizeMove)
3131         MESSAGE_HANDLER(WM_EXITSIZEMOVE, OnExitSizeMove)
3132         MESSAGE_HANDLER(WM_NCLBUTTONDOWN, OnNcLButtonDown)
3133         MESSAGE_HANDLER(WM_SYSCHAR, OnSysChar)
3134         MESSAGE_HANDLER(WM_NCRBUTTONUP, OnNcRButtonUp)
3135         MESSAGE_HANDLER(WM_NCLBUTTONDBLCLK, OnNcLButtonDblClick)
3136         MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
3137         MESSAGE_HANDLER(WM_NCMOUSEMOVE, OnMouseMove)
3138         MESSAGE_HANDLER(WM_APP_TRAYDESTROY, OnAppTrayDestroy)
3139         MESSAGE_HANDLER(WM_CLOSE, OnDoExitWindows)
3140         MESSAGE_HANDLER(WM_HOTKEY, OnHotkey)
3141         MESSAGE_HANDLER(WM_NCCALCSIZE, OnNcCalcSize)
3142         MESSAGE_HANDLER(WM_INITMENUPOPUP, OnInitMenuPopup)
3143         MESSAGE_HANDLER(TWM_SETTINGSCHANGED, OnTaskbarSettingsChanged)
3144         MESSAGE_HANDLER(TWM_OPENSTARTMENU, OnOpenStartMenu)
3145         MESSAGE_HANDLER(TWM_DOEXITWINDOWS, OnDoExitWindows)
3146         MESSAGE_HANDLER(TWM_GETTASKSWITCH, OnGetTaskSwitch)
3147     ALT_MSG_MAP(1)
3148     END_MSG_MAP()
3149 
3150     /*****************************************************************************/
3151 
3152     VOID TrayProcessMessages()
3153     {
3154         MSG Msg;
3155 
3156         /* FIXME: We should keep a reference here... */
3157 
3158         while (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE))
3159         {
3160             if (Msg.message == WM_QUIT)
3161                 break;
3162 
3163             if (m_StartMenuBand == NULL ||
3164                 m_StartMenuBand->IsMenuMessage(&Msg) != S_OK)
3165             {
3166                 TranslateMessage(&Msg);
3167                 DispatchMessage(&Msg);
3168             }
3169         }
3170     }
3171 
3172     VOID TrayMessageLoop()
3173     {
3174         MSG Msg;
3175         BOOL Ret;
3176 
3177         /* FIXME: We should keep a reference here... */
3178 
3179         while (true)
3180         {
3181             Ret = GetMessage(&Msg, NULL, 0, 0);
3182 
3183             if (!Ret || Ret == -1)
3184                 break;
3185 
3186             if (m_StartMenuBand == NULL ||
3187                 m_StartMenuBand->IsMenuMessage(&Msg) != S_OK)
3188             {
3189                 TranslateMessage(&Msg);
3190                 DispatchMessage(&Msg);
3191             }
3192         }
3193     }
3194 
3195     /*
3196      * IShellDesktopTray
3197      *
3198      * NOTE: this is a very windows-specific COM interface used by SHCreateDesktop()!
3199      *       These are the calls I observed, it may be wrong/incomplete/buggy!!!
3200      *       The reason we implement it is because we have to use SHCreateDesktop() so
3201      *       that the shell provides the desktop window and all the features that come
3202      *       with it (especially positioning of desktop icons)
3203      */
3204 
3205     virtual ULONG STDMETHODCALLTYPE GetState()
3206     {
3207         /* FIXME: Return ABS_ flags? */
3208         TRACE("IShellDesktopTray::GetState() unimplemented!\n");
3209         return 0;
3210     }
3211 
3212     virtual HRESULT STDMETHODCALLTYPE GetTrayWindow(OUT HWND *phWndTray)
3213     {
3214         TRACE("IShellDesktopTray::GetTrayWindow(0x%p)\n", phWndTray);
3215         *phWndTray = m_hWnd;
3216         return S_OK;
3217     }
3218 
3219     virtual HRESULT STDMETHODCALLTYPE RegisterDesktopWindow(IN HWND hWndDesktop)
3220     {
3221         TRACE("IShellDesktopTray::RegisterDesktopWindow(0x%p)\n", hWndDesktop);
3222 
3223         m_DesktopWnd = hWndDesktop;
3224         return S_OK;
3225     }
3226 
3227     virtual HRESULT STDMETHODCALLTYPE Unknown(IN DWORD dwUnknown1, IN DWORD dwUnknown2)
3228     {
3229         TRACE("IShellDesktopTray::Unknown(%u,%u) unimplemented!\n", dwUnknown1, dwUnknown2);
3230         return S_OK;
3231     }
3232 
3233     virtual HRESULT RaiseStartButton()
3234     {
3235         m_StartButton.SendMessageW(BM_SETSTATE, FALSE, 0);
3236         return S_OK;
3237     }
3238 
3239     HRESULT WINAPI GetWindow(HWND* phwnd)
3240     {
3241         if (!phwnd)
3242             return E_INVALIDARG;
3243         *phwnd = m_hWnd;
3244         return S_OK;
3245     }
3246 
3247     HRESULT WINAPI ContextSensitiveHelp(BOOL fEnterMode)
3248     {
3249         return E_NOTIMPL;
3250     }
3251 
3252     void _Init()
3253     {
3254         m_Position = (DWORD) -1;
3255     }
3256 
3257     DECLARE_NOT_AGGREGATABLE(CTrayWindow)
3258 
3259     DECLARE_PROTECT_FINAL_CONSTRUCT()
3260     BEGIN_COM_MAP(CTrayWindow)
3261         /*COM_INTERFACE_ENTRY_IID(IID_ITrayWindow, ITrayWindow)*/
3262         COM_INTERFACE_ENTRY_IID(IID_IShellDesktopTray, IShellDesktopTray)
3263         COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow)
3264         COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu)
3265     END_COM_MAP()
3266 };
3267 
3268 class CTrayWindowCtxMenu :
3269     public CComCoClass<CTrayWindowCtxMenu>,
3270     public CComObjectRootEx<CComMultiThreadModelNoCS>,
3271     public IContextMenu
3272 {
3273     HWND hWndOwner;
3274     CComPtr<CTrayWindow> TrayWnd;
3275     CComPtr<IContextMenu> pcm;
3276     UINT m_idCmdCmFirst;
3277 
3278 public:
3279     HRESULT Initialize(ITrayWindow * pTrayWnd, IN HWND hWndOwner)
3280     {
3281         this->TrayWnd = (CTrayWindow *) pTrayWnd;
3282         this->hWndOwner = hWndOwner;
3283         this->m_idCmdCmFirst = 0;
3284         return S_OK;
3285     }
3286 
3287     virtual HRESULT STDMETHODCALLTYPE
3288         QueryContextMenu(HMENU hPopup,
3289                          UINT indexMenu,
3290                          UINT idCmdFirst,
3291                          UINT idCmdLast,
3292                          UINT uFlags)
3293     {
3294         HMENU hMenuBase;
3295 
3296         hMenuBase = LoadPopupMenu(hExplorerInstance, MAKEINTRESOURCEW(IDM_TRAYWND));
3297 
3298         if (g_MinimizedAll.GetSize() != 0 && !::IsThereAnyEffectiveWindow(TRUE))
3299         {
3300             CStringW strRestoreAll(MAKEINTRESOURCEW(IDS_RESTORE_ALL));
3301             MENUITEMINFOW mii = { sizeof(mii) };
3302             mii.fMask = MIIM_ID | MIIM_TYPE;
3303             mii.wID = ID_SHELL_CMD_RESTORE_ALL;
3304             mii.fType = MFT_STRING;
3305             mii.dwTypeData = const_cast<LPWSTR>(&strRestoreAll[0]);
3306             SetMenuItemInfoW(hMenuBase, ID_SHELL_CMD_SHOW_DESKTOP, FALSE, &mii);
3307         }
3308 
3309         if (!hMenuBase)
3310             return HRESULT_FROM_WIN32(GetLastError());
3311 
3312         if (SHRestricted(REST_CLASSICSHELL) != 0)
3313         {
3314             DeleteMenu(hPopup,
3315                        ID_LOCKTASKBAR,
3316                        MF_BYCOMMAND);
3317         }
3318 
3319         CheckMenuItem(hMenuBase,
3320                       ID_LOCKTASKBAR,
3321                       MF_BYCOMMAND | (g_TaskbarSettings.bLock ? MF_CHECKED : MF_UNCHECKED));
3322 
3323         UINT idCmdNext;
3324         idCmdNext = Shell_MergeMenus(hPopup, hMenuBase, indexMenu, idCmdFirst, idCmdLast, MM_SUBMENUSHAVEIDS | MM_ADDSEPARATOR);
3325         m_idCmdCmFirst = idCmdNext - idCmdFirst;
3326 
3327         ::DestroyMenu(hMenuBase);
3328 
3329         if (TrayWnd->m_TrayBandSite != NULL)
3330         {
3331             pcm.Release();
3332             if (FAILED(TrayWnd->m_TrayBandSite->AddContextMenus(
3333                 hPopup,
3334                 indexMenu,
3335                 idCmdNext,
3336                 idCmdLast,
3337                 CMF_NORMAL,
3338                 &pcm)))
3339             {
3340                 WARN("AddContextMenus failed.\n");
3341                 pcm.Release();
3342             }
3343         }
3344 
3345         return S_OK;
3346     }
3347 
3348     virtual HRESULT STDMETHODCALLTYPE
3349         InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
3350     {
3351         UINT uiCmdId = PtrToUlong(lpici->lpVerb);
3352         if (uiCmdId != 0)
3353         {
3354             if (uiCmdId >= m_idCmdCmFirst)
3355             {
3356                 CMINVOKECOMMANDINFO cmici = { 0 };
3357 
3358                 if (pcm != NULL)
3359                 {
3360                     /* Setup and invoke the shell command */
3361                     cmici.cbSize = sizeof(cmici);
3362                     cmici.hwnd = hWndOwner;
3363                     cmici.lpVerb = (LPCSTR) MAKEINTRESOURCEW(uiCmdId - m_idCmdCmFirst);
3364                     cmici.nShow = SW_NORMAL;
3365 
3366                     pcm->InvokeCommand(&cmici);
3367                 }
3368             }
3369             else
3370             {
3371                 TrayWnd->ExecContextMenuCmd(uiCmdId);
3372             }
3373         }
3374 
3375         return S_OK;
3376     }
3377 
3378     virtual HRESULT STDMETHODCALLTYPE
3379         GetCommandString(UINT_PTR idCmd,
3380         UINT uType,
3381         UINT *pwReserved,
3382         LPSTR pszName,
3383         UINT cchMax)
3384     {
3385         return E_NOTIMPL;
3386     }
3387 
3388     CTrayWindowCtxMenu()
3389     {
3390     }
3391 
3392     virtual ~CTrayWindowCtxMenu()
3393     {
3394     }
3395 
3396     BEGIN_COM_MAP(CTrayWindowCtxMenu)
3397         COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu)
3398     END_COM_MAP()
3399 };
3400 
3401 HRESULT TrayWindowCtxMenuCreator(ITrayWindow * TrayWnd, IN HWND hWndOwner, IContextMenu ** ppCtxMenu)
3402 {
3403     CTrayWindowCtxMenu * mnu = new CComObject<CTrayWindowCtxMenu>();
3404     mnu->Initialize(TrayWnd, hWndOwner);
3405     *ppCtxMenu = mnu;
3406     return S_OK;
3407 }
3408 
3409 HRESULT CreateTrayWindow(ITrayWindow ** ppTray)
3410 {
3411     CComPtr<CTrayWindow> Tray = new CComObject<CTrayWindow>();
3412     if (Tray == NULL)
3413         return E_OUTOFMEMORY;
3414 
3415     Tray->_Init();
3416     Tray->Open();
3417 
3418     *ppTray = (ITrayWindow *) Tray;
3419 
3420     return S_OK;
3421 }
3422 
3423 HRESULT
3424 Tray_OnStartMenuDismissed(ITrayWindow* Tray)
3425 {
3426     CTrayWindow * TrayWindow = static_cast<CTrayWindow *>(Tray);
3427     return TrayWindow->RaiseStartButton();
3428 }
3429 
3430 VOID TrayProcessMessages(ITrayWindow *Tray)
3431 {
3432     CTrayWindow * TrayWindow = static_cast<CTrayWindow *>(Tray);
3433     TrayWindow->TrayProcessMessages();
3434 }
3435 
3436 VOID TrayMessageLoop(ITrayWindow *Tray)
3437 {
3438     CTrayWindow * TrayWindow = static_cast<CTrayWindow *>(Tray);
3439     TrayWindow->TrayMessageLoop();
3440 }
3441