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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 GetWindowLong(This
->hWnd
,
368 GetWindowLong(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 (sr
.Position
> ABE_BOTTOM
)
703 This
->Position
= ABE_BOTTOM
;
705 This
->Position
= sr
.Position
;
707 /* Try to find out which monitor the tray window was located on last.
708 Here we're only interested in the monitor screen that we think
709 is the last one used. We're going to determine on which monitor
710 we really are after calculating the docked position. */
712 ITrayWindowImpl_GetScreenRectFromRect(This
,
714 MONITOR_DEFAULTTONEAREST
);
718 This
->Position
= ABE_BOTTOM
;
720 /* Use the minimum size of the taskbar, we'll use the start
721 button as a minimum for now. Make sure we calculate the
722 entire window size, not just the client size. However, we
723 use a thinner border than a standard thick border, so that
724 the start button and bands are not stuck to the screen border. */
725 sr
.Size
.cx
= This
->StartBtnSize
.cx
+ (2 * (EdgeSize
.cx
+ DlgFrameSize
.cx
));
726 sr
.Size
.cy
= This
->StartBtnSize
.cy
+ (2 * (EdgeSize
.cy
+ DlgFrameSize
.cy
));
728 /* Use the primary screen by default */
731 rcScreen
.right
= GetSystemMetrics(SM_CXSCREEN
);
732 rcScreen
.right
= GetSystemMetrics(SM_CYSCREEN
);
733 ITrayWindowImpl_GetScreenRectFromRect(This
,
735 MONITOR_DEFAULTTOPRIMARY
);
738 /* Determine a minimum tray window rectangle. The "client" height is
739 zero here since we cannot determine an optimal minimum width when
740 loaded as a vertical tray window. We just need to make sure the values
741 loaded from the registry are at least. The windows explorer behaves
742 the same way, it allows the user to save a zero width vertical tray
743 window, but not a zero height horizontal tray window. */
744 WndSize
.cx
= 2 * (EdgeSize
.cx
+ DlgFrameSize
.cx
);
745 WndSize
.cy
= This
->StartBtnSize
.cy
+ (2 * (EdgeSize
.cy
+ DlgFrameSize
.cy
));
747 if (WndSize
.cx
< sr
.Size
.cx
)
748 WndSize
.cx
= sr
.Size
.cx
;
749 if (WndSize
.cy
< sr
.Size
.cy
)
750 WndSize
.cy
= sr
.Size
.cy
;
752 /* Save the calculated size */
753 This
->TraySize
= WndSize
;
755 /* Calculate all docking rectangles. We need to do this here so they're
756 initialized and dragging the tray window to another position gives
762 ITrayWindowImpl_GetTrayRectFromScreenRect(This
,
766 &This
->rcTrayWnd
[Pos
]);
767 // 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);
770 /* Determine which monitor we are on. It shouldn't matter which docked
771 position rectangle we use */
772 This
->Monitor
= ITrayWindowImpl_GetMonitorFromRect(This
,
773 &This
->rcTrayWnd
[ABE_LEFT
]);
777 ITrayWindowImpl_TrackMenu(IN OUT ITrayWindowImpl
*This
,
779 IN POINT
*ppt OPTIONAL
,
780 IN HWND hwndExclude OPTIONAL
,
782 IN BOOL IsContextMenu
)
784 TPMPARAMS tmp
, *ptmp
= NULL
;
789 if (hwndExclude
!= NULL
)
791 /* Get the client rectangle and map it to screen coordinates */
792 if (GetClientRect(hwndExclude
,
794 MapWindowPoints(hwndExclude
,
796 (LPPOINT
)&tmp
.rcExclude
,
806 GetClientRect(This
->hWnd
,
808 MapWindowPoints(This
->hWnd
,
810 (LPPOINT
)&tmp
.rcExclude
,
818 /* NOTE: TrackPopupMenuEx will eventually align the track position
819 for us, no need to take care of it here as long as the
820 coordinates are somewhere within the exclusion rectangle */
821 pt
.x
= ptmp
->rcExclude
.left
;
822 pt
.y
= ptmp
->rcExclude
.top
;
830 tmp
.cbSize
= sizeof(tmp
);
832 fuFlags
= TPM_RETURNCMD
| TPM_VERTICAL
;
833 fuFlags
|= (TrackUp
? TPM_BOTTOMALIGN
: TPM_TOPALIGN
);
835 fuFlags
|= TPM_RIGHTBUTTON
;
837 fuFlags
|= (TrackUp
? TPM_VERNEGANIMATION
: TPM_VERPOSANIMATION
);
839 cmdId
= TrackPopupMenuEx(hMenu
,
850 ITrayWindowImpl_TrackCtxMenu(IN OUT ITrayWindowImpl
*This
,
851 IN
const TRAYWINDOW_CTXMENU
*pMenu
,
852 IN POINT
*ppt OPTIONAL
,
853 IN HWND hwndExclude OPTIONAL
,
855 IN PVOID Context OPTIONAL
)
859 PVOID pcmContext
= NULL
;
861 hPopup
= pMenu
->CreateCtxMenu(This
->hWnd
,
866 cmdId
= ITrayWindowImpl_TrackMenu(This
,
873 pMenu
->CtxMenuCommand(This
->hWnd
,
883 ITrayWindowImpl_Free(ITrayWindowImpl
*This
)
885 HeapFree(hProcessHeap
,
891 static ULONG STDMETHODCALLTYPE
892 ITrayWindowImpl_Release(IN OUT ITrayWindow
*iface
)
894 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
897 Ret
= InterlockedDecrement(&This
->Ref
);
899 ITrayWindowImpl_Free(This
);
905 ITrayWindowImpl_Destroy(ITrayWindowImpl
*This
)
907 (void)InterlockedExchangePointer((PVOID
*)&This
->hWnd
,
910 if (This
->himlStartBtn
!= NULL
)
912 ImageList_Destroy(This
->himlStartBtn
);
913 This
->himlStartBtn
= NULL
;
916 if (This
->hCaptionFont
!= NULL
)
918 DeleteObject(This
->hCaptionFont
);
919 This
->hCaptionFont
= NULL
;
922 if (This
->hStartBtnFont
!= NULL
)
924 DeleteObject(This
->hStartBtnFont
);
925 This
->hStartBtnFont
= NULL
;
928 if (This
->hFont
!= NULL
)
930 DeleteObject(This
->hFont
);
934 if (This
->StartMenuPopup
!= NULL
)
936 IMenuPopup_Release(This
->StartMenuPopup
);
937 This
->StartMenuPopup
= NULL
;
940 if (This
->hbmStartMenu
!= NULL
)
942 DeleteObject(This
->hbmStartMenu
);
943 This
->hbmStartMenu
= NULL
;
946 if (This
->StartMenuBand
!= NULL
)
948 IMenuBand_Release(This
->StartMenuBand
);
949 This
->StartMenuBand
= NULL
;
952 if (This
->TrayBandSite
!= NULL
)
954 /* FIXME: Unload bands */
955 ITrayBandSite_Release(This
->TrayBandSite
);
956 This
->TrayBandSite
= NULL
;
959 ITrayWindowImpl_Release(ITrayWindow_from_impl(This
));
961 if (InterlockedDecrement(&TrayWndCount
) == 0)
965 static ULONG STDMETHODCALLTYPE
966 ITrayWindowImpl_AddRef(IN OUT ITrayWindow
*iface
)
968 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
970 return InterlockedIncrement(&This
->Ref
);
975 ITrayWindowImpl_NCCreate(IN OUT ITrayWindowImpl
*This
)
977 ITrayWindowImpl_AddRef(ITrayWindow_from_impl(This
));
983 ITrayWindowImpl_UpdateStartButton(IN OUT ITrayWindowImpl
*This
,
984 IN HBITMAP hbmStart OPTIONAL
)
988 if (This
->himlStartBtn
== NULL
||
989 !SendMessage(This
->hwndStart
,
994 Size
.cx
= GetSystemMetrics(SM_CXEDGE
);
995 Size
.cy
= GetSystemMetrics(SM_CYEDGE
);
997 if (hbmStart
== NULL
)
999 hbmStart
= (HBITMAP
)SendMessage(This
->hwndStart
,
1005 if (hbmStart
!= NULL
)
1009 if (GetObject(hbmStart
,
1013 Size
.cx
+= bmp
.bmWidth
;
1014 Size
.cy
+= max(bmp
.bmHeight
,
1015 GetSystemMetrics(SM_CYCAPTION
));
1019 /* Huh?! Shouldn't happen... */
1026 Size
.cx
+= GetSystemMetrics(SM_CXMINIMIZED
);
1027 Size
.cy
+= GetSystemMetrics(SM_CYCAPTION
);
1031 /* Save the size of the start button */
1032 This
->StartBtnSize
= Size
;
1036 ITrayWindowImpl_AlignControls(IN OUT ITrayWindowImpl
*This
,
1037 IN PRECT prcClient OPTIONAL
)
1040 SIZE TraySize
, StartSize
;
1041 POINT ptTrayNotify
= {0};
1045 if (prcClient
!= NULL
)
1047 rcClient
= *prcClient
;
1051 if (!GetClientRect(This
->hWnd
,
1058 Horizontal
= ITrayWindowImpl_IsPosHorizontal(This
);
1060 /* We're about to resize/move the start button, the rebar control and
1061 the tray notification control */
1062 dwp
= BeginDeferWindowPos(3);
1066 /* Limit the Start button width to the client width, if neccessary */
1067 StartSize
= This
->StartBtnSize
;
1068 if (StartSize
.cx
> rcClient
.right
)
1069 StartSize
.cx
= rcClient
.right
;
1071 if (This
->hwndStart
!= NULL
)
1073 /* Resize and reposition the button */
1074 dwp
= DeferWindowPos(dwp
,
1081 SWP_NOZORDER
| SWP_NOACTIVATE
);
1086 /* Determine the size that the tray notification window needs */
1090 TraySize
.cy
= rcClient
.bottom
;
1094 TraySize
.cx
= rcClient
.right
;
1098 if (This
->hwndTrayNotify
!= NULL
&&
1099 SendMessage(This
->hwndTrayNotify
,
1100 TNWM_GETMINIMUMSIZE
,
1104 /* Move the tray notification window to the desired location */
1106 ptTrayNotify
.x
= rcClient
.right
- TraySize
.cx
;
1108 ptTrayNotify
.y
= rcClient
.bottom
- TraySize
.cy
;
1110 dwp
= DeferWindowPos(dwp
,
1111 This
->hwndTrayNotify
,
1117 SWP_NOZORDER
| SWP_NOACTIVATE
);
1122 /* Resize/Move the rebar control */
1123 if (This
->hwndRebar
!= NULL
)
1125 POINT ptRebar
= {0};
1128 SetWindowStyle(This
->hwndRebar
,
1130 Horizontal
? 0 : CCS_VERT
);
1134 ptRebar
.x
= StartSize
.cx
+ GetSystemMetrics(SM_CXSIZEFRAME
);
1135 szRebar
.cx
= ptTrayNotify
.x
- ptRebar
.x
;
1136 szRebar
.cy
= rcClient
.bottom
;
1140 ptRebar
.y
= StartSize
.cy
+ GetSystemMetrics(SM_CYSIZEFRAME
);
1141 szRebar
.cx
= rcClient
.right
;
1142 szRebar
.cy
= ptTrayNotify
.y
- ptRebar
.y
;
1145 dwp
= DeferWindowPos(dwp
,
1152 SWP_NOZORDER
| SWP_NOACTIVATE
);
1156 EndDeferWindowPos(dwp
);
1158 if (This
->hwndTaskSwitch
!= NULL
)
1160 /* Update the task switch window configuration */
1161 SendMessage(This
->hwndTaskSwitch
,
1162 TSWM_UPDATETASKBARPOS
,
1169 ITrayWindowImpl_CreateStartBtnImageList(IN OUT ITrayWindowImpl
*This
)
1174 if (This
->himlStartBtn
!= NULL
)
1177 IconSize
.cx
= GetSystemMetrics(SM_CXSMICON
);
1178 IconSize
.cy
= GetSystemMetrics(SM_CYSMICON
);
1180 /* Load the start button icon and create a image list for it */
1181 hIconStart
= LoadImage(hExplorerInstance
,
1182 MAKEINTRESOURCE(IDI_START
),
1186 LR_SHARED
| LR_DEFAULTCOLOR
);
1188 if (hIconStart
!= NULL
)
1190 This
->himlStartBtn
= ImageList_Create(IconSize
.cx
,
1192 ILC_COLOR32
| ILC_MASK
,
1195 if (This
->himlStartBtn
!= NULL
)
1197 if (ImageList_AddIcon(This
->himlStartBtn
,
1203 /* Failed to add the icon! */
1204 ImageList_Destroy(This
->himlStartBtn
);
1205 This
->himlStartBtn
= NULL
;
1213 ITrayWindowImpl_CreateStartButtonBitmap(IN OUT ITrayWindowImpl
*This
)
1215 TCHAR szStartCaption
[32];
1218 HDC hDCScreen
= NULL
;
1219 SIZE Size
, SmallIcon
;
1220 HBITMAP hbmpOld
, hbmp
= NULL
;
1221 HBITMAP hBitmap
= NULL
;
1227 /* NOTE: This is the backwards compatibility code that is used if the
1228 Common Controls Version 6.0 are not available! */
1230 if (!LoadString(hExplorerInstance
,
1233 sizeof(szStartCaption
) / sizeof(szStartCaption
[0])))
1238 /* Load the start button icon */
1239 SmallIcon
.cx
= GetSystemMetrics(SM_CXSMICON
);
1240 SmallIcon
.cy
= GetSystemMetrics(SM_CYSMICON
);
1241 hIconStart
= LoadImage(hExplorerInstance
,
1242 MAKEINTRESOURCE(IDI_START
),
1246 LR_SHARED
| LR_DEFAULTCOLOR
);
1248 hDCScreen
= GetDC(NULL
);
1249 if (hDCScreen
== NULL
)
1252 hDC
= CreateCompatibleDC(hDCScreen
);
1256 hFontOld
= SelectObject(hDC
,
1257 This
->hStartBtnFont
);
1259 Ret
= GetTextExtentPoint32(hDC
,
1261 _tcslen(szStartCaption
),
1269 /* Make sure the height is at least the size of a caption icon. */
1270 if (hIconStart
!= NULL
)
1271 Size
.cx
+= SmallIcon
.cx
+ 4;
1272 Size
.cy
= max(Size
.cy
,
1275 /* Create the bitmap */
1276 hbmp
= CreateCompatibleBitmap(hDCScreen
,
1282 /* Caluclate the button rect */
1285 rcButton
.right
= Size
.cx
;
1286 rcButton
.bottom
= Size
.cy
;
1288 /* Draw the button */
1289 hbmpOld
= SelectObject(hDC
,
1292 Flags
= DC_TEXT
| DC_INBUTTON
;
1293 if (hIconStart
!= NULL
)
1296 if (DrawCapTemp
!= NULL
)
1298 Ret
= DrawCapTemp(NULL
,
1301 This
->hStartBtnFont
,
1313 /* We successfully created the bitmap! */
1318 if (hDCScreen
!= NULL
)
1334 ITrayWindowImpl_Create(IN OUT ITrayWindowImpl
*This
)
1336 TCHAR szStartCaption
[32];
1338 InterlockedIncrement(&TrayWndCount
);
1340 if (!LoadString(hExplorerInstance
,
1343 sizeof(szStartCaption
) / sizeof(szStartCaption
[0])))
1345 szStartCaption
[0] = TEXT('\0');
1348 if (This
->hStartBtnFont
== NULL
|| This
->hCaptionFont
== NULL
)
1350 NONCLIENTMETRICS ncm
;
1352 /* Get the system fonts, we use the caption font,
1353 always bold, though. */
1354 ncm
.cbSize
= sizeof(ncm
);
1355 if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS
,
1360 if (This
->hCaptionFont
== NULL
)
1362 ncm
.lfCaptionFont
.lfWeight
= FW_NORMAL
;
1363 This
->hCaptionFont
= CreateFontIndirect(&ncm
.lfCaptionFont
);
1366 if (This
->hStartBtnFont
== NULL
)
1368 ncm
.lfCaptionFont
.lfWeight
= FW_BOLD
;
1369 This
->hStartBtnFont
= CreateFontIndirect(&ncm
.lfCaptionFont
);
1374 /* Create the Start button */
1375 This
->hwndStart
= CreateWindowEx(0,
1378 WS_CHILD
| WS_VISIBLE
| WS_CLIPSIBLINGS
|
1379 BS_PUSHBUTTON
| BS_CENTER
| BS_VCENTER
| BS_BITMAP
,
1385 (HMENU
)IDC_STARTBTN
,
1388 if (This
->hwndStart
)
1390 SendMessage(This
->hwndStart
,
1392 (WPARAM
)This
->hStartBtnFont
,
1395 if (ITrayWindowImpl_CreateStartBtnImageList(This
))
1397 BUTTON_IMAGELIST bil
;
1399 /* Try to set the start button image. This requires the Common
1400 Controls 6.0 to be present (XP and later) */
1401 bil
.himl
= This
->himlStartBtn
;
1402 bil
.margin
.left
= bil
.margin
.right
= 1;
1403 bil
.margin
.top
= bil
.margin
.bottom
= 1;
1404 bil
.uAlign
= BUTTON_IMAGELIST_ALIGN_LEFT
;
1406 if (!SendMessage(This
->hwndStart
,
1411 /* Fall back to the deprecated method on older systems that don't
1412 support Common Controls 6.0 */
1413 ImageList_Destroy(This
->himlStartBtn
);
1414 This
->himlStartBtn
= NULL
;
1416 goto SetStartBtnImage
;
1419 /* We're using the image list, remove the BS_BITMAP style and
1420 don't center it horizontally */
1421 SetWindowStyle(This
->hwndStart
,
1422 BS_BITMAP
| BS_RIGHT
,
1425 ITrayWindowImpl_UpdateStartButton(This
,
1430 HBITMAP hbmStart
, hbmOld
;
1433 hbmStart
= ITrayWindowImpl_CreateStartButtonBitmap(This
);
1434 if (hbmStart
!= NULL
)
1436 ITrayWindowImpl_UpdateStartButton(This
,
1439 hbmOld
= (HBITMAP
)SendMessage(This
->hwndStart
,
1445 DeleteObject(hbmOld
);
1450 /* Load the saved tray window settings */
1451 ITrayWindowImpl_RegLoadSettings(This
);
1453 /* Create and initialize the start menu */
1454 This
->hbmStartMenu
= LoadBitmap(hExplorerInstance
,
1455 MAKEINTRESOURCE(IDB_STARTMENU
));
1456 This
->StartMenuPopup
= CreateStartMenu(ITrayWindow_from_impl(This
),
1457 &This
->StartMenuBand
,
1461 /* Load the tray band site */
1462 if (This
->TrayBandSite
!= NULL
)
1464 ITrayBandSite_Release(This
->TrayBandSite
);
1467 This
->TrayBandSite
= CreateTrayBandSite(ITrayWindow_from_impl(This
),
1469 &This
->hwndTaskSwitch
);
1471 /* Create the tray notification window */
1472 This
->hwndTrayNotify
= CreateTrayNotifyWnd(ITrayWindow_from_impl(This
),
1475 if (ITrayWindowImpl_UpdateNonClientMetrics(This
))
1477 ITrayWindowImpl_SetWindowsFont(This
);
1480 /* Move the tray window to the right position and resize it if neccessary */
1481 ITrayWindowImpl_CheckTrayWndPosition(This
);
1483 /* Align all controls on the tray window */
1484 ITrayWindowImpl_AlignControls(This
,
1488 static HRESULT STDMETHODCALLTYPE
1489 ITrayWindowImpl_QueryInterface(IN OUT ITrayWindow
*iface
,
1493 ITrayWindowImpl
*This
;
1498 This
= impl_from_ITrayWindow(iface
);
1500 if (IsEqualIID(riid
,
1503 *ppvObj
= IUnknown_from_impl(This
);
1505 else if (IsEqualIID(riid
,
1506 &IID_IShellDesktopTray
))
1508 *ppvObj
= IShellDesktopTray_from_impl(This
);
1513 return E_NOINTERFACE
;
1516 ITrayWindowImpl_AddRef(iface
);
1520 static ITrayWindowImpl
*
1521 ITrayWindowImpl_Construct(VOID
)
1523 ITrayWindowImpl
*This
;
1525 This
= HeapAlloc(hProcessHeap
,
1533 This
->lpVtbl
= &ITrayWindowImpl_Vtbl
;
1534 This
->lpVtblShellDesktopTray
= &IShellDesktopTrayImpl_Vtbl
;
1536 This
->Position
= (DWORD
)-1;
1541 static HRESULT STDMETHODCALLTYPE
1542 ITrayWindowImpl_Open(IN OUT ITrayWindow
*iface
)
1544 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1549 /* Check if there's already a window created and try to show it.
1550 If it was somehow destroyed just create a new tray window. */
1551 if (This
->hWnd
!= NULL
)
1553 if (IsWindow(This
->hWnd
))
1555 if (!IsWindowVisible(This
->hWnd
))
1557 ITrayWindowImpl_CheckTrayWndPosition(This
);
1559 ShowWindow(This
->hWnd
,
1564 goto TryCreateTrayWnd
;
1571 dwExStyle
= WS_EX_TOOLWINDOW
| WS_EX_WINDOWEDGE
;
1572 if (This
->AlwaysOnTop
)
1573 dwExStyle
|= WS_EX_TOPMOST
;
1575 if (This
->Position
!= (DWORD
)-1)
1576 rcWnd
= This
->rcTrayWnd
[This
->Position
];
1583 hWnd
= CreateWindowEx(dwExStyle
,
1586 WS_POPUP
| WS_VISIBLE
| WS_CLIPSIBLINGS
| WS_CLIPCHILDREN
|
1587 WS_BORDER
| WS_THICKFRAME
,
1590 rcWnd
.right
- rcWnd
.left
,
1591 rcWnd
.bottom
- rcWnd
.top
,
1603 static HRESULT STDMETHODCALLTYPE
1604 ITrayWindowImpl_Close(IN OUT ITrayWindow
*iface
)
1606 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1608 if (This
->hWnd
!= NULL
)
1610 SendMessage(This
->hWnd
,
1619 static HWND STDMETHODCALLTYPE
1620 ITrayWindowImpl_GetHWND(IN OUT ITrayWindow
*iface
)
1622 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1627 static BOOL STDMETHODCALLTYPE
1628 ITrayWindowImpl_IsSpecialHWND(IN OUT ITrayWindow
*iface
,
1631 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1633 return (hWnd
== This
->hWnd
||
1634 (This
->hWndDesktop
!= NULL
&& hWnd
== This
->hWndDesktop
));
1637 static BOOL STDMETHODCALLTYPE
1638 ITrayWindowImpl_IsHorizontal(IN OUT ITrayWindow
*iface
)
1640 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1641 return ITrayWindowImpl_IsPosHorizontal(This
);
1644 static HFONT STDMETHODCALLTYPE
1645 ITrayWIndowImpl_GetCaptionFonts(IN OUT ITrayWindow
*iface
,
1646 OUT HFONT
*phBoldCaption OPTIONAL
)
1648 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1650 if (phBoldCaption
!= NULL
)
1651 *phBoldCaption
= This
->hStartBtnFont
;
1653 return This
->hCaptionFont
;
1656 static HWND STDMETHODCALLTYPE
1657 ITrayWindowImpl_DisplayProperties(IN OUT ITrayWindow
*iface
)
1659 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1661 if (This
->hWndTrayProperties
!= NULL
)
1663 BringWindowToTop(This
->hWndTrayProperties
);
1664 return This
->hWndTrayProperties
;
1667 This
->hWndTrayProperties
= DisplayTrayProperties(ITrayWindow_from_impl(This
));
1668 return This
->hWndTrayProperties
;
1672 OpenCommonStartMenuDirectory(IN HWND hWndOwner
,
1673 IN LPCTSTR lpOperation
)
1675 TCHAR szDir
[MAX_PATH
];
1677 if (SHGetSpecialFolderPath(hWndOwner
,
1679 CSIDL_COMMON_STARTMENU
,
1682 ShellExecute(hWndOwner
,
1691 static BOOL STDMETHODCALLTYPE
1692 ITrayWindowImpl_ExecContextMenuCmd(IN OUT ITrayWindow
*iface
,
1695 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1696 BOOL bHandled
= TRUE
;
1700 case ID_SHELL_CMD_PROPERTIES
:
1701 ITrayWindow_DisplayProperties(iface
);
1704 case ID_SHELL_CMD_OPEN_ALL_USERS
:
1705 OpenCommonStartMenuDirectory(This
->hWnd
,
1709 case ID_SHELL_CMD_EXPLORE_ALL_USERS
:
1710 OpenCommonStartMenuDirectory(This
->hWnd
,
1714 case ID_LOCKTASKBAR
:
1715 if (SHRestricted(REST_CLASSICSHELL
) == 0)
1717 ITrayWindow_Lock(iface
,
1723 DbgPrint("ITrayWindow::ExecContextMenuCmd(%u): Unhandled Command ID!\n", uiCmd
);
1731 static BOOL STDMETHODCALLTYPE
1732 ITrayWindowImpl_Lock(IN OUT ITrayWindow
*iface
,
1736 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1738 bPrevLock
= This
->Locked
;
1739 if (This
->Locked
!= bLock
)
1741 This
->Locked
= bLock
;
1743 if (This
->TrayBandSite
!= NULL
)
1745 if (!SUCCEEDED(ITrayBandSite_Lock(This
->TrayBandSite
,
1749 This
->Locked
= bPrevLock
;
1757 static const ITrayWindowVtbl ITrayWindowImpl_Vtbl
=
1760 ITrayWindowImpl_QueryInterface
,
1761 ITrayWindowImpl_AddRef
,
1762 ITrayWindowImpl_Release
,
1764 ITrayWindowImpl_Open
,
1765 ITrayWindowImpl_Close
,
1766 ITrayWindowImpl_GetHWND
,
1767 ITrayWindowImpl_IsSpecialHWND
,
1768 ITrayWindowImpl_IsHorizontal
,
1769 ITrayWIndowImpl_GetCaptionFonts
,
1770 ITrayWindowImpl_DisplayProperties
,
1771 ITrayWindowImpl_ExecContextMenuCmd
,
1772 ITrayWindowImpl_Lock
1775 static LRESULT CALLBACK
1776 TrayWndProc(IN HWND hwnd
,
1781 ITrayWindowImpl
*This
= NULL
;
1782 LRESULT Ret
= FALSE
;
1784 if (uMsg
!= WM_NCCREATE
)
1786 This
= (ITrayWindowImpl
*)GetWindowLongPtr(hwnd
,
1790 if (This
!= NULL
|| uMsg
== WM_NCCREATE
)
1792 if (This
!= NULL
&& This
->StartMenuBand
!= NULL
)
1799 Msg
.wParam
= wParam
;
1800 Msg
.lParam
= lParam
;
1802 if (IMenuBand_TranslateMenuMessage(This
->StartMenuBand
,
1809 wParam
= Msg
.wParam
;
1810 lParam
= Msg
.lParam
;
1822 /* The user may not be able to resize the tray window.
1823 Pretend like the window is not sizeable when the user
1824 clicks on the border. */
1828 if (GetClientRect(hwnd
,
1830 MapWindowPoints(hwnd
,
1835 pt
.x
= (SHORT
)LOWORD(lParam
);
1836 pt
.y
= (SHORT
)HIWORD(lParam
);
1838 if (PtInRect(&rcClient
,
1841 /* The user is trying to drag the tray window */
1845 /* Depending on the position of the tray window, allow only
1846 changing the border next to the monitor working area */
1847 switch (This
->Position
)
1850 if (pt
.y
> rcClient
.bottom
)
1855 if (pt
.y
< rcClient
.top
)
1860 if (pt
.x
> rcClient
.right
)
1865 if (pt
.x
< rcClient
.left
)
1880 PRECT pRect
= (PRECT
)lParam
;
1882 /* We need to ensure that an application can not accidently
1883 move the tray window (using SetWindowPos). However, we still
1884 need to be able to move the window in case the user wants to
1885 drag the tray window to another position or in case the user
1886 wants to resize the tray window. */
1887 if (!This
->Locked
&& GetCursorPos(&ptCursor
))
1889 This
->IsDragging
= TRUE
;
1890 This
->DraggingPosition
= ITrayWindowImpl_GetDraggingRectFromPt(This
,
1893 &This
->DraggingMonitor
);
1897 *pRect
= This
->rcTrayWnd
[This
->Position
];
1904 PRECT pRect
= (PRECT
)lParam
;
1908 ITrayWindowImpl_CalculateValidSize(This
,
1914 *pRect
= This
->rcTrayWnd
[This
->Position
];
1919 case WM_WINDOWPOSCHANGING
:
1921 ITrayWindowImpl_ChangingWinPos(This
,
1922 (LPWINDOWPOS
)lParam
);
1930 if (wParam
== SIZE_RESTORED
&& lParam
== 0)
1932 /* Clip the tray window on multi monitor systems so the edges can't
1933 overlap into another monitor */
1934 ITrayWindowImpl_ApplyClipping(This
,
1937 if (!GetClientRect(This
->hWnd
,
1945 rcClient
.left
= rcClient
.top
= 0;
1946 rcClient
.right
= LOWORD(lParam
);
1947 rcClient
.bottom
= HIWORD(lParam
);
1950 ITrayWindowImpl_AlignControls(This
,
1955 case WM_ENTERSIZEMOVE
:
1956 This
->InSizeMove
= TRUE
;
1957 This
->IsDragging
= FALSE
;
1960 /* Remove the clipping on multi monitor systems while dragging around */
1961 ITrayWindowImpl_ApplyClipping(This
,
1966 case WM_EXITSIZEMOVE
:
1967 This
->InSizeMove
= FALSE
;
1970 /* Apply clipping */
1971 PostMessage(This
->hWnd
,
1983 /* The user pressed Alt+Space, this usually brings up the system menu of a window.
1984 The tray window needs to handle this specially, since it normally doesn't have
1987 static const UINT uidDisableItem
[] = {
1998 /* temporarily enable the system menu */
1999 SetWindowStyle(hwnd
,
2003 hSysMenu
= GetSystemMenu(hwnd
,
2005 if (hSysMenu
!= NULL
)
2007 /* Disable all items that are not relevant */
2008 for (i
= 0; i
!= sizeof(uidDisableItem
) / sizeof(uidDisableItem
[0]); i
++)
2010 EnableMenuItem(hSysMenu
,
2012 MF_BYCOMMAND
| MF_GRAYED
);
2015 EnableMenuItem(hSysMenu
,
2018 (SHRestricted(REST_NOCLOSE
) ? MF_GRAYED
: MF_ENABLED
));
2020 /* Display the system menu */
2021 uId
= ITrayWindowImpl_TrackMenu(This
,
2025 This
->Position
!= ABE_TOP
,
2029 SendMessage(This
->hWnd
,
2036 /* revert the system menu window style */
2037 SetWindowStyle(hwnd
,
2048 case WM_NCRBUTTONUP
:
2049 /* We want the user to be able to get a context menu even on the nonclient
2050 area (including the sizing border)! */
2051 uMsg
= WM_CONTEXTMENU
;
2052 wParam
= (WPARAM
)hwnd
;
2055 case WM_CONTEXTMENU
:
2057 POINT pt
, *ppt
= NULL
;
2058 HWND hWndExclude
= NULL
;
2060 /* Check if the administrator has forbidden access to context menus */
2061 if (SHRestricted(REST_NOTRAYCONTEXTMENU
))
2064 pt
.x
= (SHORT
)LOWORD(lParam
);
2065 pt
.y
= (SHORT
)HIWORD(lParam
);
2067 if (pt
.x
!= -1 || pt
.y
!= -1)
2070 hWndExclude
= This
->hwndStart
;
2072 if ((HWND
)wParam
== This
->hwndStart
)
2074 /* Make sure we can't track the context menu if the start
2075 menu is currently being shown */
2076 if (!(SendMessage(This
->hwndStart
,
2081 ITrayWindowImpl_TrackCtxMenu(This
,
2082 &StartMenuBtnCtxMenu
,
2085 This
->Position
== ABE_BOTTOM
,
2091 /* See if the context menu should be handled by the task band site */
2092 if (ppt
!= NULL
&& This
->TrayBandSite
!= NULL
)
2095 POINT ptClient
= *ppt
;
2097 /* Convert the coordinates to client-coordinates */
2098 MapWindowPoints(NULL
,
2103 hWndAtPt
= ChildWindowFromPoint(This
->hWnd
,
2105 if (hWndAtPt
!= NULL
&&
2106 (hWndAtPt
== This
->hwndRebar
|| IsChild(This
->hwndRebar
,
2109 /* Check if the user clicked on the task switch window */
2111 MapWindowPoints(NULL
,
2116 hWndAtPt
= ChildWindowFromPointEx(This
->hwndRebar
,
2118 CWP_SKIPINVISIBLE
| CWP_SKIPDISABLED
);
2119 if (hWndAtPt
== This
->hwndTaskSwitch
)
2120 goto HandleTrayContextMenu
;
2122 /* Forward the message to the task band site */
2123 ITrayBandSite_ProcessMessage(This
->TrayBandSite
,
2131 goto HandleTrayContextMenu
;
2135 HandleTrayContextMenu
:
2136 /* Tray the default tray window context menu */
2137 ITrayWindowImpl_TrackCtxMenu(This
,
2150 /* FIXME: We can't check with IsChild whether the hwnd is somewhere inside
2151 the rebar control! But we shouldn't forward messages that the band
2152 site doesn't handle, such as other controls (start button, tray window */
2153 if (This
->TrayBandSite
== NULL
||
2154 !SUCCEEDED(ITrayBandSite_ProcessMessage(This
->TrayBandSite
,
2161 const NMHDR
*nmh
= (const NMHDR
*)lParam
;
2163 if (nmh
->hwndFrom
== This
->hwndTrayNotify
)
2168 /* Cause all controls to be aligned */
2169 PostMessage(This
->hWnd
,
2180 case WM_NCLBUTTONDBLCLK
:
2181 /* We "handle" this message so users can't cause a weird maximize/restore
2182 window animation when double-clicking the tray window! */
2187 LPCREATESTRUCT CreateStruct
= (LPCREATESTRUCT
)lParam
;
2188 This
= (ITrayWindowImpl
*)CreateStruct
->lpCreateParams
;
2190 if (InterlockedCompareExchangePointer((PVOID
*)&This
->hWnd
,
2194 /* Somebody else was faster... */
2198 SetWindowLongPtr(hwnd
,
2202 return ITrayWindowImpl_NCCreate(This
);
2206 ITrayWindowImpl_Create(This
);
2210 ITrayWindowImpl_Destroy(This
);
2213 case WM_APP_TRAYDESTROY
:
2214 DestroyWindow(hwnd
);
2218 if ((HWND
)lParam
== This
->hwndStart
)
2220 if (This
->StartMenuPopup
!= NULL
)
2226 if (GetWindowRect(This
->hwndStart
,
2229 if (ITrayWindowImpl_IsPosHorizontal(This
))
2231 pt
.x
= rcExclude
.left
;
2232 pt
.y
= rcExclude
.top
;
2233 dwFlags
|= MPPF_BOTTOM
;
2237 if (This
->Position
== ABE_LEFT
)
2238 pt
.x
= rcExclude
.left
;
2240 pt
.x
= rcExclude
.right
;
2242 pt
.y
= rcExclude
.bottom
;
2243 dwFlags
|= MPPF_BOTTOM
;
2246 IMenuPopup_Popup(This
->StartMenuPopup
,
2255 if (This
->TrayBandSite
== NULL
||
2256 !SUCCEEDED(ITrayBandSite_ProcessMessage(This
->TrayBandSite
,
2263 switch(LOWORD(wParam
))
2265 /* FIXME: Handle these commands as well */
2266 case IDM_TASKBARANDSTARTMENU
:
2268 case IDM_HELPANDSUPPORT
:
2274 RUNFILEDLG RunFileDlg
;
2276 hShell32
= GetModuleHandle(TEXT("SHELL32.DLL"));
2277 RunFileDlg
= (RUNFILEDLG
)GetProcAddress(hShell32
, (LPCSTR
)61);
2279 RunFileDlg(hwnd
, NULL
, NULL
, NULL
, NULL
, RFF_CALCDIRECTORY
);
2283 /* FIXME: Handle these commands as well */
2284 case IDM_SYNCHRONIZE
:
2286 case IDM_DISCONNECT
:
2287 case IDM_UNDOCKCOMPUTER
:
2293 EXITWINDLG ExitWinDlg
;
2295 hShell32
= GetModuleHandle(TEXT("SHELL32.DLL"));
2296 ExitWinDlg
= (EXITWINDLG
)GetProcAddress(hShell32
, (LPCSTR
)60);
2312 Ret
= DefWindowProc(hwnd
,
2322 * Tray Window Context Menu
2326 CreateTrayWindowContextMenu(IN HWND hWndOwner
,
2327 IN PVOID
*ppcmContext
,
2328 IN PVOID Context OPTIONAL
)
2330 ITrayWindowImpl
*This
= (ITrayWindowImpl
*)Context
;
2331 IContextMenu
*pcm
= NULL
;
2334 hPopup
= LoadPopupMenu(hExplorerInstance
,
2335 MAKEINTRESOURCE(IDM_TRAYWND
));
2339 if (SHRestricted(REST_CLASSICSHELL
) != 0)
2346 CheckMenuItem(hPopup
,
2348 MF_BYCOMMAND
| (This
->Locked
? MF_CHECKED
: MF_UNCHECKED
));
2350 if (This
->TrayBandSite
!= NULL
)
2352 if (SUCCEEDED(ITrayBandSite_AddContextMenus(This
->TrayBandSite
,
2360 DbgPrint("ITrayBandSite::AddContextMenus succeeded!\n");
2361 *(IContextMenu
**)ppcmContext
= pcm
;
2370 OnTrayWindowContextMenuCommand(IN HWND hWndOwner
,
2372 IN PVOID pcmContext OPTIONAL
,
2373 IN PVOID Context OPTIONAL
)
2375 ITrayWindowImpl
*This
= (ITrayWindowImpl
*)Context
;
2376 IContextMenu
*pcm
= (IContextMenu
*)pcmContext
;
2380 if (uiCmdId
>= ID_SHELL_CMD_FIRST
&& uiCmdId
<= ID_SHELL_CMD_LAST
)
2382 CMINVOKECOMMANDINFO cmici
= {0};
2386 /* Setup and invoke the shell command */
2387 cmici
.cbSize
= sizeof(cmici
);
2388 cmici
.hwnd
= hWndOwner
;
2389 cmici
.lpVerb
= (LPCSTR
)MAKEINTRESOURCE(uiCmdId
- ID_SHELL_CMD_FIRST
);
2390 cmici
.nShow
= SW_NORMAL
;
2392 IContextMenu_InvokeCommand(pcm
,
2398 ITrayWindow_ExecContextMenuCmd(ITrayWindow_from_impl(This
),
2404 IContextMenu_Release(pcm
);
2407 static const TRAYWINDOW_CTXMENU TrayWindowCtxMenu
= {
2408 CreateTrayWindowContextMenu
,
2409 OnTrayWindowContextMenuCommand
2412 /*****************************************************************************/
2415 RegisterTrayWindowClass(VOID
)
2420 if (!RegisterTrayNotifyWndClass())
2423 wcTrayWnd
.style
= CS_DBLCLKS
;
2424 wcTrayWnd
.lpfnWndProc
= TrayWndProc
;
2425 wcTrayWnd
.cbClsExtra
= 0;
2426 wcTrayWnd
.cbWndExtra
= sizeof(ITrayWindowImpl
*);
2427 wcTrayWnd
.hInstance
= hExplorerInstance
;
2428 wcTrayWnd
.hIcon
= NULL
;
2429 wcTrayWnd
.hCursor
= LoadCursor(NULL
,
2431 wcTrayWnd
.hbrBackground
= (HBRUSH
)(COLOR_3DFACE
+ 1);
2432 wcTrayWnd
.lpszMenuName
= NULL
;
2433 wcTrayWnd
.lpszClassName
= szTrayWndClass
;
2435 Ret
= RegisterClass(&wcTrayWnd
) != 0;
2438 UnregisterTrayNotifyWndClass();
2444 UnregisterTrayWindowClass(VOID
)
2446 UnregisterTrayNotifyWndClass();
2448 UnregisterClass(szTrayWndClass
,
2453 CreateTrayWindow(VOID
)
2455 ITrayWindowImpl
*This
;
2456 ITrayWindow
*TrayWindow
;
2458 This
= ITrayWindowImpl_Construct();
2461 TrayWindow
= ITrayWindow_from_impl(This
);
2463 ITrayWindowImpl_Open(TrayWindow
);
2472 TrayProcessMessages(IN OUT ITrayWindow
*Tray
)
2474 ITrayWindowImpl
*This
;
2477 This
= impl_from_ITrayWindow(Tray
);
2479 /* FIXME: We should keep a reference here... */
2481 while (PeekMessage(&Msg
,
2487 if (Msg
.message
== WM_QUIT
)
2490 if (This
->StartMenuBand
== NULL
||
2491 IMenuBand_IsMenuMessage(This
->StartMenuBand
,
2494 TranslateMessage(&Msg
);
2495 DispatchMessage(&Msg
);
2501 TrayMessageLoop(IN OUT ITrayWindow
*Tray
)
2503 ITrayWindowImpl
*This
;
2507 This
= impl_from_ITrayWindow(Tray
);
2509 /* FIXME: We should keep a reference here... */
2513 Ret
= (GetMessage(&Msg
,
2523 if (This
->StartMenuBand
== NULL
||
2524 IMenuBand_IsMenuMessage(This
->StartMenuBand
,
2527 TranslateMessage(&Msg
);
2528 DispatchMessage(&Msg
);
2537 * NOTE: This is a very windows-specific COM interface used by SHCreateDesktop()!
2538 * These are the calls I observed, it may be wrong/incomplete/buggy!!!
2539 * The reason we implement it is because we have to use SHCreateDesktop() so
2540 * that the shell provides the desktop window and all the features that come
2541 * with it (especially positioning of desktop icons)
2544 static HRESULT STDMETHODCALLTYPE
2545 ITrayWindowImpl_IShellDesktopTray_QueryInterface(IN OUT IShellDesktopTray
*iface
,
2549 ITrayWindowImpl
*This
= impl_from_IShellDesktopTray(iface
);
2550 ITrayWindow
*tray
= ITrayWindow_from_impl(This
);
2552 DbgPrint("IShellDesktopTray::QueryInterface(0x%p, 0x%p)\n", riid
, ppvObj
);
2553 return ITrayWindowImpl_QueryInterface(tray
,
2558 static ULONG STDMETHODCALLTYPE
2559 ITrayWindowImpl_IShellDesktopTray_Release(IN OUT IShellDesktopTray
*iface
)
2561 ITrayWindowImpl
*This
= impl_from_IShellDesktopTray(iface
);
2562 ITrayWindow
*tray
= ITrayWindow_from_impl(This
);
2564 DbgPrint("IShellDesktopTray::Release()\n");
2565 return ITrayWindowImpl_Release(tray
);
2568 static ULONG STDMETHODCALLTYPE
2569 ITrayWindowImpl_IShellDesktopTray_AddRef(IN OUT IShellDesktopTray
*iface
)
2571 ITrayWindowImpl
*This
= impl_from_IShellDesktopTray(iface
);
2572 ITrayWindow
*tray
= ITrayWindow_from_impl(This
);
2574 DbgPrint("IShellDesktopTray::AddRef()\n");
2575 return ITrayWindowImpl_AddRef(tray
);
2578 static ULONG STDMETHODCALLTYPE
2579 ITrayWindowImpl_IShellDesktopTray_GetState(IN OUT IShellDesktopTray
*iface
)
2581 /* FIXME: Return ABS_ flags? */
2582 DbgPrint("IShellDesktopTray::GetState() unimplemented!\n");
2586 static HRESULT STDMETHODCALLTYPE
2587 ITrayWindowImpl_IShellDesktopTray_GetTrayWindow(IN OUT IShellDesktopTray
*iface
,
2588 OUT HWND
*phWndTray
)
2590 ITrayWindowImpl
*This
= impl_from_IShellDesktopTray(iface
);
2591 DbgPrint("IShellDesktopTray::GetTrayWindow(0x%p)\n", phWndTray
);
2592 *phWndTray
= This
->hWnd
;
2596 static HRESULT STDMETHODCALLTYPE
2597 ITrayWindowImpl_IShellDesktopTray_RegisterDesktopWindow(IN OUT IShellDesktopTray
*iface
,
2598 IN HWND hWndDesktop
)
2600 ITrayWindowImpl
*This
= impl_from_IShellDesktopTray(iface
);
2601 DbgPrint("IShellDesktopTray::RegisterDesktopWindow(0x%p)\n", hWndDesktop
);
2603 This
->hWndDesktop
= hWndDesktop
;
2607 static HRESULT STDMETHODCALLTYPE
2608 ITrayWindowImpl_IShellDesktopTray_Unknown(IN OUT IShellDesktopTray
*iface
,
2609 IN DWORD dwUnknown1
,
2610 IN DWORD dwUnknown2
)
2612 DbgPrint("IShellDesktopTray::Unknown(%u,%u) unimplemented!\n", dwUnknown1
, dwUnknown2
);
2616 static const IShellDesktopTrayVtbl IShellDesktopTrayImpl_Vtbl
=
2619 ITrayWindowImpl_IShellDesktopTray_QueryInterface
,
2620 ITrayWindowImpl_IShellDesktopTray_AddRef
,
2621 ITrayWindowImpl_IShellDesktopTray_Release
,
2622 /*** IShellDesktopTray ***/
2623 ITrayWindowImpl_IShellDesktopTray_GetState
,
2624 ITrayWindowImpl_IShellDesktopTray_GetTrayWindow
,
2625 ITrayWindowImpl_IShellDesktopTray_RegisterDesktopWindow
,
2626 ITrayWindowImpl_IShellDesktopTray_Unknown