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