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