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
];
103 StringCbCopy(szParams
, sizeof(szParams
),
104 TEXT("shell32.dll,Control_RunDLL "));
105 if (FAILED(StringCbCat(szParams
, sizeof(szParams
),
109 return (ShellExecute(hwnd
, TEXT("open"), TEXT("rundll32.exe"), szParams
, NULL
, SW_SHOWDEFAULT
) > (HINSTANCE
)32);
113 IUnknown_from_impl(ITrayWindowImpl
*This
)
115 return (IUnknown
*)&This
->lpVtbl
;
119 ITrayWindow_from_impl(ITrayWindowImpl
*This
)
121 return (ITrayWindow
*)&This
->lpVtbl
;
124 static IShellDesktopTray
*
125 IShellDesktopTray_from_impl(ITrayWindowImpl
*This
)
127 return (IShellDesktopTray
*)&This
->lpVtblShellDesktopTray
;
130 static ITrayWindowImpl
*
131 impl_from_ITrayWindow(ITrayWindow
*iface
)
133 return (ITrayWindowImpl
*)((ULONG_PTR
)iface
- FIELD_OFFSET(ITrayWindowImpl
,
137 static ITrayWindowImpl
*
138 impl_from_IShellDesktopTray(IShellDesktopTray
*iface
)
140 return (ITrayWindowImpl
*)((ULONG_PTR
)iface
- FIELD_OFFSET(ITrayWindowImpl
,
141 lpVtblShellDesktopTray
));
149 ITrayWindowImpl_UpdateNonClientMetrics(IN OUT ITrayWindowImpl
*This
)
151 This
->ncm
.cbSize
= sizeof(This
->ncm
);
152 if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS
,
157 if (This
->hFont
!= NULL
)
158 DeleteObject(This
->hFont
);
160 This
->hFont
= CreateFontIndirect(&This
->ncm
.lfMessageFont
);
168 ITrayWindowImpl_SetWindowsFont(IN OUT ITrayWindowImpl
*This
)
170 if (This
->hwndTrayNotify
!= NULL
)
172 SendMessage(This
->hwndTrayNotify
,
180 ITrayWindowImpl_GetScreenRectFromRect(IN OUT ITrayWindowImpl
*This
,
187 mi
.cbSize
= sizeof(mi
);
188 hMon
= MonitorFromRect(pRect
,
194 *pRect
= mi
.rcMonitor
;
200 pRect
->right
= GetSystemMetrics(SM_CXSCREEN
);
201 pRect
->bottom
= GetSystemMetrics(SM_CYSCREEN
);
210 ITrayWindowImpl_GetMonitorFromRect(IN OUT ITrayWindowImpl
*This
,
211 IN
const RECT
*pRect
)
215 /* In case the monitor sizes or saved sizes differ a bit (probably
216 not a lot, only so the tray window overlaps into another monitor
217 now), minimize the risk that we determine a wrong monitor by
218 using the center point of the tray window if we can't determine
219 it using the rectangle. */
220 hMon
= MonitorFromRect(pRect
,
221 MONITOR_DEFAULTTONULL
);
226 pt
.x
= pRect
->left
+ ((pRect
->right
- pRect
->left
) / 2);
227 pt
.y
= pRect
->top
+ ((pRect
->bottom
- pRect
->top
) / 2);
229 /* be less error-prone, find the nearest monitor */
230 hMon
= MonitorFromPoint(pt
,
231 MONITOR_DEFAULTTONEAREST
);
238 ITrayWindowImpl_GetScreenRect(IN OUT ITrayWindowImpl
*This
,
239 IN HMONITOR hMonitor
,
242 HMONITOR hMon
= NULL
;
244 if (hMonitor
!= NULL
)
248 mi
.cbSize
= sizeof(mi
);
249 if (!GetMonitorInfo(hMonitor
,
252 /* Hm, the monitor is gone? Try to find a monitor where it
253 could be located now */
254 hMon
= ITrayWindowImpl_GetMonitorFromRect(This
,
257 !GetMonitorInfo(hMon
,
265 *pRect
= mi
.rcMonitor
;
272 pRect
->right
= GetSystemMetrics(SM_CXSCREEN
);
273 pRect
->bottom
= GetSystemMetrics(SM_CYSCREEN
);
280 ITrayWindowImpl_MakeTrayRectWithSize(IN DWORD Position
,
281 IN
const SIZE
*pTraySize
,
287 pRect
->right
= pRect
->left
+ pTraySize
->cx
;
291 pRect
->bottom
= pRect
->top
+ pTraySize
->cy
;
295 pRect
->left
= pRect
->right
- pTraySize
->cx
;
300 pRect
->top
= pRect
->bottom
- pTraySize
->cy
;
306 ITrayWindowImpl_GetTrayRectFromScreenRect(IN OUT ITrayWindowImpl
*This
,
308 IN
const RECT
*pScreen
,
309 IN
const SIZE
*pTraySize OPTIONAL
,
312 if (pTraySize
== NULL
)
313 pTraySize
= &This
->TraySize
;
317 /* Move the border outside of the screen */
319 GetSystemMetrics(SM_CXEDGE
),
320 GetSystemMetrics(SM_CYEDGE
));
322 ITrayWindowImpl_MakeTrayRectWithSize(Position
,
328 ITrayWindowImpl_IsPosHorizontal(IN OUT ITrayWindowImpl
*This
)
330 return This
->Position
== ABE_TOP
|| This
->Position
== ABE_BOTTOM
;
334 ITrayWindowImpl_CalculateValidSize(IN OUT ITrayWindowImpl
*This
,
343 //Horizontal = ITrayWindowImpl_IsPosHorizontal(This);
345 szWnd
.cx
= pRect
->right
- pRect
->left
;
346 szWnd
.cy
= pRect
->bottom
- pRect
->top
;
349 hMon
= ITrayWindowImpl_GetScreenRectFromRect(This
,
351 MONITOR_DEFAULTTONEAREST
);
353 /* Calculate the maximum size of the tray window and limit the window
354 size to half of the screen's size. */
355 szMax
.cx
= (rcScreen
.right
- rcScreen
.left
) / 2;
356 szMax
.cy
= (rcScreen
.bottom
- rcScreen
.top
) / 2;
357 if (szWnd
.cx
> szMax
.cx
)
359 if (szWnd
.cy
> szMax
.cy
)
362 /* FIXME - calculate */
364 ITrayWindowImpl_GetTrayRectFromScreenRect(This
,
375 ITrayWindowImpl_GetMinimumWindowSize(IN OUT ITrayWindowImpl
*This
,
380 AdjustWindowRectEx(&rcMin
,
381 GetWindowLong(This
->hWnd
,
384 GetWindowLong(This
->hWnd
,
393 ITrayWindowImpl_GetDraggingRectFromPt(IN OUT ITrayWindowImpl
*This
,
396 OUT HMONITOR
*phMonitor
)
398 HMONITOR hMon
, hMonNew
;
399 DWORD PosH
, PosV
, Pos
;
400 SIZE DeltaPt
, ScreenOffset
;
406 /* Determine the screen rectangle */
407 hMon
= MonitorFromPoint(pt
,
408 MONITOR_DEFAULTTONULL
);
414 mi
.cbSize
= sizeof(mi
);
415 if (!GetMonitorInfo(hMon
,
419 goto GetPrimaryScreenRect
;
422 /* make left top corner of the screen zero based to
423 make calculations easier */
424 pt
.x
-= mi
.rcMonitor
.left
;
425 pt
.y
-= mi
.rcMonitor
.top
;
427 ScreenOffset
.cx
= mi
.rcMonitor
.left
;
428 ScreenOffset
.cy
= mi
.rcMonitor
.top
;
429 rcScreen
.right
= mi
.rcMonitor
.right
- mi
.rcMonitor
.left
;
430 rcScreen
.bottom
= mi
.rcMonitor
.bottom
- mi
.rcMonitor
.top
;
434 GetPrimaryScreenRect
:
437 rcScreen
.right
= GetSystemMetrics(SM_CXSCREEN
);
438 rcScreen
.bottom
= GetSystemMetrics(SM_CYSCREEN
);
441 /* Calculate the nearest screen border */
442 if (pt
.x
< rcScreen
.right
/ 2)
449 DeltaPt
.cx
= rcScreen
.right
- pt
.x
;
453 if (pt
.y
< rcScreen
.bottom
/ 2)
460 DeltaPt
.cy
= rcScreen
.bottom
- pt
.y
;
464 Pos
= (DeltaPt
.cx
* rcScreen
.bottom
< DeltaPt
.cy
* rcScreen
.right
) ? PosH
: PosV
;
466 /* Fix the screen origin to be relative to the primary monitor again */
467 OffsetRect(&rcScreen
,
471 hMonNew
= ITrayWindowImpl_GetMonitorFromRect(This
,
472 &This
->rcTrayWnd
[Pos
]);
477 /* Recalculate the rectangle, we're dragging to another monitor.
478 We don't need to recalculate the rect on single monitor systems. */
479 szTray
.cx
= This
->rcTrayWnd
[Pos
].right
- This
->rcTrayWnd
[Pos
].left
;
480 szTray
.cy
= This
->rcTrayWnd
[Pos
].bottom
- This
->rcTrayWnd
[Pos
].top
;
482 ITrayWindowImpl_GetTrayRectFromScreenRect(This
,
492 /* The user is dragging the tray window on the same monitor. We don't need
493 to recalculate the rectangle */
494 *pRect
= This
->rcTrayWnd
[Pos
];
503 ITrayWindowImpl_GetDraggingRectFromRect(IN OUT ITrayWindowImpl
*This
,
505 OUT HMONITOR
*phMonitor
)
509 /* Calculate the center of the rectangle. We call
510 ITrayWindowImpl_GetDraggingRectFromPt to calculate a valid
511 dragging rectangle */
512 pt
.x
= pRect
->left
+ ((pRect
->right
- pRect
->left
) / 2);
513 pt
.y
= pRect
->top
+ ((pRect
->bottom
- pRect
->top
) / 2);
515 return ITrayWindowImpl_GetDraggingRectFromPt(This
,
522 ITrayWindowImpl_ChangingWinPos(IN OUT ITrayWindowImpl
*This
,
523 IN OUT LPWINDOWPOS pwp
)
527 if (This
->IsDragging
)
529 rcTray
.left
= pwp
->x
;
531 rcTray
.right
= rcTray
.left
+ pwp
->cx
;
532 rcTray
.bottom
= rcTray
.top
+ pwp
->cy
;
534 if (!EqualRect(&rcTray
,
535 &This
->rcTrayWnd
[This
->DraggingPosition
]))
537 /* Recalculate the rectangle, the user dragged the tray
538 window to another monitor or the window was somehow else
540 This
->DraggingPosition
= ITrayWindowImpl_GetDraggingRectFromRect(This
,
542 &This
->DraggingMonitor
);
543 //This->rcTrayWnd[This->DraggingPosition] = rcTray;
546 //This->Monitor = ITrayWindowImpl_CalculateValidSize(This,
547 // This->DraggingPosition,
550 This
->Monitor
= This
->DraggingMonitor
;
551 This
->Position
= This
->DraggingPosition
;
552 This
->IsDragging
= FALSE
;
554 This
->rcTrayWnd
[This
->Position
] = rcTray
;
557 else if (GetWindowRect(This
->hWnd
,
560 if (This
->InSizeMove
)
562 if (!(pwp
->flags
& SWP_NOMOVE
))
564 rcTray
.left
= pwp
->x
;
568 if (!(pwp
->flags
& SWP_NOSIZE
))
570 rcTray
.right
= rcTray
.left
+ pwp
->cx
;
571 rcTray
.bottom
= rcTray
.top
+ pwp
->cy
;
574 This
->Position
= ITrayWindowImpl_GetDraggingRectFromRect(This
,
578 if (!(pwp
->flags
& (SWP_NOMOVE
| SWP_NOSIZE
)))
585 ITrayWindowImpl_MakeTrayRectWithSize(This
->Position
,
590 This
->rcTrayWnd
[This
->Position
] = rcTray
;
594 /* If the user isn't resizing the tray window we need to make sure the
595 new size or position is valid. This is to prevent changes to the window
596 without user interaction. */
597 rcTray
= This
->rcTrayWnd
[This
->Position
];
601 This
->TraySize
.cx
= rcTray
.right
- rcTray
.left
;
602 This
->TraySize
.cy
= rcTray
.bottom
- rcTray
.top
;
604 pwp
->flags
&= ~(SWP_NOMOVE
| SWP_NOSIZE
);
605 pwp
->x
= rcTray
.left
;
607 pwp
->cx
= This
->TraySize
.cx
;
608 pwp
->cy
= This
->TraySize
.cy
;
613 ITrayWindowImpl_ApplyClipping(IN OUT ITrayWindowImpl
*This
,
616 RECT rcClip
, rcWindow
;
619 if (GetWindowRect(This
->hWnd
,
622 /* Disable clipping on systems with only one monitor */
623 if (GetSystemMetrics(SM_CMONITORS
) <= 1)
630 ITrayWindowImpl_GetScreenRect(This
,
634 if (!IntersectRect(&rcClip
,
645 hClipRgn
= CreateRectRgnIndirect(&rcClip
);
650 /* Set the clipping region or make sure the window isn't clipped
651 by disabling it explicitly. */
652 SetWindowRgn(This
->hWnd
,
659 ITrayWindowImpl_ResizeWorkArea(IN OUT ITrayWindowImpl
*This
)
661 RECT rcTray
,rcWorkArea
;
663 /* If monitor has changed then fix the previous monitors work area */
664 if (This
->PreviousMonitor
!= This
->Monitor
)
666 ITrayWindowImpl_GetScreenRect(This
,
667 This
->PreviousMonitor
,
669 SystemParametersInfo(SPI_SETWORKAREA
,
675 rcTray
= This
->rcTrayWnd
[This
->Position
];
677 ITrayWindowImpl_GetScreenRect(This
,
680 This
->PreviousMonitor
= This
->Monitor
;
682 /* If AutoHide is false then change the workarea to exclude the area that
683 the taskbar covers. */
686 switch (This
->Position
)
689 rcWorkArea
.top
= rcTray
.bottom
;
692 rcWorkArea
.left
= rcTray
.right
;
695 rcWorkArea
.right
= rcTray
.left
;
698 rcWorkArea
.bottom
= rcTray
.top
;
703 SystemParametersInfo(SPI_SETWORKAREA
,
710 ITrayWindowImpl_CheckTrayWndPosition(IN OUT ITrayWindowImpl
*This
)
714 rcTray
= This
->rcTrayWnd
[This
->Position
];
715 // DbgPrint("CheckTray: %d: %d,%d,%d,%d\n", This->Position, rcTray.left, rcTray.top, rcTray.right, rcTray.bottom);
717 /* Move the tray window */
718 SetWindowPos(This
->hWnd
,
722 rcTray
.right
- rcTray
.left
,
723 rcTray
.bottom
- rcTray
.top
,
726 ITrayWindowImpl_ResizeWorkArea(This
);
728 ITrayWindowImpl_ApplyClipping(This
,
732 typedef struct _TW_STUCKRECTS2
740 } TW_STRUCKRECTS2
, *PTW_STUCKRECTS2
;
743 ITrayWindowImpl_RegLoadSettings(IN OUT ITrayWindowImpl
*This
)
748 SIZE WndSize
, EdgeSize
, DlgFrameSize
;
749 DWORD cbSize
= sizeof(sr
);
751 EdgeSize
.cx
= GetSystemMetrics(SM_CXEDGE
);
752 EdgeSize
.cy
= GetSystemMetrics(SM_CYEDGE
);
753 DlgFrameSize
.cx
= GetSystemMetrics(SM_CXDLGFRAME
);
754 DlgFrameSize
.cy
= GetSystemMetrics(SM_CYDLGFRAME
);
756 if (SHGetValue(hkExplorer
,
761 &cbSize
) == ERROR_SUCCESS
&&
762 sr
.cbSize
== sizeof(sr
))
764 This
->AutoHide
= (sr
.dwFlags
& ABS_AUTOHIDE
) != 0;
765 This
->AlwaysOnTop
= (sr
.dwFlags
& ABS_ALWAYSONTOP
) != 0;
766 This
->SmSmallIcons
= (sr
.dwFlags
& 0x4) != 0;
767 This
->HideClock
= (sr
.dwFlags
& 0x8) != 0;
769 /* FIXME: Are there more flags? */
771 if (This
->hWnd
!= NULL
)
772 SetWindowPos (This
->hWnd
,
773 This
->AlwaysOnTop
? HWND_TOPMOST
: HWND_NOTOPMOST
,
778 SWP_NOMOVE
| SWP_NOSIZE
);
780 if (sr
.Position
> ABE_BOTTOM
)
781 This
->Position
= ABE_BOTTOM
;
783 This
->Position
= sr
.Position
;
785 /* Try to find out which monitor the tray window was located on last.
786 Here we're only interested in the monitor screen that we think
787 is the last one used. We're going to determine on which monitor
788 we really are after calculating the docked position. */
790 ITrayWindowImpl_GetScreenRectFromRect(This
,
792 MONITOR_DEFAULTTONEAREST
);
796 This
->Position
= ABE_BOTTOM
;
798 /* Use the minimum size of the taskbar, we'll use the start
799 button as a minimum for now. Make sure we calculate the
800 entire window size, not just the client size. However, we
801 use a thinner border than a standard thick border, so that
802 the start button and bands are not stuck to the screen border. */
803 sr
.Size
.cx
= This
->StartBtnSize
.cx
+ (2 * (EdgeSize
.cx
+ DlgFrameSize
.cx
));
804 sr
.Size
.cy
= This
->StartBtnSize
.cy
+ (2 * (EdgeSize
.cy
+ DlgFrameSize
.cy
));
806 /* Use the primary screen by default */
809 rcScreen
.right
= GetSystemMetrics(SM_CXSCREEN
);
810 rcScreen
.bottom
= GetSystemMetrics(SM_CYSCREEN
);
811 ITrayWindowImpl_GetScreenRectFromRect(This
,
813 MONITOR_DEFAULTTOPRIMARY
);
816 /* Determine a minimum tray window rectangle. The "client" height is
817 zero here since we cannot determine an optimal minimum width when
818 loaded as a vertical tray window. We just need to make sure the values
819 loaded from the registry are at least. The windows explorer behaves
820 the same way, it allows the user to save a zero width vertical tray
821 window, but not a zero height horizontal tray window. */
822 WndSize
.cx
= 2 * (EdgeSize
.cx
+ DlgFrameSize
.cx
);
823 WndSize
.cy
= This
->StartBtnSize
.cy
+ (2 * (EdgeSize
.cy
+ DlgFrameSize
.cy
));
825 if (WndSize
.cx
< sr
.Size
.cx
)
826 WndSize
.cx
= sr
.Size
.cx
;
827 if (WndSize
.cy
< sr
.Size
.cy
)
828 WndSize
.cy
= sr
.Size
.cy
;
830 /* Save the calculated size */
831 This
->TraySize
= WndSize
;
833 /* Calculate all docking rectangles. We need to do this here so they're
834 initialized and dragging the tray window to another position gives
840 ITrayWindowImpl_GetTrayRectFromScreenRect(This
,
844 &This
->rcTrayWnd
[Pos
]);
845 // 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);
848 /* Determine which monitor we are on. It shouldn't matter which docked
849 position rectangle we use */
850 This
->Monitor
= ITrayWindowImpl_GetMonitorFromRect(This
,
851 &This
->rcTrayWnd
[ABE_LEFT
]);
855 ITrayWindowImpl_TrackMenu(IN OUT ITrayWindowImpl
*This
,
857 IN POINT
*ppt OPTIONAL
,
858 IN HWND hwndExclude OPTIONAL
,
860 IN BOOL IsContextMenu
)
862 TPMPARAMS tmp
, *ptmp
= NULL
;
867 if (hwndExclude
!= NULL
)
869 /* Get the client rectangle and map it to screen coordinates */
870 if (GetClientRect(hwndExclude
,
872 MapWindowPoints(hwndExclude
,
874 (LPPOINT
)&tmp
.rcExclude
,
884 GetClientRect(This
->hWnd
,
886 MapWindowPoints(This
->hWnd
,
888 (LPPOINT
)&tmp
.rcExclude
,
896 /* NOTE: TrackPopupMenuEx will eventually align the track position
897 for us, no need to take care of it here as long as the
898 coordinates are somewhere within the exclusion rectangle */
899 pt
.x
= ptmp
->rcExclude
.left
;
900 pt
.y
= ptmp
->rcExclude
.top
;
908 tmp
.cbSize
= sizeof(tmp
);
910 fuFlags
= TPM_RETURNCMD
| TPM_VERTICAL
;
911 fuFlags
|= (TrackUp
? TPM_BOTTOMALIGN
: TPM_TOPALIGN
);
913 fuFlags
|= TPM_RIGHTBUTTON
;
915 fuFlags
|= (TrackUp
? TPM_VERNEGANIMATION
: TPM_VERPOSANIMATION
);
917 cmdId
= TrackPopupMenuEx(hMenu
,
928 ITrayWindowImpl_TrackCtxMenu(IN OUT ITrayWindowImpl
*This
,
929 IN
const TRAYWINDOW_CTXMENU
*pMenu
,
930 IN POINT
*ppt OPTIONAL
,
931 IN HWND hwndExclude OPTIONAL
,
933 IN PVOID Context OPTIONAL
)
937 PVOID pcmContext
= NULL
;
939 hPopup
= pMenu
->CreateCtxMenu(This
->hWnd
,
944 cmdId
= ITrayWindowImpl_TrackMenu(This
,
951 pMenu
->CtxMenuCommand(This
->hWnd
,
961 ITrayWindowImpl_Free(ITrayWindowImpl
*This
)
963 HeapFree(hProcessHeap
,
969 static ULONG STDMETHODCALLTYPE
970 ITrayWindowImpl_Release(IN OUT ITrayWindow
*iface
)
972 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
975 Ret
= InterlockedDecrement(&This
->Ref
);
977 ITrayWindowImpl_Free(This
);
983 ITrayWindowImpl_Destroy(ITrayWindowImpl
*This
)
985 (void)InterlockedExchangePointer((PVOID
*)&This
->hWnd
,
988 if (This
->himlStartBtn
!= NULL
)
990 ImageList_Destroy(This
->himlStartBtn
);
991 This
->himlStartBtn
= NULL
;
994 if (This
->hCaptionFont
!= NULL
)
996 DeleteObject(This
->hCaptionFont
);
997 This
->hCaptionFont
= NULL
;
1000 if (This
->hStartBtnFont
!= NULL
)
1002 DeleteObject(This
->hStartBtnFont
);
1003 This
->hStartBtnFont
= NULL
;
1006 if (This
->hFont
!= NULL
)
1008 DeleteObject(This
->hFont
);
1012 if (This
->StartMenuPopup
!= NULL
)
1014 IMenuPopup_Release(This
->StartMenuPopup
);
1015 This
->StartMenuPopup
= NULL
;
1018 if (This
->hbmStartMenu
!= NULL
)
1020 DeleteObject(This
->hbmStartMenu
);
1021 This
->hbmStartMenu
= NULL
;
1024 if (This
->StartMenuBand
!= NULL
)
1026 IMenuBand_Release(This
->StartMenuBand
);
1027 This
->StartMenuBand
= NULL
;
1030 if (This
->TrayBandSite
!= NULL
)
1032 /* FIXME: Unload bands */
1033 ITrayBandSite_Release(This
->TrayBandSite
);
1034 This
->TrayBandSite
= NULL
;
1037 if (This
->TaskbarTheme
)
1039 CloseThemeData(This
->TaskbarTheme
);
1040 This
->TaskbarTheme
= NULL
;
1043 ITrayWindowImpl_Release(ITrayWindow_from_impl(This
));
1045 if (InterlockedDecrement(&TrayWndCount
) == 0)
1049 static ULONG STDMETHODCALLTYPE
1050 ITrayWindowImpl_AddRef(IN OUT ITrayWindow
*iface
)
1052 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1054 return InterlockedIncrement(&This
->Ref
);
1059 ITrayWindowImpl_NCCreate(IN OUT ITrayWindowImpl
*This
)
1061 ITrayWindowImpl_AddRef(ITrayWindow_from_impl(This
));
1067 ITrayWindowImpl_UpdateStartButton(IN OUT ITrayWindowImpl
*This
,
1068 IN HBITMAP hbmStart OPTIONAL
)
1070 SIZE Size
= { 0, 0 };
1072 if (This
->himlStartBtn
== NULL
||
1073 !SendMessage(This
->hwndStart
,
1078 Size
.cx
= GetSystemMetrics(SM_CXEDGE
);
1079 Size
.cy
= GetSystemMetrics(SM_CYEDGE
);
1081 if (hbmStart
== NULL
)
1083 hbmStart
= (HBITMAP
)SendMessage(This
->hwndStart
,
1089 if (hbmStart
!= NULL
)
1093 if (GetObject(hbmStart
,
1097 Size
.cx
+= bmp
.bmWidth
;
1098 Size
.cy
+= max(bmp
.bmHeight
,
1099 GetSystemMetrics(SM_CYCAPTION
));
1103 /* Huh?! Shouldn't happen... */
1110 Size
.cx
+= GetSystemMetrics(SM_CXMINIMIZED
);
1111 Size
.cy
+= GetSystemMetrics(SM_CYCAPTION
);
1115 /* Save the size of the start button */
1116 This
->StartBtnSize
= Size
;
1120 ITrayWindowImpl_AlignControls(IN OUT ITrayWindowImpl
*This
,
1121 IN PRECT prcClient OPTIONAL
)
1124 SIZE TraySize
, StartSize
;
1125 POINT ptTrayNotify
= { 0, 0 };
1129 ITrayWindowImpl_UpdateStartButton(This
, NULL
);
1130 if (prcClient
!= NULL
)
1132 rcClient
= *prcClient
;
1136 if (!GetClientRect(This
->hWnd
,
1143 Horizontal
= ITrayWindowImpl_IsPosHorizontal(This
);
1145 /* We're about to resize/move the start button, the rebar control and
1146 the tray notification control */
1147 dwp
= BeginDeferWindowPos(3);
1151 /* Limit the Start button width to the client width, if neccessary */
1152 StartSize
= This
->StartBtnSize
;
1153 if (StartSize
.cx
> rcClient
.right
)
1154 StartSize
.cx
= rcClient
.right
;
1156 if (This
->hwndStart
!= NULL
)
1158 /* Resize and reposition the button */
1159 dwp
= DeferWindowPos(dwp
,
1166 SWP_NOZORDER
| SWP_NOACTIVATE
);
1171 /* Determine the size that the tray notification window needs */
1175 TraySize
.cy
= rcClient
.bottom
;
1179 TraySize
.cx
= rcClient
.right
;
1183 if (This
->hwndTrayNotify
!= NULL
&&
1184 SendMessage(This
->hwndTrayNotify
,
1185 TNWM_GETMINIMUMSIZE
,
1189 /* Move the tray notification window to the desired location */
1191 ptTrayNotify
.x
= rcClient
.right
- TraySize
.cx
;
1193 ptTrayNotify
.y
= rcClient
.bottom
- TraySize
.cy
;
1195 dwp
= DeferWindowPos(dwp
,
1196 This
->hwndTrayNotify
,
1202 SWP_NOZORDER
| SWP_NOACTIVATE
);
1207 /* Resize/Move the rebar control */
1208 if (This
->hwndRebar
!= NULL
)
1210 POINT ptRebar
= { 0, 0 };
1213 SetWindowStyle(This
->hwndRebar
,
1215 Horizontal
? 0 : CCS_VERT
);
1219 ptRebar
.x
= StartSize
.cx
+ GetSystemMetrics(SM_CXSIZEFRAME
);
1220 szRebar
.cx
= ptTrayNotify
.x
- ptRebar
.x
;
1221 szRebar
.cy
= rcClient
.bottom
;
1225 ptRebar
.y
= StartSize
.cy
+ GetSystemMetrics(SM_CYSIZEFRAME
);
1226 szRebar
.cx
= rcClient
.right
;
1227 szRebar
.cy
= ptTrayNotify
.y
- ptRebar
.y
;
1230 dwp
= DeferWindowPos(dwp
,
1237 SWP_NOZORDER
| SWP_NOACTIVATE
);
1241 EndDeferWindowPos(dwp
);
1243 if (This
->hwndTaskSwitch
!= NULL
)
1245 /* Update the task switch window configuration */
1246 SendMessage(This
->hwndTaskSwitch
,
1247 TSWM_UPDATETASKBARPOS
,
1254 ITrayWindowImpl_CreateStartBtnImageList(IN OUT ITrayWindowImpl
*This
)
1259 if (This
->himlStartBtn
!= NULL
)
1262 IconSize
.cx
= GetSystemMetrics(SM_CXSMICON
);
1263 IconSize
.cy
= GetSystemMetrics(SM_CYSMICON
);
1265 /* Load the start button icon and create a image list for it */
1266 hIconStart
= LoadImage(hExplorerInstance
,
1267 MAKEINTRESOURCE(IDI_START
),
1271 LR_SHARED
| LR_DEFAULTCOLOR
);
1273 if (hIconStart
!= NULL
)
1275 This
->himlStartBtn
= ImageList_Create(IconSize
.cx
,
1277 ILC_COLOR32
| ILC_MASK
,
1280 if (This
->himlStartBtn
!= NULL
)
1282 if (ImageList_AddIcon(This
->himlStartBtn
,
1288 /* Failed to add the icon! */
1289 ImageList_Destroy(This
->himlStartBtn
);
1290 This
->himlStartBtn
= NULL
;
1298 ITrayWindowImpl_CreateStartButtonBitmap(IN OUT ITrayWindowImpl
*This
)
1300 TCHAR szStartCaption
[32];
1303 HDC hDCScreen
= NULL
;
1304 SIZE Size
, SmallIcon
;
1305 HBITMAP hbmpOld
, hbmp
= NULL
;
1306 HBITMAP hBitmap
= NULL
;
1312 /* NOTE: This is the backwards compatibility code that is used if the
1313 Common Controls Version 6.0 are not available! */
1315 if (!LoadString(hExplorerInstance
,
1318 sizeof(szStartCaption
) / sizeof(szStartCaption
[0])))
1323 /* Load the start button icon */
1324 SmallIcon
.cx
= GetSystemMetrics(SM_CXSMICON
);
1325 SmallIcon
.cy
= GetSystemMetrics(SM_CYSMICON
);
1326 hIconStart
= LoadImage(hExplorerInstance
,
1327 MAKEINTRESOURCE(IDI_START
),
1331 LR_SHARED
| LR_DEFAULTCOLOR
);
1333 hDCScreen
= GetDC(NULL
);
1334 if (hDCScreen
== NULL
)
1337 hDC
= CreateCompatibleDC(hDCScreen
);
1341 hFontOld
= SelectObject(hDC
,
1342 This
->hStartBtnFont
);
1344 Ret
= GetTextExtentPoint32(hDC
,
1346 _tcslen(szStartCaption
),
1354 /* Make sure the height is at least the size of a caption icon. */
1355 if (hIconStart
!= NULL
)
1356 Size
.cx
+= SmallIcon
.cx
+ 4;
1357 Size
.cy
= max(Size
.cy
,
1360 /* Create the bitmap */
1361 hbmp
= CreateCompatibleBitmap(hDCScreen
,
1367 /* Caluclate the button rect */
1370 rcButton
.right
= Size
.cx
;
1371 rcButton
.bottom
= Size
.cy
;
1373 /* Draw the button */
1374 hbmpOld
= SelectObject(hDC
,
1377 Flags
= DC_TEXT
| DC_INBUTTON
;
1378 if (hIconStart
!= NULL
)
1381 if (DrawCapTemp
!= NULL
)
1383 Ret
= DrawCapTemp(NULL
,
1386 This
->hStartBtnFont
,
1398 /* We successfully created the bitmap! */
1403 if (hDCScreen
!= NULL
)
1419 ITrayWindowImpl_UpdateTheme(IN OUT ITrayWindowImpl
*This
)
1421 if (This
->TaskbarTheme
)
1422 CloseThemeData(This
->TaskbarTheme
);
1424 if (IsThemeActive())
1425 This
->TaskbarTheme
= OpenThemeData(This
->hWnd
, L
"Taskbar");
1427 This
->TaskbarTheme
= 0;
1431 ITrayWindowImpl_Create(IN OUT ITrayWindowImpl
*This
)
1433 TCHAR szStartCaption
[32];
1435 SetWindowTheme(This
->hWnd
, L
"TaskBar", NULL
);
1436 ITrayWindowImpl_UpdateTheme(This
);
1438 InterlockedIncrement(&TrayWndCount
);
1440 if (!LoadString(hExplorerInstance
,
1443 sizeof(szStartCaption
) / sizeof(szStartCaption
[0])))
1445 szStartCaption
[0] = TEXT('\0');
1448 if (This
->hStartBtnFont
== NULL
|| This
->hCaptionFont
== NULL
)
1450 NONCLIENTMETRICS ncm
;
1452 /* Get the system fonts, we use the caption font,
1453 always bold, though. */
1454 ncm
.cbSize
= sizeof(ncm
);
1455 if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS
,
1460 if (This
->hCaptionFont
== NULL
)
1462 ncm
.lfCaptionFont
.lfWeight
= FW_NORMAL
;
1463 This
->hCaptionFont
= CreateFontIndirect(&ncm
.lfCaptionFont
);
1466 if (This
->hStartBtnFont
== NULL
)
1468 ncm
.lfCaptionFont
.lfWeight
= FW_BOLD
;
1469 This
->hStartBtnFont
= CreateFontIndirect(&ncm
.lfCaptionFont
);
1474 /* Create the Start button */
1475 This
->hwndStart
= CreateWindowEx(0,
1478 WS_CHILD
| WS_VISIBLE
| WS_CLIPSIBLINGS
|
1479 BS_PUSHBUTTON
| BS_CENTER
| BS_VCENTER
| BS_BITMAP
,
1485 (HMENU
)IDC_STARTBTN
,
1488 if (This
->hwndStart
)
1490 SetWindowTheme(This
->hwndStart
, L
"Start", NULL
);
1491 SendMessage(This
->hwndStart
,
1493 (WPARAM
)This
->hStartBtnFont
,
1496 if (ITrayWindowImpl_CreateStartBtnImageList(This
))
1498 BUTTON_IMAGELIST bil
;
1500 /* Try to set the start button image. This requires the Common
1501 Controls 6.0 to be present (XP and later) */
1502 bil
.himl
= This
->himlStartBtn
;
1503 bil
.margin
.left
= bil
.margin
.right
= 1;
1504 bil
.margin
.top
= bil
.margin
.bottom
= 1;
1505 bil
.uAlign
= BUTTON_IMAGELIST_ALIGN_LEFT
;
1507 if (!SendMessage(This
->hwndStart
,
1512 /* Fall back to the deprecated method on older systems that don't
1513 support Common Controls 6.0 */
1514 ImageList_Destroy(This
->himlStartBtn
);
1515 This
->himlStartBtn
= NULL
;
1517 goto SetStartBtnImage
;
1520 /* We're using the image list, remove the BS_BITMAP style and
1521 don't center it horizontally */
1522 SetWindowStyle(This
->hwndStart
,
1523 BS_BITMAP
| BS_RIGHT
,
1526 ITrayWindowImpl_UpdateStartButton(This
,
1531 HBITMAP hbmStart
, hbmOld
;
1534 hbmStart
= ITrayWindowImpl_CreateStartButtonBitmap(This
);
1535 if (hbmStart
!= NULL
)
1537 ITrayWindowImpl_UpdateStartButton(This
,
1540 hbmOld
= (HBITMAP
)SendMessage(This
->hwndStart
,
1546 DeleteObject(hbmOld
);
1551 /* Load the saved tray window settings */
1552 ITrayWindowImpl_RegLoadSettings(This
);
1554 /* Create and initialize the start menu */
1555 This
->hbmStartMenu
= LoadBitmap(hExplorerInstance
,
1556 MAKEINTRESOURCE(IDB_STARTMENU
));
1557 This
->StartMenuPopup
= CreateStartMenu(ITrayWindow_from_impl(This
),
1558 &This
->StartMenuBand
,
1562 /* Load the tray band site */
1563 if (This
->TrayBandSite
!= NULL
)
1565 ITrayBandSite_Release(This
->TrayBandSite
);
1568 This
->TrayBandSite
= CreateTrayBandSite(ITrayWindow_from_impl(This
),
1570 &This
->hwndTaskSwitch
);
1571 SetWindowTheme(This
->hwndRebar
, L
"TaskBar", NULL
);
1573 /* Create the tray notification window */
1574 This
->hwndTrayNotify
= CreateTrayNotifyWnd(ITrayWindow_from_impl(This
),
1577 if (ITrayWindowImpl_UpdateNonClientMetrics(This
))
1579 ITrayWindowImpl_SetWindowsFont(This
);
1582 /* Move the tray window to the right position and resize it if neccessary */
1583 ITrayWindowImpl_CheckTrayWndPosition(This
);
1585 /* Align all controls on the tray window */
1586 ITrayWindowImpl_AlignControls(This
,
1590 static HRESULT STDMETHODCALLTYPE
1591 ITrayWindowImpl_QueryInterface(IN OUT ITrayWindow
*iface
,
1595 ITrayWindowImpl
*This
;
1600 This
= impl_from_ITrayWindow(iface
);
1602 if (IsEqualIID(riid
,
1605 *ppvObj
= IUnknown_from_impl(This
);
1607 else if (IsEqualIID(riid
,
1608 &IID_IShellDesktopTray
))
1610 *ppvObj
= IShellDesktopTray_from_impl(This
);
1615 return E_NOINTERFACE
;
1618 ITrayWindowImpl_AddRef(iface
);
1622 static ITrayWindowImpl
*
1623 ITrayWindowImpl_Construct(VOID
)
1625 ITrayWindowImpl
*This
;
1627 This
= HeapAlloc(hProcessHeap
,
1633 This
->lpVtbl
= &ITrayWindowImpl_Vtbl
;
1634 This
->lpVtblShellDesktopTray
= &IShellDesktopTrayImpl_Vtbl
;
1636 This
->Position
= (DWORD
)-1;
1641 static HRESULT STDMETHODCALLTYPE
1642 ITrayWindowImpl_Open(IN OUT ITrayWindow
*iface
)
1644 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1649 /* Check if there's already a window created and try to show it.
1650 If it was somehow destroyed just create a new tray window. */
1651 if (This
->hWnd
!= NULL
)
1653 if (IsWindow(This
->hWnd
))
1655 if (!IsWindowVisible(This
->hWnd
))
1657 ITrayWindowImpl_CheckTrayWndPosition(This
);
1659 ShowWindow(This
->hWnd
,
1664 goto TryCreateTrayWnd
;
1671 dwExStyle
= WS_EX_TOOLWINDOW
| WS_EX_WINDOWEDGE
;
1672 if (This
->AlwaysOnTop
)
1673 dwExStyle
|= WS_EX_TOPMOST
;
1675 if (This
->Position
!= (DWORD
)-1)
1676 rcWnd
= This
->rcTrayWnd
[This
->Position
];
1683 hWnd
= CreateWindowEx(dwExStyle
,
1686 WS_POPUP
| WS_VISIBLE
| WS_CLIPSIBLINGS
| WS_CLIPCHILDREN
|
1687 WS_BORDER
| WS_THICKFRAME
,
1690 rcWnd
.right
- rcWnd
.left
,
1691 rcWnd
.bottom
- rcWnd
.top
,
1703 static HRESULT STDMETHODCALLTYPE
1704 ITrayWindowImpl_Close(IN OUT ITrayWindow
*iface
)
1706 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1708 if (This
->hWnd
!= NULL
)
1710 SendMessage(This
->hWnd
,
1719 static HWND STDMETHODCALLTYPE
1720 ITrayWindowImpl_GetHWND(IN OUT ITrayWindow
*iface
)
1722 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1727 static BOOL STDMETHODCALLTYPE
1728 ITrayWindowImpl_IsSpecialHWND(IN OUT ITrayWindow
*iface
,
1731 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1733 return (hWnd
== This
->hWnd
||
1734 (This
->hWndDesktop
!= NULL
&& hWnd
== This
->hWndDesktop
));
1737 static BOOL STDMETHODCALLTYPE
1738 ITrayWindowImpl_IsHorizontal(IN OUT ITrayWindow
*iface
)
1740 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1741 return ITrayWindowImpl_IsPosHorizontal(This
);
1744 static HFONT STDMETHODCALLTYPE
1745 ITrayWIndowImpl_GetCaptionFonts(IN OUT ITrayWindow
*iface
,
1746 OUT HFONT
*phBoldCaption OPTIONAL
)
1748 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1750 if (phBoldCaption
!= NULL
)
1751 *phBoldCaption
= This
->hStartBtnFont
;
1753 return This
->hCaptionFont
;
1757 TrayPropertiesThread(IN OUT PVOID pParam
)
1759 ITrayWindowImpl
*This
= pParam
;
1763 GetWindowRect(This
->hwndStart
, &posRect
);
1764 hwnd
= CreateWindowEx(0,
1767 WS_OVERLAPPED
| WS_DISABLED
| WS_CLIPSIBLINGS
| WS_BORDER
| SS_LEFT
,
1770 posRect
.right
- posRect
.left
,
1771 posRect
.bottom
- posRect
.top
,
1777 This
->hwndTrayPropertiesOwner
= hwnd
;
1779 DisplayTrayProperties(hwnd
);
1781 This
->hwndTrayPropertiesOwner
= NULL
;
1782 DestroyWindow(hwnd
);
1787 static HWND STDMETHODCALLTYPE
1788 ITrayWindowImpl_DisplayProperties(IN OUT ITrayWindow
*iface
)
1790 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1793 if (This
->hwndTrayPropertiesOwner
)
1795 hTrayProp
= GetLastActivePopup(This
->hwndTrayPropertiesOwner
);
1796 if (hTrayProp
!= NULL
&&
1797 hTrayProp
!= This
->hwndTrayPropertiesOwner
)
1799 SetForegroundWindow(hTrayProp
);
1804 CloseHandle(CreateThread(NULL
, 0, TrayPropertiesThread
, This
, 0, NULL
));
1809 OpenCommonStartMenuDirectory(IN HWND hWndOwner
,
1810 IN LPCTSTR lpOperation
)
1812 TCHAR szDir
[MAX_PATH
];
1814 if (SHGetSpecialFolderPath(hWndOwner
,
1816 CSIDL_COMMON_STARTMENU
,
1819 ShellExecute(hWndOwner
,
1829 OpenTaskManager(IN HWND hWndOwner
)
1831 ShellExecute(hWndOwner
,
1833 TEXT("taskmgr.exe"),
1839 static BOOL STDMETHODCALLTYPE
1840 ITrayWindowImpl_ExecContextMenuCmd(IN OUT ITrayWindow
*iface
,
1843 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1844 BOOL bHandled
= TRUE
;
1848 case ID_SHELL_CMD_PROPERTIES
:
1849 ITrayWindow_DisplayProperties(iface
);
1852 case ID_SHELL_CMD_OPEN_ALL_USERS
:
1853 OpenCommonStartMenuDirectory(This
->hWnd
,
1857 case ID_SHELL_CMD_EXPLORE_ALL_USERS
:
1858 OpenCommonStartMenuDirectory(This
->hWnd
,
1862 case ID_LOCKTASKBAR
:
1863 if (SHRestricted(REST_CLASSICSHELL
) == 0)
1865 ITrayWindow_Lock(iface
,
1870 case ID_SHELL_CMD_OPEN_TASKMGR
:
1871 OpenTaskManager(This
->hWnd
);
1874 case ID_SHELL_CMD_UNDO_ACTION
:
1877 case ID_SHELL_CMD_SHOW_DESKTOP
:
1880 case ID_SHELL_CMD_TILE_WND_H
:
1881 TileWindows(NULL
, MDITILE_HORIZONTAL
, NULL
, 0, NULL
);
1884 case ID_SHELL_CMD_TILE_WND_V
:
1885 TileWindows(NULL
, MDITILE_VERTICAL
, NULL
, 0, NULL
);
1888 case ID_SHELL_CMD_CASCADE_WND
:
1889 CascadeWindows(NULL
, MDITILE_SKIPDISABLED
, NULL
, 0, NULL
);
1892 case ID_SHELL_CMD_CUST_NOTIF
:
1895 case ID_SHELL_CMD_ADJUST_DAT
:
1896 LaunchCPanel(NULL
, TEXT("timedate.cpl"));
1900 DbgPrint("ITrayWindow::ExecContextMenuCmd(%u): Unhandled Command ID!\n", uiCmd
);
1908 static BOOL STDMETHODCALLTYPE
1909 ITrayWindowImpl_Lock(IN OUT ITrayWindow
*iface
,
1913 ITrayWindowImpl
*This
= impl_from_ITrayWindow(iface
);
1915 bPrevLock
= This
->Locked
;
1916 if (This
->Locked
!= bLock
)
1918 This
->Locked
= bLock
;
1920 if (This
->TrayBandSite
!= NULL
)
1922 if (!SUCCEEDED(ITrayBandSite_Lock(This
->TrayBandSite
,
1926 This
->Locked
= bPrevLock
;
1934 static const ITrayWindowVtbl ITrayWindowImpl_Vtbl
=
1937 ITrayWindowImpl_QueryInterface
,
1938 ITrayWindowImpl_AddRef
,
1939 ITrayWindowImpl_Release
,
1941 ITrayWindowImpl_Open
,
1942 ITrayWindowImpl_Close
,
1943 ITrayWindowImpl_GetHWND
,
1944 ITrayWindowImpl_IsSpecialHWND
,
1945 ITrayWindowImpl_IsHorizontal
,
1946 ITrayWIndowImpl_GetCaptionFonts
,
1947 ITrayWindowImpl_DisplayProperties
,
1948 ITrayWindowImpl_ExecContextMenuCmd
,
1949 ITrayWindowImpl_Lock
1953 ITrayWindowImpl_DrawBackground(IN ITrayWindowImpl
*This
,
1959 GetClientRect(This
->hWnd
, &rect
);
1960 switch (This
->Position
)
1963 backoundPart
= TBP_BACKGROUNDLEFT
;
1966 backoundPart
= TBP_BACKGROUNDTOP
;
1969 backoundPart
= TBP_BACKGROUNDRIGHT
;
1973 backoundPart
= TBP_BACKGROUNDBOTTOM
;
1976 DrawThemeBackground(This
->TaskbarTheme
, dc
, backoundPart
, 0, &rect
, 0);
1981 ITrayWindowImpl_DrawSizer(IN ITrayWindowImpl
*This
,
1988 GetWindowRect(This
->hWnd
, &rect
);
1989 OffsetRect(&rect
, -rect
.left
, -rect
.top
);
1991 hdc
= GetDCEx(This
->hWnd
, hRgn
, DCX_WINDOW
| DCX_INTERSECTRGN
| DCX_PARENTCLIP
);
1993 switch (This
->Position
)
1996 backoundPart
= TBP_SIZINGBARLEFT
;
1997 rect
.left
= rect
.right
- GetSystemMetrics(SM_CXSIZEFRAME
);
2000 backoundPart
= TBP_SIZINGBARTOP
;
2001 rect
.top
= rect
.bottom
- GetSystemMetrics(SM_CYSIZEFRAME
);
2004 backoundPart
= TBP_SIZINGBARRIGHT
;
2005 rect
.right
= rect
.left
+ GetSystemMetrics(SM_CXSIZEFRAME
);
2009 backoundPart
= TBP_SIZINGBARBOTTOM
;
2010 rect
.bottom
= rect
.top
+ GetSystemMetrics(SM_CYSIZEFRAME
);
2014 DrawThemeBackground(This
->TaskbarTheme
, hdc
, backoundPart
, 0, &rect
, 0);
2016 ReleaseDC(This
->hWnd
, hdc
);
2021 RunFileDlgThread(IN OUT PVOID pParam
)
2023 ITrayWindowImpl
*This
= pParam
;
2025 RUNFILEDLG RunFileDlg
;
2029 GetWindowRect(This
->hwndStart
,&posRect
);
2031 hwnd
= CreateWindowEx(0,
2034 WS_OVERLAPPED
| WS_DISABLED
| WS_CLIPSIBLINGS
| WS_BORDER
| SS_LEFT
,
2037 posRect
.right
- posRect
.left
,
2038 posRect
.bottom
- posRect
.top
,
2044 This
->hwndRunFileDlgOwner
= hwnd
;
2046 hShell32
= GetModuleHandle(TEXT("SHELL32.DLL"));
2047 RunFileDlg
= (RUNFILEDLG
)GetProcAddress(hShell32
, (LPCSTR
)61);
2049 RunFileDlg(hwnd
, NULL
, NULL
, NULL
, NULL
, RFF_CALCDIRECTORY
);
2051 This
->hwndRunFileDlgOwner
= NULL
;
2052 DestroyWindow(hwnd
);
2058 ITrayWindowImpl_DisplayRunFileDlg(IN ITrayWindowImpl
*This
)
2061 if (This
->hwndRunFileDlgOwner
)
2063 hRunDlg
= GetLastActivePopup(This
->hwndRunFileDlgOwner
);
2064 if (hRunDlg
!= NULL
&&
2065 hRunDlg
!= This
->hwndRunFileDlgOwner
)
2067 SetForegroundWindow(hRunDlg
);
2072 CloseHandle(CreateThread(NULL
, 0, RunFileDlgThread
, This
, 0, NULL
));
2075 static LRESULT CALLBACK
2076 TrayWndProc(IN HWND hwnd
,
2081 ITrayWindowImpl
*This
= NULL
;
2082 LRESULT Ret
= FALSE
;
2084 if (uMsg
!= WM_NCCREATE
)
2086 This
= (ITrayWindowImpl
*)GetWindowLongPtr(hwnd
,
2090 if (This
!= NULL
|| uMsg
== WM_NCCREATE
)
2092 if (This
!= NULL
&& This
->StartMenuBand
!= NULL
)
2099 Msg
.wParam
= wParam
;
2100 Msg
.lParam
= lParam
;
2102 if (IMenuBand_TranslateMenuMessage(This
->StartMenuBand
,
2109 wParam
= Msg
.wParam
;
2110 lParam
= Msg
.lParam
;
2117 if (This
->hwndTrayNotify
)
2119 TrayNotify_NotifyMsg(This
->hwndTrayNotify
,
2125 case WM_THEMECHANGED
:
2126 ITrayWindowImpl_UpdateTheme(This
);
2129 if (!This
->TaskbarTheme
)
2131 return ITrayWindowImpl_DrawSizer(This
,
2134 if (!This
->TaskbarTheme
)
2136 return ITrayWindowImpl_DrawBackground(This
,
2138 case WM_CTLCOLORBTN
:
2139 SetBkMode((HDC
)wParam
, TRANSPARENT
);
2140 return (LRESULT
)GetStockObject(HOLLOW_BRUSH
);
2148 /* The user may not be able to resize the tray window.
2149 Pretend like the window is not sizeable when the user
2150 clicks on the border. */
2154 SetLastError(ERROR_SUCCESS
);
2155 if (GetClientRect(hwnd
,
2157 (MapWindowPoints(hwnd
,
2160 2) != 0 || GetLastError() == ERROR_SUCCESS
))
2162 pt
.x
= (SHORT
)LOWORD(lParam
);
2163 pt
.y
= (SHORT
)HIWORD(lParam
);
2165 if (PtInRect(&rcClient
,
2168 /* The user is trying to drag the tray window */
2172 /* Depending on the position of the tray window, allow only
2173 changing the border next to the monitor working area */
2174 switch (This
->Position
)
2177 if (pt
.y
> rcClient
.bottom
)
2181 if (pt
.x
> rcClient
.right
)
2185 if (pt
.x
< rcClient
.left
)
2190 if (pt
.y
< rcClient
.top
)
2200 PRECT pRect
= (PRECT
)lParam
;
2202 /* We need to ensure that an application can not accidently
2203 move the tray window (using SetWindowPos). However, we still
2204 need to be able to move the window in case the user wants to
2205 drag the tray window to another position or in case the user
2206 wants to resize the tray window. */
2207 if (!This
->Locked
&& GetCursorPos(&ptCursor
))
2209 This
->IsDragging
= TRUE
;
2210 This
->DraggingPosition
= ITrayWindowImpl_GetDraggingRectFromPt(This
,
2213 &This
->DraggingMonitor
);
2217 *pRect
= This
->rcTrayWnd
[This
->Position
];
2224 PRECT pRect
= (PRECT
)lParam
;
2228 ITrayWindowImpl_CalculateValidSize(This
,
2234 *pRect
= This
->rcTrayWnd
[This
->Position
];
2239 case WM_WINDOWPOSCHANGING
:
2241 ITrayWindowImpl_ChangingWinPos(This
,
2242 (LPWINDOWPOS
)lParam
);
2249 InvalidateRect(This
->hWnd
, NULL
, TRUE
);
2250 if (wParam
== SIZE_RESTORED
&& lParam
== 0)
2252 ITrayWindowImpl_ResizeWorkArea(This
);
2253 /* Clip the tray window on multi monitor systems so the edges can't
2254 overlap into another monitor */
2255 ITrayWindowImpl_ApplyClipping(This
,
2258 if (!GetClientRect(This
->hWnd
,
2266 rcClient
.left
= rcClient
.top
= 0;
2267 rcClient
.right
= LOWORD(lParam
);
2268 rcClient
.bottom
= HIWORD(lParam
);
2271 ITrayWindowImpl_AlignControls(This
,
2276 case WM_ENTERSIZEMOVE
:
2277 This
->InSizeMove
= TRUE
;
2278 This
->IsDragging
= FALSE
;
2281 /* Remove the clipping on multi monitor systems while dragging around */
2282 ITrayWindowImpl_ApplyClipping(This
,
2287 case WM_EXITSIZEMOVE
:
2288 This
->InSizeMove
= FALSE
;
2291 /* Apply clipping */
2292 PostMessage(This
->hWnd
,
2304 /* The user pressed Alt+Space, this usually brings up the system menu of a window.
2305 The tray window needs to handle this specially, since it normally doesn't have
2308 static const UINT uidDisableItem
[] = {
2319 /* temporarily enable the system menu */
2320 SetWindowStyle(hwnd
,
2324 hSysMenu
= GetSystemMenu(hwnd
,
2326 if (hSysMenu
!= NULL
)
2328 /* Disable all items that are not relevant */
2329 for (i
= 0; i
!= sizeof(uidDisableItem
) / sizeof(uidDisableItem
[0]); i
++)
2331 EnableMenuItem(hSysMenu
,
2333 MF_BYCOMMAND
| MF_GRAYED
);
2336 EnableMenuItem(hSysMenu
,
2339 (SHRestricted(REST_NOCLOSE
) ? MF_GRAYED
: MF_ENABLED
));
2341 /* Display the system menu */
2342 uId
= ITrayWindowImpl_TrackMenu(This
,
2346 This
->Position
!= ABE_TOP
,
2350 SendMessage(This
->hWnd
,
2357 /* revert the system menu window style */
2358 SetWindowStyle(hwnd
,
2369 case WM_NCRBUTTONUP
:
2370 /* We want the user to be able to get a context menu even on the nonclient
2371 area (including the sizing border)! */
2372 uMsg
= WM_CONTEXTMENU
;
2373 wParam
= (WPARAM
)hwnd
;
2376 case WM_CONTEXTMENU
:
2378 POINT pt
, *ppt
= NULL
;
2379 HWND hWndExclude
= NULL
;
2381 /* Check if the administrator has forbidden access to context menus */
2382 if (SHRestricted(REST_NOTRAYCONTEXTMENU
))
2385 pt
.x
= (SHORT
)LOWORD(lParam
);
2386 pt
.y
= (SHORT
)HIWORD(lParam
);
2388 if (pt
.x
!= -1 || pt
.y
!= -1)
2391 hWndExclude
= This
->hwndStart
;
2393 if ((HWND
)wParam
== This
->hwndStart
)
2395 /* Make sure we can't track the context menu if the start
2396 menu is currently being shown */
2397 if (!(SendMessage(This
->hwndStart
,
2402 ITrayWindowImpl_TrackCtxMenu(This
,
2403 &StartMenuBtnCtxMenu
,
2406 This
->Position
== ABE_BOTTOM
,
2412 /* See if the context menu should be handled by the task band site */
2413 if (ppt
!= NULL
&& This
->TrayBandSite
!= NULL
)
2416 POINT ptClient
= *ppt
;
2418 /* Convert the coordinates to client-coordinates */
2419 MapWindowPoints(NULL
,
2424 hWndAtPt
= ChildWindowFromPoint(This
->hWnd
,
2426 if (hWndAtPt
!= NULL
&&
2427 (hWndAtPt
== This
->hwndRebar
|| IsChild(This
->hwndRebar
,
2430 /* Check if the user clicked on the task switch window */
2432 MapWindowPoints(NULL
,
2437 hWndAtPt
= ChildWindowFromPointEx(This
->hwndRebar
,
2439 CWP_SKIPINVISIBLE
| CWP_SKIPDISABLED
);
2440 if (hWndAtPt
== This
->hwndTaskSwitch
)
2441 goto HandleTrayContextMenu
;
2443 /* Forward the message to the task band site */
2444 ITrayBandSite_ProcessMessage(This
->TrayBandSite
,
2452 goto HandleTrayContextMenu
;
2456 HandleTrayContextMenu
:
2457 /* Tray the default tray window context menu */
2458 ITrayWindowImpl_TrackCtxMenu(This
,
2471 /* FIXME: We can't check with IsChild whether the hwnd is somewhere inside
2472 the rebar control! But we shouldn't forward messages that the band
2473 site doesn't handle, such as other controls (start button, tray window */
2474 if (This
->TrayBandSite
== NULL
||
2475 !SUCCEEDED(ITrayBandSite_ProcessMessage(This
->TrayBandSite
,
2482 const NMHDR
*nmh
= (const NMHDR
*)lParam
;
2484 if (nmh
->hwndFrom
== This
->hwndTrayNotify
)
2489 /* Cause all controls to be aligned */
2490 PostMessage(This
->hWnd
,
2501 case WM_NCLBUTTONDBLCLK
:
2503 /* We "handle" this message so users can't cause a weird maximize/restore
2504 window animation when double-clicking the tray window! */
2506 /* We should forward mouse messages to child windows here.
2507 Right now, this is only clock double-click */
2509 if (TrayNotify_GetClockRect(This
->hwndTrayNotify
, &rcClock
))
2512 ptClick
.x
= MAKEPOINTS(lParam
).x
;
2513 ptClick
.y
= MAKEPOINTS(lParam
).y
;
2514 if (PtInRect(&rcClock
, ptClick
))
2515 LaunchCPanel(NULL
, TEXT("timedate.cpl"));
2522 LPCREATESTRUCT CreateStruct
= (LPCREATESTRUCT
)lParam
;
2523 This
= (ITrayWindowImpl
*)CreateStruct
->lpCreateParams
;
2525 if (InterlockedCompareExchangePointer((PVOID
*)&This
->hWnd
,
2529 /* Somebody else was faster... */
2533 SetWindowLongPtr(hwnd
,
2537 return ITrayWindowImpl_NCCreate(This
);
2541 ITrayWindowImpl_Create(This
);
2545 ITrayWindowImpl_Destroy(This
);
2548 case WM_APP_TRAYDESTROY
:
2549 DestroyWindow(hwnd
);
2552 case TWM_OPENSTARTMENU
:
2553 SendMessage(This
->hWnd
, WM_COMMAND
, MAKEWPARAM(BN_CLICKED
, IDC_STARTBTN
), (LPARAM
)This
->hwndStart
);
2557 if ((HWND
)lParam
== This
->hwndStart
)
2559 if (This
->StartMenuPopup
!= NULL
)
2565 if (GetWindowRect(This
->hwndStart
,
2568 if (ITrayWindowImpl_IsPosHorizontal(This
))
2570 pt
.x
= rcExclude
.left
;
2571 pt
.y
= rcExclude
.top
;
2572 dwFlags
|= MPPF_BOTTOM
;
2576 if (This
->Position
== ABE_LEFT
)
2577 pt
.x
= rcExclude
.left
;
2579 pt
.x
= rcExclude
.right
;
2581 pt
.y
= rcExclude
.bottom
;
2582 dwFlags
|= MPPF_BOTTOM
;
2585 IMenuPopup_Popup(This
->StartMenuPopup
,
2594 if (This
->TrayBandSite
== NULL
||
2595 !SUCCEEDED(ITrayBandSite_ProcessMessage(This
->TrayBandSite
,
2602 switch (LOWORD(wParam
))
2604 /* FIXME: Handle these commands as well */
2605 case IDM_TASKBARANDSTARTMENU
:
2607 case IDM_HELPANDSUPPORT
:
2612 ITrayWindowImpl_DisplayRunFileDlg(This
);
2616 /* FIXME: Handle these commands as well */
2617 case IDM_SYNCHRONIZE
:
2619 case IDM_DISCONNECT
:
2620 case IDM_UNDOCKCOMPUTER
:
2626 EXITWINDLG ExitWinDlg
;
2628 hShell32
= GetModuleHandle(TEXT("SHELL32.DLL"));
2629 ExitWinDlg
= (EXITWINDLG
)GetProcAddress(hShell32
, (LPCSTR
)60);
2645 Ret
= DefWindowProc(hwnd
,
2655 * Tray Window Context Menu
2659 CreateTrayWindowContextMenu(IN HWND hWndOwner
,
2660 IN PVOID
*ppcmContext
,
2661 IN PVOID Context OPTIONAL
)
2663 ITrayWindowImpl
*This
= (ITrayWindowImpl
*)Context
;
2664 IContextMenu
*pcm
= NULL
;
2667 hPopup
= LoadPopupMenu(hExplorerInstance
,
2668 MAKEINTRESOURCE(IDM_TRAYWND
));
2672 if (SHRestricted(REST_CLASSICSHELL
) != 0)
2679 CheckMenuItem(hPopup
,
2681 MF_BYCOMMAND
| (This
->Locked
? MF_CHECKED
: MF_UNCHECKED
));
2683 if (This
->TrayBandSite
!= NULL
)
2685 if (SUCCEEDED(ITrayBandSite_AddContextMenus(This
->TrayBandSite
,
2693 DbgPrint("ITrayBandSite::AddContextMenus succeeded!\n");
2694 *(IContextMenu
**)ppcmContext
= pcm
;
2703 OnTrayWindowContextMenuCommand(IN HWND hWndOwner
,
2705 IN PVOID pcmContext OPTIONAL
,
2706 IN PVOID Context OPTIONAL
)
2708 ITrayWindowImpl
*This
= (ITrayWindowImpl
*)Context
;
2709 IContextMenu
*pcm
= (IContextMenu
*)pcmContext
;
2713 if (uiCmdId
>= ID_SHELL_CMD_FIRST
&& uiCmdId
<= ID_SHELL_CMD_LAST
)
2715 CMINVOKECOMMANDINFO cmici
= {0};
2719 /* Setup and invoke the shell command */
2720 cmici
.cbSize
= sizeof(cmici
);
2721 cmici
.hwnd
= hWndOwner
;
2722 cmici
.lpVerb
= (LPCSTR
)MAKEINTRESOURCE(uiCmdId
- ID_SHELL_CMD_FIRST
);
2723 cmici
.nShow
= SW_NORMAL
;
2725 IContextMenu_InvokeCommand(pcm
,
2731 ITrayWindow_ExecContextMenuCmd(ITrayWindow_from_impl(This
),
2737 IContextMenu_Release(pcm
);
2740 static const TRAYWINDOW_CTXMENU TrayWindowCtxMenu
= {
2741 CreateTrayWindowContextMenu
,
2742 OnTrayWindowContextMenuCommand
2745 /*****************************************************************************/
2748 RegisterTrayWindowClass(VOID
)
2753 if (!RegisterTrayNotifyWndClass())
2756 wcTrayWnd
.style
= CS_DBLCLKS
;
2757 wcTrayWnd
.lpfnWndProc
= TrayWndProc
;
2758 wcTrayWnd
.cbClsExtra
= 0;
2759 wcTrayWnd
.cbWndExtra
= sizeof(ITrayWindowImpl
*);
2760 wcTrayWnd
.hInstance
= hExplorerInstance
;
2761 wcTrayWnd
.hIcon
= NULL
;
2762 wcTrayWnd
.hCursor
= LoadCursor(NULL
,
2764 wcTrayWnd
.hbrBackground
= (HBRUSH
)(COLOR_3DFACE
+ 1);
2765 wcTrayWnd
.lpszMenuName
= NULL
;
2766 wcTrayWnd
.lpszClassName
= szTrayWndClass
;
2768 Ret
= RegisterClass(&wcTrayWnd
) != 0;
2771 UnregisterTrayNotifyWndClass();
2777 UnregisterTrayWindowClass(VOID
)
2779 UnregisterTrayNotifyWndClass();
2781 UnregisterClass(szTrayWndClass
,
2786 CreateTrayWindow(VOID
)
2788 ITrayWindowImpl
*This
;
2789 ITrayWindow
*TrayWindow
;
2791 This
= ITrayWindowImpl_Construct();
2794 TrayWindow
= ITrayWindow_from_impl(This
);
2796 ITrayWindowImpl_Open(TrayWindow
);
2805 TrayProcessMessages(IN OUT ITrayWindow
*Tray
)
2807 ITrayWindowImpl
*This
;
2810 This
= impl_from_ITrayWindow(Tray
);
2812 /* FIXME: We should keep a reference here... */
2814 while (PeekMessage(&Msg
,
2820 if (Msg
.message
== WM_QUIT
)
2823 if (This
->StartMenuBand
== NULL
||
2824 IMenuBand_IsMenuMessage(This
->StartMenuBand
,
2827 TranslateMessage(&Msg
);
2828 DispatchMessage(&Msg
);
2834 TrayMessageLoop(IN OUT ITrayWindow
*Tray
)
2836 ITrayWindowImpl
*This
;
2840 This
= impl_from_ITrayWindow(Tray
);
2842 /* FIXME: We should keep a reference here... */
2846 Ret
= GetMessage(&Msg
,
2851 if (!Ret
|| Ret
== -1)
2854 if (Msg
.message
== WM_HOTKEY
)
2858 case IDHK_RUN
: /* Win+R */
2859 ITrayWindowImpl_DisplayRunFileDlg(This
);
2864 if (This
->StartMenuBand
== NULL
||
2865 IMenuBand_IsMenuMessage(This
->StartMenuBand
,
2868 TranslateMessage(&Msg
);
2869 DispatchMessage(&Msg
);
2877 * NOTE: This is a very windows-specific COM interface used by SHCreateDesktop()!
2878 * These are the calls I observed, it may be wrong/incomplete/buggy!!!
2879 * The reason we implement it is because we have to use SHCreateDesktop() so
2880 * that the shell provides the desktop window and all the features that come
2881 * with it (especially positioning of desktop icons)
2884 static HRESULT STDMETHODCALLTYPE
2885 ITrayWindowImpl_IShellDesktopTray_QueryInterface(IN OUT IShellDesktopTray
*iface
,
2889 ITrayWindowImpl
*This
= impl_from_IShellDesktopTray(iface
);
2890 ITrayWindow
*tray
= ITrayWindow_from_impl(This
);
2892 DbgPrint("IShellDesktopTray::QueryInterface(0x%p, 0x%p)\n", riid
, ppvObj
);
2893 return ITrayWindowImpl_QueryInterface(tray
,
2898 static ULONG STDMETHODCALLTYPE
2899 ITrayWindowImpl_IShellDesktopTray_Release(IN OUT IShellDesktopTray
*iface
)
2901 ITrayWindowImpl
*This
= impl_from_IShellDesktopTray(iface
);
2902 ITrayWindow
*tray
= ITrayWindow_from_impl(This
);
2904 DbgPrint("IShellDesktopTray::Release()\n");
2905 return ITrayWindowImpl_Release(tray
);
2908 static ULONG STDMETHODCALLTYPE
2909 ITrayWindowImpl_IShellDesktopTray_AddRef(IN OUT IShellDesktopTray
*iface
)
2911 ITrayWindowImpl
*This
= impl_from_IShellDesktopTray(iface
);
2912 ITrayWindow
*tray
= ITrayWindow_from_impl(This
);
2914 DbgPrint("IShellDesktopTray::AddRef()\n");
2915 return ITrayWindowImpl_AddRef(tray
);
2918 static ULONG STDMETHODCALLTYPE
2919 ITrayWindowImpl_IShellDesktopTray_GetState(IN OUT IShellDesktopTray
*iface
)
2921 /* FIXME: Return ABS_ flags? */
2922 DbgPrint("IShellDesktopTray::GetState() unimplemented!\n");
2926 static HRESULT STDMETHODCALLTYPE
2927 ITrayWindowImpl_IShellDesktopTray_GetTrayWindow(IN OUT IShellDesktopTray
*iface
,
2928 OUT HWND
*phWndTray
)
2930 ITrayWindowImpl
*This
= impl_from_IShellDesktopTray(iface
);
2931 DbgPrint("IShellDesktopTray::GetTrayWindow(0x%p)\n", phWndTray
);
2932 *phWndTray
= This
->hWnd
;
2936 static HRESULT STDMETHODCALLTYPE
2937 ITrayWindowImpl_IShellDesktopTray_RegisterDesktopWindow(IN OUT IShellDesktopTray
*iface
,
2938 IN HWND hWndDesktop
)
2940 ITrayWindowImpl
*This
= impl_from_IShellDesktopTray(iface
);
2941 DbgPrint("IShellDesktopTray::RegisterDesktopWindow(0x%p)\n", hWndDesktop
);
2943 This
->hWndDesktop
= hWndDesktop
;
2947 static HRESULT STDMETHODCALLTYPE
2948 ITrayWindowImpl_IShellDesktopTray_Unknown(IN OUT IShellDesktopTray
*iface
,
2949 IN DWORD dwUnknown1
,
2950 IN DWORD dwUnknown2
)
2952 DbgPrint("IShellDesktopTray::Unknown(%u,%u) unimplemented!\n", dwUnknown1
, dwUnknown2
);
2956 static const IShellDesktopTrayVtbl IShellDesktopTrayImpl_Vtbl
=
2959 ITrayWindowImpl_IShellDesktopTray_QueryInterface
,
2960 ITrayWindowImpl_IShellDesktopTray_AddRef
,
2961 ITrayWindowImpl_IShellDesktopTray_Release
,
2962 /*** IShellDesktopTray ***/
2963 ITrayWindowImpl_IShellDesktopTray_GetState
,
2964 ITrayWindowImpl_IShellDesktopTray_GetTrayWindow
,
2965 ITrayWindowImpl_IShellDesktopTray_RegisterDesktopWindow
,
2966 ITrayWindowImpl_IShellDesktopTray_Unknown