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