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