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