xref: /reactos/base/shell/explorer/traywnd.cpp (revision d5399189)
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                 StartMenuBtnCtxMenuCreator(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 OnHotkey(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2805     {
2806         return HandleHotKey(wParam);
2807     }
2808 
2809     struct MINIMIZE_INFO
2810     {
2811         HWND hwndDesktop;
2812         HWND hTrayWnd;
2813         HWND hwndProgman;
2814         BOOL bRet;
2815         CSimpleArray<HWND> *pMinimizedAll;
2816         BOOL bShowDesktop;
2817     };
2818 
2819     static BOOL IsDialog(HWND hwnd)
2820     {
2821         WCHAR szClass[32];
2822         GetClassNameW(hwnd, szClass, _countof(szClass));
2823         return wcscmp(szClass, L"#32770") == 0;
2824     }
2825 
2826     static BOOL CALLBACK MinimizeWindowsProc(HWND hwnd, LPARAM lParam)
2827     {
2828         MINIMIZE_INFO *info = (MINIMIZE_INFO *)lParam;
2829         if (hwnd == info->hwndDesktop || hwnd == info->hTrayWnd ||
2830             hwnd == info->hwndProgman)
2831         {
2832             return TRUE;
2833         }
2834         if (!info->bShowDesktop)
2835         {
2836             if (!::IsWindowEnabled(hwnd) || IsDialog(hwnd))
2837                 return TRUE;
2838             HWND hwndOwner = ::GetWindow(hwnd, GW_OWNER);
2839             if (hwndOwner && !::IsWindowEnabled(hwndOwner))
2840                 return TRUE;
2841         }
2842         if (::IsWindowVisible(hwnd) && !::IsIconic(hwnd))
2843         {
2844             ::ShowWindowAsync(hwnd, SW_MINIMIZE);
2845             info->bRet = TRUE;
2846             info->pMinimizedAll->Add(hwnd);
2847         }
2848         return TRUE;
2849     }
2850 
2851     VOID MinimizeAll(BOOL bShowDesktop = FALSE)
2852     {
2853         MINIMIZE_INFO info;
2854         info.hwndDesktop = GetDesktopWindow();;
2855         info.hTrayWnd = FindWindowW(L"Shell_TrayWnd", NULL);
2856         info.hwndProgman = FindWindowW(L"Progman", NULL);
2857         info.bRet = FALSE;
2858         info.pMinimizedAll = &g_MinimizedAll;
2859         info.bShowDesktop = bShowDesktop;
2860         EnumWindows(MinimizeWindowsProc, (LPARAM)&info);
2861 
2862         // invalid handles should be cleared to avoid mismatch of handles
2863         for (INT i = 0; i < g_MinimizedAll.GetSize(); ++i)
2864         {
2865             if (!::IsWindow(g_MinimizedAll[i]))
2866                 g_MinimizedAll[i] = NULL;
2867         }
2868 
2869         ::SetForegroundWindow(m_DesktopWnd);
2870         ::SetFocus(m_DesktopWnd);
2871     }
2872 
2873     VOID ShowDesktop()
2874     {
2875         MinimizeAll(TRUE);
2876     }
2877 
2878     VOID RestoreAll()
2879     {
2880         for (INT i = g_MinimizedAll.GetSize() - 1; i >= 0; --i)
2881         {
2882             HWND hwnd = g_MinimizedAll[i];
2883             if (::IsWindowVisible(hwnd) && ::IsIconic(hwnd))
2884             {
2885                 ::ShowWindow(hwnd, SW_RESTORE);
2886             }
2887         }
2888         g_MinimizedAll.RemoveAll();
2889     }
2890 
2891     LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2892     {
2893         LRESULT Ret = FALSE;
2894 
2895         if ((HWND) lParam == m_StartButton.m_hWnd)
2896         {
2897             return FALSE;
2898         }
2899 
2900         if (m_TrayBandSite == NULL || FAILED_UNEXPECTEDLY(m_TrayBandSite->ProcessMessage(m_hWnd, uMsg, wParam, lParam, &Ret)))
2901         {
2902             return HandleCommand(LOWORD(wParam));
2903         }
2904         return Ret;
2905     }
2906 
2907     LRESULT OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2908     {
2909         if (g_TaskbarSettings.sr.AutoHide)
2910         {
2911             SetTimer(TIMER_ID_MOUSETRACK, MOUSETRACK_INTERVAL, NULL);
2912         }
2913 
2914         return TRUE;
2915     }
2916 
2917     LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2918     {
2919         if (wParam == TIMER_ID_MOUSETRACK)
2920         {
2921             ProcessMouseTracking();
2922         }
2923         else if (wParam == TIMER_ID_AUTOHIDE)
2924         {
2925             ProcessAutoHide();
2926         }
2927 
2928         bHandled = FALSE;
2929         return TRUE;
2930     }
2931 
2932     LRESULT OnNcCalcSize(INT code, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2933     {
2934         RECT *rc = NULL;
2935         /* Ignore WM_NCCALCSIZE if we are not themed or locked */
2936         if(!m_Theme || g_TaskbarSettings.bLock)
2937         {
2938             bHandled = FALSE;
2939             return 0;
2940         }
2941         if(!wParam)
2942         {
2943             rc = (RECT*)wParam;
2944         }
2945         else
2946         {
2947             NCCALCSIZE_PARAMS *prms = (NCCALCSIZE_PARAMS*)lParam;
2948             if(prms->lppos->flags & SWP_NOSENDCHANGING)
2949             {
2950                 bHandled = FALSE;
2951                 return 0;
2952             }
2953             rc = &prms->rgrc[0];
2954         }
2955 
2956         AdjustSizerRect(rc, m_Position);
2957 
2958         return 0;
2959     }
2960 
2961     LRESULT OnInitMenuPopup(INT code, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2962     {
2963         HMENU hMenu = (HMENU)wParam;
2964         if (::IsThereAnyEffectiveWindow(FALSE))
2965         {
2966             ::EnableMenuItem(hMenu, ID_SHELL_CMD_CASCADE_WND, MF_BYCOMMAND | MF_ENABLED);
2967             ::EnableMenuItem(hMenu, ID_SHELL_CMD_TILE_WND_H, MF_BYCOMMAND | MF_ENABLED);
2968             ::EnableMenuItem(hMenu, ID_SHELL_CMD_TILE_WND_V, MF_BYCOMMAND | MF_ENABLED);
2969         }
2970         else
2971         {
2972             ::EnableMenuItem(hMenu, ID_SHELL_CMD_CASCADE_WND, MF_BYCOMMAND | MF_GRAYED);
2973             ::EnableMenuItem(hMenu, ID_SHELL_CMD_TILE_WND_H, MF_BYCOMMAND | MF_GRAYED);
2974             ::EnableMenuItem(hMenu, ID_SHELL_CMD_TILE_WND_V, MF_BYCOMMAND | MF_GRAYED);
2975         }
2976         return 0;
2977     }
2978 
2979     LRESULT OnRebarAutoSize(INT code, LPNMHDR nmhdr, BOOL& bHandled)
2980     {
2981 #if 0
2982         LPNMRBAUTOSIZE as = (LPNMRBAUTOSIZE) nmhdr;
2983 
2984         if (!as->fChanged)
2985             return 0;
2986 
2987         RECT rc;
2988         ::GetWindowRect(m_hWnd, &rc);
2989 
2990         SIZE szWindow = {
2991             rc.right - rc.left,
2992             rc.bottom - rc.top };
2993         SIZE szTarget = {
2994             as->rcTarget.right - as->rcTarget.left,
2995             as->rcTarget.bottom - as->rcTarget.top };
2996         SIZE szActual = {
2997             as->rcActual.right - as->rcActual.left,
2998             as->rcActual.bottom - as->rcActual.top };
2999 
3000         SIZE borders = {
3001             szWindow.cx - szTarget.cx,
3002             szWindow.cy - szTarget.cx,
3003         };
3004 
3005         switch (m_Position)
3006         {
3007         case ABE_LEFT:
3008             szWindow.cx = szActual.cx + borders.cx;
3009             break;
3010         case ABE_TOP:
3011             szWindow.cy = szActual.cy + borders.cy;
3012             break;
3013         case ABE_RIGHT:
3014             szWindow.cx = szActual.cx + borders.cx;
3015             rc.left = rc.right - szWindow.cy;
3016             break;
3017         case ABE_BOTTOM:
3018             szWindow.cy = szActual.cy + borders.cy;
3019             rc.top = rc.bottom - szWindow.cy;
3020             break;
3021         }
3022 
3023         SetWindowPos(NULL, rc.left, rc.top, szWindow.cx, szWindow.cy, SWP_NOACTIVATE | SWP_NOZORDER);
3024 #else
3025         bHandled = FALSE;
3026 #endif
3027         return 0;
3028     }
3029 
3030     LRESULT OnTaskbarSettingsChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
3031     {
3032         TaskbarSettings* newSettings = (TaskbarSettings*)lParam;
3033 
3034         /* Propagate the new settings to the children */
3035         ::SendMessageW(m_TaskSwitch, uMsg, wParam, lParam);
3036         ::SendMessageW(m_TrayNotify, uMsg, wParam, lParam);
3037 
3038         /* Toggle autohide */
3039         if (newSettings->sr.AutoHide != g_TaskbarSettings.sr.AutoHide)
3040         {
3041             g_TaskbarSettings.sr.AutoHide = newSettings->sr.AutoHide;
3042             memset(&m_AutoHideOffset, 0, sizeof(m_AutoHideOffset));
3043             m_AutoHideState = AUTOHIDE_SHOWN;
3044             if (!newSettings->sr.AutoHide)
3045                 SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER);
3046             else
3047                 SetTimer(TIMER_ID_MOUSETRACK, MOUSETRACK_INTERVAL, NULL);
3048         }
3049 
3050         /* Toggle lock state */
3051         Lock(newSettings->bLock);
3052 
3053         /* Toggle OnTop state */
3054         if (newSettings->sr.AlwaysOnTop != g_TaskbarSettings.sr.AlwaysOnTop)
3055         {
3056             g_TaskbarSettings.sr.AlwaysOnTop = newSettings->sr.AlwaysOnTop;
3057             HWND hWndInsertAfter = newSettings->sr.AlwaysOnTop ? HWND_TOPMOST : HWND_BOTTOM;
3058             SetWindowPos(hWndInsertAfter, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
3059         }
3060 
3061         g_TaskbarSettings.Save();
3062         return 0;
3063     }
3064 
3065     DECLARE_WND_CLASS_EX(szTrayWndClass, CS_DBLCLKS, COLOR_3DFACE)
3066 
3067     BEGIN_MSG_MAP(CTrayWindow)
3068         if (m_StartMenuBand != NULL)
3069         {
3070             MSG Msg;
3071             LRESULT lRet;
3072 
3073             Msg.hwnd = m_hWnd;
3074             Msg.message = uMsg;
3075             Msg.wParam = wParam;
3076             Msg.lParam = lParam;
3077 
3078             if (m_StartMenuBand->TranslateMenuMessage(&Msg, &lRet) == S_OK)
3079             {
3080                 return lRet;
3081             }
3082 
3083             wParam = Msg.wParam;
3084             lParam = Msg.lParam;
3085         }
3086         MESSAGE_HANDLER(WM_THEMECHANGED, OnThemeChanged)
3087         MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChanged)
3088         NOTIFY_CODE_HANDLER(RBN_AUTOSIZE, OnRebarAutoSize) // Doesn't quite work ;P
3089         MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
3090         MESSAGE_HANDLER(WM_SIZE, OnSize)
3091         MESSAGE_HANDLER(WM_CREATE, OnCreate)
3092         /*MESSAGE_HANDLER(WM_DESTROY, OnDestroy)*/
3093         MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTest)
3094         MESSAGE_HANDLER(WM_COMMAND, OnCommand)
3095         MESSAGE_HANDLER(WM_SYSCOMMAND, OnSysCommand)
3096         MESSAGE_HANDLER(WM_NOTIFY, OnNotify)
3097         MESSAGE_HANDLER(WM_CONTEXTMENU, OnContextMenu)
3098         MESSAGE_HANDLER(WM_TIMER, OnTimer)
3099         MESSAGE_HANDLER(WM_DISPLAYCHANGE, OnDisplayChange)
3100         MESSAGE_HANDLER(WM_COPYDATA, OnCopyData)
3101         MESSAGE_HANDLER(WM_NCPAINT, OnNcPaint)
3102         MESSAGE_HANDLER(WM_CTLCOLORBTN, OnCtlColorBtn)
3103         MESSAGE_HANDLER(WM_MOVING, OnMoving)
3104         MESSAGE_HANDLER(WM_SIZING, OnSizing)
3105         MESSAGE_HANDLER(WM_WINDOWPOSCHANGING, OnWindowPosChanging)
3106         MESSAGE_HANDLER(WM_ENTERSIZEMOVE, OnEnterSizeMove)
3107         MESSAGE_HANDLER(WM_EXITSIZEMOVE, OnExitSizeMove)
3108         MESSAGE_HANDLER(WM_NCLBUTTONDOWN, OnNcLButtonDown)
3109         MESSAGE_HANDLER(WM_SYSCHAR, OnSysChar)
3110         MESSAGE_HANDLER(WM_NCRBUTTONUP, OnNcRButtonUp)
3111         MESSAGE_HANDLER(WM_NCLBUTTONDBLCLK, OnNcLButtonDblClick)
3112         MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
3113         MESSAGE_HANDLER(WM_NCMOUSEMOVE, OnMouseMove)
3114         MESSAGE_HANDLER(WM_APP_TRAYDESTROY, OnAppTrayDestroy)
3115         MESSAGE_HANDLER(WM_CLOSE, OnDoExitWindows)
3116         MESSAGE_HANDLER(WM_HOTKEY, OnHotkey)
3117         MESSAGE_HANDLER(WM_NCCALCSIZE, OnNcCalcSize)
3118         MESSAGE_HANDLER(WM_INITMENUPOPUP, OnInitMenuPopup)
3119         MESSAGE_HANDLER(TWM_SETTINGSCHANGED, OnTaskbarSettingsChanged)
3120         MESSAGE_HANDLER(TWM_OPENSTARTMENU, OnOpenStartMenu)
3121         MESSAGE_HANDLER(TWM_DOEXITWINDOWS, OnDoExitWindows)
3122     ALT_MSG_MAP(1)
3123     END_MSG_MAP()
3124 
3125     /*****************************************************************************/
3126 
3127     VOID TrayProcessMessages()
3128     {
3129         MSG Msg;
3130 
3131         /* FIXME: We should keep a reference here... */
3132 
3133         while (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE))
3134         {
3135             if (Msg.message == WM_QUIT)
3136                 break;
3137 
3138             if (m_StartMenuBand == NULL ||
3139                 m_StartMenuBand->IsMenuMessage(&Msg) != S_OK)
3140             {
3141                 TranslateMessage(&Msg);
3142                 DispatchMessage(&Msg);
3143             }
3144         }
3145     }
3146 
3147     VOID TrayMessageLoop()
3148     {
3149         MSG Msg;
3150         BOOL Ret;
3151 
3152         /* FIXME: We should keep a reference here... */
3153 
3154         while (true)
3155         {
3156             Ret = GetMessage(&Msg, NULL, 0, 0);
3157 
3158             if (!Ret || Ret == -1)
3159                 break;
3160 
3161             if (m_StartMenuBand == NULL ||
3162                 m_StartMenuBand->IsMenuMessage(&Msg) != S_OK)
3163             {
3164                 TranslateMessage(&Msg);
3165                 DispatchMessage(&Msg);
3166             }
3167         }
3168     }
3169 
3170     /*
3171      * IShellDesktopTray
3172      *
3173      * NOTE: this is a very windows-specific COM interface used by SHCreateDesktop()!
3174      *       These are the calls I observed, it may be wrong/incomplete/buggy!!!
3175      *       The reason we implement it is because we have to use SHCreateDesktop() so
3176      *       that the shell provides the desktop window and all the features that come
3177      *       with it (especially positioning of desktop icons)
3178      */
3179 
3180     virtual ULONG STDMETHODCALLTYPE GetState()
3181     {
3182         /* FIXME: Return ABS_ flags? */
3183         TRACE("IShellDesktopTray::GetState() unimplemented!\n");
3184         return 0;
3185     }
3186 
3187     virtual HRESULT STDMETHODCALLTYPE GetTrayWindow(OUT HWND *phWndTray)
3188     {
3189         TRACE("IShellDesktopTray::GetTrayWindow(0x%p)\n", phWndTray);
3190         *phWndTray = m_hWnd;
3191         return S_OK;
3192     }
3193 
3194     virtual HRESULT STDMETHODCALLTYPE RegisterDesktopWindow(IN HWND hWndDesktop)
3195     {
3196         TRACE("IShellDesktopTray::RegisterDesktopWindow(0x%p)\n", hWndDesktop);
3197 
3198         m_DesktopWnd = hWndDesktop;
3199         return S_OK;
3200     }
3201 
3202     virtual HRESULT STDMETHODCALLTYPE Unknown(IN DWORD dwUnknown1, IN DWORD dwUnknown2)
3203     {
3204         TRACE("IShellDesktopTray::Unknown(%u,%u) unimplemented!\n", dwUnknown1, dwUnknown2);
3205         return S_OK;
3206     }
3207 
3208     virtual HRESULT RaiseStartButton()
3209     {
3210         m_StartButton.SendMessageW(BM_SETSTATE, FALSE, 0);
3211         return S_OK;
3212     }
3213 
3214     HRESULT WINAPI GetWindow(HWND* phwnd)
3215     {
3216         if (!phwnd)
3217             return E_INVALIDARG;
3218         *phwnd = m_hWnd;
3219         return S_OK;
3220     }
3221 
3222     HRESULT WINAPI ContextSensitiveHelp(BOOL fEnterMode)
3223     {
3224         return E_NOTIMPL;
3225     }
3226 
3227     void _Init()
3228     {
3229         m_Position = (DWORD) -1;
3230     }
3231 
3232     DECLARE_NOT_AGGREGATABLE(CTrayWindow)
3233 
3234     DECLARE_PROTECT_FINAL_CONSTRUCT()
3235     BEGIN_COM_MAP(CTrayWindow)
3236         /*COM_INTERFACE_ENTRY_IID(IID_ITrayWindow, ITrayWindow)*/
3237         COM_INTERFACE_ENTRY_IID(IID_IShellDesktopTray, IShellDesktopTray)
3238         COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow)
3239         COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu)
3240     END_COM_MAP()
3241 };
3242 
3243 class CTrayWindowCtxMenu :
3244     public CComCoClass<CTrayWindowCtxMenu>,
3245     public CComObjectRootEx<CComMultiThreadModelNoCS>,
3246     public IContextMenu
3247 {
3248     HWND hWndOwner;
3249     CComPtr<CTrayWindow> TrayWnd;
3250     CComPtr<IContextMenu> pcm;
3251     UINT m_idCmdCmFirst;
3252 
3253 public:
3254     HRESULT Initialize(ITrayWindow * pTrayWnd, IN HWND hWndOwner)
3255     {
3256         this->TrayWnd = (CTrayWindow *) pTrayWnd;
3257         this->hWndOwner = hWndOwner;
3258         this->m_idCmdCmFirst = 0;
3259         return S_OK;
3260     }
3261 
3262     virtual HRESULT STDMETHODCALLTYPE
3263         QueryContextMenu(HMENU hPopup,
3264                          UINT indexMenu,
3265                          UINT idCmdFirst,
3266                          UINT idCmdLast,
3267                          UINT uFlags)
3268     {
3269         HMENU hMenuBase;
3270 
3271         hMenuBase = LoadPopupMenu(hExplorerInstance, MAKEINTRESOURCEW(IDM_TRAYWND));
3272 
3273         if (g_MinimizedAll.GetSize() != 0 && !::IsThereAnyEffectiveWindow(TRUE))
3274         {
3275             CStringW strRestoreAll(MAKEINTRESOURCEW(IDS_RESTORE_ALL));
3276             MENUITEMINFOW mii = { sizeof(mii) };
3277             mii.fMask = MIIM_ID | MIIM_TYPE;
3278             mii.wID = ID_SHELL_CMD_RESTORE_ALL;
3279             mii.fType = MFT_STRING;
3280             mii.dwTypeData = const_cast<LPWSTR>(&strRestoreAll[0]);
3281             SetMenuItemInfoW(hMenuBase, ID_SHELL_CMD_SHOW_DESKTOP, FALSE, &mii);
3282         }
3283 
3284         if (!hMenuBase)
3285             return HRESULT_FROM_WIN32(GetLastError());
3286 
3287         if (SHRestricted(REST_CLASSICSHELL) != 0)
3288         {
3289             DeleteMenu(hPopup,
3290                        ID_LOCKTASKBAR,
3291                        MF_BYCOMMAND);
3292         }
3293 
3294         CheckMenuItem(hMenuBase,
3295                       ID_LOCKTASKBAR,
3296                       MF_BYCOMMAND | (g_TaskbarSettings.bLock ? MF_CHECKED : MF_UNCHECKED));
3297 
3298         UINT idCmdNext;
3299         idCmdNext = Shell_MergeMenus(hPopup, hMenuBase, indexMenu, idCmdFirst, idCmdLast, MM_SUBMENUSHAVEIDS | MM_ADDSEPARATOR);
3300         m_idCmdCmFirst = idCmdNext - idCmdFirst;
3301 
3302         ::DestroyMenu(hMenuBase);
3303 
3304         if (TrayWnd->m_TrayBandSite != NULL)
3305         {
3306             if (FAILED(TrayWnd->m_TrayBandSite->AddContextMenus(
3307                 hPopup,
3308                 indexMenu,
3309                 idCmdNext,
3310                 idCmdLast,
3311                 CMF_NORMAL,
3312                 &pcm)))
3313             {
3314                 WARN("AddContextMenus failed.\n");
3315                 pcm = NULL;
3316             }
3317         }
3318 
3319         return S_OK;
3320     }
3321 
3322     virtual HRESULT STDMETHODCALLTYPE
3323         InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
3324     {
3325         UINT uiCmdId = PtrToUlong(lpici->lpVerb);
3326         if (uiCmdId != 0)
3327         {
3328             if (uiCmdId >= m_idCmdCmFirst)
3329             {
3330                 CMINVOKECOMMANDINFO cmici = { 0 };
3331 
3332                 if (pcm != NULL)
3333                 {
3334                     /* Setup and invoke the shell command */
3335                     cmici.cbSize = sizeof(cmici);
3336                     cmici.hwnd = hWndOwner;
3337                     cmici.lpVerb = (LPCSTR) MAKEINTRESOURCEW(uiCmdId - m_idCmdCmFirst);
3338                     cmici.nShow = SW_NORMAL;
3339 
3340                     pcm->InvokeCommand(&cmici);
3341                 }
3342             }
3343             else
3344             {
3345                 TrayWnd->ExecContextMenuCmd(uiCmdId);
3346             }
3347         }
3348 
3349         return S_OK;
3350     }
3351 
3352     virtual HRESULT STDMETHODCALLTYPE
3353         GetCommandString(UINT_PTR idCmd,
3354         UINT uType,
3355         UINT *pwReserved,
3356         LPSTR pszName,
3357         UINT cchMax)
3358     {
3359         return E_NOTIMPL;
3360     }
3361 
3362     CTrayWindowCtxMenu()
3363     {
3364     }
3365 
3366     virtual ~CTrayWindowCtxMenu()
3367     {
3368     }
3369 
3370     BEGIN_COM_MAP(CTrayWindowCtxMenu)
3371         COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu)
3372     END_COM_MAP()
3373 };
3374 
3375 HRESULT TrayWindowCtxMenuCreator(ITrayWindow * TrayWnd, IN HWND hWndOwner, IContextMenu ** ppCtxMenu)
3376 {
3377     CTrayWindowCtxMenu * mnu = new CComObject<CTrayWindowCtxMenu>();
3378     mnu->Initialize(TrayWnd, hWndOwner);
3379     *ppCtxMenu = mnu;
3380     return S_OK;
3381 }
3382 
3383 HRESULT CreateTrayWindow(ITrayWindow ** ppTray)
3384 {
3385     CComPtr<CTrayWindow> Tray = new CComObject<CTrayWindow>();
3386     if (Tray == NULL)
3387         return E_OUTOFMEMORY;
3388 
3389     Tray->_Init();
3390     Tray->Open();
3391 
3392     *ppTray = (ITrayWindow *) Tray;
3393 
3394     return S_OK;
3395 }
3396 
3397 HRESULT
3398 Tray_OnStartMenuDismissed(ITrayWindow* Tray)
3399 {
3400     CTrayWindow * TrayWindow = static_cast<CTrayWindow *>(Tray);
3401     return TrayWindow->RaiseStartButton();
3402 }
3403 
3404 VOID TrayProcessMessages(ITrayWindow *Tray)
3405 {
3406     CTrayWindow * TrayWindow = static_cast<CTrayWindow *>(Tray);
3407     TrayWindow->TrayProcessMessages();
3408 }
3409 
3410 VOID TrayMessageLoop(ITrayWindow *Tray)
3411 {
3412     CTrayWindow * TrayWindow = static_cast<CTrayWindow *>(Tray);
3413     TrayWindow->TrayMessageLoop();
3414 }
3415