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