[EXPLORER] -Make CSysPagerWnd, CTaskSwitchWnd, CTrayClockWnd and CTrayNotifyWnd prope...
[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 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 RegisterHotKey(m_hWnd, IDHK_RUN, MOD_WIN, 'R');
2084 RegisterHotKey(m_hWnd, IDHK_MINIMIZE_ALL, MOD_WIN, 'M');
2085 RegisterHotKey(m_hWnd, IDHK_RESTORE_ALL, MOD_WIN|MOD_SHIFT, 'M');
2086 RegisterHotKey(m_hWnd, IDHK_HELP, MOD_WIN, VK_F1);
2087 RegisterHotKey(m_hWnd, IDHK_EXPLORE, MOD_WIN, 'E');
2088 RegisterHotKey(m_hWnd, IDHK_FIND, MOD_WIN, 'F');
2089 RegisterHotKey(m_hWnd, IDHK_FIND_COMPUTER, MOD_WIN|MOD_CONTROL, 'F');
2090 RegisterHotKey(m_hWnd, IDHK_NEXT_TASK, MOD_WIN, VK_TAB);
2091 RegisterHotKey(m_hWnd, IDHK_PREV_TASK, MOD_WIN|MOD_SHIFT, VK_TAB);
2092 RegisterHotKey(m_hWnd, IDHK_SYS_PROPERTIES, MOD_WIN, VK_PAUSE);
2093 RegisterHotKey(m_hWnd, IDHK_DESKTOP, MOD_WIN, 'D');
2094 RegisterHotKey(m_hWnd, IDHK_PAGER, MOD_WIN, 'B');
2095
2096 return TRUE;
2097 }
2098
2099 LRESULT OnThemeChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2100 {
2101 if (m_Theme)
2102 CloseThemeData(m_Theme);
2103
2104 m_Theme = OpenThemeData(m_hWnd, L"TaskBar");
2105
2106 if (m_Theme)
2107 {
2108 SetWindowStyle(m_hWnd, WS_THICKFRAME | WS_BORDER, 0);
2109 }
2110 else
2111 {
2112 SetWindowStyle(m_hWnd, WS_THICKFRAME | WS_BORDER, WS_THICKFRAME | WS_BORDER);
2113 }
2114 SetWindowPos(NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
2115
2116 return TRUE;
2117 }
2118
2119 LRESULT OnSettingChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2120 {
2121 if (wParam == SPI_SETNONCLIENTMETRICS)
2122 {
2123 SendMessage(m_TrayNotify, uMsg, wParam, lParam);
2124 SendMessage(m_TaskSwitch, uMsg, wParam, lParam);
2125 UpdateFonts();
2126 AlignControls(NULL);
2127 CheckTrayWndPosition();
2128 }
2129
2130 return 0;
2131 }
2132
2133 LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2134 {
2135 HDC hdc = (HDC) wParam;
2136
2137 if (!m_Theme)
2138 {
2139 bHandled = FALSE;
2140 return 0;
2141 }
2142
2143 return EraseBackgroundWithTheme(hdc);
2144 }
2145
2146 LRESULT OnDisplayChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2147 {
2148 /* Load the saved tray window settings */
2149 RegLoadSettings();
2150
2151 /* Move the tray window to the right position and resize it if necessary */
2152 CheckTrayWndPosition();
2153
2154 return TRUE;
2155 }
2156
2157 LRESULT OnCopyData(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2158 {
2159 if (m_TrayNotify)
2160 ::SendMessageW(m_TrayNotify, uMsg, wParam, lParam);
2161 return TRUE;
2162 }
2163
2164 LRESULT OnNcPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2165 {
2166 if (!m_Theme)
2167 {
2168 bHandled = FALSE;
2169 return 0;
2170 }
2171
2172 return DrawSizerWithTheme((HRGN) wParam);
2173 }
2174
2175 LRESULT OnCtlColorBtn(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2176 {
2177 SetBkMode((HDC) wParam, TRANSPARENT);
2178 return (LRESULT) GetStockObject(HOLLOW_BRUSH);
2179 }
2180
2181 LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2182 {
2183 RECT rcClient;
2184 POINT pt;
2185
2186 if (g_TaskbarSettings.bLock)
2187 {
2188 /* The user may not be able to resize the tray window.
2189 Pretend like the window is not sizeable when the user
2190 clicks on the border. */
2191 return HTBORDER;
2192 }
2193
2194 SetLastError(ERROR_SUCCESS);
2195 if (GetClientRect(&rcClient) &&
2196 (MapWindowPoints(NULL, (LPPOINT) &rcClient, 2) != 0 || GetLastError() == ERROR_SUCCESS))
2197 {
2198 pt.x = (SHORT) LOWORD(lParam);
2199 pt.y = (SHORT) HIWORD(lParam);
2200
2201 if (PtInRect(&rcClient,
2202 pt))
2203 {
2204 /* The user is trying to drag the tray window */
2205 return HTCAPTION;
2206 }
2207
2208 /* Depending on the position of the tray window, allow only
2209 changing the border next to the monitor working area */
2210 switch (m_Position)
2211 {
2212 case ABE_TOP:
2213 if (pt.y > rcClient.bottom)
2214 return HTBOTTOM;
2215 break;
2216 case ABE_LEFT:
2217 if (pt.x > rcClient.right)
2218 return HTRIGHT;
2219 break;
2220 case ABE_RIGHT:
2221 if (pt.x < rcClient.left)
2222 return HTLEFT;
2223 break;
2224 case ABE_BOTTOM:
2225 default:
2226 if (pt.y < rcClient.top)
2227 return HTTOP;
2228 break;
2229 }
2230 }
2231 return HTBORDER;
2232 return TRUE;
2233 }
2234
2235 LRESULT OnMoving(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2236 {
2237 POINT ptCursor;
2238 PRECT pRect = (PRECT) lParam;
2239
2240 /* We need to ensure that an application can not accidently
2241 move the tray window (using SetWindowPos). However, we still
2242 need to be able to move the window in case the user wants to
2243 drag the tray window to another position or in case the user
2244 wants to resize the tray window. */
2245 if (!g_TaskbarSettings.bLock && GetCursorPos(&ptCursor))
2246 {
2247 IsDragging = TRUE;
2248 m_DraggingPosition = GetDraggingRectFromPt(ptCursor, pRect, &m_DraggingMonitor);
2249 }
2250 else
2251 {
2252 *pRect = m_TrayRects[m_Position];
2253 }
2254 return TRUE;
2255 }
2256
2257 LRESULT OnSizing(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2258 {
2259 PRECT pRect = (PRECT) lParam;
2260
2261 if (!g_TaskbarSettings.bLock)
2262 {
2263 FitToRebar(pRect);
2264 }
2265 else
2266 {
2267 *pRect = m_TrayRects[m_Position];
2268 }
2269 return TRUE;
2270 }
2271
2272 LRESULT OnWindowPosChanging(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2273 {
2274 ChangingWinPos((LPWINDOWPOS) lParam);
2275 return TRUE;
2276 }
2277
2278 LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2279 {
2280 RECT rcClient;
2281 if (wParam == SIZE_RESTORED && lParam == 0)
2282 {
2283 ResizeWorkArea();
2284 /* Clip the tray window on multi monitor systems so the edges can't
2285 overlap into another monitor */
2286 ApplyClipping(TRUE);
2287
2288 if (!GetClientRect(&rcClient))
2289 {
2290 return FALSE;
2291 }
2292 }
2293 else
2294 {
2295 rcClient.left = rcClient.top = 0;
2296 rcClient.right = LOWORD(lParam);
2297 rcClient.bottom = HIWORD(lParam);
2298 }
2299
2300 AlignControls(&rcClient);
2301 return TRUE;
2302 }
2303
2304 LRESULT OnEnterSizeMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2305 {
2306 InSizeMove = TRUE;
2307 IsDragging = FALSE;
2308 if (!g_TaskbarSettings.bLock)
2309 {
2310 /* Remove the clipping on multi monitor systems while dragging around */
2311 ApplyClipping(FALSE);
2312 }
2313 return TRUE;
2314 }
2315
2316 LRESULT OnExitSizeMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2317 {
2318 InSizeMove = FALSE;
2319 if (!g_TaskbarSettings.bLock)
2320 {
2321 FitToRebar(&m_TrayRects[m_Position]);
2322
2323 /* Apply clipping */
2324 PostMessage(WM_SIZE, SIZE_RESTORED, 0);
2325 }
2326 return TRUE;
2327 }
2328
2329 LRESULT OnSysChar(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2330 {
2331 switch (wParam)
2332 {
2333 case TEXT(' '):
2334 {
2335 /* The user pressed Alt+Space, this usually brings up the system menu of a window.
2336 The tray window needs to handle this specially, since it normally doesn't have
2337 a system menu. */
2338
2339 static const UINT uidDisableItem [] = {
2340 SC_RESTORE,
2341 SC_MOVE,
2342 SC_SIZE,
2343 SC_MAXIMIZE,
2344 SC_MINIMIZE,
2345 };
2346 HMENU hSysMenu;
2347 UINT i, uId;
2348
2349 /* temporarily enable the system menu */
2350 SetWindowStyle(m_hWnd, WS_SYSMENU, WS_SYSMENU);
2351
2352 hSysMenu = GetSystemMenu(FALSE);
2353 if (hSysMenu != NULL)
2354 {
2355 /* Disable all items that are not relevant */
2356 for (i = 0; i < _countof(uidDisableItem); i++)
2357 {
2358 EnableMenuItem(hSysMenu,
2359 uidDisableItem[i],
2360 MF_BYCOMMAND | MF_GRAYED);
2361 }
2362
2363 EnableMenuItem(hSysMenu,
2364 SC_CLOSE,
2365 MF_BYCOMMAND |
2366 (SHRestricted(REST_NOCLOSE) ? MF_GRAYED : MF_ENABLED));
2367
2368 /* Display the system menu */
2369 uId = TrackMenu(
2370 hSysMenu,
2371 NULL,
2372 m_StartButton.m_hWnd,
2373 m_Position != ABE_TOP,
2374 FALSE);
2375 if (uId != 0)
2376 {
2377 SendMessage(m_hWnd, WM_SYSCOMMAND, (WPARAM) uId, 0);
2378 }
2379 }
2380
2381 /* revert the system menu window style */
2382 SetWindowStyle(m_hWnd, WS_SYSMENU, 0);
2383 break;
2384 }
2385
2386 default:
2387 bHandled = FALSE;
2388 }
2389 return TRUE;
2390 }
2391
2392 LRESULT OnNcLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2393 {
2394 /* This handler implements the trick that makes the start button to
2395 get pressed when the user clicked left or below the button */
2396
2397 POINT pt = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
2398 WINDOWINFO wi = {sizeof(WINDOWINFO)};
2399 RECT rcStartBtn;
2400
2401 bHandled = FALSE;
2402
2403 m_StartButton.GetWindowRect(&rcStartBtn);
2404 GetWindowInfo(m_hWnd, &wi);
2405
2406 switch (m_Position)
2407 {
2408 case ABE_TOP:
2409 case ABE_LEFT:
2410 {
2411 if (pt.x > rcStartBtn.right || pt.y > rcStartBtn.bottom)
2412 return 0;
2413 break;
2414 }
2415 case ABE_RIGHT:
2416 {
2417 if (pt.x < rcStartBtn.left || pt.y > rcStartBtn.bottom)
2418 return 0;
2419
2420 if (rcStartBtn.right + (int)wi.cxWindowBorders * 2 + 1 < wi.rcWindow.right &&
2421 pt.x > rcStartBtn.right)
2422 {
2423 return 0;
2424 }
2425 break;
2426 }
2427 case ABE_BOTTOM:
2428 {
2429 if (pt.x > rcStartBtn.right || pt.y < rcStartBtn.top)
2430 {
2431 return 0;
2432 }
2433
2434 if (rcStartBtn.bottom + (int)wi.cyWindowBorders * 2 + 1 < wi.rcWindow.bottom &&
2435 pt.y > rcStartBtn.bottom)
2436 {
2437 return 0;
2438 }
2439
2440 break;
2441 }
2442 }
2443
2444 bHandled = TRUE;
2445 PopupStartMenu();
2446 return 0;
2447 }
2448
2449 LRESULT OnNcRButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2450 {
2451 /* We want the user to be able to get a context menu even on the nonclient
2452 area (including the sizing border)! */
2453 uMsg = WM_CONTEXTMENU;
2454 wParam = (WPARAM) m_hWnd;
2455
2456 return OnContextMenu(uMsg, wParam, lParam, bHandled);
2457 }
2458
2459 LRESULT OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2460 {
2461 LRESULT Ret = FALSE;
2462 POINT pt, *ppt = NULL;
2463 HWND hWndExclude = NULL;
2464
2465 /* Check if the administrator has forbidden access to context menus */
2466 if (SHRestricted(REST_NOTRAYCONTEXTMENU))
2467 return FALSE;
2468
2469 pt.x = (SHORT) LOWORD(lParam);
2470 pt.y = (SHORT) HIWORD(lParam);
2471
2472 if (pt.x != -1 || pt.y != -1)
2473 ppt = &pt;
2474 else
2475 hWndExclude = m_StartButton.m_hWnd;
2476
2477 if ((HWND) wParam == m_StartButton.m_hWnd)
2478 {
2479 /* Make sure we can't track the context menu if the start
2480 menu is currently being shown */
2481 if (!(m_StartButton.SendMessage(BM_GETSTATE, 0, 0) & BST_PUSHED))
2482 {
2483 CComPtr<IContextMenu> ctxMenu;
2484 StartMenuBtnCtxMenuCreator(this, m_hWnd, &ctxMenu);
2485 TrackCtxMenu(ctxMenu, ppt, hWndExclude, m_Position == ABE_BOTTOM, this);
2486 }
2487 }
2488 else
2489 {
2490 /* See if the context menu should be handled by the task band site */
2491 if (ppt != NULL && m_TrayBandSite != NULL)
2492 {
2493 HWND hWndAtPt;
2494 POINT ptClient = *ppt;
2495
2496 /* Convert the coordinates to client-coordinates */
2497 ::MapWindowPoints(NULL, m_hWnd, &ptClient, 1);
2498
2499 hWndAtPt = ChildWindowFromPoint(ptClient);
2500 if (hWndAtPt != NULL &&
2501 (hWndAtPt == m_Rebar || ::IsChild(m_Rebar, hWndAtPt)))
2502 {
2503 /* Check if the user clicked on the task switch window */
2504 ptClient = *ppt;
2505 ::MapWindowPoints(NULL, m_Rebar, &ptClient, 1);
2506
2507 hWndAtPt = ::ChildWindowFromPointEx(m_Rebar, ptClient, CWP_SKIPINVISIBLE | CWP_SKIPDISABLED);
2508 if (hWndAtPt == m_TaskSwitch)
2509 goto HandleTrayContextMenu;
2510
2511 /* Forward the message to the task band site */
2512 m_TrayBandSite->ProcessMessage(m_hWnd, uMsg, wParam, lParam, &Ret);
2513 }
2514 else
2515 goto HandleTrayContextMenu;
2516 }
2517 else
2518 {
2519 HandleTrayContextMenu:
2520 /* Tray the default tray window context menu */
2521 TrackCtxMenu(this, ppt, NULL, FALSE, this);
2522 }
2523 }
2524 return Ret;
2525 }
2526
2527 LRESULT OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2528 {
2529 LRESULT Ret = FALSE;
2530 /* FIXME: We can't check with IsChild whether the hwnd is somewhere inside
2531 the rebar control! But we shouldn't forward messages that the band
2532 site doesn't handle, such as other controls (start button, tray window */
2533
2534 HRESULT hr = E_FAIL;
2535
2536 if (m_TrayBandSite)
2537 {
2538 hr = m_TrayBandSite->ProcessMessage(m_hWnd, uMsg, wParam, lParam, &Ret);
2539 if (SUCCEEDED(hr))
2540 return Ret;
2541 }
2542
2543 if (m_TrayBandSite == NULL || FAILED(hr))
2544 {
2545 const NMHDR *nmh = (const NMHDR *) lParam;
2546
2547 if (nmh->hwndFrom == m_TrayNotify)
2548 {
2549 switch (nmh->code)
2550 {
2551 case NTNWM_REALIGN:
2552 /* Cause all controls to be aligned */
2553 PostMessage(WM_SIZE, SIZE_RESTORED, 0);
2554 break;
2555 }
2556 }
2557 }
2558 return Ret;
2559 }
2560
2561 LRESULT OnNcLButtonDblClick(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2562 {
2563 /* Let the clock handle the double click */
2564 ::SendMessageW(m_TrayNotify, uMsg, wParam, lParam);
2565
2566 /* We "handle" this message so users can't cause a weird maximize/restore
2567 window animation when double-clicking the tray window! */
2568 return TRUE;
2569 }
2570
2571 LRESULT OnAppTrayDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2572 {
2573 DestroyWindow();
2574 return TRUE;
2575 }
2576
2577 LRESULT OnOpenStartMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2578 {
2579 HWND hwndStartMenu;
2580 HRESULT hr = IUnknown_GetWindow(m_StartMenuPopup, &hwndStartMenu);
2581 if (FAILED_UNEXPECTEDLY(hr))
2582 return FALSE;
2583
2584 if (::IsWindowVisible(hwndStartMenu))
2585 {
2586 m_StartMenuPopup->OnSelect(MPOS_CANCELLEVEL);
2587 }
2588 else
2589 {
2590 PopupStartMenu();
2591 }
2592
2593 return TRUE;
2594 }
2595
2596 LRESULT OnDoExitWindows(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2597 {
2598 /*
2599 * TWM_DOEXITWINDOWS is send by the CDesktopBrowser to us
2600 * to show the shutdown dialog. Also a WM_CLOSE message sent
2601 * by apps should show the dialog.
2602 */
2603 return DoExitWindows();
2604 }
2605
2606 LRESULT OnSysCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2607 {
2608 if (wParam == SC_CLOSE)
2609 {
2610 return DoExitWindows();
2611 }
2612
2613 bHandled = FALSE;
2614 return TRUE;
2615 }
2616
2617 LRESULT OnHotkey(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2618 {
2619 return HandleHotKey(wParam);
2620 }
2621
2622 LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2623 {
2624 LRESULT Ret = FALSE;
2625
2626 if ((HWND) lParam == m_StartButton.m_hWnd)
2627 {
2628 return FALSE;
2629 }
2630
2631 if (m_TrayBandSite == NULL || FAILED_UNEXPECTEDLY(m_TrayBandSite->ProcessMessage(m_hWnd, uMsg, wParam, lParam, &Ret)))
2632 {
2633 return HandleCommand(LOWORD(wParam));
2634 }
2635 return Ret;
2636 }
2637
2638 LRESULT OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2639 {
2640 if (g_TaskbarSettings.sr.AutoHide)
2641 {
2642 SetTimer(TIMER_ID_MOUSETRACK, MOUSETRACK_INTERVAL, NULL);
2643 }
2644
2645 return TRUE;
2646 }
2647
2648 LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2649 {
2650 if (wParam == TIMER_ID_MOUSETRACK)
2651 {
2652 ProcessMouseTracking();
2653 }
2654 else if (wParam == TIMER_ID_AUTOHIDE)
2655 {
2656 ProcessAutoHide();
2657 }
2658
2659 bHandled = FALSE;
2660 return TRUE;
2661 }
2662
2663 LRESULT OnNcCalcSize(INT code, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2664 {
2665 RECT *rc = NULL;
2666 /* Ignore WM_NCCALCSIZE if we are not themed or locked */
2667 if(!m_Theme || g_TaskbarSettings.bLock)
2668 {
2669 bHandled = FALSE;
2670 return 0;
2671 }
2672 if(!wParam)
2673 {
2674 rc = (RECT*)wParam;
2675 }
2676 else
2677 {
2678 NCCALCSIZE_PARAMS *prms = (NCCALCSIZE_PARAMS*)lParam;
2679 if(prms->lppos->flags & SWP_NOSENDCHANGING)
2680 {
2681 bHandled = FALSE;
2682 return 0;
2683 }
2684 rc = &prms->rgrc[0];
2685 }
2686
2687 AdjustSizerRect(rc, m_Position);
2688
2689 return 0;
2690 }
2691
2692 LRESULT OnRebarAutoSize(INT code, LPNMHDR nmhdr, BOOL& bHandled)
2693 {
2694 #if 0
2695 LPNMRBAUTOSIZE as = (LPNMRBAUTOSIZE) nmhdr;
2696
2697 if (!as->fChanged)
2698 return 0;
2699
2700 RECT rc;
2701 ::GetWindowRect(m_hWnd, &rc);
2702
2703 SIZE szWindow = {
2704 rc.right - rc.left,
2705 rc.bottom - rc.top };
2706 SIZE szTarget = {
2707 as->rcTarget.right - as->rcTarget.left,
2708 as->rcTarget.bottom - as->rcTarget.top };
2709 SIZE szActual = {
2710 as->rcActual.right - as->rcActual.left,
2711 as->rcActual.bottom - as->rcActual.top };
2712
2713 SIZE borders = {
2714 szWindow.cx - szTarget.cx,
2715 szWindow.cy - szTarget.cx,
2716 };
2717
2718 switch (m_Position)
2719 {
2720 case ABE_LEFT:
2721 szWindow.cx = szActual.cx + borders.cx;
2722 break;
2723 case ABE_TOP:
2724 szWindow.cy = szActual.cy + borders.cy;
2725 break;
2726 case ABE_RIGHT:
2727 szWindow.cx = szActual.cx + borders.cx;
2728 rc.left = rc.right - szWindow.cy;
2729 break;
2730 case ABE_BOTTOM:
2731 szWindow.cy = szActual.cy + borders.cy;
2732 rc.top = rc.bottom - szWindow.cy;
2733 break;
2734 }
2735
2736 SetWindowPos(NULL, rc.left, rc.top, szWindow.cx, szWindow.cy, SWP_NOACTIVATE | SWP_NOZORDER);
2737 #else
2738 bHandled = FALSE;
2739 #endif
2740 return 0;
2741 }
2742
2743 LRESULT OnTaskbarSettingsChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2744 {
2745 TaskbarSettings* newSettings = (TaskbarSettings*)lParam;
2746
2747 /* Propagate the new settings to the children */
2748 ::SendMessageW(m_TaskSwitch, uMsg, wParam, lParam);
2749 ::SendMessageW(m_TrayNotify, uMsg, wParam, lParam);
2750
2751 /* Toggle autohide */
2752 if (newSettings->sr.AutoHide != g_TaskbarSettings.sr.AutoHide)
2753 {
2754 g_TaskbarSettings.sr.AutoHide = newSettings->sr.AutoHide;
2755 memset(&m_AutoHideOffset, 0, sizeof(m_AutoHideOffset));
2756 m_AutoHideState = AUTOHIDE_SHOWN;
2757 if (!newSettings->sr.AutoHide)
2758 SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER);
2759 else
2760 SetTimer(TIMER_ID_MOUSETRACK, MOUSETRACK_INTERVAL, NULL);
2761 }
2762
2763 /* Toggle lock state */
2764 Lock(newSettings->bLock);
2765
2766 /* Toggle OnTop state */
2767 if (newSettings->sr.AlwaysOnTop != g_TaskbarSettings.sr.AlwaysOnTop)
2768 {
2769 g_TaskbarSettings.sr.AlwaysOnTop = newSettings->sr.AlwaysOnTop;
2770 HWND hWndInsertAfter = newSettings->sr.AlwaysOnTop ? HWND_TOPMOST : HWND_BOTTOM;
2771 SetWindowPos(hWndInsertAfter, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
2772 }
2773
2774 g_TaskbarSettings.Save();
2775 return 0;
2776 }
2777
2778 DECLARE_WND_CLASS_EX(szTrayWndClass, CS_DBLCLKS, COLOR_3DFACE)
2779
2780 BEGIN_MSG_MAP(CTrayWindow)
2781 if (m_StartMenuBand != NULL)
2782 {
2783 MSG Msg;
2784 LRESULT lRet;
2785
2786 Msg.hwnd = m_hWnd;
2787 Msg.message = uMsg;
2788 Msg.wParam = wParam;
2789 Msg.lParam = lParam;
2790
2791 if (m_StartMenuBand->TranslateMenuMessage(&Msg, &lRet) == S_OK)
2792 {
2793 return lRet;
2794 }
2795
2796 wParam = Msg.wParam;
2797 lParam = Msg.lParam;
2798 }
2799 MESSAGE_HANDLER(WM_THEMECHANGED, OnThemeChanged)
2800 MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChanged)
2801 NOTIFY_CODE_HANDLER(RBN_AUTOSIZE, OnRebarAutoSize) // Doesn't quite work ;P
2802 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
2803 MESSAGE_HANDLER(WM_SIZE, OnSize)
2804 MESSAGE_HANDLER(WM_CREATE, OnCreate)
2805 /*MESSAGE_HANDLER(WM_DESTROY, OnDestroy)*/
2806 MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTest)
2807 MESSAGE_HANDLER(WM_COMMAND, OnCommand)
2808 MESSAGE_HANDLER(WM_SYSCOMMAND, OnSysCommand)
2809 MESSAGE_HANDLER(WM_NOTIFY, OnNotify)
2810 MESSAGE_HANDLER(WM_CONTEXTMENU, OnContextMenu)
2811 MESSAGE_HANDLER(WM_TIMER, OnTimer)
2812 MESSAGE_HANDLER(WM_DISPLAYCHANGE, OnDisplayChange)
2813 MESSAGE_HANDLER(WM_COPYDATA, OnCopyData)
2814 MESSAGE_HANDLER(WM_NCPAINT, OnNcPaint)
2815 MESSAGE_HANDLER(WM_CTLCOLORBTN, OnCtlColorBtn)
2816 MESSAGE_HANDLER(WM_MOVING, OnMoving)
2817 MESSAGE_HANDLER(WM_SIZING, OnSizing)
2818 MESSAGE_HANDLER(WM_WINDOWPOSCHANGING, OnWindowPosChanging)
2819 MESSAGE_HANDLER(WM_ENTERSIZEMOVE, OnEnterSizeMove)
2820 MESSAGE_HANDLER(WM_EXITSIZEMOVE, OnExitSizeMove)
2821 MESSAGE_HANDLER(WM_NCLBUTTONDOWN, OnNcLButtonDown)
2822 MESSAGE_HANDLER(WM_SYSCHAR, OnSysChar)
2823 MESSAGE_HANDLER(WM_NCRBUTTONUP, OnNcRButtonUp)
2824 MESSAGE_HANDLER(WM_NCLBUTTONDBLCLK, OnNcLButtonDblClick)
2825 MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
2826 MESSAGE_HANDLER(WM_NCMOUSEMOVE, OnMouseMove)
2827 MESSAGE_HANDLER(WM_APP_TRAYDESTROY, OnAppTrayDestroy)
2828 MESSAGE_HANDLER(TWM_OPENSTARTMENU, OnOpenStartMenu)
2829 MESSAGE_HANDLER(TWM_DOEXITWINDOWS, OnDoExitWindows)
2830 MESSAGE_HANDLER(WM_CLOSE, OnDoExitWindows)
2831 MESSAGE_HANDLER(WM_HOTKEY, OnHotkey)
2832 MESSAGE_HANDLER(WM_NCCALCSIZE, OnNcCalcSize)
2833 MESSAGE_HANDLER(TWM_SETTINGSCHANGED, OnTaskbarSettingsChanged)
2834 ALT_MSG_MAP(1)
2835 END_MSG_MAP()
2836
2837 /*****************************************************************************/
2838
2839 VOID TrayProcessMessages()
2840 {
2841 MSG Msg;
2842
2843 /* FIXME: We should keep a reference here... */
2844
2845 while (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE))
2846 {
2847 if (Msg.message == WM_QUIT)
2848 break;
2849
2850 if (m_StartMenuBand == NULL ||
2851 m_StartMenuBand->IsMenuMessage(&Msg) != S_OK)
2852 {
2853 TranslateMessage(&Msg);
2854 DispatchMessage(&Msg);
2855 }
2856 }
2857 }
2858
2859 VOID TrayMessageLoop()
2860 {
2861 MSG Msg;
2862 BOOL Ret;
2863
2864 /* FIXME: We should keep a reference here... */
2865
2866 while (true)
2867 {
2868 Ret = GetMessage(&Msg, NULL, 0, 0);
2869
2870 if (!Ret || Ret == -1)
2871 break;
2872
2873 if (m_StartMenuBand == NULL ||
2874 m_StartMenuBand->IsMenuMessage(&Msg) != S_OK)
2875 {
2876 TranslateMessage(&Msg);
2877 DispatchMessage(&Msg);
2878 }
2879 }
2880 }
2881
2882 /*
2883 * IShellDesktopTray
2884 *
2885 * NOTE: this is a very windows-specific COM interface used by SHCreateDesktop()!
2886 * These are the calls I observed, it may be wrong/incomplete/buggy!!!
2887 * The reason we implement it is because we have to use SHCreateDesktop() so
2888 * that the shell provides the desktop window and all the features that come
2889 * with it (especially positioning of desktop icons)
2890 */
2891
2892 virtual ULONG STDMETHODCALLTYPE GetState()
2893 {
2894 /* FIXME: Return ABS_ flags? */
2895 TRACE("IShellDesktopTray::GetState() unimplemented!\n");
2896 return 0;
2897 }
2898
2899 virtual HRESULT STDMETHODCALLTYPE GetTrayWindow(OUT HWND *phWndTray)
2900 {
2901 TRACE("IShellDesktopTray::GetTrayWindow(0x%p)\n", phWndTray);
2902 *phWndTray = m_hWnd;
2903 return S_OK;
2904 }
2905
2906 virtual HRESULT STDMETHODCALLTYPE RegisterDesktopWindow(IN HWND hWndDesktop)
2907 {
2908 TRACE("IShellDesktopTray::RegisterDesktopWindow(0x%p)\n", hWndDesktop);
2909
2910 m_DesktopWnd = hWndDesktop;
2911 return S_OK;
2912 }
2913
2914 virtual HRESULT STDMETHODCALLTYPE Unknown(IN DWORD dwUnknown1, IN DWORD dwUnknown2)
2915 {
2916 TRACE("IShellDesktopTray::Unknown(%u,%u) unimplemented!\n", dwUnknown1, dwUnknown2);
2917 return S_OK;
2918 }
2919
2920 virtual HRESULT RaiseStartButton()
2921 {
2922 m_StartButton.SendMessageW(BM_SETSTATE, FALSE, 0);
2923 return S_OK;
2924 }
2925
2926 HRESULT WINAPI GetWindow(HWND* phwnd)
2927 {
2928 if (!phwnd)
2929 return E_INVALIDARG;
2930 *phwnd = m_hWnd;
2931 return S_OK;
2932 }
2933
2934 HRESULT WINAPI ContextSensitiveHelp(BOOL fEnterMode)
2935 {
2936 return E_NOTIMPL;
2937 }
2938
2939 void _Init()
2940 {
2941 m_Position = (DWORD) -1;
2942 }
2943
2944 DECLARE_NOT_AGGREGATABLE(CTrayWindow)
2945
2946 DECLARE_PROTECT_FINAL_CONSTRUCT()
2947 BEGIN_COM_MAP(CTrayWindow)
2948 /*COM_INTERFACE_ENTRY_IID(IID_ITrayWindow, ITrayWindow)*/
2949 COM_INTERFACE_ENTRY_IID(IID_IShellDesktopTray, IShellDesktopTray)
2950 COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow)
2951 COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu)
2952 END_COM_MAP()
2953 };
2954
2955 class CTrayWindowCtxMenu :
2956 public CComCoClass<CTrayWindowCtxMenu>,
2957 public CComObjectRootEx<CComMultiThreadModelNoCS>,
2958 public IContextMenu
2959 {
2960 HWND hWndOwner;
2961 CComPtr<CTrayWindow> TrayWnd;
2962 CComPtr<IContextMenu> pcm;
2963 UINT m_idCmdCmFirst;
2964
2965 public:
2966 HRESULT Initialize(ITrayWindow * pTrayWnd, IN HWND hWndOwner)
2967 {
2968 this->TrayWnd = (CTrayWindow *) pTrayWnd;
2969 this->hWndOwner = hWndOwner;
2970 this->m_idCmdCmFirst = 0;
2971 return S_OK;
2972 }
2973
2974 virtual HRESULT STDMETHODCALLTYPE
2975 QueryContextMenu(HMENU hPopup,
2976 UINT indexMenu,
2977 UINT idCmdFirst,
2978 UINT idCmdLast,
2979 UINT uFlags)
2980 {
2981 HMENU menubase = LoadPopupMenu(hExplorerInstance, MAKEINTRESOURCEW(IDM_TRAYWND));
2982 if (!menubase)
2983 return HRESULT_FROM_WIN32(GetLastError());
2984
2985 if (SHRestricted(REST_CLASSICSHELL) != 0)
2986 {
2987 DeleteMenu(hPopup,
2988 ID_LOCKTASKBAR,
2989 MF_BYCOMMAND);
2990 }
2991
2992 CheckMenuItem(hPopup,
2993 ID_LOCKTASKBAR,
2994 MF_BYCOMMAND | (g_TaskbarSettings.bLock ? MF_CHECKED : MF_UNCHECKED));
2995
2996 UINT idCmdNext;
2997 idCmdNext = Shell_MergeMenus(hPopup, menubase, indexMenu, idCmdFirst, idCmdLast, MM_SUBMENUSHAVEIDS | MM_ADDSEPARATOR);
2998 m_idCmdCmFirst = idCmdNext - idCmdFirst;
2999
3000 ::DestroyMenu(menubase);
3001
3002 if (TrayWnd->m_TrayBandSite != NULL)
3003 {
3004 if (FAILED(TrayWnd->m_TrayBandSite->AddContextMenus(
3005 hPopup,
3006 indexMenu,
3007 idCmdNext,
3008 idCmdLast,
3009 CMF_NORMAL,
3010 &pcm)))
3011 {
3012 WARN("AddContextMenus failed.\n");
3013 pcm = NULL;
3014 }
3015 }
3016
3017 return S_OK;
3018 }
3019
3020 virtual HRESULT STDMETHODCALLTYPE
3021 InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
3022 {
3023 UINT uiCmdId = (UINT) lpici->lpVerb;
3024 if (uiCmdId != 0)
3025 {
3026 if (uiCmdId >= m_idCmdCmFirst)
3027 {
3028 CMINVOKECOMMANDINFO cmici = { 0 };
3029
3030 if (pcm != NULL)
3031 {
3032 /* Setup and invoke the shell command */
3033 cmici.cbSize = sizeof(cmici);
3034 cmici.hwnd = hWndOwner;
3035 cmici.lpVerb = (LPCSTR) MAKEINTRESOURCEW(uiCmdId - m_idCmdCmFirst);
3036 cmici.nShow = SW_NORMAL;
3037
3038 pcm->InvokeCommand(&cmici);
3039 }
3040 }
3041 else
3042 {
3043 TrayWnd->ExecContextMenuCmd(uiCmdId);
3044 }
3045 }
3046
3047 return S_OK;
3048 }
3049
3050 virtual HRESULT STDMETHODCALLTYPE
3051 GetCommandString(UINT_PTR idCmd,
3052 UINT uType,
3053 UINT *pwReserved,
3054 LPSTR pszName,
3055 UINT cchMax)
3056 {
3057 return E_NOTIMPL;
3058 }
3059
3060 CTrayWindowCtxMenu()
3061 {
3062 }
3063
3064 virtual ~CTrayWindowCtxMenu()
3065 {
3066 }
3067
3068 BEGIN_COM_MAP(CTrayWindowCtxMenu)
3069 COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu)
3070 END_COM_MAP()
3071 };
3072
3073 HRESULT TrayWindowCtxMenuCreator(ITrayWindow * TrayWnd, IN HWND hWndOwner, IContextMenu ** ppCtxMenu)
3074 {
3075 CTrayWindowCtxMenu * mnu = new CComObject<CTrayWindowCtxMenu>();
3076 mnu->Initialize(TrayWnd, hWndOwner);
3077 *ppCtxMenu = mnu;
3078 return S_OK;
3079 }
3080
3081 HRESULT CreateTrayWindow(ITrayWindow ** ppTray)
3082 {
3083 CComPtr<CTrayWindow> Tray = new CComObject<CTrayWindow>();
3084 if (Tray == NULL)
3085 return E_OUTOFMEMORY;
3086
3087 Tray->_Init();
3088 Tray->Open();
3089
3090 *ppTray = (ITrayWindow *) Tray;
3091
3092 return S_OK;
3093 }
3094
3095 HRESULT
3096 Tray_OnStartMenuDismissed(ITrayWindow* Tray)
3097 {
3098 CTrayWindow * TrayWindow = static_cast<CTrayWindow *>(Tray);
3099 return TrayWindow->RaiseStartButton();
3100 }
3101
3102 VOID TrayProcessMessages(ITrayWindow *Tray)
3103 {
3104 CTrayWindow * TrayWindow = static_cast<CTrayWindow *>(Tray);
3105 TrayWindow->TrayProcessMessages();
3106 }
3107
3108 VOID TrayMessageLoop(ITrayWindow *Tray)
3109 {
3110 CTrayWindow * TrayWindow = static_cast<CTrayWindow *>(Tray);
3111 TrayWindow->TrayMessageLoop();
3112 }