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