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