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