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