b806c5dd4909b87de57477b92f484547d33d51cf
[reactos.git] / base / shell / explorer-new / traywnd.c
1 /*
2 * ReactOS Explorer
3 *
4 * Copyright 2006 - 2007 Thomas Weidenmueller <w3seek@reactos.org>
5 *
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.
10 *
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.
15 *
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
19 */
20
21 #include "precomp.h"
22
23 static const TRAYWINDOW_CTXMENU TrayWindowCtxMenu;
24
25 #define WM_APP_TRAYDESTROY (WM_APP + 0x100)
26
27 static LONG TrayWndCount = 0;
28
29 static const TCHAR szTrayWndClass[] = TEXT("Shell_TrayWnd");
30
31 static const ITrayWindowVtbl ITrayWindowImpl_Vtbl;
32 static const IShellDesktopTrayVtbl IShellDesktopTrayImpl_Vtbl;
33
34 /*
35 * ITrayWindow
36 */
37
38 const GUID IID_IShellDesktopTray = {0x213e2df9, 0x9a14, 0x4328, {0x99, 0xb1, 0x69, 0x61, 0xf9, 0x14, 0x3c, 0xe9}};
39
40 typedef struct
41 {
42 const ITrayWindowVtbl *lpVtbl;
43 const IShellDesktopTrayVtbl *lpVtblShellDesktopTray;
44 LONG Ref;
45
46 HTHEME TaskbarTheme;
47 HWND hWnd;
48 HWND hWndDesktop;
49
50 HWND hwndStart;
51 HIMAGELIST himlStartBtn;
52 SIZE StartBtnSize;
53 HFONT hStartBtnFont;
54 HFONT hCaptionFont;
55
56 ITrayBandSite *TrayBandSite;
57 HWND hwndRebar;
58 HWND hwndTaskSwitch;
59 HWND hwndTrayNotify;
60
61 DWORD Position;
62 HMONITOR Monitor;
63 HMONITOR PreviousMonitor;
64 DWORD DraggingPosition;
65 HMONITOR DraggingMonitor;
66
67 RECT rcTrayWnd[4];
68 RECT rcNewPosSize;
69 SIZE TraySize;
70 union
71 {
72 DWORD Flags;
73 struct
74 {
75 DWORD AutoHide : 1;
76 DWORD AlwaysOnTop : 1;
77 DWORD SmSmallIcons : 1;
78 DWORD HideClock : 1;
79 DWORD Locked : 1;
80
81 /* UI Status */
82 DWORD InSizeMove : 1;
83 DWORD IsDragging : 1;
84 DWORD NewPosSize : 1;
85 };
86 };
87
88 NONCLIENTMETRICS ncm;
89 HFONT hFont;
90
91 IMenuBand *StartMenuBand;
92 IMenuPopup *StartMenuPopup;
93 HBITMAP hbmStartMenu;
94
95 HWND hwndTrayPropertiesOwner;
96 HWND hwndRunFileDlgOwner;
97 } ITrayWindowImpl;
98
99 BOOL LaunchCPanel(HWND hwnd, LPCTSTR applet)
100 {
101 TCHAR szParams[MAX_PATH];
102
103 StringCbCopy(szParams, sizeof(szParams),
104 TEXT("shell32.dll,Control_RunDLL "));
105 if (FAILED(StringCbCat(szParams, sizeof(szParams),
106 applet)))
107 return FALSE;
108
109 return (ShellExecute(hwnd, TEXT("open"), TEXT("rundll32.exe"), szParams, NULL, SW_SHOWDEFAULT) > (HINSTANCE)32);
110 }
111
112 static IUnknown *
113 IUnknown_from_impl(ITrayWindowImpl *This)
114 {
115 return (IUnknown *)&This->lpVtbl;
116 }
117
118 static ITrayWindow *
119 ITrayWindow_from_impl(ITrayWindowImpl *This)
120 {
121 return (ITrayWindow *)&This->lpVtbl;
122 }
123
124 static IShellDesktopTray *
125 IShellDesktopTray_from_impl(ITrayWindowImpl *This)
126 {
127 return (IShellDesktopTray *)&This->lpVtblShellDesktopTray;
128 }
129
130 static ITrayWindowImpl *
131 impl_from_ITrayWindow(ITrayWindow *iface)
132 {
133 return (ITrayWindowImpl *)((ULONG_PTR)iface - FIELD_OFFSET(ITrayWindowImpl,
134 lpVtbl));
135 }
136
137 static ITrayWindowImpl *
138 impl_from_IShellDesktopTray(IShellDesktopTray *iface)
139 {
140 return (ITrayWindowImpl *)((ULONG_PTR)iface - FIELD_OFFSET(ITrayWindowImpl,
141 lpVtblShellDesktopTray));
142 }
143
144 /*
145 * ITrayWindow
146 */
147
148 static BOOL
149 ITrayWindowImpl_UpdateNonClientMetrics(IN OUT ITrayWindowImpl *This)
150 {
151 This->ncm.cbSize = sizeof(This->ncm);
152 if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS,
153 sizeof(This->ncm),
154 &This->ncm,
155 0))
156 {
157 if (This->hFont != NULL)
158 DeleteObject(This->hFont);
159
160 This->hFont = CreateFontIndirect(&This->ncm.lfMessageFont);
161 return TRUE;
162 }
163
164 return FALSE;
165 }
166
167 static VOID
168 ITrayWindowImpl_SetWindowsFont(IN OUT ITrayWindowImpl *This)
169 {
170 if (This->hwndTrayNotify != NULL)
171 {
172 SendMessage(This->hwndTrayNotify,
173 WM_SETFONT,
174 (WPARAM)This->hFont,
175 TRUE);
176 }
177 }
178
179 static HMONITOR
180 ITrayWindowImpl_GetScreenRectFromRect(IN OUT ITrayWindowImpl *This,
181 IN OUT RECT *pRect,
182 IN DWORD dwFlags)
183 {
184 MONITORINFO mi;
185 HMONITOR hMon;
186
187 mi.cbSize = sizeof(mi);
188 hMon = MonitorFromRect(pRect,
189 dwFlags);
190 if (hMon != NULL &&
191 GetMonitorInfo(hMon,
192 &mi))
193 {
194 *pRect = mi.rcMonitor;
195 }
196 else
197 {
198 pRect->left = 0;
199 pRect->top = 0;
200 pRect->right = GetSystemMetrics(SM_CXSCREEN);
201 pRect->bottom = GetSystemMetrics(SM_CYSCREEN);
202
203 hMon = NULL;
204 }
205
206 return hMon;
207 }
208
209 static HMONITOR
210 ITrayWindowImpl_GetMonitorFromRect(IN OUT ITrayWindowImpl *This,
211 IN const RECT *pRect)
212 {
213 HMONITOR hMon;
214
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);
222 if (hMon == NULL)
223 {
224 POINT pt;
225
226 pt.x = pRect->left + ((pRect->right - pRect->left) / 2);
227 pt.y = pRect->top + ((pRect->bottom - pRect->top) / 2);
228
229 /* be less error-prone, find the nearest monitor */
230 hMon = MonitorFromPoint(pt,
231 MONITOR_DEFAULTTONEAREST);
232 }
233
234 return hMon;
235 }
236
237 static HMONITOR
238 ITrayWindowImpl_GetScreenRect(IN OUT ITrayWindowImpl *This,
239 IN HMONITOR hMonitor,
240 IN OUT RECT *pRect)
241 {
242 HMONITOR hMon = NULL;
243
244 if (hMonitor != NULL)
245 {
246 MONITORINFO mi;
247
248 mi.cbSize = sizeof(mi);
249 if (!GetMonitorInfo(hMonitor,
250 &mi))
251 {
252 /* Hm, the monitor is gone? Try to find a monitor where it
253 could be located now */
254 hMon = ITrayWindowImpl_GetMonitorFromRect(This,
255 pRect);
256 if (hMon == NULL ||
257 !GetMonitorInfo(hMon,
258 &mi))
259 {
260 hMon = NULL;
261 goto GetPrimaryRect;
262 }
263 }
264
265 *pRect = mi.rcMonitor;
266 }
267 else
268 {
269 GetPrimaryRect:
270 pRect->left = 0;
271 pRect->top = 0;
272 pRect->right = GetSystemMetrics(SM_CXSCREEN);
273 pRect->bottom = GetSystemMetrics(SM_CYSCREEN);
274 }
275
276 return hMon;
277 }
278
279 static VOID
280 ITrayWindowImpl_MakeTrayRectWithSize(IN DWORD Position,
281 IN const SIZE *pTraySize,
282 IN OUT RECT *pRect)
283 {
284 switch (Position)
285 {
286 case ABE_LEFT:
287 pRect->right = pRect->left + pTraySize->cx;
288 break;
289
290 case ABE_TOP:
291 pRect->bottom = pRect->top + pTraySize->cy;
292 break;
293
294 case ABE_RIGHT:
295 pRect->left = pRect->right - pTraySize->cx;
296 break;
297
298 case ABE_BOTTOM:
299 default:
300 pRect->top = pRect->bottom - pTraySize->cy;
301 break;
302 }
303 }
304
305 static VOID
306 ITrayWindowImpl_GetTrayRectFromScreenRect(IN OUT ITrayWindowImpl *This,
307 IN DWORD Position,
308 IN const RECT *pScreen,
309 IN const SIZE *pTraySize OPTIONAL,
310 OUT RECT *pRect)
311 {
312 if (pTraySize == NULL)
313 pTraySize = &This->TraySize;
314
315 *pRect = *pScreen;
316
317 /* Move the border outside of the screen */
318 InflateRect(pRect,
319 GetSystemMetrics(SM_CXEDGE),
320 GetSystemMetrics(SM_CYEDGE));
321
322 ITrayWindowImpl_MakeTrayRectWithSize(Position,
323 pTraySize,
324 pRect);
325 }
326
327 BOOL
328 ITrayWindowImpl_IsPosHorizontal(IN OUT ITrayWindowImpl *This)
329 {
330 return This->Position == ABE_TOP || This->Position == ABE_BOTTOM;
331 }
332
333 static HMONITOR
334 ITrayWindowImpl_CalculateValidSize(IN OUT ITrayWindowImpl *This,
335 IN DWORD Position,
336 IN OUT RECT *pRect)
337 {
338 RECT rcScreen;
339 //BOOL Horizontal;
340 HMONITOR hMon;
341 SIZE szMax, szWnd;
342
343 //Horizontal = ITrayWindowImpl_IsPosHorizontal(This);
344
345 szWnd.cx = pRect->right - pRect->left;
346 szWnd.cy = pRect->bottom - pRect->top;
347
348 rcScreen = *pRect;
349 hMon = ITrayWindowImpl_GetScreenRectFromRect(This,
350 &rcScreen,
351 MONITOR_DEFAULTTONEAREST);
352
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)
358 szWnd.cx = szMax.cx;
359 if (szWnd.cy > szMax.cy)
360 szWnd.cy = szMax.cy;
361
362 /* FIXME - calculate */
363
364 ITrayWindowImpl_GetTrayRectFromScreenRect(This,
365 Position,
366 &rcScreen,
367 &szWnd,
368 pRect);
369
370 return hMon;
371 }
372
373 #if 0
374 static VOID
375 ITrayWindowImpl_GetMinimumWindowSize(IN OUT ITrayWindowImpl *This,
376 OUT RECT *pRect)
377 {
378 RECT rcMin = {0};
379
380 AdjustWindowRectEx(&rcMin,
381 GetWindowLong(This->hWnd,
382 GWL_STYLE),
383 FALSE,
384 GetWindowLong(This->hWnd,
385 GWL_EXSTYLE));
386
387 *pRect = rcMin;
388 }
389 #endif
390
391
392 static DWORD
393 ITrayWindowImpl_GetDraggingRectFromPt(IN OUT ITrayWindowImpl *This,
394 IN POINT pt,
395 OUT RECT *pRect,
396 OUT HMONITOR *phMonitor)
397 {
398 HMONITOR hMon, hMonNew;
399 DWORD PosH, PosV, Pos;
400 SIZE DeltaPt, ScreenOffset;
401 RECT rcScreen;
402
403 rcScreen.left = 0;
404 rcScreen.top = 0;
405
406 /* Determine the screen rectangle */
407 hMon = MonitorFromPoint(pt,
408 MONITOR_DEFAULTTONULL);
409
410 if (hMon != NULL)
411 {
412 MONITORINFO mi;
413
414 mi.cbSize = sizeof(mi);
415 if (!GetMonitorInfo(hMon,
416 &mi))
417 {
418 hMon = NULL;
419 goto GetPrimaryScreenRect;
420 }
421
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;
426
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;
431 }
432 else
433 {
434 GetPrimaryScreenRect:
435 ScreenOffset.cx = 0;
436 ScreenOffset.cy = 0;
437 rcScreen.right = GetSystemMetrics(SM_CXSCREEN);
438 rcScreen.bottom = GetSystemMetrics(SM_CYSCREEN);
439 }
440
441 /* Calculate the nearest screen border */
442 if (pt.x < rcScreen.right / 2)
443 {
444 DeltaPt.cx = pt.x;
445 PosH = ABE_LEFT;
446 }
447 else
448 {
449 DeltaPt.cx = rcScreen.right - pt.x;
450 PosH = ABE_RIGHT;
451 }
452
453 if (pt.y < rcScreen.bottom / 2)
454 {
455 DeltaPt.cy = pt.y;
456 PosV = ABE_TOP;
457 }
458 else
459 {
460 DeltaPt.cy = rcScreen.bottom - pt.y;
461 PosV = ABE_BOTTOM;
462 }
463
464 Pos = (DeltaPt.cx * rcScreen.bottom < DeltaPt.cy * rcScreen.right) ? PosH : PosV;
465
466 /* Fix the screen origin to be relative to the primary monitor again */
467 OffsetRect(&rcScreen,
468 ScreenOffset.cx,
469 ScreenOffset.cy);
470
471 hMonNew = ITrayWindowImpl_GetMonitorFromRect(This,
472 &This->rcTrayWnd[Pos]);
473 if (hMon != hMonNew)
474 {
475 SIZE szTray;
476
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;
481
482 ITrayWindowImpl_GetTrayRectFromScreenRect(This,
483 Pos,
484 &rcScreen,
485 &szTray,
486 pRect);
487
488 hMon = hMonNew;
489 }
490 else
491 {
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];
495 }
496
497 *phMonitor = hMon;
498
499 return Pos;
500 }
501
502 static DWORD
503 ITrayWindowImpl_GetDraggingRectFromRect(IN OUT ITrayWindowImpl *This,
504 IN OUT RECT *pRect,
505 OUT HMONITOR *phMonitor)
506 {
507 POINT pt;
508
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);
514
515 return ITrayWindowImpl_GetDraggingRectFromPt(This,
516 pt,
517 pRect,
518 phMonitor);
519 }
520
521 static VOID
522 ITrayWindowImpl_ChangingWinPos(IN OUT ITrayWindowImpl *This,
523 IN OUT LPWINDOWPOS pwp)
524 {
525 RECT rcTray;
526
527 if (This->IsDragging)
528 {
529 rcTray.left = pwp->x;
530 rcTray.top = pwp->y;
531 rcTray.right = rcTray.left + pwp->cx;
532 rcTray.bottom = rcTray.top + pwp->cy;
533
534 if (!EqualRect(&rcTray,
535 &This->rcTrayWnd[This->DraggingPosition]))
536 {
537 /* Recalculate the rectangle, the user dragged the tray
538 window to another monitor or the window was somehow else
539 moved or resized */
540 This->DraggingPosition = ITrayWindowImpl_GetDraggingRectFromRect(This,
541 &rcTray,
542 &This->DraggingMonitor);
543 //This->rcTrayWnd[This->DraggingPosition] = rcTray;
544 }
545
546 //This->Monitor = ITrayWindowImpl_CalculateValidSize(This,
547 // This->DraggingPosition,
548 // &rcTray);
549
550 This->Monitor = This->DraggingMonitor;
551 This->Position = This->DraggingPosition;
552 This->IsDragging = FALSE;
553
554 This->rcTrayWnd[This->Position] = rcTray;
555 goto ChangePos;
556 }
557 else if (GetWindowRect(This->hWnd,
558 &rcTray))
559 {
560 if (This->InSizeMove)
561 {
562 if (!(pwp->flags & SWP_NOMOVE))
563 {
564 rcTray.left = pwp->x;
565 rcTray.top = pwp->y;
566 }
567
568 if (!(pwp->flags & SWP_NOSIZE))
569 {
570 rcTray.right = rcTray.left + pwp->cx;
571 rcTray.bottom = rcTray.top + pwp->cy;
572 }
573
574 This->Position = ITrayWindowImpl_GetDraggingRectFromRect(This,
575 &rcTray,
576 &This->Monitor);
577
578 if (!(pwp->flags & (SWP_NOMOVE | SWP_NOSIZE)))
579 {
580 SIZE szWnd;
581
582 szWnd.cx = pwp->cx;
583 szWnd.cy = pwp->cy;
584
585 ITrayWindowImpl_MakeTrayRectWithSize(This->Position,
586 &szWnd,
587 &rcTray);
588 }
589
590 This->rcTrayWnd[This->Position] = rcTray;
591 }
592 else
593 {
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];
598 }
599
600 ChangePos:
601 This->TraySize.cx = rcTray.right - rcTray.left;
602 This->TraySize.cy = rcTray.bottom - rcTray.top;
603
604 pwp->flags &= ~(SWP_NOMOVE | SWP_NOSIZE);
605 pwp->x = rcTray.left;
606 pwp->y = rcTray.top;
607 pwp->cx = This->TraySize.cx;
608 pwp->cy = This->TraySize.cy;
609 }
610 }
611
612 static VOID
613 ITrayWindowImpl_ApplyClipping(IN OUT ITrayWindowImpl *This,
614 IN BOOL Clip)
615 {
616 RECT rcClip, rcWindow;
617 HRGN hClipRgn;
618
619 if (GetWindowRect(This->hWnd,
620 &rcWindow))
621 {
622 /* Disable clipping on systems with only one monitor */
623 if (GetSystemMetrics(SM_CMONITORS) <= 1)
624 Clip = FALSE;
625
626 if (Clip)
627 {
628 rcClip = rcWindow;
629
630 ITrayWindowImpl_GetScreenRect(This,
631 This->Monitor,
632 &rcClip);
633
634 if (!IntersectRect(&rcClip,
635 &rcClip,
636 &rcWindow))
637 {
638 rcClip = rcWindow;
639 }
640
641 OffsetRect(&rcClip,
642 -rcWindow.left,
643 -rcWindow.top);
644
645 hClipRgn = CreateRectRgnIndirect(&rcClip);
646 }
647 else
648 hClipRgn = NULL;
649
650 /* Set the clipping region or make sure the window isn't clipped
651 by disabling it explicitly. */
652 SetWindowRgn(This->hWnd,
653 hClipRgn,
654 TRUE);
655 }
656 }
657
658 static VOID
659 ITrayWindowImpl_ResizeWorkArea(IN OUT ITrayWindowImpl *This)
660 {
661 RECT rcTray,rcWorkArea;
662
663 /* If monitor has changed then fix the previous monitors work area */
664 if (This->PreviousMonitor != This->Monitor)
665 {
666 ITrayWindowImpl_GetScreenRect(This,
667 This->PreviousMonitor,
668 &rcWorkArea);
669 SystemParametersInfo(SPI_SETWORKAREA,
670 1,
671 &rcWorkArea,
672 SPIF_SENDCHANGE);
673 }
674
675 rcTray = This->rcTrayWnd[This->Position];
676
677 ITrayWindowImpl_GetScreenRect(This,
678 This->Monitor,
679 &rcWorkArea);
680 This->PreviousMonitor = This->Monitor;
681
682 /* If AutoHide is false then change the workarea to exclude the area that
683 the taskbar covers. */
684 if (!This->AutoHide)
685 {
686 switch (This->Position)
687 {
688 case ABE_TOP:
689 rcWorkArea.top = rcTray.bottom;
690 break;
691 case ABE_LEFT:
692 rcWorkArea.left = rcTray.right;
693 break;
694 case ABE_RIGHT:
695 rcWorkArea.right = rcTray.left;
696 break;
697 case ABE_BOTTOM:
698 rcWorkArea.bottom = rcTray.top;
699 break;
700 }
701 }
702
703 SystemParametersInfo(SPI_SETWORKAREA,
704 1,
705 &rcWorkArea,
706 SPIF_SENDCHANGE);
707 }
708
709 static VOID
710 ITrayWindowImpl_CheckTrayWndPosition(IN OUT ITrayWindowImpl *This)
711 {
712 RECT rcTray;
713
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);
716
717 /* Move the tray window */
718 SetWindowPos(This->hWnd,
719 NULL,
720 rcTray.left,
721 rcTray.top,
722 rcTray.right - rcTray.left,
723 rcTray.bottom - rcTray.top,
724 SWP_NOZORDER);
725
726 ITrayWindowImpl_ResizeWorkArea(This);
727
728 ITrayWindowImpl_ApplyClipping(This,
729 TRUE);
730 }
731
732 typedef struct _TW_STUCKRECTS2
733 {
734 DWORD cbSize;
735 LONG Unknown;
736 DWORD dwFlags;
737 DWORD Position;
738 SIZE Size;
739 RECT Rect;
740 } TW_STRUCKRECTS2, *PTW_STUCKRECTS2;
741
742 static VOID
743 ITrayWindowImpl_RegLoadSettings(IN OUT ITrayWindowImpl *This)
744 {
745 DWORD Pos;
746 TW_STRUCKRECTS2 sr;
747 RECT rcScreen;
748 SIZE WndSize, EdgeSize, DlgFrameSize;
749 DWORD cbSize = sizeof(sr);
750
751 EdgeSize.cx = GetSystemMetrics(SM_CXEDGE);
752 EdgeSize.cy = GetSystemMetrics(SM_CYEDGE);
753 DlgFrameSize.cx = GetSystemMetrics(SM_CXDLGFRAME);
754 DlgFrameSize.cy = GetSystemMetrics(SM_CYDLGFRAME);
755
756 if (SHGetValue(hkExplorer,
757 TEXT("StuckRects2"),
758 TEXT("Settings"),
759 NULL,
760 &sr,
761 &cbSize) == ERROR_SUCCESS &&
762 sr.cbSize == sizeof(sr))
763 {
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;
768
769 /* FIXME: Are there more flags? */
770
771 if (This->hWnd != NULL)
772 SetWindowPos (This->hWnd,
773 This->AlwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
774 0,
775 0,
776 0,
777 0,
778 SWP_NOMOVE | SWP_NOSIZE);
779
780 if (sr.Position > ABE_BOTTOM)
781 This->Position = ABE_BOTTOM;
782 else
783 This->Position = sr.Position;
784
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. */
789 rcScreen = sr.Rect;
790 ITrayWindowImpl_GetScreenRectFromRect(This,
791 &rcScreen,
792 MONITOR_DEFAULTTONEAREST);
793 }
794 else
795 {
796 This->Position = ABE_BOTTOM;
797
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));
805
806 /* Use the primary screen by default */
807 rcScreen.left = 0;
808 rcScreen.top = 0;
809 rcScreen.right = GetSystemMetrics(SM_CXSCREEN);
810 rcScreen.bottom = GetSystemMetrics(SM_CYSCREEN);
811 ITrayWindowImpl_GetScreenRectFromRect(This,
812 &rcScreen,
813 MONITOR_DEFAULTTOPRIMARY);
814 }
815
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));
824
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;
829
830 /* Save the calculated size */
831 This->TraySize = WndSize;
832
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
835 usable results */
836 for (Pos = ABE_LEFT;
837 Pos <= ABE_BOTTOM;
838 Pos++)
839 {
840 ITrayWindowImpl_GetTrayRectFromScreenRect(This,
841 Pos,
842 &rcScreen,
843 &This->TraySize,
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);
846 }
847
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]);
852 }
853
854 static UINT
855 ITrayWindowImpl_TrackMenu(IN OUT ITrayWindowImpl *This,
856 IN HMENU hMenu,
857 IN POINT *ppt OPTIONAL,
858 IN HWND hwndExclude OPTIONAL,
859 IN BOOL TrackUp,
860 IN BOOL IsContextMenu)
861 {
862 TPMPARAMS tmp, *ptmp = NULL;
863 POINT pt;
864 UINT cmdId;
865 UINT fuFlags;
866
867 if (hwndExclude != NULL)
868 {
869 /* Get the client rectangle and map it to screen coordinates */
870 if (GetClientRect(hwndExclude,
871 &tmp.rcExclude) &&
872 MapWindowPoints(hwndExclude,
873 NULL,
874 (LPPOINT)&tmp.rcExclude,
875 2) != 0)
876 {
877 ptmp = &tmp;
878 }
879 }
880
881 if (ppt == NULL)
882 {
883 if (ptmp == NULL &&
884 GetClientRect(This->hWnd,
885 &tmp.rcExclude) &&
886 MapWindowPoints(This->hWnd,
887 NULL,
888 (LPPOINT)&tmp.rcExclude,
889 2) != 0)
890 {
891 ptmp = &tmp;
892 }
893
894 if (ptmp != NULL)
895 {
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;
901 }
902 else
903 pt.x = pt.y = 0;
904 }
905 else
906 pt = *ppt;
907
908 tmp.cbSize = sizeof(tmp);
909
910 fuFlags = TPM_RETURNCMD | TPM_VERTICAL;
911 fuFlags |= (TrackUp ? TPM_BOTTOMALIGN : TPM_TOPALIGN);
912 if (IsContextMenu)
913 fuFlags |= TPM_RIGHTBUTTON;
914 else
915 fuFlags |= (TrackUp ? TPM_VERNEGANIMATION : TPM_VERPOSANIMATION);
916
917 cmdId = TrackPopupMenuEx(hMenu,
918 fuFlags,
919 pt.x,
920 pt.y,
921 This->hWnd,
922 ptmp);
923
924 return cmdId;
925 }
926
927 static UINT
928 ITrayWindowImpl_TrackCtxMenu(IN OUT ITrayWindowImpl *This,
929 IN const TRAYWINDOW_CTXMENU *pMenu,
930 IN POINT *ppt OPTIONAL,
931 IN HWND hwndExclude OPTIONAL,
932 IN BOOL TrackUp,
933 IN PVOID Context OPTIONAL)
934 {
935 HMENU hPopup;
936 UINT cmdId = 0;
937 PVOID pcmContext = NULL;
938
939 hPopup = pMenu->CreateCtxMenu(This->hWnd,
940 &pcmContext,
941 Context);
942 if (hPopup != NULL)
943 {
944 cmdId = ITrayWindowImpl_TrackMenu(This,
945 hPopup,
946 ppt,
947 hwndExclude,
948 TrackUp,
949 TRUE);
950
951 pMenu->CtxMenuCommand(This->hWnd,
952 cmdId,
953 pcmContext,
954 Context);
955 }
956
957 return cmdId;
958 }
959
960 static VOID
961 ITrayWindowImpl_Free(ITrayWindowImpl *This)
962 {
963 HeapFree(hProcessHeap,
964 0,
965 This);
966 }
967
968
969 static ULONG STDMETHODCALLTYPE
970 ITrayWindowImpl_Release(IN OUT ITrayWindow *iface)
971 {
972 ITrayWindowImpl *This = impl_from_ITrayWindow(iface);
973 ULONG Ret;
974
975 Ret = InterlockedDecrement(&This->Ref);
976 if (Ret == 0)
977 ITrayWindowImpl_Free(This);
978
979 return Ret;
980 }
981
982 static VOID
983 ITrayWindowImpl_Destroy(ITrayWindowImpl *This)
984 {
985 (void)InterlockedExchangePointer((PVOID*)&This->hWnd,
986 NULL);
987
988 if (This->himlStartBtn != NULL)
989 {
990 ImageList_Destroy(This->himlStartBtn);
991 This->himlStartBtn = NULL;
992 }
993
994 if (This->hCaptionFont != NULL)
995 {
996 DeleteObject(This->hCaptionFont);
997 This->hCaptionFont = NULL;
998 }
999
1000 if (This->hStartBtnFont != NULL)
1001 {
1002 DeleteObject(This->hStartBtnFont);
1003 This->hStartBtnFont = NULL;
1004 }
1005
1006 if (This->hFont != NULL)
1007 {
1008 DeleteObject(This->hFont);
1009 This->hFont = NULL;
1010 }
1011
1012 if (This->StartMenuPopup != NULL)
1013 {
1014 IMenuPopup_Release(This->StartMenuPopup);
1015 This->StartMenuPopup = NULL;
1016 }
1017
1018 if (This->hbmStartMenu != NULL)
1019 {
1020 DeleteObject(This->hbmStartMenu);
1021 This->hbmStartMenu = NULL;
1022 }
1023
1024 if (This->StartMenuBand != NULL)
1025 {
1026 IMenuBand_Release(This->StartMenuBand);
1027 This->StartMenuBand = NULL;
1028 }
1029
1030 if (This->TrayBandSite != NULL)
1031 {
1032 /* FIXME: Unload bands */
1033 ITrayBandSite_Release(This->TrayBandSite);
1034 This->TrayBandSite = NULL;
1035 }
1036
1037 if (This->TaskbarTheme)
1038 {
1039 CloseThemeData(This->TaskbarTheme);
1040 This->TaskbarTheme = NULL;
1041 }
1042
1043 ITrayWindowImpl_Release(ITrayWindow_from_impl(This));
1044
1045 if (InterlockedDecrement(&TrayWndCount) == 0)
1046 PostQuitMessage(0);
1047 }
1048
1049 static ULONG STDMETHODCALLTYPE
1050 ITrayWindowImpl_AddRef(IN OUT ITrayWindow *iface)
1051 {
1052 ITrayWindowImpl *This = impl_from_ITrayWindow(iface);
1053
1054 return InterlockedIncrement(&This->Ref);
1055 }
1056
1057
1058 static BOOL
1059 ITrayWindowImpl_NCCreate(IN OUT ITrayWindowImpl *This)
1060 {
1061 ITrayWindowImpl_AddRef(ITrayWindow_from_impl(This));
1062
1063 return TRUE;
1064 }
1065
1066 static VOID
1067 ITrayWindowImpl_UpdateStartButton(IN OUT ITrayWindowImpl *This,
1068 IN HBITMAP hbmStart OPTIONAL)
1069 {
1070 SIZE Size = { 0, 0 };
1071
1072 if (This->himlStartBtn == NULL ||
1073 !SendMessage(This->hwndStart,
1074 BCM_GETIDEALSIZE,
1075 0,
1076 (LPARAM)&Size))
1077 {
1078 Size.cx = GetSystemMetrics(SM_CXEDGE);
1079 Size.cy = GetSystemMetrics(SM_CYEDGE);
1080
1081 if (hbmStart == NULL)
1082 {
1083 hbmStart = (HBITMAP)SendMessage(This->hwndStart,
1084 BM_GETIMAGE,
1085 IMAGE_BITMAP,
1086 0);
1087 }
1088
1089 if (hbmStart != NULL)
1090 {
1091 BITMAP bmp;
1092
1093 if (GetObject(hbmStart,
1094 sizeof(bmp),
1095 &bmp) != 0)
1096 {
1097 Size.cx += bmp.bmWidth;
1098 Size.cy += max(bmp.bmHeight,
1099 GetSystemMetrics(SM_CYCAPTION));
1100 }
1101 else
1102 {
1103 /* Huh?! Shouldn't happen... */
1104 goto DefSize;
1105 }
1106 }
1107 else
1108 {
1109 DefSize:
1110 Size.cx += GetSystemMetrics(SM_CXMINIMIZED);
1111 Size.cy += GetSystemMetrics(SM_CYCAPTION);
1112 }
1113 }
1114
1115 /* Save the size of the start button */
1116 This->StartBtnSize = Size;
1117 }
1118
1119 static VOID
1120 ITrayWindowImpl_AlignControls(IN OUT ITrayWindowImpl *This,
1121 IN PRECT prcClient OPTIONAL)
1122 {
1123 RECT rcClient;
1124 SIZE TraySize, StartSize;
1125 POINT ptTrayNotify = { 0, 0 };
1126 BOOL Horizontal;
1127 HDWP dwp;
1128
1129 ITrayWindowImpl_UpdateStartButton(This, NULL);
1130 if (prcClient != NULL)
1131 {
1132 rcClient = *prcClient;
1133 }
1134 else
1135 {
1136 if (!GetClientRect(This->hWnd,
1137 &rcClient))
1138 {
1139 return;
1140 }
1141 }
1142
1143 Horizontal = ITrayWindowImpl_IsPosHorizontal(This);
1144
1145 /* We're about to resize/move the start button, the rebar control and
1146 the tray notification control */
1147 dwp = BeginDeferWindowPos(3);
1148 if (dwp == NULL)
1149 return;
1150
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;
1155
1156 if (This->hwndStart != NULL)
1157 {
1158 /* Resize and reposition the button */
1159 dwp = DeferWindowPos(dwp,
1160 This->hwndStart,
1161 NULL,
1162 0,
1163 0,
1164 StartSize.cx,
1165 StartSize.cy,
1166 SWP_NOZORDER | SWP_NOACTIVATE);
1167 if (dwp == NULL)
1168 return;
1169 }
1170
1171 /* Determine the size that the tray notification window needs */
1172 if (Horizontal)
1173 {
1174 TraySize.cx = 0;
1175 TraySize.cy = rcClient.bottom;
1176 }
1177 else
1178 {
1179 TraySize.cx = rcClient.right;
1180 TraySize.cy = 0;
1181 }
1182
1183 if (This->hwndTrayNotify != NULL &&
1184 SendMessage(This->hwndTrayNotify,
1185 TNWM_GETMINIMUMSIZE,
1186 (WPARAM)Horizontal,
1187 (LPARAM)&TraySize))
1188 {
1189 /* Move the tray notification window to the desired location */
1190 if (Horizontal)
1191 ptTrayNotify.x = rcClient.right - TraySize.cx;
1192 else
1193 ptTrayNotify.y = rcClient.bottom - TraySize.cy;
1194
1195 dwp = DeferWindowPos(dwp,
1196 This->hwndTrayNotify,
1197 NULL,
1198 ptTrayNotify.x,
1199 ptTrayNotify.y,
1200 TraySize.cx,
1201 TraySize.cy,
1202 SWP_NOZORDER | SWP_NOACTIVATE);
1203 if (dwp == NULL)
1204 return;
1205 }
1206
1207 /* Resize/Move the rebar control */
1208 if (This->hwndRebar != NULL)
1209 {
1210 POINT ptRebar = { 0, 0 };
1211 SIZE szRebar;
1212
1213 SetWindowStyle(This->hwndRebar,
1214 CCS_VERT,
1215 Horizontal ? 0 : CCS_VERT);
1216
1217 if (Horizontal)
1218 {
1219 ptRebar.x = StartSize.cx + GetSystemMetrics(SM_CXSIZEFRAME);
1220 szRebar.cx = ptTrayNotify.x - ptRebar.x;
1221 szRebar.cy = rcClient.bottom;
1222 }
1223 else
1224 {
1225 ptRebar.y = StartSize.cy + GetSystemMetrics(SM_CYSIZEFRAME);
1226 szRebar.cx = rcClient.right;
1227 szRebar.cy = ptTrayNotify.y - ptRebar.y;
1228 }
1229
1230 dwp = DeferWindowPos(dwp,
1231 This->hwndRebar,
1232 NULL,
1233 ptRebar.x,
1234 ptRebar.y,
1235 szRebar.cx,
1236 szRebar.cy,
1237 SWP_NOZORDER | SWP_NOACTIVATE);
1238 }
1239
1240 if (dwp != NULL)
1241 EndDeferWindowPos(dwp);
1242
1243 if (This->hwndTaskSwitch != NULL)
1244 {
1245 /* Update the task switch window configuration */
1246 SendMessage(This->hwndTaskSwitch,
1247 TSWM_UPDATETASKBARPOS,
1248 0,
1249 0);
1250 }
1251 }
1252
1253 static BOOL
1254 ITrayWindowImpl_CreateStartBtnImageList(IN OUT ITrayWindowImpl *This)
1255 {
1256 HICON hIconStart;
1257 SIZE IconSize;
1258
1259 if (This->himlStartBtn != NULL)
1260 return TRUE;
1261
1262 IconSize.cx = GetSystemMetrics(SM_CXSMICON);
1263 IconSize.cy = GetSystemMetrics(SM_CYSMICON);
1264
1265 /* Load the start button icon and create a image list for it */
1266 hIconStart = LoadImage(hExplorerInstance,
1267 MAKEINTRESOURCE(IDI_START),
1268 IMAGE_ICON,
1269 IconSize.cx,
1270 IconSize.cy,
1271 LR_SHARED | LR_DEFAULTCOLOR);
1272
1273 if (hIconStart != NULL)
1274 {
1275 This->himlStartBtn = ImageList_Create(IconSize.cx,
1276 IconSize.cy,
1277 ILC_COLOR32 | ILC_MASK,
1278 1,
1279 1);
1280 if (This->himlStartBtn != NULL)
1281 {
1282 if (ImageList_AddIcon(This->himlStartBtn,
1283 hIconStart) >= 0)
1284 {
1285 return TRUE;
1286 }
1287
1288 /* Failed to add the icon! */
1289 ImageList_Destroy(This->himlStartBtn);
1290 This->himlStartBtn = NULL;
1291 }
1292 }
1293
1294 return FALSE;
1295 }
1296
1297 static HBITMAP
1298 ITrayWindowImpl_CreateStartButtonBitmap(IN OUT ITrayWindowImpl *This)
1299 {
1300 TCHAR szStartCaption[32];
1301 HFONT hFontOld;
1302 HDC hDC = NULL;
1303 HDC hDCScreen = NULL;
1304 SIZE Size, SmallIcon;
1305 HBITMAP hbmpOld, hbmp = NULL;
1306 HBITMAP hBitmap = NULL;
1307 HICON hIconStart;
1308 BOOL Ret;
1309 UINT Flags;
1310 RECT rcButton;
1311
1312 /* NOTE: This is the backwards compatibility code that is used if the
1313 Common Controls Version 6.0 are not available! */
1314
1315 if (!LoadString(hExplorerInstance,
1316 IDS_START,
1317 szStartCaption,
1318 sizeof(szStartCaption) / sizeof(szStartCaption[0])))
1319 {
1320 return NULL;
1321 }
1322
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),
1328 IMAGE_ICON,
1329 SmallIcon.cx,
1330 SmallIcon.cy,
1331 LR_SHARED | LR_DEFAULTCOLOR);
1332
1333 hDCScreen = GetDC(NULL);
1334 if (hDCScreen == NULL)
1335 goto Cleanup;
1336
1337 hDC = CreateCompatibleDC(hDCScreen);
1338 if (hDC == NULL)
1339 goto Cleanup;
1340
1341 hFontOld = SelectObject(hDC,
1342 This->hStartBtnFont);
1343
1344 Ret = GetTextExtentPoint32(hDC,
1345 szStartCaption,
1346 _tcslen(szStartCaption),
1347 &Size);
1348
1349 SelectObject(hDC,
1350 hFontOld);
1351 if (!Ret)
1352 goto Cleanup;
1353
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,
1358 SmallIcon.cy);
1359
1360 /* Create the bitmap */
1361 hbmp = CreateCompatibleBitmap(hDCScreen,
1362 Size.cx,
1363 Size.cy);
1364 if (hbmp == NULL)
1365 goto Cleanup;
1366
1367 /* Caluclate the button rect */
1368 rcButton.left = 0;
1369 rcButton.top = 0;
1370 rcButton.right = Size.cx;
1371 rcButton.bottom = Size.cy;
1372
1373 /* Draw the button */
1374 hbmpOld = SelectObject(hDC,
1375 hbmp);
1376
1377 Flags = DC_TEXT | DC_INBUTTON;
1378 if (hIconStart != NULL)
1379 Flags |= DC_ICON;
1380
1381 if (DrawCapTemp != NULL)
1382 {
1383 Ret = DrawCapTemp(NULL,
1384 hDC,
1385 &rcButton,
1386 This->hStartBtnFont,
1387 hIconStart,
1388 szStartCaption,
1389 Flags);
1390 }
1391
1392 SelectObject(hDC,
1393 hbmpOld);
1394
1395 if (!Ret)
1396 goto Cleanup;
1397
1398 /* We successfully created the bitmap! */
1399 hBitmap = hbmp;
1400 hbmp = NULL;
1401
1402 Cleanup:
1403 if (hDCScreen != NULL)
1404 {
1405 ReleaseDC(NULL,
1406 hDCScreen);
1407 }
1408
1409 if (hbmp != NULL)
1410 DeleteObject(hbmp);
1411
1412 if (hDC != NULL)
1413 DeleteDC(hDC);
1414
1415 return hBitmap;
1416 }
1417
1418 static VOID
1419 ITrayWindowImpl_UpdateTheme(IN OUT ITrayWindowImpl *This)
1420 {
1421 if (This->TaskbarTheme)
1422 CloseThemeData(This->TaskbarTheme);
1423
1424 if (IsThemeActive())
1425 This->TaskbarTheme = OpenThemeData(This->hWnd, L"Taskbar");
1426 else
1427 This->TaskbarTheme = 0;
1428 }
1429
1430 static VOID
1431 ITrayWindowImpl_Create(IN OUT ITrayWindowImpl *This)
1432 {
1433 TCHAR szStartCaption[32];
1434
1435 SetWindowTheme(This->hWnd, L"TaskBar", NULL);
1436 ITrayWindowImpl_UpdateTheme(This);
1437
1438 InterlockedIncrement(&TrayWndCount);
1439
1440 if (!LoadString(hExplorerInstance,
1441 IDS_START,
1442 szStartCaption,
1443 sizeof(szStartCaption) / sizeof(szStartCaption[0])))
1444 {
1445 szStartCaption[0] = TEXT('\0');
1446 }
1447
1448 if (This->hStartBtnFont == NULL || This->hCaptionFont == NULL)
1449 {
1450 NONCLIENTMETRICS ncm;
1451
1452 /* Get the system fonts, we use the caption font,
1453 always bold, though. */
1454 ncm.cbSize = sizeof(ncm);
1455 if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS,
1456 sizeof(ncm),
1457 &ncm,
1458 FALSE))
1459 {
1460 if (This->hCaptionFont == NULL)
1461 {
1462 ncm.lfCaptionFont.lfWeight = FW_NORMAL;
1463 This->hCaptionFont = CreateFontIndirect(&ncm.lfCaptionFont);
1464 }
1465
1466 if (This->hStartBtnFont == NULL)
1467 {
1468 ncm.lfCaptionFont.lfWeight = FW_BOLD;
1469 This->hStartBtnFont = CreateFontIndirect(&ncm.lfCaptionFont);
1470 }
1471 }
1472 }
1473
1474 /* Create the Start button */
1475 This->hwndStart = CreateWindowEx(0,
1476 WC_BUTTON,
1477 szStartCaption,
1478 WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS |
1479 BS_PUSHBUTTON | BS_CENTER | BS_VCENTER | BS_BITMAP,
1480 0,
1481 0,
1482 0,
1483 0,
1484 This->hWnd,
1485 (HMENU)IDC_STARTBTN,
1486 hExplorerInstance,
1487 NULL);
1488 if (This->hwndStart)
1489 {
1490 SetWindowTheme(This->hwndStart, L"Start", NULL);
1491 SendMessage(This->hwndStart,
1492 WM_SETFONT,
1493 (WPARAM)This->hStartBtnFont,
1494 FALSE);
1495
1496 if (ITrayWindowImpl_CreateStartBtnImageList(This))
1497 {
1498 BUTTON_IMAGELIST bil;
1499
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;
1506
1507 if (!SendMessage(This->hwndStart,
1508 BCM_SETIMAGELIST,
1509 0,
1510 (LPARAM)&bil))
1511 {
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;
1516
1517 goto SetStartBtnImage;
1518 }
1519
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,
1524 0);
1525
1526 ITrayWindowImpl_UpdateStartButton(This,
1527 NULL);
1528 }
1529 else
1530 {
1531 HBITMAP hbmStart, hbmOld;
1532
1533 SetStartBtnImage:
1534 hbmStart = ITrayWindowImpl_CreateStartButtonBitmap(This);
1535 if (hbmStart != NULL)
1536 {
1537 ITrayWindowImpl_UpdateStartButton(This,
1538 hbmStart);
1539
1540 hbmOld = (HBITMAP)SendMessage(This->hwndStart,
1541 BM_SETIMAGE,
1542 IMAGE_BITMAP,
1543 (LPARAM)hbmStart);
1544
1545 if (hbmOld != NULL)
1546 DeleteObject(hbmOld);
1547 }
1548 }
1549 }
1550
1551 /* Load the saved tray window settings */
1552 ITrayWindowImpl_RegLoadSettings(This);
1553
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,
1559 This->hbmStartMenu,
1560 0);
1561
1562 /* Load the tray band site */
1563 if (This->TrayBandSite != NULL)
1564 {
1565 ITrayBandSite_Release(This->TrayBandSite);
1566 }
1567
1568 This->TrayBandSite = CreateTrayBandSite(ITrayWindow_from_impl(This),
1569 &This->hwndRebar,
1570 &This->hwndTaskSwitch);
1571 SetWindowTheme(This->hwndRebar, L"TaskBar", NULL);
1572
1573 /* Create the tray notification window */
1574 This->hwndTrayNotify = CreateTrayNotifyWnd(ITrayWindow_from_impl(This),
1575 This->HideClock);
1576
1577 if (ITrayWindowImpl_UpdateNonClientMetrics(This))
1578 {
1579 ITrayWindowImpl_SetWindowsFont(This);
1580 }
1581
1582 /* Move the tray window to the right position and resize it if neccessary */
1583 ITrayWindowImpl_CheckTrayWndPosition(This);
1584
1585 /* Align all controls on the tray window */
1586 ITrayWindowImpl_AlignControls(This,
1587 NULL);
1588 }
1589
1590 static HRESULT STDMETHODCALLTYPE
1591 ITrayWindowImpl_QueryInterface(IN OUT ITrayWindow *iface,
1592 IN REFIID riid,
1593 OUT LPVOID *ppvObj)
1594 {
1595 ITrayWindowImpl *This;
1596
1597 if (ppvObj == NULL)
1598 return E_POINTER;
1599
1600 This = impl_from_ITrayWindow(iface);
1601
1602 if (IsEqualIID(riid,
1603 &IID_IUnknown))
1604 {
1605 *ppvObj = IUnknown_from_impl(This);
1606 }
1607 else if (IsEqualIID(riid,
1608 &IID_IShellDesktopTray))
1609 {
1610 *ppvObj = IShellDesktopTray_from_impl(This);
1611 }
1612 else
1613 {
1614 *ppvObj = NULL;
1615 return E_NOINTERFACE;
1616 }
1617
1618 ITrayWindowImpl_AddRef(iface);
1619 return S_OK;
1620 }
1621
1622 static ITrayWindowImpl *
1623 ITrayWindowImpl_Construct(VOID)
1624 {
1625 ITrayWindowImpl *This;
1626
1627 This = HeapAlloc(hProcessHeap,
1628 HEAP_ZERO_MEMORY,
1629 sizeof(*This));
1630 if (This == NULL)
1631 return NULL;
1632
1633 This->lpVtbl = &ITrayWindowImpl_Vtbl;
1634 This->lpVtblShellDesktopTray = &IShellDesktopTrayImpl_Vtbl;
1635 This->Ref = 1;
1636 This->Position = (DWORD)-1;
1637
1638 return This;
1639 }
1640
1641 static HRESULT STDMETHODCALLTYPE
1642 ITrayWindowImpl_Open(IN OUT ITrayWindow *iface)
1643 {
1644 ITrayWindowImpl *This = impl_from_ITrayWindow(iface);
1645 HRESULT Ret = S_OK;
1646 HWND hWnd;
1647 DWORD dwExStyle;
1648
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)
1652 {
1653 if (IsWindow(This->hWnd))
1654 {
1655 if (!IsWindowVisible(This->hWnd))
1656 {
1657 ITrayWindowImpl_CheckTrayWndPosition(This);
1658
1659 ShowWindow(This->hWnd,
1660 SW_SHOW);
1661 }
1662 }
1663 else
1664 goto TryCreateTrayWnd;
1665 }
1666 else
1667 {
1668 RECT rcWnd;
1669
1670 TryCreateTrayWnd:
1671 dwExStyle = WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE;
1672 if (This->AlwaysOnTop)
1673 dwExStyle |= WS_EX_TOPMOST;
1674
1675 if (This->Position != (DWORD)-1)
1676 rcWnd = This->rcTrayWnd[This->Position];
1677 else
1678 {
1679 ZeroMemory(&rcWnd,
1680 sizeof(rcWnd));
1681 }
1682
1683 hWnd = CreateWindowEx(dwExStyle,
1684 szTrayWndClass,
1685 NULL,
1686 WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN |
1687 WS_BORDER | WS_THICKFRAME,
1688 rcWnd.left,
1689 rcWnd.top,
1690 rcWnd.right - rcWnd.left,
1691 rcWnd.bottom - rcWnd.top,
1692 NULL,
1693 NULL,
1694 hExplorerInstance,
1695 This);
1696 if (hWnd == NULL)
1697 Ret = E_FAIL;
1698 }
1699
1700 return Ret;
1701 }
1702
1703 static HRESULT STDMETHODCALLTYPE
1704 ITrayWindowImpl_Close(IN OUT ITrayWindow *iface)
1705 {
1706 ITrayWindowImpl *This = impl_from_ITrayWindow(iface);
1707
1708 if (This->hWnd != NULL)
1709 {
1710 SendMessage(This->hWnd,
1711 WM_APP_TRAYDESTROY,
1712 0,
1713 0);
1714 }
1715
1716 return S_OK;
1717 }
1718
1719 static HWND STDMETHODCALLTYPE
1720 ITrayWindowImpl_GetHWND(IN OUT ITrayWindow *iface)
1721 {
1722 ITrayWindowImpl *This = impl_from_ITrayWindow(iface);
1723
1724 return This->hWnd;
1725 }
1726
1727 static BOOL STDMETHODCALLTYPE
1728 ITrayWindowImpl_IsSpecialHWND(IN OUT ITrayWindow *iface,
1729 IN HWND hWnd)
1730 {
1731 ITrayWindowImpl *This = impl_from_ITrayWindow(iface);
1732
1733 return (hWnd == This->hWnd ||
1734 (This->hWndDesktop != NULL && hWnd == This->hWndDesktop));
1735 }
1736
1737 static BOOL STDMETHODCALLTYPE
1738 ITrayWindowImpl_IsHorizontal(IN OUT ITrayWindow *iface)
1739 {
1740 ITrayWindowImpl *This = impl_from_ITrayWindow(iface);
1741 return ITrayWindowImpl_IsPosHorizontal(This);
1742 }
1743
1744 static HFONT STDMETHODCALLTYPE
1745 ITrayWIndowImpl_GetCaptionFonts(IN OUT ITrayWindow *iface,
1746 OUT HFONT *phBoldCaption OPTIONAL)
1747 {
1748 ITrayWindowImpl *This = impl_from_ITrayWindow(iface);
1749
1750 if (phBoldCaption != NULL)
1751 *phBoldCaption = This->hStartBtnFont;
1752
1753 return This->hCaptionFont;
1754 }
1755
1756 static DWORD WINAPI
1757 TrayPropertiesThread(IN OUT PVOID pParam)
1758 {
1759 ITrayWindowImpl *This = pParam;
1760 HWND hwnd;
1761 RECT posRect;
1762
1763 GetWindowRect(This->hwndStart, &posRect);
1764 hwnd = CreateWindowEx(0,
1765 WC_STATIC,
1766 NULL,
1767 WS_OVERLAPPED | WS_DISABLED | WS_CLIPSIBLINGS | WS_BORDER | SS_LEFT,
1768 posRect.left,
1769 posRect.top,
1770 posRect.right - posRect.left,
1771 posRect.bottom - posRect.top,
1772 NULL,
1773 NULL,
1774 NULL,
1775 NULL);
1776
1777 This->hwndTrayPropertiesOwner = hwnd;
1778
1779 DisplayTrayProperties(hwnd);
1780
1781 This->hwndTrayPropertiesOwner = NULL;
1782 DestroyWindow(hwnd);
1783
1784 return 0;
1785 }
1786
1787 static HWND STDMETHODCALLTYPE
1788 ITrayWindowImpl_DisplayProperties(IN OUT ITrayWindow *iface)
1789 {
1790 ITrayWindowImpl *This = impl_from_ITrayWindow(iface);
1791 HWND hTrayProp;
1792
1793 if (This->hwndTrayPropertiesOwner)
1794 {
1795 hTrayProp = GetLastActivePopup(This->hwndTrayPropertiesOwner);
1796 if (hTrayProp != NULL &&
1797 hTrayProp != This->hwndTrayPropertiesOwner)
1798 {
1799 SetForegroundWindow(hTrayProp);
1800 return NULL;
1801 }
1802 }
1803
1804 CloseHandle(CreateThread(NULL, 0, TrayPropertiesThread, This, 0, NULL));
1805 return NULL;
1806 }
1807
1808 static VOID
1809 OpenCommonStartMenuDirectory(IN HWND hWndOwner,
1810 IN LPCTSTR lpOperation)
1811 {
1812 TCHAR szDir[MAX_PATH];
1813
1814 if (SHGetSpecialFolderPath(hWndOwner,
1815 szDir,
1816 CSIDL_COMMON_STARTMENU,
1817 FALSE))
1818 {
1819 ShellExecute(hWndOwner,
1820 lpOperation,
1821 NULL,
1822 NULL,
1823 szDir,
1824 SW_SHOWNORMAL);
1825 }
1826 }
1827
1828 static VOID
1829 OpenTaskManager(IN HWND hWndOwner)
1830 {
1831 ShellExecute(hWndOwner,
1832 TEXT("open"),
1833 TEXT("taskmgr.exe"),
1834 NULL,
1835 NULL,
1836 SW_SHOWNORMAL);
1837 }
1838
1839 static BOOL STDMETHODCALLTYPE
1840 ITrayWindowImpl_ExecContextMenuCmd(IN OUT ITrayWindow *iface,
1841 IN UINT uiCmd)
1842 {
1843 ITrayWindowImpl *This = impl_from_ITrayWindow(iface);
1844 BOOL bHandled = TRUE;
1845
1846 switch (uiCmd)
1847 {
1848 case ID_SHELL_CMD_PROPERTIES:
1849 ITrayWindow_DisplayProperties(iface);
1850 break;
1851
1852 case ID_SHELL_CMD_OPEN_ALL_USERS:
1853 OpenCommonStartMenuDirectory(This->hWnd,
1854 TEXT("open"));
1855 break;
1856
1857 case ID_SHELL_CMD_EXPLORE_ALL_USERS:
1858 OpenCommonStartMenuDirectory(This->hWnd,
1859 TEXT("explore"));
1860 break;
1861
1862 case ID_LOCKTASKBAR:
1863 if (SHRestricted(REST_CLASSICSHELL) == 0)
1864 {
1865 ITrayWindow_Lock(iface,
1866 !This->Locked);
1867 }
1868 break;
1869
1870 case ID_SHELL_CMD_OPEN_TASKMGR:
1871 OpenTaskManager(This->hWnd);
1872 break;
1873
1874 case ID_SHELL_CMD_UNDO_ACTION:
1875 break;
1876
1877 case ID_SHELL_CMD_SHOW_DESKTOP:
1878 break;
1879
1880 case ID_SHELL_CMD_TILE_WND_H:
1881 TileWindows(NULL, MDITILE_HORIZONTAL, NULL, 0, NULL);
1882 break;
1883
1884 case ID_SHELL_CMD_TILE_WND_V:
1885 TileWindows(NULL, MDITILE_VERTICAL, NULL, 0, NULL);
1886 break;
1887
1888 case ID_SHELL_CMD_CASCADE_WND:
1889 CascadeWindows(NULL, MDITILE_SKIPDISABLED, NULL, 0, NULL);
1890 break;
1891
1892 case ID_SHELL_CMD_CUST_NOTIF:
1893 break;
1894
1895 case ID_SHELL_CMD_ADJUST_DAT:
1896 LaunchCPanel(NULL, TEXT("timedate.cpl"));
1897 break;
1898
1899 default:
1900 DbgPrint("ITrayWindow::ExecContextMenuCmd(%u): Unhandled Command ID!\n", uiCmd);
1901 bHandled = FALSE;
1902 break;
1903 }
1904
1905 return bHandled;
1906 }
1907
1908 static BOOL STDMETHODCALLTYPE
1909 ITrayWindowImpl_Lock(IN OUT ITrayWindow *iface,
1910 IN BOOL bLock)
1911 {
1912 BOOL bPrevLock;
1913 ITrayWindowImpl *This = impl_from_ITrayWindow(iface);
1914
1915 bPrevLock = This->Locked;
1916 if (This->Locked != bLock)
1917 {
1918 This->Locked = bLock;
1919
1920 if (This->TrayBandSite != NULL)
1921 {
1922 if (!SUCCEEDED(ITrayBandSite_Lock(This->TrayBandSite,
1923 bLock)))
1924 {
1925 /* Reset?? */
1926 This->Locked = bPrevLock;
1927 }
1928 }
1929 }
1930
1931 return bPrevLock;
1932 }
1933
1934 static const ITrayWindowVtbl ITrayWindowImpl_Vtbl =
1935 {
1936 /* IUnknown */
1937 ITrayWindowImpl_QueryInterface,
1938 ITrayWindowImpl_AddRef,
1939 ITrayWindowImpl_Release,
1940 /* ITrayWindow */
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
1950 };
1951
1952 static int
1953 ITrayWindowImpl_DrawBackground(IN ITrayWindowImpl *This,
1954 IN HDC dc)
1955 {
1956 int backoundPart;
1957 RECT rect;
1958
1959 GetClientRect(This->hWnd, &rect);
1960 switch (This->Position)
1961 {
1962 case ABE_LEFT:
1963 backoundPart = TBP_BACKGROUNDLEFT;
1964 break;
1965 case ABE_TOP:
1966 backoundPart = TBP_BACKGROUNDTOP;
1967 break;
1968 case ABE_RIGHT:
1969 backoundPart = TBP_BACKGROUNDRIGHT;
1970 break;
1971 case ABE_BOTTOM:
1972 default:
1973 backoundPart = TBP_BACKGROUNDBOTTOM;
1974 break;
1975 }
1976 DrawThemeBackground(This->TaskbarTheme, dc, backoundPart, 0, &rect, 0);
1977 return 0;
1978 }
1979
1980 static int
1981 ITrayWindowImpl_DrawSizer(IN ITrayWindowImpl *This,
1982 IN HRGN hRgn)
1983 {
1984 HDC hdc;
1985 RECT rect;
1986 int backoundPart;
1987
1988 GetWindowRect(This->hWnd, &rect);
1989 OffsetRect(&rect, -rect.left, -rect.top);
1990
1991 hdc = GetDCEx(This->hWnd, hRgn, DCX_WINDOW | DCX_INTERSECTRGN | DCX_PARENTCLIP);
1992
1993 switch (This->Position)
1994 {
1995 case ABE_LEFT:
1996 backoundPart = TBP_SIZINGBARLEFT;
1997 rect.left = rect.right - GetSystemMetrics(SM_CXSIZEFRAME);
1998 break;
1999 case ABE_TOP:
2000 backoundPart = TBP_SIZINGBARTOP;
2001 rect.top = rect.bottom - GetSystemMetrics(SM_CYSIZEFRAME);
2002 break;
2003 case ABE_RIGHT:
2004 backoundPart = TBP_SIZINGBARRIGHT;
2005 rect.right = rect.left + GetSystemMetrics(SM_CXSIZEFRAME);
2006 break;
2007 case ABE_BOTTOM:
2008 default:
2009 backoundPart = TBP_SIZINGBARBOTTOM;
2010 rect.bottom = rect.top + GetSystemMetrics(SM_CYSIZEFRAME);
2011 break;
2012 }
2013
2014 DrawThemeBackground(This->TaskbarTheme, hdc, backoundPart, 0, &rect, 0);
2015
2016 ReleaseDC(This->hWnd, hdc);
2017 return 0;
2018 }
2019
2020 static DWORD WINAPI
2021 RunFileDlgThread(IN OUT PVOID pParam)
2022 {
2023 ITrayWindowImpl *This = pParam;
2024 HANDLE hShell32;
2025 RUNFILEDLG RunFileDlg;
2026 HWND hwnd;
2027 RECT posRect;
2028
2029 GetWindowRect(This->hwndStart,&posRect);
2030
2031 hwnd = CreateWindowEx(0,
2032 WC_STATIC,
2033 NULL,
2034 WS_OVERLAPPED | WS_DISABLED | WS_CLIPSIBLINGS | WS_BORDER | SS_LEFT,
2035 posRect.left,
2036 posRect.top,
2037 posRect.right - posRect.left,
2038 posRect.bottom - posRect.top,
2039 NULL,
2040 NULL,
2041 NULL,
2042 NULL);
2043
2044 This->hwndRunFileDlgOwner = hwnd;
2045
2046 hShell32 = GetModuleHandle(TEXT("SHELL32.DLL"));
2047 RunFileDlg = (RUNFILEDLG)GetProcAddress(hShell32, (LPCSTR)61);
2048
2049 RunFileDlg(hwnd, NULL, NULL, NULL, NULL, RFF_CALCDIRECTORY);
2050
2051 This->hwndRunFileDlgOwner = NULL;
2052 DestroyWindow(hwnd);
2053
2054 return 0;
2055 }
2056
2057 static void
2058 ITrayWindowImpl_DisplayRunFileDlg(IN ITrayWindowImpl *This)
2059 {
2060 HWND hRunDlg;
2061 if (This->hwndRunFileDlgOwner)
2062 {
2063 hRunDlg = GetLastActivePopup(This->hwndRunFileDlgOwner);
2064 if (hRunDlg != NULL &&
2065 hRunDlg != This->hwndRunFileDlgOwner)
2066 {
2067 SetForegroundWindow(hRunDlg);
2068 return;
2069 }
2070 }
2071
2072 CloseHandle(CreateThread(NULL, 0, RunFileDlgThread, This, 0, NULL));
2073 }
2074
2075 static LRESULT CALLBACK
2076 TrayWndProc(IN HWND hwnd,
2077 IN UINT uMsg,
2078 IN WPARAM wParam,
2079 IN LPARAM lParam)
2080 {
2081 ITrayWindowImpl *This = NULL;
2082 LRESULT Ret = FALSE;
2083
2084 if (uMsg != WM_NCCREATE)
2085 {
2086 This = (ITrayWindowImpl*)GetWindowLongPtr(hwnd,
2087 0);
2088 }
2089
2090 if (This != NULL || uMsg == WM_NCCREATE)
2091 {
2092 if (This != NULL && This->StartMenuBand != NULL)
2093 {
2094 MSG Msg;
2095 LRESULT lRet;
2096
2097 Msg.hwnd = hwnd;
2098 Msg.message = uMsg;
2099 Msg.wParam = wParam;
2100 Msg.lParam = lParam;
2101
2102 if (IMenuBand_TranslateMenuMessage(This->StartMenuBand,
2103 &Msg,
2104 &lRet) == S_OK)
2105 {
2106 return lRet;
2107 }
2108
2109 wParam = Msg.wParam;
2110 lParam = Msg.lParam;
2111 }
2112
2113 switch (uMsg)
2114 {
2115 case WM_COPYDATA:
2116 {
2117 if (This->hwndTrayNotify)
2118 {
2119 TrayNotify_NotifyMsg(This->hwndTrayNotify,
2120 wParam,
2121 lParam);
2122 }
2123 return TRUE;
2124 }
2125 case WM_THEMECHANGED:
2126 ITrayWindowImpl_UpdateTheme(This);
2127 return 0;
2128 case WM_NCPAINT:
2129 if (!This->TaskbarTheme)
2130 goto DefHandler;
2131 return ITrayWindowImpl_DrawSizer(This,
2132 (HRGN)wParam);
2133 case WM_ERASEBKGND:
2134 if (!This->TaskbarTheme)
2135 goto DefHandler;
2136 return ITrayWindowImpl_DrawBackground(This,
2137 (HDC)wParam);
2138 case WM_CTLCOLORBTN:
2139 SetBkMode((HDC)wParam, TRANSPARENT);
2140 return (LRESULT)GetStockObject(HOLLOW_BRUSH);
2141 case WM_NCHITTEST:
2142 {
2143 RECT rcClient;
2144 POINT pt;
2145
2146 if (This->Locked)
2147 {
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. */
2151 return HTBORDER;
2152 }
2153
2154 SetLastError(ERROR_SUCCESS);
2155 if (GetClientRect(hwnd,
2156 &rcClient) &&
2157 (MapWindowPoints(hwnd,
2158 NULL,
2159 (LPPOINT)&rcClient,
2160 2) != 0 || GetLastError() == ERROR_SUCCESS))
2161 {
2162 pt.x = (SHORT)LOWORD(lParam);
2163 pt.y = (SHORT)HIWORD(lParam);
2164
2165 if (PtInRect(&rcClient,
2166 pt))
2167 {
2168 /* The user is trying to drag the tray window */
2169 return HTCAPTION;
2170 }
2171
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)
2175 {
2176 case ABE_TOP:
2177 if (pt.y > rcClient.bottom)
2178 return HTBOTTOM;
2179 break;
2180 case ABE_LEFT:
2181 if (pt.x > rcClient.right)
2182 return HTRIGHT;
2183 break;
2184 case ABE_RIGHT:
2185 if (pt.x < rcClient.left)
2186 return HTLEFT;
2187 break;
2188 case ABE_BOTTOM:
2189 default:
2190 if (pt.y < rcClient.top)
2191 return HTTOP;
2192 break;
2193 }
2194 }
2195 return HTBORDER;
2196 }
2197 case WM_MOVING:
2198 {
2199 POINT ptCursor;
2200 PRECT pRect = (PRECT)lParam;
2201
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))
2208 {
2209 This->IsDragging = TRUE;
2210 This->DraggingPosition = ITrayWindowImpl_GetDraggingRectFromPt(This,
2211 ptCursor,
2212 pRect,
2213 &This->DraggingMonitor);
2214 }
2215 else
2216 {
2217 *pRect = This->rcTrayWnd[This->Position];
2218 }
2219 return TRUE;
2220 }
2221
2222 case WM_SIZING:
2223 {
2224 PRECT pRect = (PRECT)lParam;
2225
2226 if (!This->Locked)
2227 {
2228 ITrayWindowImpl_CalculateValidSize(This,
2229 This->Position,
2230 pRect);
2231 }
2232 else
2233 {
2234 *pRect = This->rcTrayWnd[This->Position];
2235 }
2236 return TRUE;
2237 }
2238
2239 case WM_WINDOWPOSCHANGING:
2240 {
2241 ITrayWindowImpl_ChangingWinPos(This,
2242 (LPWINDOWPOS)lParam);
2243 break;
2244 }
2245
2246 case WM_SIZE:
2247 {
2248 RECT rcClient;
2249 InvalidateRect(This->hWnd, NULL, TRUE);
2250 if (wParam == SIZE_RESTORED && lParam == 0)
2251 {
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,
2256 TRUE);
2257
2258 if (!GetClientRect(This->hWnd,
2259 &rcClient))
2260 {
2261 break;
2262 }
2263 }
2264 else
2265 {
2266 rcClient.left = rcClient.top = 0;
2267 rcClient.right = LOWORD(lParam);
2268 rcClient.bottom = HIWORD(lParam);
2269 }
2270
2271 ITrayWindowImpl_AlignControls(This,
2272 &rcClient);
2273 break;
2274 }
2275
2276 case WM_ENTERSIZEMOVE:
2277 This->InSizeMove = TRUE;
2278 This->IsDragging = FALSE;
2279 if (!This->Locked)
2280 {
2281 /* Remove the clipping on multi monitor systems while dragging around */
2282 ITrayWindowImpl_ApplyClipping(This,
2283 FALSE);
2284 }
2285 break;
2286
2287 case WM_EXITSIZEMOVE:
2288 This->InSizeMove = FALSE;
2289 if (!This->Locked)
2290 {
2291 /* Apply clipping */
2292 PostMessage(This->hWnd,
2293 WM_SIZE,
2294 SIZE_RESTORED,
2295 0);
2296 }
2297 break;
2298
2299 case WM_SYSCHAR:
2300 switch (wParam)
2301 {
2302 case TEXT(' '):
2303 {
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
2306 a system menu. */
2307
2308 static const UINT uidDisableItem[] = {
2309 SC_RESTORE,
2310 SC_MOVE,
2311 SC_SIZE,
2312 SC_MAXIMIZE,
2313 SC_MINIMIZE,
2314 };
2315 HMENU hSysMenu;
2316 INT i;
2317 UINT uId;
2318
2319 /* temporarily enable the system menu */
2320 SetWindowStyle(hwnd,
2321 WS_SYSMENU,
2322 WS_SYSMENU);
2323
2324 hSysMenu = GetSystemMenu(hwnd,
2325 FALSE);
2326 if (hSysMenu != NULL)
2327 {
2328 /* Disable all items that are not relevant */
2329 for (i = 0; i != sizeof(uidDisableItem) / sizeof(uidDisableItem[0]); i++)
2330 {
2331 EnableMenuItem(hSysMenu,
2332 uidDisableItem[i],
2333 MF_BYCOMMAND | MF_GRAYED);
2334 }
2335
2336 EnableMenuItem(hSysMenu,
2337 SC_CLOSE,
2338 MF_BYCOMMAND |
2339 (SHRestricted(REST_NOCLOSE) ? MF_GRAYED : MF_ENABLED));
2340
2341 /* Display the system menu */
2342 uId = ITrayWindowImpl_TrackMenu(This,
2343 hSysMenu,
2344 NULL,
2345 This->hwndStart,
2346 This->Position != ABE_TOP,
2347 FALSE);
2348 if (uId != 0)
2349 {
2350 SendMessage(This->hWnd,
2351 WM_SYSCOMMAND,
2352 (WPARAM)uId,
2353 0);
2354 }
2355 }
2356
2357 /* revert the system menu window style */
2358 SetWindowStyle(hwnd,
2359 WS_SYSMENU,
2360 0);
2361 break;
2362 }
2363
2364 default:
2365 goto DefHandler;
2366 }
2367 break;
2368
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;
2374 /* fall through */
2375
2376 case WM_CONTEXTMENU:
2377 {
2378 POINT pt, *ppt = NULL;
2379 HWND hWndExclude = NULL;
2380
2381 /* Check if the administrator has forbidden access to context menus */
2382 if (SHRestricted(REST_NOTRAYCONTEXTMENU))
2383 break;
2384
2385 pt.x = (SHORT)LOWORD(lParam);
2386 pt.y = (SHORT)HIWORD(lParam);
2387
2388 if (pt.x != -1 || pt.y != -1)
2389 ppt = &pt;
2390 else
2391 hWndExclude = This->hwndStart;
2392
2393 if ((HWND)wParam == This->hwndStart)
2394 {
2395 /* Make sure we can't track the context menu if the start
2396 menu is currently being shown */
2397 if (!(SendMessage(This->hwndStart,
2398 BM_GETSTATE,
2399 0,
2400 0) & BST_PUSHED))
2401 {
2402 ITrayWindowImpl_TrackCtxMenu(This,
2403 &StartMenuBtnCtxMenu,
2404 ppt,
2405 hWndExclude,
2406 This->Position == ABE_BOTTOM,
2407 This);
2408 }
2409 }
2410 else
2411 {
2412 /* See if the context menu should be handled by the task band site */
2413 if (ppt != NULL && This->TrayBandSite != NULL)
2414 {
2415 HWND hWndAtPt;
2416 POINT ptClient = *ppt;
2417
2418 /* Convert the coordinates to client-coordinates */
2419 MapWindowPoints(NULL,
2420 This->hWnd,
2421 &ptClient,
2422 1);
2423
2424 hWndAtPt = ChildWindowFromPoint(This->hWnd,
2425 ptClient);
2426 if (hWndAtPt != NULL &&
2427 (hWndAtPt == This->hwndRebar || IsChild(This->hwndRebar,
2428 hWndAtPt)))
2429 {
2430 /* Check if the user clicked on the task switch window */
2431 ptClient = *ppt;
2432 MapWindowPoints(NULL,
2433 This->hwndRebar,
2434 &ptClient,
2435 1);
2436
2437 hWndAtPt = ChildWindowFromPointEx(This->hwndRebar,
2438 ptClient,
2439 CWP_SKIPINVISIBLE | CWP_SKIPDISABLED);
2440 if (hWndAtPt == This->hwndTaskSwitch)
2441 goto HandleTrayContextMenu;
2442
2443 /* Forward the message to the task band site */
2444 ITrayBandSite_ProcessMessage(This->TrayBandSite,
2445 hwnd,
2446 uMsg,
2447 wParam,
2448 lParam,
2449 &Ret);
2450 }
2451 else
2452 goto HandleTrayContextMenu;
2453 }
2454 else
2455 {
2456 HandleTrayContextMenu:
2457 /* Tray the default tray window context menu */
2458 ITrayWindowImpl_TrackCtxMenu(This,
2459 &TrayWindowCtxMenu,
2460 ppt,
2461 NULL,
2462 FALSE,
2463 This);
2464 }
2465 }
2466 break;
2467 }
2468
2469 case WM_NOTIFY:
2470 {
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,
2476 hwnd,
2477 uMsg,
2478 wParam,
2479 lParam,
2480 &Ret)))
2481 {
2482 const NMHDR *nmh = (const NMHDR *)lParam;
2483
2484 if (nmh->hwndFrom == This->hwndTrayNotify)
2485 {
2486 switch (nmh->code)
2487 {
2488 case NTNWM_REALIGN:
2489 /* Cause all controls to be aligned */
2490 PostMessage(This->hWnd,
2491 WM_SIZE,
2492 SIZE_RESTORED,
2493 0);
2494 break;
2495 }
2496 }
2497 }
2498 break;
2499 }
2500
2501 case WM_NCLBUTTONDBLCLK:
2502 {
2503 /* We "handle" this message so users can't cause a weird maximize/restore
2504 window animation when double-clicking the tray window! */
2505
2506 /* We should forward mouse messages to child windows here.
2507 Right now, this is only clock double-click */
2508 RECT rcClock;
2509 if (TrayNotify_GetClockRect(This->hwndTrayNotify, &rcClock))
2510 {
2511 POINT ptClick;
2512 ptClick.x = MAKEPOINTS(lParam).x;
2513 ptClick.y = MAKEPOINTS(lParam).y;
2514 if (PtInRect(&rcClock, ptClick))
2515 LaunchCPanel(NULL, TEXT("timedate.cpl"));
2516 }
2517 break;
2518 }
2519
2520 case WM_NCCREATE:
2521 {
2522 LPCREATESTRUCT CreateStruct = (LPCREATESTRUCT)lParam;
2523 This = (ITrayWindowImpl*)CreateStruct->lpCreateParams;
2524
2525 if (InterlockedCompareExchangePointer((PVOID*)&This->hWnd,
2526 (PVOID)hwnd,
2527 NULL) != NULL)
2528 {
2529 /* Somebody else was faster... */
2530 return FALSE;
2531 }
2532
2533 SetWindowLongPtr(hwnd,
2534 0,
2535 (LONG_PTR)This);
2536
2537 return ITrayWindowImpl_NCCreate(This);
2538 }
2539
2540 case WM_CREATE:
2541 ITrayWindowImpl_Create(This);
2542 break;
2543
2544 case WM_NCDESTROY:
2545 ITrayWindowImpl_Destroy(This);
2546 break;
2547
2548 case WM_APP_TRAYDESTROY:
2549 DestroyWindow(hwnd);
2550 break;
2551
2552 case TWM_OPENSTARTMENU:
2553 {
2554 HWND hwndStartMenu;
2555 HRESULT hr = IUnknown_GetWindow((IUnknown*)This->StartMenuPopup, &hwndStartMenu);
2556 if (FAILED(hr))
2557 break;
2558
2559 if (IsWindowVisible(hwndStartMenu))
2560 SetWindowPos(hwndStartMenu, 0,0,0,0,0, SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
2561 else
2562 SendMessage(This->hWnd, WM_COMMAND, MAKEWPARAM(BN_CLICKED, IDC_STARTBTN), (LPARAM)This->hwndStart);
2563
2564 break;
2565 }
2566 case WM_COMMAND:
2567 if ((HWND)lParam == This->hwndStart)
2568 {
2569 if (This->StartMenuPopup != NULL)
2570 {
2571 POINTL pt;
2572 RECTL rcExclude;
2573 DWORD dwFlags = 0;
2574
2575 if (GetWindowRect(This->hwndStart,
2576 (RECT*)&rcExclude))
2577 {
2578 if (ITrayWindowImpl_IsPosHorizontal(This))
2579 {
2580 pt.x = rcExclude.left;
2581 pt.y = rcExclude.top;
2582 dwFlags |= MPPF_BOTTOM;
2583 }
2584 else
2585 {
2586 if (This->Position == ABE_LEFT)
2587 pt.x = rcExclude.left;
2588 else
2589 pt.x = rcExclude.right;
2590
2591 pt.y = rcExclude.bottom;
2592 dwFlags |= MPPF_BOTTOM;
2593 }
2594
2595 IMenuPopup_Popup(This->StartMenuPopup,
2596 &pt,
2597 &rcExclude,
2598 dwFlags);
2599 }
2600 }
2601 break;
2602 }
2603
2604 if (This->TrayBandSite == NULL ||
2605 !SUCCEEDED(ITrayBandSite_ProcessMessage(This->TrayBandSite,
2606 hwnd,
2607 uMsg,
2608 wParam,
2609 lParam,
2610 &Ret)))
2611 {
2612 switch (LOWORD(wParam))
2613 {
2614 /* FIXME: Handle these commands as well */
2615 case IDM_TASKBARANDSTARTMENU:
2616
2617 ITrayWindowImpl_DisplayProperties(ITrayWindow_from_impl(This));
2618 break;
2619
2620 case IDM_SEARCH:
2621 case IDM_HELPANDSUPPORT:
2622 break;
2623
2624 case IDM_RUN:
2625 {
2626 ITrayWindowImpl_DisplayRunFileDlg(This);
2627 break;
2628 }
2629
2630 /* FIXME: Handle these commands as well */
2631 case IDM_SYNCHRONIZE:
2632 case IDM_LOGOFF:
2633 case IDM_DISCONNECT:
2634 case IDM_UNDOCKCOMPUTER:
2635 break;
2636
2637 case IDM_SHUTDOWN:
2638 {
2639 HANDLE hShell32;
2640 EXITWINDLG ExitWinDlg;
2641
2642 hShell32 = GetModuleHandle(TEXT("SHELL32.DLL"));
2643 ExitWinDlg = (EXITWINDLG)GetProcAddress(hShell32, (LPCSTR)60);
2644
2645 ExitWinDlg(hwnd);
2646 break;
2647 }
2648 }
2649 }
2650 break;
2651
2652 default:
2653 goto DefHandler;
2654 }
2655 }
2656 else
2657 {
2658 DefHandler:
2659 Ret = DefWindowProc(hwnd,
2660 uMsg,
2661 wParam,
2662 lParam);
2663 }
2664
2665 return Ret;
2666 }
2667
2668 /*
2669 * Tray Window Context Menu
2670 */
2671
2672 static HMENU
2673 CreateTrayWindowContextMenu(IN HWND hWndOwner,
2674 IN PVOID *ppcmContext,
2675 IN PVOID Context OPTIONAL)
2676 {
2677 ITrayWindowImpl *This = (ITrayWindowImpl *)Context;
2678 IContextMenu *pcm = NULL;
2679 HMENU hPopup;
2680
2681 hPopup = LoadPopupMenu(hExplorerInstance,
2682 MAKEINTRESOURCE(IDM_TRAYWND));
2683
2684 if (hPopup != NULL)
2685 {
2686 if (SHRestricted(REST_CLASSICSHELL) != 0)
2687 {
2688 DeleteMenu(hPopup,
2689 ID_LOCKTASKBAR,
2690 MF_BYCOMMAND);
2691 }
2692
2693 CheckMenuItem(hPopup,
2694 ID_LOCKTASKBAR,
2695 MF_BYCOMMAND | (This->Locked ? MF_CHECKED : MF_UNCHECKED));
2696
2697 if (This->TrayBandSite != NULL)
2698 {
2699 if (SUCCEEDED(ITrayBandSite_AddContextMenus(This->TrayBandSite,
2700 hPopup,
2701 0,
2702 ID_SHELL_CMD_FIRST,
2703 ID_SHELL_CMD_LAST,
2704 CMF_NORMAL,
2705 &pcm)))
2706 {
2707 DbgPrint("ITrayBandSite::AddContextMenus succeeded!\n");
2708 *(IContextMenu **)ppcmContext = pcm;
2709 }
2710 }
2711 }
2712
2713 return hPopup;
2714 }
2715
2716 static VOID
2717 OnTrayWindowContextMenuCommand(IN HWND hWndOwner,
2718 IN UINT uiCmdId,
2719 IN PVOID pcmContext OPTIONAL,
2720 IN PVOID Context OPTIONAL)
2721 {
2722 ITrayWindowImpl *This = (ITrayWindowImpl *)Context;
2723 IContextMenu *pcm = (IContextMenu *)pcmContext;
2724
2725 if (uiCmdId != 0)
2726 {
2727 if (uiCmdId >= ID_SHELL_CMD_FIRST && uiCmdId <= ID_SHELL_CMD_LAST)
2728 {
2729 CMINVOKECOMMANDINFO cmici = {0};
2730
2731 if (pcm != NULL)
2732 {
2733 /* Setup and invoke the shell command */
2734 cmici.cbSize = sizeof(cmici);
2735 cmici.hwnd = hWndOwner;
2736 cmici.lpVerb = (LPCSTR)MAKEINTRESOURCE(uiCmdId - ID_SHELL_CMD_FIRST);
2737 cmici.nShow = SW_NORMAL;
2738
2739 IContextMenu_InvokeCommand(pcm,
2740 &cmici);
2741 }
2742 }
2743 else
2744 {
2745 ITrayWindow_ExecContextMenuCmd(ITrayWindow_from_impl(This),
2746 uiCmdId);
2747 }
2748 }
2749
2750 if (pcm != NULL)
2751 IContextMenu_Release(pcm);
2752 }
2753
2754 static const TRAYWINDOW_CTXMENU TrayWindowCtxMenu = {
2755 CreateTrayWindowContextMenu,
2756 OnTrayWindowContextMenuCommand
2757 };
2758
2759 /*****************************************************************************/
2760
2761 BOOL
2762 RegisterTrayWindowClass(VOID)
2763 {
2764 WNDCLASS wcTrayWnd;
2765 BOOL Ret;
2766
2767 if (!RegisterTrayNotifyWndClass())
2768 return FALSE;
2769
2770 wcTrayWnd.style = CS_DBLCLKS;
2771 wcTrayWnd.lpfnWndProc = TrayWndProc;
2772 wcTrayWnd.cbClsExtra = 0;
2773 wcTrayWnd.cbWndExtra = sizeof(ITrayWindowImpl *);
2774 wcTrayWnd.hInstance = hExplorerInstance;
2775 wcTrayWnd.hIcon = NULL;
2776 wcTrayWnd.hCursor = LoadCursor(NULL,
2777 IDC_ARROW);
2778 wcTrayWnd.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
2779 wcTrayWnd.lpszMenuName = NULL;
2780 wcTrayWnd.lpszClassName = szTrayWndClass;
2781
2782 Ret = RegisterClass(&wcTrayWnd) != 0;
2783
2784 if (!Ret)
2785 UnregisterTrayNotifyWndClass();
2786
2787 return Ret;
2788 }
2789
2790 VOID
2791 UnregisterTrayWindowClass(VOID)
2792 {
2793 UnregisterTrayNotifyWndClass();
2794
2795 UnregisterClass(szTrayWndClass,
2796 hExplorerInstance);
2797 }
2798
2799 ITrayWindow *
2800 CreateTrayWindow(VOID)
2801 {
2802 ITrayWindowImpl *This;
2803 ITrayWindow *TrayWindow;
2804
2805 This = ITrayWindowImpl_Construct();
2806 if (This != NULL)
2807 {
2808 TrayWindow = ITrayWindow_from_impl(This);
2809
2810 ITrayWindowImpl_Open(TrayWindow);
2811
2812 return TrayWindow;
2813 }
2814
2815 return NULL;
2816 }
2817
2818 VOID
2819 TrayProcessMessages(IN OUT ITrayWindow *Tray)
2820 {
2821 ITrayWindowImpl *This;
2822 MSG Msg;
2823
2824 This = impl_from_ITrayWindow(Tray);
2825
2826 /* FIXME: We should keep a reference here... */
2827
2828 while (PeekMessage(&Msg,
2829 NULL,
2830 0,
2831 0,
2832 PM_REMOVE))
2833 {
2834 if (Msg.message == WM_QUIT)
2835 break;
2836
2837 if (This->StartMenuBand == NULL ||
2838 IMenuBand_IsMenuMessage(This->StartMenuBand,
2839 &Msg) != S_OK)
2840 {
2841 TranslateMessage(&Msg);
2842 DispatchMessage(&Msg);
2843 }
2844 }
2845 }
2846
2847 VOID
2848 TrayMessageLoop(IN OUT ITrayWindow *Tray)
2849 {
2850 ITrayWindowImpl *This;
2851 MSG Msg;
2852 BOOL Ret;
2853
2854 This = impl_from_ITrayWindow(Tray);
2855
2856 /* FIXME: We should keep a reference here... */
2857
2858 while (1)
2859 {
2860 Ret = GetMessage(&Msg,
2861 NULL,
2862 0,
2863 0);
2864
2865 if (!Ret || Ret == -1)
2866 break;
2867
2868 if (Msg.message == WM_HOTKEY)
2869 {
2870 switch (Msg.wParam)
2871 {
2872 case IDHK_RUN: /* Win+R */
2873 ITrayWindowImpl_DisplayRunFileDlg(This);
2874 break;
2875 }
2876 }
2877
2878 if (This->StartMenuBand == NULL ||
2879 IMenuBand_IsMenuMessage(This->StartMenuBand,
2880 &Msg) != S_OK)
2881 {
2882 TranslateMessage(&Msg);
2883 DispatchMessage(&Msg);
2884 }
2885 }
2886 }
2887
2888 /*
2889 * IShellDesktopTray
2890 *
2891 * NOTE: This is a very windows-specific COM interface used by SHCreateDesktop()!
2892 * These are the calls I observed, it may be wrong/incomplete/buggy!!!
2893 * The reason we implement it is because we have to use SHCreateDesktop() so
2894 * that the shell provides the desktop window and all the features that come
2895 * with it (especially positioning of desktop icons)
2896 */
2897
2898 static HRESULT STDMETHODCALLTYPE
2899 ITrayWindowImpl_IShellDesktopTray_QueryInterface(IN OUT IShellDesktopTray *iface,
2900 IN REFIID riid,
2901 OUT LPVOID *ppvObj)
2902 {
2903 ITrayWindowImpl *This = impl_from_IShellDesktopTray(iface);
2904 ITrayWindow *tray = ITrayWindow_from_impl(This);
2905
2906 DbgPrint("IShellDesktopTray::QueryInterface(0x%p, 0x%p)\n", riid, ppvObj);
2907 return ITrayWindowImpl_QueryInterface(tray,
2908 riid,
2909 ppvObj);
2910 }
2911
2912 static ULONG STDMETHODCALLTYPE
2913 ITrayWindowImpl_IShellDesktopTray_Release(IN OUT IShellDesktopTray *iface)
2914 {
2915 ITrayWindowImpl *This = impl_from_IShellDesktopTray(iface);
2916 ITrayWindow *tray = ITrayWindow_from_impl(This);
2917
2918 DbgPrint("IShellDesktopTray::Release()\n");
2919 return ITrayWindowImpl_Release(tray);
2920 }
2921
2922 static ULONG STDMETHODCALLTYPE
2923 ITrayWindowImpl_IShellDesktopTray_AddRef(IN OUT IShellDesktopTray *iface)
2924 {
2925 ITrayWindowImpl *This = impl_from_IShellDesktopTray(iface);
2926 ITrayWindow *tray = ITrayWindow_from_impl(This);
2927
2928 DbgPrint("IShellDesktopTray::AddRef()\n");
2929 return ITrayWindowImpl_AddRef(tray);
2930 }
2931
2932 static ULONG STDMETHODCALLTYPE
2933 ITrayWindowImpl_IShellDesktopTray_GetState(IN OUT IShellDesktopTray *iface)
2934 {
2935 /* FIXME: Return ABS_ flags? */
2936 DbgPrint("IShellDesktopTray::GetState() unimplemented!\n");
2937 return 0;
2938 }
2939
2940 static HRESULT STDMETHODCALLTYPE
2941 ITrayWindowImpl_IShellDesktopTray_GetTrayWindow(IN OUT IShellDesktopTray *iface,
2942 OUT HWND *phWndTray)
2943 {
2944 ITrayWindowImpl *This = impl_from_IShellDesktopTray(iface);
2945 DbgPrint("IShellDesktopTray::GetTrayWindow(0x%p)\n", phWndTray);
2946 *phWndTray = This->hWnd;
2947 return S_OK;
2948 }
2949
2950 static HRESULT STDMETHODCALLTYPE
2951 ITrayWindowImpl_IShellDesktopTray_RegisterDesktopWindow(IN OUT IShellDesktopTray *iface,
2952 IN HWND hWndDesktop)
2953 {
2954 ITrayWindowImpl *This = impl_from_IShellDesktopTray(iface);
2955 DbgPrint("IShellDesktopTray::RegisterDesktopWindow(0x%p)\n", hWndDesktop);
2956
2957 This->hWndDesktop = hWndDesktop;
2958 return S_OK;
2959 }
2960
2961 static HRESULT STDMETHODCALLTYPE
2962 ITrayWindowImpl_IShellDesktopTray_Unknown(IN OUT IShellDesktopTray *iface,
2963 IN DWORD dwUnknown1,
2964 IN DWORD dwUnknown2)
2965 {
2966 DbgPrint("IShellDesktopTray::Unknown(%u,%u) unimplemented!\n", dwUnknown1, dwUnknown2);
2967 return S_OK;
2968 }
2969
2970 static const IShellDesktopTrayVtbl IShellDesktopTrayImpl_Vtbl =
2971 {
2972 /*** IUnknown ***/
2973 ITrayWindowImpl_IShellDesktopTray_QueryInterface,
2974 ITrayWindowImpl_IShellDesktopTray_AddRef,
2975 ITrayWindowImpl_IShellDesktopTray_Release,
2976 /*** IShellDesktopTray ***/
2977 ITrayWindowImpl_IShellDesktopTray_GetState,
2978 ITrayWindowImpl_IShellDesktopTray_GetTrayWindow,
2979 ITrayWindowImpl_IShellDesktopTray_RegisterDesktopWindow,
2980 ITrayWindowImpl_IShellDesktopTray_Unknown
2981 };