4 * Copyright 2006 - 2007 Thomas Weidenmueller <w3seek@reactos.org>
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.
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.
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
23 extern HRESULT
InitShellServices(HDPA
* phdpa
);
24 extern HRESULT
ShutdownShellServices(HDPA hdpa
);
26 static const TRAYWINDOW_CTXMENU TrayWindowCtxMenu
;
28 #define WM_APP_TRAYDESTROY (WM_APP + 0x100)
30 #define TIMER_ID_AUTOHIDE 1
31 #define TIMER_ID_MOUSETRACK 2
32 #define MOUSETRACK_INTERVAL 100
33 #define AUTOHIDE_DELAY_HIDE 2000
34 #define AUTOHIDE_DELAY_SHOW 50
35 #define AUTOHIDE_INTERVAL_ANIMATING 10
37 #define AUTOHIDE_SPEED_SHOW 10
38 #define AUTOHIDE_SPEED_HIDE 1
40 #define AUTOHIDE_HIDDEN 0
41 #define AUTOHIDE_SHOWING 1
42 #define AUTOHIDE_SHOWN 2
43 #define AUTOHIDE_HIDING 3
45 static LONG TrayWndCount
= 0;
47 static const TCHAR szTrayWndClass
[] = TEXT("Shell_TrayWnd");
49 static const ITrayWindowVtbl ITrayWindowImpl_Vtbl
;
50 static const IShellDesktopTrayVtbl IShellDesktopTrayImpl_Vtbl
;
56 const GUID IID_IShellDesktopTray
= {0x213e2df9, 0x9a14, 0x4328, {0x99, 0xb1, 0x69, 0x61, 0xf9, 0x14, 0x3c, 0xe9}};
60 const ITrayWindowVtbl
*lpVtbl
;
61 const IShellDesktopTrayVtbl
*lpVtblShellDesktopTray
;
69 HIMAGELIST himlStartBtn
;
74 ITrayBandSite
*TrayBandSite
;
81 HMONITOR PreviousMonitor
;
82 DWORD DraggingPosition
;
83 HMONITOR DraggingMonitor
;
94 DWORD AlwaysOnTop
: 1;
95 DWORD SmSmallIcons
: 1;
100 DWORD InSizeMove
: 1;
101 DWORD IsDragging
: 1;
102 DWORD NewPosSize
: 1;
106 NONCLIENTMETRICS ncm
;
109 IMenuBand
*StartMenuBand
;
110 IMenuPopup
*StartMenuPopup
;
111 HBITMAP hbmStartMenu
;
113 HWND hwndTrayPropertiesOwner
;
114 HWND hwndRunFileDlgOwner
;
118 TRACKMOUSEEVENT MouseTrackingInfo
;
120 HDPA hdpaShellServices
;
123 static ITrayWindowImpl
* g_TrayWindow
;
125 BOOL
LaunchCPanel(HWND hwnd
, LPCTSTR applet
)
127 TCHAR szParams
[MAX_PATH
];
129 StringCbCopy(szParams
, sizeof(szParams
),
130 TEXT("shell32.dll,Control_RunDLL "));
131 if (FAILED(StringCbCat(szParams
, sizeof(szParams
),
135 return (ShellExecute(hwnd
, TEXT("open"), TEXT("rundll32.exe"), szParams
, NULL
, SW_SHOWDEFAULT
) > (HINSTANCE
)32);
139 IUnknown_from_impl(ITrayWindowImpl
*This
)
141 return (IUnknown
*)&This
->lpVtbl
;
145 ITrayWindow_from_impl(ITrayWindowImpl
*This
)
147 return (ITrayWindow
*)&This
->lpVtbl
;
150 static IShellDesktopTray
*
151 IShellDesktopTray_from_impl(ITrayWindowImpl
*This
)
153 return (IShellDesktopTray
*)&This
->lpVtblShellDesktopTray
;
156 static ITrayWindowImpl
*
157 impl_from_ITrayWindow(ITrayWindow
*iface
)
159 return (ITrayWindowImpl
*)((ULONG_PTR
)iface
- FIELD_OFFSET(ITrayWindowImpl
,
163 static ITrayWindowImpl
*
164 impl_from_IShellDesktopTray(IShellDesktopTray
*iface
)
166 return (ITrayWindowImpl
*)((ULONG_PTR
)iface
- FIELD_OFFSET(ITrayWindowImpl
,
167 lpVtblShellDesktopTray
));
175 ITrayWindowImpl_UpdateNonClientMetrics(IN OUT ITrayWindowImpl
*This
)
177 This
->ncm
.cbSize
= sizeof(This
->ncm
);
178 if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS
,
183 if (This
->hFont
!= NULL
)
184 DeleteObject(This
->hFont
);
186 This
->hFont
= CreateFontIndirect(&This
->ncm
.lfMessageFont
);
194 ITrayWindowImpl_SetWindowsFont(IN OUT ITrayWindowImpl
*This
)
196 if (This
->hwndTrayNotify
!= NULL
)
198 SendMessage(This
->hwndTrayNotify
,
206 ITrayWindowImpl_GetScreenRectFromRect(IN OUT ITrayWindowImpl
*This
,
213 mi
.cbSize
= sizeof(mi
);
214 hMon
= MonitorFromRect(pRect
,
220 *pRect
= mi
.rcMonitor
;
226 pRect
->right
= GetSystemMetrics(SM_CXSCREEN
);
227 pRect
->bottom
= GetSystemMetrics(SM_CYSCREEN
);
236 ITrayWindowImpl_GetMonitorFromRect(IN OUT ITrayWindowImpl
*This
,
237 IN
const RECT
*pRect
)
241 /* In case the monitor sizes or saved sizes differ a bit (probably
242 not a lot, only so the tray window overlaps into another monitor
243 now), minimize the risk that we determine a wrong monitor by
244 using the center point of the tray window if we can't determine
245 it using the rectangle. */
246 hMon
= MonitorFromRect(pRect
,
247 MONITOR_DEFAULTTONULL
);
252 pt
.x
= pRect
->left
+ ((pRect
->right
- pRect
->left
) / 2);
253 pt
.y
= pRect
->top
+ ((pRect
->bottom
- pRect
->top
) / 2);
255 /* be less error-prone, find the nearest monitor */
256 hMon
= MonitorFromPoint(pt
,
257 MONITOR_DEFAULTTONEAREST
);
264 ITrayWindowImpl_GetScreenRect(IN OUT ITrayWindowImpl
*This
,
265 IN HMONITOR hMonitor
,
268 HMONITOR hMon
= NULL
;
270 if (hMonitor
!= NULL
)
274 mi
.cbSize
= sizeof(mi
);
275 if (!GetMonitorInfo(hMonitor
,
278 /* Hm, the monitor is gone? Try to find a monitor where it
279 could be located now */
280 hMon
= ITrayWindowImpl_GetMonitorFromRect(This
,
283 !GetMonitorInfo(hMon
,
291 *pRect
= mi
.rcMonitor
;
298 pRect
->right
= GetSystemMetrics(SM_CXSCREEN
);
299 pRect
->bottom
= GetSystemMetrics(SM_CYSCREEN
);
306 ITrayWindowImpl_MakeTrayRectWithSize(IN DWORD Position
,
307 IN
const SIZE
*pTraySize
,
313 pRect
->right
= pRect
->left
+ pTraySize
->cx
;
317 pRect
->bottom
= pRect
->top
+ pTraySize
->cy
;
321 pRect
->left
= pRect
->right
- pTraySize
->cx
;
326 pRect
->top
= pRect
->bottom
- pTraySize
->cy
;
332 ITrayWindowImpl_GetTrayRectFromScreenRect(IN OUT ITrayWindowImpl
*This
,
334 IN
const RECT
*pScreen
,
335 IN
const SIZE
*pTraySize OPTIONAL
,
338 if (pTraySize
== NULL
)
339 pTraySize
= &This
->TraySize
;
343 /* Move the border outside of the screen */
345 GetSystemMetrics(SM_CXEDGE
),
346 GetSystemMetrics(SM_CYEDGE
));
348 ITrayWindowImpl_MakeTrayRectWithSize(Position
,
354 ITrayWindowImpl_IsPosHorizontal(IN OUT ITrayWindowImpl
*This
)
356 return This
->Position
== ABE_TOP
|| This
->Position
== ABE_BOTTOM
;
360 ITrayWindowImpl_CalculateValidSize(IN OUT ITrayWindowImpl
*This
,
369 //Horizontal = ITrayWindowImpl_IsPosHorizontal(This);
371 szWnd
.cx
= pRect
->right
- pRect
->left
;
372 szWnd
.cy
= pRect
->bottom
- pRect
->top
;
375 hMon
= ITrayWindowImpl_GetScreenRectFromRect(This
,
377 MONITOR_DEFAULTTONEAREST
);
379 /* Calculate the maximum size of the tray window and limit the window
380 size to half of the screen's size. */
381 szMax
.cx
= (rcScreen
.right
- rcScreen
.left
) / 2;
382 szMax
.cy
= (rcScreen
.bottom
- rcScreen
.top
) / 2;
383 if (szWnd
.cx
> szMax
.cx
)
385 if (szWnd
.cy
> szMax
.cy
)
388 /* FIXME - calculate */
390 ITrayWindowImpl_GetTrayRectFromScreenRect(This
,
401 ITrayWindowImpl_GetMinimumWindowSize(IN OUT ITrayWindowImpl
*This
,
406 AdjustWindowRectEx(&rcMin
,
407 GetWindowLong(This
->hWnd
,
410 GetWindowLong(This
->hWnd
,
419 ITrayWindowImpl_GetDraggingRectFromPt(IN OUT ITrayWindowImpl
*This
,
422 OUT HMONITOR
*phMonitor
)
424 HMONITOR hMon
, hMonNew
;
425 DWORD PosH
, PosV
, Pos
;
426 SIZE DeltaPt
, ScreenOffset
;
432 /* Determine the screen rectangle */
433 hMon
= MonitorFromPoint(pt
,
434 MONITOR_DEFAULTTONULL
);
440 mi
.cbSize
= sizeof(mi
);
441 if (!GetMonitorInfo(hMon
,
445 goto GetPrimaryScreenRect
;
448 /* make left top corner of the screen zero based to
449 make calculations easier */
450 pt
.x
-= mi
.rcMonitor
.left
;
451 pt
.y
-= mi
.rcMonitor
.top
;
453 ScreenOffset
.cx
= mi
.rcMonitor
.left
;
454 ScreenOffset
.cy
= mi
.rcMonitor
.top
;
455 rcScreen
.right
= mi
.rcMonitor
.right
- mi
.rcMonitor
.left
;
456 rcScreen
.bottom
= mi
.rcMonitor
.bottom
- mi
.rcMonitor
.top
;
460 GetPrimaryScreenRect
:
463 rcScreen
.right
= GetSystemMetrics(SM_CXSCREEN
);
464 rcScreen
.bottom
= GetSystemMetrics(SM_CYSCREEN
);
467 /* Calculate the nearest screen border */
468 if (pt
.x
< rcScreen
.right
/ 2)
475 DeltaPt
.cx
= rcScreen
.right
- pt
.x
;
479 if (pt
.y
< rcScreen
.bottom
/ 2)
486 DeltaPt
.cy
= rcScreen
.bottom
- pt
.y
;
490 Pos
= (DeltaPt
.cx
* rcScreen
.bottom
< DeltaPt
.cy
* rcScreen
.right
) ? PosH
: PosV
;
492 /* Fix the screen origin to be relative to the primary monitor again */
493 OffsetRect(&rcScreen
,
497 hMonNew
= ITrayWindowImpl_GetMonitorFromRect(This
,
498 &This
->rcTrayWnd
[Pos
]);
503 /* Recalculate the rectangle, we're dragging to another monitor.
504 We don't need to recalculate the rect on single monitor systems. */
505 szTray
.cx
= This
->rcTrayWnd
[Pos
].right
- This
->rcTrayWnd
[Pos
].left
;
506 szTray
.cy
= This
->rcTrayWnd
[Pos
].bottom
- This
->rcTrayWnd
[Pos
].top
;
508 ITrayWindowImpl_GetTrayRectFromScreenRect(This
,
515 pRect
->left
+= This
->AutoHideOffset
.cx
;
516 pRect
->right
+= This
->AutoHideOffset
.cx
;
517 pRect
->top
+= This
->AutoHideOffset
.cy
;
518 pRect
->bottom
+= This
->AutoHideOffset
.cy
;
524 /* The user is dragging the tray window on the same monitor. We don't need
525 to recalculate the rectangle */
526 *pRect
= This
->rcTrayWnd
[Pos
];
529 pRect
->left
+= This
->AutoHideOffset
.cx
;
530 pRect
->right
+= This
->AutoHideOffset
.cx
;
531 pRect
->top
+= This
->AutoHideOffset
.cy
;
532 pRect
->bottom
+= This
->AutoHideOffset
.cy
;
542 ITrayWindowImpl_GetDraggingRectFromRect(IN OUT ITrayWindowImpl
*This
,
544 OUT HMONITOR
*phMonitor
)
548 /* Calculate the center of the rectangle. We call
549 ITrayWindowImpl_GetDraggingRectFromPt to calculate a valid
550 dragging rectangle */
551 pt
.x
= pRect
->left
+ ((pRect
->right
- pRect
->left
) / 2);
552 pt
.y
= pRect
->top
+ ((pRect
->bottom
- pRect
->top
) / 2);
554 return ITrayWindowImpl_GetDraggingRectFromPt(This
,
561 ITrayWindowImpl_ChangingWinPos(IN OUT ITrayWindowImpl
*This
,
562 IN OUT LPWINDOWPOS pwp
)
566 if (This
->IsDragging
)
568 rcTray
.left
= pwp
->x
;
570 rcTray
.right
= rcTray
.left
+ pwp
->cx
;
571 rcTray
.bottom
= rcTray
.top
+ pwp
->cy
;
574 rcTray
.left
-= This
->AutoHideOffset
.cx
;
575 rcTray
.right
-= This
->AutoHideOffset
.cx
;
576 rcTray
.top
-= This
->AutoHideOffset
.cy
;
577 rcTray
.bottom
-= This
->AutoHideOffset
.cy
;
580 if (!EqualRect(&rcTray
,
581 &This
->rcTrayWnd
[This
->DraggingPosition
]))
583 /* Recalculate the rectangle, the user dragged the tray
584 window to another monitor or the window was somehow else
586 This
->DraggingPosition
= ITrayWindowImpl_GetDraggingRectFromRect(This
,
588 &This
->DraggingMonitor
);
589 //This->rcTrayWnd[This->DraggingPosition] = rcTray;
592 //This->Monitor = ITrayWindowImpl_CalculateValidSize(This,
593 // This->DraggingPosition,
596 This
->Monitor
= This
->DraggingMonitor
;
597 This
->Position
= This
->DraggingPosition
;
598 This
->IsDragging
= FALSE
;
600 This
->rcTrayWnd
[This
->Position
] = rcTray
;
603 else if (GetWindowRect(This
->hWnd
,
606 if (This
->InSizeMove
)
608 if (!(pwp
->flags
& SWP_NOMOVE
))
610 rcTray
.left
= pwp
->x
;
614 if (!(pwp
->flags
& SWP_NOSIZE
))
616 rcTray
.right
= rcTray
.left
+ pwp
->cx
;
617 rcTray
.bottom
= rcTray
.top
+ pwp
->cy
;
620 This
->Position
= ITrayWindowImpl_GetDraggingRectFromRect(This
,
624 if (!(pwp
->flags
& (SWP_NOMOVE
| SWP_NOSIZE
)))
631 ITrayWindowImpl_MakeTrayRectWithSize(This
->Position
,
638 rcTray
.left
-= This
->AutoHideOffset
.cx
;
639 rcTray
.right
-= This
->AutoHideOffset
.cx
;
640 rcTray
.top
-= This
->AutoHideOffset
.cy
;
641 rcTray
.bottom
-= This
->AutoHideOffset
.cy
;
643 This
->rcTrayWnd
[This
->Position
] = rcTray
;
647 /* If the user isn't resizing the tray window we need to make sure the
648 new size or position is valid. This is to prevent changes to the window
649 without user interaction. */
650 rcTray
= This
->rcTrayWnd
[This
->Position
];
654 This
->TraySize
.cx
= rcTray
.right
- rcTray
.left
;
655 This
->TraySize
.cy
= rcTray
.bottom
- rcTray
.top
;
659 rcTray
.left
+= This
->AutoHideOffset
.cx
;
660 rcTray
.right
+= This
->AutoHideOffset
.cx
;
661 rcTray
.top
+= This
->AutoHideOffset
.cy
;
662 rcTray
.bottom
+= This
->AutoHideOffset
.cy
;
665 pwp
->flags
&= ~(SWP_NOMOVE
| SWP_NOSIZE
);
666 pwp
->x
= rcTray
.left
;
668 pwp
->cx
= This
->TraySize
.cx
;
669 pwp
->cy
= This
->TraySize
.cy
;
674 ITrayWindowImpl_ApplyClipping(IN OUT ITrayWindowImpl
*This
,
677 RECT rcClip
, rcWindow
;
680 if (GetWindowRect(This
->hWnd
,
683 /* Disable clipping on systems with only one monitor */
684 if (GetSystemMetrics(SM_CMONITORS
) <= 1)
691 ITrayWindowImpl_GetScreenRect(This
,
695 if (!IntersectRect(&rcClip
,
706 hClipRgn
= CreateRectRgnIndirect(&rcClip
);
711 /* Set the clipping region or make sure the window isn't clipped
712 by disabling it explicitly. */
713 SetWindowRgn(This
->hWnd
,
720 ITrayWindowImpl_ResizeWorkArea(IN OUT ITrayWindowImpl
*This
)
722 RECT rcTray
,rcWorkArea
;
724 /* If monitor has changed then fix the previous monitors work area */
725 if (This
->PreviousMonitor
!= This
->Monitor
)
727 ITrayWindowImpl_GetScreenRect(This
,
728 This
->PreviousMonitor
,
730 SystemParametersInfo(SPI_SETWORKAREA
,
736 rcTray
= This
->rcTrayWnd
[This
->Position
];
738 ITrayWindowImpl_GetScreenRect(This
,
741 This
->PreviousMonitor
= This
->Monitor
;
743 /* If AutoHide is false then change the workarea to exclude the area that
744 the taskbar covers. */
747 switch (This
->Position
)
750 rcWorkArea
.top
= rcTray
.bottom
;
753 rcWorkArea
.left
= rcTray
.right
;
756 rcWorkArea
.right
= rcTray
.left
;
759 rcWorkArea
.bottom
= rcTray
.top
;
764 SystemParametersInfo(SPI_SETWORKAREA
,
771 ITrayWindowImpl_CheckTrayWndPosition(IN OUT ITrayWindowImpl
*This
)
775 rcTray
= This
->rcTrayWnd
[This
->Position
];
779 rcTray
.left
+= This
->AutoHideOffset
.cx
;
780 rcTray
.right
+= This
->AutoHideOffset
.cx
;
781 rcTray
.top
+= This
->AutoHideOffset
.cy
;
782 rcTray
.bottom
+= This
->AutoHideOffset
.cy
;
785 // TRACE("CheckTray: %d: %d,%d,%d,%d\n", This->Position, rcTray.left, rcTray.top, rcTray.right, rcTray.bottom);
787 /* Move the tray window */
788 SetWindowPos(This
->hWnd
,
792 rcTray
.right
- rcTray
.left
,
793 rcTray
.bottom
- rcTray
.top
,
796 ITrayWindowImpl_ResizeWorkArea(This
);
798 ITrayWindowImpl_ApplyClipping(This
,
802 typedef struct _TW_STUCKRECTS2
810 } TW_STRUCKRECTS2
, *PTW_STUCKRECTS2
;
813 ITrayWindowImpl_RegLoadSettings(IN OUT ITrayWindowImpl
*This
)
818 SIZE WndSize
, EdgeSize
, DlgFrameSize
;
819 DWORD cbSize
= sizeof(sr
);
821 EdgeSize
.cx
= GetSystemMetrics(SM_CXEDGE
);
822 EdgeSize
.cy
= GetSystemMetrics(SM_CYEDGE
);
823 DlgFrameSize
.cx
= GetSystemMetrics(SM_CXDLGFRAME
);
824 DlgFrameSize
.cy
= GetSystemMetrics(SM_CYDLGFRAME
);
826 if (SHGetValue(hkExplorer
,
831 &cbSize
) == ERROR_SUCCESS
&&
832 sr
.cbSize
== sizeof(sr
))
834 This
->AutoHide
= (sr
.dwFlags
& ABS_AUTOHIDE
) != 0;
835 This
->AlwaysOnTop
= (sr
.dwFlags
& ABS_ALWAYSONTOP
) != 0;
836 This
->SmSmallIcons
= (sr
.dwFlags
& 0x4) != 0;
837 This
->HideClock
= (sr
.dwFlags
& 0x8) != 0;
839 /* FIXME: Are there more flags? */
841 if (sr
.Position
> ABE_BOTTOM
)
842 This
->Position
= ABE_BOTTOM
;
844 This
->Position
= sr
.Position
;
846 /* Try to find out which monitor the tray window was located on last.
847 Here we're only interested in the monitor screen that we think
848 is the last one used. We're going to determine on which monitor
849 we really are after calculating the docked position. */
851 ITrayWindowImpl_GetScreenRectFromRect(This
,
853 MONITOR_DEFAULTTONEAREST
);
857 This
->Position
= ABE_BOTTOM
;
858 This
->AlwaysOnTop
= TRUE
;
860 /* Use the minimum size of the taskbar, we'll use the start
861 button as a minimum for now. Make sure we calculate the
862 entire window size, not just the client size. However, we
863 use a thinner border than a standard thick border, so that
864 the start button and bands are not stuck to the screen border. */
865 sr
.Size
.cx
= This
->StartBtnSize
.cx
+ (2 * (EdgeSize
.cx
+ DlgFrameSize
.cx
));
866 sr
.Size
.cy
= This
->StartBtnSize
.cy
+ (2 * (EdgeSize
.cy
+ DlgFrameSize
.cy
));
868 /* Use the primary screen by default */
871 rcScreen
.right
= GetSystemMetrics(SM_CXSCREEN
);
872 rcScreen
.bottom
= GetSystemMetrics(SM_CYSCREEN
);
873 ITrayWindowImpl_GetScreenRectFromRect(This
,
875 MONITOR_DEFAULTTOPRIMARY
);
878 if (This
->hWnd
!= NULL
)
879 SetWindowPos(This
->hWnd
,
880 This
->AlwaysOnTop
? HWND_TOPMOST
: HWND_NOTOPMOST
,
885 SWP_NOMOVE
| SWP_NOSIZE
);
887 /* Determine a minimum tray window rectangle. The "client" height is
888 zero here since we cannot determine an optimal minimum width when
889 loaded as a vertical tray window. We just need to make sure the values
890 loaded from the registry are at least. The windows explorer behaves
891 the same way, it allows the user to save a zero width vertical tray
892 window, but not a zero height horizontal tray window. */
893 WndSize
.cx
= 2 * (EdgeSize
.cx
+ DlgFrameSize
.cx
);
894 WndSize
.cy
= This
->StartBtnSize
.cy
+ (2 * (EdgeSize
.cy
+ DlgFrameSize
.cy
));
896 if (WndSize
.cx
< sr
.Size
.cx
)
897 WndSize
.cx
= sr
.Size
.cx
;
898 if (WndSize
.cy
< sr
.Size
.cy
)
899 WndSize
.cy
= sr
.Size
.cy
;
901 /* Save the calculated size */
902 This
->TraySize
= WndSize
;
904 /* Calculate all docking rectangles. We need to do this here so they're
905 initialized and dragging the tray window to another position gives
911 ITrayWindowImpl_GetTrayRectFromScreenRect(This
,
915 &This
->rcTrayWnd
[Pos
]);
916 // TRACE("rcTrayWnd[%d(%d)]: %d,%d,%d,%d\n", Pos, This->Position, This->rcTrayWnd[Pos].left, This->rcTrayWnd[Pos].top, This->rcTrayWnd[Pos].right, This->rcTrayWnd[Pos].bottom);
919 /* Determine which monitor we are on. It shouldn't matter which docked
920 position rectangle we use */
921 This
->Monitor
= ITrayWindowImpl_GetMonitorFromRect(This
,
922 &This
->rcTrayWnd
[ABE_LEFT
]);
926 ITrayWindowImpl_TrackMenu(IN OUT ITrayWindowImpl
*This
,
928 IN POINT
*ppt OPTIONAL
,
929 IN HWND hwndExclude OPTIONAL
,
931 IN BOOL IsContextMenu
)
933 TPMPARAMS tmp
, *ptmp
= NULL
;
938 if (hwndExclude
!= NULL
)
940 /* Get the client rectangle and map it to screen coordinates */
941 if (GetClientRect(hwndExclude
,
943 MapWindowPoints(hwndExclude
,
945 (LPPOINT
)&tmp
.rcExclude
,
955 GetClientRect(This
->hWnd
,
957 MapWindowPoints(This
->hWnd
,
959 (LPPOINT
)&tmp
.rcExclude
,
967 /* NOTE: TrackPopupMenuEx will eventually align the track position
968 for us, no need to take care of it here as long as the
969 coordinates are somewhere within the exclusion rectangle */
970 pt
.x
= ptmp
->rcExclude
.left
;
971 pt
.y
= ptmp
->rcExclude
.top
;
979 tmp
.cbSize
= sizeof(tmp
);
981 fuFlags
= TPM_RETURNCMD
| TPM_VERTICAL
;
982 fuFlags
|= (TrackUp
? TPM_BOTTOMALIGN
: TPM_TOPALIGN
);
984 fuFlags
|= TPM_RIGHTBUTTON
;
986 fuFlags
|= (TrackUp
? TPM_VERNEGANIMATION
: TPM_VERPOSANIMATION
);
988 cmdId
= TrackPopupMenuEx(hMenu
,
999 ITrayWindowImpl_TrackCtxMenu(IN OUT ITrayWindowImpl
*This
,
1000 IN
const TRAYWINDOW_CTXMENU
*pMenu
,
1001 IN POINT
*ppt OPTIONAL
,
1002 IN HWND hwndExclude OPTIONAL
,
1004 IN PVOID Context OPTIONAL
)
1008 PVOID pcmContext
= NULL
;
1010 hPopup
= pMenu
->CreateCtxMenu(This
->hWnd
,
1015 cmdId
= ITrayWindowImpl_TrackMenu(This
,
1022 pMenu
->CtxMenuCommand(This
->hWnd
,
1027 DestroyMenu(hPopup
);
1034 ITrayWindowImpl_Free(ITrayWindowImpl
*This
)
1036 HeapFree(hProcessHeap
,
1042 static ULONG STDMETHODCALLTYPE
1043 ITrayWindowImpl_Release(IN OUT ITrayWindow
*iface
)
1045 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1048 Ret
= InterlockedDecrement(&This
->Ref
);
1050 ITrayWindowImpl_Free(This
);
1056 ITrayWindowImpl_Destroy(ITrayWindowImpl
*This
)
1058 (void)InterlockedExchangePointer((PVOID
*)&This
->hWnd
,
1062 if (This
->hdpaShellServices
!= NULL
)
1064 ShutdownShellServices(This
->hdpaShellServices
);
1065 This
->hdpaShellServices
= NULL
;
1068 if (This
->himlStartBtn
!= NULL
)
1070 ImageList_Destroy(This
->himlStartBtn
);
1071 This
->himlStartBtn
= NULL
;
1074 if (This
->hCaptionFont
!= NULL
)
1076 DeleteObject(This
->hCaptionFont
);
1077 This
->hCaptionFont
= NULL
;
1080 if (This
->hStartBtnFont
!= NULL
)
1082 DeleteObject(This
->hStartBtnFont
);
1083 This
->hStartBtnFont
= NULL
;
1086 if (This
->hFont
!= NULL
)
1088 DeleteObject(This
->hFont
);
1092 if (This
->StartMenuPopup
!= NULL
)
1094 IMenuPopup_Release(This
->StartMenuPopup
);
1095 This
->StartMenuPopup
= NULL
;
1098 if (This
->hbmStartMenu
!= NULL
)
1100 DeleteObject(This
->hbmStartMenu
);
1101 This
->hbmStartMenu
= NULL
;
1104 if (This
->StartMenuBand
!= NULL
)
1106 IMenuBand_Release(This
->StartMenuBand
);
1107 This
->StartMenuBand
= NULL
;
1110 if (This
->TrayBandSite
!= NULL
)
1112 /* FIXME: Unload bands */
1113 ITrayBandSite_Release(This
->TrayBandSite
);
1114 This
->TrayBandSite
= NULL
;
1117 if (This
->TaskbarTheme
)
1119 CloseThemeData(This
->TaskbarTheme
);
1120 This
->TaskbarTheme
= NULL
;
1123 ITrayWindowImpl_Release(ITrayWindow_from_impl(This
));
1125 if (InterlockedDecrement(&TrayWndCount
) == 0)
1129 static ULONG STDMETHODCALLTYPE
1130 ITrayWindowImpl_AddRef(IN OUT ITrayWindow
*iface
)
1132 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1134 return InterlockedIncrement(&This
->Ref
);
1139 ITrayWindowImpl_NCCreate(IN OUT ITrayWindowImpl
*This
)
1141 ITrayWindowImpl_AddRef(ITrayWindow_from_impl(This
));
1147 ITrayWindowImpl_UpdateStartButton(IN OUT ITrayWindowImpl
*This
,
1148 IN HBITMAP hbmStart OPTIONAL
)
1150 SIZE Size
= { 0, 0 };
1152 if (This
->himlStartBtn
== NULL
||
1153 !SendMessage(This
->hwndStart
,
1158 Size
.cx
= GetSystemMetrics(SM_CXEDGE
);
1159 Size
.cy
= GetSystemMetrics(SM_CYEDGE
);
1161 if (hbmStart
== NULL
)
1163 hbmStart
= (HBITMAP
)SendMessage(This
->hwndStart
,
1169 if (hbmStart
!= NULL
)
1173 if (GetObject(hbmStart
,
1177 Size
.cx
+= bmp
.bmWidth
;
1178 Size
.cy
+= max(bmp
.bmHeight
,
1179 GetSystemMetrics(SM_CYCAPTION
));
1183 /* Huh?! Shouldn't happen... */
1190 Size
.cx
+= GetSystemMetrics(SM_CXMINIMIZED
);
1191 Size
.cy
+= GetSystemMetrics(SM_CYCAPTION
);
1195 /* Save the size of the start button */
1196 This
->StartBtnSize
= Size
;
1200 ITrayWindowImpl_AlignControls(IN OUT ITrayWindowImpl
*This
,
1201 IN PRECT prcClient OPTIONAL
)
1204 SIZE TraySize
, StartSize
;
1205 POINT ptTrayNotify
= { 0, 0 };
1209 ITrayWindowImpl_UpdateStartButton(This
, NULL
);
1210 if (prcClient
!= NULL
)
1212 rcClient
= *prcClient
;
1216 if (!GetClientRect(This
->hWnd
,
1223 Horizontal
= ITrayWindowImpl_IsPosHorizontal(This
);
1225 /* We're about to resize/move the start button, the rebar control and
1226 the tray notification control */
1227 dwp
= BeginDeferWindowPos(3);
1231 /* Limit the Start button width to the client width, if neccessary */
1232 StartSize
= This
->StartBtnSize
;
1233 if (StartSize
.cx
> rcClient
.right
)
1234 StartSize
.cx
= rcClient
.right
;
1236 if (This
->hwndStart
!= NULL
)
1238 /* Resize and reposition the button */
1239 dwp
= DeferWindowPos(dwp
,
1246 SWP_NOZORDER
| SWP_NOACTIVATE
);
1251 /* Determine the size that the tray notification window needs */
1255 TraySize
.cy
= rcClient
.bottom
;
1259 TraySize
.cx
= rcClient
.right
;
1263 if (This
->hwndTrayNotify
!= NULL
&&
1264 SendMessage(This
->hwndTrayNotify
,
1265 TNWM_GETMINIMUMSIZE
,
1269 /* Move the tray notification window to the desired location */
1271 ptTrayNotify
.x
= rcClient
.right
- TraySize
.cx
;
1273 ptTrayNotify
.y
= rcClient
.bottom
- TraySize
.cy
;
1275 dwp
= DeferWindowPos(dwp
,
1276 This
->hwndTrayNotify
,
1282 SWP_NOZORDER
| SWP_NOACTIVATE
);
1287 /* Resize/Move the rebar control */
1288 if (This
->hwndRebar
!= NULL
)
1290 POINT ptRebar
= { 0, 0 };
1293 SetWindowStyle(This
->hwndRebar
,
1295 Horizontal
? 0 : CCS_VERT
);
1299 ptRebar
.x
= StartSize
.cx
+ GetSystemMetrics(SM_CXSIZEFRAME
);
1300 szRebar
.cx
= ptTrayNotify
.x
- ptRebar
.x
;
1301 szRebar
.cy
= rcClient
.bottom
;
1305 ptRebar
.y
= StartSize
.cy
+ GetSystemMetrics(SM_CYSIZEFRAME
);
1306 szRebar
.cx
= rcClient
.right
;
1307 szRebar
.cy
= ptTrayNotify
.y
- ptRebar
.y
;
1310 dwp
= DeferWindowPos(dwp
,
1317 SWP_NOZORDER
| SWP_NOACTIVATE
);
1321 EndDeferWindowPos(dwp
);
1323 if (This
->hwndTaskSwitch
!= NULL
)
1325 /* Update the task switch window configuration */
1326 SendMessage(This
->hwndTaskSwitch
,
1327 TSWM_UPDATETASKBARPOS
,
1334 ITrayWindowImpl_CreateStartBtnImageList(IN OUT ITrayWindowImpl
*This
)
1339 if (This
->himlStartBtn
!= NULL
)
1342 IconSize
.cx
= GetSystemMetrics(SM_CXSMICON
);
1343 IconSize
.cy
= GetSystemMetrics(SM_CYSMICON
);
1345 /* Load the start button icon and create a image list for it */
1346 hIconStart
= LoadImage(hExplorerInstance
,
1347 MAKEINTRESOURCE(IDI_START
),
1351 LR_SHARED
| LR_DEFAULTCOLOR
);
1353 if (hIconStart
!= NULL
)
1355 This
->himlStartBtn
= ImageList_Create(IconSize
.cx
,
1357 ILC_COLOR32
| ILC_MASK
,
1360 if (This
->himlStartBtn
!= NULL
)
1362 if (ImageList_AddIcon(This
->himlStartBtn
,
1368 /* Failed to add the icon! */
1369 ImageList_Destroy(This
->himlStartBtn
);
1370 This
->himlStartBtn
= NULL
;
1378 ITrayWindowImpl_CreateStartButtonBitmap(IN OUT ITrayWindowImpl
*This
)
1380 TCHAR szStartCaption
[32];
1383 HDC hDCScreen
= NULL
;
1384 SIZE Size
, SmallIcon
;
1385 HBITMAP hbmpOld
, hbmp
= NULL
;
1386 HBITMAP hBitmap
= NULL
;
1392 /* NOTE: This is the backwards compatibility code that is used if the
1393 Common Controls Version 6.0 are not available! */
1395 if (!LoadString(hExplorerInstance
,
1398 sizeof(szStartCaption
) / sizeof(szStartCaption
[0])))
1403 /* Load the start button icon */
1404 SmallIcon
.cx
= GetSystemMetrics(SM_CXSMICON
);
1405 SmallIcon
.cy
= GetSystemMetrics(SM_CYSMICON
);
1406 hIconStart
= LoadImage(hExplorerInstance
,
1407 MAKEINTRESOURCE(IDI_START
),
1411 LR_SHARED
| LR_DEFAULTCOLOR
);
1413 hDCScreen
= GetDC(NULL
);
1414 if (hDCScreen
== NULL
)
1417 hDC
= CreateCompatibleDC(hDCScreen
);
1421 hFontOld
= SelectObject(hDC
,
1422 This
->hStartBtnFont
);
1424 Ret
= GetTextExtentPoint32(hDC
,
1426 _tcslen(szStartCaption
),
1434 /* Make sure the height is at least the size of a caption icon. */
1435 if (hIconStart
!= NULL
)
1436 Size
.cx
+= SmallIcon
.cx
+ 4;
1437 Size
.cy
= max(Size
.cy
,
1440 /* Create the bitmap */
1441 hbmp
= CreateCompatibleBitmap(hDCScreen
,
1447 /* Caluclate the button rect */
1450 rcButton
.right
= Size
.cx
;
1451 rcButton
.bottom
= Size
.cy
;
1453 /* Draw the button */
1454 hbmpOld
= SelectObject(hDC
,
1457 Flags
= DC_TEXT
| DC_INBUTTON
;
1458 if (hIconStart
!= NULL
)
1461 if (DrawCapTemp
!= NULL
)
1463 Ret
= DrawCapTemp(NULL
,
1466 This
->hStartBtnFont
,
1478 /* We successfully created the bitmap! */
1483 if (hDCScreen
!= NULL
)
1499 ITrayWindowImpl_UpdateTheme(IN OUT ITrayWindowImpl
*This
)
1501 if (This
->TaskbarTheme
)
1502 CloseThemeData(This
->TaskbarTheme
);
1504 if (IsThemeActive())
1505 This
->TaskbarTheme
= OpenThemeData(This
->hWnd
, L
"Taskbar");
1507 This
->TaskbarTheme
= 0;
1511 ITrayWindowImpl_Create(IN OUT ITrayWindowImpl
*This
)
1513 TCHAR szStartCaption
[32];
1515 SetWindowTheme(This
->hWnd
, L
"TaskBar", NULL
);
1516 ITrayWindowImpl_UpdateTheme(This
);
1518 InterlockedIncrement(&TrayWndCount
);
1520 if (!LoadString(hExplorerInstance
,
1523 sizeof(szStartCaption
) / sizeof(szStartCaption
[0])))
1525 szStartCaption
[0] = TEXT('\0');
1528 if (This
->hStartBtnFont
== NULL
|| This
->hCaptionFont
== NULL
)
1530 NONCLIENTMETRICS ncm
;
1532 /* Get the system fonts, we use the caption font,
1533 always bold, though. */
1534 ncm
.cbSize
= sizeof(ncm
);
1535 if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS
,
1540 if (This
->hCaptionFont
== NULL
)
1542 ncm
.lfCaptionFont
.lfWeight
= FW_NORMAL
;
1543 This
->hCaptionFont
= CreateFontIndirect(&ncm
.lfCaptionFont
);
1546 if (This
->hStartBtnFont
== NULL
)
1548 ncm
.lfCaptionFont
.lfWeight
= FW_BOLD
;
1549 This
->hStartBtnFont
= CreateFontIndirect(&ncm
.lfCaptionFont
);
1554 /* Create the Start button */
1555 This
->hwndStart
= CreateWindowEx(0,
1558 WS_CHILD
| WS_VISIBLE
| WS_CLIPSIBLINGS
|
1559 BS_PUSHBUTTON
| BS_CENTER
| BS_VCENTER
| BS_BITMAP
,
1565 (HMENU
)IDC_STARTBTN
,
1568 if (This
->hwndStart
)
1570 SetWindowTheme(This
->hwndStart
, L
"Start", NULL
);
1571 SendMessage(This
->hwndStart
,
1573 (WPARAM
)This
->hStartBtnFont
,
1576 if (ITrayWindowImpl_CreateStartBtnImageList(This
))
1578 BUTTON_IMAGELIST bil
;
1580 /* Try to set the start button image. This requires the Common
1581 Controls 6.0 to be present (XP and later) */
1582 bil
.himl
= This
->himlStartBtn
;
1583 bil
.margin
.left
= bil
.margin
.right
= 1;
1584 bil
.margin
.top
= bil
.margin
.bottom
= 1;
1585 bil
.uAlign
= BUTTON_IMAGELIST_ALIGN_LEFT
;
1587 if (!SendMessage(This
->hwndStart
,
1592 /* Fall back to the deprecated method on older systems that don't
1593 support Common Controls 6.0 */
1594 ImageList_Destroy(This
->himlStartBtn
);
1595 This
->himlStartBtn
= NULL
;
1597 goto SetStartBtnImage
;
1600 /* We're using the image list, remove the BS_BITMAP style and
1601 don't center it horizontally */
1602 SetWindowStyle(This
->hwndStart
,
1603 BS_BITMAP
| BS_RIGHT
,
1606 ITrayWindowImpl_UpdateStartButton(This
,
1611 HBITMAP hbmStart
, hbmOld
;
1614 hbmStart
= ITrayWindowImpl_CreateStartButtonBitmap(This
);
1615 if (hbmStart
!= NULL
)
1617 ITrayWindowImpl_UpdateStartButton(This
,
1620 hbmOld
= (HBITMAP
)SendMessage(This
->hwndStart
,
1626 DeleteObject(hbmOld
);
1631 /* Load the saved tray window settings */
1632 ITrayWindowImpl_RegLoadSettings(This
);
1634 /* Create and initialize the start menu */
1635 This
->hbmStartMenu
= LoadBitmap(hExplorerInstance
,
1636 MAKEINTRESOURCE(IDB_STARTMENU
));
1637 This
->StartMenuPopup
= CreateStartMenu(ITrayWindow_from_impl(This
),
1638 &This
->StartMenuBand
,
1642 /* Load the tray band site */
1643 if (This
->TrayBandSite
!= NULL
)
1645 ITrayBandSite_Release(This
->TrayBandSite
);
1648 This
->TrayBandSite
= CreateTrayBandSite(ITrayWindow_from_impl(This
),
1650 &This
->hwndTaskSwitch
);
1651 SetWindowTheme(This
->hwndRebar
, L
"TaskBar", NULL
);
1653 /* Create the tray notification window */
1654 This
->hwndTrayNotify
= CreateTrayNotifyWnd(ITrayWindow_from_impl(This
),
1657 if (ITrayWindowImpl_UpdateNonClientMetrics(This
))
1659 ITrayWindowImpl_SetWindowsFont(This
);
1662 /* Move the tray window to the right position and resize it if neccessary */
1663 ITrayWindowImpl_CheckTrayWndPosition(This
);
1665 /* Align all controls on the tray window */
1666 ITrayWindowImpl_AlignControls(This
,
1669 InitShellServices(&(This
->hdpaShellServices
));
1673 This
->AutoHideState
= AUTOHIDE_HIDING
;
1674 SetTimer(This
->hWnd
, TIMER_ID_AUTOHIDE
, AUTOHIDE_DELAY_HIDE
, NULL
);
1678 static HRESULT STDMETHODCALLTYPE
1679 ITrayWindowImpl_QueryInterface(IN OUT ITrayWindow
*iface
,
1683 ITrayWindowImpl
*This
;
1688 This
= impl_from_ITrayWindow(iface
);
1690 if (IsEqualIID(riid
,
1693 *ppvObj
= IUnknown_from_impl(This
);
1695 else if (IsEqualIID(riid
,
1696 &IID_IShellDesktopTray
))
1698 *ppvObj
= IShellDesktopTray_from_impl(This
);
1703 return E_NOINTERFACE
;
1706 ITrayWindowImpl_AddRef(iface
);
1710 static ITrayWindowImpl
*
1711 ITrayWindowImpl_Construct(VOID
)
1713 ITrayWindowImpl
*This
;
1715 This
= HeapAlloc(hProcessHeap
,
1721 This
->lpVtbl
= &ITrayWindowImpl_Vtbl
;
1722 This
->lpVtblShellDesktopTray
= &IShellDesktopTrayImpl_Vtbl
;
1724 This
->Position
= (DWORD
)-1;
1729 static HRESULT STDMETHODCALLTYPE
1730 ITrayWindowImpl_Open(IN OUT ITrayWindow
*iface
)
1732 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1737 /* Check if there's already a window created and try to show it.
1738 If it was somehow destroyed just create a new tray window. */
1739 if (This
->hWnd
!= NULL
)
1741 if (IsWindow(This
->hWnd
))
1743 if (!IsWindowVisible(This
->hWnd
))
1745 ITrayWindowImpl_CheckTrayWndPosition(This
);
1747 ShowWindow(This
->hWnd
,
1752 goto TryCreateTrayWnd
;
1759 dwExStyle
= WS_EX_TOOLWINDOW
| WS_EX_WINDOWEDGE
;
1760 if (This
->AlwaysOnTop
)
1761 dwExStyle
|= WS_EX_TOPMOST
;
1763 if (This
->Position
!= (DWORD
)-1)
1764 rcWnd
= This
->rcTrayWnd
[This
->Position
];
1771 hWnd
= CreateWindowEx(dwExStyle
,
1774 WS_POPUP
| WS_VISIBLE
| WS_CLIPSIBLINGS
| WS_CLIPCHILDREN
|
1775 WS_BORDER
| WS_THICKFRAME
,
1778 rcWnd
.right
- rcWnd
.left
,
1779 rcWnd
.bottom
- rcWnd
.top
,
1791 static HRESULT STDMETHODCALLTYPE
1792 ITrayWindowImpl_Close(IN OUT ITrayWindow
*iface
)
1794 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1796 if (This
->hWnd
!= NULL
)
1798 SendMessage(This
->hWnd
,
1807 static HWND STDMETHODCALLTYPE
1808 ITrayWindowImpl_GetHWND(IN OUT ITrayWindow
*iface
)
1810 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1815 static BOOL STDMETHODCALLTYPE
1816 ITrayWindowImpl_IsSpecialHWND(IN OUT ITrayWindow
*iface
,
1819 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1821 return (hWnd
== This
->hWnd
||
1822 (This
->hWndDesktop
!= NULL
&& hWnd
== This
->hWndDesktop
));
1825 static BOOL STDMETHODCALLTYPE
1826 ITrayWindowImpl_IsHorizontal(IN OUT ITrayWindow
*iface
)
1828 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1829 return ITrayWindowImpl_IsPosHorizontal(This
);
1832 static HFONT STDMETHODCALLTYPE
1833 ITrayWIndowImpl_GetCaptionFonts(IN OUT ITrayWindow
*iface
,
1834 OUT HFONT
*phBoldCaption OPTIONAL
)
1836 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1838 if (phBoldCaption
!= NULL
)
1839 *phBoldCaption
= This
->hStartBtnFont
;
1841 return This
->hCaptionFont
;
1845 TrayPropertiesThread(IN OUT PVOID pParam
)
1847 ITrayWindowImpl
*This
= pParam
;
1851 GetWindowRect(This
->hwndStart
, &posRect
);
1852 hwnd
= CreateWindowEx(0,
1855 WS_OVERLAPPED
| WS_DISABLED
| WS_CLIPSIBLINGS
| WS_BORDER
| SS_LEFT
,
1858 posRect
.right
- posRect
.left
,
1859 posRect
.bottom
- posRect
.top
,
1865 This
->hwndTrayPropertiesOwner
= hwnd
;
1867 DisplayTrayProperties(hwnd
);
1869 This
->hwndTrayPropertiesOwner
= NULL
;
1870 DestroyWindow(hwnd
);
1875 static HWND STDMETHODCALLTYPE
1876 ITrayWindowImpl_DisplayProperties(IN OUT ITrayWindow
*iface
)
1878 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1881 if (This
->hwndTrayPropertiesOwner
)
1883 hTrayProp
= GetLastActivePopup(This
->hwndTrayPropertiesOwner
);
1884 if (hTrayProp
!= NULL
&&
1885 hTrayProp
!= This
->hwndTrayPropertiesOwner
)
1887 SetForegroundWindow(hTrayProp
);
1892 CloseHandle(CreateThread(NULL
, 0, TrayPropertiesThread
, This
, 0, NULL
));
1897 OpenCommonStartMenuDirectory(IN HWND hWndOwner
,
1898 IN LPCTSTR lpOperation
)
1900 TCHAR szDir
[MAX_PATH
];
1902 if (SHGetSpecialFolderPath(hWndOwner
,
1904 CSIDL_COMMON_STARTMENU
,
1907 ShellExecute(hWndOwner
,
1917 OpenTaskManager(IN HWND hWndOwner
)
1919 ShellExecute(hWndOwner
,
1921 TEXT("taskmgr.exe"),
1927 static BOOL STDMETHODCALLTYPE
1928 ITrayWindowImpl_ExecContextMenuCmd(IN OUT ITrayWindow
*iface
,
1931 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1932 BOOL bHandled
= TRUE
;
1936 case ID_SHELL_CMD_PROPERTIES
:
1937 ITrayWindow_DisplayProperties(iface
);
1940 case ID_SHELL_CMD_OPEN_ALL_USERS
:
1941 OpenCommonStartMenuDirectory(This
->hWnd
,
1945 case ID_SHELL_CMD_EXPLORE_ALL_USERS
:
1946 OpenCommonStartMenuDirectory(This
->hWnd
,
1950 case ID_LOCKTASKBAR
:
1951 if (SHRestricted(REST_CLASSICSHELL
) == 0)
1953 ITrayWindow_Lock(iface
,
1958 case ID_SHELL_CMD_OPEN_TASKMGR
:
1959 OpenTaskManager(This
->hWnd
);
1962 case ID_SHELL_CMD_UNDO_ACTION
:
1965 case ID_SHELL_CMD_SHOW_DESKTOP
:
1968 case ID_SHELL_CMD_TILE_WND_H
:
1969 TileWindows(NULL
, MDITILE_HORIZONTAL
, NULL
, 0, NULL
);
1972 case ID_SHELL_CMD_TILE_WND_V
:
1973 TileWindows(NULL
, MDITILE_VERTICAL
, NULL
, 0, NULL
);
1976 case ID_SHELL_CMD_CASCADE_WND
:
1977 CascadeWindows(NULL
, MDITILE_SKIPDISABLED
, NULL
, 0, NULL
);
1980 case ID_SHELL_CMD_CUST_NOTIF
:
1983 case ID_SHELL_CMD_ADJUST_DAT
:
1984 LaunchCPanel(NULL
, TEXT("timedate.cpl"));
1988 TRACE("ITrayWindow::ExecContextMenuCmd(%u): Unhandled Command ID!\n", uiCmd
);
1996 static BOOL STDMETHODCALLTYPE
1997 ITrayWindowImpl_Lock(IN OUT ITrayWindow
*iface
,
2001 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
2003 bPrevLock
= This
->Locked
;
2004 if (This
->Locked
!= bLock
)
2006 This
->Locked
= bLock
;
2008 if (This
->TrayBandSite
!= NULL
)
2010 if (!SUCCEEDED(ITrayBandSite_Lock(This
->TrayBandSite
,
2014 This
->Locked
= bPrevLock
;
2022 static const ITrayWindowVtbl ITrayWindowImpl_Vtbl
=
2025 ITrayWindowImpl_QueryInterface
,
2026 ITrayWindowImpl_AddRef
,
2027 ITrayWindowImpl_Release
,
2029 ITrayWindowImpl_Open
,
2030 ITrayWindowImpl_Close
,
2031 ITrayWindowImpl_GetHWND
,
2032 ITrayWindowImpl_IsSpecialHWND
,
2033 ITrayWindowImpl_IsHorizontal
,
2034 ITrayWIndowImpl_GetCaptionFonts
,
2035 ITrayWindowImpl_DisplayProperties
,
2036 ITrayWindowImpl_ExecContextMenuCmd
,
2037 ITrayWindowImpl_Lock
2041 ITrayWindowImpl_DrawBackground(IN ITrayWindowImpl
*This
,
2047 GetClientRect(This
->hWnd
, &rect
);
2048 switch (This
->Position
)
2051 backoundPart
= TBP_BACKGROUNDLEFT
;
2054 backoundPart
= TBP_BACKGROUNDTOP
;
2057 backoundPart
= TBP_BACKGROUNDRIGHT
;
2061 backoundPart
= TBP_BACKGROUNDBOTTOM
;
2064 DrawThemeBackground(This
->TaskbarTheme
, dc
, backoundPart
, 0, &rect
, 0);
2069 ITrayWindowImpl_DrawSizer(IN ITrayWindowImpl
*This
,
2076 GetWindowRect(This
->hWnd
, &rect
);
2077 OffsetRect(&rect
, -rect
.left
, -rect
.top
);
2079 hdc
= GetDCEx(This
->hWnd
, hRgn
, DCX_WINDOW
| DCX_INTERSECTRGN
| DCX_PARENTCLIP
);
2081 switch (This
->Position
)
2084 backoundPart
= TBP_SIZINGBARLEFT
;
2085 rect
.left
= rect
.right
- GetSystemMetrics(SM_CXSIZEFRAME
);
2088 backoundPart
= TBP_SIZINGBARTOP
;
2089 rect
.top
= rect
.bottom
- GetSystemMetrics(SM_CYSIZEFRAME
);
2092 backoundPart
= TBP_SIZINGBARRIGHT
;
2093 rect
.right
= rect
.left
+ GetSystemMetrics(SM_CXSIZEFRAME
);
2097 backoundPart
= TBP_SIZINGBARBOTTOM
;
2098 rect
.bottom
= rect
.top
+ GetSystemMetrics(SM_CYSIZEFRAME
);
2102 DrawThemeBackground(This
->TaskbarTheme
, hdc
, backoundPart
, 0, &rect
, 0);
2104 ReleaseDC(This
->hWnd
, hdc
);
2109 RunFileDlgThread(IN OUT PVOID pParam
)
2111 ITrayWindowImpl
*This
= pParam
;
2113 RUNFILEDLG RunFileDlg
;
2117 GetWindowRect(This
->hwndStart
,&posRect
);
2119 hwnd
= CreateWindowEx(0,
2122 WS_OVERLAPPED
| WS_DISABLED
| WS_CLIPSIBLINGS
| WS_BORDER
| SS_LEFT
,
2125 posRect
.right
- posRect
.left
,
2126 posRect
.bottom
- posRect
.top
,
2132 This
->hwndRunFileDlgOwner
= hwnd
;
2134 hShell32
= GetModuleHandle(TEXT("SHELL32.DLL"));
2135 RunFileDlg
= (RUNFILEDLG
)GetProcAddress(hShell32
, (LPCSTR
)61);
2137 RunFileDlg(hwnd
, NULL
, NULL
, NULL
, NULL
, RFF_CALCDIRECTORY
);
2139 This
->hwndRunFileDlgOwner
= NULL
;
2140 DestroyWindow(hwnd
);
2146 ITrayWindowImpl_DisplayRunFileDlg(IN ITrayWindowImpl
*This
)
2149 if (This
->hwndRunFileDlgOwner
)
2151 hRunDlg
= GetLastActivePopup(This
->hwndRunFileDlgOwner
);
2152 if (hRunDlg
!= NULL
&&
2153 hRunDlg
!= This
->hwndRunFileDlgOwner
)
2155 SetForegroundWindow(hRunDlg
);
2160 CloseHandle(CreateThread(NULL
, 0, RunFileDlgThread
, This
, 0, NULL
));
2163 static void PopupStartMenu(IN ITrayWindowImpl
*This
)
2165 if (This
->StartMenuPopup
!= NULL
)
2171 if (GetWindowRect(This
->hwndStart
,
2172 (RECT
*) &rcExclude
))
2174 switch (This
->Position
)
2177 pt
.x
= rcExclude
.left
;
2178 pt
.y
= rcExclude
.top
;
2179 dwFlags
|= MPPF_BOTTOM
;
2183 pt
.x
= rcExclude
.left
;
2184 pt
.y
= rcExclude
.bottom
;
2185 dwFlags
|= MPPF_TOP
| MPPF_ALIGN_RIGHT
;
2188 pt
.x
= rcExclude
.right
;
2189 pt
.y
= rcExclude
.bottom
;
2190 dwFlags
|= MPPF_TOP
| MPPF_ALIGN_LEFT
;
2194 IMenuPopup_Popup(This
->StartMenuPopup
,
2199 SendMessageW(This
->hwndStart
, BM_SETSTATE
, TRUE
, 0);
2205 ProcessMouseTracking(ITrayWindowImpl
* This
)
2210 UINT state
= This
->AutoHideState
;
2213 GetWindowRect(This
->hWnd
, &rcCurrent
);
2214 over
= PtInRect(&rcCurrent
, pt
);
2216 if (SendMessage(This
->hwndStart
, BM_GETSTATE
, 0, 0) != BST_UNCHECKED
)
2223 if (state
== AUTOHIDE_HIDING
)
2225 TRACE("AutoHide cancelling hide.\n");
2226 This
->AutoHideState
= AUTOHIDE_SHOWING
;
2227 SetTimer(This
->hWnd
, TIMER_ID_AUTOHIDE
, AUTOHIDE_INTERVAL_ANIMATING
, NULL
);
2229 else if (state
== AUTOHIDE_HIDDEN
)
2231 TRACE("AutoHide starting show.\n");
2232 This
->AutoHideState
= AUTOHIDE_SHOWING
;
2233 SetTimer(This
->hWnd
, TIMER_ID_AUTOHIDE
, AUTOHIDE_DELAY_SHOW
, NULL
);
2238 if (state
== AUTOHIDE_SHOWING
)
2240 TRACE("AutoHide cancelling show.\n");
2241 This
->AutoHideState
= AUTOHIDE_HIDING
;
2242 SetTimer(This
->hWnd
, TIMER_ID_AUTOHIDE
, AUTOHIDE_INTERVAL_ANIMATING
, NULL
);
2244 else if (state
== AUTOHIDE_SHOWN
)
2246 TRACE("AutoHide starting hide.\n");
2247 This
->AutoHideState
= AUTOHIDE_HIDING
;
2248 SetTimer(This
->hWnd
, TIMER_ID_AUTOHIDE
, AUTOHIDE_DELAY_HIDE
, NULL
);
2251 KillTimer(This
->hWnd
, TIMER_ID_MOUSETRACK
);
2256 ProcessAutoHide(ITrayWindowImpl
* This
)
2258 RECT rc
= This
->rcTrayWnd
[This
->Position
];
2259 INT w
= This
->TraySize
.cx
- GetSystemMetrics(SM_CXBORDER
) * 2 - 1;
2260 INT h
= This
->TraySize
.cy
- GetSystemMetrics(SM_CYBORDER
) * 2 - 1;
2262 TRACE("AutoHide Timer received for %u, rc=(%d, %d, %d, %d), w=%d, h=%d.\n", This
->AutoHideState
, rc
.left
, rc
.top
, rc
.right
, rc
.bottom
, w
, h
);
2264 switch (This
->AutoHideState
)
2266 case AUTOHIDE_HIDING
:
2267 switch (This
->Position
)
2270 This
->AutoHideOffset
.cy
= 0;
2271 This
->AutoHideOffset
.cx
-= AUTOHIDE_SPEED_HIDE
;
2272 if (This
->AutoHideOffset
.cx
< -w
)
2273 This
->AutoHideOffset
.cx
= -w
;
2276 This
->AutoHideOffset
.cx
= 0;
2277 This
->AutoHideOffset
.cy
-= AUTOHIDE_SPEED_HIDE
;
2278 if (This
->AutoHideOffset
.cy
< -h
)
2279 This
->AutoHideOffset
.cy
= -h
;
2282 This
->AutoHideOffset
.cy
= 0;
2283 This
->AutoHideOffset
.cx
+= AUTOHIDE_SPEED_HIDE
;
2284 if (This
->AutoHideOffset
.cx
> w
)
2285 This
->AutoHideOffset
.cx
= w
;
2288 This
->AutoHideOffset
.cx
= 0;
2289 This
->AutoHideOffset
.cy
+= AUTOHIDE_SPEED_HIDE
;
2290 if (This
->AutoHideOffset
.cy
> h
)
2291 This
->AutoHideOffset
.cy
= h
;
2295 if (This
->AutoHideOffset
.cx
!= w
&& This
->AutoHideOffset
.cy
!= h
)
2297 SetTimer(This
->hWnd
, TIMER_ID_AUTOHIDE
, AUTOHIDE_INTERVAL_ANIMATING
, NULL
);
2302 case AUTOHIDE_HIDDEN
:
2304 switch (This
->Position
)
2307 This
->AutoHideOffset
.cx
= -w
;
2308 This
->AutoHideOffset
.cy
= 0;
2311 This
->AutoHideOffset
.cx
= 0;
2312 This
->AutoHideOffset
.cy
= -h
;
2315 This
->AutoHideOffset
.cx
= w
;
2316 This
->AutoHideOffset
.cy
= 0;
2319 This
->AutoHideOffset
.cx
= 0;
2320 This
->AutoHideOffset
.cy
= h
;
2324 KillTimer(This
->hWnd
, TIMER_ID_AUTOHIDE
);
2325 This
->AutoHideState
= AUTOHIDE_HIDDEN
;
2328 case AUTOHIDE_SHOWING
:
2329 if (This
->AutoHideOffset
.cx
>= AUTOHIDE_SPEED_SHOW
)
2331 This
->AutoHideOffset
.cx
-= AUTOHIDE_SPEED_SHOW
;
2333 else if (This
->AutoHideOffset
.cx
<= -AUTOHIDE_SPEED_SHOW
)
2335 This
->AutoHideOffset
.cx
+= AUTOHIDE_SPEED_SHOW
;
2339 This
->AutoHideOffset
.cx
= 0;
2342 if (This
->AutoHideOffset
.cy
>= AUTOHIDE_SPEED_SHOW
)
2344 This
->AutoHideOffset
.cy
-= AUTOHIDE_SPEED_SHOW
;
2346 else if (This
->AutoHideOffset
.cy
<= -AUTOHIDE_SPEED_SHOW
)
2348 This
->AutoHideOffset
.cy
+= AUTOHIDE_SPEED_SHOW
;
2352 This
->AutoHideOffset
.cy
= 0;
2355 if (This
->AutoHideOffset
.cx
!= 0 || This
->AutoHideOffset
.cy
!= 0)
2357 SetTimer(This
->hWnd
, TIMER_ID_AUTOHIDE
, AUTOHIDE_INTERVAL_ANIMATING
, NULL
);
2362 case AUTOHIDE_SHOWN
:
2364 KillTimer(This
->hWnd
, TIMER_ID_AUTOHIDE
);
2365 This
->AutoHideState
= AUTOHIDE_SHOWN
;
2369 rc
.left
+= This
->AutoHideOffset
.cx
;
2370 rc
.right
+= This
->AutoHideOffset
.cx
;
2371 rc
.top
+= This
->AutoHideOffset
.cy
;
2372 rc
.bottom
+= This
->AutoHideOffset
.cy
;
2374 TRACE("AutoHide Changing position to (%d, %d, %d, %d) and state=%u.\n", rc
.left
, rc
.top
, rc
.right
, rc
.bottom
, This
->AutoHideState
);
2375 SetWindowPos(This
->hWnd
, NULL
, rc
.left
, rc
.top
, rc
.right
- rc
.left
, rc
.bottom
- rc
.top
, SWP_NOACTIVATE
| SWP_NOZORDER
);
2378 static LRESULT CALLBACK
2379 TrayWndProc(IN HWND hwnd
,
2384 ITrayWindowImpl
*This
= NULL
;
2385 LRESULT Ret
= FALSE
;
2387 if (uMsg
!= WM_NCCREATE
)
2389 This
= (ITrayWindowImpl
*)GetWindowLongPtr(hwnd
,
2393 if (This
!= NULL
|| uMsg
== WM_NCCREATE
)
2395 if (This
!= NULL
&& This
->StartMenuBand
!= NULL
)
2402 Msg
.wParam
= wParam
;
2403 Msg
.lParam
= lParam
;
2405 if (IMenuBand_TranslateMenuMessage(This
->StartMenuBand
,
2412 wParam
= Msg
.wParam
;
2413 lParam
= Msg
.lParam
;
2418 case WM_DISPLAYCHANGE
:
2420 /* Load the saved tray window settings */
2421 ITrayWindowImpl_RegLoadSettings(This
);
2423 /* Move the tray window to the right position and resize it if neccessary */
2424 ITrayWindowImpl_CheckTrayWndPosition(This
);
2426 /* Align all controls on the tray window */
2427 ITrayWindowImpl_AlignControls(This
, NULL
);
2433 if (This
->hwndTrayNotify
)
2435 TrayNotify_NotifyMsg(This
->hwndTrayNotify
,
2441 case WM_THEMECHANGED
:
2442 ITrayWindowImpl_UpdateTheme(This
);
2445 if (!This
->TaskbarTheme
)
2447 return ITrayWindowImpl_DrawSizer(This
,
2450 if (!This
->TaskbarTheme
)
2452 return ITrayWindowImpl_DrawBackground(This
, (HDC
)wParam
);
2453 case WM_CTLCOLORBTN
:
2454 SetBkMode((HDC
)wParam
, TRANSPARENT
);
2455 return (LRESULT
)GetStockObject(HOLLOW_BRUSH
);
2463 /* The user may not be able to resize the tray window.
2464 Pretend like the window is not sizeable when the user
2465 clicks on the border. */
2469 SetLastError(ERROR_SUCCESS
);
2470 if (GetClientRect(hwnd
,
2472 (MapWindowPoints(hwnd
,
2475 2) != 0 || GetLastError() == ERROR_SUCCESS
))
2477 pt
.x
= (SHORT
)LOWORD(lParam
);
2478 pt
.y
= (SHORT
)HIWORD(lParam
);
2480 if (PtInRect(&rcClient
,
2483 /* The user is trying to drag the tray window */
2487 /* Depending on the position of the tray window, allow only
2488 changing the border next to the monitor working area */
2489 switch (This
->Position
)
2492 if (pt
.y
> rcClient
.bottom
)
2496 if (pt
.x
> rcClient
.right
)
2500 if (pt
.x
< rcClient
.left
)
2505 if (pt
.y
< rcClient
.top
)
2515 PRECT pRect
= (PRECT
)lParam
;
2517 /* We need to ensure that an application can not accidently
2518 move the tray window (using SetWindowPos). However, we still
2519 need to be able to move the window in case the user wants to
2520 drag the tray window to another position or in case the user
2521 wants to resize the tray window. */
2522 if (!This
->Locked
&& GetCursorPos(&ptCursor
))
2524 This
->IsDragging
= TRUE
;
2525 This
->DraggingPosition
= ITrayWindowImpl_GetDraggingRectFromPt(This
,
2528 &This
->DraggingMonitor
);
2532 *pRect
= This
->rcTrayWnd
[This
->Position
];
2536 pRect
->left
+= This
->AutoHideOffset
.cx
;
2537 pRect
->right
+= This
->AutoHideOffset
.cx
;
2538 pRect
->top
+= This
->AutoHideOffset
.cy
;
2539 pRect
->bottom
+= This
->AutoHideOffset
.cy
;
2547 PRECT pRect
= (PRECT
)lParam
;
2551 ITrayWindowImpl_CalculateValidSize(This
,
2557 *pRect
= This
->rcTrayWnd
[This
->Position
];
2561 pRect
->left
+= This
->AutoHideOffset
.cx
;
2562 pRect
->right
+= This
->AutoHideOffset
.cx
;
2563 pRect
->top
+= This
->AutoHideOffset
.cy
;
2564 pRect
->bottom
+= This
->AutoHideOffset
.cy
;
2570 case WM_WINDOWPOSCHANGING
:
2572 ITrayWindowImpl_ChangingWinPos(This
,
2573 (LPWINDOWPOS
)lParam
);
2580 InvalidateRect(This
->hWnd
, NULL
, TRUE
);
2581 if (wParam
== SIZE_RESTORED
&& lParam
== 0)
2583 ITrayWindowImpl_ResizeWorkArea(This
);
2584 /* Clip the tray window on multi monitor systems so the edges can't
2585 overlap into another monitor */
2586 ITrayWindowImpl_ApplyClipping(This
,
2589 if (!GetClientRect(This
->hWnd
,
2597 rcClient
.left
= rcClient
.top
= 0;
2598 rcClient
.right
= LOWORD(lParam
);
2599 rcClient
.bottom
= HIWORD(lParam
);
2602 ITrayWindowImpl_AlignControls(This
,
2607 case WM_ENTERSIZEMOVE
:
2608 This
->InSizeMove
= TRUE
;
2609 This
->IsDragging
= FALSE
;
2612 /* Remove the clipping on multi monitor systems while dragging around */
2613 ITrayWindowImpl_ApplyClipping(This
,
2618 case WM_EXITSIZEMOVE
:
2619 This
->InSizeMove
= FALSE
;
2622 /* Apply clipping */
2623 PostMessage(This
->hWnd
,
2635 /* The user pressed Alt+Space, this usually brings up the system menu of a window.
2636 The tray window needs to handle this specially, since it normally doesn't have
2639 static const UINT uidDisableItem
[] = {
2650 /* temporarily enable the system menu */
2651 SetWindowStyle(hwnd
,
2655 hSysMenu
= GetSystemMenu(hwnd
,
2657 if (hSysMenu
!= NULL
)
2659 /* Disable all items that are not relevant */
2660 for (i
= 0; i
!= sizeof(uidDisableItem
) / sizeof(uidDisableItem
[0]); i
++)
2662 EnableMenuItem(hSysMenu
,
2664 MF_BYCOMMAND
| MF_GRAYED
);
2667 EnableMenuItem(hSysMenu
,
2670 (SHRestricted(REST_NOCLOSE
) ? MF_GRAYED
: MF_ENABLED
));
2672 /* Display the system menu */
2673 uId
= ITrayWindowImpl_TrackMenu(This
,
2677 This
->Position
!= ABE_TOP
,
2681 SendMessage(This
->hWnd
,
2688 /* revert the system menu window style */
2689 SetWindowStyle(hwnd
,
2700 case WM_NCRBUTTONUP
:
2701 /* We want the user to be able to get a context menu even on the nonclient
2702 area (including the sizing border)! */
2703 uMsg
= WM_CONTEXTMENU
;
2704 wParam
= (WPARAM
)hwnd
;
2707 case WM_CONTEXTMENU
:
2709 POINT pt
, *ppt
= NULL
;
2710 HWND hWndExclude
= NULL
;
2712 /* Check if the administrator has forbidden access to context menus */
2713 if (SHRestricted(REST_NOTRAYCONTEXTMENU
))
2716 pt
.x
= (SHORT
)LOWORD(lParam
);
2717 pt
.y
= (SHORT
)HIWORD(lParam
);
2719 if (pt
.x
!= -1 || pt
.y
!= -1)
2722 hWndExclude
= This
->hwndStart
;
2724 if ((HWND
)wParam
== This
->hwndStart
)
2726 /* Make sure we can't track the context menu if the start
2727 menu is currently being shown */
2728 if (!(SendMessage(This
->hwndStart
,
2733 ITrayWindowImpl_TrackCtxMenu(This
,
2734 &StartMenuBtnCtxMenu
,
2737 This
->Position
== ABE_BOTTOM
,
2743 /* See if the context menu should be handled by the task band site */
2744 if (ppt
!= NULL
&& This
->TrayBandSite
!= NULL
)
2747 POINT ptClient
= *ppt
;
2749 /* Convert the coordinates to client-coordinates */
2750 MapWindowPoints(NULL
,
2755 hWndAtPt
= ChildWindowFromPoint(This
->hWnd
,
2757 if (hWndAtPt
!= NULL
&&
2758 (hWndAtPt
== This
->hwndRebar
|| IsChild(This
->hwndRebar
,
2761 /* Check if the user clicked on the task switch window */
2763 MapWindowPoints(NULL
,
2768 hWndAtPt
= ChildWindowFromPointEx(This
->hwndRebar
,
2770 CWP_SKIPINVISIBLE
| CWP_SKIPDISABLED
);
2771 if (hWndAtPt
== This
->hwndTaskSwitch
)
2772 goto HandleTrayContextMenu
;
2774 /* Forward the message to the task band site */
2775 ITrayBandSite_ProcessMessage(This
->TrayBandSite
,
2783 goto HandleTrayContextMenu
;
2787 HandleTrayContextMenu
:
2788 /* Tray the default tray window context menu */
2789 ITrayWindowImpl_TrackCtxMenu(This
,
2802 /* FIXME: We can't check with IsChild whether the hwnd is somewhere inside
2803 the rebar control! But we shouldn't forward messages that the band
2804 site doesn't handle, such as other controls (start button, tray window */
2805 if (This
->TrayBandSite
== NULL
||
2806 !SUCCEEDED(ITrayBandSite_ProcessMessage(This
->TrayBandSite
,
2813 const NMHDR
*nmh
= (const NMHDR
*)lParam
;
2815 if (nmh
->hwndFrom
== This
->hwndTrayNotify
)
2820 /* Cause all controls to be aligned */
2821 PostMessage(This
->hWnd
,
2832 case WM_NCLBUTTONDBLCLK
:
2834 /* We "handle" this message so users can't cause a weird maximize/restore
2835 window animation when double-clicking the tray window! */
2837 /* We should forward mouse messages to child windows here.
2838 Right now, this is only clock double-click */
2840 if (TrayNotify_GetClockRect(This
->hwndTrayNotify
, &rcClock
))
2843 ptClick
.x
= MAKEPOINTS(lParam
).x
;
2844 ptClick
.y
= MAKEPOINTS(lParam
).y
;
2845 if (PtInRect(&rcClock
, ptClick
))
2846 LaunchCPanel(NULL
, TEXT("timedate.cpl"));
2853 LPCREATESTRUCT CreateStruct
= (LPCREATESTRUCT
)lParam
;
2854 This
= (ITrayWindowImpl
*)CreateStruct
->lpCreateParams
;
2856 if (InterlockedCompareExchangePointer((PVOID
*)&This
->hWnd
,
2860 /* Somebody else was faster... */
2864 SetWindowLongPtr(hwnd
,
2868 return ITrayWindowImpl_NCCreate(This
);
2872 ITrayWindowImpl_Create(This
);
2876 ITrayWindowImpl_Destroy(This
);
2879 case WM_APP_TRAYDESTROY
:
2880 DestroyWindow(hwnd
);
2883 case TWM_OPENSTARTMENU
:
2886 HRESULT hr
= IUnknown_GetWindow((IUnknown
*)This
->StartMenuPopup
, &hwndStartMenu
);
2890 if (IsWindowVisible(hwndStartMenu
))
2892 IMenuPopup_OnSelect(This
->StartMenuPopup
, MPOS_CANCELLEVEL
);
2896 SendMessage(This
->hWnd
, WM_COMMAND
, MAKEWPARAM(BN_CLICKED
, IDC_STARTBTN
), (LPARAM
)This
->hwndStart
);
2902 if ((HWND
)lParam
== This
->hwndStart
)
2904 PopupStartMenu(This
);
2908 if (This
->TrayBandSite
== NULL
||
2909 FAILED(ITrayBandSite_ProcessMessage(This
->TrayBandSite
,
2916 switch (LOWORD(wParam
))
2918 /* FIXME: Handle these commands as well */
2919 case IDM_TASKBARANDSTARTMENU
:
2921 ITrayWindowImpl_DisplayProperties(ITrayWindow_from_impl(This
));
2927 case IDM_HELPANDSUPPORT
:
2929 /* TODO: Implement properly */
2931 LPCWSTR strSite
= L
"https://www.reactos.org/";
2933 /* TODO: Make localizable */
2934 LPCWSTR strCaption
= L
"Sorry";
2935 LPCWSTR strMessage
= L
"ReactOS could not browse to '%s' (error %d). Please make sure there is a web browser installed.";
2936 WCHAR tmpMessage
[512];
2938 /* TODO: Read from the registry */
2939 LPCWSTR strVerb
= NULL
; /* default */
2940 LPCWSTR strPath
= strSite
;
2941 LPCWSTR strParams
= NULL
;
2943 /* The return value is defined as HINSTANCE for backwards compatibility only, the cast is needed */
2944 int result
= (int) ShellExecuteW(hwnd
, strVerb
, strPath
, strParams
, NULL
, SW_SHOWNORMAL
);
2947 StringCchPrintfW(tmpMessage
, 512, strMessage
, strSite
, result
);
2948 MessageBoxExW(hwnd
, tmpMessage
, strCaption
, MB_OK
, 0);
2955 ITrayWindowImpl_DisplayRunFileDlg(This
);
2959 /* FIXME: Handle these commands as well */
2960 case IDM_SYNCHRONIZE
:
2962 case IDM_DISCONNECT
:
2963 case IDM_UNDOCKCOMPUTER
:
2969 EXITWINDLG ExitWinDlg
;
2971 hShell32
= GetModuleHandle(TEXT("SHELL32.DLL"));
2972 ExitWinDlg
= (EXITWINDLG
)GetProcAddress(hShell32
, (LPCSTR
)60);
2982 case WM_NCMOUSEMOVE
:
2986 SetTimer(This
->hWnd
, TIMER_ID_MOUSETRACK
, MOUSETRACK_INTERVAL
, NULL
);
2991 if (wParam
== TIMER_ID_MOUSETRACK
)
2993 ProcessMouseTracking(This
);
2995 else if (wParam
== TIMER_ID_AUTOHIDE
)
2997 ProcessAutoHide(This
);
3009 Ret
= DefWindowProc(hwnd
,
3019 * Tray Window Context Menu
3023 CreateTrayWindowContextMenu(IN HWND hWndOwner
,
3024 IN PVOID
*ppcmContext
,
3025 IN PVOID Context OPTIONAL
)
3027 ITrayWindowImpl
*This
= (ITrayWindowImpl
*)Context
;
3028 IContextMenu
*pcm
= NULL
;
3031 hPopup
= LoadPopupMenu(hExplorerInstance
,
3032 MAKEINTRESOURCE(IDM_TRAYWND
));
3036 if (SHRestricted(REST_CLASSICSHELL
) != 0)
3043 CheckMenuItem(hPopup
,
3045 MF_BYCOMMAND
| (This
->Locked
? MF_CHECKED
: MF_UNCHECKED
));
3047 if (This
->TrayBandSite
!= NULL
)
3049 if (SUCCEEDED(ITrayBandSite_AddContextMenus(This
->TrayBandSite
,
3057 TRACE("ITrayBandSite::AddContextMenus succeeded!\n");
3058 *(IContextMenu
**)ppcmContext
= pcm
;
3067 OnTrayWindowContextMenuCommand(IN HWND hWndOwner
,
3069 IN PVOID pcmContext OPTIONAL
,
3070 IN PVOID Context OPTIONAL
)
3072 ITrayWindowImpl
*This
= (ITrayWindowImpl
*)Context
;
3073 IContextMenu
*pcm
= (IContextMenu
*)pcmContext
;
3077 if (uiCmdId
>= ID_SHELL_CMD_FIRST
&& uiCmdId
<= ID_SHELL_CMD_LAST
)
3079 CMINVOKECOMMANDINFO cmici
= {0};
3083 /* Setup and invoke the shell command */
3084 cmici
.cbSize
= sizeof(cmici
);
3085 cmici
.hwnd
= hWndOwner
;
3086 cmici
.lpVerb
= (LPCSTR
)MAKEINTRESOURCE(uiCmdId
- ID_SHELL_CMD_FIRST
);
3087 cmici
.nShow
= SW_NORMAL
;
3089 IContextMenu_InvokeCommand(pcm
,
3095 ITrayWindow_ExecContextMenuCmd(ITrayWindow_from_impl(This
),
3101 IContextMenu_Release(pcm
);
3104 static const TRAYWINDOW_CTXMENU TrayWindowCtxMenu
= {
3105 CreateTrayWindowContextMenu
,
3106 OnTrayWindowContextMenuCommand
3109 /*****************************************************************************/
3112 RegisterTrayWindowClass(VOID
)
3117 if (!RegisterTrayNotifyWndClass())
3120 wcTrayWnd
.style
= CS_DBLCLKS
;
3121 wcTrayWnd
.lpfnWndProc
= TrayWndProc
;
3122 wcTrayWnd
.cbClsExtra
= 0;
3123 wcTrayWnd
.cbWndExtra
= sizeof(ITrayWindowImpl
*);
3124 wcTrayWnd
.hInstance
= hExplorerInstance
;
3125 wcTrayWnd
.hIcon
= NULL
;
3126 wcTrayWnd
.hCursor
= LoadCursor(NULL
,
3128 wcTrayWnd
.hbrBackground
= (HBRUSH
)(COLOR_3DFACE
+ 1);
3129 wcTrayWnd
.lpszMenuName
= NULL
;
3130 wcTrayWnd
.lpszClassName
= szTrayWndClass
;
3132 Ret
= RegisterClass(&wcTrayWnd
) != 0;
3135 UnregisterTrayNotifyWndClass();
3141 UnregisterTrayWindowClass(VOID
)
3143 UnregisterTrayNotifyWndClass();
3145 UnregisterClass(szTrayWndClass
,
3150 CreateTrayWindow(VOID
)
3152 ITrayWindowImpl
*This
;
3153 ITrayWindow
*TrayWindow
;
3155 This
= ITrayWindowImpl_Construct();
3158 TrayWindow
= ITrayWindow_from_impl(This
);
3160 ITrayWindowImpl_Open(TrayWindow
);
3162 g_TrayWindow
= This
;
3171 TrayProcessMessages(IN OUT ITrayWindow
*Tray
)
3173 ITrayWindowImpl
*This
;
3176 This
= impl_from_ITrayWindow(Tray
);
3178 /* FIXME: We should keep a reference here... */
3180 while (PeekMessage(&Msg
,
3186 if (Msg
.message
== WM_QUIT
)
3189 if (This
->StartMenuBand
== NULL
||
3190 IMenuBand_IsMenuMessage(This
->StartMenuBand
,
3193 TranslateMessage(&Msg
);
3194 DispatchMessage(&Msg
);
3200 TrayMessageLoop(IN OUT ITrayWindow
*Tray
)
3202 ITrayWindowImpl
*This
;
3206 This
= impl_from_ITrayWindow(Tray
);
3208 /* FIXME: We should keep a reference here... */
3212 Ret
= GetMessage(&Msg
,
3217 if (!Ret
|| Ret
== -1)
3220 if (Msg
.message
== WM_HOTKEY
)
3224 case IDHK_RUN
: /* Win+R */
3225 ITrayWindowImpl_DisplayRunFileDlg(This
);
3230 if (This
->StartMenuBand
== NULL
||
3231 IMenuBand_IsMenuMessage(This
->StartMenuBand
,
3234 TranslateMessage(&Msg
);
3235 DispatchMessage(&Msg
);
3243 * NOTE: This is a very windows-specific COM interface used by SHCreateDesktop()!
3244 * These are the calls I observed, it may be wrong/incomplete/buggy!!!
3245 * The reason we implement it is because we have to use SHCreateDesktop() so
3246 * that the shell provides the desktop window and all the features that come
3247 * with it (especially positioning of desktop icons)
3250 static HRESULT STDMETHODCALLTYPE
3251 ITrayWindowImpl_IShellDesktopTray_QueryInterface(IN OUT IShellDesktopTray
*iface
,
3255 ITrayWindowImpl
*This
= impl_from_IShellDesktopTray(iface
);
3256 ITrayWindow
*tray
= ITrayWindow_from_impl(This
);
3258 TRACE("IShellDesktopTray::QueryInterface(0x%p, 0x%p)\n", riid
, ppvObj
);
3259 return ITrayWindowImpl_QueryInterface(tray
,
3264 static ULONG STDMETHODCALLTYPE
3265 ITrayWindowImpl_IShellDesktopTray_Release(IN OUT IShellDesktopTray
*iface
)
3267 ITrayWindowImpl
*This
= impl_from_IShellDesktopTray(iface
);
3268 ITrayWindow
*tray
= ITrayWindow_from_impl(This
);
3270 TRACE("IShellDesktopTray::Release()\n");
3271 return ITrayWindowImpl_Release(tray
);
3274 static ULONG STDMETHODCALLTYPE
3275 ITrayWindowImpl_IShellDesktopTray_AddRef(IN OUT IShellDesktopTray
*iface
)
3277 ITrayWindowImpl
*This
= impl_from_IShellDesktopTray(iface
);
3278 ITrayWindow
*tray
= ITrayWindow_from_impl(This
);
3280 TRACE("IShellDesktopTray::AddRef()\n");
3281 return ITrayWindowImpl_AddRef(tray
);
3284 static ULONG STDMETHODCALLTYPE
3285 ITrayWindowImpl_IShellDesktopTray_GetState(IN OUT IShellDesktopTray
*iface
)
3287 /* FIXME: Return ABS_ flags? */
3288 TRACE("IShellDesktopTray::GetState() unimplemented!\n");
3292 static HRESULT STDMETHODCALLTYPE
3293 ITrayWindowImpl_IShellDesktopTray_GetTrayWindow(IN OUT IShellDesktopTray
*iface
,
3294 OUT HWND
*phWndTray
)
3296 ITrayWindowImpl
*This
= impl_from_IShellDesktopTray(iface
);
3297 TRACE("IShellDesktopTray::GetTrayWindow(0x%p)\n", phWndTray
);
3298 *phWndTray
= This
->hWnd
;
3302 static HRESULT STDMETHODCALLTYPE
3303 ITrayWindowImpl_IShellDesktopTray_RegisterDesktopWindow(IN OUT IShellDesktopTray
*iface
,
3304 IN HWND hWndDesktop
)
3306 ITrayWindowImpl
*This
= impl_from_IShellDesktopTray(iface
);
3307 TRACE("IShellDesktopTray::RegisterDesktopWindow(0x%p)\n", hWndDesktop
);
3309 This
->hWndDesktop
= hWndDesktop
;
3313 static HRESULT STDMETHODCALLTYPE
3314 ITrayWindowImpl_IShellDesktopTray_Unknown(IN OUT IShellDesktopTray
*iface
,
3315 IN DWORD dwUnknown1
,
3316 IN DWORD dwUnknown2
)
3318 TRACE("IShellDesktopTray::Unknown(%u,%u) unimplemented!\n", dwUnknown1
, dwUnknown2
);
3322 static const IShellDesktopTrayVtbl IShellDesktopTrayImpl_Vtbl
=
3325 ITrayWindowImpl_IShellDesktopTray_QueryInterface
,
3326 ITrayWindowImpl_IShellDesktopTray_AddRef
,
3327 ITrayWindowImpl_IShellDesktopTray_Release
,
3328 /*** IShellDesktopTray ***/
3329 ITrayWindowImpl_IShellDesktopTray_GetState
,
3330 ITrayWindowImpl_IShellDesktopTray_GetTrayWindow
,
3331 ITrayWindowImpl_IShellDesktopTray_RegisterDesktopWindow
,
3332 ITrayWindowImpl_IShellDesktopTray_Unknown
3336 ITrayWindowImpl_RaiseStartButton(ITrayWindowImpl
* This
)
3338 SendMessageW(This
->hwndStart
, BM_SETSTATE
, FALSE
, 0);
3343 Tray_OnStartMenuDismissed()
3345 return ITrayWindowImpl_RaiseStartButton(g_TrayWindow
);