4c3b6b2957613de6b25679db32a3a1fb095c8dd0
[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 int iSizerPart[4] = {TBP_SIZINGBARLEFT, TBP_SIZINGBARTOP, TBP_SIZINGBARRIGHT, TBP_SIZINGBARBOTTOM};
854 SIZE size;
855
856 if (pos > ABE_BOTTOM)
857 pos = ABE_BOTTOM;
858
859 HRESULT hr = GetThemePartSize(m_Theme, NULL, iSizerPart[pos], 0, NULL, TS_TRUE, &size);
860 if (FAILED_UNEXPECTEDLY(hr))
861 return;
862
863 switch (pos)
864 {
865 case ABE_TOP:
866 rc->bottom -= size.cy;
867 break;
868 case ABE_BOTTOM:
869 rc->top += size.cy;
870 break;
871 case ABE_LEFT:
872 rc->right -= size.cx;
873 break;
874 case ABE_RIGHT:
875 rc->left += size.cx;
876 break;
877 }
878 }
879
880 VOID MakeTrayRectWithSize(IN DWORD Position,
881 IN const SIZE *pTraySize,
882 IN OUT RECT *pRect)
883 {
884 switch (Position)
885 {
886 case ABE_LEFT:
887 pRect->right = pRect->left + pTraySize->cx;
888 break;
889
890 case ABE_TOP:
891 pRect->bottom = pRect->top + pTraySize->cy;
892 break;
893
894 case ABE_RIGHT:
895 pRect->left = pRect->right - pTraySize->cx;
896 break;
897
898 case ABE_BOTTOM:
899 default:
900 pRect->top = pRect->bottom - pTraySize->cy;
901 break;
902 }
903 }
904
905 VOID GetTrayRectFromScreenRect(IN DWORD Position,
906 IN const RECT *pScreen,
907 IN const SIZE *pTraySize OPTIONAL,
908 OUT RECT *pRect)
909 {
910 if (pTraySize == NULL)
911 pTraySize = &m_TraySize;
912
913 *pRect = *pScreen;
914
915 if(!m_Theme)
916 {
917 /* Move the border outside of the screen */
918 InflateRect(pRect,
919 GetSystemMetrics(SM_CXEDGE),
920 GetSystemMetrics(SM_CYEDGE));
921 }
922
923 MakeTrayRectWithSize(Position, pTraySize, pRect);
924 }
925
926 BOOL IsPosHorizontal()
927 {
928 return m_Position == ABE_TOP || m_Position == ABE_BOTTOM;
929 }
930
931 HMONITOR CalculateValidSize(
932 IN DWORD Position,
933 IN OUT RECT *pRect)
934 {
935 RECT rcScreen;
936 //BOOL Horizontal;
937 HMONITOR hMon;
938 SIZE szMax, szWnd;
939
940 //Horizontal = IsPosHorizontal();
941
942 szWnd.cx = pRect->right - pRect->left;
943 szWnd.cy = pRect->bottom - pRect->top;
944
945 rcScreen = *pRect;
946 hMon = GetScreenRectFromRect(
947 &rcScreen,
948 MONITOR_DEFAULTTONEAREST);
949
950 /* Calculate the maximum size of the tray window and limit the window
951 size to half of the screen's size. */
952 szMax.cx = (rcScreen.right - rcScreen.left) / 2;
953 szMax.cy = (rcScreen.bottom - rcScreen.top) / 2;
954 if (szWnd.cx > szMax.cx)
955 szWnd.cx = szMax.cx;
956 if (szWnd.cy > szMax.cy)
957 szWnd.cy = szMax.cy;
958
959 /* FIXME - calculate */
960
961 GetTrayRectFromScreenRect(Position,
962 &rcScreen,
963 &szWnd,
964 pRect);
965
966 return hMon;
967 }
968
969 #if 0
970 VOID
971 GetMinimumWindowSize(
972 OUT RECT *pRect)
973 {
974 RECT rcMin = {0};
975
976 AdjustWindowRectEx(&rcMin,
977 GetWindowLong(m_hWnd,
978 GWL_STYLE),
979 FALSE,
980 GetWindowLong(m_hWnd,
981 GWL_EXSTYLE));
982
983 *pRect = rcMin;
984 }
985 #endif
986
987
988 DWORD GetDraggingRectFromPt(
989 IN POINT pt,
990 OUT RECT *pRect,
991 OUT HMONITOR *phMonitor)
992 {
993 HMONITOR hMon, hMonNew;
994 DWORD PosH, PosV, Pos;
995 SIZE DeltaPt, ScreenOffset;
996 RECT rcScreen;
997
998 rcScreen.left = 0;
999 rcScreen.top = 0;
1000
1001 /* Determine the screen rectangle */
1002 hMon = MonitorFromPoint(pt, MONITOR_DEFAULTTONULL);
1003 if (hMon != NULL)
1004 {
1005 MONITORINFO mi;
1006
1007 mi.cbSize = sizeof(mi);
1008 if (!GetMonitorInfo(hMon, &mi))
1009 {
1010 hMon = NULL;
1011 goto GetPrimaryScreenRect;
1012 }
1013
1014 /* make left top corner of the screen zero based to
1015 make calculations easier */
1016 pt.x -= mi.rcMonitor.left;
1017 pt.y -= mi.rcMonitor.top;
1018
1019 ScreenOffset.cx = mi.rcMonitor.left;
1020 ScreenOffset.cy = mi.rcMonitor.top;
1021 rcScreen.right = mi.rcMonitor.right - mi.rcMonitor.left;
1022 rcScreen.bottom = mi.rcMonitor.bottom - mi.rcMonitor.top;
1023 }
1024 else
1025 {
1026 GetPrimaryScreenRect:
1027 ScreenOffset.cx = 0;
1028 ScreenOffset.cy = 0;
1029 rcScreen.right = GetSystemMetrics(SM_CXSCREEN);
1030 rcScreen.bottom = GetSystemMetrics(SM_CYSCREEN);
1031 }
1032
1033 /* Calculate the nearest screen border */
1034 if (pt.x < rcScreen.right / 2)
1035 {
1036 DeltaPt.cx = pt.x;
1037 PosH = ABE_LEFT;
1038 }
1039 else
1040 {
1041 DeltaPt.cx = rcScreen.right - pt.x;
1042 PosH = ABE_RIGHT;
1043 }
1044
1045 if (pt.y < rcScreen.bottom / 2)
1046 {
1047 DeltaPt.cy = pt.y;
1048 PosV = ABE_TOP;
1049 }
1050 else
1051 {
1052 DeltaPt.cy = rcScreen.bottom - pt.y;
1053 PosV = ABE_BOTTOM;
1054 }
1055
1056 Pos = (DeltaPt.cx * rcScreen.bottom < DeltaPt.cy * rcScreen.right) ? PosH : PosV;
1057
1058 /* Fix the screen origin to be relative to the primary monitor again */
1059 OffsetRect(&rcScreen,
1060 ScreenOffset.cx,
1061 ScreenOffset.cy);
1062
1063 RECT rcPos = m_TrayRects[Pos];
1064
1065 hMonNew = GetMonitorFromRect(&rcPos);
1066 if (hMon != hMonNew)
1067 {
1068 SIZE szTray;
1069
1070 /* Recalculate the rectangle, we're dragging to another monitor.
1071 We don't need to recalculate the rect on single monitor systems. */
1072 szTray.cx = rcPos.right - rcPos.left;
1073 szTray.cy = rcPos.bottom - rcPos.top;
1074
1075 GetTrayRectFromScreenRect(Pos, &rcScreen, &szTray, pRect);
1076 if (AutoHide)
1077 {
1078 pRect->left += m_AutoHideOffset.cx;
1079 pRect->right += m_AutoHideOffset.cx;
1080 pRect->top += m_AutoHideOffset.cy;
1081 pRect->bottom += m_AutoHideOffset.cy;
1082 }
1083 hMon = hMonNew;
1084 }
1085 else
1086 {
1087 /* The user is dragging the tray window on the same monitor. We don't need
1088 to recalculate the rectangle */
1089 *pRect = rcPos;
1090 if (AutoHide)
1091 {
1092 pRect->left += m_AutoHideOffset.cx;
1093 pRect->right += m_AutoHideOffset.cx;
1094 pRect->top += m_AutoHideOffset.cy;
1095 pRect->bottom += m_AutoHideOffset.cy;
1096 }
1097 }
1098
1099 *phMonitor = hMon;
1100
1101 return Pos;
1102 }
1103
1104 DWORD GetDraggingRectFromRect(
1105 IN OUT RECT *pRect,
1106 OUT HMONITOR *phMonitor)
1107 {
1108 POINT pt;
1109
1110 /* Calculate the center of the rectangle. We call
1111 GetDraggingRectFromPt to calculate a valid
1112 dragging rectangle */
1113 pt.x = pRect->left + ((pRect->right - pRect->left) / 2);
1114 pt.y = pRect->top + ((pRect->bottom - pRect->top) / 2);
1115
1116 return GetDraggingRectFromPt(
1117 pt,
1118 pRect,
1119 phMonitor);
1120 }
1121
1122 VOID ChangingWinPos(IN OUT LPWINDOWPOS pwp)
1123 {
1124 RECT rcTray;
1125
1126 if (IsDragging)
1127 {
1128 rcTray.left = pwp->x;
1129 rcTray.top = pwp->y;
1130 rcTray.right = rcTray.left + pwp->cx;
1131 rcTray.bottom = rcTray.top + pwp->cy;
1132 if (AutoHide)
1133 {
1134 rcTray.left -= m_AutoHideOffset.cx;
1135 rcTray.right -= m_AutoHideOffset.cx;
1136 rcTray.top -= m_AutoHideOffset.cy;
1137 rcTray.bottom -= m_AutoHideOffset.cy;
1138 }
1139
1140 if (!EqualRect(&rcTray,
1141 &m_TrayRects[m_DraggingPosition]))
1142 {
1143 /* Recalculate the rectangle, the user dragged the tray
1144 window to another monitor or the window was somehow else
1145 moved or resized */
1146 m_DraggingPosition = GetDraggingRectFromRect(
1147 &rcTray,
1148 &m_DraggingMonitor);
1149 //m_TrayRects[DraggingPosition] = rcTray;
1150 }
1151
1152 //Monitor = CalculateValidSize(DraggingPosition,
1153 // &rcTray);
1154
1155 m_Monitor = m_DraggingMonitor;
1156 m_Position = m_DraggingPosition;
1157 IsDragging = FALSE;
1158
1159 m_TrayRects[m_Position] = rcTray;
1160 goto ChangePos;
1161 }
1162 else if (GetWindowRect(&rcTray))
1163 {
1164 if (InSizeMove)
1165 {
1166 if (!(pwp->flags & SWP_NOMOVE))
1167 {
1168 rcTray.left = pwp->x;
1169 rcTray.top = pwp->y;
1170 }
1171
1172 if (!(pwp->flags & SWP_NOSIZE))
1173 {
1174 rcTray.right = rcTray.left + pwp->cx;
1175 rcTray.bottom = rcTray.top + pwp->cy;
1176 }
1177
1178 m_Position = GetDraggingRectFromRect(&rcTray, &m_Monitor);
1179
1180 if (!(pwp->flags & (SWP_NOMOVE | SWP_NOSIZE)))
1181 {
1182 SIZE szWnd;
1183
1184 szWnd.cx = pwp->cx;
1185 szWnd.cy = pwp->cy;
1186
1187 MakeTrayRectWithSize(m_Position, &szWnd, &rcTray);
1188 }
1189
1190 if (AutoHide)
1191 {
1192 rcTray.left -= m_AutoHideOffset.cx;
1193 rcTray.right -= m_AutoHideOffset.cx;
1194 rcTray.top -= m_AutoHideOffset.cy;
1195 rcTray.bottom -= m_AutoHideOffset.cy;
1196 }
1197 m_TrayRects[m_Position] = rcTray;
1198 }
1199 else
1200 {
1201 /* If the user isn't resizing the tray window we need to make sure the
1202 new size or position is valid. this is to prevent changes to the window
1203 without user interaction. */
1204 rcTray = m_TrayRects[m_Position];
1205 }
1206
1207 ChangePos:
1208 m_TraySize.cx = rcTray.right - rcTray.left;
1209 m_TraySize.cy = rcTray.bottom - rcTray.top;
1210
1211 if (AutoHide)
1212 {
1213 rcTray.left += m_AutoHideOffset.cx;
1214 rcTray.right += m_AutoHideOffset.cx;
1215 rcTray.top += m_AutoHideOffset.cy;
1216 rcTray.bottom += m_AutoHideOffset.cy;
1217 }
1218
1219 pwp->flags &= ~(SWP_NOMOVE | SWP_NOSIZE);
1220 pwp->x = rcTray.left;
1221 pwp->y = rcTray.top;
1222 pwp->cx = m_TraySize.cx;
1223 pwp->cy = m_TraySize.cy;
1224 }
1225 }
1226
1227 VOID ApplyClipping(IN BOOL Clip)
1228 {
1229 RECT rcClip, rcWindow;
1230 HRGN hClipRgn;
1231
1232 if (GetWindowRect(&rcWindow))
1233 {
1234 /* Disable clipping on systems with only one monitor */
1235 if (GetSystemMetrics(SM_CMONITORS) <= 1)
1236 Clip = FALSE;
1237
1238 if (Clip)
1239 {
1240 rcClip = rcWindow;
1241
1242 GetScreenRect(m_Monitor, &rcClip);
1243
1244 if (!IntersectRect(&rcClip, &rcClip, &rcWindow))
1245 {
1246 rcClip = rcWindow;
1247 }
1248
1249 OffsetRect(&rcClip,
1250 -rcWindow.left,
1251 -rcWindow.top);
1252
1253 hClipRgn = CreateRectRgnIndirect(&rcClip);
1254 }
1255 else
1256 hClipRgn = NULL;
1257
1258 /* Set the clipping region or make sure the window isn't clipped
1259 by disabling it explicitly. */
1260 SetWindowRgn(hClipRgn, TRUE);
1261 }
1262 }
1263
1264 VOID ResizeWorkArea()
1265 {
1266 #if !WIN7_DEBUG_MODE
1267 RECT rcTray, rcWorkArea;
1268
1269 /* If monitor has changed then fix the previous monitors work area */
1270 if (m_PreviousMonitor != m_Monitor)
1271 {
1272 GetScreenRect(m_PreviousMonitor, &rcWorkArea);
1273 SystemParametersInfoW(SPI_SETWORKAREA,
1274 1,
1275 &rcWorkArea,
1276 SPIF_SENDCHANGE);
1277 }
1278
1279 rcTray = m_TrayRects[m_Position];
1280
1281 GetScreenRect(m_Monitor, &rcWorkArea);
1282 m_PreviousMonitor = m_Monitor;
1283
1284 /* If AutoHide is false then change the workarea to exclude
1285 the area that the taskbar covers. */
1286 if (!AutoHide)
1287 {
1288 switch (m_Position)
1289 {
1290 case ABE_TOP:
1291 rcWorkArea.top = rcTray.bottom;
1292 break;
1293 case ABE_LEFT:
1294 rcWorkArea.left = rcTray.right;
1295 break;
1296 case ABE_RIGHT:
1297 rcWorkArea.right = rcTray.left;
1298 break;
1299 case ABE_BOTTOM:
1300 rcWorkArea.bottom = rcTray.top;
1301 break;
1302 }
1303 }
1304
1305 /*
1306 * Resize the current monitor work area. Win32k will also send
1307 * a WM_SIZE message to automatically resize the desktop.
1308 */
1309 SystemParametersInfoW(SPI_SETWORKAREA,
1310 1,
1311 &rcWorkArea,
1312 SPIF_SENDCHANGE);
1313 #endif
1314 }
1315
1316 VOID CheckTrayWndPosition()
1317 {
1318 RECT rcTray;
1319
1320 rcTray = m_TrayRects[m_Position];
1321
1322 if (AutoHide)
1323 {
1324 rcTray.left += m_AutoHideOffset.cx;
1325 rcTray.right += m_AutoHideOffset.cx;
1326 rcTray.top += m_AutoHideOffset.cy;
1327 rcTray.bottom += m_AutoHideOffset.cy;
1328 }
1329
1330 IUnknown_Exec(m_TrayBandSite,
1331 IID_IDeskBand,
1332 DBID_BANDINFOCHANGED,
1333 0,
1334 NULL,
1335 NULL);
1336
1337 FitToRebar(&rcTray);
1338 m_TrayRects[m_Position] = rcTray;
1339
1340 /* Move the tray window */
1341 SetWindowPos(NULL,
1342 rcTray.left,
1343 rcTray.top,
1344 rcTray.right - rcTray.left,
1345 rcTray.bottom - rcTray.top,
1346 SWP_NOZORDER | SWP_NOACTIVATE);
1347
1348 ResizeWorkArea();
1349
1350 ApplyClipping(TRUE);
1351 }
1352
1353 typedef struct _TW_STUCKRECTS2
1354 {
1355 DWORD cbSize;
1356 LONG Unknown;
1357 DWORD dwFlags;
1358 DWORD Position;
1359 SIZE Size;
1360 RECT Rect;
1361 } TW_STRUCKRECTS2, *PTW_STUCKRECTS2;
1362
1363 VOID RegLoadSettings()
1364 {
1365 DWORD Pos;
1366 TW_STRUCKRECTS2 sr;
1367 RECT rcScreen;
1368 SIZE WndSize, EdgeSize, DlgFrameSize;
1369 DWORD cbSize = sizeof(sr);
1370 SIZE StartBtnSize = m_StartButton.GetSize();
1371
1372 EdgeSize.cx = GetSystemMetrics(SM_CXEDGE);
1373 EdgeSize.cy = GetSystemMetrics(SM_CYEDGE);
1374 DlgFrameSize.cx = GetSystemMetrics(SM_CXDLGFRAME);
1375 DlgFrameSize.cy = GetSystemMetrics(SM_CYDLGFRAME);
1376
1377 if (SHGetValue(hkExplorer,
1378 TEXT("StuckRects2"),
1379 TEXT("Settings"),
1380 NULL,
1381 &sr,
1382 &cbSize) == ERROR_SUCCESS &&
1383 sr.cbSize == sizeof(sr))
1384 {
1385 AutoHide = (sr.dwFlags & ABS_AUTOHIDE) != 0;
1386 AlwaysOnTop = (sr.dwFlags & ABS_ALWAYSONTOP) != 0;
1387 SmSmallIcons = (sr.dwFlags & 0x4) != 0;
1388 HideClock = (sr.dwFlags & 0x8) != 0;
1389
1390 /* FIXME: Are there more flags? */
1391
1392 #if WIN7_DEBUG_MODE
1393 m_Position = ABE_LEFT;
1394 #else
1395 if (sr.Position > ABE_BOTTOM)
1396 m_Position = ABE_BOTTOM;
1397 else
1398 m_Position = sr.Position;
1399 #endif
1400
1401 /* Try to find out which monitor the tray window was located on last.
1402 Here we're only interested in the monitor screen that we think
1403 is the last one used. We're going to determine on which monitor
1404 we really are after calculating the docked position. */
1405 rcScreen = sr.Rect;
1406 GetScreenRectFromRect(
1407 &rcScreen,
1408 MONITOR_DEFAULTTONEAREST);
1409 }
1410 else
1411 {
1412 m_Position = ABE_BOTTOM;
1413 AlwaysOnTop = TRUE;
1414
1415 /* Use the minimum size of the taskbar, we'll use the start
1416 button as a minimum for now. Make sure we calculate the
1417 entire window size, not just the client size. However, we
1418 use a thinner border than a standard thick border, so that
1419 the start button and bands are not stuck to the screen border. */
1420 if(!m_Theme)
1421 {
1422 sr.Size.cx = StartBtnSize.cx + (2 * (EdgeSize.cx + DlgFrameSize.cx));
1423 sr.Size.cy = StartBtnSize.cy + (2 * (EdgeSize.cy + DlgFrameSize.cy));
1424 }
1425 else
1426 {
1427 sr.Size.cx = StartBtnSize.cx - EdgeSize.cx;
1428 sr.Size.cy = StartBtnSize.cy - EdgeSize.cy;
1429 if(!Locked)
1430 sr.Size.cy += GetSystemMetrics(SM_CYSIZEFRAME);
1431 }
1432
1433 /* Use the primary screen by default */
1434 rcScreen.left = 0;
1435 rcScreen.top = 0;
1436 rcScreen.right = GetSystemMetrics(SM_CXSCREEN);
1437 rcScreen.bottom = GetSystemMetrics(SM_CYSCREEN);
1438 GetScreenRectFromRect(
1439 &rcScreen,
1440 MONITOR_DEFAULTTOPRIMARY);
1441 }
1442
1443 if (m_hWnd != NULL)
1444 SetWindowPos(
1445 AlwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
1446 0,
1447 0,
1448 0,
1449 0,
1450 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
1451
1452 /* Determine a minimum tray window rectangle. The "client" height is
1453 zero here since we cannot determine an optimal minimum width when
1454 loaded as a vertical tray window. We just need to make sure the values
1455 loaded from the registry are at least. The windows explorer behaves
1456 the same way, it allows the user to save a zero width vertical tray
1457 window, but not a zero height horizontal tray window. */
1458 if(!m_Theme)
1459 {
1460 WndSize.cx = 2 * (EdgeSize.cx + DlgFrameSize.cx);
1461 WndSize.cy = StartBtnSize.cy + (2 * (EdgeSize.cy + DlgFrameSize.cy));
1462 }
1463 else
1464 {
1465 WndSize.cx = StartBtnSize.cx;
1466 WndSize.cy = StartBtnSize.cy - EdgeSize.cx;
1467 }
1468
1469 if (WndSize.cx < sr.Size.cx)
1470 WndSize.cx = sr.Size.cx;
1471 if (WndSize.cy < sr.Size.cy)
1472 WndSize.cy = sr.Size.cy;
1473
1474 /* Save the calculated size */
1475 m_TraySize = WndSize;
1476
1477 /* Calculate all docking rectangles. We need to do this here so they're
1478 initialized and dragging the tray window to another position gives
1479 usable results */
1480 for (Pos = ABE_LEFT; Pos <= ABE_BOTTOM; Pos++)
1481 {
1482 GetTrayRectFromScreenRect(Pos,
1483 &rcScreen,
1484 &m_TraySize,
1485 &m_TrayRects[Pos]);
1486 // 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);
1487 }
1488
1489 /* Determine which monitor we are on. It shouldn't matter which docked
1490 position rectangle we use */
1491 m_Monitor = GetMonitorFromRect(&m_TrayRects[ABE_LEFT]);
1492 }
1493
1494 VOID AlignControls(IN PRECT prcClient OPTIONAL)
1495 {
1496 RECT rcClient;
1497 SIZE TraySize, StartSize;
1498 POINT ptTrayNotify = { 0, 0 };
1499 BOOL Horizontal;
1500 HDWP dwp;
1501
1502 m_StartButton.UpdateSize();
1503 if (prcClient != NULL)
1504 {
1505 rcClient = *prcClient;
1506 }
1507 else
1508 {
1509 if (!GetClientRect(&rcClient))
1510 {
1511 ERR("Could not get client rect lastErr=%d\n", GetLastError());
1512 return;
1513 }
1514 }
1515
1516 Horizontal = IsPosHorizontal();
1517
1518 /* We're about to resize/move the start button, the rebar control and
1519 the tray notification control */
1520 dwp = BeginDeferWindowPos(3);
1521 if (dwp == NULL)
1522 {
1523 ERR("BeginDeferWindowPos failed. lastErr=%d\n", GetLastError());
1524 return;
1525 }
1526
1527 /* Limit the Start button width to the client width, if necessary */
1528 StartSize = m_StartButton.GetSize();
1529 if (StartSize.cx > rcClient.right)
1530 StartSize.cx = rcClient.right;
1531
1532 if (m_StartButton.m_hWnd != NULL)
1533 {
1534 /* Resize and reposition the button */
1535 dwp = m_StartButton.DeferWindowPos(dwp,
1536 NULL,
1537 0,
1538 0,
1539 StartSize.cx,
1540 StartSize.cy,
1541 SWP_NOZORDER | SWP_NOACTIVATE);
1542 if (dwp == NULL)
1543 {
1544 ERR("DeferWindowPos for start button failed. lastErr=%d\n", GetLastError());
1545 return;
1546 }
1547 }
1548
1549 /* Determine the size that the tray notification window needs */
1550 if (Horizontal)
1551 {
1552 TraySize.cx = 0;
1553 TraySize.cy = rcClient.bottom;
1554 }
1555 else
1556 {
1557 TraySize.cx = rcClient.right;
1558 TraySize.cy = 0;
1559 }
1560
1561 if (m_TrayNotify != NULL &&
1562 SendMessage(m_TrayNotify,
1563 TNWM_GETMINIMUMSIZE,
1564 (WPARAM)Horizontal,
1565 (LPARAM)&TraySize))
1566 {
1567 /* Move the tray notification window to the desired location */
1568 if (Horizontal)
1569 ptTrayNotify.x = rcClient.right - TraySize.cx;
1570 else
1571 ptTrayNotify.y = rcClient.bottom - TraySize.cy;
1572
1573 dwp = ::DeferWindowPos(dwp,
1574 m_TrayNotify,
1575 NULL,
1576 ptTrayNotify.x,
1577 ptTrayNotify.y,
1578 TraySize.cx,
1579 TraySize.cy,
1580 SWP_NOZORDER | SWP_NOACTIVATE);
1581 if (dwp == NULL)
1582 {
1583 ERR("DeferWindowPos for notification area failed. lastErr=%d\n", GetLastError());
1584 return;
1585 }
1586 }
1587
1588 /* Resize/Move the rebar control */
1589 if (m_Rebar != NULL)
1590 {
1591 POINT ptRebar = { 0, 0 };
1592 SIZE szRebar;
1593
1594 SetWindowStyle(m_Rebar, CCS_VERT, Horizontal ? 0 : CCS_VERT);
1595
1596 if (Horizontal)
1597 {
1598 ptRebar.x = StartSize.cx + GetSystemMetrics(SM_CXSIZEFRAME);
1599 szRebar.cx = ptTrayNotify.x - ptRebar.x;
1600 szRebar.cy = rcClient.bottom;
1601 }
1602 else
1603 {
1604 ptRebar.y = StartSize.cy + GetSystemMetrics(SM_CYSIZEFRAME);
1605 szRebar.cx = rcClient.right;
1606 szRebar.cy = ptTrayNotify.y - ptRebar.y;
1607 }
1608
1609 dwp = ::DeferWindowPos(dwp,
1610 m_Rebar,
1611 NULL,
1612 ptRebar.x,
1613 ptRebar.y,
1614 szRebar.cx,
1615 szRebar.cy,
1616 SWP_NOZORDER | SWP_NOACTIVATE);
1617 }
1618
1619 if (dwp != NULL)
1620 EndDeferWindowPos(dwp);
1621
1622 if (m_TaskSwitch != NULL)
1623 {
1624 /* Update the task switch window configuration */
1625 SendMessage(m_TaskSwitch, TSWM_UPDATETASKBARPOS, 0, 0);
1626 }
1627 }
1628
1629 void FitToRebar(PRECT pRect)
1630 {
1631 /* Get the rect of the rebar */
1632 RECT rebarRect, taskbarRect;
1633 ::GetWindowRect(m_Rebar, &rebarRect);
1634 ::GetWindowRect(m_hWnd, &taskbarRect);
1635 OffsetRect(&rebarRect, -taskbarRect.left, -taskbarRect.top);
1636
1637 /* Calculate the difference of size of the taskbar and the rebar */
1638 SIZE margins;
1639 margins.cx = taskbarRect.right - taskbarRect.left - rebarRect.right + rebarRect.left;
1640 margins.cy = taskbarRect.bottom - taskbarRect.top - rebarRect.bottom + rebarRect.top;
1641
1642 /* Calculate the new size of the rebar and make it resize, then change the new taskbar size */
1643 switch (m_Position)
1644 {
1645 case ABE_TOP:
1646 rebarRect.bottom = rebarRect.top + pRect->bottom - pRect->top - margins.cy;
1647 ::SendMessageW(m_Rebar, RB_SIZETORECT, RBSTR_CHANGERECT, (LPARAM)&rebarRect);
1648 pRect->bottom = pRect->top + rebarRect.bottom - rebarRect.top + margins.cy;
1649 break;
1650 case ABE_BOTTOM:
1651 rebarRect.top = rebarRect.bottom - (pRect->bottom - pRect->top - margins.cy);
1652 ::SendMessageW(m_Rebar, RB_SIZETORECT, RBSTR_CHANGERECT, (LPARAM)&rebarRect);
1653 pRect->top = pRect->bottom - (rebarRect.bottom - rebarRect.top + margins.cy);
1654 break;
1655 case ABE_LEFT:
1656 case ABE_RIGHT:
1657 /* FIXME: what to do here? */
1658 break;
1659 }
1660
1661 CalculateValidSize(m_Position, pRect);
1662 }
1663
1664 void PopupStartMenu()
1665 {
1666 if (m_StartMenuPopup != NULL)
1667 {
1668 POINTL pt;
1669 RECTL rcExclude;
1670 DWORD dwFlags = 0;
1671
1672 if (m_StartButton.GetWindowRect((RECT*) &rcExclude))
1673 {
1674 switch (m_Position)
1675 {
1676 case ABE_BOTTOM:
1677 pt.x = rcExclude.left;
1678 pt.y = rcExclude.top;
1679 dwFlags |= MPPF_TOP;
1680 break;
1681 case ABE_TOP:
1682 pt.x = rcExclude.left;
1683 pt.y = rcExclude.bottom;
1684 dwFlags |= MPPF_BOTTOM;
1685 break;
1686 case ABE_LEFT:
1687 pt.x = rcExclude.right;
1688 pt.y = rcExclude.top;
1689 dwFlags |= MPPF_RIGHT;
1690 break;
1691 case ABE_RIGHT:
1692 pt.x = rcExclude.left;
1693 pt.y = rcExclude.top;
1694 dwFlags |= MPPF_LEFT;
1695 break;
1696 }
1697
1698 m_StartMenuPopup->Popup(&pt, &rcExclude, dwFlags);
1699
1700 m_StartButton.SendMessageW(BM_SETSTATE, TRUE, 0);
1701 }
1702 }
1703 }
1704
1705 void ProcessMouseTracking()
1706 {
1707 RECT rcCurrent;
1708 POINT pt;
1709 BOOL over;
1710 UINT state = m_AutoHideState;
1711
1712 GetCursorPos(&pt);
1713 GetWindowRect(&rcCurrent);
1714 over = PtInRect(&rcCurrent, pt);
1715
1716 if (m_StartButton.SendMessage( BM_GETSTATE, 0, 0) != BST_UNCHECKED)
1717 {
1718 over = TRUE;
1719 }
1720
1721 if (over)
1722 {
1723 if (state == AUTOHIDE_HIDING)
1724 {
1725 TRACE("AutoHide cancelling hide.\n");
1726 m_AutoHideState = AUTOHIDE_SHOWING;
1727 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_INTERVAL_ANIMATING, NULL);
1728 }
1729 else if (state == AUTOHIDE_HIDDEN)
1730 {
1731 TRACE("AutoHide starting show.\n");
1732 m_AutoHideState = AUTOHIDE_SHOWING;
1733 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_DELAY_SHOW, NULL);
1734 }
1735 }
1736 else
1737 {
1738 if (state == AUTOHIDE_SHOWING)
1739 {
1740 TRACE("AutoHide cancelling show.\n");
1741 m_AutoHideState = AUTOHIDE_HIDING;
1742 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_INTERVAL_ANIMATING, NULL);
1743 }
1744 else if (state == AUTOHIDE_SHOWN)
1745 {
1746 TRACE("AutoHide starting hide.\n");
1747 m_AutoHideState = AUTOHIDE_HIDING;
1748 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_DELAY_HIDE, NULL);
1749 }
1750
1751 KillTimer(TIMER_ID_MOUSETRACK);
1752 }
1753 }
1754
1755 void ProcessAutoHide()
1756 {
1757 RECT rc = m_TrayRects[m_Position];
1758 INT w = m_TraySize.cx - GetSystemMetrics(SM_CXBORDER) * 2 - 1;
1759 INT h = m_TraySize.cy - GetSystemMetrics(SM_CYBORDER) * 2 - 1;
1760
1761 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);
1762
1763 switch (m_AutoHideState)
1764 {
1765 case AUTOHIDE_HIDING:
1766 switch (m_Position)
1767 {
1768 case ABE_LEFT:
1769 m_AutoHideOffset.cy = 0;
1770 m_AutoHideOffset.cx -= AUTOHIDE_SPEED_HIDE;
1771 if (m_AutoHideOffset.cx < -w)
1772 m_AutoHideOffset.cx = -w;
1773 break;
1774 case ABE_TOP:
1775 m_AutoHideOffset.cx = 0;
1776 m_AutoHideOffset.cy -= AUTOHIDE_SPEED_HIDE;
1777 if (m_AutoHideOffset.cy < -h)
1778 m_AutoHideOffset.cy = -h;
1779 break;
1780 case ABE_RIGHT:
1781 m_AutoHideOffset.cy = 0;
1782 m_AutoHideOffset.cx += AUTOHIDE_SPEED_HIDE;
1783 if (m_AutoHideOffset.cx > w)
1784 m_AutoHideOffset.cx = w;
1785 break;
1786 case ABE_BOTTOM:
1787 m_AutoHideOffset.cx = 0;
1788 m_AutoHideOffset.cy += AUTOHIDE_SPEED_HIDE;
1789 if (m_AutoHideOffset.cy > h)
1790 m_AutoHideOffset.cy = h;
1791 break;
1792 }
1793
1794 if (m_AutoHideOffset.cx != w && m_AutoHideOffset.cy != h)
1795 {
1796 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_INTERVAL_ANIMATING, NULL);
1797 break;
1798 }
1799
1800 /* fallthrough */
1801 case AUTOHIDE_HIDDEN:
1802
1803 switch (m_Position)
1804 {
1805 case ABE_LEFT:
1806 m_AutoHideOffset.cx = -w;
1807 m_AutoHideOffset.cy = 0;
1808 break;
1809 case ABE_TOP:
1810 m_AutoHideOffset.cx = 0;
1811 m_AutoHideOffset.cy = -h;
1812 break;
1813 case ABE_RIGHT:
1814 m_AutoHideOffset.cx = w;
1815 m_AutoHideOffset.cy = 0;
1816 break;
1817 case ABE_BOTTOM:
1818 m_AutoHideOffset.cx = 0;
1819 m_AutoHideOffset.cy = h;
1820 break;
1821 }
1822
1823 KillTimer(TIMER_ID_AUTOHIDE);
1824 m_AutoHideState = AUTOHIDE_HIDDEN;
1825 break;
1826
1827 case AUTOHIDE_SHOWING:
1828 if (m_AutoHideOffset.cx >= AUTOHIDE_SPEED_SHOW)
1829 {
1830 m_AutoHideOffset.cx -= AUTOHIDE_SPEED_SHOW;
1831 }
1832 else if (m_AutoHideOffset.cx <= -AUTOHIDE_SPEED_SHOW)
1833 {
1834 m_AutoHideOffset.cx += AUTOHIDE_SPEED_SHOW;
1835 }
1836 else
1837 {
1838 m_AutoHideOffset.cx = 0;
1839 }
1840
1841 if (m_AutoHideOffset.cy >= AUTOHIDE_SPEED_SHOW)
1842 {
1843 m_AutoHideOffset.cy -= AUTOHIDE_SPEED_SHOW;
1844 }
1845 else if (m_AutoHideOffset.cy <= -AUTOHIDE_SPEED_SHOW)
1846 {
1847 m_AutoHideOffset.cy += AUTOHIDE_SPEED_SHOW;
1848 }
1849 else
1850 {
1851 m_AutoHideOffset.cy = 0;
1852 }
1853
1854 if (m_AutoHideOffset.cx != 0 || m_AutoHideOffset.cy != 0)
1855 {
1856 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_INTERVAL_ANIMATING, NULL);
1857 break;
1858 }
1859
1860 /* fallthrough */
1861 case AUTOHIDE_SHOWN:
1862
1863 KillTimer(TIMER_ID_AUTOHIDE);
1864 m_AutoHideState = AUTOHIDE_SHOWN;
1865 break;
1866 }
1867
1868 rc.left += m_AutoHideOffset.cx;
1869 rc.right += m_AutoHideOffset.cx;
1870 rc.top += m_AutoHideOffset.cy;
1871 rc.bottom += m_AutoHideOffset.cy;
1872
1873 TRACE("AutoHide Changing position to (%d, %d, %d, %d) and state=%u.\n", rc.left, rc.top, rc.right, rc.bottom, m_AutoHideState);
1874 SetWindowPos(NULL, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER);
1875 }
1876
1877
1878
1879
1880
1881 /**********************************************************
1882 * ##### taskbar drawing #####
1883 */
1884
1885 LRESULT EraseBackgroundWithTheme(HDC hdc)
1886 {
1887 RECT rect;
1888 int iSBkgndPart[4] = {TBP_BACKGROUNDLEFT, TBP_BACKGROUNDTOP, TBP_BACKGROUNDRIGHT, TBP_BACKGROUNDBOTTOM};
1889
1890 ASSERT(m_Position <= ABE_BOTTOM);
1891
1892 if (m_Theme)
1893 {
1894 GetClientRect(&rect);
1895 DrawThemeBackground(m_Theme, hdc, iSBkgndPart[m_Position], 0, &rect, 0);
1896 }
1897
1898 return 0;
1899 }
1900
1901 int DrawSizerWithTheme(IN HRGN hRgn)
1902 {
1903 HDC hdc;
1904 RECT rect;
1905 int iSizerPart[4] = {TBP_SIZINGBARLEFT, TBP_SIZINGBARTOP, TBP_SIZINGBARRIGHT, TBP_SIZINGBARBOTTOM};
1906 SIZE size;
1907
1908 ASSERT(m_Position <= ABE_BOTTOM);
1909
1910 HRESULT hr = GetThemePartSize(m_Theme, NULL, iSizerPart[m_Position], 0, NULL, TS_TRUE, &size);
1911 if (FAILED_UNEXPECTEDLY(hr))
1912 return 0;
1913
1914 GetWindowRect(&rect);
1915 OffsetRect(&rect, -rect.left, -rect.top);
1916
1917 hdc = GetWindowDC();
1918
1919 switch (m_Position)
1920 {
1921 case ABE_LEFT:
1922 rect.left = rect.right - size.cx;
1923 break;
1924 case ABE_TOP:
1925 rect.top = rect.bottom - size.cy;
1926 break;
1927 case ABE_RIGHT:
1928 rect.right = rect.left + size.cx;
1929 break;
1930 case ABE_BOTTOM:
1931 default:
1932 rect.bottom = rect.top + size.cy;
1933 break;
1934 }
1935
1936 DrawThemeBackground(m_Theme, hdc, iSizerPart[m_Position], 0, &rect, 0);
1937
1938 ReleaseDC(hdc);
1939 return 0;
1940 }
1941
1942
1943
1944
1945
1946 /*
1947 * ITrayWindow
1948 */
1949 HRESULT STDMETHODCALLTYPE Open()
1950 {
1951 RECT rcWnd;
1952
1953 /* Check if there's already a window created and try to show it.
1954 If it was somehow destroyed just create a new tray window. */
1955 if (m_hWnd != NULL && IsWindow())
1956 {
1957 return S_OK;
1958 }
1959
1960 DWORD dwExStyle = WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE;
1961 if (AlwaysOnTop)
1962 dwExStyle |= WS_EX_TOPMOST;
1963
1964 DWORD dwStyle = WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
1965 if(!m_Theme)
1966 {
1967 dwStyle |= WS_THICKFRAME | WS_BORDER;
1968 }
1969
1970 ZeroMemory(&rcWnd, sizeof(rcWnd));
1971 if (m_Position != (DWORD) -1)
1972 rcWnd = m_TrayRects[m_Position];
1973
1974 if (!Create(NULL, rcWnd, NULL, dwStyle, dwExStyle))
1975 return E_FAIL;
1976
1977 /* Align all controls on the tray window */
1978 AlignControls(NULL);
1979
1980 /* Move the tray window to the right position and resize it if necessary */
1981 CheckTrayWndPosition();
1982
1983 return S_OK;
1984 }
1985
1986 HRESULT STDMETHODCALLTYPE Close()
1987 {
1988 if (m_hWnd != NULL)
1989 {
1990 SendMessage(m_hWnd,
1991 WM_APP_TRAYDESTROY,
1992 0,
1993 0);
1994 }
1995
1996 return S_OK;
1997 }
1998
1999 HWND STDMETHODCALLTYPE GetHWND()
2000 {
2001 return m_hWnd;
2002 }
2003
2004 BOOL STDMETHODCALLTYPE IsSpecialHWND(IN HWND hWnd)
2005 {
2006 return (m_hWnd == hWnd ||
2007 (m_DesktopWnd != NULL && m_hWnd == m_DesktopWnd));
2008 }
2009
2010 BOOL STDMETHODCALLTYPE IsHorizontal()
2011 {
2012 return IsPosHorizontal();
2013 }
2014
2015 BOOL STDMETHODCALLTYPE Lock(IN BOOL bLock)
2016 {
2017 BOOL bPrevLock = Locked;
2018
2019 if (Locked != bLock)
2020 {
2021 Locked = bLock;
2022
2023 if (m_TrayBandSite != NULL)
2024 {
2025 if (!SUCCEEDED(m_TrayBandSite->Lock(bLock)))
2026 {
2027 /* Reset?? */
2028 Locked = bPrevLock;
2029 return bPrevLock;
2030 }
2031 }
2032
2033 if (m_Theme)
2034 {
2035 /* Update cached tray sizes */
2036 for(DWORD Pos = ABE_LEFT; Pos <= ABE_BOTTOM; Pos++)
2037 {
2038 RECT rcGripper = {0};
2039 AdjustSizerRect(&rcGripper, Pos);
2040
2041 if(Locked)
2042 {
2043 m_TrayRects[Pos].top += rcGripper.top;
2044 m_TrayRects[Pos].left += rcGripper.left;
2045 m_TrayRects[Pos].bottom += rcGripper.bottom;
2046 m_TrayRects[Pos].right += rcGripper.right;
2047 }
2048 else
2049 {
2050 m_TrayRects[Pos].top -= rcGripper.top;
2051 m_TrayRects[Pos].left -= rcGripper.left;
2052 m_TrayRects[Pos].bottom -= rcGripper.bottom;
2053 m_TrayRects[Pos].right -= rcGripper.right;
2054 }
2055 }
2056 }
2057 SetWindowPos(NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER);
2058 ResizeWorkArea();
2059 ApplyClipping(TRUE);
2060 }
2061
2062 return bPrevLock;
2063 }
2064
2065
2066
2067
2068
2069
2070 /**********************************************************
2071 * ##### message handling #####
2072 */
2073
2074 LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2075 {
2076 HRESULT hRet;
2077
2078 ((ITrayWindow*)this)->AddRef();
2079
2080 SetWindowTheme(m_hWnd, L"TaskBar", NULL);
2081
2082 /* Create the Start button */
2083 m_StartButton.Create(m_hWnd);
2084
2085 /* Load the saved tray window settings */
2086 RegLoadSettings();
2087
2088 /* Create and initialize the start menu */
2089 HBITMAP hbmBanner = LoadBitmapW(hExplorerInstance, MAKEINTRESOURCEW(IDB_STARTMENU));
2090 m_StartMenuPopup = CreateStartMenu(this, &m_StartMenuBand, hbmBanner, 0);
2091
2092 /* Create the task band */
2093 hRet = CTaskBand_CreateInstance(this, m_StartButton.m_hWnd, IID_PPV_ARG(IDeskBand, &m_TaskBand));
2094 if (FAILED_UNEXPECTEDLY(hRet))
2095 return FALSE;
2096
2097 /* Create the rebar band site. This actually creates the rebar and the tasks toolbar. */
2098 hRet = CTrayBandSite_CreateInstance(this, m_TaskBand, &m_TrayBandSite);
2099 if (FAILED_UNEXPECTEDLY(hRet))
2100 return FALSE;
2101
2102 /* Get the hwnd of the rebar */
2103 hRet = IUnknown_GetWindow(m_TrayBandSite, &m_Rebar);
2104 if (FAILED_UNEXPECTEDLY(hRet))
2105 return FALSE;
2106
2107 /* Get the hwnd of the tasks toolbar */
2108 hRet = IUnknown_GetWindow(m_TaskBand, &m_TaskSwitch);
2109 if (FAILED_UNEXPECTEDLY(hRet))
2110 return FALSE;
2111
2112 SetWindowTheme(m_Rebar, L"TaskBar", NULL);
2113
2114 /* Create the tray notification window */
2115 m_TrayNotify = CreateTrayNotifyWnd(this, HideClock, &m_TrayNotifyInstance);
2116
2117 UpdateFonts();
2118
2119 InitShellServices(&m_ShellServices);
2120
2121 if (AutoHide)
2122 {
2123 m_AutoHideState = AUTOHIDE_HIDING;
2124 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_DELAY_HIDE, NULL);
2125 }
2126
2127 RegisterHotKey(m_hWnd, IDHK_RUN, MOD_WIN, 'R');
2128 RegisterHotKey(m_hWnd, IDHK_MINIMIZE_ALL, MOD_WIN, 'M');
2129 RegisterHotKey(m_hWnd, IDHK_RESTORE_ALL, MOD_WIN|MOD_SHIFT, 'M');
2130 RegisterHotKey(m_hWnd, IDHK_HELP, MOD_WIN, VK_F1);
2131 RegisterHotKey(m_hWnd, IDHK_EXPLORE, MOD_WIN, 'E');
2132 RegisterHotKey(m_hWnd, IDHK_FIND, MOD_WIN, 'F');
2133 RegisterHotKey(m_hWnd, IDHK_FIND_COMPUTER, MOD_WIN|MOD_CONTROL, 'F');
2134 RegisterHotKey(m_hWnd, IDHK_NEXT_TASK, MOD_WIN, VK_TAB);
2135 RegisterHotKey(m_hWnd, IDHK_PREV_TASK, MOD_WIN|MOD_SHIFT, VK_TAB);
2136 RegisterHotKey(m_hWnd, IDHK_SYS_PROPERTIES, MOD_WIN, VK_PAUSE);
2137 RegisterHotKey(m_hWnd, IDHK_DESKTOP, MOD_WIN, 'D');
2138 RegisterHotKey(m_hWnd, IDHK_PAGER, MOD_WIN, 'B');
2139
2140 return TRUE;
2141 }
2142
2143 LRESULT OnThemeChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2144 {
2145 if (m_Theme)
2146 CloseThemeData(m_Theme);
2147
2148 m_Theme = OpenThemeData(m_hWnd, L"TaskBar");
2149
2150 if (m_Theme)
2151 {
2152 SetWindowStyle(m_hWnd, WS_THICKFRAME | WS_BORDER, 0);
2153 }
2154 else
2155 {
2156 SetWindowStyle(m_hWnd, WS_THICKFRAME | WS_BORDER, WS_THICKFRAME | WS_BORDER);
2157 }
2158 SetWindowPos(NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
2159
2160 return TRUE;
2161 }
2162
2163 LRESULT OnSettingChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2164 {
2165 if (wParam == SPI_SETNONCLIENTMETRICS)
2166 {
2167 SendMessage(m_TaskSwitch, uMsg, wParam, lParam);
2168 UpdateFonts();
2169 AlignControls(NULL);
2170 CheckTrayWndPosition();
2171 }
2172
2173 return 0;
2174 }
2175
2176 LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2177 {
2178 HDC hdc = (HDC) wParam;
2179
2180 if (!m_Theme)
2181 {
2182 bHandled = FALSE;
2183 return 0;
2184 }
2185
2186 return EraseBackgroundWithTheme(hdc);
2187 }
2188
2189 LRESULT OnDisplayChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2190 {
2191 /* Move the tray window to the right position and resize it if necessary */
2192 CheckTrayWndPosition();
2193
2194 return TRUE;
2195 }
2196
2197 LRESULT OnCopyData(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2198 {
2199 if (m_TrayNotify)
2200 {
2201 TRACE("WM_COPYDATA notify message received. Handling...\n");
2202 return TrayNotify_NotifyIconCmd(m_TrayNotifyInstance, wParam, lParam);
2203 }
2204 return TRUE;
2205 }
2206
2207 LRESULT OnNcPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2208 {
2209 if (!m_Theme)
2210 {
2211 bHandled = FALSE;
2212 return 0;
2213 }
2214
2215 return DrawSizerWithTheme((HRGN) wParam);
2216 }
2217
2218 LRESULT OnCtlColorBtn(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2219 {
2220 SetBkMode((HDC) wParam, TRANSPARENT);
2221 return (LRESULT) GetStockObject(HOLLOW_BRUSH);
2222 }
2223
2224 LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2225 {
2226 RECT rcClient;
2227 POINT pt;
2228
2229 if (Locked)
2230 {
2231 /* The user may not be able to resize the tray window.
2232 Pretend like the window is not sizeable when the user
2233 clicks on the border. */
2234 return HTBORDER;
2235 }
2236
2237 SetLastError(ERROR_SUCCESS);
2238 if (GetClientRect(&rcClient) &&
2239 (MapWindowPoints(NULL, (LPPOINT) &rcClient, 2) != 0 || GetLastError() == ERROR_SUCCESS))
2240 {
2241 pt.x = (SHORT) LOWORD(lParam);
2242 pt.y = (SHORT) HIWORD(lParam);
2243
2244 if (PtInRect(&rcClient,
2245 pt))
2246 {
2247 /* The user is trying to drag the tray window */
2248 return HTCAPTION;
2249 }
2250
2251 /* Depending on the position of the tray window, allow only
2252 changing the border next to the monitor working area */
2253 switch (m_Position)
2254 {
2255 case ABE_TOP:
2256 if (pt.y > rcClient.bottom)
2257 return HTBOTTOM;
2258 break;
2259 case ABE_LEFT:
2260 if (pt.x > rcClient.right)
2261 return HTRIGHT;
2262 break;
2263 case ABE_RIGHT:
2264 if (pt.x < rcClient.left)
2265 return HTLEFT;
2266 break;
2267 case ABE_BOTTOM:
2268 default:
2269 if (pt.y < rcClient.top)
2270 return HTTOP;
2271 break;
2272 }
2273 }
2274 return HTBORDER;
2275 return TRUE;
2276 }
2277
2278 LRESULT OnMoving(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2279 {
2280 POINT ptCursor;
2281 PRECT pRect = (PRECT) lParam;
2282
2283 /* We need to ensure that an application can not accidently
2284 move the tray window (using SetWindowPos). However, we still
2285 need to be able to move the window in case the user wants to
2286 drag the tray window to another position or in case the user
2287 wants to resize the tray window. */
2288 if (!Locked && GetCursorPos(&ptCursor))
2289 {
2290 IsDragging = TRUE;
2291 m_DraggingPosition = GetDraggingRectFromPt(ptCursor, pRect, &m_DraggingMonitor);
2292 }
2293 else
2294 {
2295 *pRect = m_TrayRects[m_Position];
2296
2297 if (AutoHide)
2298 {
2299 pRect->left += m_AutoHideOffset.cx;
2300 pRect->right += m_AutoHideOffset.cx;
2301 pRect->top += m_AutoHideOffset.cy;
2302 pRect->bottom += m_AutoHideOffset.cy;
2303 }
2304 }
2305 return TRUE;
2306 }
2307
2308 LRESULT OnSizing(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2309 {
2310 PRECT pRect = (PRECT) lParam;
2311
2312 if (!Locked)
2313 {
2314 FitToRebar(pRect);
2315 }
2316 else
2317 {
2318 *pRect = m_TrayRects[m_Position];
2319
2320 if (AutoHide)
2321 {
2322 pRect->left += m_AutoHideOffset.cx;
2323 pRect->right += m_AutoHideOffset.cx;
2324 pRect->top += m_AutoHideOffset.cy;
2325 pRect->bottom += m_AutoHideOffset.cy;
2326 }
2327 }
2328 return TRUE;
2329 }
2330
2331 LRESULT OnWindowPosChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2332 {
2333 ChangingWinPos((LPWINDOWPOS) lParam);
2334 return TRUE;
2335 }
2336
2337 LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2338 {
2339 RECT rcClient;
2340 if (wParam == SIZE_RESTORED && lParam == 0)
2341 {
2342 ResizeWorkArea();
2343 /* Clip the tray window on multi monitor systems so the edges can't
2344 overlap into another monitor */
2345 ApplyClipping(TRUE);
2346
2347 if (!GetClientRect(&rcClient))
2348 {
2349 return FALSE;
2350 }
2351 }
2352 else
2353 {
2354 rcClient.left = rcClient.top = 0;
2355 rcClient.right = LOWORD(lParam);
2356 rcClient.bottom = HIWORD(lParam);
2357 }
2358
2359 AlignControls(&rcClient);
2360 return TRUE;
2361 }
2362
2363 LRESULT OnEnterSizeMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2364 {
2365 InSizeMove = TRUE;
2366 IsDragging = FALSE;
2367 if (!Locked)
2368 {
2369 /* Remove the clipping on multi monitor systems while dragging around */
2370 ApplyClipping(FALSE);
2371 }
2372 return TRUE;
2373 }
2374
2375 LRESULT OnExitSizeMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2376 {
2377 InSizeMove = FALSE;
2378 if (!Locked)
2379 {
2380 /* Apply clipping */
2381 PostMessage(WM_SIZE, SIZE_RESTORED, 0);
2382 }
2383 return TRUE;
2384 }
2385
2386 LRESULT OnSysChar(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2387 {
2388 switch (wParam)
2389 {
2390 case TEXT(' '):
2391 {
2392 /* The user pressed Alt+Space, this usually brings up the system menu of a window.
2393 The tray window needs to handle this specially, since it normally doesn't have
2394 a system menu. */
2395
2396 static const UINT uidDisableItem [] = {
2397 SC_RESTORE,
2398 SC_MOVE,
2399 SC_SIZE,
2400 SC_MAXIMIZE,
2401 SC_MINIMIZE,
2402 };
2403 HMENU hSysMenu;
2404 UINT i, uId;
2405
2406 /* temporarily enable the system menu */
2407 SetWindowStyle(m_hWnd, WS_SYSMENU, WS_SYSMENU);
2408
2409 hSysMenu = GetSystemMenu(FALSE);
2410 if (hSysMenu != NULL)
2411 {
2412 /* Disable all items that are not relevant */
2413 for (i = 0; i < _countof(uidDisableItem); i++)
2414 {
2415 EnableMenuItem(hSysMenu,
2416 uidDisableItem[i],
2417 MF_BYCOMMAND | MF_GRAYED);
2418 }
2419
2420 EnableMenuItem(hSysMenu,
2421 SC_CLOSE,
2422 MF_BYCOMMAND |
2423 (SHRestricted(REST_NOCLOSE) ? MF_GRAYED : MF_ENABLED));
2424
2425 /* Display the system menu */
2426 uId = TrackMenu(
2427 hSysMenu,
2428 NULL,
2429 m_StartButton.m_hWnd,
2430 m_Position != ABE_TOP,
2431 FALSE);
2432 if (uId != 0)
2433 {
2434 SendMessage(m_hWnd, WM_SYSCOMMAND, (WPARAM) uId, 0);
2435 }
2436 }
2437
2438 /* revert the system menu window style */
2439 SetWindowStyle(m_hWnd, WS_SYSMENU, 0);
2440 break;
2441 }
2442
2443 default:
2444 bHandled = FALSE;
2445 }
2446 return TRUE;
2447 }
2448
2449 LRESULT OnNcRButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2450 {
2451 /* We want the user to be able to get a context menu even on the nonclient
2452 area (including the sizing border)! */
2453 uMsg = WM_CONTEXTMENU;
2454 wParam = (WPARAM) m_hWnd;
2455
2456 return OnContextMenu(uMsg, wParam, lParam, bHandled);
2457 }
2458
2459 LRESULT OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2460 {
2461 LRESULT Ret = FALSE;
2462 POINT pt, *ppt = NULL;
2463 HWND hWndExclude = NULL;
2464
2465 /* Check if the administrator has forbidden access to context menus */
2466 if (SHRestricted(REST_NOTRAYCONTEXTMENU))
2467 return FALSE;
2468
2469 pt.x = (SHORT) LOWORD(lParam);
2470 pt.y = (SHORT) HIWORD(lParam);
2471
2472 if (pt.x != -1 || pt.y != -1)
2473 ppt = &pt;
2474 else
2475 hWndExclude = m_StartButton.m_hWnd;
2476
2477 if ((HWND) wParam == m_StartButton.m_hWnd)
2478 {
2479 /* Make sure we can't track the context menu if the start
2480 menu is currently being shown */
2481 if (!(m_StartButton.SendMessage(BM_GETSTATE, 0, 0) & BST_PUSHED))
2482 {
2483 CComPtr<IContextMenu> ctxMenu;
2484 StartMenuBtnCtxMenuCreator(this, m_hWnd, &ctxMenu);
2485 TrackCtxMenu(ctxMenu, ppt, hWndExclude, m_Position == ABE_BOTTOM, this);
2486 }
2487 }
2488 else
2489 {
2490 /* See if the context menu should be handled by the task band site */
2491 if (ppt != NULL && m_TrayBandSite != NULL)
2492 {
2493 HWND hWndAtPt;
2494 POINT ptClient = *ppt;
2495
2496 /* Convert the coordinates to client-coordinates */
2497 ::MapWindowPoints(NULL, m_hWnd, &ptClient, 1);
2498
2499 hWndAtPt = ChildWindowFromPoint(ptClient);
2500 if (hWndAtPt != NULL &&
2501 (hWndAtPt == m_Rebar || ::IsChild(m_Rebar, hWndAtPt)))
2502 {
2503 /* Check if the user clicked on the task switch window */
2504 ptClient = *ppt;
2505 ::MapWindowPoints(NULL, m_Rebar, &ptClient, 1);
2506
2507 hWndAtPt = ::ChildWindowFromPointEx(m_Rebar, ptClient, CWP_SKIPINVISIBLE | CWP_SKIPDISABLED);
2508 if (hWndAtPt == m_TaskSwitch)
2509 goto HandleTrayContextMenu;
2510
2511 /* Forward the message to the task band site */
2512 m_TrayBandSite->ProcessMessage(m_hWnd, uMsg, wParam, lParam, &Ret);
2513 }
2514 else
2515 goto HandleTrayContextMenu;
2516 }
2517 else
2518 {
2519 HandleTrayContextMenu:
2520 /* Tray the default tray window context menu */
2521 CComPtr<IContextMenu> ctxMenu;
2522 TrayWindowCtxMenuCreator(this, m_hWnd, &ctxMenu);
2523 TrackCtxMenu(ctxMenu, ppt, NULL, FALSE, this);
2524 }
2525 }
2526 return Ret;
2527 }
2528
2529 LRESULT OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2530 {
2531 LRESULT Ret = FALSE;
2532 /* FIXME: We can't check with IsChild whether the hwnd is somewhere inside
2533 the rebar control! But we shouldn't forward messages that the band
2534 site doesn't handle, such as other controls (start button, tray window */
2535
2536 HRESULT hr = E_FAIL;
2537
2538 if (m_TrayBandSite)
2539 {
2540 hr = m_TrayBandSite->ProcessMessage(m_hWnd, uMsg, wParam, lParam, &Ret);
2541 if (SUCCEEDED(hr))
2542 return Ret;
2543 }
2544
2545 if (m_TrayBandSite == NULL || FAILED(hr))
2546 {
2547 const NMHDR *nmh = (const NMHDR *) lParam;
2548
2549 if (nmh->hwndFrom == m_TrayNotify)
2550 {
2551 switch (nmh->code)
2552 {
2553 case NTNWM_REALIGN:
2554 /* Cause all controls to be aligned */
2555 PostMessage(WM_SIZE, SIZE_RESTORED, 0);
2556 break;
2557 }
2558 }
2559 }
2560 return Ret;
2561 }
2562
2563 LRESULT OnNcLButtonDblClick(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2564 {
2565 /* We "handle" this message so users can't cause a weird maximize/restore
2566 window animation when double-clicking the tray window! */
2567
2568 /* We should forward mouse messages to child windows here.
2569 Right now, this is only clock double-click */
2570 RECT rcClock;
2571 if (TrayNotify_GetClockRect(m_TrayNotifyInstance, &rcClock))
2572 {
2573 POINT ptClick;
2574 ptClick.x = MAKEPOINTS(lParam).x;
2575 ptClick.y = MAKEPOINTS(lParam).y;
2576 if (PtInRect(&rcClock, ptClick))
2577 {
2578 //FIXME: use SHRunControlPanel
2579 ShellExecuteW(m_hWnd, NULL, L"timedate.cpl", NULL, NULL, SW_NORMAL);
2580 }
2581 }
2582 return TRUE;
2583 }
2584
2585 LRESULT OnAppTrayDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2586 {
2587 DestroyWindow();
2588 return TRUE;
2589 }
2590
2591 LRESULT OnOpenStartMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2592 {
2593 HWND hwndStartMenu;
2594 HRESULT hr = IUnknown_GetWindow(m_StartMenuPopup, &hwndStartMenu);
2595 if (FAILED_UNEXPECTEDLY(hr))
2596 return FALSE;
2597
2598 if (::IsWindowVisible(hwndStartMenu))
2599 {
2600 m_StartMenuPopup->OnSelect(MPOS_CANCELLEVEL);
2601 }
2602 else
2603 {
2604 PopupStartMenu();
2605 }
2606
2607 return TRUE;
2608 }
2609
2610 LRESULT OnDoExitWindows(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2611 {
2612 /*
2613 * TWM_DOEXITWINDOWS is send by the CDesktopBrowser to us
2614 * to show the shutdown dialog. Also a WM_CLOSE message sent
2615 * by apps should show the dialog.
2616 */
2617 return DoExitWindows();
2618 }
2619
2620 LRESULT OnSysCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2621 {
2622 if (wParam == SC_CLOSE)
2623 {
2624 return DoExitWindows();
2625 }
2626
2627 bHandled = FALSE;
2628 return TRUE;
2629 }
2630
2631 LRESULT OnHotkey(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2632 {
2633 return HandleHotKey(wParam);
2634 }
2635
2636 LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2637 {
2638 LRESULT Ret = FALSE;
2639
2640 if ((HWND) lParam == m_StartButton.m_hWnd)
2641 {
2642 PopupStartMenu();
2643 return FALSE;
2644 }
2645
2646 if (m_TrayBandSite == NULL || FAILED_UNEXPECTEDLY(m_TrayBandSite->ProcessMessage(m_hWnd, uMsg, wParam, lParam, &Ret)))
2647 {
2648 return HandleCommand(LOWORD(wParam));
2649 }
2650 return Ret;
2651 }
2652
2653 LRESULT OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2654 {
2655 if (AutoHide)
2656 {
2657 SetTimer(TIMER_ID_MOUSETRACK, MOUSETRACK_INTERVAL, NULL);
2658 }
2659
2660 return TRUE;
2661 }
2662
2663 LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2664 {
2665 if (wParam == TIMER_ID_MOUSETRACK)
2666 {
2667 ProcessMouseTracking();
2668 }
2669 else if (wParam == TIMER_ID_AUTOHIDE)
2670 {
2671 ProcessAutoHide();
2672 }
2673
2674 bHandled = FALSE;
2675 return TRUE;
2676 }
2677
2678 LRESULT OnNcCalcSize(INT code, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2679 {
2680 RECT *rc = NULL;
2681 /* Ignore WM_NCCALCSIZE if we are not themed or locked */
2682 if(!m_Theme || Locked)
2683 {
2684 bHandled = FALSE;
2685 return 0;
2686 }
2687 if(!wParam)
2688 {
2689 rc = (RECT*)wParam;
2690 }
2691 else
2692 {
2693 NCCALCSIZE_PARAMS *prms = (NCCALCSIZE_PARAMS*)lParam;
2694 if(prms->lppos->flags & SWP_NOSENDCHANGING)
2695 {
2696 bHandled = FALSE;
2697 return 0;
2698 }
2699 rc = &prms->rgrc[0];
2700 }
2701
2702 AdjustSizerRect(rc, m_Position);
2703
2704 return 0;
2705 }
2706
2707 LRESULT OnRebarAutoSize(INT code, LPNMHDR nmhdr, BOOL& bHandled)
2708 {
2709 #if 0
2710 LPNMRBAUTOSIZE as = (LPNMRBAUTOSIZE) nmhdr;
2711
2712 if (!as->fChanged)
2713 return 0;
2714
2715 RECT rc;
2716 ::GetWindowRect(m_hWnd, &rc);
2717
2718 SIZE szWindow = {
2719 rc.right - rc.left,
2720 rc.bottom - rc.top };
2721 SIZE szTarget = {
2722 as->rcTarget.right - as->rcTarget.left,
2723 as->rcTarget.bottom - as->rcTarget.top };
2724 SIZE szActual = {
2725 as->rcActual.right - as->rcActual.left,
2726 as->rcActual.bottom - as->rcActual.top };
2727
2728 SIZE borders = {
2729 szWindow.cx - szTarget.cx,
2730 szWindow.cy - szTarget.cx,
2731 };
2732
2733 switch (m_Position)
2734 {
2735 case ABE_LEFT:
2736 szWindow.cx = szActual.cx + borders.cx;
2737 break;
2738 case ABE_TOP:
2739 szWindow.cy = szActual.cy + borders.cy;
2740 break;
2741 case ABE_RIGHT:
2742 szWindow.cx = szActual.cx + borders.cx;
2743 rc.left = rc.right - szWindow.cy;
2744 break;
2745 case ABE_BOTTOM:
2746 szWindow.cy = szActual.cy + borders.cy;
2747 rc.top = rc.bottom - szWindow.cy;
2748 break;
2749 }
2750
2751 SetWindowPos(NULL, rc.left, rc.top, szWindow.cx, szWindow.cy, SWP_NOACTIVATE | SWP_NOZORDER);
2752 #else
2753 bHandled = FALSE;
2754 #endif
2755 return 0;
2756 }
2757
2758 DECLARE_WND_CLASS_EX(szTrayWndClass, CS_DBLCLKS, COLOR_3DFACE)
2759
2760 BEGIN_MSG_MAP(CTrayWindow)
2761 if (m_StartMenuBand != NULL)
2762 {
2763 MSG Msg;
2764 LRESULT lRet;
2765
2766 Msg.hwnd = m_hWnd;
2767 Msg.message = uMsg;
2768 Msg.wParam = wParam;
2769 Msg.lParam = lParam;
2770
2771 if (m_StartMenuBand->TranslateMenuMessage(&Msg, &lRet) == S_OK)
2772 {
2773 return lRet;
2774 }
2775
2776 wParam = Msg.wParam;
2777 lParam = Msg.lParam;
2778 }
2779 MESSAGE_HANDLER(WM_THEMECHANGED, OnThemeChanged)
2780 MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChanged)
2781 NOTIFY_CODE_HANDLER(RBN_AUTOSIZE, OnRebarAutoSize) // Doesn't quite work ;P
2782 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
2783 MESSAGE_HANDLER(WM_SIZE, OnSize)
2784 MESSAGE_HANDLER(WM_CREATE, OnCreate)
2785 /*MESSAGE_HANDLER(WM_DESTROY, OnDestroy)*/
2786 MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTest)
2787 MESSAGE_HANDLER(WM_COMMAND, OnCommand)
2788 MESSAGE_HANDLER(WM_SYSCOMMAND, OnSysCommand)
2789 MESSAGE_HANDLER(WM_NOTIFY, OnNotify)
2790 MESSAGE_HANDLER(WM_CONTEXTMENU, OnContextMenu)
2791 MESSAGE_HANDLER(WM_TIMER, OnTimer)
2792 MESSAGE_HANDLER(WM_DISPLAYCHANGE, OnDisplayChange)
2793 MESSAGE_HANDLER(WM_COPYDATA, OnCopyData)
2794 MESSAGE_HANDLER(WM_NCPAINT, OnNcPaint)
2795 MESSAGE_HANDLER(WM_CTLCOLORBTN, OnCtlColorBtn)
2796 MESSAGE_HANDLER(WM_MOVING, OnMoving)
2797 MESSAGE_HANDLER(WM_SIZING, OnSizing)
2798 MESSAGE_HANDLER(WM_WINDOWPOSCHANGING, OnWindowPosChange)
2799 MESSAGE_HANDLER(WM_ENTERSIZEMOVE, OnEnterSizeMove)
2800 MESSAGE_HANDLER(WM_EXITSIZEMOVE, OnExitSizeMove)
2801 MESSAGE_HANDLER(WM_SYSCHAR, OnSysChar)
2802 MESSAGE_HANDLER(WM_NCRBUTTONUP, OnNcRButtonUp)
2803 MESSAGE_HANDLER(WM_NCLBUTTONDBLCLK, OnNcLButtonDblClick)
2804 MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
2805 MESSAGE_HANDLER(WM_NCMOUSEMOVE, OnMouseMove)
2806 MESSAGE_HANDLER(WM_APP_TRAYDESTROY, OnAppTrayDestroy)
2807 MESSAGE_HANDLER(TWM_OPENSTARTMENU, OnOpenStartMenu)
2808 MESSAGE_HANDLER(TWM_DOEXITWINDOWS, OnDoExitWindows)
2809 MESSAGE_HANDLER(WM_CLOSE, OnDoExitWindows)
2810 MESSAGE_HANDLER(WM_HOTKEY, OnHotkey)
2811 MESSAGE_HANDLER(WM_NCCALCSIZE, OnNcCalcSize)
2812 ALT_MSG_MAP(1)
2813 END_MSG_MAP()
2814
2815 /*****************************************************************************/
2816
2817 VOID TrayProcessMessages()
2818 {
2819 MSG Msg;
2820
2821 /* FIXME: We should keep a reference here... */
2822
2823 while (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE))
2824 {
2825 if (Msg.message == WM_QUIT)
2826 break;
2827
2828 if (m_StartMenuBand == NULL ||
2829 m_StartMenuBand->IsMenuMessage(&Msg) != S_OK)
2830 {
2831 TranslateMessage(&Msg);
2832 DispatchMessage(&Msg);
2833 }
2834 }
2835 }
2836
2837 VOID TrayMessageLoop()
2838 {
2839 MSG Msg;
2840 BOOL Ret;
2841
2842 /* FIXME: We should keep a reference here... */
2843
2844 while (true)
2845 {
2846 Ret = GetMessage(&Msg, NULL, 0, 0);
2847
2848 if (!Ret || Ret == -1)
2849 break;
2850
2851 if (m_StartMenuBand == NULL ||
2852 m_StartMenuBand->IsMenuMessage(&Msg) != S_OK)
2853 {
2854 TranslateMessage(&Msg);
2855 DispatchMessage(&Msg);
2856 }
2857 }
2858 }
2859
2860 /*
2861 * IShellDesktopTray
2862 *
2863 * NOTE: this is a very windows-specific COM interface used by SHCreateDesktop()!
2864 * These are the calls I observed, it may be wrong/incomplete/buggy!!!
2865 * The reason we implement it is because we have to use SHCreateDesktop() so
2866 * that the shell provides the desktop window and all the features that come
2867 * with it (especially positioning of desktop icons)
2868 */
2869
2870 virtual ULONG STDMETHODCALLTYPE GetState()
2871 {
2872 /* FIXME: Return ABS_ flags? */
2873 TRACE("IShellDesktopTray::GetState() unimplemented!\n");
2874 return 0;
2875 }
2876
2877 virtual HRESULT STDMETHODCALLTYPE GetTrayWindow(OUT HWND *phWndTray)
2878 {
2879 TRACE("IShellDesktopTray::GetTrayWindow(0x%p)\n", phWndTray);
2880 *phWndTray = m_hWnd;
2881 return S_OK;
2882 }
2883
2884 virtual HRESULT STDMETHODCALLTYPE RegisterDesktopWindow(IN HWND hWndDesktop)
2885 {
2886 TRACE("IShellDesktopTray::RegisterDesktopWindow(0x%p)\n", hWndDesktop);
2887
2888 m_DesktopWnd = hWndDesktop;
2889 return S_OK;
2890 }
2891
2892 virtual HRESULT STDMETHODCALLTYPE Unknown(IN DWORD dwUnknown1, IN DWORD dwUnknown2)
2893 {
2894 TRACE("IShellDesktopTray::Unknown(%u,%u) unimplemented!\n", dwUnknown1, dwUnknown2);
2895 return S_OK;
2896 }
2897
2898 virtual HRESULT RaiseStartButton()
2899 {
2900 m_StartButton.SendMessageW(BM_SETSTATE, FALSE, 0);
2901 return S_OK;
2902 }
2903
2904 HRESULT WINAPI GetWindow(HWND* phwnd)
2905 {
2906 if (!phwnd)
2907 return E_INVALIDARG;
2908 *phwnd = m_hWnd;
2909 return S_OK;
2910 }
2911
2912 HRESULT WINAPI ContextSensitiveHelp(BOOL fEnterMode)
2913 {
2914 return E_NOTIMPL;
2915 }
2916
2917 void _Init()
2918 {
2919 m_Position = (DWORD) -1;
2920 }
2921
2922 DECLARE_NOT_AGGREGATABLE(CTrayWindow)
2923
2924 DECLARE_PROTECT_FINAL_CONSTRUCT()
2925 BEGIN_COM_MAP(CTrayWindow)
2926 /*COM_INTERFACE_ENTRY_IID(IID_ITrayWindow, ITrayWindow)*/
2927 COM_INTERFACE_ENTRY_IID(IID_IShellDesktopTray, IShellDesktopTray)
2928 COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow)
2929 END_COM_MAP()
2930 };
2931
2932 class CTrayWindowCtxMenu :
2933 public CComCoClass<CTrayWindowCtxMenu>,
2934 public CComObjectRootEx<CComMultiThreadModelNoCS>,
2935 public IContextMenu
2936 {
2937 HWND hWndOwner;
2938 CComPtr<CTrayWindow> TrayWnd;
2939 CComPtr<IContextMenu> pcm;
2940
2941 public:
2942 HRESULT Initialize(ITrayWindow * pTrayWnd, IN HWND hWndOwner)
2943 {
2944 this->TrayWnd = (CTrayWindow *) pTrayWnd;
2945 this->hWndOwner = hWndOwner;
2946 return S_OK;
2947 }
2948
2949 virtual HRESULT STDMETHODCALLTYPE
2950 QueryContextMenu(HMENU hPopup,
2951 UINT indexMenu,
2952 UINT idCmdFirst,
2953 UINT idCmdLast,
2954 UINT uFlags)
2955 {
2956 HMENU menubase = LoadPopupMenu(hExplorerInstance, MAKEINTRESOURCEW(IDM_TRAYWND));
2957
2958 if (!menubase)
2959 return HRESULT_FROM_WIN32(GetLastError());
2960
2961 int count = ::GetMenuItemCount(menubase);
2962
2963 for (int i = 0; i < count; i++)
2964 {
2965 WCHAR label[128];
2966
2967 MENUITEMINFOW mii = { 0 };
2968 mii.cbSize = sizeof(mii);
2969 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_SUBMENU | MIIM_CHECKMARKS
2970 | MIIM_DATA | MIIM_STRING | MIIM_BITMAP | MIIM_FTYPE;
2971 mii.dwTypeData = label;
2972 mii.cch = _countof(label);
2973 ::GetMenuItemInfoW(menubase, i, TRUE, &mii);
2974
2975 TRACE("Adding item %d label %S type %d\n", mii.wID, mii.dwTypeData, mii.fType);
2976
2977 ::InsertMenuItemW(hPopup, i + 1, TRUE, &mii);
2978 }
2979
2980 ::DestroyMenu(menubase);
2981
2982 if (SHRestricted(REST_CLASSICSHELL) != 0)
2983 {
2984 DeleteMenu(hPopup,
2985 ID_LOCKTASKBAR,
2986 MF_BYCOMMAND);
2987 }
2988
2989 CheckMenuItem(hPopup,
2990 ID_LOCKTASKBAR,
2991 MF_BYCOMMAND | (TrayWnd->Locked ? MF_CHECKED : MF_UNCHECKED));
2992
2993 if (TrayWnd->m_TrayBandSite != NULL)
2994 {
2995 if (FAILED(TrayWnd->m_TrayBandSite->AddContextMenus(
2996 hPopup,
2997 0,
2998 ID_SHELL_CMD_FIRST,
2999 ID_SHELL_CMD_LAST,
3000 CMF_NORMAL,
3001 &pcm)))
3002 {
3003 WARN("AddContextMenus failed.\n");
3004 pcm = NULL;
3005 }
3006 }
3007
3008 return S_OK;
3009 }
3010
3011 virtual HRESULT STDMETHODCALLTYPE
3012 InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
3013 {
3014 UINT uiCmdId = (UINT) lpici->lpVerb;
3015 if (uiCmdId != 0)
3016 {
3017 if (uiCmdId >= ID_SHELL_CMD_FIRST && uiCmdId <= ID_SHELL_CMD_LAST)
3018 {
3019 CMINVOKECOMMANDINFO cmici = { 0 };
3020
3021 if (pcm != NULL)
3022 {
3023 /* Setup and invoke the shell command */
3024 cmici.cbSize = sizeof(cmici);
3025 cmici.hwnd = hWndOwner;
3026 cmici.lpVerb = (LPCSTR) MAKEINTRESOURCEW(uiCmdId - ID_SHELL_CMD_FIRST);
3027 cmici.nShow = SW_NORMAL;
3028
3029 pcm->InvokeCommand(&cmici);
3030 }
3031 }
3032 else
3033 {
3034 TrayWnd->ExecContextMenuCmd(uiCmdId);
3035 }
3036 }
3037
3038 return S_OK;
3039 }
3040
3041 virtual HRESULT STDMETHODCALLTYPE
3042 GetCommandString(UINT_PTR idCmd,
3043 UINT uType,
3044 UINT *pwReserved,
3045 LPSTR pszName,
3046 UINT cchMax)
3047 {
3048 return E_NOTIMPL;
3049 }
3050
3051 CTrayWindowCtxMenu()
3052 {
3053 }
3054
3055 virtual ~CTrayWindowCtxMenu()
3056 {
3057 }
3058
3059 BEGIN_COM_MAP(CTrayWindowCtxMenu)
3060 COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu)
3061 END_COM_MAP()
3062 };
3063
3064 HRESULT TrayWindowCtxMenuCreator(ITrayWindow * TrayWnd, IN HWND hWndOwner, IContextMenu ** ppCtxMenu)
3065 {
3066 CTrayWindowCtxMenu * mnu = new CComObject<CTrayWindowCtxMenu>();
3067 mnu->Initialize(TrayWnd, hWndOwner);
3068 *ppCtxMenu = mnu;
3069 return S_OK;
3070 }
3071
3072 HRESULT CreateTrayWindow(ITrayWindow ** ppTray)
3073 {
3074 CComPtr<CTrayWindow> Tray = new CComObject<CTrayWindow>();
3075 if (Tray == NULL)
3076 return E_OUTOFMEMORY;
3077
3078 Tray->_Init();
3079 Tray->Open();
3080
3081 *ppTray = (ITrayWindow *) Tray;
3082
3083 return S_OK;
3084 }
3085
3086 HRESULT
3087 Tray_OnStartMenuDismissed(ITrayWindow* Tray)
3088 {
3089 CTrayWindow * TrayWindow = static_cast<CTrayWindow *>(Tray);
3090 return TrayWindow->RaiseStartButton();
3091 }
3092
3093 VOID TrayProcessMessages(ITrayWindow *Tray)
3094 {
3095 CTrayWindow * TrayWindow = static_cast<CTrayWindow *>(Tray);
3096 TrayWindow->TrayProcessMessages();
3097 }
3098
3099 VOID TrayMessageLoop(ITrayWindow *Tray)
3100 {
3101 CTrayWindow * TrayWindow = static_cast<CTrayWindow *>(Tray);
3102 TrayWindow->TrayMessageLoop();
3103 }