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