Sync with trunk r63270.
[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 void PopupStartMenu(IN ITrayWindowImpl *This)
2076 {
2077 if (This->StartMenuPopup != NULL)
2078 {
2079 POINTL pt;
2080 RECTL rcExclude;
2081 DWORD dwFlags = 0;
2082
2083 if (GetWindowRect(This->hwndStart,
2084 (RECT*) &rcExclude))
2085 {
2086 switch (This->Position)
2087 {
2088 case ABE_BOTTOM:
2089 pt.x = rcExclude.left;
2090 pt.y = rcExclude.top;
2091 dwFlags |= MPPF_BOTTOM;
2092 break;
2093 case ABE_TOP:
2094 case ABE_LEFT:
2095 pt.x = rcExclude.left;
2096 pt.y = rcExclude.bottom;
2097 dwFlags |= MPPF_TOP | MPPF_ALIGN_RIGHT;
2098 break;
2099 case ABE_RIGHT:
2100 pt.x = rcExclude.right;
2101 pt.y = rcExclude.bottom;
2102 dwFlags |= MPPF_TOP | MPPF_ALIGN_LEFT;
2103 break;
2104 }
2105
2106 IMenuPopup_Popup(This->StartMenuPopup,
2107 &pt,
2108 &rcExclude,
2109 dwFlags);
2110 }
2111 }
2112 }
2113
2114 static LRESULT CALLBACK
2115 TrayWndProc(IN HWND hwnd,
2116 IN UINT uMsg,
2117 IN WPARAM wParam,
2118 IN LPARAM lParam)
2119 {
2120 ITrayWindowImpl *This = NULL;
2121 LRESULT Ret = FALSE;
2122
2123 if (uMsg != WM_NCCREATE)
2124 {
2125 This = (ITrayWindowImpl*)GetWindowLongPtr(hwnd,
2126 0);
2127 }
2128
2129 if (This != NULL || uMsg == WM_NCCREATE)
2130 {
2131 if (This != NULL && This->StartMenuBand != NULL)
2132 {
2133 MSG Msg;
2134 LRESULT lRet;
2135
2136 Msg.hwnd = hwnd;
2137 Msg.message = uMsg;
2138 Msg.wParam = wParam;
2139 Msg.lParam = lParam;
2140
2141 if (IMenuBand_TranslateMenuMessage(This->StartMenuBand,
2142 &Msg,
2143 &lRet) == S_OK)
2144 {
2145 return lRet;
2146 }
2147
2148 wParam = Msg.wParam;
2149 lParam = Msg.lParam;
2150 }
2151
2152 switch (uMsg)
2153 {
2154 case WM_COPYDATA:
2155 {
2156 if (This->hwndTrayNotify)
2157 {
2158 TrayNotify_NotifyMsg(This->hwndTrayNotify,
2159 wParam,
2160 lParam);
2161 }
2162 return TRUE;
2163 }
2164 case WM_THEMECHANGED:
2165 ITrayWindowImpl_UpdateTheme(This);
2166 return 0;
2167 case WM_NCPAINT:
2168 if (!This->TaskbarTheme)
2169 goto DefHandler;
2170 return ITrayWindowImpl_DrawSizer(This,
2171 (HRGN)wParam);
2172 case WM_ERASEBKGND:
2173 if (!This->TaskbarTheme)
2174 goto DefHandler;
2175 return ITrayWindowImpl_DrawBackground(This,
2176 (HDC)wParam);
2177 case WM_CTLCOLORBTN:
2178 SetBkMode((HDC)wParam, TRANSPARENT);
2179 return (LRESULT)GetStockObject(HOLLOW_BRUSH);
2180 case WM_NCHITTEST:
2181 {
2182 RECT rcClient;
2183 POINT pt;
2184
2185 if (This->Locked)
2186 {
2187 /* The user may not be able to resize the tray window.
2188 Pretend like the window is not sizeable when the user
2189 clicks on the border. */
2190 return HTBORDER;
2191 }
2192
2193 SetLastError(ERROR_SUCCESS);
2194 if (GetClientRect(hwnd,
2195 &rcClient) &&
2196 (MapWindowPoints(hwnd,
2197 NULL,
2198 (LPPOINT)&rcClient,
2199 2) != 0 || GetLastError() == ERROR_SUCCESS))
2200 {
2201 pt.x = (SHORT)LOWORD(lParam);
2202 pt.y = (SHORT)HIWORD(lParam);
2203
2204 if (PtInRect(&rcClient,
2205 pt))
2206 {
2207 /* The user is trying to drag the tray window */
2208 return HTCAPTION;
2209 }
2210
2211 /* Depending on the position of the tray window, allow only
2212 changing the border next to the monitor working area */
2213 switch (This->Position)
2214 {
2215 case ABE_TOP:
2216 if (pt.y > rcClient.bottom)
2217 return HTBOTTOM;
2218 break;
2219 case ABE_LEFT:
2220 if (pt.x > rcClient.right)
2221 return HTRIGHT;
2222 break;
2223 case ABE_RIGHT:
2224 if (pt.x < rcClient.left)
2225 return HTLEFT;
2226 break;
2227 case ABE_BOTTOM:
2228 default:
2229 if (pt.y < rcClient.top)
2230 return HTTOP;
2231 break;
2232 }
2233 }
2234 return HTBORDER;
2235 }
2236 case WM_MOVING:
2237 {
2238 POINT ptCursor;
2239 PRECT pRect = (PRECT)lParam;
2240
2241 /* We need to ensure that an application can not accidently
2242 move the tray window (using SetWindowPos). However, we still
2243 need to be able to move the window in case the user wants to
2244 drag the tray window to another position or in case the user
2245 wants to resize the tray window. */
2246 if (!This->Locked && GetCursorPos(&ptCursor))
2247 {
2248 This->IsDragging = TRUE;
2249 This->DraggingPosition = ITrayWindowImpl_GetDraggingRectFromPt(This,
2250 ptCursor,
2251 pRect,
2252 &This->DraggingMonitor);
2253 }
2254 else
2255 {
2256 *pRect = This->rcTrayWnd[This->Position];
2257 }
2258 return TRUE;
2259 }
2260
2261 case WM_SIZING:
2262 {
2263 PRECT pRect = (PRECT)lParam;
2264
2265 if (!This->Locked)
2266 {
2267 ITrayWindowImpl_CalculateValidSize(This,
2268 This->Position,
2269 pRect);
2270 }
2271 else
2272 {
2273 *pRect = This->rcTrayWnd[This->Position];
2274 }
2275 return TRUE;
2276 }
2277
2278 case WM_WINDOWPOSCHANGING:
2279 {
2280 ITrayWindowImpl_ChangingWinPos(This,
2281 (LPWINDOWPOS)lParam);
2282 break;
2283 }
2284
2285 case WM_SIZE:
2286 {
2287 RECT rcClient;
2288 InvalidateRect(This->hWnd, NULL, TRUE);
2289 if (wParam == SIZE_RESTORED && lParam == 0)
2290 {
2291 ITrayWindowImpl_ResizeWorkArea(This);
2292 /* Clip the tray window on multi monitor systems so the edges can't
2293 overlap into another monitor */
2294 ITrayWindowImpl_ApplyClipping(This,
2295 TRUE);
2296
2297 if (!GetClientRect(This->hWnd,
2298 &rcClient))
2299 {
2300 break;
2301 }
2302 }
2303 else
2304 {
2305 rcClient.left = rcClient.top = 0;
2306 rcClient.right = LOWORD(lParam);
2307 rcClient.bottom = HIWORD(lParam);
2308 }
2309
2310 ITrayWindowImpl_AlignControls(This,
2311 &rcClient);
2312 break;
2313 }
2314
2315 case WM_ENTERSIZEMOVE:
2316 This->InSizeMove = TRUE;
2317 This->IsDragging = FALSE;
2318 if (!This->Locked)
2319 {
2320 /* Remove the clipping on multi monitor systems while dragging around */
2321 ITrayWindowImpl_ApplyClipping(This,
2322 FALSE);
2323 }
2324 break;
2325
2326 case WM_EXITSIZEMOVE:
2327 This->InSizeMove = FALSE;
2328 if (!This->Locked)
2329 {
2330 /* Apply clipping */
2331 PostMessage(This->hWnd,
2332 WM_SIZE,
2333 SIZE_RESTORED,
2334 0);
2335 }
2336 break;
2337
2338 case WM_SYSCHAR:
2339 switch (wParam)
2340 {
2341 case TEXT(' '):
2342 {
2343 /* The user pressed Alt+Space, this usually brings up the system menu of a window.
2344 The tray window needs to handle this specially, since it normally doesn't have
2345 a system menu. */
2346
2347 static const UINT uidDisableItem[] = {
2348 SC_RESTORE,
2349 SC_MOVE,
2350 SC_SIZE,
2351 SC_MAXIMIZE,
2352 SC_MINIMIZE,
2353 };
2354 HMENU hSysMenu;
2355 INT i;
2356 UINT uId;
2357
2358 /* temporarily enable the system menu */
2359 SetWindowStyle(hwnd,
2360 WS_SYSMENU,
2361 WS_SYSMENU);
2362
2363 hSysMenu = GetSystemMenu(hwnd,
2364 FALSE);
2365 if (hSysMenu != NULL)
2366 {
2367 /* Disable all items that are not relevant */
2368 for (i = 0; i != sizeof(uidDisableItem) / sizeof(uidDisableItem[0]); i++)
2369 {
2370 EnableMenuItem(hSysMenu,
2371 uidDisableItem[i],
2372 MF_BYCOMMAND | MF_GRAYED);
2373 }
2374
2375 EnableMenuItem(hSysMenu,
2376 SC_CLOSE,
2377 MF_BYCOMMAND |
2378 (SHRestricted(REST_NOCLOSE) ? MF_GRAYED : MF_ENABLED));
2379
2380 /* Display the system menu */
2381 uId = ITrayWindowImpl_TrackMenu(This,
2382 hSysMenu,
2383 NULL,
2384 This->hwndStart,
2385 This->Position != ABE_TOP,
2386 FALSE);
2387 if (uId != 0)
2388 {
2389 SendMessage(This->hWnd,
2390 WM_SYSCOMMAND,
2391 (WPARAM)uId,
2392 0);
2393 }
2394 }
2395
2396 /* revert the system menu window style */
2397 SetWindowStyle(hwnd,
2398 WS_SYSMENU,
2399 0);
2400 break;
2401 }
2402
2403 default:
2404 goto DefHandler;
2405 }
2406 break;
2407
2408 case WM_NCRBUTTONUP:
2409 /* We want the user to be able to get a context menu even on the nonclient
2410 area (including the sizing border)! */
2411 uMsg = WM_CONTEXTMENU;
2412 wParam = (WPARAM)hwnd;
2413 /* fall through */
2414
2415 case WM_CONTEXTMENU:
2416 {
2417 POINT pt, *ppt = NULL;
2418 HWND hWndExclude = NULL;
2419
2420 /* Check if the administrator has forbidden access to context menus */
2421 if (SHRestricted(REST_NOTRAYCONTEXTMENU))
2422 break;
2423
2424 pt.x = (SHORT)LOWORD(lParam);
2425 pt.y = (SHORT)HIWORD(lParam);
2426
2427 if (pt.x != -1 || pt.y != -1)
2428 ppt = &pt;
2429 else
2430 hWndExclude = This->hwndStart;
2431
2432 if ((HWND)wParam == This->hwndStart)
2433 {
2434 /* Make sure we can't track the context menu if the start
2435 menu is currently being shown */
2436 if (!(SendMessage(This->hwndStart,
2437 BM_GETSTATE,
2438 0,
2439 0) & BST_PUSHED))
2440 {
2441 ITrayWindowImpl_TrackCtxMenu(This,
2442 &StartMenuBtnCtxMenu,
2443 ppt,
2444 hWndExclude,
2445 This->Position == ABE_BOTTOM,
2446 This);
2447 }
2448 }
2449 else
2450 {
2451 /* See if the context menu should be handled by the task band site */
2452 if (ppt != NULL && This->TrayBandSite != NULL)
2453 {
2454 HWND hWndAtPt;
2455 POINT ptClient = *ppt;
2456
2457 /* Convert the coordinates to client-coordinates */
2458 MapWindowPoints(NULL,
2459 This->hWnd,
2460 &ptClient,
2461 1);
2462
2463 hWndAtPt = ChildWindowFromPoint(This->hWnd,
2464 ptClient);
2465 if (hWndAtPt != NULL &&
2466 (hWndAtPt == This->hwndRebar || IsChild(This->hwndRebar,
2467 hWndAtPt)))
2468 {
2469 /* Check if the user clicked on the task switch window */
2470 ptClient = *ppt;
2471 MapWindowPoints(NULL,
2472 This->hwndRebar,
2473 &ptClient,
2474 1);
2475
2476 hWndAtPt = ChildWindowFromPointEx(This->hwndRebar,
2477 ptClient,
2478 CWP_SKIPINVISIBLE | CWP_SKIPDISABLED);
2479 if (hWndAtPt == This->hwndTaskSwitch)
2480 goto HandleTrayContextMenu;
2481
2482 /* Forward the message to the task band site */
2483 ITrayBandSite_ProcessMessage(This->TrayBandSite,
2484 hwnd,
2485 uMsg,
2486 wParam,
2487 lParam,
2488 &Ret);
2489 }
2490 else
2491 goto HandleTrayContextMenu;
2492 }
2493 else
2494 {
2495 HandleTrayContextMenu:
2496 /* Tray the default tray window context menu */
2497 ITrayWindowImpl_TrackCtxMenu(This,
2498 &TrayWindowCtxMenu,
2499 ppt,
2500 NULL,
2501 FALSE,
2502 This);
2503 }
2504 }
2505 break;
2506 }
2507
2508 case WM_NOTIFY:
2509 {
2510 /* FIXME: We can't check with IsChild whether the hwnd is somewhere inside
2511 the rebar control! But we shouldn't forward messages that the band
2512 site doesn't handle, such as other controls (start button, tray window */
2513 if (This->TrayBandSite == NULL ||
2514 !SUCCEEDED(ITrayBandSite_ProcessMessage(This->TrayBandSite,
2515 hwnd,
2516 uMsg,
2517 wParam,
2518 lParam,
2519 &Ret)))
2520 {
2521 const NMHDR *nmh = (const NMHDR *)lParam;
2522
2523 if (nmh->hwndFrom == This->hwndTrayNotify)
2524 {
2525 switch (nmh->code)
2526 {
2527 case NTNWM_REALIGN:
2528 /* Cause all controls to be aligned */
2529 PostMessage(This->hWnd,
2530 WM_SIZE,
2531 SIZE_RESTORED,
2532 0);
2533 break;
2534 }
2535 }
2536 }
2537 break;
2538 }
2539
2540 case WM_NCLBUTTONDBLCLK:
2541 {
2542 /* We "handle" this message so users can't cause a weird maximize/restore
2543 window animation when double-clicking the tray window! */
2544
2545 /* We should forward mouse messages to child windows here.
2546 Right now, this is only clock double-click */
2547 RECT rcClock;
2548 if (TrayNotify_GetClockRect(This->hwndTrayNotify, &rcClock))
2549 {
2550 POINT ptClick;
2551 ptClick.x = MAKEPOINTS(lParam).x;
2552 ptClick.y = MAKEPOINTS(lParam).y;
2553 if (PtInRect(&rcClock, ptClick))
2554 LaunchCPanel(NULL, TEXT("timedate.cpl"));
2555 }
2556 break;
2557 }
2558
2559 case WM_NCCREATE:
2560 {
2561 LPCREATESTRUCT CreateStruct = (LPCREATESTRUCT)lParam;
2562 This = (ITrayWindowImpl*)CreateStruct->lpCreateParams;
2563
2564 if (InterlockedCompareExchangePointer((PVOID*)&This->hWnd,
2565 (PVOID)hwnd,
2566 NULL) != NULL)
2567 {
2568 /* Somebody else was faster... */
2569 return FALSE;
2570 }
2571
2572 SetWindowLongPtr(hwnd,
2573 0,
2574 (LONG_PTR)This);
2575
2576 return ITrayWindowImpl_NCCreate(This);
2577 }
2578
2579 case WM_CREATE:
2580 ITrayWindowImpl_Create(This);
2581 break;
2582
2583 case WM_NCDESTROY:
2584 ITrayWindowImpl_Destroy(This);
2585 break;
2586
2587 case WM_APP_TRAYDESTROY:
2588 DestroyWindow(hwnd);
2589 break;
2590
2591 case TWM_OPENSTARTMENU:
2592 {
2593 HWND hwndStartMenu;
2594 HRESULT hr = IUnknown_GetWindow((IUnknown*)This->StartMenuPopup, &hwndStartMenu);
2595 if (FAILED(hr))
2596 break;
2597
2598 if (IsWindowVisible(hwndStartMenu))
2599 SetWindowPos(hwndStartMenu, 0,0,0,0,0, SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
2600 else
2601 SendMessage(This->hWnd, WM_COMMAND, MAKEWPARAM(BN_CLICKED, IDC_STARTBTN), (LPARAM)This->hwndStart);
2602
2603 break;
2604 }
2605 case WM_COMMAND:
2606 if ((HWND)lParam == This->hwndStart)
2607 {
2608 PopupStartMenu(This);
2609 break;
2610 }
2611
2612 if (This->TrayBandSite == NULL ||
2613 FAILED(ITrayBandSite_ProcessMessage(This->TrayBandSite,
2614 hwnd,
2615 uMsg,
2616 wParam,
2617 lParam,
2618 &Ret)))
2619 {
2620 switch (LOWORD(wParam))
2621 {
2622 /* FIXME: Handle these commands as well */
2623 case IDM_TASKBARANDSTARTMENU:
2624
2625 ITrayWindowImpl_DisplayProperties(ITrayWindow_from_impl(This));
2626 break;
2627
2628 case IDM_SEARCH:
2629 case IDM_HELPANDSUPPORT:
2630 break;
2631
2632 case IDM_RUN:
2633 {
2634 ITrayWindowImpl_DisplayRunFileDlg(This);
2635 break;
2636 }
2637
2638 /* FIXME: Handle these commands as well */
2639 case IDM_SYNCHRONIZE:
2640 case IDM_LOGOFF:
2641 case IDM_DISCONNECT:
2642 case IDM_UNDOCKCOMPUTER:
2643 break;
2644
2645 case IDM_SHUTDOWN:
2646 {
2647 HANDLE hShell32;
2648 EXITWINDLG ExitWinDlg;
2649
2650 hShell32 = GetModuleHandle(TEXT("SHELL32.DLL"));
2651 ExitWinDlg = (EXITWINDLG)GetProcAddress(hShell32, (LPCSTR)60);
2652
2653 ExitWinDlg(hwnd);
2654 break;
2655 }
2656 }
2657 }
2658 break;
2659
2660 default:
2661 goto DefHandler;
2662 }
2663 }
2664 else
2665 {
2666 DefHandler:
2667 Ret = DefWindowProc(hwnd,
2668 uMsg,
2669 wParam,
2670 lParam);
2671 }
2672
2673 return Ret;
2674 }
2675
2676 /*
2677 * Tray Window Context Menu
2678 */
2679
2680 static HMENU
2681 CreateTrayWindowContextMenu(IN HWND hWndOwner,
2682 IN PVOID *ppcmContext,
2683 IN PVOID Context OPTIONAL)
2684 {
2685 ITrayWindowImpl *This = (ITrayWindowImpl *)Context;
2686 IContextMenu *pcm = NULL;
2687 HMENU hPopup;
2688
2689 hPopup = LoadPopupMenu(hExplorerInstance,
2690 MAKEINTRESOURCE(IDM_TRAYWND));
2691
2692 if (hPopup != NULL)
2693 {
2694 if (SHRestricted(REST_CLASSICSHELL) != 0)
2695 {
2696 DeleteMenu(hPopup,
2697 ID_LOCKTASKBAR,
2698 MF_BYCOMMAND);
2699 }
2700
2701 CheckMenuItem(hPopup,
2702 ID_LOCKTASKBAR,
2703 MF_BYCOMMAND | (This->Locked ? MF_CHECKED : MF_UNCHECKED));
2704
2705 if (This->TrayBandSite != NULL)
2706 {
2707 if (SUCCEEDED(ITrayBandSite_AddContextMenus(This->TrayBandSite,
2708 hPopup,
2709 0,
2710 ID_SHELL_CMD_FIRST,
2711 ID_SHELL_CMD_LAST,
2712 CMF_NORMAL,
2713 &pcm)))
2714 {
2715 DbgPrint("ITrayBandSite::AddContextMenus succeeded!\n");
2716 *(IContextMenu **)ppcmContext = pcm;
2717 }
2718 }
2719 }
2720
2721 return hPopup;
2722 }
2723
2724 static VOID
2725 OnTrayWindowContextMenuCommand(IN HWND hWndOwner,
2726 IN UINT uiCmdId,
2727 IN PVOID pcmContext OPTIONAL,
2728 IN PVOID Context OPTIONAL)
2729 {
2730 ITrayWindowImpl *This = (ITrayWindowImpl *)Context;
2731 IContextMenu *pcm = (IContextMenu *)pcmContext;
2732
2733 if (uiCmdId != 0)
2734 {
2735 if (uiCmdId >= ID_SHELL_CMD_FIRST && uiCmdId <= ID_SHELL_CMD_LAST)
2736 {
2737 CMINVOKECOMMANDINFO cmici = {0};
2738
2739 if (pcm != NULL)
2740 {
2741 /* Setup and invoke the shell command */
2742 cmici.cbSize = sizeof(cmici);
2743 cmici.hwnd = hWndOwner;
2744 cmici.lpVerb = (LPCSTR)MAKEINTRESOURCE(uiCmdId - ID_SHELL_CMD_FIRST);
2745 cmici.nShow = SW_NORMAL;
2746
2747 IContextMenu_InvokeCommand(pcm,
2748 &cmici);
2749 }
2750 }
2751 else
2752 {
2753 ITrayWindow_ExecContextMenuCmd(ITrayWindow_from_impl(This),
2754 uiCmdId);
2755 }
2756 }
2757
2758 if (pcm != NULL)
2759 IContextMenu_Release(pcm);
2760 }
2761
2762 static const TRAYWINDOW_CTXMENU TrayWindowCtxMenu = {
2763 CreateTrayWindowContextMenu,
2764 OnTrayWindowContextMenuCommand
2765 };
2766
2767 /*****************************************************************************/
2768
2769 BOOL
2770 RegisterTrayWindowClass(VOID)
2771 {
2772 WNDCLASS wcTrayWnd;
2773 BOOL Ret;
2774
2775 if (!RegisterTrayNotifyWndClass())
2776 return FALSE;
2777
2778 wcTrayWnd.style = CS_DBLCLKS;
2779 wcTrayWnd.lpfnWndProc = TrayWndProc;
2780 wcTrayWnd.cbClsExtra = 0;
2781 wcTrayWnd.cbWndExtra = sizeof(ITrayWindowImpl *);
2782 wcTrayWnd.hInstance = hExplorerInstance;
2783 wcTrayWnd.hIcon = NULL;
2784 wcTrayWnd.hCursor = LoadCursor(NULL,
2785 IDC_ARROW);
2786 wcTrayWnd.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
2787 wcTrayWnd.lpszMenuName = NULL;
2788 wcTrayWnd.lpszClassName = szTrayWndClass;
2789
2790 Ret = RegisterClass(&wcTrayWnd) != 0;
2791
2792 if (!Ret)
2793 UnregisterTrayNotifyWndClass();
2794
2795 return Ret;
2796 }
2797
2798 VOID
2799 UnregisterTrayWindowClass(VOID)
2800 {
2801 UnregisterTrayNotifyWndClass();
2802
2803 UnregisterClass(szTrayWndClass,
2804 hExplorerInstance);
2805 }
2806
2807 ITrayWindow *
2808 CreateTrayWindow(VOID)
2809 {
2810 ITrayWindowImpl *This;
2811 ITrayWindow *TrayWindow;
2812
2813 This = ITrayWindowImpl_Construct();
2814 if (This != NULL)
2815 {
2816 TrayWindow = ITrayWindow_from_impl(This);
2817
2818 ITrayWindowImpl_Open(TrayWindow);
2819
2820 return TrayWindow;
2821 }
2822
2823 return NULL;
2824 }
2825
2826 VOID
2827 TrayProcessMessages(IN OUT ITrayWindow *Tray)
2828 {
2829 ITrayWindowImpl *This;
2830 MSG Msg;
2831
2832 This = impl_from_ITrayWindow(Tray);
2833
2834 /* FIXME: We should keep a reference here... */
2835
2836 while (PeekMessage(&Msg,
2837 NULL,
2838 0,
2839 0,
2840 PM_REMOVE))
2841 {
2842 if (Msg.message == WM_QUIT)
2843 break;
2844
2845 if (This->StartMenuBand == NULL ||
2846 IMenuBand_IsMenuMessage(This->StartMenuBand,
2847 &Msg) != S_OK)
2848 {
2849 TranslateMessage(&Msg);
2850 DispatchMessage(&Msg);
2851 }
2852 }
2853 }
2854
2855 VOID
2856 TrayMessageLoop(IN OUT ITrayWindow *Tray)
2857 {
2858 ITrayWindowImpl *This;
2859 MSG Msg;
2860 BOOL Ret;
2861
2862 This = impl_from_ITrayWindow(Tray);
2863
2864 /* FIXME: We should keep a reference here... */
2865
2866 while (1)
2867 {
2868 Ret = GetMessage(&Msg,
2869 NULL,
2870 0,
2871 0);
2872
2873 if (!Ret || Ret == -1)
2874 break;
2875
2876 if (Msg.message == WM_HOTKEY)
2877 {
2878 switch (Msg.wParam)
2879 {
2880 case IDHK_RUN: /* Win+R */
2881 ITrayWindowImpl_DisplayRunFileDlg(This);
2882 break;
2883 }
2884 }
2885
2886 if (This->StartMenuBand == NULL ||
2887 IMenuBand_IsMenuMessage(This->StartMenuBand,
2888 &Msg) != S_OK)
2889 {
2890 TranslateMessage(&Msg);
2891 DispatchMessage(&Msg);
2892 }
2893 }
2894 }
2895
2896 /*
2897 * IShellDesktopTray
2898 *
2899 * NOTE: This is a very windows-specific COM interface used by SHCreateDesktop()!
2900 * These are the calls I observed, it may be wrong/incomplete/buggy!!!
2901 * The reason we implement it is because we have to use SHCreateDesktop() so
2902 * that the shell provides the desktop window and all the features that come
2903 * with it (especially positioning of desktop icons)
2904 */
2905
2906 static HRESULT STDMETHODCALLTYPE
2907 ITrayWindowImpl_IShellDesktopTray_QueryInterface(IN OUT IShellDesktopTray *iface,
2908 IN REFIID riid,
2909 OUT LPVOID *ppvObj)
2910 {
2911 ITrayWindowImpl *This = impl_from_IShellDesktopTray(iface);
2912 ITrayWindow *tray = ITrayWindow_from_impl(This);
2913
2914 DbgPrint("IShellDesktopTray::QueryInterface(0x%p, 0x%p)\n", riid, ppvObj);
2915 return ITrayWindowImpl_QueryInterface(tray,
2916 riid,
2917 ppvObj);
2918 }
2919
2920 static ULONG STDMETHODCALLTYPE
2921 ITrayWindowImpl_IShellDesktopTray_Release(IN OUT IShellDesktopTray *iface)
2922 {
2923 ITrayWindowImpl *This = impl_from_IShellDesktopTray(iface);
2924 ITrayWindow *tray = ITrayWindow_from_impl(This);
2925
2926 DbgPrint("IShellDesktopTray::Release()\n");
2927 return ITrayWindowImpl_Release(tray);
2928 }
2929
2930 static ULONG STDMETHODCALLTYPE
2931 ITrayWindowImpl_IShellDesktopTray_AddRef(IN OUT IShellDesktopTray *iface)
2932 {
2933 ITrayWindowImpl *This = impl_from_IShellDesktopTray(iface);
2934 ITrayWindow *tray = ITrayWindow_from_impl(This);
2935
2936 DbgPrint("IShellDesktopTray::AddRef()\n");
2937 return ITrayWindowImpl_AddRef(tray);
2938 }
2939
2940 static ULONG STDMETHODCALLTYPE
2941 ITrayWindowImpl_IShellDesktopTray_GetState(IN OUT IShellDesktopTray *iface)
2942 {
2943 /* FIXME: Return ABS_ flags? */
2944 DbgPrint("IShellDesktopTray::GetState() unimplemented!\n");
2945 return 0;
2946 }
2947
2948 static HRESULT STDMETHODCALLTYPE
2949 ITrayWindowImpl_IShellDesktopTray_GetTrayWindow(IN OUT IShellDesktopTray *iface,
2950 OUT HWND *phWndTray)
2951 {
2952 ITrayWindowImpl *This = impl_from_IShellDesktopTray(iface);
2953 DbgPrint("IShellDesktopTray::GetTrayWindow(0x%p)\n", phWndTray);
2954 *phWndTray = This->hWnd;
2955 return S_OK;
2956 }
2957
2958 static HRESULT STDMETHODCALLTYPE
2959 ITrayWindowImpl_IShellDesktopTray_RegisterDesktopWindow(IN OUT IShellDesktopTray *iface,
2960 IN HWND hWndDesktop)
2961 {
2962 ITrayWindowImpl *This = impl_from_IShellDesktopTray(iface);
2963 DbgPrint("IShellDesktopTray::RegisterDesktopWindow(0x%p)\n", hWndDesktop);
2964
2965 This->hWndDesktop = hWndDesktop;
2966 return S_OK;
2967 }
2968
2969 static HRESULT STDMETHODCALLTYPE
2970 ITrayWindowImpl_IShellDesktopTray_Unknown(IN OUT IShellDesktopTray *iface,
2971 IN DWORD dwUnknown1,
2972 IN DWORD dwUnknown2)
2973 {
2974 DbgPrint("IShellDesktopTray::Unknown(%u,%u) unimplemented!\n", dwUnknown1, dwUnknown2);
2975 return S_OK;
2976 }
2977
2978 static const IShellDesktopTrayVtbl IShellDesktopTrayImpl_Vtbl =
2979 {
2980 /*** IUnknown ***/
2981 ITrayWindowImpl_IShellDesktopTray_QueryInterface,
2982 ITrayWindowImpl_IShellDesktopTray_AddRef,
2983 ITrayWindowImpl_IShellDesktopTray_Release,
2984 /*** IShellDesktopTray ***/
2985 ITrayWindowImpl_IShellDesktopTray_GetState,
2986 ITrayWindowImpl_IShellDesktopTray_GetTrayWindow,
2987 ITrayWindowImpl_IShellDesktopTray_RegisterDesktopWindow,
2988 ITrayWindowImpl_IShellDesktopTray_Unknown
2989 };