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
;
51 HIMAGELIST himlStartBtn
;
56 ITrayBandSite
*TrayBandSite
;
63 HMONITOR PreviousMonitor
;
64 DWORD DraggingPosition
;
65 HMONITOR DraggingMonitor
;
76 DWORD AlwaysOnTop
: 1;
77 DWORD SmSmallIcons
: 1;
91 IMenuBand
*StartMenuBand
;
92 IMenuPopup
*StartMenuPopup
;
95 HWND hwndTrayPropertiesOwner
;
96 HWND hwndRunFileDlgOwner
;
99 BOOL
LaunchCPanel(HWND hwnd
, LPCTSTR applet
)
101 TCHAR szParams
[MAX_PATH
];
102 _tcscpy(szParams
, TEXT("shell32.dll,Control_RunDLL "));
103 _tcscat(szParams
, applet
);
104 return (ShellExecute(hwnd
, TEXT("open"), TEXT("rundll32.exe"), szParams
, NULL
, SW_SHOWDEFAULT
) > (HINSTANCE
)32);
108 IUnknown_from_impl(ITrayWindowImpl
*This
)
110 return (IUnknown
*)&This
->lpVtbl
;
114 ITrayWindow_from_impl(ITrayWindowImpl
*This
)
116 return (ITrayWindow
*)&This
->lpVtbl
;
119 static IShellDesktopTray
*
120 IShellDesktopTray_from_impl(ITrayWindowImpl
*This
)
122 return (IShellDesktopTray
*)&This
->lpVtblShellDesktopTray
;
125 static ITrayWindowImpl
*
126 impl_from_ITrayWindow(ITrayWindow
*iface
)
128 return (ITrayWindowImpl
*)((ULONG_PTR
)iface
- FIELD_OFFSET(ITrayWindowImpl
,
132 static ITrayWindowImpl
*
133 impl_from_IShellDesktopTray(IShellDesktopTray
*iface
)
135 return (ITrayWindowImpl
*)((ULONG_PTR
)iface
- FIELD_OFFSET(ITrayWindowImpl
,
136 lpVtblShellDesktopTray
));
144 ITrayWindowImpl_UpdateNonClientMetrics(IN OUT ITrayWindowImpl
*This
)
146 This
->ncm
.cbSize
= sizeof(This
->ncm
);
147 if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS
,
152 if (This
->hFont
!= NULL
)
153 DeleteObject(This
->hFont
);
155 This
->hFont
= CreateFontIndirect(&This
->ncm
.lfMessageFont
);
163 ITrayWindowImpl_SetWindowsFont(IN OUT ITrayWindowImpl
*This
)
165 if (This
->hwndTrayNotify
!= NULL
)
167 SendMessage(This
->hwndTrayNotify
,
175 ITrayWindowImpl_GetScreenRectFromRect(IN OUT ITrayWindowImpl
*This
,
182 mi
.cbSize
= sizeof(mi
);
183 hMon
= MonitorFromRect(pRect
,
189 *pRect
= mi
.rcMonitor
;
195 pRect
->right
= GetSystemMetrics(SM_CXSCREEN
);
196 pRect
->bottom
= GetSystemMetrics(SM_CYSCREEN
);
205 ITrayWindowImpl_GetMonitorFromRect(IN OUT ITrayWindowImpl
*This
,
206 IN
const RECT
*pRect
)
210 /* In case the monitor sizes or saved sizes differ a bit (probably
211 not a lot, only so the tray window overlaps into another monitor
212 now), minimize the risk that we determine a wrong monitor by
213 using the center point of the tray window if we can't determine
214 it using the rectangle. */
215 hMon
= MonitorFromRect(pRect
,
216 MONITOR_DEFAULTTONULL
);
221 pt
.x
= pRect
->left
+ ((pRect
->right
- pRect
->left
) / 2);
222 pt
.y
= pRect
->top
+ ((pRect
->bottom
- pRect
->top
) / 2);
224 /* be less error-prone, find the nearest monitor */
225 hMon
= MonitorFromPoint(pt
,
226 MONITOR_DEFAULTTONEAREST
);
233 ITrayWindowImpl_GetScreenRect(IN OUT ITrayWindowImpl
*This
,
234 IN HMONITOR hMonitor
,
237 HMONITOR hMon
= NULL
;
239 if (hMonitor
!= NULL
)
243 mi
.cbSize
= sizeof(mi
);
244 if (!GetMonitorInfo(hMonitor
,
247 /* Hm, the monitor is gone? Try to find a monitor where it
248 could be located now */
249 hMon
= ITrayWindowImpl_GetMonitorFromRect(This
,
252 !GetMonitorInfo(hMon
,
260 *pRect
= mi
.rcMonitor
;
267 pRect
->right
= GetSystemMetrics(SM_CXSCREEN
);
268 pRect
->bottom
= GetSystemMetrics(SM_CYSCREEN
);
275 ITrayWindowImpl_MakeTrayRectWithSize(IN DWORD Position
,
276 IN
const SIZE
*pTraySize
,
282 pRect
->right
= pRect
->left
+ pTraySize
->cx
;
286 pRect
->bottom
= pRect
->top
+ pTraySize
->cy
;
290 pRect
->left
= pRect
->right
- pTraySize
->cx
;
295 pRect
->top
= pRect
->bottom
- pTraySize
->cy
;
301 ITrayWindowImpl_GetTrayRectFromScreenRect(IN OUT ITrayWindowImpl
*This
,
303 IN
const RECT
*pScreen
,
304 IN
const SIZE
*pTraySize OPTIONAL
,
307 if (pTraySize
== NULL
)
308 pTraySize
= &This
->TraySize
;
312 /* Move the border outside of the screen */
314 GetSystemMetrics(SM_CXEDGE
),
315 GetSystemMetrics(SM_CYEDGE
));
317 ITrayWindowImpl_MakeTrayRectWithSize(Position
,
323 ITrayWindowImpl_IsPosHorizontal(IN OUT ITrayWindowImpl
*This
)
325 return This
->Position
== ABE_TOP
|| This
->Position
== ABE_BOTTOM
;
329 ITrayWindowImpl_CalculateValidSize(IN OUT ITrayWindowImpl
*This
,
338 //Horizontal = ITrayWindowImpl_IsPosHorizontal(This);
340 szWnd
.cx
= pRect
->right
- pRect
->left
;
341 szWnd
.cy
= pRect
->bottom
- pRect
->top
;
344 hMon
= ITrayWindowImpl_GetScreenRectFromRect(This
,
346 MONITOR_DEFAULTTONEAREST
);
348 /* Calculate the maximum size of the tray window and limit the window
349 size to half of the screen's size. */
350 szMax
.cx
= (rcScreen
.right
- rcScreen
.left
) / 2;
351 szMax
.cy
= (rcScreen
.bottom
- rcScreen
.top
) / 2;
352 if (szWnd
.cx
> szMax
.cx
)
354 if (szWnd
.cy
> szMax
.cy
)
357 /* FIXME - calculate */
359 ITrayWindowImpl_GetTrayRectFromScreenRect(This
,
370 ITrayWindowImpl_GetMinimumWindowSize(IN OUT ITrayWindowImpl
*This
,
375 AdjustWindowRectEx(&rcMin
,
376 GetWindowLong(This
->hWnd
,
379 GetWindowLong(This
->hWnd
,
388 ITrayWindowImpl_GetDraggingRectFromPt(IN OUT ITrayWindowImpl
*This
,
391 OUT HMONITOR
*phMonitor
)
393 HMONITOR hMon
, hMonNew
;
394 DWORD PosH
, PosV
, Pos
;
395 SIZE DeltaPt
, ScreenOffset
;
401 /* Determine the screen rectangle */
402 hMon
= MonitorFromPoint(pt
,
403 MONITOR_DEFAULTTONULL
);
409 mi
.cbSize
= sizeof(mi
);
410 if (!GetMonitorInfo(hMon
,
414 goto GetPrimaryScreenRect
;
417 /* make left top corner of the screen zero based to
418 make calculations easier */
419 pt
.x
-= mi
.rcMonitor
.left
;
420 pt
.y
-= mi
.rcMonitor
.top
;
422 ScreenOffset
.cx
= mi
.rcMonitor
.left
;
423 ScreenOffset
.cy
= mi
.rcMonitor
.top
;
424 rcScreen
.right
= mi
.rcMonitor
.right
- mi
.rcMonitor
.left
;
425 rcScreen
.bottom
= mi
.rcMonitor
.bottom
- mi
.rcMonitor
.top
;
429 GetPrimaryScreenRect
:
432 rcScreen
.right
= GetSystemMetrics(SM_CXSCREEN
);
433 rcScreen
.bottom
= GetSystemMetrics(SM_CYSCREEN
);
436 /* Calculate the nearest screen border */
437 if (pt
.x
< rcScreen
.right
/ 2)
444 DeltaPt
.cx
= rcScreen
.right
- pt
.x
;
448 if (pt
.y
< rcScreen
.bottom
/ 2)
455 DeltaPt
.cy
= rcScreen
.bottom
- pt
.y
;
459 Pos
= (DeltaPt
.cx
* rcScreen
.bottom
< DeltaPt
.cy
* rcScreen
.right
) ? PosH
: PosV
;
461 /* Fix the screen origin to be relative to the primary monitor again */
462 OffsetRect(&rcScreen
,
466 hMonNew
= ITrayWindowImpl_GetMonitorFromRect(This
,
467 &This
->rcTrayWnd
[Pos
]);
472 /* Recalculate the rectangle, we're dragging to another monitor.
473 We don't need to recalculate the rect on single monitor systems. */
474 szTray
.cx
= This
->rcTrayWnd
[Pos
].right
- This
->rcTrayWnd
[Pos
].left
;
475 szTray
.cy
= This
->rcTrayWnd
[Pos
].bottom
- This
->rcTrayWnd
[Pos
].top
;
477 ITrayWindowImpl_GetTrayRectFromScreenRect(This
,
487 /* The user is dragging the tray window on the same monitor. We don't need
488 to recalculate the rectangle */
489 *pRect
= This
->rcTrayWnd
[Pos
];
498 ITrayWindowImpl_GetDraggingRectFromRect(IN OUT ITrayWindowImpl
*This
,
500 OUT HMONITOR
*phMonitor
)
504 /* Calculate the center of the rectangle. We call
505 ITrayWindowImpl_GetDraggingRectFromPt to calculate a valid
506 dragging rectangle */
507 pt
.x
= pRect
->left
+ ((pRect
->right
- pRect
->left
) / 2);
508 pt
.y
= pRect
->top
+ ((pRect
->bottom
- pRect
->top
) / 2);
510 return ITrayWindowImpl_GetDraggingRectFromPt(This
,
517 ITrayWindowImpl_ChangingWinPos(IN OUT ITrayWindowImpl
*This
,
518 IN OUT LPWINDOWPOS pwp
)
522 if (This
->IsDragging
)
524 rcTray
.left
= pwp
->x
;
526 rcTray
.right
= rcTray
.left
+ pwp
->cx
;
527 rcTray
.bottom
= rcTray
.top
+ pwp
->cy
;
529 if (!EqualRect(&rcTray
,
530 &This
->rcTrayWnd
[This
->DraggingPosition
]))
532 /* Recalculate the rectangle, the user dragged the tray
533 window to another monitor or the window was somehow else
535 This
->DraggingPosition
= ITrayWindowImpl_GetDraggingRectFromRect(This
,
537 &This
->DraggingMonitor
);
538 //This->rcTrayWnd[This->DraggingPosition] = rcTray;
541 //This->Monitor = ITrayWindowImpl_CalculateValidSize(This,
542 // This->DraggingPosition,
545 This
->Monitor
= This
->DraggingMonitor
;
546 This
->Position
= This
->DraggingPosition
;
547 This
->IsDragging
= FALSE
;
549 This
->rcTrayWnd
[This
->Position
] = rcTray
;
552 else if (GetWindowRect(This
->hWnd
,
555 if (This
->InSizeMove
)
557 if (!(pwp
->flags
& SWP_NOMOVE
))
559 rcTray
.left
= pwp
->x
;
563 if (!(pwp
->flags
& SWP_NOSIZE
))
565 rcTray
.right
= rcTray
.left
+ pwp
->cx
;
566 rcTray
.bottom
= rcTray
.top
+ pwp
->cy
;
569 This
->Position
= ITrayWindowImpl_GetDraggingRectFromRect(This
,
573 if (!(pwp
->flags
& (SWP_NOMOVE
| SWP_NOSIZE
)))
580 ITrayWindowImpl_MakeTrayRectWithSize(This
->Position
,
585 This
->rcTrayWnd
[This
->Position
] = rcTray
;
589 /* If the user isn't resizing the tray window we need to make sure the
590 new size or position is valid. This is to prevent changes to the window
591 without user interaction. */
592 rcTray
= This
->rcTrayWnd
[This
->Position
];
596 This
->TraySize
.cx
= rcTray
.right
- rcTray
.left
;
597 This
->TraySize
.cy
= rcTray
.bottom
- rcTray
.top
;
599 pwp
->flags
&= ~(SWP_NOMOVE
| SWP_NOSIZE
);
600 pwp
->x
= rcTray
.left
;
602 pwp
->cx
= This
->TraySize
.cx
;
603 pwp
->cy
= This
->TraySize
.cy
;
608 ITrayWindowImpl_ApplyClipping(IN OUT ITrayWindowImpl
*This
,
611 RECT rcClip
, rcWindow
;
614 if (GetWindowRect(This
->hWnd
,
617 /* Disable clipping on systems with only one monitor */
618 if (GetSystemMetrics(SM_CMONITORS
) <= 1)
625 ITrayWindowImpl_GetScreenRect(This
,
629 if (!IntersectRect(&rcClip
,
640 hClipRgn
= CreateRectRgnIndirect(&rcClip
);
645 /* Set the clipping region or make sure the window isn't clipped
646 by disabling it explicitly. */
647 SetWindowRgn(This
->hWnd
,
654 ITrayWindowImpl_ResizeWorkArea(IN OUT ITrayWindowImpl
*This
)
656 RECT rcTray
,rcWorkArea
;
658 /* If monitor has changed then fix the previous monitors work area */
659 if (This
->PreviousMonitor
!= This
->Monitor
)
661 ITrayWindowImpl_GetScreenRect(This
,
662 This
->PreviousMonitor
,
664 SystemParametersInfo(SPI_SETWORKAREA
,
670 rcTray
= This
->rcTrayWnd
[This
->Position
];
672 ITrayWindowImpl_GetScreenRect(This
,
675 This
->PreviousMonitor
= This
->Monitor
;
677 /* If AutoHide is false then change the workarea to exclude the area that
678 the taskbar covers. */
681 switch (This
->Position
)
684 rcWorkArea
.top
= rcTray
.bottom
;
687 rcWorkArea
.left
= rcTray
.right
;
690 rcWorkArea
.right
= rcTray
.left
;
693 rcWorkArea
.bottom
= rcTray
.top
;
698 SystemParametersInfo(SPI_SETWORKAREA
,
705 ITrayWindowImpl_CheckTrayWndPosition(IN OUT ITrayWindowImpl
*This
)
709 rcTray
= This
->rcTrayWnd
[This
->Position
];
710 // DbgPrint("CheckTray: %d: %d,%d,%d,%d\n", This->Position, rcTray.left, rcTray.top, rcTray.right, rcTray.bottom);
712 /* Move the tray window */
713 SetWindowPos(This
->hWnd
,
717 rcTray
.right
- rcTray
.left
,
718 rcTray
.bottom
- rcTray
.top
,
721 ITrayWindowImpl_ResizeWorkArea(This
);
723 ITrayWindowImpl_ApplyClipping(This
,
727 typedef struct _TW_STUCKRECTS2
735 } TW_STRUCKRECTS2
, *PTW_STUCKRECTS2
;
738 ITrayWindowImpl_RegLoadSettings(IN OUT ITrayWindowImpl
*This
)
743 SIZE WndSize
, EdgeSize
, DlgFrameSize
;
744 DWORD cbSize
= sizeof(sr
);
746 EdgeSize
.cx
= GetSystemMetrics(SM_CXEDGE
);
747 EdgeSize
.cy
= GetSystemMetrics(SM_CYEDGE
);
748 DlgFrameSize
.cx
= GetSystemMetrics(SM_CXDLGFRAME
);
749 DlgFrameSize
.cy
= GetSystemMetrics(SM_CYDLGFRAME
);
751 if (SHGetValue(hkExplorer
,
756 &cbSize
) == ERROR_SUCCESS
&&
757 sr
.cbSize
== sizeof(sr
))
759 This
->AutoHide
= (sr
.dwFlags
& ABS_AUTOHIDE
) != 0;
760 This
->AlwaysOnTop
= (sr
.dwFlags
& ABS_ALWAYSONTOP
) != 0;
761 This
->SmSmallIcons
= (sr
.dwFlags
& 0x4) != 0;
762 This
->HideClock
= (sr
.dwFlags
& 0x8) != 0;
764 /* FIXME: Are there more flags? */
766 if (This
->hWnd
!= NULL
)
767 SetWindowPos (This
->hWnd
,
768 This
->AlwaysOnTop
? HWND_TOPMOST
: HWND_NOTOPMOST
,
773 SWP_NOMOVE
| SWP_NOSIZE
);
775 if (sr
.Position
> ABE_BOTTOM
)
776 This
->Position
= ABE_BOTTOM
;
778 This
->Position
= sr
.Position
;
780 /* Try to find out which monitor the tray window was located on last.
781 Here we're only interested in the monitor screen that we think
782 is the last one used. We're going to determine on which monitor
783 we really are after calculating the docked position. */
785 ITrayWindowImpl_GetScreenRectFromRect(This
,
787 MONITOR_DEFAULTTONEAREST
);
791 This
->Position
= ABE_BOTTOM
;
793 /* Use the minimum size of the taskbar, we'll use the start
794 button as a minimum for now. Make sure we calculate the
795 entire window size, not just the client size. However, we
796 use a thinner border than a standard thick border, so that
797 the start button and bands are not stuck to the screen border. */
798 sr
.Size
.cx
= This
->StartBtnSize
.cx
+ (2 * (EdgeSize
.cx
+ DlgFrameSize
.cx
));
799 sr
.Size
.cy
= This
->StartBtnSize
.cy
+ (2 * (EdgeSize
.cy
+ DlgFrameSize
.cy
));
801 /* Use the primary screen by default */
804 rcScreen
.right
= GetSystemMetrics(SM_CXSCREEN
);
805 rcScreen
.bottom
= GetSystemMetrics(SM_CYSCREEN
);
806 ITrayWindowImpl_GetScreenRectFromRect(This
,
808 MONITOR_DEFAULTTOPRIMARY
);
811 /* Determine a minimum tray window rectangle. The "client" height is
812 zero here since we cannot determine an optimal minimum width when
813 loaded as a vertical tray window. We just need to make sure the values
814 loaded from the registry are at least. The windows explorer behaves
815 the same way, it allows the user to save a zero width vertical tray
816 window, but not a zero height horizontal tray window. */
817 WndSize
.cx
= 2 * (EdgeSize
.cx
+ DlgFrameSize
.cx
);
818 WndSize
.cy
= This
->StartBtnSize
.cy
+ (2 * (EdgeSize
.cy
+ DlgFrameSize
.cy
));
820 if (WndSize
.cx
< sr
.Size
.cx
)
821 WndSize
.cx
= sr
.Size
.cx
;
822 if (WndSize
.cy
< sr
.Size
.cy
)
823 WndSize
.cy
= sr
.Size
.cy
;
825 /* Save the calculated size */
826 This
->TraySize
= WndSize
;
828 /* Calculate all docking rectangles. We need to do this here so they're
829 initialized and dragging the tray window to another position gives
835 ITrayWindowImpl_GetTrayRectFromScreenRect(This
,
839 &This
->rcTrayWnd
[Pos
]);
840 // 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);
843 /* Determine which monitor we are on. It shouldn't matter which docked
844 position rectangle we use */
845 This
->Monitor
= ITrayWindowImpl_GetMonitorFromRect(This
,
846 &This
->rcTrayWnd
[ABE_LEFT
]);
850 ITrayWindowImpl_TrackMenu(IN OUT ITrayWindowImpl
*This
,
852 IN POINT
*ppt OPTIONAL
,
853 IN HWND hwndExclude OPTIONAL
,
855 IN BOOL IsContextMenu
)
857 TPMPARAMS tmp
, *ptmp
= NULL
;
862 if (hwndExclude
!= NULL
)
864 /* Get the client rectangle and map it to screen coordinates */
865 if (GetClientRect(hwndExclude
,
867 MapWindowPoints(hwndExclude
,
869 (LPPOINT
)&tmp
.rcExclude
,
879 GetClientRect(This
->hWnd
,
881 MapWindowPoints(This
->hWnd
,
883 (LPPOINT
)&tmp
.rcExclude
,
891 /* NOTE: TrackPopupMenuEx will eventually align the track position
892 for us, no need to take care of it here as long as the
893 coordinates are somewhere within the exclusion rectangle */
894 pt
.x
= ptmp
->rcExclude
.left
;
895 pt
.y
= ptmp
->rcExclude
.top
;
903 tmp
.cbSize
= sizeof(tmp
);
905 fuFlags
= TPM_RETURNCMD
| TPM_VERTICAL
;
906 fuFlags
|= (TrackUp
? TPM_BOTTOMALIGN
: TPM_TOPALIGN
);
908 fuFlags
|= TPM_RIGHTBUTTON
;
910 fuFlags
|= (TrackUp
? TPM_VERNEGANIMATION
: TPM_VERPOSANIMATION
);
912 cmdId
= TrackPopupMenuEx(hMenu
,
923 ITrayWindowImpl_TrackCtxMenu(IN OUT ITrayWindowImpl
*This
,
924 IN
const TRAYWINDOW_CTXMENU
*pMenu
,
925 IN POINT
*ppt OPTIONAL
,
926 IN HWND hwndExclude OPTIONAL
,
928 IN PVOID Context OPTIONAL
)
932 PVOID pcmContext
= NULL
;
934 hPopup
= pMenu
->CreateCtxMenu(This
->hWnd
,
939 cmdId
= ITrayWindowImpl_TrackMenu(This
,
946 pMenu
->CtxMenuCommand(This
->hWnd
,
956 ITrayWindowImpl_Free(ITrayWindowImpl
*This
)
958 HeapFree(hProcessHeap
,
964 static ULONG STDMETHODCALLTYPE
965 ITrayWindowImpl_Release(IN OUT ITrayWindow
*iface
)
967 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
970 Ret
= InterlockedDecrement(&This
->Ref
);
972 ITrayWindowImpl_Free(This
);
978 ITrayWindowImpl_Destroy(ITrayWindowImpl
*This
)
980 (void)InterlockedExchangePointer((PVOID
*)&This
->hWnd
,
983 if (This
->himlStartBtn
!= NULL
)
985 ImageList_Destroy(This
->himlStartBtn
);
986 This
->himlStartBtn
= NULL
;
989 if (This
->hCaptionFont
!= NULL
)
991 DeleteObject(This
->hCaptionFont
);
992 This
->hCaptionFont
= NULL
;
995 if (This
->hStartBtnFont
!= NULL
)
997 DeleteObject(This
->hStartBtnFont
);
998 This
->hStartBtnFont
= NULL
;
1001 if (This
->hFont
!= NULL
)
1003 DeleteObject(This
->hFont
);
1007 if (This
->StartMenuPopup
!= NULL
)
1009 IMenuPopup_Release(This
->StartMenuPopup
);
1010 This
->StartMenuPopup
= NULL
;
1013 if (This
->hbmStartMenu
!= NULL
)
1015 DeleteObject(This
->hbmStartMenu
);
1016 This
->hbmStartMenu
= NULL
;
1019 if (This
->StartMenuBand
!= NULL
)
1021 IMenuBand_Release(This
->StartMenuBand
);
1022 This
->StartMenuBand
= NULL
;
1025 if (This
->TrayBandSite
!= NULL
)
1027 /* FIXME: Unload bands */
1028 ITrayBandSite_Release(This
->TrayBandSite
);
1029 This
->TrayBandSite
= NULL
;
1032 if (This
->TaskbarTheme
)
1034 CloseThemeData(This
->TaskbarTheme
);
1035 This
->TaskbarTheme
= NULL
;
1038 ITrayWindowImpl_Release(ITrayWindow_from_impl(This
));
1040 if (InterlockedDecrement(&TrayWndCount
) == 0)
1044 static ULONG STDMETHODCALLTYPE
1045 ITrayWindowImpl_AddRef(IN OUT ITrayWindow
*iface
)
1047 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1049 return InterlockedIncrement(&This
->Ref
);
1054 ITrayWindowImpl_NCCreate(IN OUT ITrayWindowImpl
*This
)
1056 ITrayWindowImpl_AddRef(ITrayWindow_from_impl(This
));
1062 ITrayWindowImpl_UpdateStartButton(IN OUT ITrayWindowImpl
*This
,
1063 IN HBITMAP hbmStart OPTIONAL
)
1065 SIZE Size
= { 0, 0 };
1067 if (This
->himlStartBtn
== NULL
||
1068 !SendMessage(This
->hwndStart
,
1073 Size
.cx
= GetSystemMetrics(SM_CXEDGE
);
1074 Size
.cy
= GetSystemMetrics(SM_CYEDGE
);
1076 if (hbmStart
== NULL
)
1078 hbmStart
= (HBITMAP
)SendMessage(This
->hwndStart
,
1084 if (hbmStart
!= NULL
)
1088 if (GetObject(hbmStart
,
1092 Size
.cx
+= bmp
.bmWidth
;
1093 Size
.cy
+= max(bmp
.bmHeight
,
1094 GetSystemMetrics(SM_CYCAPTION
));
1098 /* Huh?! Shouldn't happen... */
1105 Size
.cx
+= GetSystemMetrics(SM_CXMINIMIZED
);
1106 Size
.cy
+= GetSystemMetrics(SM_CYCAPTION
);
1110 /* Save the size of the start button */
1111 This
->StartBtnSize
= Size
;
1115 ITrayWindowImpl_AlignControls(IN OUT ITrayWindowImpl
*This
,
1116 IN PRECT prcClient OPTIONAL
)
1119 SIZE TraySize
, StartSize
;
1120 POINT ptTrayNotify
= { 0, 0 };
1124 ITrayWindowImpl_UpdateStartButton(This
, NULL
);
1125 if (prcClient
!= NULL
)
1127 rcClient
= *prcClient
;
1131 if (!GetClientRect(This
->hWnd
,
1138 Horizontal
= ITrayWindowImpl_IsPosHorizontal(This
);
1140 /* We're about to resize/move the start button, the rebar control and
1141 the tray notification control */
1142 dwp
= BeginDeferWindowPos(3);
1146 /* Limit the Start button width to the client width, if neccessary */
1147 StartSize
= This
->StartBtnSize
;
1148 if (StartSize
.cx
> rcClient
.right
)
1149 StartSize
.cx
= rcClient
.right
;
1151 if (This
->hwndStart
!= NULL
)
1153 /* Resize and reposition the button */
1154 dwp
= DeferWindowPos(dwp
,
1161 SWP_NOZORDER
| SWP_NOACTIVATE
);
1166 /* Determine the size that the tray notification window needs */
1170 TraySize
.cy
= rcClient
.bottom
;
1174 TraySize
.cx
= rcClient
.right
;
1178 if (This
->hwndTrayNotify
!= NULL
&&
1179 SendMessage(This
->hwndTrayNotify
,
1180 TNWM_GETMINIMUMSIZE
,
1184 /* Move the tray notification window to the desired location */
1186 ptTrayNotify
.x
= rcClient
.right
- TraySize
.cx
;
1188 ptTrayNotify
.y
= rcClient
.bottom
- TraySize
.cy
;
1190 dwp
= DeferWindowPos(dwp
,
1191 This
->hwndTrayNotify
,
1197 SWP_NOZORDER
| SWP_NOACTIVATE
);
1202 /* Resize/Move the rebar control */
1203 if (This
->hwndRebar
!= NULL
)
1205 POINT ptRebar
= { 0, 0 };
1208 SetWindowStyle(This
->hwndRebar
,
1210 Horizontal
? 0 : CCS_VERT
);
1214 ptRebar
.x
= StartSize
.cx
+ GetSystemMetrics(SM_CXSIZEFRAME
);
1215 szRebar
.cx
= ptTrayNotify
.x
- ptRebar
.x
;
1216 szRebar
.cy
= rcClient
.bottom
;
1220 ptRebar
.y
= StartSize
.cy
+ GetSystemMetrics(SM_CYSIZEFRAME
);
1221 szRebar
.cx
= rcClient
.right
;
1222 szRebar
.cy
= ptTrayNotify
.y
- ptRebar
.y
;
1225 dwp
= DeferWindowPos(dwp
,
1232 SWP_NOZORDER
| SWP_NOACTIVATE
);
1236 EndDeferWindowPos(dwp
);
1238 if (This
->hwndTaskSwitch
!= NULL
)
1240 /* Update the task switch window configuration */
1241 SendMessage(This
->hwndTaskSwitch
,
1242 TSWM_UPDATETASKBARPOS
,
1249 ITrayWindowImpl_CreateStartBtnImageList(IN OUT ITrayWindowImpl
*This
)
1254 if (This
->himlStartBtn
!= NULL
)
1257 IconSize
.cx
= GetSystemMetrics(SM_CXSMICON
);
1258 IconSize
.cy
= GetSystemMetrics(SM_CYSMICON
);
1260 /* Load the start button icon and create a image list for it */
1261 hIconStart
= LoadImage(hExplorerInstance
,
1262 MAKEINTRESOURCE(IDI_START
),
1266 LR_SHARED
| LR_DEFAULTCOLOR
);
1268 if (hIconStart
!= NULL
)
1270 This
->himlStartBtn
= ImageList_Create(IconSize
.cx
,
1272 ILC_COLOR32
| ILC_MASK
,
1275 if (This
->himlStartBtn
!= NULL
)
1277 if (ImageList_AddIcon(This
->himlStartBtn
,
1283 /* Failed to add the icon! */
1284 ImageList_Destroy(This
->himlStartBtn
);
1285 This
->himlStartBtn
= NULL
;
1293 ITrayWindowImpl_CreateStartButtonBitmap(IN OUT ITrayWindowImpl
*This
)
1295 TCHAR szStartCaption
[32];
1298 HDC hDCScreen
= NULL
;
1299 SIZE Size
, SmallIcon
;
1300 HBITMAP hbmpOld
, hbmp
= NULL
;
1301 HBITMAP hBitmap
= NULL
;
1307 /* NOTE: This is the backwards compatibility code that is used if the
1308 Common Controls Version 6.0 are not available! */
1310 if (!LoadString(hExplorerInstance
,
1313 sizeof(szStartCaption
) / sizeof(szStartCaption
[0])))
1318 /* Load the start button icon */
1319 SmallIcon
.cx
= GetSystemMetrics(SM_CXSMICON
);
1320 SmallIcon
.cy
= GetSystemMetrics(SM_CYSMICON
);
1321 hIconStart
= LoadImage(hExplorerInstance
,
1322 MAKEINTRESOURCE(IDI_START
),
1326 LR_SHARED
| LR_DEFAULTCOLOR
);
1328 hDCScreen
= GetDC(NULL
);
1329 if (hDCScreen
== NULL
)
1332 hDC
= CreateCompatibleDC(hDCScreen
);
1336 hFontOld
= SelectObject(hDC
,
1337 This
->hStartBtnFont
);
1339 Ret
= GetTextExtentPoint32(hDC
,
1341 _tcslen(szStartCaption
),
1349 /* Make sure the height is at least the size of a caption icon. */
1350 if (hIconStart
!= NULL
)
1351 Size
.cx
+= SmallIcon
.cx
+ 4;
1352 Size
.cy
= max(Size
.cy
,
1355 /* Create the bitmap */
1356 hbmp
= CreateCompatibleBitmap(hDCScreen
,
1362 /* Caluclate the button rect */
1365 rcButton
.right
= Size
.cx
;
1366 rcButton
.bottom
= Size
.cy
;
1368 /* Draw the button */
1369 hbmpOld
= SelectObject(hDC
,
1372 Flags
= DC_TEXT
| DC_INBUTTON
;
1373 if (hIconStart
!= NULL
)
1376 if (DrawCapTemp
!= NULL
)
1378 Ret
= DrawCapTemp(NULL
,
1381 This
->hStartBtnFont
,
1393 /* We successfully created the bitmap! */
1398 if (hDCScreen
!= NULL
)
1414 ITrayWindowImpl_UpdateTheme(IN OUT ITrayWindowImpl
*This
)
1416 if (This
->TaskbarTheme
)
1417 CloseThemeData(This
->TaskbarTheme
);
1419 if (IsThemeActive())
1420 This
->TaskbarTheme
= OpenThemeData(This
->hWnd
, L
"Taskbar");
1422 This
->TaskbarTheme
= 0;
1426 ITrayWindowImpl_Create(IN OUT ITrayWindowImpl
*This
)
1428 TCHAR szStartCaption
[32];
1430 SetWindowTheme(This
->hWnd
, L
"TaskBar", NULL
);
1431 ITrayWindowImpl_UpdateTheme(This
);
1433 InterlockedIncrement(&TrayWndCount
);
1435 if (!LoadString(hExplorerInstance
,
1438 sizeof(szStartCaption
) / sizeof(szStartCaption
[0])))
1440 szStartCaption
[0] = TEXT('\0');
1443 if (This
->hStartBtnFont
== NULL
|| This
->hCaptionFont
== NULL
)
1445 NONCLIENTMETRICS ncm
;
1447 /* Get the system fonts, we use the caption font,
1448 always bold, though. */
1449 ncm
.cbSize
= sizeof(ncm
);
1450 if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS
,
1455 if (This
->hCaptionFont
== NULL
)
1457 ncm
.lfCaptionFont
.lfWeight
= FW_NORMAL
;
1458 This
->hCaptionFont
= CreateFontIndirect(&ncm
.lfCaptionFont
);
1461 if (This
->hStartBtnFont
== NULL
)
1463 ncm
.lfCaptionFont
.lfWeight
= FW_BOLD
;
1464 This
->hStartBtnFont
= CreateFontIndirect(&ncm
.lfCaptionFont
);
1469 /* Create the Start button */
1470 This
->hwndStart
= CreateWindowEx(0,
1473 WS_CHILD
| WS_VISIBLE
| WS_CLIPSIBLINGS
|
1474 BS_PUSHBUTTON
| BS_CENTER
| BS_VCENTER
| BS_BITMAP
,
1480 (HMENU
)IDC_STARTBTN
,
1483 if (This
->hwndStart
)
1485 SetWindowTheme(This
->hwndStart
, L
"Start", NULL
);
1486 SendMessage(This
->hwndStart
,
1488 (WPARAM
)This
->hStartBtnFont
,
1491 if (ITrayWindowImpl_CreateStartBtnImageList(This
))
1493 BUTTON_IMAGELIST bil
;
1495 /* Try to set the start button image. This requires the Common
1496 Controls 6.0 to be present (XP and later) */
1497 bil
.himl
= This
->himlStartBtn
;
1498 bil
.margin
.left
= bil
.margin
.right
= 1;
1499 bil
.margin
.top
= bil
.margin
.bottom
= 1;
1500 bil
.uAlign
= BUTTON_IMAGELIST_ALIGN_LEFT
;
1502 if (!SendMessage(This
->hwndStart
,
1507 /* Fall back to the deprecated method on older systems that don't
1508 support Common Controls 6.0 */
1509 ImageList_Destroy(This
->himlStartBtn
);
1510 This
->himlStartBtn
= NULL
;
1512 goto SetStartBtnImage
;
1515 /* We're using the image list, remove the BS_BITMAP style and
1516 don't center it horizontally */
1517 SetWindowStyle(This
->hwndStart
,
1518 BS_BITMAP
| BS_RIGHT
,
1521 ITrayWindowImpl_UpdateStartButton(This
,
1526 HBITMAP hbmStart
, hbmOld
;
1529 hbmStart
= ITrayWindowImpl_CreateStartButtonBitmap(This
);
1530 if (hbmStart
!= NULL
)
1532 ITrayWindowImpl_UpdateStartButton(This
,
1535 hbmOld
= (HBITMAP
)SendMessage(This
->hwndStart
,
1541 DeleteObject(hbmOld
);
1546 /* Load the saved tray window settings */
1547 ITrayWindowImpl_RegLoadSettings(This
);
1549 /* Create and initialize the start menu */
1550 This
->hbmStartMenu
= LoadBitmap(hExplorerInstance
,
1551 MAKEINTRESOURCE(IDB_STARTMENU
));
1552 This
->StartMenuPopup
= CreateStartMenu(ITrayWindow_from_impl(This
),
1553 &This
->StartMenuBand
,
1557 /* Load the tray band site */
1558 if (This
->TrayBandSite
!= NULL
)
1560 ITrayBandSite_Release(This
->TrayBandSite
);
1563 This
->TrayBandSite
= CreateTrayBandSite(ITrayWindow_from_impl(This
),
1565 &This
->hwndTaskSwitch
);
1566 SetWindowTheme(This
->hwndRebar
, L
"TaskBar", NULL
);
1568 /* Create the tray notification window */
1569 This
->hwndTrayNotify
= CreateTrayNotifyWnd(ITrayWindow_from_impl(This
),
1572 if (ITrayWindowImpl_UpdateNonClientMetrics(This
))
1574 ITrayWindowImpl_SetWindowsFont(This
);
1577 /* Move the tray window to the right position and resize it if neccessary */
1578 ITrayWindowImpl_CheckTrayWndPosition(This
);
1580 /* Align all controls on the tray window */
1581 ITrayWindowImpl_AlignControls(This
,
1585 static HRESULT STDMETHODCALLTYPE
1586 ITrayWindowImpl_QueryInterface(IN OUT ITrayWindow
*iface
,
1590 ITrayWindowImpl
*This
;
1595 This
= impl_from_ITrayWindow(iface
);
1597 if (IsEqualIID(riid
,
1600 *ppvObj
= IUnknown_from_impl(This
);
1602 else if (IsEqualIID(riid
,
1603 &IID_IShellDesktopTray
))
1605 *ppvObj
= IShellDesktopTray_from_impl(This
);
1610 return E_NOINTERFACE
;
1613 ITrayWindowImpl_AddRef(iface
);
1617 static ITrayWindowImpl
*
1618 ITrayWindowImpl_Construct(VOID
)
1620 ITrayWindowImpl
*This
;
1622 This
= HeapAlloc(hProcessHeap
,
1628 This
->lpVtbl
= &ITrayWindowImpl_Vtbl
;
1629 This
->lpVtblShellDesktopTray
= &IShellDesktopTrayImpl_Vtbl
;
1631 This
->Position
= (DWORD
)-1;
1636 static HRESULT STDMETHODCALLTYPE
1637 ITrayWindowImpl_Open(IN OUT ITrayWindow
*iface
)
1639 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1644 /* Check if there's already a window created and try to show it.
1645 If it was somehow destroyed just create a new tray window. */
1646 if (This
->hWnd
!= NULL
)
1648 if (IsWindow(This
->hWnd
))
1650 if (!IsWindowVisible(This
->hWnd
))
1652 ITrayWindowImpl_CheckTrayWndPosition(This
);
1654 ShowWindow(This
->hWnd
,
1659 goto TryCreateTrayWnd
;
1666 dwExStyle
= WS_EX_TOOLWINDOW
| WS_EX_WINDOWEDGE
;
1667 if (This
->AlwaysOnTop
)
1668 dwExStyle
|= WS_EX_TOPMOST
;
1670 if (This
->Position
!= (DWORD
)-1)
1671 rcWnd
= This
->rcTrayWnd
[This
->Position
];
1678 hWnd
= CreateWindowEx(dwExStyle
,
1681 WS_POPUP
| WS_VISIBLE
| WS_CLIPSIBLINGS
| WS_CLIPCHILDREN
|
1682 WS_BORDER
| WS_THICKFRAME
,
1685 rcWnd
.right
- rcWnd
.left
,
1686 rcWnd
.bottom
- rcWnd
.top
,
1698 static HRESULT STDMETHODCALLTYPE
1699 ITrayWindowImpl_Close(IN OUT ITrayWindow
*iface
)
1701 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1703 if (This
->hWnd
!= NULL
)
1705 SendMessage(This
->hWnd
,
1714 static HWND STDMETHODCALLTYPE
1715 ITrayWindowImpl_GetHWND(IN OUT ITrayWindow
*iface
)
1717 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1722 static BOOL STDMETHODCALLTYPE
1723 ITrayWindowImpl_IsSpecialHWND(IN OUT ITrayWindow
*iface
,
1726 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1728 return (hWnd
== This
->hWnd
||
1729 (This
->hWndDesktop
!= NULL
&& hWnd
== This
->hWndDesktop
));
1732 static BOOL STDMETHODCALLTYPE
1733 ITrayWindowImpl_IsHorizontal(IN OUT ITrayWindow
*iface
)
1735 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1736 return ITrayWindowImpl_IsPosHorizontal(This
);
1739 static HFONT STDMETHODCALLTYPE
1740 ITrayWIndowImpl_GetCaptionFonts(IN OUT ITrayWindow
*iface
,
1741 OUT HFONT
*phBoldCaption OPTIONAL
)
1743 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1745 if (phBoldCaption
!= NULL
)
1746 *phBoldCaption
= This
->hStartBtnFont
;
1748 return This
->hCaptionFont
;
1752 TrayPropertiesThread(IN OUT PVOID pParam
)
1754 ITrayWindowImpl
*This
= pParam
;
1758 GetWindowRect(This
->hwndStart
, &posRect
);
1759 hwnd
= CreateWindowEx(0,
1762 WS_OVERLAPPED
| WS_DISABLED
| WS_CLIPSIBLINGS
| WS_BORDER
| SS_LEFT
,
1765 posRect
.right
- posRect
.left
,
1766 posRect
.bottom
- posRect
.top
,
1772 This
->hwndTrayPropertiesOwner
= hwnd
;
1774 DisplayTrayProperties(hwnd
);
1776 This
->hwndTrayPropertiesOwner
= NULL
;
1777 DestroyWindow(hwnd
);
1782 static HWND STDMETHODCALLTYPE
1783 ITrayWindowImpl_DisplayProperties(IN OUT ITrayWindow
*iface
)
1785 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1788 if (This
->hwndTrayPropertiesOwner
)
1790 hTrayProp
= GetLastActivePopup(This
->hwndTrayPropertiesOwner
);
1791 if (hTrayProp
!= NULL
&&
1792 hTrayProp
!= This
->hwndTrayPropertiesOwner
)
1794 SetForegroundWindow(hTrayProp
);
1799 CloseHandle(CreateThread(NULL
, 0, TrayPropertiesThread
, This
, 0, NULL
));
1804 OpenCommonStartMenuDirectory(IN HWND hWndOwner
,
1805 IN LPCTSTR lpOperation
)
1807 TCHAR szDir
[MAX_PATH
];
1809 if (SHGetSpecialFolderPath(hWndOwner
,
1811 CSIDL_COMMON_STARTMENU
,
1814 ShellExecute(hWndOwner
,
1824 OpenTaskManager(IN HWND hWndOwner
)
1826 ShellExecute(hWndOwner
,
1828 TEXT("taskmgr.exe"),
1834 static BOOL STDMETHODCALLTYPE
1835 ITrayWindowImpl_ExecContextMenuCmd(IN OUT ITrayWindow
*iface
,
1838 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1839 BOOL bHandled
= TRUE
;
1843 case ID_SHELL_CMD_PROPERTIES
:
1844 ITrayWindow_DisplayProperties(iface
);
1847 case ID_SHELL_CMD_OPEN_ALL_USERS
:
1848 OpenCommonStartMenuDirectory(This
->hWnd
,
1852 case ID_SHELL_CMD_EXPLORE_ALL_USERS
:
1853 OpenCommonStartMenuDirectory(This
->hWnd
,
1857 case ID_LOCKTASKBAR
:
1858 if (SHRestricted(REST_CLASSICSHELL
) == 0)
1860 ITrayWindow_Lock(iface
,
1865 case ID_SHELL_CMD_OPEN_TASKMGR
:
1866 OpenTaskManager(This
->hWnd
);
1869 case ID_SHELL_CMD_UNDO_ACTION
:
1872 case ID_SHELL_CMD_SHOW_DESKTOP
:
1875 case ID_SHELL_CMD_TILE_WND_H
:
1876 TileWindows(NULL
, MDITILE_HORIZONTAL
, NULL
, 0, NULL
);
1879 case ID_SHELL_CMD_TILE_WND_V
:
1880 TileWindows(NULL
, MDITILE_VERTICAL
, NULL
, 0, NULL
);
1883 case ID_SHELL_CMD_CASCADE_WND
:
1884 CascadeWindows(NULL
, MDITILE_SKIPDISABLED
, NULL
, 0, NULL
);
1887 case ID_SHELL_CMD_CUST_NOTIF
:
1890 case ID_SHELL_CMD_ADJUST_DAT
:
1891 LaunchCPanel(NULL
, TEXT("timedate.cpl"));
1895 DbgPrint("ITrayWindow::ExecContextMenuCmd(%u): Unhandled Command ID!\n", uiCmd
);
1903 static BOOL STDMETHODCALLTYPE
1904 ITrayWindowImpl_Lock(IN OUT ITrayWindow
*iface
,
1908 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1910 bPrevLock
= This
->Locked
;
1911 if (This
->Locked
!= bLock
)
1913 This
->Locked
= bLock
;
1915 if (This
->TrayBandSite
!= NULL
)
1917 if (!SUCCEEDED(ITrayBandSite_Lock(This
->TrayBandSite
,
1921 This
->Locked
= bPrevLock
;
1929 static const ITrayWindowVtbl ITrayWindowImpl_Vtbl
=
1932 ITrayWindowImpl_QueryInterface
,
1933 ITrayWindowImpl_AddRef
,
1934 ITrayWindowImpl_Release
,
1936 ITrayWindowImpl_Open
,
1937 ITrayWindowImpl_Close
,
1938 ITrayWindowImpl_GetHWND
,
1939 ITrayWindowImpl_IsSpecialHWND
,
1940 ITrayWindowImpl_IsHorizontal
,
1941 ITrayWIndowImpl_GetCaptionFonts
,
1942 ITrayWindowImpl_DisplayProperties
,
1943 ITrayWindowImpl_ExecContextMenuCmd
,
1944 ITrayWindowImpl_Lock
1948 ITrayWindowImpl_DrawBackground(IN ITrayWindowImpl
*This
,
1954 GetClientRect(This
->hWnd
, &rect
);
1955 switch (This
->Position
)
1958 backoundPart
= TBP_BACKGROUNDLEFT
;
1961 backoundPart
= TBP_BACKGROUNDTOP
;
1964 backoundPart
= TBP_BACKGROUNDRIGHT
;
1968 backoundPart
= TBP_BACKGROUNDBOTTOM
;
1971 DrawThemeBackground(This
->TaskbarTheme
, dc
, backoundPart
, 0, &rect
, 0);
1976 ITrayWindowImpl_DrawSizer(IN ITrayWindowImpl
*This
,
1983 GetWindowRect(This
->hWnd
, &rect
);
1984 OffsetRect(&rect
, -rect
.left
, -rect
.top
);
1986 hdc
= GetDCEx(This
->hWnd
, hRgn
, DCX_WINDOW
| DCX_INTERSECTRGN
| DCX_PARENTCLIP
);
1988 switch (This
->Position
)
1991 backoundPart
= TBP_SIZINGBARLEFT
;
1992 rect
.left
= rect
.right
- GetSystemMetrics(SM_CXSIZEFRAME
);
1995 backoundPart
= TBP_SIZINGBARTOP
;
1996 rect
.top
= rect
.bottom
- GetSystemMetrics(SM_CYSIZEFRAME
);
1999 backoundPart
= TBP_SIZINGBARRIGHT
;
2000 rect
.right
= rect
.left
+ GetSystemMetrics(SM_CXSIZEFRAME
);
2004 backoundPart
= TBP_SIZINGBARBOTTOM
;
2005 rect
.bottom
= rect
.top
+ GetSystemMetrics(SM_CYSIZEFRAME
);
2009 DrawThemeBackground(This
->TaskbarTheme
, hdc
, backoundPart
, 0, &rect
, 0);
2011 ReleaseDC(This
->hWnd
, hdc
);
2016 RunFileDlgThread(IN OUT PVOID pParam
)
2018 ITrayWindowImpl
*This
= pParam
;
2020 RUNFILEDLG RunFileDlg
;
2024 GetWindowRect(This
->hwndStart
,&posRect
);
2026 hwnd
= CreateWindowEx(0,
2029 WS_OVERLAPPED
| WS_DISABLED
| WS_CLIPSIBLINGS
| WS_BORDER
| SS_LEFT
,
2032 posRect
.right
- posRect
.left
,
2033 posRect
.bottom
- posRect
.top
,
2039 This
->hwndRunFileDlgOwner
= hwnd
;
2041 hShell32
= GetModuleHandle(TEXT("SHELL32.DLL"));
2042 RunFileDlg
= (RUNFILEDLG
)GetProcAddress(hShell32
, (LPCSTR
)61);
2044 RunFileDlg(hwnd
, NULL
, NULL
, NULL
, NULL
, RFF_CALCDIRECTORY
);
2046 This
->hwndRunFileDlgOwner
= NULL
;
2047 DestroyWindow(hwnd
);
2053 ITrayWindowImpl_DisplayRunFileDlg(IN ITrayWindowImpl
*This
)
2056 if (This
->hwndRunFileDlgOwner
)
2058 hRunDlg
= GetLastActivePopup(This
->hwndRunFileDlgOwner
);
2059 if (hRunDlg
!= NULL
&&
2060 hRunDlg
!= This
->hwndRunFileDlgOwner
)
2062 SetForegroundWindow(hRunDlg
);
2067 CloseHandle(CreateThread(NULL
, 0, RunFileDlgThread
, This
, 0, NULL
));
2070 static LRESULT CALLBACK
2071 TrayWndProc(IN HWND hwnd
,
2076 ITrayWindowImpl
*This
= NULL
;
2077 LRESULT Ret
= FALSE
;
2079 if (uMsg
!= WM_NCCREATE
)
2081 This
= (ITrayWindowImpl
*)GetWindowLongPtr(hwnd
,
2085 if (This
!= NULL
|| uMsg
== WM_NCCREATE
)
2087 if (This
!= NULL
&& This
->StartMenuBand
!= NULL
)
2094 Msg
.wParam
= wParam
;
2095 Msg
.lParam
= lParam
;
2097 if (IMenuBand_TranslateMenuMessage(This
->StartMenuBand
,
2104 wParam
= Msg
.wParam
;
2105 lParam
= Msg
.lParam
;
2112 if (This
->hwndTrayNotify
)
2114 TrayNotify_NotifyMsg(This
->hwndTrayNotify
,
2120 case WM_THEMECHANGED
:
2121 ITrayWindowImpl_UpdateTheme(This
);
2124 if (!This
->TaskbarTheme
)
2126 return ITrayWindowImpl_DrawSizer(This
,
2129 if (!This
->TaskbarTheme
)
2131 return ITrayWindowImpl_DrawBackground(This
,
2133 case WM_CTLCOLORBTN
:
2134 SetBkMode((HDC
)wParam
, TRANSPARENT
);
2135 return (LRESULT
)GetStockObject(HOLLOW_BRUSH
);
2143 /* The user may not be able to resize the tray window.
2144 Pretend like the window is not sizeable when the user
2145 clicks on the border. */
2149 SetLastError(ERROR_SUCCESS
);
2150 if (GetClientRect(hwnd
,
2152 (MapWindowPoints(hwnd
,
2155 2) != 0 || GetLastError() == ERROR_SUCCESS
))
2157 pt
.x
= (SHORT
)LOWORD(lParam
);
2158 pt
.y
= (SHORT
)HIWORD(lParam
);
2160 if (PtInRect(&rcClient
,
2163 /* The user is trying to drag the tray window */
2167 /* Depending on the position of the tray window, allow only
2168 changing the border next to the monitor working area */
2169 switch (This
->Position
)
2172 if (pt
.y
> rcClient
.bottom
)
2176 if (pt
.x
> rcClient
.right
)
2180 if (pt
.x
< rcClient
.left
)
2185 if (pt
.y
< rcClient
.top
)
2195 PRECT pRect
= (PRECT
)lParam
;
2197 /* We need to ensure that an application can not accidently
2198 move the tray window (using SetWindowPos). However, we still
2199 need to be able to move the window in case the user wants to
2200 drag the tray window to another position or in case the user
2201 wants to resize the tray window. */
2202 if (!This
->Locked
&& GetCursorPos(&ptCursor
))
2204 This
->IsDragging
= TRUE
;
2205 This
->DraggingPosition
= ITrayWindowImpl_GetDraggingRectFromPt(This
,
2208 &This
->DraggingMonitor
);
2212 *pRect
= This
->rcTrayWnd
[This
->Position
];
2219 PRECT pRect
= (PRECT
)lParam
;
2223 ITrayWindowImpl_CalculateValidSize(This
,
2229 *pRect
= This
->rcTrayWnd
[This
->Position
];
2234 case WM_WINDOWPOSCHANGING
:
2236 ITrayWindowImpl_ChangingWinPos(This
,
2237 (LPWINDOWPOS
)lParam
);
2244 InvalidateRect(This
->hWnd
, NULL
, TRUE
);
2245 if (wParam
== SIZE_RESTORED
&& lParam
== 0)
2247 ITrayWindowImpl_ResizeWorkArea(This
);
2248 /* Clip the tray window on multi monitor systems so the edges can't
2249 overlap into another monitor */
2250 ITrayWindowImpl_ApplyClipping(This
,
2253 if (!GetClientRect(This
->hWnd
,
2261 rcClient
.left
= rcClient
.top
= 0;
2262 rcClient
.right
= LOWORD(lParam
);
2263 rcClient
.bottom
= HIWORD(lParam
);
2266 ITrayWindowImpl_AlignControls(This
,
2271 case WM_ENTERSIZEMOVE
:
2272 This
->InSizeMove
= TRUE
;
2273 This
->IsDragging
= FALSE
;
2276 /* Remove the clipping on multi monitor systems while dragging around */
2277 ITrayWindowImpl_ApplyClipping(This
,
2282 case WM_EXITSIZEMOVE
:
2283 This
->InSizeMove
= FALSE
;
2286 /* Apply clipping */
2287 PostMessage(This
->hWnd
,
2299 /* The user pressed Alt+Space, this usually brings up the system menu of a window.
2300 The tray window needs to handle this specially, since it normally doesn't have
2303 static const UINT uidDisableItem
[] = {
2314 /* temporarily enable the system menu */
2315 SetWindowStyle(hwnd
,
2319 hSysMenu
= GetSystemMenu(hwnd
,
2321 if (hSysMenu
!= NULL
)
2323 /* Disable all items that are not relevant */
2324 for (i
= 0; i
!= sizeof(uidDisableItem
) / sizeof(uidDisableItem
[0]); i
++)
2326 EnableMenuItem(hSysMenu
,
2328 MF_BYCOMMAND
| MF_GRAYED
);
2331 EnableMenuItem(hSysMenu
,
2334 (SHRestricted(REST_NOCLOSE
) ? MF_GRAYED
: MF_ENABLED
));
2336 /* Display the system menu */
2337 uId
= ITrayWindowImpl_TrackMenu(This
,
2341 This
->Position
!= ABE_TOP
,
2345 SendMessage(This
->hWnd
,
2352 /* revert the system menu window style */
2353 SetWindowStyle(hwnd
,
2364 case WM_NCRBUTTONUP
:
2365 /* We want the user to be able to get a context menu even on the nonclient
2366 area (including the sizing border)! */
2367 uMsg
= WM_CONTEXTMENU
;
2368 wParam
= (WPARAM
)hwnd
;
2371 case WM_CONTEXTMENU
:
2373 POINT pt
, *ppt
= NULL
;
2374 HWND hWndExclude
= NULL
;
2376 /* Check if the administrator has forbidden access to context menus */
2377 if (SHRestricted(REST_NOTRAYCONTEXTMENU
))
2380 pt
.x
= (SHORT
)LOWORD(lParam
);
2381 pt
.y
= (SHORT
)HIWORD(lParam
);
2383 if (pt
.x
!= -1 || pt
.y
!= -1)
2386 hWndExclude
= This
->hwndStart
;
2388 if ((HWND
)wParam
== This
->hwndStart
)
2390 /* Make sure we can't track the context menu if the start
2391 menu is currently being shown */
2392 if (!(SendMessage(This
->hwndStart
,
2397 ITrayWindowImpl_TrackCtxMenu(This
,
2398 &StartMenuBtnCtxMenu
,
2401 This
->Position
== ABE_BOTTOM
,
2407 /* See if the context menu should be handled by the task band site */
2408 if (ppt
!= NULL
&& This
->TrayBandSite
!= NULL
)
2411 POINT ptClient
= *ppt
;
2413 /* Convert the coordinates to client-coordinates */
2414 MapWindowPoints(NULL
,
2419 hWndAtPt
= ChildWindowFromPoint(This
->hWnd
,
2421 if (hWndAtPt
!= NULL
&&
2422 (hWndAtPt
== This
->hwndRebar
|| IsChild(This
->hwndRebar
,
2425 /* Check if the user clicked on the task switch window */
2427 MapWindowPoints(NULL
,
2432 hWndAtPt
= ChildWindowFromPointEx(This
->hwndRebar
,
2434 CWP_SKIPINVISIBLE
| CWP_SKIPDISABLED
);
2435 if (hWndAtPt
== This
->hwndTaskSwitch
)
2436 goto HandleTrayContextMenu
;
2438 /* Forward the message to the task band site */
2439 ITrayBandSite_ProcessMessage(This
->TrayBandSite
,
2447 goto HandleTrayContextMenu
;
2451 HandleTrayContextMenu
:
2452 /* Tray the default tray window context menu */
2453 ITrayWindowImpl_TrackCtxMenu(This
,
2466 /* FIXME: We can't check with IsChild whether the hwnd is somewhere inside
2467 the rebar control! But we shouldn't forward messages that the band
2468 site doesn't handle, such as other controls (start button, tray window */
2469 if (This
->TrayBandSite
== NULL
||
2470 !SUCCEEDED(ITrayBandSite_ProcessMessage(This
->TrayBandSite
,
2477 const NMHDR
*nmh
= (const NMHDR
*)lParam
;
2479 if (nmh
->hwndFrom
== This
->hwndTrayNotify
)
2484 /* Cause all controls to be aligned */
2485 PostMessage(This
->hWnd
,
2496 case WM_NCLBUTTONDBLCLK
:
2497 /* We "handle" this message so users can't cause a weird maximize/restore
2498 window animation when double-clicking the tray window! */
2503 LPCREATESTRUCT CreateStruct
= (LPCREATESTRUCT
)lParam
;
2504 This
= (ITrayWindowImpl
*)CreateStruct
->lpCreateParams
;
2506 if (InterlockedCompareExchangePointer((PVOID
*)&This
->hWnd
,
2510 /* Somebody else was faster... */
2514 SetWindowLongPtr(hwnd
,
2518 return ITrayWindowImpl_NCCreate(This
);
2522 ITrayWindowImpl_Create(This
);
2526 ITrayWindowImpl_Destroy(This
);
2529 case WM_APP_TRAYDESTROY
:
2530 DestroyWindow(hwnd
);
2533 case TWM_OPENSTARTMENU
:
2534 SendMessage(This
->hWnd
, WM_COMMAND
, MAKEWPARAM(BN_CLICKED
, IDC_STARTBTN
), (LPARAM
)This
->hwndStart
);
2538 if ((HWND
)lParam
== This
->hwndStart
)
2540 if (This
->StartMenuPopup
!= NULL
)
2546 if (GetWindowRect(This
->hwndStart
,
2549 if (ITrayWindowImpl_IsPosHorizontal(This
))
2551 pt
.x
= rcExclude
.left
;
2552 pt
.y
= rcExclude
.top
;
2553 dwFlags
|= MPPF_BOTTOM
;
2557 if (This
->Position
== ABE_LEFT
)
2558 pt
.x
= rcExclude
.left
;
2560 pt
.x
= rcExclude
.right
;
2562 pt
.y
= rcExclude
.bottom
;
2563 dwFlags
|= MPPF_BOTTOM
;
2566 IMenuPopup_Popup(This
->StartMenuPopup
,
2575 if (This
->TrayBandSite
== NULL
||
2576 !SUCCEEDED(ITrayBandSite_ProcessMessage(This
->TrayBandSite
,
2583 switch (LOWORD(wParam
))
2585 /* FIXME: Handle these commands as well */
2586 case IDM_TASKBARANDSTARTMENU
:
2588 case IDM_HELPANDSUPPORT
:
2593 ITrayWindowImpl_DisplayRunFileDlg(This
);
2597 /* FIXME: Handle these commands as well */
2598 case IDM_SYNCHRONIZE
:
2600 case IDM_DISCONNECT
:
2601 case IDM_UNDOCKCOMPUTER
:
2607 EXITWINDLG ExitWinDlg
;
2609 hShell32
= GetModuleHandle(TEXT("SHELL32.DLL"));
2610 ExitWinDlg
= (EXITWINDLG
)GetProcAddress(hShell32
, (LPCSTR
)60);
2626 Ret
= DefWindowProc(hwnd
,
2636 * Tray Window Context Menu
2640 CreateTrayWindowContextMenu(IN HWND hWndOwner
,
2641 IN PVOID
*ppcmContext
,
2642 IN PVOID Context OPTIONAL
)
2644 ITrayWindowImpl
*This
= (ITrayWindowImpl
*)Context
;
2645 IContextMenu
*pcm
= NULL
;
2648 hPopup
= LoadPopupMenu(hExplorerInstance
,
2649 MAKEINTRESOURCE(IDM_TRAYWND
));
2653 if (SHRestricted(REST_CLASSICSHELL
) != 0)
2660 CheckMenuItem(hPopup
,
2662 MF_BYCOMMAND
| (This
->Locked
? MF_CHECKED
: MF_UNCHECKED
));
2664 if (This
->TrayBandSite
!= NULL
)
2666 if (SUCCEEDED(ITrayBandSite_AddContextMenus(This
->TrayBandSite
,
2674 DbgPrint("ITrayBandSite::AddContextMenus succeeded!\n");
2675 *(IContextMenu
**)ppcmContext
= pcm
;
2684 OnTrayWindowContextMenuCommand(IN HWND hWndOwner
,
2686 IN PVOID pcmContext OPTIONAL
,
2687 IN PVOID Context OPTIONAL
)
2689 ITrayWindowImpl
*This
= (ITrayWindowImpl
*)Context
;
2690 IContextMenu
*pcm
= (IContextMenu
*)pcmContext
;
2694 if (uiCmdId
>= ID_SHELL_CMD_FIRST
&& uiCmdId
<= ID_SHELL_CMD_LAST
)
2696 CMINVOKECOMMANDINFO cmici
= {0};
2700 /* Setup and invoke the shell command */
2701 cmici
.cbSize
= sizeof(cmici
);
2702 cmici
.hwnd
= hWndOwner
;
2703 cmici
.lpVerb
= (LPCSTR
)MAKEINTRESOURCE(uiCmdId
- ID_SHELL_CMD_FIRST
);
2704 cmici
.nShow
= SW_NORMAL
;
2706 IContextMenu_InvokeCommand(pcm
,
2712 ITrayWindow_ExecContextMenuCmd(ITrayWindow_from_impl(This
),
2718 IContextMenu_Release(pcm
);
2721 static const TRAYWINDOW_CTXMENU TrayWindowCtxMenu
= {
2722 CreateTrayWindowContextMenu
,
2723 OnTrayWindowContextMenuCommand
2726 /*****************************************************************************/
2729 RegisterTrayWindowClass(VOID
)
2734 if (!RegisterTrayNotifyWndClass())
2737 wcTrayWnd
.style
= CS_DBLCLKS
;
2738 wcTrayWnd
.lpfnWndProc
= TrayWndProc
;
2739 wcTrayWnd
.cbClsExtra
= 0;
2740 wcTrayWnd
.cbWndExtra
= sizeof(ITrayWindowImpl
*);
2741 wcTrayWnd
.hInstance
= hExplorerInstance
;
2742 wcTrayWnd
.hIcon
= NULL
;
2743 wcTrayWnd
.hCursor
= LoadCursor(NULL
,
2745 wcTrayWnd
.hbrBackground
= (HBRUSH
)(COLOR_3DFACE
+ 1);
2746 wcTrayWnd
.lpszMenuName
= NULL
;
2747 wcTrayWnd
.lpszClassName
= szTrayWndClass
;
2749 Ret
= RegisterClass(&wcTrayWnd
) != 0;
2752 UnregisterTrayNotifyWndClass();
2758 UnregisterTrayWindowClass(VOID
)
2760 UnregisterTrayNotifyWndClass();
2762 UnregisterClass(szTrayWndClass
,
2767 CreateTrayWindow(VOID
)
2769 ITrayWindowImpl
*This
;
2770 ITrayWindow
*TrayWindow
;
2772 This
= ITrayWindowImpl_Construct();
2775 TrayWindow
= ITrayWindow_from_impl(This
);
2777 ITrayWindowImpl_Open(TrayWindow
);
2786 TrayProcessMessages(IN OUT ITrayWindow
*Tray
)
2788 ITrayWindowImpl
*This
;
2791 This
= impl_from_ITrayWindow(Tray
);
2793 /* FIXME: We should keep a reference here... */
2795 while (PeekMessage(&Msg
,
2801 if (Msg
.message
== WM_QUIT
)
2804 if (This
->StartMenuBand
== NULL
||
2805 IMenuBand_IsMenuMessage(This
->StartMenuBand
,
2808 TranslateMessage(&Msg
);
2809 DispatchMessage(&Msg
);
2815 TrayMessageLoop(IN OUT ITrayWindow
*Tray
)
2817 ITrayWindowImpl
*This
;
2821 This
= impl_from_ITrayWindow(Tray
);
2823 /* FIXME: We should keep a reference here... */
2827 Ret
= GetMessage(&Msg
,
2832 if (!Ret
|| Ret
== -1)
2835 if (Msg
.message
== WM_HOTKEY
)
2839 case IDHK_RUN
: /* Win+R */
2840 ITrayWindowImpl_DisplayRunFileDlg(This
);
2845 if (This
->StartMenuBand
== NULL
||
2846 IMenuBand_IsMenuMessage(This
->StartMenuBand
,
2849 TranslateMessage(&Msg
);
2850 DispatchMessage(&Msg
);
2858 * NOTE: This is a very windows-specific COM interface used by SHCreateDesktop()!
2859 * These are the calls I observed, it may be wrong/incomplete/buggy!!!
2860 * The reason we implement it is because we have to use SHCreateDesktop() so
2861 * that the shell provides the desktop window and all the features that come
2862 * with it (especially positioning of desktop icons)
2865 static HRESULT STDMETHODCALLTYPE
2866 ITrayWindowImpl_IShellDesktopTray_QueryInterface(IN OUT IShellDesktopTray
*iface
,
2870 ITrayWindowImpl
*This
= impl_from_IShellDesktopTray(iface
);
2871 ITrayWindow
*tray
= ITrayWindow_from_impl(This
);
2873 DbgPrint("IShellDesktopTray::QueryInterface(0x%p, 0x%p)\n", riid
, ppvObj
);
2874 return ITrayWindowImpl_QueryInterface(tray
,
2879 static ULONG STDMETHODCALLTYPE
2880 ITrayWindowImpl_IShellDesktopTray_Release(IN OUT IShellDesktopTray
*iface
)
2882 ITrayWindowImpl
*This
= impl_from_IShellDesktopTray(iface
);
2883 ITrayWindow
*tray
= ITrayWindow_from_impl(This
);
2885 DbgPrint("IShellDesktopTray::Release()\n");
2886 return ITrayWindowImpl_Release(tray
);
2889 static ULONG STDMETHODCALLTYPE
2890 ITrayWindowImpl_IShellDesktopTray_AddRef(IN OUT IShellDesktopTray
*iface
)
2892 ITrayWindowImpl
*This
= impl_from_IShellDesktopTray(iface
);
2893 ITrayWindow
*tray
= ITrayWindow_from_impl(This
);
2895 DbgPrint("IShellDesktopTray::AddRef()\n");
2896 return ITrayWindowImpl_AddRef(tray
);
2899 static ULONG STDMETHODCALLTYPE
2900 ITrayWindowImpl_IShellDesktopTray_GetState(IN OUT IShellDesktopTray
*iface
)
2902 /* FIXME: Return ABS_ flags? */
2903 DbgPrint("IShellDesktopTray::GetState() unimplemented!\n");
2907 static HRESULT STDMETHODCALLTYPE
2908 ITrayWindowImpl_IShellDesktopTray_GetTrayWindow(IN OUT IShellDesktopTray
*iface
,
2909 OUT HWND
*phWndTray
)
2911 ITrayWindowImpl
*This
= impl_from_IShellDesktopTray(iface
);
2912 DbgPrint("IShellDesktopTray::GetTrayWindow(0x%p)\n", phWndTray
);
2913 *phWndTray
= This
->hWnd
;
2917 static HRESULT STDMETHODCALLTYPE
2918 ITrayWindowImpl_IShellDesktopTray_RegisterDesktopWindow(IN OUT IShellDesktopTray
*iface
,
2919 IN HWND hWndDesktop
)
2921 ITrayWindowImpl
*This
= impl_from_IShellDesktopTray(iface
);
2922 DbgPrint("IShellDesktopTray::RegisterDesktopWindow(0x%p)\n", hWndDesktop
);
2924 This
->hWndDesktop
= hWndDesktop
;
2928 static HRESULT STDMETHODCALLTYPE
2929 ITrayWindowImpl_IShellDesktopTray_Unknown(IN OUT IShellDesktopTray
*iface
,
2930 IN DWORD dwUnknown1
,
2931 IN DWORD dwUnknown2
)
2933 DbgPrint("IShellDesktopTray::Unknown(%u,%u) unimplemented!\n", dwUnknown1
, dwUnknown2
);
2937 static const IShellDesktopTrayVtbl IShellDesktopTrayImpl_Vtbl
=
2940 ITrayWindowImpl_IShellDesktopTray_QueryInterface
,
2941 ITrayWindowImpl_IShellDesktopTray_AddRef
,
2942 ITrayWindowImpl_IShellDesktopTray_Release
,
2943 /*** IShellDesktopTray ***/
2944 ITrayWindowImpl_IShellDesktopTray_GetState
,
2945 ITrayWindowImpl_IShellDesktopTray_GetTrayWindow
,
2946 ITrayWindowImpl_IShellDesktopTray_RegisterDesktopWindow
,
2947 ITrayWindowImpl_IShellDesktopTray_Unknown