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