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