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