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