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