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