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