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 DWORD DraggingPosition
;
63 HMONITOR DraggingMonitor
;
74 DWORD AlwaysOnTop
: 1;
75 DWORD SmSmallIcons
: 1;
89 IMenuBand
*StartMenuBand
;
90 IMenuPopup
*StartMenuPopup
;
93 HWND hWndTrayProperties
;
97 IUnknown_from_impl(ITrayWindowImpl
*This
)
99 return (IUnknown
*)&This
->lpVtbl
;
103 ITrayWindow_from_impl(ITrayWindowImpl
*This
)
105 return (ITrayWindow
*)&This
->lpVtbl
;
108 static IShellDesktopTray
*
109 IShellDesktopTray_from_impl(ITrayWindowImpl
*This
)
111 return (IShellDesktopTray
*)&This
->lpVtblShellDesktopTray
;
114 static ITrayWindowImpl
*
115 impl_from_ITrayWindow(ITrayWindow
*iface
)
117 return (ITrayWindowImpl
*)((ULONG_PTR
)iface
- FIELD_OFFSET(ITrayWindowImpl
,
121 static ITrayWindowImpl
*
122 impl_from_IShellDesktopTray(IShellDesktopTray
*iface
)
124 return (ITrayWindowImpl
*)((ULONG_PTR
)iface
- FIELD_OFFSET(ITrayWindowImpl
,
125 lpVtblShellDesktopTray
));
133 ITrayWindowImpl_UpdateNonClientMetrics(IN OUT ITrayWindowImpl
*This
)
135 This
->ncm
.cbSize
= sizeof(This
->ncm
);
136 if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS
,
141 if (This
->hFont
!= NULL
)
142 DeleteObject(This
->hFont
);
144 This
->hFont
= CreateFontIndirect(&This
->ncm
.lfMessageFont
);
152 ITrayWindowImpl_SetWindowsFont(IN OUT ITrayWindowImpl
*This
)
154 if (This
->hwndTrayNotify
!= NULL
)
156 SendMessage(This
->hwndTrayNotify
,
164 ITrayWindowImpl_GetScreenRectFromRect(IN OUT ITrayWindowImpl
*This
,
171 mi
.cbSize
= sizeof(mi
);
172 hMon
= MonitorFromRect(pRect
,
178 *pRect
= mi
.rcMonitor
;
184 pRect
->right
= GetSystemMetrics(SM_CXSCREEN
);
185 pRect
->bottom
= GetSystemMetrics(SM_CYSCREEN
);
194 ITrayWindowImpl_GetMonitorFromRect(IN OUT ITrayWindowImpl
*This
,
195 IN
const RECT
*pRect
)
199 /* In case the monitor sizes or saved sizes differ a bit (probably
200 not a lot, only so the tray window overlaps into another monitor
201 now), minimize the risk that we determine a wrong monitor by
202 using the center point of the tray window if we can't determine
203 it using the rectangle. */
204 hMon
= MonitorFromRect(pRect
,
205 MONITOR_DEFAULTTONULL
);
210 pt
.x
= pRect
->left
+ ((pRect
->right
- pRect
->left
) / 2);
211 pt
.y
= pRect
->top
+ ((pRect
->bottom
- pRect
->top
) / 2);
213 /* be less error-prone, find the nearest monitor */
214 hMon
= MonitorFromPoint(pt
,
215 MONITOR_DEFAULTTONEAREST
);
222 ITrayWindowImpl_GetScreenRect(IN OUT ITrayWindowImpl
*This
,
223 IN HMONITOR hMonitor
,
226 HMONITOR hMon
= NULL
;
228 if (hMonitor
!= NULL
)
232 mi
.cbSize
= sizeof(mi
);
233 if (!GetMonitorInfo(This
->Monitor
,
236 /* Hm, the monitor is gone? Try to find a monitor where it
237 could be located now */
238 hMon
= ITrayWindowImpl_GetMonitorFromRect(This
,
241 !GetMonitorInfo(hMon
,
249 *pRect
= mi
.rcMonitor
;
256 pRect
->right
= GetSystemMetrics(SM_CXSCREEN
);
257 pRect
->bottom
= GetSystemMetrics(SM_CYSCREEN
);
264 ITrayWindowImpl_MakeTrayRectWithSize(IN DWORD Position
,
265 IN
const SIZE
*pTraySize
,
271 pRect
->right
= pRect
->left
+ pTraySize
->cx
;
275 pRect
->bottom
= pRect
->top
+ pTraySize
->cy
;
279 pRect
->left
= pRect
->right
- pTraySize
->cx
;
284 pRect
->top
= pRect
->bottom
- pTraySize
->cy
;
290 ITrayWindowImpl_GetTrayRectFromScreenRect(IN OUT ITrayWindowImpl
*This
,
292 IN
const RECT
*pScreen
,
293 IN
const SIZE
*pTraySize OPTIONAL
,
296 if (pTraySize
== NULL
)
297 pTraySize
= &This
->TraySize
;
301 /* Move the border outside of the screen */
303 GetSystemMetrics(SM_CXEDGE
),
304 GetSystemMetrics(SM_CYEDGE
));
306 ITrayWindowImpl_MakeTrayRectWithSize(Position
,
312 ITrayWindowImpl_IsPosHorizontal(IN OUT ITrayWindowImpl
*This
)
314 return This
->Position
== ABE_TOP
|| This
->Position
== ABE_BOTTOM
;
318 ITrayWindowImpl_CalculateValidSize(IN OUT ITrayWindowImpl
*This
,
327 Horizontal
= ITrayWindowImpl_IsPosHorizontal(This
);
329 szWnd
.cx
= pRect
->right
- pRect
->left
;
330 szWnd
.cy
= pRect
->bottom
- pRect
->top
;
333 hMon
= ITrayWindowImpl_GetScreenRectFromRect(This
,
335 MONITOR_DEFAULTTONEAREST
);
337 /* Calculate the maximum size of the tray window and limit the window
338 size to half of the screen's size. */
339 szMax
.cx
= (rcScreen
.right
- rcScreen
.left
) / 2;
340 szMax
.cy
= (rcScreen
.bottom
- rcScreen
.top
) / 2;
341 if (szWnd
.cx
> szMax
.cx
)
343 if (szWnd
.cy
> szMax
.cy
)
346 /* FIXME - calculate */
348 ITrayWindowImpl_GetTrayRectFromScreenRect(This
,
359 ITrayWindowImpl_GetMinimumWindowSize(IN OUT ITrayWindowImpl
*This
,
364 AdjustWindowRectEx(&rcMin
,
365 GetWindowLongPtr(This
->hWnd
,
368 GetWindowLongPtr(This
->hWnd
,
377 ITrayWindowImpl_GetDraggingRectFromPt(IN OUT ITrayWindowImpl
*This
,
380 OUT HMONITOR
*phMonitor
)
382 HMONITOR hMon
, hMonNew
;
383 DWORD PosH
, PosV
, Pos
;
384 SIZE DeltaPt
, ScreenOffset
;
390 /* Determine the screen rectangle */
391 hMon
= MonitorFromPoint(pt
,
392 MONITOR_DEFAULTTONULL
);
398 mi
.cbSize
= sizeof(mi
);
399 if (!GetMonitorInfo(hMon
,
403 goto GetPrimaryScreenRect
;
406 /* make left top corner of the screen zero based to
407 make calculations easier */
408 pt
.x
-= mi
.rcMonitor
.left
;
409 pt
.y
-= mi
.rcMonitor
.top
;
411 ScreenOffset
.cx
= mi
.rcMonitor
.left
;
412 ScreenOffset
.cy
= mi
.rcMonitor
.top
;
413 rcScreen
.right
= mi
.rcMonitor
.right
- mi
.rcMonitor
.left
;
414 rcScreen
.bottom
= mi
.rcMonitor
.bottom
- mi
.rcMonitor
.top
;
418 GetPrimaryScreenRect
:
421 rcScreen
.right
= GetSystemMetrics(SM_CXSCREEN
);
422 rcScreen
.bottom
= GetSystemMetrics(SM_CYSCREEN
);
425 /* Calculate the nearest screen border */
426 if (pt
.x
< rcScreen
.right
/ 2)
433 DeltaPt
.cx
= rcScreen
.right
- pt
.x
;
437 if (pt
.y
< rcScreen
.bottom
/ 2)
444 DeltaPt
.cy
= rcScreen
.bottom
- pt
.y
;
448 Pos
= (DeltaPt
.cx
* rcScreen
.bottom
< DeltaPt
.cy
* rcScreen
.right
) ? PosH
: PosV
;
450 /* Fix the screen origin to be relative to the primary monitor again */
451 OffsetRect(&rcScreen
,
455 hMonNew
= ITrayWindowImpl_GetMonitorFromRect(This
,
456 &This
->rcTrayWnd
[Pos
]);
461 /* Recalculate the rectangle, we're dragging to another monitor.
462 We don't need to recalculate the rect on single monitor systems. */
463 szTray
.cx
= This
->rcTrayWnd
[Pos
].right
- This
->rcTrayWnd
[Pos
].left
;
464 szTray
.cy
= This
->rcTrayWnd
[Pos
].bottom
- This
->rcTrayWnd
[Pos
].top
;
466 ITrayWindowImpl_GetTrayRectFromScreenRect(This
,
476 /* The user is dragging the tray window on the same monitor. We don't need
477 to recalculate the rectangle */
478 *pRect
= This
->rcTrayWnd
[Pos
];
487 ITrayWindowImpl_GetDraggingRectFromRect(IN OUT ITrayWindowImpl
*This
,
489 OUT HMONITOR
*phMonitor
)
493 /* Calculate the center of the rectangle. We call
494 ITrayWindowImpl_GetDraggingRectFromPt to calculate a valid
495 dragging rectangle */
496 pt
.x
= pRect
->left
+ ((pRect
->right
- pRect
->left
) / 2);
497 pt
.y
= pRect
->top
+ ((pRect
->bottom
- pRect
->top
) / 2);
499 return ITrayWindowImpl_GetDraggingRectFromPt(This
,
506 ITrayWindowImpl_ChangingWinPos(IN OUT ITrayWindowImpl
*This
,
507 IN OUT LPWINDOWPOS pwp
)
511 if (This
->IsDragging
)
513 rcTray
.left
= pwp
->x
;
515 rcTray
.right
= rcTray
.left
+ pwp
->cx
;
516 rcTray
.bottom
= rcTray
.top
+ pwp
->cy
;
518 if (!EqualRect(&rcTray
,
519 &This
->rcTrayWnd
[This
->DraggingPosition
]))
521 /* Recalculate the rectangle, the user dragged the tray
522 window to another monitor or the window was somehow else
524 This
->DraggingPosition
= ITrayWindowImpl_GetDraggingRectFromRect(This
,
526 &This
->DraggingMonitor
);
527 //This->rcTrayWnd[This->DraggingPosition] = rcTray;
530 //This->Monitor = ITrayWindowImpl_CalculateValidSize(This,
531 // This->DraggingPosition,
534 This
->Monitor
= This
->DraggingMonitor
;
535 This
->Position
= This
->DraggingPosition
;
536 This
->IsDragging
= FALSE
;
538 This
->rcTrayWnd
[This
->Position
] = rcTray
;
541 else if (GetWindowRect(This
->hWnd
,
544 if (This
->InSizeMove
)
546 if (!(pwp
->flags
& SWP_NOMOVE
))
548 rcTray
.left
= pwp
->x
;
552 if (!(pwp
->flags
& SWP_NOSIZE
))
554 rcTray
.right
= rcTray
.left
+ pwp
->cx
;
555 rcTray
.bottom
= rcTray
.top
+ pwp
->cy
;
558 This
->Position
= ITrayWindowImpl_GetDraggingRectFromRect(This
,
562 if (!(pwp
->flags
& (SWP_NOMOVE
| SWP_NOSIZE
)))
569 ITrayWindowImpl_MakeTrayRectWithSize(This
->Position
,
574 This
->rcTrayWnd
[This
->Position
] = rcTray
;
578 /* If the user isn't resizing the tray window we need to make sure the
579 new size or position is valid. This is to prevent changes to the window
580 without user interaction. */
581 rcTray
= This
->rcTrayWnd
[This
->Position
];
585 This
->TraySize
.cx
= rcTray
.right
- rcTray
.left
;
586 This
->TraySize
.cy
= rcTray
.bottom
- rcTray
.top
;
588 pwp
->flags
&= ~(SWP_NOMOVE
| SWP_NOSIZE
);
589 pwp
->x
= rcTray
.left
;
591 pwp
->cx
= This
->TraySize
.cx
;
592 pwp
->cy
= This
->TraySize
.cy
;
597 ITrayWindowImpl_ApplyClipping(IN OUT ITrayWindowImpl
*This
,
600 RECT rcClip
, rcWindow
;
603 if (GetWindowRect(This
->hWnd
,
606 /* Disable clipping on systems with only one monitor */
607 if (GetSystemMetrics(SM_CMONITORS
) <= 1)
614 ITrayWindowImpl_GetScreenRect(This
,
618 if (!IntersectRect(&rcClip
,
629 hClipRgn
= CreateRectRgnIndirect(&rcClip
);
634 /* Set the clipping region or make sure the window isn't clipped
635 by disabling it explicitly. */
636 SetWindowRgn(This
->hWnd
,
643 ITrayWindowImpl_CheckTrayWndPosition(IN OUT ITrayWindowImpl
*This
)
647 rcTray
= This
->rcTrayWnd
[This
->Position
];
648 // DbgPrint("CheckTray: %d: %d,%d,%d,%d\n", This->Position, rcTray.left, rcTray.top, rcTray.right, rcTray.bottom);
650 /* Move the tray window */
651 SetWindowPos(This
->hWnd
,
655 rcTray
.right
- rcTray
.left
,
656 rcTray
.bottom
- rcTray
.top
,
659 ITrayWindowImpl_ApplyClipping(This
,
663 typedef struct _TW_STUCKRECTS2
671 } TW_STRUCKRECTS2
, *PTW_STUCKRECTS2
;
674 ITrayWindowImpl_RegLoadSettings(IN OUT ITrayWindowImpl
*This
)
679 SIZE WndSize
, EdgeSize
, DlgFrameSize
;
680 DWORD cbSize
= sizeof(sr
);
682 EdgeSize
.cx
= GetSystemMetrics(SM_CXEDGE
);
683 EdgeSize
.cy
= GetSystemMetrics(SM_CYEDGE
);
684 DlgFrameSize
.cx
= GetSystemMetrics(SM_CXDLGFRAME
);
685 DlgFrameSize
.cy
= GetSystemMetrics(SM_CYDLGFRAME
);
687 if (SHGetValue(hkExplorer
,
692 &cbSize
) == ERROR_SUCCESS
&&
693 sr
.cbSize
== sizeof(sr
))
695 This
->AutoHide
= (sr
.dwFlags
& ABS_AUTOHIDE
) != 0;
696 This
->AlwaysOnTop
= (sr
.dwFlags
& ABS_ALWAYSONTOP
) != 0;
697 This
->SmSmallIcons
= (sr
.dwFlags
& 0x4) != 0;
698 This
->HideClock
= (sr
.dwFlags
& 0x8) != 0;
700 /* FIXME: Are there more flags? */
702 if (This
->hWnd
!= NULL
)
703 SetWindowPos (This
->hWnd
,
704 This
->AlwaysOnTop
? HWND_TOPMOST
: HWND_NOTOPMOST
,
709 SWP_NOMOVE
| SWP_NOSIZE
);
711 if (sr
.Position
> ABE_BOTTOM
)
712 This
->Position
= ABE_BOTTOM
;
714 This
->Position
= sr
.Position
;
716 /* Try to find out which monitor the tray window was located on last.
717 Here we're only interested in the monitor screen that we think
718 is the last one used. We're going to determine on which monitor
719 we really are after calculating the docked position. */
721 ITrayWindowImpl_GetScreenRectFromRect(This
,
723 MONITOR_DEFAULTTONEAREST
);
727 This
->Position
= ABE_BOTTOM
;
729 /* Use the minimum size of the taskbar, we'll use the start
730 button as a minimum for now. Make sure we calculate the
731 entire window size, not just the client size. However, we
732 use a thinner border than a standard thick border, so that
733 the start button and bands are not stuck to the screen border. */
734 sr
.Size
.cx
= This
->StartBtnSize
.cx
+ (2 * (EdgeSize
.cx
+ DlgFrameSize
.cx
));
735 sr
.Size
.cy
= This
->StartBtnSize
.cy
+ (2 * (EdgeSize
.cy
+ DlgFrameSize
.cy
));
737 /* Use the primary screen by default */
740 rcScreen
.right
= GetSystemMetrics(SM_CXSCREEN
);
741 rcScreen
.right
= GetSystemMetrics(SM_CYSCREEN
);
742 ITrayWindowImpl_GetScreenRectFromRect(This
,
744 MONITOR_DEFAULTTOPRIMARY
);
747 /* Determine a minimum tray window rectangle. The "client" height is
748 zero here since we cannot determine an optimal minimum width when
749 loaded as a vertical tray window. We just need to make sure the values
750 loaded from the registry are at least. The windows explorer behaves
751 the same way, it allows the user to save a zero width vertical tray
752 window, but not a zero height horizontal tray window. */
753 WndSize
.cx
= 2 * (EdgeSize
.cx
+ DlgFrameSize
.cx
);
754 WndSize
.cy
= This
->StartBtnSize
.cy
+ (2 * (EdgeSize
.cy
+ DlgFrameSize
.cy
));
756 if (WndSize
.cx
< sr
.Size
.cx
)
757 WndSize
.cx
= sr
.Size
.cx
;
758 if (WndSize
.cy
< sr
.Size
.cy
)
759 WndSize
.cy
= sr
.Size
.cy
;
761 /* Save the calculated size */
762 This
->TraySize
= WndSize
;
764 /* Calculate all docking rectangles. We need to do this here so they're
765 initialized and dragging the tray window to another position gives
771 ITrayWindowImpl_GetTrayRectFromScreenRect(This
,
775 &This
->rcTrayWnd
[Pos
]);
776 // 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);
779 /* Determine which monitor we are on. It shouldn't matter which docked
780 position rectangle we use */
781 This
->Monitor
= ITrayWindowImpl_GetMonitorFromRect(This
,
782 &This
->rcTrayWnd
[ABE_LEFT
]);
786 ITrayWindowImpl_TrackMenu(IN OUT ITrayWindowImpl
*This
,
788 IN POINT
*ppt OPTIONAL
,
789 IN HWND hwndExclude OPTIONAL
,
791 IN BOOL IsContextMenu
)
793 TPMPARAMS tmp
, *ptmp
= NULL
;
798 if (hwndExclude
!= NULL
)
800 /* Get the client rectangle and map it to screen coordinates */
801 if (GetClientRect(hwndExclude
,
803 MapWindowPoints(hwndExclude
,
805 (LPPOINT
)&tmp
.rcExclude
,
815 GetClientRect(This
->hWnd
,
817 MapWindowPoints(This
->hWnd
,
819 (LPPOINT
)&tmp
.rcExclude
,
827 /* NOTE: TrackPopupMenuEx will eventually align the track position
828 for us, no need to take care of it here as long as the
829 coordinates are somewhere within the exclusion rectangle */
830 pt
.x
= ptmp
->rcExclude
.left
;
831 pt
.y
= ptmp
->rcExclude
.top
;
839 tmp
.cbSize
= sizeof(tmp
);
841 fuFlags
= TPM_RETURNCMD
| TPM_VERTICAL
;
842 fuFlags
|= (TrackUp
? TPM_BOTTOMALIGN
: TPM_TOPALIGN
);
844 fuFlags
|= TPM_RIGHTBUTTON
;
846 fuFlags
|= (TrackUp
? TPM_VERNEGANIMATION
: TPM_VERPOSANIMATION
);
848 cmdId
= TrackPopupMenuEx(hMenu
,
859 ITrayWindowImpl_TrackCtxMenu(IN OUT ITrayWindowImpl
*This
,
860 IN
const TRAYWINDOW_CTXMENU
*pMenu
,
861 IN POINT
*ppt OPTIONAL
,
862 IN HWND hwndExclude OPTIONAL
,
864 IN PVOID Context OPTIONAL
)
868 PVOID pcmContext
= NULL
;
870 hPopup
= pMenu
->CreateCtxMenu(This
->hWnd
,
875 cmdId
= ITrayWindowImpl_TrackMenu(This
,
882 pMenu
->CtxMenuCommand(This
->hWnd
,
892 ITrayWindowImpl_Free(ITrayWindowImpl
*This
)
894 HeapFree(hProcessHeap
,
900 static ULONG STDMETHODCALLTYPE
901 ITrayWindowImpl_Release(IN OUT ITrayWindow
*iface
)
903 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
906 Ret
= InterlockedDecrement(&This
->Ref
);
908 ITrayWindowImpl_Free(This
);
914 ITrayWindowImpl_Destroy(ITrayWindowImpl
*This
)
916 (void)InterlockedExchangePointer((PVOID
*)&This
->hWnd
,
919 if (This
->himlStartBtn
!= NULL
)
921 ImageList_Destroy(This
->himlStartBtn
);
922 This
->himlStartBtn
= NULL
;
925 if (This
->hCaptionFont
!= NULL
)
927 DeleteObject(This
->hCaptionFont
);
928 This
->hCaptionFont
= NULL
;
931 if (This
->hStartBtnFont
!= NULL
)
933 DeleteObject(This
->hStartBtnFont
);
934 This
->hStartBtnFont
= NULL
;
937 if (This
->hFont
!= NULL
)
939 DeleteObject(This
->hFont
);
943 if (This
->StartMenuPopup
!= NULL
)
945 IMenuPopup_Release(This
->StartMenuPopup
);
946 This
->StartMenuPopup
= NULL
;
949 if (This
->hbmStartMenu
!= NULL
)
951 DeleteObject(This
->hbmStartMenu
);
952 This
->hbmStartMenu
= NULL
;
955 if (This
->StartMenuBand
!= NULL
)
957 IMenuBand_Release(This
->StartMenuBand
);
958 This
->StartMenuBand
= NULL
;
961 if (This
->TrayBandSite
!= NULL
)
963 /* FIXME: Unload bands */
964 ITrayBandSite_Release(This
->TrayBandSite
);
965 This
->TrayBandSite
= NULL
;
968 ITrayWindowImpl_Release(ITrayWindow_from_impl(This
));
970 if (InterlockedDecrement(&TrayWndCount
) == 0)
974 static ULONG STDMETHODCALLTYPE
975 ITrayWindowImpl_AddRef(IN OUT ITrayWindow
*iface
)
977 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
979 return InterlockedIncrement(&This
->Ref
);
984 ITrayWindowImpl_NCCreate(IN OUT ITrayWindowImpl
*This
)
986 ITrayWindowImpl_AddRef(ITrayWindow_from_impl(This
));
992 ITrayWindowImpl_UpdateStartButton(IN OUT ITrayWindowImpl
*This
,
993 IN HBITMAP hbmStart OPTIONAL
)
995 SIZE Size
= { 0, 0 };
997 if (This
->himlStartBtn
== NULL
||
998 !SendMessage(This
->hwndStart
,
1003 Size
.cx
= GetSystemMetrics(SM_CXEDGE
);
1004 Size
.cy
= GetSystemMetrics(SM_CYEDGE
);
1006 if (hbmStart
== NULL
)
1008 hbmStart
= (HBITMAP
)SendMessage(This
->hwndStart
,
1014 if (hbmStart
!= NULL
)
1018 if (GetObject(hbmStart
,
1022 Size
.cx
+= bmp
.bmWidth
;
1023 Size
.cy
+= max(bmp
.bmHeight
,
1024 GetSystemMetrics(SM_CYCAPTION
));
1028 /* Huh?! Shouldn't happen... */
1035 Size
.cx
+= GetSystemMetrics(SM_CXMINIMIZED
);
1036 Size
.cy
+= GetSystemMetrics(SM_CYCAPTION
);
1040 /* Save the size of the start button */
1041 This
->StartBtnSize
= Size
;
1045 ITrayWindowImpl_AlignControls(IN OUT ITrayWindowImpl
*This
,
1046 IN PRECT prcClient OPTIONAL
)
1049 SIZE TraySize
, StartSize
;
1050 POINT ptTrayNotify
= { 0, 0 };
1054 if (prcClient
!= NULL
)
1056 rcClient
= *prcClient
;
1060 if (!GetClientRect(This
->hWnd
,
1067 Horizontal
= ITrayWindowImpl_IsPosHorizontal(This
);
1069 /* We're about to resize/move the start button, the rebar control and
1070 the tray notification control */
1071 dwp
= BeginDeferWindowPos(3);
1075 /* Limit the Start button width to the client width, if neccessary */
1076 StartSize
= This
->StartBtnSize
;
1077 if (StartSize
.cx
> rcClient
.right
)
1078 StartSize
.cx
= rcClient
.right
;
1080 if (This
->hwndStart
!= NULL
)
1082 /* Resize and reposition the button */
1083 dwp
= DeferWindowPos(dwp
,
1090 SWP_NOZORDER
| SWP_NOACTIVATE
);
1095 /* Determine the size that the tray notification window needs */
1099 TraySize
.cy
= rcClient
.bottom
;
1103 TraySize
.cx
= rcClient
.right
;
1107 if (This
->hwndTrayNotify
!= NULL
&&
1108 SendMessage(This
->hwndTrayNotify
,
1109 TNWM_GETMINIMUMSIZE
,
1113 /* Move the tray notification window to the desired location */
1115 ptTrayNotify
.x
= rcClient
.right
- TraySize
.cx
;
1117 ptTrayNotify
.y
= rcClient
.bottom
- TraySize
.cy
;
1119 dwp
= DeferWindowPos(dwp
,
1120 This
->hwndTrayNotify
,
1126 SWP_NOZORDER
| SWP_NOACTIVATE
);
1131 /* Resize/Move the rebar control */
1132 if (This
->hwndRebar
!= NULL
)
1134 POINT ptRebar
= { 0, 0 };
1137 SetWindowStyle(This
->hwndRebar
,
1139 Horizontal
? 0 : CCS_VERT
);
1143 ptRebar
.x
= StartSize
.cx
+ GetSystemMetrics(SM_CXSIZEFRAME
);
1144 szRebar
.cx
= ptTrayNotify
.x
- ptRebar
.x
;
1145 szRebar
.cy
= rcClient
.bottom
;
1149 ptRebar
.y
= StartSize
.cy
+ GetSystemMetrics(SM_CYSIZEFRAME
);
1150 szRebar
.cx
= rcClient
.right
;
1151 szRebar
.cy
= ptTrayNotify
.y
- ptRebar
.y
;
1154 dwp
= DeferWindowPos(dwp
,
1161 SWP_NOZORDER
| SWP_NOACTIVATE
);
1165 EndDeferWindowPos(dwp
);
1167 if (This
->hwndTaskSwitch
!= NULL
)
1169 /* Update the task switch window configuration */
1170 SendMessage(This
->hwndTaskSwitch
,
1171 TSWM_UPDATETASKBARPOS
,
1178 ITrayWindowImpl_CreateStartBtnImageList(IN OUT ITrayWindowImpl
*This
)
1183 if (This
->himlStartBtn
!= NULL
)
1186 IconSize
.cx
= GetSystemMetrics(SM_CXSMICON
);
1187 IconSize
.cy
= GetSystemMetrics(SM_CYSMICON
);
1189 /* Load the start button icon and create a image list for it */
1190 hIconStart
= LoadImage(hExplorerInstance
,
1191 MAKEINTRESOURCE(IDI_START
),
1195 LR_SHARED
| LR_DEFAULTCOLOR
);
1197 if (hIconStart
!= NULL
)
1199 This
->himlStartBtn
= ImageList_Create(IconSize
.cx
,
1201 ILC_COLOR32
| ILC_MASK
,
1204 if (This
->himlStartBtn
!= NULL
)
1206 if (ImageList_AddIcon(This
->himlStartBtn
,
1212 /* Failed to add the icon! */
1213 ImageList_Destroy(This
->himlStartBtn
);
1214 This
->himlStartBtn
= NULL
;
1222 ITrayWindowImpl_CreateStartButtonBitmap(IN OUT ITrayWindowImpl
*This
)
1224 TCHAR szStartCaption
[32];
1227 HDC hDCScreen
= NULL
;
1228 SIZE Size
, SmallIcon
;
1229 HBITMAP hbmpOld
, hbmp
= NULL
;
1230 HBITMAP hBitmap
= NULL
;
1236 /* NOTE: This is the backwards compatibility code that is used if the
1237 Common Controls Version 6.0 are not available! */
1239 if (!LoadString(hExplorerInstance
,
1242 sizeof(szStartCaption
) / sizeof(szStartCaption
[0])))
1247 /* Load the start button icon */
1248 SmallIcon
.cx
= GetSystemMetrics(SM_CXSMICON
);
1249 SmallIcon
.cy
= GetSystemMetrics(SM_CYSMICON
);
1250 hIconStart
= LoadImage(hExplorerInstance
,
1251 MAKEINTRESOURCE(IDI_START
),
1255 LR_SHARED
| LR_DEFAULTCOLOR
);
1257 hDCScreen
= GetDC(NULL
);
1258 if (hDCScreen
== NULL
)
1261 hDC
= CreateCompatibleDC(hDCScreen
);
1265 hFontOld
= SelectObject(hDC
,
1266 This
->hStartBtnFont
);
1268 Ret
= GetTextExtentPoint32(hDC
,
1270 _tcslen(szStartCaption
),
1278 /* Make sure the height is at least the size of a caption icon. */
1279 if (hIconStart
!= NULL
)
1280 Size
.cx
+= SmallIcon
.cx
+ 4;
1281 Size
.cy
= max(Size
.cy
,
1284 /* Create the bitmap */
1285 hbmp
= CreateCompatibleBitmap(hDCScreen
,
1291 /* Caluclate the button rect */
1294 rcButton
.right
= Size
.cx
;
1295 rcButton
.bottom
= Size
.cy
;
1297 /* Draw the button */
1298 hbmpOld
= SelectObject(hDC
,
1301 Flags
= DC_TEXT
| DC_INBUTTON
;
1302 if (hIconStart
!= NULL
)
1305 if (DrawCapTemp
!= NULL
)
1307 Ret
= DrawCapTemp(NULL
,
1310 This
->hStartBtnFont
,
1322 /* We successfully created the bitmap! */
1327 if (hDCScreen
!= NULL
)
1343 ITrayWindowImpl_Create(IN OUT ITrayWindowImpl
*This
)
1345 TCHAR szStartCaption
[32];
1347 InterlockedIncrement(&TrayWndCount
);
1349 if (!LoadString(hExplorerInstance
,
1352 sizeof(szStartCaption
) / sizeof(szStartCaption
[0])))
1354 szStartCaption
[0] = TEXT('\0');
1357 if (This
->hStartBtnFont
== NULL
|| This
->hCaptionFont
== NULL
)
1359 NONCLIENTMETRICS ncm
;
1361 /* Get the system fonts, we use the caption font,
1362 always bold, though. */
1363 ncm
.cbSize
= sizeof(ncm
);
1364 if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS
,
1369 if (This
->hCaptionFont
== NULL
)
1371 ncm
.lfCaptionFont
.lfWeight
= FW_NORMAL
;
1372 This
->hCaptionFont
= CreateFontIndirect(&ncm
.lfCaptionFont
);
1375 if (This
->hStartBtnFont
== NULL
)
1377 ncm
.lfCaptionFont
.lfWeight
= FW_BOLD
;
1378 This
->hStartBtnFont
= CreateFontIndirect(&ncm
.lfCaptionFont
);
1383 /* Create the Start button */
1384 This
->hwndStart
= CreateWindowEx(0,
1387 WS_CHILD
| WS_VISIBLE
| WS_CLIPSIBLINGS
|
1388 BS_PUSHBUTTON
| BS_CENTER
| BS_VCENTER
| BS_BITMAP
,
1394 (HMENU
)IDC_STARTBTN
,
1397 if (This
->hwndStart
)
1399 SendMessage(This
->hwndStart
,
1401 (WPARAM
)This
->hStartBtnFont
,
1404 if (ITrayWindowImpl_CreateStartBtnImageList(This
))
1406 BUTTON_IMAGELIST bil
;
1408 /* Try to set the start button image. This requires the Common
1409 Controls 6.0 to be present (XP and later) */
1410 bil
.himl
= This
->himlStartBtn
;
1411 bil
.margin
.left
= bil
.margin
.right
= 1;
1412 bil
.margin
.top
= bil
.margin
.bottom
= 1;
1413 bil
.uAlign
= BUTTON_IMAGELIST_ALIGN_LEFT
;
1415 if (!SendMessage(This
->hwndStart
,
1420 /* Fall back to the deprecated method on older systems that don't
1421 support Common Controls 6.0 */
1422 ImageList_Destroy(This
->himlStartBtn
);
1423 This
->himlStartBtn
= NULL
;
1425 goto SetStartBtnImage
;
1428 /* We're using the image list, remove the BS_BITMAP style and
1429 don't center it horizontally */
1430 SetWindowStyle(This
->hwndStart
,
1431 BS_BITMAP
| BS_RIGHT
,
1434 ITrayWindowImpl_UpdateStartButton(This
,
1439 HBITMAP hbmStart
, hbmOld
;
1442 hbmStart
= ITrayWindowImpl_CreateStartButtonBitmap(This
);
1443 if (hbmStart
!= NULL
)
1445 ITrayWindowImpl_UpdateStartButton(This
,
1448 hbmOld
= (HBITMAP
)SendMessage(This
->hwndStart
,
1454 DeleteObject(hbmOld
);
1459 /* Load the saved tray window settings */
1460 ITrayWindowImpl_RegLoadSettings(This
);
1462 /* Create and initialize the start menu */
1463 This
->hbmStartMenu
= LoadBitmap(hExplorerInstance
,
1464 MAKEINTRESOURCE(IDB_STARTMENU
));
1465 This
->StartMenuPopup
= CreateStartMenu(ITrayWindow_from_impl(This
),
1466 &This
->StartMenuBand
,
1470 /* Load the tray band site */
1471 if (This
->TrayBandSite
!= NULL
)
1473 ITrayBandSite_Release(This
->TrayBandSite
);
1476 This
->TrayBandSite
= CreateTrayBandSite(ITrayWindow_from_impl(This
),
1478 &This
->hwndTaskSwitch
);
1480 /* Create the tray notification window */
1481 This
->hwndTrayNotify
= CreateTrayNotifyWnd(ITrayWindow_from_impl(This
),
1484 if (ITrayWindowImpl_UpdateNonClientMetrics(This
))
1486 ITrayWindowImpl_SetWindowsFont(This
);
1489 /* Move the tray window to the right position and resize it if neccessary */
1490 ITrayWindowImpl_CheckTrayWndPosition(This
);
1492 /* Align all controls on the tray window */
1493 ITrayWindowImpl_AlignControls(This
,
1497 static HRESULT STDMETHODCALLTYPE
1498 ITrayWindowImpl_QueryInterface(IN OUT ITrayWindow
*iface
,
1502 ITrayWindowImpl
*This
;
1507 This
= impl_from_ITrayWindow(iface
);
1509 if (IsEqualIID(riid
,
1512 *ppvObj
= IUnknown_from_impl(This
);
1514 else if (IsEqualIID(riid
,
1515 &IID_IShellDesktopTray
))
1517 *ppvObj
= IShellDesktopTray_from_impl(This
);
1522 return E_NOINTERFACE
;
1525 ITrayWindowImpl_AddRef(iface
);
1529 static ITrayWindowImpl
*
1530 ITrayWindowImpl_Construct(VOID
)
1532 ITrayWindowImpl
*This
;
1534 This
= HeapAlloc(hProcessHeap
,
1542 This
->lpVtbl
= &ITrayWindowImpl_Vtbl
;
1543 This
->lpVtblShellDesktopTray
= &IShellDesktopTrayImpl_Vtbl
;
1545 This
->Position
= (DWORD
)-1;
1550 static HRESULT STDMETHODCALLTYPE
1551 ITrayWindowImpl_Open(IN OUT ITrayWindow
*iface
)
1553 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1558 /* Check if there's already a window created and try to show it.
1559 If it was somehow destroyed just create a new tray window. */
1560 if (This
->hWnd
!= NULL
)
1562 if (IsWindow(This
->hWnd
))
1564 if (!IsWindowVisible(This
->hWnd
))
1566 ITrayWindowImpl_CheckTrayWndPosition(This
);
1568 ShowWindow(This
->hWnd
,
1573 goto TryCreateTrayWnd
;
1580 dwExStyle
= WS_EX_TOOLWINDOW
| WS_EX_WINDOWEDGE
;
1581 if (This
->AlwaysOnTop
)
1582 dwExStyle
|= WS_EX_TOPMOST
;
1584 if (This
->Position
!= (DWORD
)-1)
1585 rcWnd
= This
->rcTrayWnd
[This
->Position
];
1592 hWnd
= CreateWindowEx(dwExStyle
,
1595 WS_POPUP
| WS_VISIBLE
| WS_CLIPSIBLINGS
| WS_CLIPCHILDREN
|
1596 WS_BORDER
| WS_THICKFRAME
,
1599 rcWnd
.right
- rcWnd
.left
,
1600 rcWnd
.bottom
- rcWnd
.top
,
1612 static HRESULT STDMETHODCALLTYPE
1613 ITrayWindowImpl_Close(IN OUT ITrayWindow
*iface
)
1615 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1617 if (This
->hWnd
!= NULL
)
1619 SendMessage(This
->hWnd
,
1628 static HWND STDMETHODCALLTYPE
1629 ITrayWindowImpl_GetHWND(IN OUT ITrayWindow
*iface
)
1631 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1636 static BOOL STDMETHODCALLTYPE
1637 ITrayWindowImpl_IsSpecialHWND(IN OUT ITrayWindow
*iface
,
1640 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1642 return (hWnd
== This
->hWnd
||
1643 (This
->hWndDesktop
!= NULL
&& hWnd
== This
->hWndDesktop
));
1646 static BOOL STDMETHODCALLTYPE
1647 ITrayWindowImpl_IsHorizontal(IN OUT ITrayWindow
*iface
)
1649 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1650 return ITrayWindowImpl_IsPosHorizontal(This
);
1653 static HFONT STDMETHODCALLTYPE
1654 ITrayWIndowImpl_GetCaptionFonts(IN OUT ITrayWindow
*iface
,
1655 OUT HFONT
*phBoldCaption OPTIONAL
)
1657 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1659 if (phBoldCaption
!= NULL
)
1660 *phBoldCaption
= This
->hStartBtnFont
;
1662 return This
->hCaptionFont
;
1665 static HWND STDMETHODCALLTYPE
1666 ITrayWindowImpl_DisplayProperties(IN OUT ITrayWindow
*iface
)
1668 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1670 if (This
->hWndTrayProperties
!= NULL
)
1672 BringWindowToTop(This
->hWndTrayProperties
);
1673 return This
->hWndTrayProperties
;
1676 This
->hWndTrayProperties
= DisplayTrayProperties(ITrayWindow_from_impl(This
));
1677 return This
->hWndTrayProperties
;
1681 OpenCommonStartMenuDirectory(IN HWND hWndOwner
,
1682 IN LPCTSTR lpOperation
)
1684 TCHAR szDir
[MAX_PATH
];
1686 if (SHGetSpecialFolderPath(hWndOwner
,
1688 CSIDL_COMMON_STARTMENU
,
1691 ShellExecute(hWndOwner
,
1701 OpenTaskManager(IN HWND hWndOwner
)
1703 ShellExecute(hWndOwner
,
1705 TEXT("taskmgr.exe"),
1711 static BOOL STDMETHODCALLTYPE
1712 ITrayWindowImpl_ExecContextMenuCmd(IN OUT ITrayWindow
*iface
,
1715 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1716 BOOL bHandled
= TRUE
;
1720 case ID_SHELL_CMD_PROPERTIES
:
1721 ITrayWindow_DisplayProperties(iface
);
1724 case ID_SHELL_CMD_OPEN_ALL_USERS
:
1725 OpenCommonStartMenuDirectory(This
->hWnd
,
1729 case ID_SHELL_CMD_EXPLORE_ALL_USERS
:
1730 OpenCommonStartMenuDirectory(This
->hWnd
,
1734 case ID_LOCKTASKBAR
:
1735 if (SHRestricted(REST_CLASSICSHELL
) == 0)
1737 ITrayWindow_Lock(iface
,
1742 case ID_SHELL_CMD_OPEN_TASKMGR
:
1743 OpenTaskManager(This
->hWnd
);
1748 DbgPrint("ITrayWindow::ExecContextMenuCmd(%u): Unhandled Command ID!\n", uiCmd
);
1756 static BOOL STDMETHODCALLTYPE
1757 ITrayWindowImpl_Lock(IN OUT ITrayWindow
*iface
,
1761 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1763 bPrevLock
= This
->Locked
;
1764 if (This
->Locked
!= bLock
)
1766 This
->Locked
= bLock
;
1768 if (This
->TrayBandSite
!= NULL
)
1770 if (!SUCCEEDED(ITrayBandSite_Lock(This
->TrayBandSite
,
1774 This
->Locked
= bPrevLock
;
1782 static const ITrayWindowVtbl ITrayWindowImpl_Vtbl
=
1785 ITrayWindowImpl_QueryInterface
,
1786 ITrayWindowImpl_AddRef
,
1787 ITrayWindowImpl_Release
,
1789 ITrayWindowImpl_Open
,
1790 ITrayWindowImpl_Close
,
1791 ITrayWindowImpl_GetHWND
,
1792 ITrayWindowImpl_IsSpecialHWND
,
1793 ITrayWindowImpl_IsHorizontal
,
1794 ITrayWIndowImpl_GetCaptionFonts
,
1795 ITrayWindowImpl_DisplayProperties
,
1796 ITrayWindowImpl_ExecContextMenuCmd
,
1797 ITrayWindowImpl_Lock
1800 static LRESULT CALLBACK
1801 TrayWndProc(IN HWND hwnd
,
1806 ITrayWindowImpl
*This
= NULL
;
1807 LRESULT Ret
= FALSE
;
1809 if (uMsg
!= WM_NCCREATE
)
1811 This
= (ITrayWindowImpl
*)GetWindowLongPtr(hwnd
,
1815 if (This
!= NULL
|| uMsg
== WM_NCCREATE
)
1817 if (This
!= NULL
&& This
->StartMenuBand
!= NULL
)
1824 Msg
.wParam
= wParam
;
1825 Msg
.lParam
= lParam
;
1827 if (IMenuBand_TranslateMenuMessage(This
->StartMenuBand
,
1834 wParam
= Msg
.wParam
;
1835 lParam
= Msg
.lParam
;
1847 /* The user may not be able to resize the tray window.
1848 Pretend like the window is not sizeable when the user
1849 clicks on the border. */
1853 if (GetClientRect(hwnd
,
1855 MapWindowPoints(hwnd
,
1860 pt
.x
= (SHORT
)LOWORD(lParam
);
1861 pt
.y
= (SHORT
)HIWORD(lParam
);
1863 if (PtInRect(&rcClient
,
1866 /* The user is trying to drag the tray window */
1870 /* Depending on the position of the tray window, allow only
1871 changing the border next to the monitor working area */
1872 switch (This
->Position
)
1875 if (pt
.y
> rcClient
.bottom
)
1880 if (pt
.y
< rcClient
.top
)
1885 if (pt
.x
> rcClient
.right
)
1890 if (pt
.x
< rcClient
.left
)
1905 PRECT pRect
= (PRECT
)lParam
;
1907 /* We need to ensure that an application can not accidently
1908 move the tray window (using SetWindowPos). However, we still
1909 need to be able to move the window in case the user wants to
1910 drag the tray window to another position or in case the user
1911 wants to resize the tray window. */
1912 if (!This
->Locked
&& GetCursorPos(&ptCursor
))
1914 This
->IsDragging
= TRUE
;
1915 This
->DraggingPosition
= ITrayWindowImpl_GetDraggingRectFromPt(This
,
1918 &This
->DraggingMonitor
);
1922 *pRect
= This
->rcTrayWnd
[This
->Position
];
1929 PRECT pRect
= (PRECT
)lParam
;
1933 ITrayWindowImpl_CalculateValidSize(This
,
1939 *pRect
= This
->rcTrayWnd
[This
->Position
];
1944 case WM_WINDOWPOSCHANGING
:
1946 ITrayWindowImpl_ChangingWinPos(This
,
1947 (LPWINDOWPOS
)lParam
);
1955 if (wParam
== SIZE_RESTORED
&& lParam
== 0)
1957 /* Clip the tray window on multi monitor systems so the edges can't
1958 overlap into another monitor */
1959 ITrayWindowImpl_ApplyClipping(This
,
1962 if (!GetClientRect(This
->hWnd
,
1970 rcClient
.left
= rcClient
.top
= 0;
1971 rcClient
.right
= LOWORD(lParam
);
1972 rcClient
.bottom
= HIWORD(lParam
);
1975 ITrayWindowImpl_AlignControls(This
,
1980 case WM_ENTERSIZEMOVE
:
1981 This
->InSizeMove
= TRUE
;
1982 This
->IsDragging
= FALSE
;
1985 /* Remove the clipping on multi monitor systems while dragging around */
1986 ITrayWindowImpl_ApplyClipping(This
,
1991 case WM_EXITSIZEMOVE
:
1992 This
->InSizeMove
= FALSE
;
1995 /* Apply clipping */
1996 PostMessage(This
->hWnd
,
2008 /* The user pressed Alt+Space, this usually brings up the system menu of a window.
2009 The tray window needs to handle this specially, since it normally doesn't have
2012 static const UINT uidDisableItem
[] = {
2023 /* temporarily enable the system menu */
2024 SetWindowStyle(hwnd
,
2028 hSysMenu
= GetSystemMenu(hwnd
,
2030 if (hSysMenu
!= NULL
)
2032 /* Disable all items that are not relevant */
2033 for (i
= 0; i
!= sizeof(uidDisableItem
) / sizeof(uidDisableItem
[0]); i
++)
2035 EnableMenuItem(hSysMenu
,
2037 MF_BYCOMMAND
| MF_GRAYED
);
2040 EnableMenuItem(hSysMenu
,
2043 (SHRestricted(REST_NOCLOSE
) ? MF_GRAYED
: MF_ENABLED
));
2045 /* Display the system menu */
2046 uId
= ITrayWindowImpl_TrackMenu(This
,
2050 This
->Position
!= ABE_TOP
,
2054 SendMessage(This
->hWnd
,
2061 /* revert the system menu window style */
2062 SetWindowStyle(hwnd
,
2073 case WM_NCRBUTTONUP
:
2074 /* We want the user to be able to get a context menu even on the nonclient
2075 area (including the sizing border)! */
2076 uMsg
= WM_CONTEXTMENU
;
2077 wParam
= (WPARAM
)hwnd
;
2080 case WM_CONTEXTMENU
:
2082 POINT pt
, *ppt
= NULL
;
2083 HWND hWndExclude
= NULL
;
2085 /* Check if the administrator has forbidden access to context menus */
2086 if (SHRestricted(REST_NOTRAYCONTEXTMENU
))
2089 pt
.x
= (SHORT
)LOWORD(lParam
);
2090 pt
.y
= (SHORT
)HIWORD(lParam
);
2092 if (pt
.x
!= -1 || pt
.y
!= -1)
2095 hWndExclude
= This
->hwndStart
;
2097 if ((HWND
)wParam
== This
->hwndStart
)
2099 /* Make sure we can't track the context menu if the start
2100 menu is currently being shown */
2101 if (!(SendMessage(This
->hwndStart
,
2106 ITrayWindowImpl_TrackCtxMenu(This
,
2107 &StartMenuBtnCtxMenu
,
2110 This
->Position
== ABE_BOTTOM
,
2116 /* See if the context menu should be handled by the task band site */
2117 if (ppt
!= NULL
&& This
->TrayBandSite
!= NULL
)
2120 POINT ptClient
= *ppt
;
2122 /* Convert the coordinates to client-coordinates */
2123 MapWindowPoints(NULL
,
2128 hWndAtPt
= ChildWindowFromPoint(This
->hWnd
,
2130 if (hWndAtPt
!= NULL
&&
2131 (hWndAtPt
== This
->hwndRebar
|| IsChild(This
->hwndRebar
,
2134 /* Check if the user clicked on the task switch window */
2136 MapWindowPoints(NULL
,
2141 hWndAtPt
= ChildWindowFromPointEx(This
->hwndRebar
,
2143 CWP_SKIPINVISIBLE
| CWP_SKIPDISABLED
);
2144 if (hWndAtPt
== This
->hwndTaskSwitch
)
2145 goto HandleTrayContextMenu
;
2147 /* Forward the message to the task band site */
2148 ITrayBandSite_ProcessMessage(This
->TrayBandSite
,
2156 goto HandleTrayContextMenu
;
2160 HandleTrayContextMenu
:
2161 /* Tray the default tray window context menu */
2162 ITrayWindowImpl_TrackCtxMenu(This
,
2175 /* FIXME: We can't check with IsChild whether the hwnd is somewhere inside
2176 the rebar control! But we shouldn't forward messages that the band
2177 site doesn't handle, such as other controls (start button, tray window */
2178 if (This
->TrayBandSite
== NULL
||
2179 !SUCCEEDED(ITrayBandSite_ProcessMessage(This
->TrayBandSite
,
2186 const NMHDR
*nmh
= (const NMHDR
*)lParam
;
2188 if (nmh
->hwndFrom
== This
->hwndTrayNotify
)
2193 /* Cause all controls to be aligned */
2194 PostMessage(This
->hWnd
,
2205 case WM_NCLBUTTONDBLCLK
:
2206 /* We "handle" this message so users can't cause a weird maximize/restore
2207 window animation when double-clicking the tray window! */
2212 LPCREATESTRUCT CreateStruct
= (LPCREATESTRUCT
)lParam
;
2213 This
= (ITrayWindowImpl
*)CreateStruct
->lpCreateParams
;
2215 if (InterlockedCompareExchangePointer((PVOID
*)&This
->hWnd
,
2219 /* Somebody else was faster... */
2223 SetWindowLongPtr(hwnd
,
2227 return ITrayWindowImpl_NCCreate(This
);
2231 ITrayWindowImpl_Create(This
);
2235 ITrayWindowImpl_Destroy(This
);
2238 case WM_APP_TRAYDESTROY
:
2239 DestroyWindow(hwnd
);
2243 if ((HWND
)lParam
== This
->hwndStart
)
2245 if (This
->StartMenuPopup
!= NULL
)
2251 if (GetWindowRect(This
->hwndStart
,
2254 if (ITrayWindowImpl_IsPosHorizontal(This
))
2256 pt
.x
= rcExclude
.left
;
2257 pt
.y
= rcExclude
.top
;
2258 dwFlags
|= MPPF_BOTTOM
;
2262 if (This
->Position
== ABE_LEFT
)
2263 pt
.x
= rcExclude
.left
;
2265 pt
.x
= rcExclude
.right
;
2267 pt
.y
= rcExclude
.bottom
;
2268 dwFlags
|= MPPF_BOTTOM
;
2271 IMenuPopup_Popup(This
->StartMenuPopup
,
2280 if (This
->TrayBandSite
== NULL
||
2281 !SUCCEEDED(ITrayBandSite_ProcessMessage(This
->TrayBandSite
,
2288 switch(LOWORD(wParam
))
2290 /* FIXME: Handle these commands as well */
2291 case IDM_TASKBARANDSTARTMENU
:
2293 case IDM_HELPANDSUPPORT
:
2299 RUNFILEDLG RunFileDlg
;
2301 hShell32
= GetModuleHandle(TEXT("SHELL32.DLL"));
2302 RunFileDlg
= (RUNFILEDLG
)GetProcAddress(hShell32
, (LPCSTR
)61);
2304 RunFileDlg(hwnd
, NULL
, NULL
, NULL
, NULL
, RFF_CALCDIRECTORY
);
2308 /* FIXME: Handle these commands as well */
2309 case IDM_SYNCHRONIZE
:
2311 case IDM_DISCONNECT
:
2312 case IDM_UNDOCKCOMPUTER
:
2318 EXITWINDLG ExitWinDlg
;
2320 hShell32
= GetModuleHandle(TEXT("SHELL32.DLL"));
2321 ExitWinDlg
= (EXITWINDLG
)GetProcAddress(hShell32
, (LPCSTR
)60);
2337 Ret
= DefWindowProc(hwnd
,
2347 * Tray Window Context Menu
2351 CreateTrayWindowContextMenu(IN HWND hWndOwner
,
2352 IN PVOID
*ppcmContext
,
2353 IN PVOID Context OPTIONAL
)
2355 ITrayWindowImpl
*This
= (ITrayWindowImpl
*)Context
;
2356 IContextMenu
*pcm
= NULL
;
2359 hPopup
= LoadPopupMenu(hExplorerInstance
,
2360 MAKEINTRESOURCE(IDM_TRAYWND
));
2364 if (SHRestricted(REST_CLASSICSHELL
) != 0)
2371 CheckMenuItem(hPopup
,
2373 MF_BYCOMMAND
| (This
->Locked
? MF_CHECKED
: MF_UNCHECKED
));
2375 if (This
->TrayBandSite
!= NULL
)
2377 if (SUCCEEDED(ITrayBandSite_AddContextMenus(This
->TrayBandSite
,
2385 DbgPrint("ITrayBandSite::AddContextMenus succeeded!\n");
2386 *(IContextMenu
**)ppcmContext
= pcm
;
2395 OnTrayWindowContextMenuCommand(IN HWND hWndOwner
,
2397 IN PVOID pcmContext OPTIONAL
,
2398 IN PVOID Context OPTIONAL
)
2400 ITrayWindowImpl
*This
= (ITrayWindowImpl
*)Context
;
2401 IContextMenu
*pcm
= (IContextMenu
*)pcmContext
;
2405 if (uiCmdId
>= ID_SHELL_CMD_FIRST
&& uiCmdId
<= ID_SHELL_CMD_LAST
)
2407 CMINVOKECOMMANDINFO cmici
= {0};
2411 /* Setup and invoke the shell command */
2412 cmici
.cbSize
= sizeof(cmici
);
2413 cmici
.hwnd
= hWndOwner
;
2414 cmici
.lpVerb
= (LPCSTR
)MAKEINTRESOURCE(uiCmdId
- ID_SHELL_CMD_FIRST
);
2415 cmici
.nShow
= SW_NORMAL
;
2417 IContextMenu_InvokeCommand(pcm
,
2423 ITrayWindow_ExecContextMenuCmd(ITrayWindow_from_impl(This
),
2429 IContextMenu_Release(pcm
);
2432 static const TRAYWINDOW_CTXMENU TrayWindowCtxMenu
= {
2433 CreateTrayWindowContextMenu
,
2434 OnTrayWindowContextMenuCommand
2437 /*****************************************************************************/
2440 RegisterTrayWindowClass(VOID
)
2445 if (!RegisterTrayNotifyWndClass())
2448 wcTrayWnd
.style
= CS_DBLCLKS
;
2449 wcTrayWnd
.lpfnWndProc
= TrayWndProc
;
2450 wcTrayWnd
.cbClsExtra
= 0;
2451 wcTrayWnd
.cbWndExtra
= sizeof(ITrayWindowImpl
*);
2452 wcTrayWnd
.hInstance
= hExplorerInstance
;
2453 wcTrayWnd
.hIcon
= NULL
;
2454 wcTrayWnd
.hCursor
= LoadCursor(NULL
,
2456 wcTrayWnd
.hbrBackground
= (HBRUSH
)(COLOR_3DFACE
+ 1);
2457 wcTrayWnd
.lpszMenuName
= NULL
;
2458 wcTrayWnd
.lpszClassName
= szTrayWndClass
;
2460 Ret
= RegisterClass(&wcTrayWnd
) != 0;
2463 UnregisterTrayNotifyWndClass();
2469 UnregisterTrayWindowClass(VOID
)
2471 UnregisterTrayNotifyWndClass();
2473 UnregisterClass(szTrayWndClass
,
2478 CreateTrayWindow(VOID
)
2480 ITrayWindowImpl
*This
;
2481 ITrayWindow
*TrayWindow
;
2483 This
= ITrayWindowImpl_Construct();
2486 TrayWindow
= ITrayWindow_from_impl(This
);
2488 ITrayWindowImpl_Open(TrayWindow
);
2497 TrayProcessMessages(IN OUT ITrayWindow
*Tray
)
2499 ITrayWindowImpl
*This
;
2502 This
= impl_from_ITrayWindow(Tray
);
2504 /* FIXME: We should keep a reference here... */
2506 while (PeekMessage(&Msg
,
2512 if (Msg
.message
== WM_QUIT
)
2515 if (This
->StartMenuBand
== NULL
||
2516 IMenuBand_IsMenuMessage(This
->StartMenuBand
,
2519 TranslateMessage(&Msg
);
2520 DispatchMessage(&Msg
);
2526 TrayMessageLoop(IN OUT ITrayWindow
*Tray
)
2528 ITrayWindowImpl
*This
;
2532 This
= impl_from_ITrayWindow(Tray
);
2534 /* FIXME: We should keep a reference here... */
2538 Ret
= (GetMessage(&Msg
,
2548 if (This
->StartMenuBand
== NULL
||
2549 IMenuBand_IsMenuMessage(This
->StartMenuBand
,
2552 TranslateMessage(&Msg
);
2553 DispatchMessage(&Msg
);
2562 * NOTE: This is a very windows-specific COM interface used by SHCreateDesktop()!
2563 * These are the calls I observed, it may be wrong/incomplete/buggy!!!
2564 * The reason we implement it is because we have to use SHCreateDesktop() so
2565 * that the shell provides the desktop window and all the features that come
2566 * with it (especially positioning of desktop icons)
2569 static HRESULT STDMETHODCALLTYPE
2570 ITrayWindowImpl_IShellDesktopTray_QueryInterface(IN OUT IShellDesktopTray
*iface
,
2574 ITrayWindowImpl
*This
= impl_from_IShellDesktopTray(iface
);
2575 ITrayWindow
*tray
= ITrayWindow_from_impl(This
);
2577 DbgPrint("IShellDesktopTray::QueryInterface(0x%p, 0x%p)\n", riid
, ppvObj
);
2578 return ITrayWindowImpl_QueryInterface(tray
,
2583 static ULONG STDMETHODCALLTYPE
2584 ITrayWindowImpl_IShellDesktopTray_Release(IN OUT IShellDesktopTray
*iface
)
2586 ITrayWindowImpl
*This
= impl_from_IShellDesktopTray(iface
);
2587 ITrayWindow
*tray
= ITrayWindow_from_impl(This
);
2589 DbgPrint("IShellDesktopTray::Release()\n");
2590 return ITrayWindowImpl_Release(tray
);
2593 static ULONG STDMETHODCALLTYPE
2594 ITrayWindowImpl_IShellDesktopTray_AddRef(IN OUT IShellDesktopTray
*iface
)
2596 ITrayWindowImpl
*This
= impl_from_IShellDesktopTray(iface
);
2597 ITrayWindow
*tray
= ITrayWindow_from_impl(This
);
2599 DbgPrint("IShellDesktopTray::AddRef()\n");
2600 return ITrayWindowImpl_AddRef(tray
);
2603 static ULONG STDMETHODCALLTYPE
2604 ITrayWindowImpl_IShellDesktopTray_GetState(IN OUT IShellDesktopTray
*iface
)
2606 /* FIXME: Return ABS_ flags? */
2607 DbgPrint("IShellDesktopTray::GetState() unimplemented!\n");
2611 static HRESULT STDMETHODCALLTYPE
2612 ITrayWindowImpl_IShellDesktopTray_GetTrayWindow(IN OUT IShellDesktopTray
*iface
,
2613 OUT HWND
*phWndTray
)
2615 ITrayWindowImpl
*This
= impl_from_IShellDesktopTray(iface
);
2616 DbgPrint("IShellDesktopTray::GetTrayWindow(0x%p)\n", phWndTray
);
2617 *phWndTray
= This
->hWnd
;
2621 static HRESULT STDMETHODCALLTYPE
2622 ITrayWindowImpl_IShellDesktopTray_RegisterDesktopWindow(IN OUT IShellDesktopTray
*iface
,
2623 IN HWND hWndDesktop
)
2625 ITrayWindowImpl
*This
= impl_from_IShellDesktopTray(iface
);
2626 DbgPrint("IShellDesktopTray::RegisterDesktopWindow(0x%p)\n", hWndDesktop
);
2628 This
->hWndDesktop
= hWndDesktop
;
2632 static HRESULT STDMETHODCALLTYPE
2633 ITrayWindowImpl_IShellDesktopTray_Unknown(IN OUT IShellDesktopTray
*iface
,
2634 IN DWORD dwUnknown1
,
2635 IN DWORD dwUnknown2
)
2637 DbgPrint("IShellDesktopTray::Unknown(%u,%u) unimplemented!\n", dwUnknown1
, dwUnknown2
);
2641 static const IShellDesktopTrayVtbl IShellDesktopTrayImpl_Vtbl
=
2644 ITrayWindowImpl_IShellDesktopTray_QueryInterface
,
2645 ITrayWindowImpl_IShellDesktopTray_AddRef
,
2646 ITrayWindowImpl_IShellDesktopTray_Release
,
2647 /*** IShellDesktopTray ***/
2648 ITrayWindowImpl_IShellDesktopTray_GetState
,
2649 ITrayWindowImpl_IShellDesktopTray_GetTrayWindow
,
2650 ITrayWindowImpl_IShellDesktopTray_RegisterDesktopWindow
,
2651 ITrayWindowImpl_IShellDesktopTray_Unknown