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