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