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