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