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