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