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 static const TRAYWINDOW_CTXMENU TrayWindowCtxMenu
;
25 #define WM_APP_TRAYDESTROY (WM_APP + 0x100)
27 static LONG TrayWndCount
= 0;
29 static const TCHAR szTrayWndClass
[] = TEXT("Shell_TrayWnd");
31 static const ITrayWindowVtbl ITrayWindowImpl_Vtbl
;
32 static const IShellDesktopTrayVtbl IShellDesktopTrayImpl_Vtbl
;
38 const GUID IID_IShellDesktopTray
= {0x213e2df9, 0x9a14, 0x4328, {0x99, 0xb1, 0x69, 0x61, 0xf9, 0x14, 0x3c, 0xe9}};
42 const ITrayWindowVtbl
*lpVtbl
;
43 const IShellDesktopTrayVtbl
*lpVtblShellDesktopTray
;
50 HIMAGELIST himlStartBtn
;
55 ITrayBandSite
*TrayBandSite
;
62 HMONITOR PreviousMonitor
;
63 DWORD DraggingPosition
;
64 HMONITOR DraggingMonitor
;
75 DWORD AlwaysOnTop
: 1;
76 DWORD SmSmallIcons
: 1;
90 IMenuBand
*StartMenuBand
;
91 IMenuPopup
*StartMenuPopup
;
94 HWND hWndTrayProperties
;
98 IUnknown_from_impl(ITrayWindowImpl
*This
)
100 return (IUnknown
*)&This
->lpVtbl
;
104 ITrayWindow_from_impl(ITrayWindowImpl
*This
)
106 return (ITrayWindow
*)&This
->lpVtbl
;
109 static IShellDesktopTray
*
110 IShellDesktopTray_from_impl(ITrayWindowImpl
*This
)
112 return (IShellDesktopTray
*)&This
->lpVtblShellDesktopTray
;
115 static ITrayWindowImpl
*
116 impl_from_ITrayWindow(ITrayWindow
*iface
)
118 return (ITrayWindowImpl
*)((ULONG_PTR
)iface
- FIELD_OFFSET(ITrayWindowImpl
,
122 static ITrayWindowImpl
*
123 impl_from_IShellDesktopTray(IShellDesktopTray
*iface
)
125 return (ITrayWindowImpl
*)((ULONG_PTR
)iface
- FIELD_OFFSET(ITrayWindowImpl
,
126 lpVtblShellDesktopTray
));
134 ITrayWindowImpl_UpdateNonClientMetrics(IN OUT ITrayWindowImpl
*This
)
136 This
->ncm
.cbSize
= sizeof(This
->ncm
);
137 if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS
,
142 if (This
->hFont
!= NULL
)
143 DeleteObject(This
->hFont
);
145 This
->hFont
= CreateFontIndirect(&This
->ncm
.lfMessageFont
);
153 ITrayWindowImpl_SetWindowsFont(IN OUT ITrayWindowImpl
*This
)
155 if (This
->hwndTrayNotify
!= NULL
)
157 SendMessage(This
->hwndTrayNotify
,
165 ITrayWindowImpl_GetScreenRectFromRect(IN OUT ITrayWindowImpl
*This
,
172 mi
.cbSize
= sizeof(mi
);
173 hMon
= MonitorFromRect(pRect
,
179 *pRect
= mi
.rcMonitor
;
185 pRect
->right
= GetSystemMetrics(SM_CXSCREEN
);
186 pRect
->bottom
= GetSystemMetrics(SM_CYSCREEN
);
195 ITrayWindowImpl_GetMonitorFromRect(IN OUT ITrayWindowImpl
*This
,
196 IN
const RECT
*pRect
)
200 /* In case the monitor sizes or saved sizes differ a bit (probably
201 not a lot, only so the tray window overlaps into another monitor
202 now), minimize the risk that we determine a wrong monitor by
203 using the center point of the tray window if we can't determine
204 it using the rectangle. */
205 hMon
= MonitorFromRect(pRect
,
206 MONITOR_DEFAULTTONULL
);
211 pt
.x
= pRect
->left
+ ((pRect
->right
- pRect
->left
) / 2);
212 pt
.y
= pRect
->top
+ ((pRect
->bottom
- pRect
->top
) / 2);
214 /* be less error-prone, find the nearest monitor */
215 hMon
= MonitorFromPoint(pt
,
216 MONITOR_DEFAULTTONEAREST
);
223 ITrayWindowImpl_GetScreenRect(IN OUT ITrayWindowImpl
*This
,
224 IN HMONITOR hMonitor
,
227 HMONITOR hMon
= NULL
;
229 if (hMonitor
!= NULL
)
233 mi
.cbSize
= sizeof(mi
);
234 if (!GetMonitorInfo(hMonitor
,
237 /* Hm, the monitor is gone? Try to find a monitor where it
238 could be located now */
239 hMon
= ITrayWindowImpl_GetMonitorFromRect(This
,
242 !GetMonitorInfo(hMon
,
250 *pRect
= mi
.rcMonitor
;
257 pRect
->right
= GetSystemMetrics(SM_CXSCREEN
);
258 pRect
->bottom
= GetSystemMetrics(SM_CYSCREEN
);
265 ITrayWindowImpl_MakeTrayRectWithSize(IN DWORD Position
,
266 IN
const SIZE
*pTraySize
,
272 pRect
->right
= pRect
->left
+ pTraySize
->cx
;
276 pRect
->bottom
= pRect
->top
+ pTraySize
->cy
;
280 pRect
->left
= pRect
->right
- pTraySize
->cx
;
285 pRect
->top
= pRect
->bottom
- pTraySize
->cy
;
291 ITrayWindowImpl_GetTrayRectFromScreenRect(IN OUT ITrayWindowImpl
*This
,
293 IN
const RECT
*pScreen
,
294 IN
const SIZE
*pTraySize OPTIONAL
,
297 if (pTraySize
== NULL
)
298 pTraySize
= &This
->TraySize
;
302 /* Move the border outside of the screen */
304 GetSystemMetrics(SM_CXEDGE
),
305 GetSystemMetrics(SM_CYEDGE
));
307 ITrayWindowImpl_MakeTrayRectWithSize(Position
,
313 ITrayWindowImpl_IsPosHorizontal(IN OUT ITrayWindowImpl
*This
)
315 return This
->Position
== ABE_TOP
|| This
->Position
== ABE_BOTTOM
;
319 ITrayWindowImpl_CalculateValidSize(IN OUT ITrayWindowImpl
*This
,
328 //Horizontal = ITrayWindowImpl_IsPosHorizontal(This);
330 szWnd
.cx
= pRect
->right
- pRect
->left
;
331 szWnd
.cy
= pRect
->bottom
- pRect
->top
;
334 hMon
= ITrayWindowImpl_GetScreenRectFromRect(This
,
336 MONITOR_DEFAULTTONEAREST
);
338 /* Calculate the maximum size of the tray window and limit the window
339 size to half of the screen's size. */
340 szMax
.cx
= (rcScreen
.right
- rcScreen
.left
) / 2;
341 szMax
.cy
= (rcScreen
.bottom
- rcScreen
.top
) / 2;
342 if (szWnd
.cx
> szMax
.cx
)
344 if (szWnd
.cy
> szMax
.cy
)
347 /* FIXME - calculate */
349 ITrayWindowImpl_GetTrayRectFromScreenRect(This
,
360 ITrayWindowImpl_GetMinimumWindowSize(IN OUT ITrayWindowImpl
*This
,
365 AdjustWindowRectEx(&rcMin
,
366 GetWindowLongPtr(This
->hWnd
,
369 GetWindowLongPtr(This
->hWnd
,
378 ITrayWindowImpl_GetDraggingRectFromPt(IN OUT ITrayWindowImpl
*This
,
381 OUT HMONITOR
*phMonitor
)
383 HMONITOR hMon
, hMonNew
;
384 DWORD PosH
, PosV
, Pos
;
385 SIZE DeltaPt
, ScreenOffset
;
391 /* Determine the screen rectangle */
392 hMon
= MonitorFromPoint(pt
,
393 MONITOR_DEFAULTTONULL
);
399 mi
.cbSize
= sizeof(mi
);
400 if (!GetMonitorInfo(hMon
,
404 goto GetPrimaryScreenRect
;
407 /* make left top corner of the screen zero based to
408 make calculations easier */
409 pt
.x
-= mi
.rcMonitor
.left
;
410 pt
.y
-= mi
.rcMonitor
.top
;
412 ScreenOffset
.cx
= mi
.rcMonitor
.left
;
413 ScreenOffset
.cy
= mi
.rcMonitor
.top
;
414 rcScreen
.right
= mi
.rcMonitor
.right
- mi
.rcMonitor
.left
;
415 rcScreen
.bottom
= mi
.rcMonitor
.bottom
- mi
.rcMonitor
.top
;
419 GetPrimaryScreenRect
:
422 rcScreen
.right
= GetSystemMetrics(SM_CXSCREEN
);
423 rcScreen
.bottom
= GetSystemMetrics(SM_CYSCREEN
);
426 /* Calculate the nearest screen border */
427 if (pt
.x
< rcScreen
.right
/ 2)
434 DeltaPt
.cx
= rcScreen
.right
- pt
.x
;
438 if (pt
.y
< rcScreen
.bottom
/ 2)
445 DeltaPt
.cy
= rcScreen
.bottom
- pt
.y
;
449 Pos
= (DeltaPt
.cx
* rcScreen
.bottom
< DeltaPt
.cy
* rcScreen
.right
) ? PosH
: PosV
;
451 /* Fix the screen origin to be relative to the primary monitor again */
452 OffsetRect(&rcScreen
,
456 hMonNew
= ITrayWindowImpl_GetMonitorFromRect(This
,
457 &This
->rcTrayWnd
[Pos
]);
462 /* Recalculate the rectangle, we're dragging to another monitor.
463 We don't need to recalculate the rect on single monitor systems. */
464 szTray
.cx
= This
->rcTrayWnd
[Pos
].right
- This
->rcTrayWnd
[Pos
].left
;
465 szTray
.cy
= This
->rcTrayWnd
[Pos
].bottom
- This
->rcTrayWnd
[Pos
].top
;
467 ITrayWindowImpl_GetTrayRectFromScreenRect(This
,
477 /* The user is dragging the tray window on the same monitor. We don't need
478 to recalculate the rectangle */
479 *pRect
= This
->rcTrayWnd
[Pos
];
488 ITrayWindowImpl_GetDraggingRectFromRect(IN OUT ITrayWindowImpl
*This
,
490 OUT HMONITOR
*phMonitor
)
494 /* Calculate the center of the rectangle. We call
495 ITrayWindowImpl_GetDraggingRectFromPt to calculate a valid
496 dragging rectangle */
497 pt
.x
= pRect
->left
+ ((pRect
->right
- pRect
->left
) / 2);
498 pt
.y
= pRect
->top
+ ((pRect
->bottom
- pRect
->top
) / 2);
500 return ITrayWindowImpl_GetDraggingRectFromPt(This
,
507 ITrayWindowImpl_ChangingWinPos(IN OUT ITrayWindowImpl
*This
,
508 IN OUT LPWINDOWPOS pwp
)
512 if (This
->IsDragging
)
514 rcTray
.left
= pwp
->x
;
516 rcTray
.right
= rcTray
.left
+ pwp
->cx
;
517 rcTray
.bottom
= rcTray
.top
+ pwp
->cy
;
519 if (!EqualRect(&rcTray
,
520 &This
->rcTrayWnd
[This
->DraggingPosition
]))
522 /* Recalculate the rectangle, the user dragged the tray
523 window to another monitor or the window was somehow else
525 This
->DraggingPosition
= ITrayWindowImpl_GetDraggingRectFromRect(This
,
527 &This
->DraggingMonitor
);
528 //This->rcTrayWnd[This->DraggingPosition] = rcTray;
531 //This->Monitor = ITrayWindowImpl_CalculateValidSize(This,
532 // This->DraggingPosition,
535 This
->Monitor
= This
->DraggingMonitor
;
536 This
->Position
= This
->DraggingPosition
;
537 This
->IsDragging
= FALSE
;
539 This
->rcTrayWnd
[This
->Position
] = rcTray
;
542 else if (GetWindowRect(This
->hWnd
,
545 if (This
->InSizeMove
)
547 if (!(pwp
->flags
& SWP_NOMOVE
))
549 rcTray
.left
= pwp
->x
;
553 if (!(pwp
->flags
& SWP_NOSIZE
))
555 rcTray
.right
= rcTray
.left
+ pwp
->cx
;
556 rcTray
.bottom
= rcTray
.top
+ pwp
->cy
;
559 This
->Position
= ITrayWindowImpl_GetDraggingRectFromRect(This
,
563 if (!(pwp
->flags
& (SWP_NOMOVE
| SWP_NOSIZE
)))
570 ITrayWindowImpl_MakeTrayRectWithSize(This
->Position
,
575 This
->rcTrayWnd
[This
->Position
] = rcTray
;
579 /* If the user isn't resizing the tray window we need to make sure the
580 new size or position is valid. This is to prevent changes to the window
581 without user interaction. */
582 rcTray
= This
->rcTrayWnd
[This
->Position
];
586 This
->TraySize
.cx
= rcTray
.right
- rcTray
.left
;
587 This
->TraySize
.cy
= rcTray
.bottom
- rcTray
.top
;
589 pwp
->flags
&= ~(SWP_NOMOVE
| SWP_NOSIZE
);
590 pwp
->x
= rcTray
.left
;
592 pwp
->cx
= This
->TraySize
.cx
;
593 pwp
->cy
= This
->TraySize
.cy
;
598 ITrayWindowImpl_ApplyClipping(IN OUT ITrayWindowImpl
*This
,
601 RECT rcClip
, rcWindow
;
604 if (GetWindowRect(This
->hWnd
,
607 /* Disable clipping on systems with only one monitor */
608 if (GetSystemMetrics(SM_CMONITORS
) <= 1)
615 ITrayWindowImpl_GetScreenRect(This
,
619 if (!IntersectRect(&rcClip
,
630 hClipRgn
= CreateRectRgnIndirect(&rcClip
);
635 /* Set the clipping region or make sure the window isn't clipped
636 by disabling it explicitly. */
637 SetWindowRgn(This
->hWnd
,
644 ITrayWindowImpl_ResizeWorkArea(IN OUT ITrayWindowImpl
*This
)
646 RECT rcTray
,rcWorkArea
;
648 /* If monitor has changed then fix the previous monitors work area */
649 if(This
->PreviousMonitor
!=This
->Monitor
)
651 ITrayWindowImpl_GetScreenRect(This
,
652 This
->PreviousMonitor
,
654 SystemParametersInfo(SPI_SETWORKAREA
,
660 rcTray
= This
->rcTrayWnd
[This
->Position
];
662 ITrayWindowImpl_GetScreenRect(This
,
665 This
->PreviousMonitor
=This
->Monitor
;
667 /* If AutoHide is false then change the workarea to exclude the area that
668 the taskbar covers. */
671 switch(This
->Position
)
674 rcWorkArea
.top
=rcTray
.bottom
;
677 rcWorkArea
.left
=rcTray
.right
;
680 rcWorkArea
.right
=rcTray
.left
;
683 rcWorkArea
.bottom
=rcTray
.top
;
688 SystemParametersInfo(SPI_SETWORKAREA
,
695 ITrayWindowImpl_CheckTrayWndPosition(IN OUT ITrayWindowImpl
*This
)
699 rcTray
= This
->rcTrayWnd
[This
->Position
];
700 // DbgPrint("CheckTray: %d: %d,%d,%d,%d\n", This->Position, rcTray.left, rcTray.top, rcTray.right, rcTray.bottom);
702 /* Move the tray window */
703 SetWindowPos(This
->hWnd
,
707 rcTray
.right
- rcTray
.left
,
708 rcTray
.bottom
- rcTray
.top
,
711 ITrayWindowImpl_ResizeWorkArea(This
);
713 ITrayWindowImpl_ApplyClipping(This
,
717 typedef struct _TW_STUCKRECTS2
725 } TW_STRUCKRECTS2
, *PTW_STUCKRECTS2
;
728 ITrayWindowImpl_RegLoadSettings(IN OUT ITrayWindowImpl
*This
)
733 SIZE WndSize
, EdgeSize
, DlgFrameSize
;
734 DWORD cbSize
= sizeof(sr
);
736 EdgeSize
.cx
= GetSystemMetrics(SM_CXEDGE
);
737 EdgeSize
.cy
= GetSystemMetrics(SM_CYEDGE
);
738 DlgFrameSize
.cx
= GetSystemMetrics(SM_CXDLGFRAME
);
739 DlgFrameSize
.cy
= GetSystemMetrics(SM_CYDLGFRAME
);
741 if (SHGetValue(hkExplorer
,
746 &cbSize
) == ERROR_SUCCESS
&&
747 sr
.cbSize
== sizeof(sr
))
749 This
->AutoHide
= (sr
.dwFlags
& ABS_AUTOHIDE
) != 0;
750 This
->AlwaysOnTop
= (sr
.dwFlags
& ABS_ALWAYSONTOP
) != 0;
751 This
->SmSmallIcons
= (sr
.dwFlags
& 0x4) != 0;
752 This
->HideClock
= (sr
.dwFlags
& 0x8) != 0;
754 /* FIXME: Are there more flags? */
756 if (This
->hWnd
!= NULL
)
757 SetWindowPos (This
->hWnd
,
758 This
->AlwaysOnTop
? HWND_TOPMOST
: HWND_NOTOPMOST
,
763 SWP_NOMOVE
| SWP_NOSIZE
);
765 if (sr
.Position
> ABE_BOTTOM
)
766 This
->Position
= ABE_BOTTOM
;
768 This
->Position
= sr
.Position
;
770 /* Try to find out which monitor the tray window was located on last.
771 Here we're only interested in the monitor screen that we think
772 is the last one used. We're going to determine on which monitor
773 we really are after calculating the docked position. */
775 ITrayWindowImpl_GetScreenRectFromRect(This
,
777 MONITOR_DEFAULTTONEAREST
);
781 This
->Position
= ABE_BOTTOM
;
783 /* Use the minimum size of the taskbar, we'll use the start
784 button as a minimum for now. Make sure we calculate the
785 entire window size, not just the client size. However, we
786 use a thinner border than a standard thick border, so that
787 the start button and bands are not stuck to the screen border. */
788 sr
.Size
.cx
= This
->StartBtnSize
.cx
+ (2 * (EdgeSize
.cx
+ DlgFrameSize
.cx
));
789 sr
.Size
.cy
= This
->StartBtnSize
.cy
+ (2 * (EdgeSize
.cy
+ DlgFrameSize
.cy
));
791 /* Use the primary screen by default */
794 rcScreen
.right
= GetSystemMetrics(SM_CXSCREEN
);
795 rcScreen
.bottom
= GetSystemMetrics(SM_CYSCREEN
);
796 ITrayWindowImpl_GetScreenRectFromRect(This
,
798 MONITOR_DEFAULTTOPRIMARY
);
801 /* Determine a minimum tray window rectangle. The "client" height is
802 zero here since we cannot determine an optimal minimum width when
803 loaded as a vertical tray window. We just need to make sure the values
804 loaded from the registry are at least. The windows explorer behaves
805 the same way, it allows the user to save a zero width vertical tray
806 window, but not a zero height horizontal tray window. */
807 WndSize
.cx
= 2 * (EdgeSize
.cx
+ DlgFrameSize
.cx
);
808 WndSize
.cy
= This
->StartBtnSize
.cy
+ (2 * (EdgeSize
.cy
+ DlgFrameSize
.cy
));
810 if (WndSize
.cx
< sr
.Size
.cx
)
811 WndSize
.cx
= sr
.Size
.cx
;
812 if (WndSize
.cy
< sr
.Size
.cy
)
813 WndSize
.cy
= sr
.Size
.cy
;
815 /* Save the calculated size */
816 This
->TraySize
= WndSize
;
818 /* Calculate all docking rectangles. We need to do this here so they're
819 initialized and dragging the tray window to another position gives
825 ITrayWindowImpl_GetTrayRectFromScreenRect(This
,
829 &This
->rcTrayWnd
[Pos
]);
830 // DbgPrint("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);
833 /* Determine which monitor we are on. It shouldn't matter which docked
834 position rectangle we use */
835 This
->Monitor
= ITrayWindowImpl_GetMonitorFromRect(This
,
836 &This
->rcTrayWnd
[ABE_LEFT
]);
840 ITrayWindowImpl_TrackMenu(IN OUT ITrayWindowImpl
*This
,
842 IN POINT
*ppt OPTIONAL
,
843 IN HWND hwndExclude OPTIONAL
,
845 IN BOOL IsContextMenu
)
847 TPMPARAMS tmp
, *ptmp
= NULL
;
852 if (hwndExclude
!= NULL
)
854 /* Get the client rectangle and map it to screen coordinates */
855 if (GetClientRect(hwndExclude
,
857 MapWindowPoints(hwndExclude
,
859 (LPPOINT
)&tmp
.rcExclude
,
869 GetClientRect(This
->hWnd
,
871 MapWindowPoints(This
->hWnd
,
873 (LPPOINT
)&tmp
.rcExclude
,
881 /* NOTE: TrackPopupMenuEx will eventually align the track position
882 for us, no need to take care of it here as long as the
883 coordinates are somewhere within the exclusion rectangle */
884 pt
.x
= ptmp
->rcExclude
.left
;
885 pt
.y
= ptmp
->rcExclude
.top
;
893 tmp
.cbSize
= sizeof(tmp
);
895 fuFlags
= TPM_RETURNCMD
| TPM_VERTICAL
;
896 fuFlags
|= (TrackUp
? TPM_BOTTOMALIGN
: TPM_TOPALIGN
);
898 fuFlags
|= TPM_RIGHTBUTTON
;
900 fuFlags
|= (TrackUp
? TPM_VERNEGANIMATION
: TPM_VERPOSANIMATION
);
902 cmdId
= TrackPopupMenuEx(hMenu
,
913 ITrayWindowImpl_TrackCtxMenu(IN OUT ITrayWindowImpl
*This
,
914 IN
const TRAYWINDOW_CTXMENU
*pMenu
,
915 IN POINT
*ppt OPTIONAL
,
916 IN HWND hwndExclude OPTIONAL
,
918 IN PVOID Context OPTIONAL
)
922 PVOID pcmContext
= NULL
;
924 hPopup
= pMenu
->CreateCtxMenu(This
->hWnd
,
929 cmdId
= ITrayWindowImpl_TrackMenu(This
,
936 pMenu
->CtxMenuCommand(This
->hWnd
,
946 ITrayWindowImpl_Free(ITrayWindowImpl
*This
)
948 HeapFree(hProcessHeap
,
954 static ULONG STDMETHODCALLTYPE
955 ITrayWindowImpl_Release(IN OUT ITrayWindow
*iface
)
957 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
960 Ret
= InterlockedDecrement(&This
->Ref
);
962 ITrayWindowImpl_Free(This
);
968 ITrayWindowImpl_Destroy(ITrayWindowImpl
*This
)
970 (void)InterlockedExchangePointer((PVOID
*)&This
->hWnd
,
973 if (This
->himlStartBtn
!= NULL
)
975 ImageList_Destroy(This
->himlStartBtn
);
976 This
->himlStartBtn
= NULL
;
979 if (This
->hCaptionFont
!= NULL
)
981 DeleteObject(This
->hCaptionFont
);
982 This
->hCaptionFont
= NULL
;
985 if (This
->hStartBtnFont
!= NULL
)
987 DeleteObject(This
->hStartBtnFont
);
988 This
->hStartBtnFont
= NULL
;
991 if (This
->hFont
!= NULL
)
993 DeleteObject(This
->hFont
);
997 if (This
->StartMenuPopup
!= NULL
)
999 IMenuPopup_Release(This
->StartMenuPopup
);
1000 This
->StartMenuPopup
= NULL
;
1003 if (This
->hbmStartMenu
!= NULL
)
1005 DeleteObject(This
->hbmStartMenu
);
1006 This
->hbmStartMenu
= NULL
;
1009 if (This
->StartMenuBand
!= NULL
)
1011 IMenuBand_Release(This
->StartMenuBand
);
1012 This
->StartMenuBand
= NULL
;
1015 if (This
->TrayBandSite
!= NULL
)
1017 /* FIXME: Unload bands */
1018 ITrayBandSite_Release(This
->TrayBandSite
);
1019 This
->TrayBandSite
= NULL
;
1022 ITrayWindowImpl_Release(ITrayWindow_from_impl(This
));
1024 if (InterlockedDecrement(&TrayWndCount
) == 0)
1028 static ULONG STDMETHODCALLTYPE
1029 ITrayWindowImpl_AddRef(IN OUT ITrayWindow
*iface
)
1031 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1033 return InterlockedIncrement(&This
->Ref
);
1038 ITrayWindowImpl_NCCreate(IN OUT ITrayWindowImpl
*This
)
1040 ITrayWindowImpl_AddRef(ITrayWindow_from_impl(This
));
1046 ITrayWindowImpl_UpdateStartButton(IN OUT ITrayWindowImpl
*This
,
1047 IN HBITMAP hbmStart OPTIONAL
)
1049 SIZE Size
= { 0, 0 };
1051 if (This
->himlStartBtn
== NULL
||
1052 !SendMessage(This
->hwndStart
,
1057 Size
.cx
= GetSystemMetrics(SM_CXEDGE
);
1058 Size
.cy
= GetSystemMetrics(SM_CYEDGE
);
1060 if (hbmStart
== NULL
)
1062 hbmStart
= (HBITMAP
)SendMessage(This
->hwndStart
,
1068 if (hbmStart
!= NULL
)
1072 if (GetObject(hbmStart
,
1076 Size
.cx
+= bmp
.bmWidth
;
1077 Size
.cy
+= max(bmp
.bmHeight
,
1078 GetSystemMetrics(SM_CYCAPTION
));
1082 /* Huh?! Shouldn't happen... */
1089 Size
.cx
+= GetSystemMetrics(SM_CXMINIMIZED
);
1090 Size
.cy
+= GetSystemMetrics(SM_CYCAPTION
);
1094 /* Save the size of the start button */
1095 This
->StartBtnSize
= Size
;
1099 ITrayWindowImpl_AlignControls(IN OUT ITrayWindowImpl
*This
,
1100 IN PRECT prcClient OPTIONAL
)
1103 SIZE TraySize
, StartSize
;
1104 POINT ptTrayNotify
= { 0, 0 };
1108 if (prcClient
!= NULL
)
1110 rcClient
= *prcClient
;
1114 if (!GetClientRect(This
->hWnd
,
1121 Horizontal
= ITrayWindowImpl_IsPosHorizontal(This
);
1123 /* We're about to resize/move the start button, the rebar control and
1124 the tray notification control */
1125 dwp
= BeginDeferWindowPos(3);
1129 /* Limit the Start button width to the client width, if neccessary */
1130 StartSize
= This
->StartBtnSize
;
1131 if (StartSize
.cx
> rcClient
.right
)
1132 StartSize
.cx
= rcClient
.right
;
1134 if (This
->hwndStart
!= NULL
)
1136 /* Resize and reposition the button */
1137 dwp
= DeferWindowPos(dwp
,
1144 SWP_NOZORDER
| SWP_NOACTIVATE
);
1149 /* Determine the size that the tray notification window needs */
1153 TraySize
.cy
= rcClient
.bottom
;
1157 TraySize
.cx
= rcClient
.right
;
1161 if (This
->hwndTrayNotify
!= NULL
&&
1162 SendMessage(This
->hwndTrayNotify
,
1163 TNWM_GETMINIMUMSIZE
,
1167 /* Move the tray notification window to the desired location */
1169 ptTrayNotify
.x
= rcClient
.right
- TraySize
.cx
;
1171 ptTrayNotify
.y
= rcClient
.bottom
- TraySize
.cy
;
1173 dwp
= DeferWindowPos(dwp
,
1174 This
->hwndTrayNotify
,
1180 SWP_NOZORDER
| SWP_NOACTIVATE
);
1185 /* Resize/Move the rebar control */
1186 if (This
->hwndRebar
!= NULL
)
1188 POINT ptRebar
= { 0, 0 };
1191 SetWindowStyle(This
->hwndRebar
,
1193 Horizontal
? 0 : CCS_VERT
);
1197 ptRebar
.x
= StartSize
.cx
+ GetSystemMetrics(SM_CXSIZEFRAME
);
1198 szRebar
.cx
= ptTrayNotify
.x
- ptRebar
.x
;
1199 szRebar
.cy
= rcClient
.bottom
;
1203 ptRebar
.y
= StartSize
.cy
+ GetSystemMetrics(SM_CYSIZEFRAME
);
1204 szRebar
.cx
= rcClient
.right
;
1205 szRebar
.cy
= ptTrayNotify
.y
- ptRebar
.y
;
1208 dwp
= DeferWindowPos(dwp
,
1215 SWP_NOZORDER
| SWP_NOACTIVATE
);
1219 EndDeferWindowPos(dwp
);
1221 if (This
->hwndTaskSwitch
!= NULL
)
1223 /* Update the task switch window configuration */
1224 SendMessage(This
->hwndTaskSwitch
,
1225 TSWM_UPDATETASKBARPOS
,
1232 ITrayWindowImpl_CreateStartBtnImageList(IN OUT ITrayWindowImpl
*This
)
1237 if (This
->himlStartBtn
!= NULL
)
1240 IconSize
.cx
= GetSystemMetrics(SM_CXSMICON
);
1241 IconSize
.cy
= GetSystemMetrics(SM_CYSMICON
);
1243 /* Load the start button icon and create a image list for it */
1244 hIconStart
= LoadImage(hExplorerInstance
,
1245 MAKEINTRESOURCE(IDI_START
),
1249 LR_SHARED
| LR_DEFAULTCOLOR
);
1251 if (hIconStart
!= NULL
)
1253 This
->himlStartBtn
= ImageList_Create(IconSize
.cx
,
1255 ILC_COLOR32
| ILC_MASK
,
1258 if (This
->himlStartBtn
!= NULL
)
1260 if (ImageList_AddIcon(This
->himlStartBtn
,
1266 /* Failed to add the icon! */
1267 ImageList_Destroy(This
->himlStartBtn
);
1268 This
->himlStartBtn
= NULL
;
1276 ITrayWindowImpl_CreateStartButtonBitmap(IN OUT ITrayWindowImpl
*This
)
1278 TCHAR szStartCaption
[32];
1281 HDC hDCScreen
= NULL
;
1282 SIZE Size
, SmallIcon
;
1283 HBITMAP hbmpOld
, hbmp
= NULL
;
1284 HBITMAP hBitmap
= NULL
;
1290 /* NOTE: This is the backwards compatibility code that is used if the
1291 Common Controls Version 6.0 are not available! */
1293 if (!LoadString(hExplorerInstance
,
1296 sizeof(szStartCaption
) / sizeof(szStartCaption
[0])))
1301 /* Load the start button icon */
1302 SmallIcon
.cx
= GetSystemMetrics(SM_CXSMICON
);
1303 SmallIcon
.cy
= GetSystemMetrics(SM_CYSMICON
);
1304 hIconStart
= LoadImage(hExplorerInstance
,
1305 MAKEINTRESOURCE(IDI_START
),
1309 LR_SHARED
| LR_DEFAULTCOLOR
);
1311 hDCScreen
= GetDC(NULL
);
1312 if (hDCScreen
== NULL
)
1315 hDC
= CreateCompatibleDC(hDCScreen
);
1319 hFontOld
= SelectObject(hDC
,
1320 This
->hStartBtnFont
);
1322 Ret
= GetTextExtentPoint32(hDC
,
1324 _tcslen(szStartCaption
),
1332 /* Make sure the height is at least the size of a caption icon. */
1333 if (hIconStart
!= NULL
)
1334 Size
.cx
+= SmallIcon
.cx
+ 4;
1335 Size
.cy
= max(Size
.cy
,
1338 /* Create the bitmap */
1339 hbmp
= CreateCompatibleBitmap(hDCScreen
,
1345 /* Caluclate the button rect */
1348 rcButton
.right
= Size
.cx
;
1349 rcButton
.bottom
= Size
.cy
;
1351 /* Draw the button */
1352 hbmpOld
= SelectObject(hDC
,
1355 Flags
= DC_TEXT
| DC_INBUTTON
;
1356 if (hIconStart
!= NULL
)
1359 if (DrawCapTemp
!= NULL
)
1361 Ret
= DrawCapTemp(NULL
,
1364 This
->hStartBtnFont
,
1376 /* We successfully created the bitmap! */
1381 if (hDCScreen
!= NULL
)
1397 ITrayWindowImpl_Create(IN OUT ITrayWindowImpl
*This
)
1399 TCHAR szStartCaption
[32];
1401 InterlockedIncrement(&TrayWndCount
);
1403 if (!LoadString(hExplorerInstance
,
1406 sizeof(szStartCaption
) / sizeof(szStartCaption
[0])))
1408 szStartCaption
[0] = TEXT('\0');
1411 if (This
->hStartBtnFont
== NULL
|| This
->hCaptionFont
== NULL
)
1413 NONCLIENTMETRICS ncm
;
1415 /* Get the system fonts, we use the caption font,
1416 always bold, though. */
1417 ncm
.cbSize
= sizeof(ncm
);
1418 if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS
,
1423 if (This
->hCaptionFont
== NULL
)
1425 ncm
.lfCaptionFont
.lfWeight
= FW_NORMAL
;
1426 This
->hCaptionFont
= CreateFontIndirect(&ncm
.lfCaptionFont
);
1429 if (This
->hStartBtnFont
== NULL
)
1431 ncm
.lfCaptionFont
.lfWeight
= FW_BOLD
;
1432 This
->hStartBtnFont
= CreateFontIndirect(&ncm
.lfCaptionFont
);
1437 /* Create the Start button */
1438 This
->hwndStart
= CreateWindowEx(0,
1441 WS_CHILD
| WS_VISIBLE
| WS_CLIPSIBLINGS
|
1442 BS_PUSHBUTTON
| BS_CENTER
| BS_VCENTER
| BS_BITMAP
,
1448 (HMENU
)IDC_STARTBTN
,
1451 if (This
->hwndStart
)
1453 SendMessage(This
->hwndStart
,
1455 (WPARAM
)This
->hStartBtnFont
,
1458 if (ITrayWindowImpl_CreateStartBtnImageList(This
))
1460 BUTTON_IMAGELIST bil
;
1462 /* Try to set the start button image. This requires the Common
1463 Controls 6.0 to be present (XP and later) */
1464 bil
.himl
= This
->himlStartBtn
;
1465 bil
.margin
.left
= bil
.margin
.right
= 1;
1466 bil
.margin
.top
= bil
.margin
.bottom
= 1;
1467 bil
.uAlign
= BUTTON_IMAGELIST_ALIGN_LEFT
;
1469 if (!SendMessage(This
->hwndStart
,
1474 /* Fall back to the deprecated method on older systems that don't
1475 support Common Controls 6.0 */
1476 ImageList_Destroy(This
->himlStartBtn
);
1477 This
->himlStartBtn
= NULL
;
1479 goto SetStartBtnImage
;
1482 /* We're using the image list, remove the BS_BITMAP style and
1483 don't center it horizontally */
1484 SetWindowStyle(This
->hwndStart
,
1485 BS_BITMAP
| BS_RIGHT
,
1488 ITrayWindowImpl_UpdateStartButton(This
,
1493 HBITMAP hbmStart
, hbmOld
;
1496 hbmStart
= ITrayWindowImpl_CreateStartButtonBitmap(This
);
1497 if (hbmStart
!= NULL
)
1499 ITrayWindowImpl_UpdateStartButton(This
,
1502 hbmOld
= (HBITMAP
)SendMessage(This
->hwndStart
,
1508 DeleteObject(hbmOld
);
1513 /* Load the saved tray window settings */
1514 ITrayWindowImpl_RegLoadSettings(This
);
1516 /* Create and initialize the start menu */
1517 This
->hbmStartMenu
= LoadBitmap(hExplorerInstance
,
1518 MAKEINTRESOURCE(IDB_STARTMENU
));
1519 This
->StartMenuPopup
= CreateStartMenu(ITrayWindow_from_impl(This
),
1520 &This
->StartMenuBand
,
1524 /* Load the tray band site */
1525 if (This
->TrayBandSite
!= NULL
)
1527 ITrayBandSite_Release(This
->TrayBandSite
);
1530 This
->TrayBandSite
= CreateTrayBandSite(ITrayWindow_from_impl(This
),
1532 &This
->hwndTaskSwitch
);
1534 /* Create the tray notification window */
1535 This
->hwndTrayNotify
= CreateTrayNotifyWnd(ITrayWindow_from_impl(This
),
1538 if (ITrayWindowImpl_UpdateNonClientMetrics(This
))
1540 ITrayWindowImpl_SetWindowsFont(This
);
1543 /* Move the tray window to the right position and resize it if neccessary */
1544 ITrayWindowImpl_CheckTrayWndPosition(This
);
1546 /* Align all controls on the tray window */
1547 ITrayWindowImpl_AlignControls(This
,
1551 static HRESULT STDMETHODCALLTYPE
1552 ITrayWindowImpl_QueryInterface(IN OUT ITrayWindow
*iface
,
1556 ITrayWindowImpl
*This
;
1561 This
= impl_from_ITrayWindow(iface
);
1563 if (IsEqualIID(riid
,
1566 *ppvObj
= IUnknown_from_impl(This
);
1568 else if (IsEqualIID(riid
,
1569 &IID_IShellDesktopTray
))
1571 *ppvObj
= IShellDesktopTray_from_impl(This
);
1576 return E_NOINTERFACE
;
1579 ITrayWindowImpl_AddRef(iface
);
1583 static ITrayWindowImpl
*
1584 ITrayWindowImpl_Construct(VOID
)
1586 ITrayWindowImpl
*This
;
1588 This
= HeapAlloc(hProcessHeap
,
1596 This
->lpVtbl
= &ITrayWindowImpl_Vtbl
;
1597 This
->lpVtblShellDesktopTray
= &IShellDesktopTrayImpl_Vtbl
;
1599 This
->Position
= (DWORD
)-1;
1604 static HRESULT STDMETHODCALLTYPE
1605 ITrayWindowImpl_Open(IN OUT ITrayWindow
*iface
)
1607 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1612 /* Check if there's already a window created and try to show it.
1613 If it was somehow destroyed just create a new tray window. */
1614 if (This
->hWnd
!= NULL
)
1616 if (IsWindow(This
->hWnd
))
1618 if (!IsWindowVisible(This
->hWnd
))
1620 ITrayWindowImpl_CheckTrayWndPosition(This
);
1622 ShowWindow(This
->hWnd
,
1627 goto TryCreateTrayWnd
;
1634 dwExStyle
= WS_EX_TOOLWINDOW
| WS_EX_WINDOWEDGE
;
1635 if (This
->AlwaysOnTop
)
1636 dwExStyle
|= WS_EX_TOPMOST
;
1638 if (This
->Position
!= (DWORD
)-1)
1639 rcWnd
= This
->rcTrayWnd
[This
->Position
];
1646 hWnd
= CreateWindowEx(dwExStyle
,
1649 WS_POPUP
| WS_VISIBLE
| WS_CLIPSIBLINGS
| WS_CLIPCHILDREN
|
1650 WS_BORDER
| WS_THICKFRAME
,
1653 rcWnd
.right
- rcWnd
.left
,
1654 rcWnd
.bottom
- rcWnd
.top
,
1666 static HRESULT STDMETHODCALLTYPE
1667 ITrayWindowImpl_Close(IN OUT ITrayWindow
*iface
)
1669 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1671 if (This
->hWnd
!= NULL
)
1673 SendMessage(This
->hWnd
,
1682 static HWND STDMETHODCALLTYPE
1683 ITrayWindowImpl_GetHWND(IN OUT ITrayWindow
*iface
)
1685 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1690 static BOOL STDMETHODCALLTYPE
1691 ITrayWindowImpl_IsSpecialHWND(IN OUT ITrayWindow
*iface
,
1694 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1696 return (hWnd
== This
->hWnd
||
1697 (This
->hWndDesktop
!= NULL
&& hWnd
== This
->hWndDesktop
));
1700 static BOOL STDMETHODCALLTYPE
1701 ITrayWindowImpl_IsHorizontal(IN OUT ITrayWindow
*iface
)
1703 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1704 return ITrayWindowImpl_IsPosHorizontal(This
);
1707 static HFONT STDMETHODCALLTYPE
1708 ITrayWIndowImpl_GetCaptionFonts(IN OUT ITrayWindow
*iface
,
1709 OUT HFONT
*phBoldCaption OPTIONAL
)
1711 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1713 if (phBoldCaption
!= NULL
)
1714 *phBoldCaption
= This
->hStartBtnFont
;
1716 return This
->hCaptionFont
;
1719 static HWND STDMETHODCALLTYPE
1720 ITrayWindowImpl_DisplayProperties(IN OUT ITrayWindow
*iface
)
1722 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1724 if (This
->hWndTrayProperties
!= NULL
)
1726 BringWindowToTop(This
->hWndTrayProperties
);
1727 return This
->hWndTrayProperties
;
1730 This
->hWndTrayProperties
= DisplayTrayProperties(ITrayWindow_from_impl(This
));
1731 return This
->hWndTrayProperties
;
1735 OpenCommonStartMenuDirectory(IN HWND hWndOwner
,
1736 IN LPCTSTR lpOperation
)
1738 TCHAR szDir
[MAX_PATH
];
1740 if (SHGetSpecialFolderPath(hWndOwner
,
1742 CSIDL_COMMON_STARTMENU
,
1745 ShellExecute(hWndOwner
,
1755 OpenTaskManager(IN HWND hWndOwner
)
1757 ShellExecute(hWndOwner
,
1759 TEXT("taskmgr.exe"),
1765 static BOOL STDMETHODCALLTYPE
1766 ITrayWindowImpl_ExecContextMenuCmd(IN OUT ITrayWindow
*iface
,
1769 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1770 BOOL bHandled
= TRUE
;
1774 case ID_SHELL_CMD_PROPERTIES
:
1775 ITrayWindow_DisplayProperties(iface
);
1778 case ID_SHELL_CMD_OPEN_ALL_USERS
:
1779 OpenCommonStartMenuDirectory(This
->hWnd
,
1783 case ID_SHELL_CMD_EXPLORE_ALL_USERS
:
1784 OpenCommonStartMenuDirectory(This
->hWnd
,
1788 case ID_LOCKTASKBAR
:
1789 if (SHRestricted(REST_CLASSICSHELL
) == 0)
1791 ITrayWindow_Lock(iface
,
1796 case ID_SHELL_CMD_OPEN_TASKMGR
:
1797 OpenTaskManager(This
->hWnd
);
1802 DbgPrint("ITrayWindow::ExecContextMenuCmd(%u): Unhandled Command ID!\n", uiCmd
);
1810 static BOOL STDMETHODCALLTYPE
1811 ITrayWindowImpl_Lock(IN OUT ITrayWindow
*iface
,
1815 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1817 bPrevLock
= This
->Locked
;
1818 if (This
->Locked
!= bLock
)
1820 This
->Locked
= bLock
;
1822 if (This
->TrayBandSite
!= NULL
)
1824 if (!SUCCEEDED(ITrayBandSite_Lock(This
->TrayBandSite
,
1828 This
->Locked
= bPrevLock
;
1836 static const ITrayWindowVtbl ITrayWindowImpl_Vtbl
=
1839 ITrayWindowImpl_QueryInterface
,
1840 ITrayWindowImpl_AddRef
,
1841 ITrayWindowImpl_Release
,
1843 ITrayWindowImpl_Open
,
1844 ITrayWindowImpl_Close
,
1845 ITrayWindowImpl_GetHWND
,
1846 ITrayWindowImpl_IsSpecialHWND
,
1847 ITrayWindowImpl_IsHorizontal
,
1848 ITrayWIndowImpl_GetCaptionFonts
,
1849 ITrayWindowImpl_DisplayProperties
,
1850 ITrayWindowImpl_ExecContextMenuCmd
,
1851 ITrayWindowImpl_Lock
1854 static LRESULT CALLBACK
1855 TrayWndProc(IN HWND hwnd
,
1860 ITrayWindowImpl
*This
= NULL
;
1861 LRESULT Ret
= FALSE
;
1863 if (uMsg
!= WM_NCCREATE
)
1865 This
= (ITrayWindowImpl
*)GetWindowLongPtr(hwnd
,
1869 if (This
!= NULL
|| uMsg
== WM_NCCREATE
)
1871 if (This
!= NULL
&& This
->StartMenuBand
!= NULL
)
1878 Msg
.wParam
= wParam
;
1879 Msg
.lParam
= lParam
;
1881 if (IMenuBand_TranslateMenuMessage(This
->StartMenuBand
,
1888 wParam
= Msg
.wParam
;
1889 lParam
= Msg
.lParam
;
1901 /* The user may not be able to resize the tray window.
1902 Pretend like the window is not sizeable when the user
1903 clicks on the border. */
1907 if (GetClientRect(hwnd
,
1909 MapWindowPoints(hwnd
,
1914 pt
.x
= (SHORT
)LOWORD(lParam
);
1915 pt
.y
= (SHORT
)HIWORD(lParam
);
1917 if (PtInRect(&rcClient
,
1920 /* The user is trying to drag the tray window */
1924 /* Depending on the position of the tray window, allow only
1925 changing the border next to the monitor working area */
1926 switch (This
->Position
)
1929 if (pt
.y
> rcClient
.bottom
)
1934 if (pt
.y
< rcClient
.top
)
1939 if (pt
.x
> rcClient
.right
)
1944 if (pt
.x
< rcClient
.left
)
1959 PRECT pRect
= (PRECT
)lParam
;
1961 /* We need to ensure that an application can not accidently
1962 move the tray window (using SetWindowPos). However, we still
1963 need to be able to move the window in case the user wants to
1964 drag the tray window to another position or in case the user
1965 wants to resize the tray window. */
1966 if (!This
->Locked
&& GetCursorPos(&ptCursor
))
1968 This
->IsDragging
= TRUE
;
1969 This
->DraggingPosition
= ITrayWindowImpl_GetDraggingRectFromPt(This
,
1972 &This
->DraggingMonitor
);
1976 *pRect
= This
->rcTrayWnd
[This
->Position
];
1983 PRECT pRect
= (PRECT
)lParam
;
1987 ITrayWindowImpl_CalculateValidSize(This
,
1993 *pRect
= This
->rcTrayWnd
[This
->Position
];
1998 case WM_WINDOWPOSCHANGING
:
2000 ITrayWindowImpl_ChangingWinPos(This
,
2001 (LPWINDOWPOS
)lParam
);
2009 if (wParam
== SIZE_RESTORED
&& lParam
== 0)
2011 ITrayWindowImpl_ResizeWorkArea(This
);
2012 /* Clip the tray window on multi monitor systems so the edges can't
2013 overlap into another monitor */
2014 ITrayWindowImpl_ApplyClipping(This
,
2017 if (!GetClientRect(This
->hWnd
,
2025 rcClient
.left
= rcClient
.top
= 0;
2026 rcClient
.right
= LOWORD(lParam
);
2027 rcClient
.bottom
= HIWORD(lParam
);
2030 ITrayWindowImpl_AlignControls(This
,
2035 case WM_ENTERSIZEMOVE
:
2036 This
->InSizeMove
= TRUE
;
2037 This
->IsDragging
= FALSE
;
2040 /* Remove the clipping on multi monitor systems while dragging around */
2041 ITrayWindowImpl_ApplyClipping(This
,
2046 case WM_EXITSIZEMOVE
:
2047 This
->InSizeMove
= FALSE
;
2050 /* Apply clipping */
2051 PostMessage(This
->hWnd
,
2063 /* The user pressed Alt+Space, this usually brings up the system menu of a window.
2064 The tray window needs to handle this specially, since it normally doesn't have
2067 static const UINT uidDisableItem
[] = {
2078 /* temporarily enable the system menu */
2079 SetWindowStyle(hwnd
,
2083 hSysMenu
= GetSystemMenu(hwnd
,
2085 if (hSysMenu
!= NULL
)
2087 /* Disable all items that are not relevant */
2088 for (i
= 0; i
!= sizeof(uidDisableItem
) / sizeof(uidDisableItem
[0]); i
++)
2090 EnableMenuItem(hSysMenu
,
2092 MF_BYCOMMAND
| MF_GRAYED
);
2095 EnableMenuItem(hSysMenu
,
2098 (SHRestricted(REST_NOCLOSE
) ? MF_GRAYED
: MF_ENABLED
));
2100 /* Display the system menu */
2101 uId
= ITrayWindowImpl_TrackMenu(This
,
2105 This
->Position
!= ABE_TOP
,
2109 SendMessage(This
->hWnd
,
2116 /* revert the system menu window style */
2117 SetWindowStyle(hwnd
,
2128 case WM_NCRBUTTONUP
:
2129 /* We want the user to be able to get a context menu even on the nonclient
2130 area (including the sizing border)! */
2131 uMsg
= WM_CONTEXTMENU
;
2132 wParam
= (WPARAM
)hwnd
;
2135 case WM_CONTEXTMENU
:
2137 POINT pt
, *ppt
= NULL
;
2138 HWND hWndExclude
= NULL
;
2140 /* Check if the administrator has forbidden access to context menus */
2141 if (SHRestricted(REST_NOTRAYCONTEXTMENU
))
2144 pt
.x
= (SHORT
)LOWORD(lParam
);
2145 pt
.y
= (SHORT
)HIWORD(lParam
);
2147 if (pt
.x
!= -1 || pt
.y
!= -1)
2150 hWndExclude
= This
->hwndStart
;
2152 if ((HWND
)wParam
== This
->hwndStart
)
2154 /* Make sure we can't track the context menu if the start
2155 menu is currently being shown */
2156 if (!(SendMessage(This
->hwndStart
,
2161 ITrayWindowImpl_TrackCtxMenu(This
,
2162 &StartMenuBtnCtxMenu
,
2165 This
->Position
== ABE_BOTTOM
,
2171 /* See if the context menu should be handled by the task band site */
2172 if (ppt
!= NULL
&& This
->TrayBandSite
!= NULL
)
2175 POINT ptClient
= *ppt
;
2177 /* Convert the coordinates to client-coordinates */
2178 MapWindowPoints(NULL
,
2183 hWndAtPt
= ChildWindowFromPoint(This
->hWnd
,
2185 if (hWndAtPt
!= NULL
&&
2186 (hWndAtPt
== This
->hwndRebar
|| IsChild(This
->hwndRebar
,
2189 /* Check if the user clicked on the task switch window */
2191 MapWindowPoints(NULL
,
2196 hWndAtPt
= ChildWindowFromPointEx(This
->hwndRebar
,
2198 CWP_SKIPINVISIBLE
| CWP_SKIPDISABLED
);
2199 if (hWndAtPt
== This
->hwndTaskSwitch
)
2200 goto HandleTrayContextMenu
;
2202 /* Forward the message to the task band site */
2203 ITrayBandSite_ProcessMessage(This
->TrayBandSite
,
2211 goto HandleTrayContextMenu
;
2215 HandleTrayContextMenu
:
2216 /* Tray the default tray window context menu */
2217 ITrayWindowImpl_TrackCtxMenu(This
,
2230 /* FIXME: We can't check with IsChild whether the hwnd is somewhere inside
2231 the rebar control! But we shouldn't forward messages that the band
2232 site doesn't handle, such as other controls (start button, tray window */
2233 if (This
->TrayBandSite
== NULL
||
2234 !SUCCEEDED(ITrayBandSite_ProcessMessage(This
->TrayBandSite
,
2241 const NMHDR
*nmh
= (const NMHDR
*)lParam
;
2243 if (nmh
->hwndFrom
== This
->hwndTrayNotify
)
2248 /* Cause all controls to be aligned */
2249 PostMessage(This
->hWnd
,
2260 case WM_NCLBUTTONDBLCLK
:
2261 /* We "handle" this message so users can't cause a weird maximize/restore
2262 window animation when double-clicking the tray window! */
2267 LPCREATESTRUCT CreateStruct
= (LPCREATESTRUCT
)lParam
;
2268 This
= (ITrayWindowImpl
*)CreateStruct
->lpCreateParams
;
2270 if (InterlockedCompareExchangePointer((PVOID
*)&This
->hWnd
,
2274 /* Somebody else was faster... */
2278 SetWindowLongPtr(hwnd
,
2282 return ITrayWindowImpl_NCCreate(This
);
2286 ITrayWindowImpl_Create(This
);
2290 ITrayWindowImpl_Destroy(This
);
2293 case WM_APP_TRAYDESTROY
:
2294 DestroyWindow(hwnd
);
2298 if ((HWND
)lParam
== This
->hwndStart
)
2300 if (This
->StartMenuPopup
!= NULL
)
2306 if (GetWindowRect(This
->hwndStart
,
2309 if (ITrayWindowImpl_IsPosHorizontal(This
))
2311 pt
.x
= rcExclude
.left
;
2312 pt
.y
= rcExclude
.top
;
2313 dwFlags
|= MPPF_BOTTOM
;
2317 if (This
->Position
== ABE_LEFT
)
2318 pt
.x
= rcExclude
.left
;
2320 pt
.x
= rcExclude
.right
;
2322 pt
.y
= rcExclude
.bottom
;
2323 dwFlags
|= MPPF_BOTTOM
;
2326 IMenuPopup_Popup(This
->StartMenuPopup
,
2335 if (This
->TrayBandSite
== NULL
||
2336 !SUCCEEDED(ITrayBandSite_ProcessMessage(This
->TrayBandSite
,
2343 switch(LOWORD(wParam
))
2345 /* FIXME: Handle these commands as well */
2346 case IDM_TASKBARANDSTARTMENU
:
2348 case IDM_HELPANDSUPPORT
:
2354 RUNFILEDLG RunFileDlg
;
2356 hShell32
= GetModuleHandle(TEXT("SHELL32.DLL"));
2357 RunFileDlg
= (RUNFILEDLG
)GetProcAddress(hShell32
, (LPCSTR
)61);
2359 RunFileDlg(hwnd
, NULL
, NULL
, NULL
, NULL
, RFF_CALCDIRECTORY
);
2363 /* FIXME: Handle these commands as well */
2364 case IDM_SYNCHRONIZE
:
2366 case IDM_DISCONNECT
:
2367 case IDM_UNDOCKCOMPUTER
:
2373 EXITWINDLG ExitWinDlg
;
2375 hShell32
= GetModuleHandle(TEXT("SHELL32.DLL"));
2376 ExitWinDlg
= (EXITWINDLG
)GetProcAddress(hShell32
, (LPCSTR
)60);
2392 Ret
= DefWindowProc(hwnd
,
2402 * Tray Window Context Menu
2406 CreateTrayWindowContextMenu(IN HWND hWndOwner
,
2407 IN PVOID
*ppcmContext
,
2408 IN PVOID Context OPTIONAL
)
2410 ITrayWindowImpl
*This
= (ITrayWindowImpl
*)Context
;
2411 IContextMenu
*pcm
= NULL
;
2414 hPopup
= LoadPopupMenu(hExplorerInstance
,
2415 MAKEINTRESOURCE(IDM_TRAYWND
));
2419 if (SHRestricted(REST_CLASSICSHELL
) != 0)
2426 CheckMenuItem(hPopup
,
2428 MF_BYCOMMAND
| (This
->Locked
? MF_CHECKED
: MF_UNCHECKED
));
2430 if (This
->TrayBandSite
!= NULL
)
2432 if (SUCCEEDED(ITrayBandSite_AddContextMenus(This
->TrayBandSite
,
2440 DbgPrint("ITrayBandSite::AddContextMenus succeeded!\n");
2441 *(IContextMenu
**)ppcmContext
= pcm
;
2450 OnTrayWindowContextMenuCommand(IN HWND hWndOwner
,
2452 IN PVOID pcmContext OPTIONAL
,
2453 IN PVOID Context OPTIONAL
)
2455 ITrayWindowImpl
*This
= (ITrayWindowImpl
*)Context
;
2456 IContextMenu
*pcm
= (IContextMenu
*)pcmContext
;
2460 if (uiCmdId
>= ID_SHELL_CMD_FIRST
&& uiCmdId
<= ID_SHELL_CMD_LAST
)
2462 CMINVOKECOMMANDINFO cmici
= {0};
2466 /* Setup and invoke the shell command */
2467 cmici
.cbSize
= sizeof(cmici
);
2468 cmici
.hwnd
= hWndOwner
;
2469 cmici
.lpVerb
= (LPCSTR
)MAKEINTRESOURCE(uiCmdId
- ID_SHELL_CMD_FIRST
);
2470 cmici
.nShow
= SW_NORMAL
;
2472 IContextMenu_InvokeCommand(pcm
,
2478 ITrayWindow_ExecContextMenuCmd(ITrayWindow_from_impl(This
),
2484 IContextMenu_Release(pcm
);
2487 static const TRAYWINDOW_CTXMENU TrayWindowCtxMenu
= {
2488 CreateTrayWindowContextMenu
,
2489 OnTrayWindowContextMenuCommand
2492 /*****************************************************************************/
2495 RegisterTrayWindowClass(VOID
)
2500 if (!RegisterTrayNotifyWndClass())
2503 wcTrayWnd
.style
= CS_DBLCLKS
;
2504 wcTrayWnd
.lpfnWndProc
= TrayWndProc
;
2505 wcTrayWnd
.cbClsExtra
= 0;
2506 wcTrayWnd
.cbWndExtra
= sizeof(ITrayWindowImpl
*);
2507 wcTrayWnd
.hInstance
= hExplorerInstance
;
2508 wcTrayWnd
.hIcon
= NULL
;
2509 wcTrayWnd
.hCursor
= LoadCursor(NULL
,
2511 wcTrayWnd
.hbrBackground
= (HBRUSH
)(COLOR_3DFACE
+ 1);
2512 wcTrayWnd
.lpszMenuName
= NULL
;
2513 wcTrayWnd
.lpszClassName
= szTrayWndClass
;
2515 Ret
= RegisterClass(&wcTrayWnd
) != 0;
2518 UnregisterTrayNotifyWndClass();
2524 UnregisterTrayWindowClass(VOID
)
2526 UnregisterTrayNotifyWndClass();
2528 UnregisterClass(szTrayWndClass
,
2533 CreateTrayWindow(VOID
)
2535 ITrayWindowImpl
*This
;
2536 ITrayWindow
*TrayWindow
;
2538 This
= ITrayWindowImpl_Construct();
2541 TrayWindow
= ITrayWindow_from_impl(This
);
2543 ITrayWindowImpl_Open(TrayWindow
);
2552 TrayProcessMessages(IN OUT ITrayWindow
*Tray
)
2554 ITrayWindowImpl
*This
;
2557 This
= impl_from_ITrayWindow(Tray
);
2559 /* FIXME: We should keep a reference here... */
2561 while (PeekMessage(&Msg
,
2567 if (Msg
.message
== WM_QUIT
)
2570 if (This
->StartMenuBand
== NULL
||
2571 IMenuBand_IsMenuMessage(This
->StartMenuBand
,
2574 TranslateMessage(&Msg
);
2575 DispatchMessage(&Msg
);
2581 TrayMessageLoop(IN OUT ITrayWindow
*Tray
)
2583 ITrayWindowImpl
*This
;
2587 This
= impl_from_ITrayWindow(Tray
);
2589 /* FIXME: We should keep a reference here... */
2593 Ret
= (GetMessage(&Msg
,
2603 if (This
->StartMenuBand
== NULL
||
2604 IMenuBand_IsMenuMessage(This
->StartMenuBand
,
2607 TranslateMessage(&Msg
);
2608 DispatchMessage(&Msg
);
2617 * NOTE: This is a very windows-specific COM interface used by SHCreateDesktop()!
2618 * These are the calls I observed, it may be wrong/incomplete/buggy!!!
2619 * The reason we implement it is because we have to use SHCreateDesktop() so
2620 * that the shell provides the desktop window and all the features that come
2621 * with it (especially positioning of desktop icons)
2624 static HRESULT STDMETHODCALLTYPE
2625 ITrayWindowImpl_IShellDesktopTray_QueryInterface(IN OUT IShellDesktopTray
*iface
,
2629 ITrayWindowImpl
*This
= impl_from_IShellDesktopTray(iface
);
2630 ITrayWindow
*tray
= ITrayWindow_from_impl(This
);
2632 DbgPrint("IShellDesktopTray::QueryInterface(0x%p, 0x%p)\n", riid
, ppvObj
);
2633 return ITrayWindowImpl_QueryInterface(tray
,
2638 static ULONG STDMETHODCALLTYPE
2639 ITrayWindowImpl_IShellDesktopTray_Release(IN OUT IShellDesktopTray
*iface
)
2641 ITrayWindowImpl
*This
= impl_from_IShellDesktopTray(iface
);
2642 ITrayWindow
*tray
= ITrayWindow_from_impl(This
);
2644 DbgPrint("IShellDesktopTray::Release()\n");
2645 return ITrayWindowImpl_Release(tray
);
2648 static ULONG STDMETHODCALLTYPE
2649 ITrayWindowImpl_IShellDesktopTray_AddRef(IN OUT IShellDesktopTray
*iface
)
2651 ITrayWindowImpl
*This
= impl_from_IShellDesktopTray(iface
);
2652 ITrayWindow
*tray
= ITrayWindow_from_impl(This
);
2654 DbgPrint("IShellDesktopTray::AddRef()\n");
2655 return ITrayWindowImpl_AddRef(tray
);
2658 static ULONG STDMETHODCALLTYPE
2659 ITrayWindowImpl_IShellDesktopTray_GetState(IN OUT IShellDesktopTray
*iface
)
2661 /* FIXME: Return ABS_ flags? */
2662 DbgPrint("IShellDesktopTray::GetState() unimplemented!\n");
2666 static HRESULT STDMETHODCALLTYPE
2667 ITrayWindowImpl_IShellDesktopTray_GetTrayWindow(IN OUT IShellDesktopTray
*iface
,
2668 OUT HWND
*phWndTray
)
2670 ITrayWindowImpl
*This
= impl_from_IShellDesktopTray(iface
);
2671 DbgPrint("IShellDesktopTray::GetTrayWindow(0x%p)\n", phWndTray
);
2672 *phWndTray
= This
->hWnd
;
2676 static HRESULT STDMETHODCALLTYPE
2677 ITrayWindowImpl_IShellDesktopTray_RegisterDesktopWindow(IN OUT IShellDesktopTray
*iface
,
2678 IN HWND hWndDesktop
)
2680 ITrayWindowImpl
*This
= impl_from_IShellDesktopTray(iface
);
2681 DbgPrint("IShellDesktopTray::RegisterDesktopWindow(0x%p)\n", hWndDesktop
);
2683 This
->hWndDesktop
= hWndDesktop
;
2687 static HRESULT STDMETHODCALLTYPE
2688 ITrayWindowImpl_IShellDesktopTray_Unknown(IN OUT IShellDesktopTray
*iface
,
2689 IN DWORD dwUnknown1
,
2690 IN DWORD dwUnknown2
)
2692 DbgPrint("IShellDesktopTray::Unknown(%u,%u) unimplemented!\n", dwUnknown1
, dwUnknown2
);
2696 static const IShellDesktopTrayVtbl IShellDesktopTrayImpl_Vtbl
=
2699 ITrayWindowImpl_IShellDesktopTray_QueryInterface
,
2700 ITrayWindowImpl_IShellDesktopTray_AddRef
,
2701 ITrayWindowImpl_IShellDesktopTray_Release
,
2702 /*** IShellDesktopTray ***/
2703 ITrayWindowImpl_IShellDesktopTray_GetState
,
2704 ITrayWindowImpl_IShellDesktopTray_GetTrayWindow
,
2705 ITrayWindowImpl_IShellDesktopTray_RegisterDesktopWindow
,
2706 ITrayWindowImpl_IShellDesktopTray_Unknown