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