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