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
;
97 BOOL
LaunchCPanel(HWND hwnd
, LPCTSTR applet
)
99 TCHAR szParams
[MAX_PATH
];
100 _tcscpy(szParams
, TEXT("shell32.dll,Control_RunDLL "));
101 _tcscat(szParams
, applet
);
102 return (ShellExecute(hwnd
, TEXT("open"), TEXT("rundll32.exe"), szParams
, NULL
, SW_SHOWDEFAULT
) > (HINSTANCE
)32);
106 IUnknown_from_impl(ITrayWindowImpl
*This
)
108 return (IUnknown
*)&This
->lpVtbl
;
112 ITrayWindow_from_impl(ITrayWindowImpl
*This
)
114 return (ITrayWindow
*)&This
->lpVtbl
;
117 static IShellDesktopTray
*
118 IShellDesktopTray_from_impl(ITrayWindowImpl
*This
)
120 return (IShellDesktopTray
*)&This
->lpVtblShellDesktopTray
;
123 static ITrayWindowImpl
*
124 impl_from_ITrayWindow(ITrayWindow
*iface
)
126 return (ITrayWindowImpl
*)((ULONG_PTR
)iface
- FIELD_OFFSET(ITrayWindowImpl
,
130 static ITrayWindowImpl
*
131 impl_from_IShellDesktopTray(IShellDesktopTray
*iface
)
133 return (ITrayWindowImpl
*)((ULONG_PTR
)iface
- FIELD_OFFSET(ITrayWindowImpl
,
134 lpVtblShellDesktopTray
));
142 ITrayWindowImpl_UpdateNonClientMetrics(IN OUT ITrayWindowImpl
*This
)
144 This
->ncm
.cbSize
= sizeof(This
->ncm
);
145 if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS
,
150 if (This
->hFont
!= NULL
)
151 DeleteObject(This
->hFont
);
153 This
->hFont
= CreateFontIndirect(&This
->ncm
.lfMessageFont
);
161 ITrayWindowImpl_SetWindowsFont(IN OUT ITrayWindowImpl
*This
)
163 if (This
->hwndTrayNotify
!= NULL
)
165 SendMessage(This
->hwndTrayNotify
,
173 ITrayWindowImpl_GetScreenRectFromRect(IN OUT ITrayWindowImpl
*This
,
180 mi
.cbSize
= sizeof(mi
);
181 hMon
= MonitorFromRect(pRect
,
187 *pRect
= mi
.rcMonitor
;
193 pRect
->right
= GetSystemMetrics(SM_CXSCREEN
);
194 pRect
->bottom
= GetSystemMetrics(SM_CYSCREEN
);
203 ITrayWindowImpl_GetMonitorFromRect(IN OUT ITrayWindowImpl
*This
,
204 IN
const RECT
*pRect
)
208 /* In case the monitor sizes or saved sizes differ a bit (probably
209 not a lot, only so the tray window overlaps into another monitor
210 now), minimize the risk that we determine a wrong monitor by
211 using the center point of the tray window if we can't determine
212 it using the rectangle. */
213 hMon
= MonitorFromRect(pRect
,
214 MONITOR_DEFAULTTONULL
);
219 pt
.x
= pRect
->left
+ ((pRect
->right
- pRect
->left
) / 2);
220 pt
.y
= pRect
->top
+ ((pRect
->bottom
- pRect
->top
) / 2);
222 /* be less error-prone, find the nearest monitor */
223 hMon
= MonitorFromPoint(pt
,
224 MONITOR_DEFAULTTONEAREST
);
231 ITrayWindowImpl_GetScreenRect(IN OUT ITrayWindowImpl
*This
,
232 IN HMONITOR hMonitor
,
235 HMONITOR hMon
= NULL
;
237 if (hMonitor
!= NULL
)
241 mi
.cbSize
= sizeof(mi
);
242 if (!GetMonitorInfo(hMonitor
,
245 /* Hm, the monitor is gone? Try to find a monitor where it
246 could be located now */
247 hMon
= ITrayWindowImpl_GetMonitorFromRect(This
,
250 !GetMonitorInfo(hMon
,
258 *pRect
= mi
.rcMonitor
;
265 pRect
->right
= GetSystemMetrics(SM_CXSCREEN
);
266 pRect
->bottom
= GetSystemMetrics(SM_CYSCREEN
);
273 ITrayWindowImpl_MakeTrayRectWithSize(IN DWORD Position
,
274 IN
const SIZE
*pTraySize
,
280 pRect
->right
= pRect
->left
+ pTraySize
->cx
;
284 pRect
->bottom
= pRect
->top
+ pTraySize
->cy
;
288 pRect
->left
= pRect
->right
- pTraySize
->cx
;
293 pRect
->top
= pRect
->bottom
- pTraySize
->cy
;
299 ITrayWindowImpl_GetTrayRectFromScreenRect(IN OUT ITrayWindowImpl
*This
,
301 IN
const RECT
*pScreen
,
302 IN
const SIZE
*pTraySize OPTIONAL
,
305 if (pTraySize
== NULL
)
306 pTraySize
= &This
->TraySize
;
310 /* Move the border outside of the screen */
312 GetSystemMetrics(SM_CXEDGE
),
313 GetSystemMetrics(SM_CYEDGE
));
315 ITrayWindowImpl_MakeTrayRectWithSize(Position
,
321 ITrayWindowImpl_IsPosHorizontal(IN OUT ITrayWindowImpl
*This
)
323 return This
->Position
== ABE_TOP
|| This
->Position
== ABE_BOTTOM
;
327 ITrayWindowImpl_CalculateValidSize(IN OUT ITrayWindowImpl
*This
,
336 //Horizontal = ITrayWindowImpl_IsPosHorizontal(This);
338 szWnd
.cx
= pRect
->right
- pRect
->left
;
339 szWnd
.cy
= pRect
->bottom
- pRect
->top
;
342 hMon
= ITrayWindowImpl_GetScreenRectFromRect(This
,
344 MONITOR_DEFAULTTONEAREST
);
346 /* Calculate the maximum size of the tray window and limit the window
347 size to half of the screen's size. */
348 szMax
.cx
= (rcScreen
.right
- rcScreen
.left
) / 2;
349 szMax
.cy
= (rcScreen
.bottom
- rcScreen
.top
) / 2;
350 if (szWnd
.cx
> szMax
.cx
)
352 if (szWnd
.cy
> szMax
.cy
)
355 /* FIXME - calculate */
357 ITrayWindowImpl_GetTrayRectFromScreenRect(This
,
368 ITrayWindowImpl_GetMinimumWindowSize(IN OUT ITrayWindowImpl
*This
,
373 AdjustWindowRectEx(&rcMin
,
374 GetWindowLongPtr(This
->hWnd
,
377 GetWindowLongPtr(This
->hWnd
,
386 ITrayWindowImpl_GetDraggingRectFromPt(IN OUT ITrayWindowImpl
*This
,
389 OUT HMONITOR
*phMonitor
)
391 HMONITOR hMon
, hMonNew
;
392 DWORD PosH
, PosV
, Pos
;
393 SIZE DeltaPt
, ScreenOffset
;
399 /* Determine the screen rectangle */
400 hMon
= MonitorFromPoint(pt
,
401 MONITOR_DEFAULTTONULL
);
407 mi
.cbSize
= sizeof(mi
);
408 if (!GetMonitorInfo(hMon
,
412 goto GetPrimaryScreenRect
;
415 /* make left top corner of the screen zero based to
416 make calculations easier */
417 pt
.x
-= mi
.rcMonitor
.left
;
418 pt
.y
-= mi
.rcMonitor
.top
;
420 ScreenOffset
.cx
= mi
.rcMonitor
.left
;
421 ScreenOffset
.cy
= mi
.rcMonitor
.top
;
422 rcScreen
.right
= mi
.rcMonitor
.right
- mi
.rcMonitor
.left
;
423 rcScreen
.bottom
= mi
.rcMonitor
.bottom
- mi
.rcMonitor
.top
;
427 GetPrimaryScreenRect
:
430 rcScreen
.right
= GetSystemMetrics(SM_CXSCREEN
);
431 rcScreen
.bottom
= GetSystemMetrics(SM_CYSCREEN
);
434 /* Calculate the nearest screen border */
435 if (pt
.x
< rcScreen
.right
/ 2)
442 DeltaPt
.cx
= rcScreen
.right
- pt
.x
;
446 if (pt
.y
< rcScreen
.bottom
/ 2)
453 DeltaPt
.cy
= rcScreen
.bottom
- pt
.y
;
457 Pos
= (DeltaPt
.cx
* rcScreen
.bottom
< DeltaPt
.cy
* rcScreen
.right
) ? PosH
: PosV
;
459 /* Fix the screen origin to be relative to the primary monitor again */
460 OffsetRect(&rcScreen
,
464 hMonNew
= ITrayWindowImpl_GetMonitorFromRect(This
,
465 &This
->rcTrayWnd
[Pos
]);
470 /* Recalculate the rectangle, we're dragging to another monitor.
471 We don't need to recalculate the rect on single monitor systems. */
472 szTray
.cx
= This
->rcTrayWnd
[Pos
].right
- This
->rcTrayWnd
[Pos
].left
;
473 szTray
.cy
= This
->rcTrayWnd
[Pos
].bottom
- This
->rcTrayWnd
[Pos
].top
;
475 ITrayWindowImpl_GetTrayRectFromScreenRect(This
,
485 /* The user is dragging the tray window on the same monitor. We don't need
486 to recalculate the rectangle */
487 *pRect
= This
->rcTrayWnd
[Pos
];
496 ITrayWindowImpl_GetDraggingRectFromRect(IN OUT ITrayWindowImpl
*This
,
498 OUT HMONITOR
*phMonitor
)
502 /* Calculate the center of the rectangle. We call
503 ITrayWindowImpl_GetDraggingRectFromPt to calculate a valid
504 dragging rectangle */
505 pt
.x
= pRect
->left
+ ((pRect
->right
- pRect
->left
) / 2);
506 pt
.y
= pRect
->top
+ ((pRect
->bottom
- pRect
->top
) / 2);
508 return ITrayWindowImpl_GetDraggingRectFromPt(This
,
515 ITrayWindowImpl_ChangingWinPos(IN OUT ITrayWindowImpl
*This
,
516 IN OUT LPWINDOWPOS pwp
)
520 if (This
->IsDragging
)
522 rcTray
.left
= pwp
->x
;
524 rcTray
.right
= rcTray
.left
+ pwp
->cx
;
525 rcTray
.bottom
= rcTray
.top
+ pwp
->cy
;
527 if (!EqualRect(&rcTray
,
528 &This
->rcTrayWnd
[This
->DraggingPosition
]))
530 /* Recalculate the rectangle, the user dragged the tray
531 window to another monitor or the window was somehow else
533 This
->DraggingPosition
= ITrayWindowImpl_GetDraggingRectFromRect(This
,
535 &This
->DraggingMonitor
);
536 //This->rcTrayWnd[This->DraggingPosition] = rcTray;
539 //This->Monitor = ITrayWindowImpl_CalculateValidSize(This,
540 // This->DraggingPosition,
543 This
->Monitor
= This
->DraggingMonitor
;
544 This
->Position
= This
->DraggingPosition
;
545 This
->IsDragging
= FALSE
;
547 This
->rcTrayWnd
[This
->Position
] = rcTray
;
550 else if (GetWindowRect(This
->hWnd
,
553 if (This
->InSizeMove
)
555 if (!(pwp
->flags
& SWP_NOMOVE
))
557 rcTray
.left
= pwp
->x
;
561 if (!(pwp
->flags
& SWP_NOSIZE
))
563 rcTray
.right
= rcTray
.left
+ pwp
->cx
;
564 rcTray
.bottom
= rcTray
.top
+ pwp
->cy
;
567 This
->Position
= ITrayWindowImpl_GetDraggingRectFromRect(This
,
571 if (!(pwp
->flags
& (SWP_NOMOVE
| SWP_NOSIZE
)))
578 ITrayWindowImpl_MakeTrayRectWithSize(This
->Position
,
583 This
->rcTrayWnd
[This
->Position
] = rcTray
;
587 /* If the user isn't resizing the tray window we need to make sure the
588 new size or position is valid. This is to prevent changes to the window
589 without user interaction. */
590 rcTray
= This
->rcTrayWnd
[This
->Position
];
594 This
->TraySize
.cx
= rcTray
.right
- rcTray
.left
;
595 This
->TraySize
.cy
= rcTray
.bottom
- rcTray
.top
;
597 pwp
->flags
&= ~(SWP_NOMOVE
| SWP_NOSIZE
);
598 pwp
->x
= rcTray
.left
;
600 pwp
->cx
= This
->TraySize
.cx
;
601 pwp
->cy
= This
->TraySize
.cy
;
606 ITrayWindowImpl_ApplyClipping(IN OUT ITrayWindowImpl
*This
,
609 RECT rcClip
, rcWindow
;
612 if (GetWindowRect(This
->hWnd
,
615 /* Disable clipping on systems with only one monitor */
616 if (GetSystemMetrics(SM_CMONITORS
) <= 1)
623 ITrayWindowImpl_GetScreenRect(This
,
627 if (!IntersectRect(&rcClip
,
638 hClipRgn
= CreateRectRgnIndirect(&rcClip
);
643 /* Set the clipping region or make sure the window isn't clipped
644 by disabling it explicitly. */
645 SetWindowRgn(This
->hWnd
,
652 ITrayWindowImpl_ResizeWorkArea(IN OUT ITrayWindowImpl
*This
)
654 RECT rcTray
,rcWorkArea
;
656 /* If monitor has changed then fix the previous monitors work area */
657 if(This
->PreviousMonitor
!=This
->Monitor
)
659 ITrayWindowImpl_GetScreenRect(This
,
660 This
->PreviousMonitor
,
662 SystemParametersInfo(SPI_SETWORKAREA
,
668 rcTray
= This
->rcTrayWnd
[This
->Position
];
670 ITrayWindowImpl_GetScreenRect(This
,
673 This
->PreviousMonitor
=This
->Monitor
;
675 /* If AutoHide is false then change the workarea to exclude the area that
676 the taskbar covers. */
679 switch(This
->Position
)
682 rcWorkArea
.top
=rcTray
.bottom
;
685 rcWorkArea
.left
=rcTray
.right
;
688 rcWorkArea
.right
=rcTray
.left
;
691 rcWorkArea
.bottom
=rcTray
.top
;
696 SystemParametersInfo(SPI_SETWORKAREA
,
703 ITrayWindowImpl_CheckTrayWndPosition(IN OUT ITrayWindowImpl
*This
)
707 rcTray
= This
->rcTrayWnd
[This
->Position
];
708 // DbgPrint("CheckTray: %d: %d,%d,%d,%d\n", This->Position, rcTray.left, rcTray.top, rcTray.right, rcTray.bottom);
710 /* Move the tray window */
711 SetWindowPos(This
->hWnd
,
715 rcTray
.right
- rcTray
.left
,
716 rcTray
.bottom
- rcTray
.top
,
719 ITrayWindowImpl_ResizeWorkArea(This
);
721 ITrayWindowImpl_ApplyClipping(This
,
725 typedef struct _TW_STUCKRECTS2
733 } TW_STRUCKRECTS2
, *PTW_STUCKRECTS2
;
736 ITrayWindowImpl_RegLoadSettings(IN OUT ITrayWindowImpl
*This
)
741 SIZE WndSize
, EdgeSize
, DlgFrameSize
;
742 DWORD cbSize
= sizeof(sr
);
744 EdgeSize
.cx
= GetSystemMetrics(SM_CXEDGE
);
745 EdgeSize
.cy
= GetSystemMetrics(SM_CYEDGE
);
746 DlgFrameSize
.cx
= GetSystemMetrics(SM_CXDLGFRAME
);
747 DlgFrameSize
.cy
= GetSystemMetrics(SM_CYDLGFRAME
);
749 if (SHGetValue(hkExplorer
,
754 &cbSize
) == ERROR_SUCCESS
&&
755 sr
.cbSize
== sizeof(sr
))
757 This
->AutoHide
= (sr
.dwFlags
& ABS_AUTOHIDE
) != 0;
758 This
->AlwaysOnTop
= (sr
.dwFlags
& ABS_ALWAYSONTOP
) != 0;
759 This
->SmSmallIcons
= (sr
.dwFlags
& 0x4) != 0;
760 This
->HideClock
= (sr
.dwFlags
& 0x8) != 0;
762 /* FIXME: Are there more flags? */
764 if (This
->hWnd
!= NULL
)
765 SetWindowPos (This
->hWnd
,
766 This
->AlwaysOnTop
? HWND_TOPMOST
: HWND_NOTOPMOST
,
771 SWP_NOMOVE
| SWP_NOSIZE
);
773 if (sr
.Position
> ABE_BOTTOM
)
774 This
->Position
= ABE_BOTTOM
;
776 This
->Position
= sr
.Position
;
778 /* Try to find out which monitor the tray window was located on last.
779 Here we're only interested in the monitor screen that we think
780 is the last one used. We're going to determine on which monitor
781 we really are after calculating the docked position. */
783 ITrayWindowImpl_GetScreenRectFromRect(This
,
785 MONITOR_DEFAULTTONEAREST
);
789 This
->Position
= ABE_BOTTOM
;
791 /* Use the minimum size of the taskbar, we'll use the start
792 button as a minimum for now. Make sure we calculate the
793 entire window size, not just the client size. However, we
794 use a thinner border than a standard thick border, so that
795 the start button and bands are not stuck to the screen border. */
796 sr
.Size
.cx
= This
->StartBtnSize
.cx
+ (2 * (EdgeSize
.cx
+ DlgFrameSize
.cx
));
797 sr
.Size
.cy
= This
->StartBtnSize
.cy
+ (2 * (EdgeSize
.cy
+ DlgFrameSize
.cy
));
799 /* Use the primary screen by default */
802 rcScreen
.right
= GetSystemMetrics(SM_CXSCREEN
);
803 rcScreen
.bottom
= GetSystemMetrics(SM_CYSCREEN
);
804 ITrayWindowImpl_GetScreenRectFromRect(This
,
806 MONITOR_DEFAULTTOPRIMARY
);
809 /* Determine a minimum tray window rectangle. The "client" height is
810 zero here since we cannot determine an optimal minimum width when
811 loaded as a vertical tray window. We just need to make sure the values
812 loaded from the registry are at least. The windows explorer behaves
813 the same way, it allows the user to save a zero width vertical tray
814 window, but not a zero height horizontal tray window. */
815 WndSize
.cx
= 2 * (EdgeSize
.cx
+ DlgFrameSize
.cx
);
816 WndSize
.cy
= This
->StartBtnSize
.cy
+ (2 * (EdgeSize
.cy
+ DlgFrameSize
.cy
));
818 if (WndSize
.cx
< sr
.Size
.cx
)
819 WndSize
.cx
= sr
.Size
.cx
;
820 if (WndSize
.cy
< sr
.Size
.cy
)
821 WndSize
.cy
= sr
.Size
.cy
;
823 /* Save the calculated size */
824 This
->TraySize
= WndSize
;
826 /* Calculate all docking rectangles. We need to do this here so they're
827 initialized and dragging the tray window to another position gives
833 ITrayWindowImpl_GetTrayRectFromScreenRect(This
,
837 &This
->rcTrayWnd
[Pos
]);
838 // 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);
841 /* Determine which monitor we are on. It shouldn't matter which docked
842 position rectangle we use */
843 This
->Monitor
= ITrayWindowImpl_GetMonitorFromRect(This
,
844 &This
->rcTrayWnd
[ABE_LEFT
]);
848 ITrayWindowImpl_TrackMenu(IN OUT ITrayWindowImpl
*This
,
850 IN POINT
*ppt OPTIONAL
,
851 IN HWND hwndExclude OPTIONAL
,
853 IN BOOL IsContextMenu
)
855 TPMPARAMS tmp
, *ptmp
= NULL
;
860 if (hwndExclude
!= NULL
)
862 /* Get the client rectangle and map it to screen coordinates */
863 if (GetClientRect(hwndExclude
,
865 MapWindowPoints(hwndExclude
,
867 (LPPOINT
)&tmp
.rcExclude
,
877 GetClientRect(This
->hWnd
,
879 MapWindowPoints(This
->hWnd
,
881 (LPPOINT
)&tmp
.rcExclude
,
889 /* NOTE: TrackPopupMenuEx will eventually align the track position
890 for us, no need to take care of it here as long as the
891 coordinates are somewhere within the exclusion rectangle */
892 pt
.x
= ptmp
->rcExclude
.left
;
893 pt
.y
= ptmp
->rcExclude
.top
;
901 tmp
.cbSize
= sizeof(tmp
);
903 fuFlags
= TPM_RETURNCMD
| TPM_VERTICAL
;
904 fuFlags
|= (TrackUp
? TPM_BOTTOMALIGN
: TPM_TOPALIGN
);
906 fuFlags
|= TPM_RIGHTBUTTON
;
908 fuFlags
|= (TrackUp
? TPM_VERNEGANIMATION
: TPM_VERPOSANIMATION
);
910 cmdId
= TrackPopupMenuEx(hMenu
,
921 ITrayWindowImpl_TrackCtxMenu(IN OUT ITrayWindowImpl
*This
,
922 IN
const TRAYWINDOW_CTXMENU
*pMenu
,
923 IN POINT
*ppt OPTIONAL
,
924 IN HWND hwndExclude OPTIONAL
,
926 IN PVOID Context OPTIONAL
)
930 PVOID pcmContext
= NULL
;
932 hPopup
= pMenu
->CreateCtxMenu(This
->hWnd
,
937 cmdId
= ITrayWindowImpl_TrackMenu(This
,
944 pMenu
->CtxMenuCommand(This
->hWnd
,
954 ITrayWindowImpl_Free(ITrayWindowImpl
*This
)
956 HeapFree(hProcessHeap
,
962 static ULONG STDMETHODCALLTYPE
963 ITrayWindowImpl_Release(IN OUT ITrayWindow
*iface
)
965 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
968 Ret
= InterlockedDecrement(&This
->Ref
);
970 ITrayWindowImpl_Free(This
);
976 ITrayWindowImpl_Destroy(ITrayWindowImpl
*This
)
978 (void)InterlockedExchangePointer((PVOID
*)&This
->hWnd
,
981 if (This
->himlStartBtn
!= NULL
)
983 ImageList_Destroy(This
->himlStartBtn
);
984 This
->himlStartBtn
= NULL
;
987 if (This
->hCaptionFont
!= NULL
)
989 DeleteObject(This
->hCaptionFont
);
990 This
->hCaptionFont
= NULL
;
993 if (This
->hStartBtnFont
!= NULL
)
995 DeleteObject(This
->hStartBtnFont
);
996 This
->hStartBtnFont
= NULL
;
999 if (This
->hFont
!= NULL
)
1001 DeleteObject(This
->hFont
);
1005 if (This
->StartMenuPopup
!= NULL
)
1007 IMenuPopup_Release(This
->StartMenuPopup
);
1008 This
->StartMenuPopup
= NULL
;
1011 if (This
->hbmStartMenu
!= NULL
)
1013 DeleteObject(This
->hbmStartMenu
);
1014 This
->hbmStartMenu
= NULL
;
1017 if (This
->StartMenuBand
!= NULL
)
1019 IMenuBand_Release(This
->StartMenuBand
);
1020 This
->StartMenuBand
= NULL
;
1023 if (This
->TrayBandSite
!= NULL
)
1025 /* FIXME: Unload bands */
1026 ITrayBandSite_Release(This
->TrayBandSite
);
1027 This
->TrayBandSite
= NULL
;
1030 ITrayWindowImpl_Release(ITrayWindow_from_impl(This
));
1032 if (InterlockedDecrement(&TrayWndCount
) == 0)
1036 static ULONG STDMETHODCALLTYPE
1037 ITrayWindowImpl_AddRef(IN OUT ITrayWindow
*iface
)
1039 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1041 return InterlockedIncrement(&This
->Ref
);
1046 ITrayWindowImpl_NCCreate(IN OUT ITrayWindowImpl
*This
)
1048 ITrayWindowImpl_AddRef(ITrayWindow_from_impl(This
));
1054 ITrayWindowImpl_UpdateStartButton(IN OUT ITrayWindowImpl
*This
,
1055 IN HBITMAP hbmStart OPTIONAL
)
1057 SIZE Size
= { 0, 0 };
1059 if (This
->himlStartBtn
== NULL
||
1060 !SendMessage(This
->hwndStart
,
1065 Size
.cx
= GetSystemMetrics(SM_CXEDGE
);
1066 Size
.cy
= GetSystemMetrics(SM_CYEDGE
);
1068 if (hbmStart
== NULL
)
1070 hbmStart
= (HBITMAP
)SendMessage(This
->hwndStart
,
1076 if (hbmStart
!= NULL
)
1080 if (GetObject(hbmStart
,
1084 Size
.cx
+= bmp
.bmWidth
;
1085 Size
.cy
+= max(bmp
.bmHeight
,
1086 GetSystemMetrics(SM_CYCAPTION
));
1090 /* Huh?! Shouldn't happen... */
1097 Size
.cx
+= GetSystemMetrics(SM_CXMINIMIZED
);
1098 Size
.cy
+= GetSystemMetrics(SM_CYCAPTION
);
1102 /* Save the size of the start button */
1103 This
->StartBtnSize
= Size
;
1107 ITrayWindowImpl_AlignControls(IN OUT ITrayWindowImpl
*This
,
1108 IN PRECT prcClient OPTIONAL
)
1111 SIZE TraySize
, StartSize
;
1112 POINT ptTrayNotify
= { 0, 0 };
1116 if (prcClient
!= NULL
)
1118 rcClient
= *prcClient
;
1122 if (!GetClientRect(This
->hWnd
,
1129 Horizontal
= ITrayWindowImpl_IsPosHorizontal(This
);
1131 /* We're about to resize/move the start button, the rebar control and
1132 the tray notification control */
1133 dwp
= BeginDeferWindowPos(3);
1137 /* Limit the Start button width to the client width, if neccessary */
1138 StartSize
= This
->StartBtnSize
;
1139 if (StartSize
.cx
> rcClient
.right
)
1140 StartSize
.cx
= rcClient
.right
;
1142 if (This
->hwndStart
!= NULL
)
1144 /* Resize and reposition the button */
1145 dwp
= DeferWindowPos(dwp
,
1152 SWP_NOZORDER
| SWP_NOACTIVATE
);
1157 /* Determine the size that the tray notification window needs */
1161 TraySize
.cy
= rcClient
.bottom
;
1165 TraySize
.cx
= rcClient
.right
;
1169 if (This
->hwndTrayNotify
!= NULL
&&
1170 SendMessage(This
->hwndTrayNotify
,
1171 TNWM_GETMINIMUMSIZE
,
1175 /* Move the tray notification window to the desired location */
1177 ptTrayNotify
.x
= rcClient
.right
- TraySize
.cx
;
1179 ptTrayNotify
.y
= rcClient
.bottom
- TraySize
.cy
;
1181 dwp
= DeferWindowPos(dwp
,
1182 This
->hwndTrayNotify
,
1188 SWP_NOZORDER
| SWP_NOACTIVATE
);
1193 /* Resize/Move the rebar control */
1194 if (This
->hwndRebar
!= NULL
)
1196 POINT ptRebar
= { 0, 0 };
1199 SetWindowStyle(This
->hwndRebar
,
1201 Horizontal
? 0 : CCS_VERT
);
1205 ptRebar
.x
= StartSize
.cx
+ GetSystemMetrics(SM_CXSIZEFRAME
);
1206 szRebar
.cx
= ptTrayNotify
.x
- ptRebar
.x
;
1207 szRebar
.cy
= rcClient
.bottom
;
1211 ptRebar
.y
= StartSize
.cy
+ GetSystemMetrics(SM_CYSIZEFRAME
);
1212 szRebar
.cx
= rcClient
.right
;
1213 szRebar
.cy
= ptTrayNotify
.y
- ptRebar
.y
;
1216 dwp
= DeferWindowPos(dwp
,
1223 SWP_NOZORDER
| SWP_NOACTIVATE
);
1227 EndDeferWindowPos(dwp
);
1229 if (This
->hwndTaskSwitch
!= NULL
)
1231 /* Update the task switch window configuration */
1232 SendMessage(This
->hwndTaskSwitch
,
1233 TSWM_UPDATETASKBARPOS
,
1240 ITrayWindowImpl_CreateStartBtnImageList(IN OUT ITrayWindowImpl
*This
)
1245 if (This
->himlStartBtn
!= NULL
)
1248 IconSize
.cx
= GetSystemMetrics(SM_CXSMICON
);
1249 IconSize
.cy
= GetSystemMetrics(SM_CYSMICON
);
1251 /* Load the start button icon and create a image list for it */
1252 hIconStart
= LoadImage(hExplorerInstance
,
1253 MAKEINTRESOURCE(IDI_START
),
1257 LR_SHARED
| LR_DEFAULTCOLOR
);
1259 if (hIconStart
!= NULL
)
1261 This
->himlStartBtn
= ImageList_Create(IconSize
.cx
,
1263 ILC_COLOR32
| ILC_MASK
,
1266 if (This
->himlStartBtn
!= NULL
)
1268 if (ImageList_AddIcon(This
->himlStartBtn
,
1274 /* Failed to add the icon! */
1275 ImageList_Destroy(This
->himlStartBtn
);
1276 This
->himlStartBtn
= NULL
;
1284 ITrayWindowImpl_CreateStartButtonBitmap(IN OUT ITrayWindowImpl
*This
)
1286 TCHAR szStartCaption
[32];
1289 HDC hDCScreen
= NULL
;
1290 SIZE Size
, SmallIcon
;
1291 HBITMAP hbmpOld
, hbmp
= NULL
;
1292 HBITMAP hBitmap
= NULL
;
1298 /* NOTE: This is the backwards compatibility code that is used if the
1299 Common Controls Version 6.0 are not available! */
1301 if (!LoadString(hExplorerInstance
,
1304 sizeof(szStartCaption
) / sizeof(szStartCaption
[0])))
1309 /* Load the start button icon */
1310 SmallIcon
.cx
= GetSystemMetrics(SM_CXSMICON
);
1311 SmallIcon
.cy
= GetSystemMetrics(SM_CYSMICON
);
1312 hIconStart
= LoadImage(hExplorerInstance
,
1313 MAKEINTRESOURCE(IDI_START
),
1317 LR_SHARED
| LR_DEFAULTCOLOR
);
1319 hDCScreen
= GetDC(NULL
);
1320 if (hDCScreen
== NULL
)
1323 hDC
= CreateCompatibleDC(hDCScreen
);
1327 hFontOld
= SelectObject(hDC
,
1328 This
->hStartBtnFont
);
1330 Ret
= GetTextExtentPoint32(hDC
,
1332 _tcslen(szStartCaption
),
1340 /* Make sure the height is at least the size of a caption icon. */
1341 if (hIconStart
!= NULL
)
1342 Size
.cx
+= SmallIcon
.cx
+ 4;
1343 Size
.cy
= max(Size
.cy
,
1346 /* Create the bitmap */
1347 hbmp
= CreateCompatibleBitmap(hDCScreen
,
1353 /* Caluclate the button rect */
1356 rcButton
.right
= Size
.cx
;
1357 rcButton
.bottom
= Size
.cy
;
1359 /* Draw the button */
1360 hbmpOld
= SelectObject(hDC
,
1363 Flags
= DC_TEXT
| DC_INBUTTON
;
1364 if (hIconStart
!= NULL
)
1367 if (DrawCapTemp
!= NULL
)
1369 Ret
= DrawCapTemp(NULL
,
1372 This
->hStartBtnFont
,
1384 /* We successfully created the bitmap! */
1389 if (hDCScreen
!= NULL
)
1405 ITrayWindowImpl_Create(IN OUT ITrayWindowImpl
*This
)
1407 TCHAR szStartCaption
[32];
1409 InterlockedIncrement(&TrayWndCount
);
1411 if (!LoadString(hExplorerInstance
,
1414 sizeof(szStartCaption
) / sizeof(szStartCaption
[0])))
1416 szStartCaption
[0] = TEXT('\0');
1419 if (This
->hStartBtnFont
== NULL
|| This
->hCaptionFont
== NULL
)
1421 NONCLIENTMETRICS ncm
;
1423 /* Get the system fonts, we use the caption font,
1424 always bold, though. */
1425 ncm
.cbSize
= sizeof(ncm
);
1426 if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS
,
1431 if (This
->hCaptionFont
== NULL
)
1433 ncm
.lfCaptionFont
.lfWeight
= FW_NORMAL
;
1434 This
->hCaptionFont
= CreateFontIndirect(&ncm
.lfCaptionFont
);
1437 if (This
->hStartBtnFont
== NULL
)
1439 ncm
.lfCaptionFont
.lfWeight
= FW_BOLD
;
1440 This
->hStartBtnFont
= CreateFontIndirect(&ncm
.lfCaptionFont
);
1445 /* Create the Start button */
1446 This
->hwndStart
= CreateWindowEx(0,
1449 WS_CHILD
| WS_VISIBLE
| WS_CLIPSIBLINGS
|
1450 BS_PUSHBUTTON
| BS_CENTER
| BS_VCENTER
| BS_BITMAP
,
1456 (HMENU
)IDC_STARTBTN
,
1459 if (This
->hwndStart
)
1461 SendMessage(This
->hwndStart
,
1463 (WPARAM
)This
->hStartBtnFont
,
1466 if (ITrayWindowImpl_CreateStartBtnImageList(This
))
1468 BUTTON_IMAGELIST bil
;
1470 /* Try to set the start button image. This requires the Common
1471 Controls 6.0 to be present (XP and later) */
1472 bil
.himl
= This
->himlStartBtn
;
1473 bil
.margin
.left
= bil
.margin
.right
= 1;
1474 bil
.margin
.top
= bil
.margin
.bottom
= 1;
1475 bil
.uAlign
= BUTTON_IMAGELIST_ALIGN_LEFT
;
1477 if (!SendMessage(This
->hwndStart
,
1482 /* Fall back to the deprecated method on older systems that don't
1483 support Common Controls 6.0 */
1484 ImageList_Destroy(This
->himlStartBtn
);
1485 This
->himlStartBtn
= NULL
;
1487 goto SetStartBtnImage
;
1490 /* We're using the image list, remove the BS_BITMAP style and
1491 don't center it horizontally */
1492 SetWindowStyle(This
->hwndStart
,
1493 BS_BITMAP
| BS_RIGHT
,
1496 ITrayWindowImpl_UpdateStartButton(This
,
1501 HBITMAP hbmStart
, hbmOld
;
1504 hbmStart
= ITrayWindowImpl_CreateStartButtonBitmap(This
);
1505 if (hbmStart
!= NULL
)
1507 ITrayWindowImpl_UpdateStartButton(This
,
1510 hbmOld
= (HBITMAP
)SendMessage(This
->hwndStart
,
1516 DeleteObject(hbmOld
);
1521 /* Load the saved tray window settings */
1522 ITrayWindowImpl_RegLoadSettings(This
);
1524 /* Create and initialize the start menu */
1525 This
->hbmStartMenu
= LoadBitmap(hExplorerInstance
,
1526 MAKEINTRESOURCE(IDB_STARTMENU
));
1527 This
->StartMenuPopup
= CreateStartMenu(ITrayWindow_from_impl(This
),
1528 &This
->StartMenuBand
,
1532 /* Load the tray band site */
1533 if (This
->TrayBandSite
!= NULL
)
1535 ITrayBandSite_Release(This
->TrayBandSite
);
1538 This
->TrayBandSite
= CreateTrayBandSite(ITrayWindow_from_impl(This
),
1540 &This
->hwndTaskSwitch
);
1542 /* Create the tray notification window */
1543 This
->hwndTrayNotify
= CreateTrayNotifyWnd(ITrayWindow_from_impl(This
),
1546 if (ITrayWindowImpl_UpdateNonClientMetrics(This
))
1548 ITrayWindowImpl_SetWindowsFont(This
);
1551 /* Move the tray window to the right position and resize it if neccessary */
1552 ITrayWindowImpl_CheckTrayWndPosition(This
);
1554 /* Align all controls on the tray window */
1555 ITrayWindowImpl_AlignControls(This
,
1559 static HRESULT STDMETHODCALLTYPE
1560 ITrayWindowImpl_QueryInterface(IN OUT ITrayWindow
*iface
,
1564 ITrayWindowImpl
*This
;
1569 This
= impl_from_ITrayWindow(iface
);
1571 if (IsEqualIID(riid
,
1574 *ppvObj
= IUnknown_from_impl(This
);
1576 else if (IsEqualIID(riid
,
1577 &IID_IShellDesktopTray
))
1579 *ppvObj
= IShellDesktopTray_from_impl(This
);
1584 return E_NOINTERFACE
;
1587 ITrayWindowImpl_AddRef(iface
);
1591 static ITrayWindowImpl
*
1592 ITrayWindowImpl_Construct(VOID
)
1594 ITrayWindowImpl
*This
;
1596 This
= HeapAlloc(hProcessHeap
,
1604 This
->lpVtbl
= &ITrayWindowImpl_Vtbl
;
1605 This
->lpVtblShellDesktopTray
= &IShellDesktopTrayImpl_Vtbl
;
1607 This
->Position
= (DWORD
)-1;
1612 static HRESULT STDMETHODCALLTYPE
1613 ITrayWindowImpl_Open(IN OUT ITrayWindow
*iface
)
1615 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1620 /* Check if there's already a window created and try to show it.
1621 If it was somehow destroyed just create a new tray window. */
1622 if (This
->hWnd
!= NULL
)
1624 if (IsWindow(This
->hWnd
))
1626 if (!IsWindowVisible(This
->hWnd
))
1628 ITrayWindowImpl_CheckTrayWndPosition(This
);
1630 ShowWindow(This
->hWnd
,
1635 goto TryCreateTrayWnd
;
1642 dwExStyle
= WS_EX_TOOLWINDOW
| WS_EX_WINDOWEDGE
;
1643 if (This
->AlwaysOnTop
)
1644 dwExStyle
|= WS_EX_TOPMOST
;
1646 if (This
->Position
!= (DWORD
)-1)
1647 rcWnd
= This
->rcTrayWnd
[This
->Position
];
1654 hWnd
= CreateWindowEx(dwExStyle
,
1657 WS_POPUP
| WS_VISIBLE
| WS_CLIPSIBLINGS
| WS_CLIPCHILDREN
|
1658 WS_BORDER
| WS_THICKFRAME
,
1661 rcWnd
.right
- rcWnd
.left
,
1662 rcWnd
.bottom
- rcWnd
.top
,
1674 static HRESULT STDMETHODCALLTYPE
1675 ITrayWindowImpl_Close(IN OUT ITrayWindow
*iface
)
1677 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1679 if (This
->hWnd
!= NULL
)
1681 SendMessage(This
->hWnd
,
1690 static HWND STDMETHODCALLTYPE
1691 ITrayWindowImpl_GetHWND(IN OUT ITrayWindow
*iface
)
1693 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1698 static BOOL STDMETHODCALLTYPE
1699 ITrayWindowImpl_IsSpecialHWND(IN OUT ITrayWindow
*iface
,
1702 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1704 return (hWnd
== This
->hWnd
||
1705 (This
->hWndDesktop
!= NULL
&& hWnd
== This
->hWndDesktop
));
1708 static BOOL STDMETHODCALLTYPE
1709 ITrayWindowImpl_IsHorizontal(IN OUT ITrayWindow
*iface
)
1711 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1712 return ITrayWindowImpl_IsPosHorizontal(This
);
1715 static HFONT STDMETHODCALLTYPE
1716 ITrayWIndowImpl_GetCaptionFonts(IN OUT ITrayWindow
*iface
,
1717 OUT HFONT
*phBoldCaption OPTIONAL
)
1719 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1721 if (phBoldCaption
!= NULL
)
1722 *phBoldCaption
= This
->hStartBtnFont
;
1724 return This
->hCaptionFont
;
1727 static HWND STDMETHODCALLTYPE
1728 ITrayWindowImpl_DisplayProperties(IN OUT ITrayWindow
*iface
)
1730 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1732 if (This
->hWndTrayProperties
!= NULL
)
1734 BringWindowToTop(This
->hWndTrayProperties
);
1735 return This
->hWndTrayProperties
;
1738 This
->hWndTrayProperties
= DisplayTrayProperties(ITrayWindow_from_impl(This
));
1739 return This
->hWndTrayProperties
;
1743 OpenCommonStartMenuDirectory(IN HWND hWndOwner
,
1744 IN LPCTSTR lpOperation
)
1746 TCHAR szDir
[MAX_PATH
];
1748 if (SHGetSpecialFolderPath(hWndOwner
,
1750 CSIDL_COMMON_STARTMENU
,
1753 ShellExecute(hWndOwner
,
1763 OpenTaskManager(IN HWND hWndOwner
)
1765 ShellExecute(hWndOwner
,
1767 TEXT("taskmgr.exe"),
1773 static BOOL STDMETHODCALLTYPE
1774 ITrayWindowImpl_ExecContextMenuCmd(IN OUT ITrayWindow
*iface
,
1777 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1778 BOOL bHandled
= TRUE
;
1782 case ID_SHELL_CMD_PROPERTIES
:
1783 ITrayWindow_DisplayProperties(iface
);
1786 case ID_SHELL_CMD_OPEN_ALL_USERS
:
1787 OpenCommonStartMenuDirectory(This
->hWnd
,
1791 case ID_SHELL_CMD_EXPLORE_ALL_USERS
:
1792 OpenCommonStartMenuDirectory(This
->hWnd
,
1796 case ID_LOCKTASKBAR
:
1797 if (SHRestricted(REST_CLASSICSHELL
) == 0)
1799 ITrayWindow_Lock(iface
,
1804 case ID_SHELL_CMD_OPEN_TASKMGR
:
1805 OpenTaskManager(This
->hWnd
);
1808 case ID_SHELL_CMD_UNDO_ACTION
:
1811 case ID_SHELL_CMD_SHOW_DESKTOP
:
1814 case ID_SHELL_CMD_TILE_WND_H
:
1815 TileWindows(NULL
, MDITILE_HORIZONTAL
, NULL
, 0, NULL
);
1818 case ID_SHELL_CMD_TILE_WND_V
:
1819 TileWindows(NULL
, MDITILE_VERTICAL
, NULL
, 0, NULL
);
1822 case ID_SHELL_CMD_CASCADE_WND
:
1823 CascadeWindows(NULL
, MDITILE_SKIPDISABLED
, NULL
, 0, NULL
);
1826 case ID_SHELL_CMD_CUST_NOTIF
:
1829 case ID_SHELL_CMD_ADJUST_DAT
:
1830 LaunchCPanel(NULL
, TEXT("timedate.cpl"));
1834 DbgPrint("ITrayWindow::ExecContextMenuCmd(%u): Unhandled Command ID!\n", uiCmd
);
1842 static BOOL STDMETHODCALLTYPE
1843 ITrayWindowImpl_Lock(IN OUT ITrayWindow
*iface
,
1847 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1849 bPrevLock
= This
->Locked
;
1850 if (This
->Locked
!= bLock
)
1852 This
->Locked
= bLock
;
1854 if (This
->TrayBandSite
!= NULL
)
1856 if (!SUCCEEDED(ITrayBandSite_Lock(This
->TrayBandSite
,
1860 This
->Locked
= bPrevLock
;
1868 static const ITrayWindowVtbl ITrayWindowImpl_Vtbl
=
1871 ITrayWindowImpl_QueryInterface
,
1872 ITrayWindowImpl_AddRef
,
1873 ITrayWindowImpl_Release
,
1875 ITrayWindowImpl_Open
,
1876 ITrayWindowImpl_Close
,
1877 ITrayWindowImpl_GetHWND
,
1878 ITrayWindowImpl_IsSpecialHWND
,
1879 ITrayWindowImpl_IsHorizontal
,
1880 ITrayWIndowImpl_GetCaptionFonts
,
1881 ITrayWindowImpl_DisplayProperties
,
1882 ITrayWindowImpl_ExecContextMenuCmd
,
1883 ITrayWindowImpl_Lock
1887 RunFileDlgThread(IN OUT PVOID pParam
)
1889 ITrayWindowImpl
*This
= pParam
;
1891 RUNFILEDLG RunFileDlg
;
1895 GetWindowRect(This
->hwndStart
,&posRect
);
1897 hwnd
= CreateWindowEx(0,
1900 WS_OVERLAPPED
| WS_DISABLED
| WS_CLIPSIBLINGS
| WS_BORDER
| SS_LEFT
,
1903 posRect
.right
- posRect
.left
,
1904 posRect
.bottom
- posRect
.top
,
1910 hShell32
= GetModuleHandle(TEXT("SHELL32.DLL"));
1911 RunFileDlg
= (RUNFILEDLG
)GetProcAddress(hShell32
, (LPCSTR
)61);
1913 RunFileDlg(hwnd
, NULL
, NULL
, NULL
, NULL
, RFF_CALCDIRECTORY
);
1915 DestroyWindow(hwnd
);
1920 static LRESULT CALLBACK
1921 TrayWndProc(IN HWND hwnd
,
1926 ITrayWindowImpl
*This
= NULL
;
1927 LRESULT Ret
= FALSE
;
1929 if (uMsg
!= WM_NCCREATE
)
1931 This
= (ITrayWindowImpl
*)GetWindowLongPtr(hwnd
,
1935 if (This
!= NULL
|| uMsg
== WM_NCCREATE
)
1937 if (This
!= NULL
&& This
->StartMenuBand
!= NULL
)
1944 Msg
.wParam
= wParam
;
1945 Msg
.lParam
= lParam
;
1947 if (IMenuBand_TranslateMenuMessage(This
->StartMenuBand
,
1954 wParam
= Msg
.wParam
;
1955 lParam
= Msg
.lParam
;
1967 /* The user may not be able to resize the tray window.
1968 Pretend like the window is not sizeable when the user
1969 clicks on the border. */
1973 SetLastError(ERROR_SUCCESS
);
1974 if (GetClientRect(hwnd
,
1976 (MapWindowPoints(hwnd
,
1979 2) != 0 || GetLastError() == ERROR_SUCCESS
))
1981 pt
.x
= (SHORT
)LOWORD(lParam
);
1982 pt
.y
= (SHORT
)HIWORD(lParam
);
1984 if (PtInRect(&rcClient
,
1987 /* The user is trying to drag the tray window */
1991 /* Depending on the position of the tray window, allow only
1992 changing the border next to the monitor working area */
1993 switch (This
->Position
)
1996 if (pt
.y
> rcClient
.bottom
)
2001 if (pt
.y
< rcClient
.top
)
2006 if (pt
.x
> rcClient
.right
)
2011 if (pt
.x
< rcClient
.left
)
2026 PRECT pRect
= (PRECT
)lParam
;
2028 /* We need to ensure that an application can not accidently
2029 move the tray window (using SetWindowPos). However, we still
2030 need to be able to move the window in case the user wants to
2031 drag the tray window to another position or in case the user
2032 wants to resize the tray window. */
2033 if (!This
->Locked
&& GetCursorPos(&ptCursor
))
2035 This
->IsDragging
= TRUE
;
2036 This
->DraggingPosition
= ITrayWindowImpl_GetDraggingRectFromPt(This
,
2039 &This
->DraggingMonitor
);
2043 *pRect
= This
->rcTrayWnd
[This
->Position
];
2050 PRECT pRect
= (PRECT
)lParam
;
2054 ITrayWindowImpl_CalculateValidSize(This
,
2060 *pRect
= This
->rcTrayWnd
[This
->Position
];
2065 case WM_WINDOWPOSCHANGING
:
2067 ITrayWindowImpl_ChangingWinPos(This
,
2068 (LPWINDOWPOS
)lParam
);
2076 if (wParam
== SIZE_RESTORED
&& lParam
== 0)
2078 ITrayWindowImpl_ResizeWorkArea(This
);
2079 /* Clip the tray window on multi monitor systems so the edges can't
2080 overlap into another monitor */
2081 ITrayWindowImpl_ApplyClipping(This
,
2084 if (!GetClientRect(This
->hWnd
,
2092 rcClient
.left
= rcClient
.top
= 0;
2093 rcClient
.right
= LOWORD(lParam
);
2094 rcClient
.bottom
= HIWORD(lParam
);
2097 ITrayWindowImpl_AlignControls(This
,
2102 case WM_ENTERSIZEMOVE
:
2103 This
->InSizeMove
= TRUE
;
2104 This
->IsDragging
= FALSE
;
2107 /* Remove the clipping on multi monitor systems while dragging around */
2108 ITrayWindowImpl_ApplyClipping(This
,
2113 case WM_EXITSIZEMOVE
:
2114 This
->InSizeMove
= FALSE
;
2117 /* Apply clipping */
2118 PostMessage(This
->hWnd
,
2130 /* The user pressed Alt+Space, this usually brings up the system menu of a window.
2131 The tray window needs to handle this specially, since it normally doesn't have
2134 static const UINT uidDisableItem
[] = {
2145 /* temporarily enable the system menu */
2146 SetWindowStyle(hwnd
,
2150 hSysMenu
= GetSystemMenu(hwnd
,
2152 if (hSysMenu
!= NULL
)
2154 /* Disable all items that are not relevant */
2155 for (i
= 0; i
!= sizeof(uidDisableItem
) / sizeof(uidDisableItem
[0]); i
++)
2157 EnableMenuItem(hSysMenu
,
2159 MF_BYCOMMAND
| MF_GRAYED
);
2162 EnableMenuItem(hSysMenu
,
2165 (SHRestricted(REST_NOCLOSE
) ? MF_GRAYED
: MF_ENABLED
));
2167 /* Display the system menu */
2168 uId
= ITrayWindowImpl_TrackMenu(This
,
2172 This
->Position
!= ABE_TOP
,
2176 SendMessage(This
->hWnd
,
2183 /* revert the system menu window style */
2184 SetWindowStyle(hwnd
,
2195 case WM_NCRBUTTONUP
:
2196 /* We want the user to be able to get a context menu even on the nonclient
2197 area (including the sizing border)! */
2198 uMsg
= WM_CONTEXTMENU
;
2199 wParam
= (WPARAM
)hwnd
;
2202 case WM_CONTEXTMENU
:
2204 POINT pt
, *ppt
= NULL
;
2205 HWND hWndExclude
= NULL
;
2207 /* Check if the administrator has forbidden access to context menus */
2208 if (SHRestricted(REST_NOTRAYCONTEXTMENU
))
2211 pt
.x
= (SHORT
)LOWORD(lParam
);
2212 pt
.y
= (SHORT
)HIWORD(lParam
);
2214 if (pt
.x
!= -1 || pt
.y
!= -1)
2217 hWndExclude
= This
->hwndStart
;
2219 if ((HWND
)wParam
== This
->hwndStart
)
2221 /* Make sure we can't track the context menu if the start
2222 menu is currently being shown */
2223 if (!(SendMessage(This
->hwndStart
,
2228 ITrayWindowImpl_TrackCtxMenu(This
,
2229 &StartMenuBtnCtxMenu
,
2232 This
->Position
== ABE_BOTTOM
,
2238 /* See if the context menu should be handled by the task band site */
2239 if (ppt
!= NULL
&& This
->TrayBandSite
!= NULL
)
2242 POINT ptClient
= *ppt
;
2244 /* Convert the coordinates to client-coordinates */
2245 MapWindowPoints(NULL
,
2250 hWndAtPt
= ChildWindowFromPoint(This
->hWnd
,
2252 if (hWndAtPt
!= NULL
&&
2253 (hWndAtPt
== This
->hwndRebar
|| IsChild(This
->hwndRebar
,
2256 /* Check if the user clicked on the task switch window */
2258 MapWindowPoints(NULL
,
2263 hWndAtPt
= ChildWindowFromPointEx(This
->hwndRebar
,
2265 CWP_SKIPINVISIBLE
| CWP_SKIPDISABLED
);
2266 if (hWndAtPt
== This
->hwndTaskSwitch
)
2267 goto HandleTrayContextMenu
;
2269 /* Forward the message to the task band site */
2270 ITrayBandSite_ProcessMessage(This
->TrayBandSite
,
2278 goto HandleTrayContextMenu
;
2282 HandleTrayContextMenu
:
2283 /* Tray the default tray window context menu */
2284 ITrayWindowImpl_TrackCtxMenu(This
,
2297 /* FIXME: We can't check with IsChild whether the hwnd is somewhere inside
2298 the rebar control! But we shouldn't forward messages that the band
2299 site doesn't handle, such as other controls (start button, tray window */
2300 if (This
->TrayBandSite
== NULL
||
2301 !SUCCEEDED(ITrayBandSite_ProcessMessage(This
->TrayBandSite
,
2308 const NMHDR
*nmh
= (const NMHDR
*)lParam
;
2310 if (nmh
->hwndFrom
== This
->hwndTrayNotify
)
2315 /* Cause all controls to be aligned */
2316 PostMessage(This
->hWnd
,
2327 case WM_NCLBUTTONDBLCLK
:
2328 /* We "handle" this message so users can't cause a weird maximize/restore
2329 window animation when double-clicking the tray window! */
2334 LPCREATESTRUCT CreateStruct
= (LPCREATESTRUCT
)lParam
;
2335 This
= (ITrayWindowImpl
*)CreateStruct
->lpCreateParams
;
2337 if (InterlockedCompareExchangePointer((PVOID
*)&This
->hWnd
,
2341 /* Somebody else was faster... */
2345 SetWindowLongPtr(hwnd
,
2349 return ITrayWindowImpl_NCCreate(This
);
2353 ITrayWindowImpl_Create(This
);
2357 ITrayWindowImpl_Destroy(This
);
2360 case WM_APP_TRAYDESTROY
:
2361 DestroyWindow(hwnd
);
2365 if ((HWND
)lParam
== This
->hwndStart
)
2367 if (This
->StartMenuPopup
!= NULL
)
2373 if (GetWindowRect(This
->hwndStart
,
2376 if (ITrayWindowImpl_IsPosHorizontal(This
))
2378 pt
.x
= rcExclude
.left
;
2379 pt
.y
= rcExclude
.top
;
2380 dwFlags
|= MPPF_BOTTOM
;
2384 if (This
->Position
== ABE_LEFT
)
2385 pt
.x
= rcExclude
.left
;
2387 pt
.x
= rcExclude
.right
;
2389 pt
.y
= rcExclude
.bottom
;
2390 dwFlags
|= MPPF_BOTTOM
;
2393 IMenuPopup_Popup(This
->StartMenuPopup
,
2402 if (This
->TrayBandSite
== NULL
||
2403 !SUCCEEDED(ITrayBandSite_ProcessMessage(This
->TrayBandSite
,
2410 switch(LOWORD(wParam
))
2412 /* FIXME: Handle these commands as well */
2413 case IDM_TASKBARANDSTARTMENU
:
2415 case IDM_HELPANDSUPPORT
:
2420 CloseHandle(CreateThread(NULL
,
2430 /* FIXME: Handle these commands as well */
2431 case IDM_SYNCHRONIZE
:
2433 case IDM_DISCONNECT
:
2434 case IDM_UNDOCKCOMPUTER
:
2440 EXITWINDLG ExitWinDlg
;
2442 hShell32
= GetModuleHandle(TEXT("SHELL32.DLL"));
2443 ExitWinDlg
= (EXITWINDLG
)GetProcAddress(hShell32
, (LPCSTR
)60);
2459 Ret
= DefWindowProc(hwnd
,
2469 * Tray Window Context Menu
2473 CreateTrayWindowContextMenu(IN HWND hWndOwner
,
2474 IN PVOID
*ppcmContext
,
2475 IN PVOID Context OPTIONAL
)
2477 ITrayWindowImpl
*This
= (ITrayWindowImpl
*)Context
;
2478 IContextMenu
*pcm
= NULL
;
2481 hPopup
= LoadPopupMenu(hExplorerInstance
,
2482 MAKEINTRESOURCE(IDM_TRAYWND
));
2486 if (SHRestricted(REST_CLASSICSHELL
) != 0)
2493 CheckMenuItem(hPopup
,
2495 MF_BYCOMMAND
| (This
->Locked
? MF_CHECKED
: MF_UNCHECKED
));
2497 if (This
->TrayBandSite
!= NULL
)
2499 if (SUCCEEDED(ITrayBandSite_AddContextMenus(This
->TrayBandSite
,
2507 DbgPrint("ITrayBandSite::AddContextMenus succeeded!\n");
2508 *(IContextMenu
**)ppcmContext
= pcm
;
2517 OnTrayWindowContextMenuCommand(IN HWND hWndOwner
,
2519 IN PVOID pcmContext OPTIONAL
,
2520 IN PVOID Context OPTIONAL
)
2522 ITrayWindowImpl
*This
= (ITrayWindowImpl
*)Context
;
2523 IContextMenu
*pcm
= (IContextMenu
*)pcmContext
;
2527 if (uiCmdId
>= ID_SHELL_CMD_FIRST
&& uiCmdId
<= ID_SHELL_CMD_LAST
)
2529 CMINVOKECOMMANDINFO cmici
= {0};
2533 /* Setup and invoke the shell command */
2534 cmici
.cbSize
= sizeof(cmici
);
2535 cmici
.hwnd
= hWndOwner
;
2536 cmici
.lpVerb
= (LPCSTR
)MAKEINTRESOURCE(uiCmdId
- ID_SHELL_CMD_FIRST
);
2537 cmici
.nShow
= SW_NORMAL
;
2539 IContextMenu_InvokeCommand(pcm
,
2545 ITrayWindow_ExecContextMenuCmd(ITrayWindow_from_impl(This
),
2551 IContextMenu_Release(pcm
);
2554 static const TRAYWINDOW_CTXMENU TrayWindowCtxMenu
= {
2555 CreateTrayWindowContextMenu
,
2556 OnTrayWindowContextMenuCommand
2559 /*****************************************************************************/
2562 RegisterTrayWindowClass(VOID
)
2567 if (!RegisterTrayNotifyWndClass())
2570 wcTrayWnd
.style
= CS_DBLCLKS
;
2571 wcTrayWnd
.lpfnWndProc
= TrayWndProc
;
2572 wcTrayWnd
.cbClsExtra
= 0;
2573 wcTrayWnd
.cbWndExtra
= sizeof(ITrayWindowImpl
*);
2574 wcTrayWnd
.hInstance
= hExplorerInstance
;
2575 wcTrayWnd
.hIcon
= NULL
;
2576 wcTrayWnd
.hCursor
= LoadCursor(NULL
,
2578 wcTrayWnd
.hbrBackground
= (HBRUSH
)(COLOR_3DFACE
+ 1);
2579 wcTrayWnd
.lpszMenuName
= NULL
;
2580 wcTrayWnd
.lpszClassName
= szTrayWndClass
;
2582 Ret
= RegisterClass(&wcTrayWnd
) != 0;
2585 UnregisterTrayNotifyWndClass();
2591 UnregisterTrayWindowClass(VOID
)
2593 UnregisterTrayNotifyWndClass();
2595 UnregisterClass(szTrayWndClass
,
2600 CreateTrayWindow(VOID
)
2602 ITrayWindowImpl
*This
;
2603 ITrayWindow
*TrayWindow
;
2605 This
= ITrayWindowImpl_Construct();
2608 TrayWindow
= ITrayWindow_from_impl(This
);
2610 ITrayWindowImpl_Open(TrayWindow
);
2619 TrayProcessMessages(IN OUT ITrayWindow
*Tray
)
2621 ITrayWindowImpl
*This
;
2624 This
= impl_from_ITrayWindow(Tray
);
2626 /* FIXME: We should keep a reference here... */
2628 while (PeekMessage(&Msg
,
2634 if (Msg
.message
== WM_QUIT
)
2637 if (This
->StartMenuBand
== NULL
||
2638 IMenuBand_IsMenuMessage(This
->StartMenuBand
,
2641 TranslateMessage(&Msg
);
2642 DispatchMessage(&Msg
);
2648 TrayMessageLoop(IN OUT ITrayWindow
*Tray
)
2650 ITrayWindowImpl
*This
;
2654 This
= impl_from_ITrayWindow(Tray
);
2656 /* FIXME: We should keep a reference here... */
2660 Ret
= (GetMessage(&Msg
,
2670 if (This
->StartMenuBand
== NULL
||
2671 IMenuBand_IsMenuMessage(This
->StartMenuBand
,
2674 TranslateMessage(&Msg
);
2675 DispatchMessage(&Msg
);
2684 * NOTE: This is a very windows-specific COM interface used by SHCreateDesktop()!
2685 * These are the calls I observed, it may be wrong/incomplete/buggy!!!
2686 * The reason we implement it is because we have to use SHCreateDesktop() so
2687 * that the shell provides the desktop window and all the features that come
2688 * with it (especially positioning of desktop icons)
2691 static HRESULT STDMETHODCALLTYPE
2692 ITrayWindowImpl_IShellDesktopTray_QueryInterface(IN OUT IShellDesktopTray
*iface
,
2696 ITrayWindowImpl
*This
= impl_from_IShellDesktopTray(iface
);
2697 ITrayWindow
*tray
= ITrayWindow_from_impl(This
);
2699 DbgPrint("IShellDesktopTray::QueryInterface(0x%p, 0x%p)\n", riid
, ppvObj
);
2700 return ITrayWindowImpl_QueryInterface(tray
,
2705 static ULONG STDMETHODCALLTYPE
2706 ITrayWindowImpl_IShellDesktopTray_Release(IN OUT IShellDesktopTray
*iface
)
2708 ITrayWindowImpl
*This
= impl_from_IShellDesktopTray(iface
);
2709 ITrayWindow
*tray
= ITrayWindow_from_impl(This
);
2711 DbgPrint("IShellDesktopTray::Release()\n");
2712 return ITrayWindowImpl_Release(tray
);
2715 static ULONG STDMETHODCALLTYPE
2716 ITrayWindowImpl_IShellDesktopTray_AddRef(IN OUT IShellDesktopTray
*iface
)
2718 ITrayWindowImpl
*This
= impl_from_IShellDesktopTray(iface
);
2719 ITrayWindow
*tray
= ITrayWindow_from_impl(This
);
2721 DbgPrint("IShellDesktopTray::AddRef()\n");
2722 return ITrayWindowImpl_AddRef(tray
);
2725 static ULONG STDMETHODCALLTYPE
2726 ITrayWindowImpl_IShellDesktopTray_GetState(IN OUT IShellDesktopTray
*iface
)
2728 /* FIXME: Return ABS_ flags? */
2729 DbgPrint("IShellDesktopTray::GetState() unimplemented!\n");
2733 static HRESULT STDMETHODCALLTYPE
2734 ITrayWindowImpl_IShellDesktopTray_GetTrayWindow(IN OUT IShellDesktopTray
*iface
,
2735 OUT HWND
*phWndTray
)
2737 ITrayWindowImpl
*This
= impl_from_IShellDesktopTray(iface
);
2738 DbgPrint("IShellDesktopTray::GetTrayWindow(0x%p)\n", phWndTray
);
2739 *phWndTray
= This
->hWnd
;
2743 static HRESULT STDMETHODCALLTYPE
2744 ITrayWindowImpl_IShellDesktopTray_RegisterDesktopWindow(IN OUT IShellDesktopTray
*iface
,
2745 IN HWND hWndDesktop
)
2747 ITrayWindowImpl
*This
= impl_from_IShellDesktopTray(iface
);
2748 DbgPrint("IShellDesktopTray::RegisterDesktopWindow(0x%p)\n", hWndDesktop
);
2750 This
->hWndDesktop
= hWndDesktop
;
2754 static HRESULT STDMETHODCALLTYPE
2755 ITrayWindowImpl_IShellDesktopTray_Unknown(IN OUT IShellDesktopTray
*iface
,
2756 IN DWORD dwUnknown1
,
2757 IN DWORD dwUnknown2
)
2759 DbgPrint("IShellDesktopTray::Unknown(%u,%u) unimplemented!\n", dwUnknown1
, dwUnknown2
);
2763 static const IShellDesktopTrayVtbl IShellDesktopTrayImpl_Vtbl
=
2766 ITrayWindowImpl_IShellDesktopTray_QueryInterface
,
2767 ITrayWindowImpl_IShellDesktopTray_AddRef
,
2768 ITrayWindowImpl_IShellDesktopTray_Release
,
2769 /*** IShellDesktopTray ***/
2770 ITrayWindowImpl_IShellDesktopTray_GetState
,
2771 ITrayWindowImpl_IShellDesktopTray_GetTrayWindow
,
2772 ITrayWindowImpl_IShellDesktopTray_RegisterDesktopWindow
,
2773 ITrayWindowImpl_IShellDesktopTray_Unknown