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