[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 return;
1320 }
1321 }
1322
1323 Horizontal = IsPosHorizontal();
1324
1325 /* We're about to resize/move the start button, the rebar control and
1326 the tray notification control */
1327 dwp = BeginDeferWindowPos(3);
1328 if (dwp == NULL)
1329 return;
1330
1331 /* Limit the Start button width to the client width, if neccessary */
1332 StartSize = m_StartButton.GetSize();
1333 if (StartSize.cx > rcClient.right)
1334 StartSize.cx = rcClient.right;
1335
1336 if (m_StartButton.m_hWnd != NULL)
1337 {
1338 /* Resize and reposition the button */
1339 dwp = DeferWindowPos(dwp,
1340 m_StartButton.m_hWnd,
1341 NULL,
1342 0,
1343 0,
1344 StartSize.cx,
1345 StartSize.cy,
1346 SWP_NOZORDER | SWP_NOACTIVATE);
1347 if (dwp == NULL)
1348 return;
1349 }
1350
1351 /* Determine the size that the tray notification window needs */
1352 if (Horizontal)
1353 {
1354 TraySize.cx = 0;
1355 TraySize.cy = rcClient.bottom;
1356 }
1357 else
1358 {
1359 TraySize.cx = rcClient.right;
1360 TraySize.cy = 0;
1361 }
1362
1363 if (m_TrayNotify != NULL &&
1364 SendMessage(m_TrayNotify,
1365 TNWM_GETMINIMUMSIZE,
1366 (WPARAM) Horizontal,
1367 (LPARAM) &TraySize))
1368 {
1369 /* Move the tray notification window to the desired location */
1370 if (Horizontal)
1371 ptTrayNotify.x = rcClient.right - TraySize.cx;
1372 else
1373 ptTrayNotify.y = rcClient.bottom - TraySize.cy;
1374
1375 dwp = DeferWindowPos(dwp,
1376 m_TrayNotify,
1377 NULL,
1378 ptTrayNotify.x,
1379 ptTrayNotify.y,
1380 TraySize.cx,
1381 TraySize.cy,
1382 SWP_NOZORDER | SWP_NOACTIVATE);
1383 if (dwp == NULL)
1384 return;
1385 }
1386
1387 /* Resize/Move the rebar control */
1388 if (m_Rebar != NULL)
1389 {
1390 POINT ptRebar = { 0, 0 };
1391 SIZE szRebar;
1392
1393 SetWindowStyle(m_Rebar, CCS_VERT, Horizontal ? 0 : CCS_VERT);
1394
1395 if (Horizontal)
1396 {
1397 ptRebar.x = StartSize.cx + GetSystemMetrics(SM_CXSIZEFRAME);
1398 szRebar.cx = ptTrayNotify.x - ptRebar.x;
1399 szRebar.cy = rcClient.bottom;
1400 }
1401 else
1402 {
1403 ptRebar.y = StartSize.cy + GetSystemMetrics(SM_CYSIZEFRAME);
1404 szRebar.cx = rcClient.right;
1405 szRebar.cy = ptTrayNotify.y - ptRebar.y;
1406 }
1407
1408 dwp = DeferWindowPos(dwp,
1409 m_Rebar,
1410 NULL,
1411 ptRebar.x,
1412 ptRebar.y,
1413 szRebar.cx,
1414 szRebar.cy,
1415 SWP_NOZORDER | SWP_NOACTIVATE);
1416 }
1417
1418 if (dwp != NULL)
1419 EndDeferWindowPos(dwp);
1420
1421 if (m_TaskSwitch != NULL)
1422 {
1423 /* Update the task switch window configuration */
1424 SendMessage(m_TaskSwitch, TSWM_UPDATETASKBARPOS, 0, 0);
1425 }
1426 }
1427
1428 LRESULT OnThemeChanged()
1429 {
1430 if (m_Theme)
1431 CloseThemeData(m_Theme);
1432
1433 if (IsThemeActive())
1434 m_Theme = OpenThemeData(m_hWnd, L"TaskBar");
1435 else
1436 m_Theme = NULL;
1437
1438 if (m_Theme)
1439 {
1440 SetWindowStyle(m_hWnd, WS_THICKFRAME | WS_BORDER, 0);
1441 }
1442 else
1443 {
1444 SetWindowStyle(m_hWnd, WS_THICKFRAME | WS_BORDER, WS_THICKFRAME | WS_BORDER);
1445 }
1446
1447 return TRUE;
1448 }
1449
1450 LRESULT OnThemeChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1451 {
1452 return OnThemeChanged();
1453 }
1454
1455 LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1456 {
1457 WCHAR szStartCaption[32];
1458
1459 ((ITrayWindow*)this)->AddRef();
1460
1461 SetWindowTheme(m_hWnd, L"TaskBar", NULL);
1462 OnThemeChanged();
1463
1464 InterlockedIncrement(&TrayWndCount);
1465
1466 if (!LoadString(hExplorerInstance,
1467 IDS_START,
1468 szStartCaption,
1469 sizeof(szStartCaption) / sizeof(szStartCaption[0])))
1470 {
1471 szStartCaption[0] = TEXT('\0');
1472 }
1473
1474 if (m_CaptionFont == NULL)
1475 {
1476 NONCLIENTMETRICS ncm;
1477
1478 /* Get the system fonts, we use the caption font,
1479 always bold, though. */
1480 ncm.cbSize = sizeof(ncm);
1481 if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, FALSE))
1482 {
1483 if (m_CaptionFont == NULL)
1484 {
1485 ncm.lfCaptionFont.lfWeight = FW_NORMAL;
1486 m_CaptionFont = CreateFontIndirect(&ncm.lfCaptionFont);
1487 }
1488 }
1489 }
1490
1491 /* Create the Start button */
1492 m_StartButton.SubclassWindow(CreateWindowEx(
1493 0,
1494 WC_BUTTON,
1495 szStartCaption,
1496 WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS |
1497 BS_PUSHBUTTON | BS_CENTER | BS_VCENTER | BS_BITMAP,
1498 0,
1499 0,
1500 0,
1501 0,
1502 m_hWnd,
1503 (HMENU) IDC_STARTBTN,
1504 hExplorerInstance,
1505 NULL));
1506
1507 if (m_StartButton.m_hWnd)
1508 {
1509 m_StartButton.Initialize();
1510 }
1511
1512 /* Load the saved tray window settings */
1513 RegLoadSettings();
1514
1515 /* Create and initialize the start menu */
1516 HBITMAP hbmBanner = LoadBitmap(hExplorerInstance, MAKEINTRESOURCE(IDB_STARTMENU));
1517 m_StartMenuPopup = CreateStartMenu(this, &m_StartMenuBand, hbmBanner, 0);
1518
1519 /* Load the tray band site */
1520 if (m_TrayBandSite != NULL)
1521 {
1522 m_TrayBandSite.Release();
1523 }
1524
1525 m_TrayBandSite = CreateTrayBandSite(this, &m_Rebar, &m_TaskSwitch);
1526 SetWindowTheme(m_Rebar, L"TaskBar", NULL);
1527
1528 /* Create the tray notification window */
1529 m_TrayNotify = CreateTrayNotifyWnd(this, HideClock, &m_TrayNotifyInstance);
1530
1531 if (UpdateNonClientMetrics())
1532 {
1533 SetWindowsFont();
1534 }
1535
1536 /* Move the tray window to the right position and resize it if neccessary */
1537 CheckTrayWndPosition();
1538
1539 /* Align all controls on the tray window */
1540 AlignControls(NULL);
1541
1542 InitShellServices(&(m_ShellServices));
1543
1544 if (AutoHide)
1545 {
1546 m_AutoHideState = AUTOHIDE_HIDING;
1547 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_DELAY_HIDE, NULL);
1548 }
1549
1550 RegisterHotKey(m_hWnd, IDHK_RUN, MOD_WIN, 'R');
1551 RegisterHotKey(m_hWnd, IDHK_MINIMIZE_ALL, MOD_WIN, 'M');
1552 RegisterHotKey(m_hWnd, IDHK_RESTORE_ALL, MOD_WIN|MOD_SHIFT, 'M');
1553 RegisterHotKey(m_hWnd, IDHK_HELP, MOD_WIN, VK_F1);
1554 RegisterHotKey(m_hWnd, IDHK_EXPLORE, MOD_WIN, 'E');
1555 RegisterHotKey(m_hWnd, IDHK_FIND, MOD_WIN, 'F');
1556 RegisterHotKey(m_hWnd, IDHK_FIND_COMPUTER, MOD_WIN|MOD_CONTROL, 'F');
1557 RegisterHotKey(m_hWnd, IDHK_NEXT_TASK, MOD_WIN, VK_TAB);
1558 RegisterHotKey(m_hWnd, IDHK_PREV_TASK, MOD_WIN|MOD_SHIFT, VK_TAB);
1559 RegisterHotKey(m_hWnd, IDHK_SYS_PROPERTIES, MOD_WIN, VK_PAUSE);
1560 RegisterHotKey(m_hWnd, IDHK_DESKTOP, MOD_WIN, 'D');
1561 RegisterHotKey(m_hWnd, IDHK_PAGER, MOD_WIN, 'B');
1562
1563 return TRUE;
1564 }
1565
1566 HRESULT STDMETHODCALLTYPE Open()
1567 {
1568 RECT rcWnd;
1569
1570 /* Check if there's already a window created and try to show it.
1571 If it was somehow destroyed just create a new tray window. */
1572 if (m_hWnd != NULL && IsWindow())
1573 {
1574 if (!IsWindowVisible(m_hWnd))
1575 {
1576 CheckTrayWndPosition();
1577
1578 ShowWindow(SW_SHOW);
1579 }
1580
1581 return S_OK;
1582 }
1583
1584 DWORD dwExStyle = WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE;
1585 if (AlwaysOnTop)
1586 dwExStyle |= WS_EX_TOPMOST;
1587
1588 DWORD dwStyle = WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN |
1589 WS_BORDER | WS_THICKFRAME;
1590
1591 ZeroMemory(&rcWnd, sizeof(rcWnd));
1592 if (m_Position != (DWORD) -1)
1593 rcWnd = m_TrayRects[m_Position];
1594
1595 if (!Create(NULL, rcWnd, NULL, dwStyle, dwExStyle))
1596 return E_FAIL;
1597
1598 return S_OK;
1599 }
1600
1601 HRESULT STDMETHODCALLTYPE Close()
1602 {
1603 if (m_hWnd != NULL)
1604 {
1605 SendMessage(m_hWnd,
1606 WM_APP_TRAYDESTROY,
1607 0,
1608 0);
1609 }
1610
1611 return S_OK;
1612 }
1613
1614 HWND STDMETHODCALLTYPE GetHWND()
1615 {
1616 return m_hWnd;
1617 }
1618
1619 BOOL STDMETHODCALLTYPE IsSpecialHWND(IN HWND hWnd)
1620 {
1621 return (m_hWnd == hWnd ||
1622 (m_DesktopWnd != NULL && m_hWnd == m_DesktopWnd));
1623 }
1624
1625 BOOL STDMETHODCALLTYPE
1626 IsHorizontal()
1627 {
1628 return IsPosHorizontal();
1629 }
1630
1631 HFONT STDMETHODCALLTYPE GetCaptionFonts(OUT HFONT *phBoldCaption OPTIONAL)
1632 {
1633 if (phBoldCaption != NULL)
1634 *phBoldCaption = m_StartButton.GetFont();
1635
1636 return m_CaptionFont;
1637 }
1638
1639 DWORD WINAPI TrayPropertiesThread()
1640 {
1641 HWND hwnd;
1642 RECT posRect;
1643
1644 GetWindowRect(m_StartButton.m_hWnd, &posRect);
1645 hwnd = CreateWindowEx(0,
1646 WC_STATIC,
1647 NULL,
1648 WS_OVERLAPPED | WS_DISABLED | WS_CLIPSIBLINGS | WS_BORDER | SS_LEFT,
1649 posRect.left,
1650 posRect.top,
1651 posRect.right - posRect.left,
1652 posRect.bottom - posRect.top,
1653 NULL,
1654 NULL,
1655 NULL,
1656 NULL);
1657
1658 m_TrayPropertiesOwner = hwnd;
1659
1660 DisplayTrayProperties(hwnd);
1661
1662 m_TrayPropertiesOwner = NULL;
1663 DestroyWindow();
1664
1665 return 0;
1666 }
1667
1668 static DWORD WINAPI s_TrayPropertiesThread(IN OUT PVOID pParam)
1669 {
1670 CTrayWindow *This = (CTrayWindow*) pParam;
1671
1672 return This->TrayPropertiesThread();
1673 }
1674
1675 HWND STDMETHODCALLTYPE DisplayProperties()
1676 {
1677 HWND hTrayProp;
1678
1679 if (m_TrayPropertiesOwner)
1680 {
1681 hTrayProp = GetLastActivePopup(m_TrayPropertiesOwner);
1682 if (hTrayProp != NULL &&
1683 hTrayProp != m_TrayPropertiesOwner)
1684 {
1685 SetForegroundWindow(hTrayProp);
1686 return NULL;
1687 }
1688 }
1689
1690 CloseHandle(CreateThread(NULL, 0, s_TrayPropertiesThread, this, 0, NULL));
1691 return NULL;
1692 }
1693
1694 VOID OpenCommonStartMenuDirectory(IN HWND hWndOwner, IN LPCTSTR lpOperation)
1695 {
1696 WCHAR szDir[MAX_PATH];
1697
1698 if (SHGetSpecialFolderPath(hWndOwner,
1699 szDir,
1700 CSIDL_COMMON_STARTMENU,
1701 FALSE))
1702 {
1703 ShellExecute(hWndOwner,
1704 lpOperation,
1705 NULL,
1706 NULL,
1707 szDir,
1708 SW_SHOWNORMAL);
1709 }
1710 }
1711
1712 VOID OpenTaskManager(IN HWND hWndOwner)
1713 {
1714 ShellExecute(hWndOwner,
1715 TEXT("open"),
1716 TEXT("taskmgr.exe"),
1717 NULL,
1718 NULL,
1719 SW_SHOWNORMAL);
1720 }
1721
1722 BOOL STDMETHODCALLTYPE ExecContextMenuCmd(IN UINT uiCmd)
1723 {
1724 BOOL bHandled = TRUE;
1725
1726 switch (uiCmd)
1727 {
1728 case ID_SHELL_CMD_PROPERTIES:
1729 DisplayProperties();
1730 break;
1731
1732 case ID_SHELL_CMD_OPEN_ALL_USERS:
1733 OpenCommonStartMenuDirectory(m_hWnd,
1734 TEXT("open"));
1735 break;
1736
1737 case ID_SHELL_CMD_EXPLORE_ALL_USERS:
1738 OpenCommonStartMenuDirectory(m_hWnd,
1739 TEXT("explore"));
1740 break;
1741
1742 case ID_LOCKTASKBAR:
1743 if (SHRestricted(REST_CLASSICSHELL) == 0)
1744 {
1745 Lock(!Locked);
1746 }
1747 break;
1748
1749 case ID_SHELL_CMD_OPEN_TASKMGR:
1750 OpenTaskManager(m_hWnd);
1751 break;
1752
1753 case ID_SHELL_CMD_UNDO_ACTION:
1754 break;
1755
1756 case ID_SHELL_CMD_SHOW_DESKTOP:
1757 break;
1758
1759 case ID_SHELL_CMD_TILE_WND_H:
1760 TileWindows(NULL, MDITILE_HORIZONTAL, NULL, 0, NULL);
1761 break;
1762
1763 case ID_SHELL_CMD_TILE_WND_V:
1764 TileWindows(NULL, MDITILE_VERTICAL, NULL, 0, NULL);
1765 break;
1766
1767 case ID_SHELL_CMD_CASCADE_WND:
1768 CascadeWindows(NULL, MDITILE_SKIPDISABLED, NULL, 0, NULL);
1769 break;
1770
1771 case ID_SHELL_CMD_CUST_NOTIF:
1772 break;
1773
1774 case ID_SHELL_CMD_ADJUST_DAT:
1775 //FIXME: Use SHRunControlPanel
1776 ShellExecuteW(m_hWnd, NULL, L"timedate.cpl", NULL, NULL, SW_NORMAL);
1777 break;
1778
1779 default:
1780 TRACE("ITrayWindow::ExecContextMenuCmd(%u): Unhandled Command ID!\n", uiCmd);
1781 bHandled = FALSE;
1782 break;
1783 }
1784
1785 return bHandled;
1786 }
1787
1788 BOOL STDMETHODCALLTYPE Lock(IN BOOL bLock)
1789 {
1790 BOOL bPrevLock;
1791
1792 bPrevLock = Locked;
1793 if (Locked != bLock)
1794 {
1795 Locked = bLock;
1796
1797 if (m_TrayBandSite != NULL)
1798 {
1799 if (!SUCCEEDED(m_TrayBandSite->Lock(bLock)))
1800 {
1801 /* Reset?? */
1802 Locked = bPrevLock;
1803 }
1804 }
1805 }
1806
1807 return bPrevLock;
1808 }
1809
1810
1811 LRESULT DrawBackground(HDC hdc)
1812 {
1813 RECT rect;
1814 int partId;
1815
1816 GetClientRect(&rect);
1817
1818 if (m_Theme)
1819 {
1820 GetClientRect(&rect);
1821 switch (m_Position)
1822 {
1823 case ABE_LEFT:
1824 partId = TBP_BACKGROUNDLEFT;
1825 break;
1826 case ABE_TOP:
1827 partId = TBP_BACKGROUNDTOP;
1828 break;
1829 case ABE_RIGHT:
1830 partId = TBP_BACKGROUNDRIGHT;
1831 break;
1832 case ABE_BOTTOM:
1833 default:
1834 partId = TBP_BACKGROUNDBOTTOM;
1835 break;
1836 }
1837
1838 DrawThemeBackground(m_Theme, hdc, partId, 0, &rect, 0);
1839 }
1840
1841 return TRUE;
1842 }
1843
1844 LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1845 {
1846 HDC hdc = (HDC) wParam;
1847
1848 if (!m_Theme)
1849 {
1850 bHandled = FALSE;
1851 return 0;
1852 }
1853
1854 return DrawBackground(hdc);
1855 }
1856
1857 int DrawSizer(IN HRGN hRgn)
1858 {
1859 HDC hdc;
1860 RECT rect;
1861 int backoundPart;
1862
1863 GetWindowRect(m_hWnd, &rect);
1864 OffsetRect(&rect, -rect.left, -rect.top);
1865
1866 hdc = GetDCEx(m_hWnd, hRgn, DCX_WINDOW | DCX_INTERSECTRGN | DCX_PARENTCLIP);
1867
1868 switch (m_Position)
1869 {
1870 case ABE_LEFT:
1871 backoundPart = TBP_SIZINGBARLEFT;
1872 rect.left = rect.right - GetSystemMetrics(SM_CXSIZEFRAME);
1873 break;
1874 case ABE_TOP:
1875 backoundPart = TBP_SIZINGBARTOP;
1876 rect.top = rect.bottom - GetSystemMetrics(SM_CYSIZEFRAME);
1877 break;
1878 case ABE_RIGHT:
1879 backoundPart = TBP_SIZINGBARRIGHT;
1880 rect.right = rect.left + GetSystemMetrics(SM_CXSIZEFRAME);
1881 break;
1882 case ABE_BOTTOM:
1883 default:
1884 backoundPart = TBP_SIZINGBARBOTTOM;
1885 rect.bottom = rect.top + GetSystemMetrics(SM_CYSIZEFRAME);
1886 break;
1887 }
1888
1889 DrawThemeBackground(m_Theme, hdc, backoundPart, 0, &rect, 0);
1890
1891 ReleaseDC(m_hWnd, hdc);
1892 return 0;
1893 }
1894
1895 DWORD WINAPI RunFileDlgThread()
1896 {
1897 HWND hwnd;
1898 RECT posRect;
1899
1900 GetWindowRect(m_StartButton.m_hWnd, &posRect);
1901
1902 hwnd = CreateWindowEx(0,
1903 WC_STATIC,
1904 NULL,
1905 WS_OVERLAPPED | WS_DISABLED | WS_CLIPSIBLINGS | WS_BORDER | SS_LEFT,
1906 posRect.left,
1907 posRect.top,
1908 posRect.right - posRect.left,
1909 posRect.bottom - posRect.top,
1910 NULL,
1911 NULL,
1912 NULL,
1913 NULL);
1914
1915 m_RunFileDlgOwner = hwnd;
1916
1917 RunFileDlg(hwnd, NULL, NULL, NULL, NULL, RFF_CALCDIRECTORY);
1918
1919 m_RunFileDlgOwner = NULL;
1920 ::DestroyWindow(hwnd);
1921
1922 return 0;
1923 }
1924
1925 static DWORD WINAPI s_RunFileDlgThread(IN OUT PVOID pParam)
1926 {
1927 CTrayWindow * This = (CTrayWindow*) pParam;
1928 return This->RunFileDlgThread();
1929 }
1930
1931 void DisplayRunFileDlg()
1932 {
1933 HWND hRunDlg;
1934 if (m_RunFileDlgOwner)
1935 {
1936 hRunDlg = GetLastActivePopup(m_RunFileDlgOwner);
1937 if (hRunDlg != NULL &&
1938 hRunDlg != m_RunFileDlgOwner)
1939 {
1940 SetForegroundWindow(hRunDlg);
1941 return;
1942 }
1943 }
1944
1945 CloseHandle(CreateThread(NULL, 0, s_RunFileDlgThread, this, 0, NULL));
1946 }
1947
1948 void PopupStartMenu()
1949 {
1950 if (m_StartMenuPopup != NULL)
1951 {
1952 POINTL pt;
1953 RECTL rcExclude;
1954 DWORD dwFlags = 0;
1955
1956 if (GetWindowRect(m_StartButton.m_hWnd, (RECT*) &rcExclude))
1957 {
1958 switch (m_Position)
1959 {
1960 case ABE_BOTTOM:
1961 pt.x = rcExclude.left;
1962 pt.y = rcExclude.top;
1963 dwFlags |= MPPF_BOTTOM;
1964 break;
1965 case ABE_TOP:
1966 case ABE_LEFT:
1967 pt.x = rcExclude.left;
1968 pt.y = rcExclude.bottom;
1969 dwFlags |= MPPF_TOP | MPPF_ALIGN_RIGHT;
1970 break;
1971 case ABE_RIGHT:
1972 pt.x = rcExclude.right;
1973 pt.y = rcExclude.bottom;
1974 dwFlags |= MPPF_TOP | MPPF_ALIGN_LEFT;
1975 break;
1976 }
1977
1978 m_StartMenuPopup->Popup(&pt, &rcExclude, dwFlags);
1979
1980 m_StartButton.SendMessageW(BM_SETSTATE, TRUE, 0);
1981 }
1982 }
1983 }
1984
1985 void ProcessMouseTracking()
1986 {
1987 RECT rcCurrent;
1988 POINT pt;
1989 BOOL over;
1990 UINT state = m_AutoHideState;
1991
1992 GetCursorPos(&pt);
1993 GetWindowRect(m_hWnd, &rcCurrent);
1994 over = PtInRect(&rcCurrent, pt);
1995
1996 if (m_StartButton.SendMessage( BM_GETSTATE, 0, 0) != BST_UNCHECKED)
1997 {
1998 over = TRUE;
1999 }
2000
2001 if (over)
2002 {
2003 if (state == AUTOHIDE_HIDING)
2004 {
2005 TRACE("AutoHide cancelling hide.\n");
2006 m_AutoHideState = AUTOHIDE_SHOWING;
2007 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_INTERVAL_ANIMATING, NULL);
2008 }
2009 else if (state == AUTOHIDE_HIDDEN)
2010 {
2011 TRACE("AutoHide starting show.\n");
2012 m_AutoHideState = AUTOHIDE_SHOWING;
2013 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_DELAY_SHOW, NULL);
2014 }
2015 }
2016 else
2017 {
2018 if (state == AUTOHIDE_SHOWING)
2019 {
2020 TRACE("AutoHide cancelling show.\n");
2021 m_AutoHideState = AUTOHIDE_HIDING;
2022 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_INTERVAL_ANIMATING, NULL);
2023 }
2024 else if (state == AUTOHIDE_SHOWN)
2025 {
2026 TRACE("AutoHide starting hide.\n");
2027 m_AutoHideState = AUTOHIDE_HIDING;
2028 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_DELAY_HIDE, NULL);
2029 }
2030
2031 KillTimer(TIMER_ID_MOUSETRACK);
2032 }
2033 }
2034
2035 void ProcessAutoHide()
2036 {
2037 RECT rc = m_TrayRects[m_Position];
2038 INT w = m_TraySize.cx - GetSystemMetrics(SM_CXBORDER) * 2 - 1;
2039 INT h = m_TraySize.cy - GetSystemMetrics(SM_CYBORDER) * 2 - 1;
2040
2041 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);
2042
2043 switch (m_AutoHideState)
2044 {
2045 case AUTOHIDE_HIDING:
2046 switch (m_Position)
2047 {
2048 case ABE_LEFT:
2049 m_AutoHideOffset.cy = 0;
2050 m_AutoHideOffset.cx -= AUTOHIDE_SPEED_HIDE;
2051 if (m_AutoHideOffset.cx < -w)
2052 m_AutoHideOffset.cx = -w;
2053 break;
2054 case ABE_TOP:
2055 m_AutoHideOffset.cx = 0;
2056 m_AutoHideOffset.cy -= AUTOHIDE_SPEED_HIDE;
2057 if (m_AutoHideOffset.cy < -h)
2058 m_AutoHideOffset.cy = -h;
2059 break;
2060 case ABE_RIGHT:
2061 m_AutoHideOffset.cy = 0;
2062 m_AutoHideOffset.cx += AUTOHIDE_SPEED_HIDE;
2063 if (m_AutoHideOffset.cx > w)
2064 m_AutoHideOffset.cx = w;
2065 break;
2066 case ABE_BOTTOM:
2067 m_AutoHideOffset.cx = 0;
2068 m_AutoHideOffset.cy += AUTOHIDE_SPEED_HIDE;
2069 if (m_AutoHideOffset.cy > h)
2070 m_AutoHideOffset.cy = h;
2071 break;
2072 }
2073
2074 if (m_AutoHideOffset.cx != w && m_AutoHideOffset.cy != h)
2075 {
2076 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_INTERVAL_ANIMATING, NULL);
2077 break;
2078 }
2079
2080 /* fallthrough */
2081 case AUTOHIDE_HIDDEN:
2082
2083 switch (m_Position)
2084 {
2085 case ABE_LEFT:
2086 m_AutoHideOffset.cx = -w;
2087 m_AutoHideOffset.cy = 0;
2088 break;
2089 case ABE_TOP:
2090 m_AutoHideOffset.cx = 0;
2091 m_AutoHideOffset.cy = -h;
2092 break;
2093 case ABE_RIGHT:
2094 m_AutoHideOffset.cx = w;
2095 m_AutoHideOffset.cy = 0;
2096 break;
2097 case ABE_BOTTOM:
2098 m_AutoHideOffset.cx = 0;
2099 m_AutoHideOffset.cy = h;
2100 break;
2101 }
2102
2103 KillTimer(TIMER_ID_AUTOHIDE);
2104 m_AutoHideState = AUTOHIDE_HIDDEN;
2105 break;
2106
2107 case AUTOHIDE_SHOWING:
2108 if (m_AutoHideOffset.cx >= AUTOHIDE_SPEED_SHOW)
2109 {
2110 m_AutoHideOffset.cx -= AUTOHIDE_SPEED_SHOW;
2111 }
2112 else if (m_AutoHideOffset.cx <= -AUTOHIDE_SPEED_SHOW)
2113 {
2114 m_AutoHideOffset.cx += AUTOHIDE_SPEED_SHOW;
2115 }
2116 else
2117 {
2118 m_AutoHideOffset.cx = 0;
2119 }
2120
2121 if (m_AutoHideOffset.cy >= AUTOHIDE_SPEED_SHOW)
2122 {
2123 m_AutoHideOffset.cy -= AUTOHIDE_SPEED_SHOW;
2124 }
2125 else if (m_AutoHideOffset.cy <= -AUTOHIDE_SPEED_SHOW)
2126 {
2127 m_AutoHideOffset.cy += AUTOHIDE_SPEED_SHOW;
2128 }
2129 else
2130 {
2131 m_AutoHideOffset.cy = 0;
2132 }
2133
2134 if (m_AutoHideOffset.cx != 0 || m_AutoHideOffset.cy != 0)
2135 {
2136 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_INTERVAL_ANIMATING, NULL);
2137 break;
2138 }
2139
2140 /* fallthrough */
2141 case AUTOHIDE_SHOWN:
2142
2143 KillTimer(TIMER_ID_AUTOHIDE);
2144 m_AutoHideState = AUTOHIDE_SHOWN;
2145 break;
2146 }
2147
2148 rc.left += m_AutoHideOffset.cx;
2149 rc.right += m_AutoHideOffset.cx;
2150 rc.top += m_AutoHideOffset.cy;
2151 rc.bottom += m_AutoHideOffset.cy;
2152
2153 TRACE("AutoHide Changing position to (%d, %d, %d, %d) and state=%u.\n", rc.left, rc.top, rc.right, rc.bottom, m_AutoHideState);
2154 SetWindowPos(NULL, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER);
2155 }
2156
2157 LRESULT OnDisplayChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2158 {
2159 /* Load the saved tray window settings */
2160 RegLoadSettings();
2161
2162 /* Move the tray window to the right position and resize it if neccessary */
2163 CheckTrayWndPosition();
2164
2165 /* Align all controls on the tray window */
2166 AlignControls(NULL);
2167
2168 return TRUE;
2169 }
2170
2171 LRESULT OnCopyData(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2172 {
2173 if (m_TrayNotify)
2174 {
2175 TrayNotify_NotifyMsg(m_TrayNotifyInstance, wParam, lParam);
2176 }
2177 return TRUE;
2178 }
2179
2180 LRESULT OnNcPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2181 {
2182 if (!m_Theme)
2183 {
2184 bHandled = FALSE;
2185 return 0;
2186 }
2187
2188 return DrawSizer((HRGN) wParam);
2189 }
2190
2191 LRESULT OnCtlColorBtn(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2192 {
2193 SetBkMode((HDC) wParam, TRANSPARENT);
2194 return (LRESULT) GetStockObject(HOLLOW_BRUSH);
2195 }
2196
2197 LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2198 {
2199 RECT rcClient;
2200 POINT pt;
2201
2202 if (Locked)
2203 {
2204 /* The user may not be able to resize the tray window.
2205 Pretend like the window is not sizeable when the user
2206 clicks on the border. */
2207 return HTBORDER;
2208 }
2209
2210 SetLastError(ERROR_SUCCESS);
2211 if (GetClientRect(&rcClient) &&
2212 (MapWindowPoints(m_hWnd, NULL, (LPPOINT) &rcClient, 2) != 0 || GetLastError() == ERROR_SUCCESS))
2213 {
2214 pt.x = (SHORT) LOWORD(lParam);
2215 pt.y = (SHORT) HIWORD(lParam);
2216
2217 if (PtInRect(&rcClient,
2218 pt))
2219 {
2220 /* The user is trying to drag the tray window */
2221 return HTCAPTION;
2222 }
2223
2224 /* Depending on the position of the tray window, allow only
2225 changing the border next to the monitor working area */
2226 switch (m_Position)
2227 {
2228 case ABE_TOP:
2229 if (pt.y > rcClient.bottom)
2230 return HTBOTTOM;
2231 break;
2232 case ABE_LEFT:
2233 if (pt.x > rcClient.right)
2234 return HTRIGHT;
2235 break;
2236 case ABE_RIGHT:
2237 if (pt.x < rcClient.left)
2238 return HTLEFT;
2239 break;
2240 case ABE_BOTTOM:
2241 default:
2242 if (pt.y < rcClient.top)
2243 return HTTOP;
2244 break;
2245 }
2246 }
2247 return HTBORDER;
2248 return TRUE;
2249 }
2250
2251 LRESULT OnMoving(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2252 {
2253 POINT ptCursor;
2254 PRECT pRect = (PRECT) lParam;
2255
2256 /* We need to ensure that an application can not accidently
2257 move the tray window (using SetWindowPos). However, we still
2258 need to be able to move the window in case the user wants to
2259 drag the tray window to another position or in case the user
2260 wants to resize the tray window. */
2261 if (!Locked && GetCursorPos(&ptCursor))
2262 {
2263 IsDragging = TRUE;
2264 m_DraggingPosition = GetDraggingRectFromPt(ptCursor, pRect, &m_DraggingMonitor);
2265 }
2266 else
2267 {
2268 *pRect = m_TrayRects[m_Position];
2269
2270 if (AutoHide)
2271 {
2272 pRect->left += m_AutoHideOffset.cx;
2273 pRect->right += m_AutoHideOffset.cx;
2274 pRect->top += m_AutoHideOffset.cy;
2275 pRect->bottom += m_AutoHideOffset.cy;
2276 }
2277 }
2278 return TRUE;
2279 }
2280
2281 LRESULT OnSizing(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2282 {
2283 PRECT pRect = (PRECT) lParam;
2284
2285 if (!Locked)
2286 {
2287 CalculateValidSize(m_Position, pRect);
2288 }
2289 else
2290 {
2291 *pRect = m_TrayRects[m_Position];
2292
2293 if (AutoHide)
2294 {
2295 pRect->left += m_AutoHideOffset.cx;
2296 pRect->right += m_AutoHideOffset.cx;
2297 pRect->top += m_AutoHideOffset.cy;
2298 pRect->bottom += m_AutoHideOffset.cy;
2299 }
2300 }
2301 return TRUE;
2302 }
2303
2304 LRESULT OnWindowPosChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2305 {
2306 ChangingWinPos((LPWINDOWPOS) lParam);
2307 return TRUE;
2308 }
2309
2310 LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2311 {
2312 RECT rcClient;
2313 InvalidateRect(NULL, TRUE);
2314 if (wParam == SIZE_RESTORED && lParam == 0)
2315 {
2316 ResizeWorkArea();
2317 /* Clip the tray window on multi monitor systems so the edges can't
2318 overlap into another monitor */
2319 ApplyClipping(TRUE);
2320
2321 if (!GetClientRect(&rcClient))
2322 {
2323 return FALSE;
2324 }
2325 }
2326 else
2327 {
2328 rcClient.left = rcClient.top = 0;
2329 rcClient.right = LOWORD(lParam);
2330 rcClient.bottom = HIWORD(lParam);
2331 }
2332
2333 AlignControls(&rcClient);
2334 return TRUE;
2335 }
2336
2337 LRESULT OnEnterSizeMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2338 {
2339 InSizeMove = TRUE;
2340 IsDragging = FALSE;
2341 if (!Locked)
2342 {
2343 /* Remove the clipping on multi monitor systems while dragging around */
2344 ApplyClipping(FALSE);
2345 }
2346 return TRUE;
2347 }
2348
2349 LRESULT OnExitSizeMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2350 {
2351 InSizeMove = FALSE;
2352 if (!Locked)
2353 {
2354 /* Apply clipping */
2355 PostMessage(m_hWnd, WM_SIZE, SIZE_RESTORED, 0);
2356 }
2357 return TRUE;
2358 }
2359
2360 LRESULT OnSysChar(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2361 {
2362 switch (wParam)
2363 {
2364 case TEXT(' '):
2365 {
2366 /* The user pressed Alt+Space, this usually brings up the system menu of a window.
2367 The tray window needs to handle this specially, since it normally doesn't have
2368 a system menu. */
2369
2370 static const UINT uidDisableItem [] = {
2371 SC_RESTORE,
2372 SC_MOVE,
2373 SC_SIZE,
2374 SC_MAXIMIZE,
2375 SC_MINIMIZE,
2376 };
2377 HMENU hSysMenu;
2378 INT i;
2379 UINT uId;
2380
2381 /* temporarily enable the system menu */
2382 SetWindowStyle(m_hWnd, WS_SYSMENU, WS_SYSMENU);
2383
2384 hSysMenu = GetSystemMenu(m_hWnd, FALSE);
2385 if (hSysMenu != NULL)
2386 {
2387 /* Disable all items that are not relevant */
2388 for (i = 0; i != sizeof(uidDisableItem) / sizeof(uidDisableItem[0]); i++)
2389 {
2390 EnableMenuItem(hSysMenu,
2391 uidDisableItem[i],
2392 MF_BYCOMMAND | MF_GRAYED);
2393 }
2394
2395 EnableMenuItem(hSysMenu,
2396 SC_CLOSE,
2397 MF_BYCOMMAND |
2398 (SHRestricted(REST_NOCLOSE) ? MF_GRAYED : MF_ENABLED));
2399
2400 /* Display the system menu */
2401 uId = TrackMenu(
2402 hSysMenu,
2403 NULL,
2404 m_StartButton.m_hWnd,
2405 m_Position != ABE_TOP,
2406 FALSE);
2407 if (uId != 0)
2408 {
2409 SendMessage(m_hWnd, WM_SYSCOMMAND, (WPARAM) uId, 0);
2410 }
2411 }
2412
2413 /* revert the system menu window style */
2414 SetWindowStyle(m_hWnd, WS_SYSMENU, 0);
2415 break;
2416 }
2417
2418 default:
2419 bHandled = FALSE;
2420 }
2421 return TRUE;
2422 }
2423
2424 LRESULT OnNcRButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2425 {
2426 /* We want the user to be able to get a context menu even on the nonclient
2427 area (including the sizing border)! */
2428 uMsg = WM_CONTEXTMENU;
2429 wParam = (WPARAM) m_hWnd;
2430
2431 return OnContextMenu(uMsg, wParam, lParam, bHandled);
2432 }
2433
2434 LRESULT OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2435 {
2436 LRESULT Ret = FALSE;
2437 POINT pt, *ppt = NULL;
2438 HWND hWndExclude = NULL;
2439
2440 /* Check if the administrator has forbidden access to context menus */
2441 if (SHRestricted(REST_NOTRAYCONTEXTMENU))
2442 return FALSE;
2443
2444 pt.x = (SHORT) LOWORD(lParam);
2445 pt.y = (SHORT) HIWORD(lParam);
2446
2447 if (pt.x != -1 || pt.y != -1)
2448 ppt = &pt;
2449 else
2450 hWndExclude = m_StartButton.m_hWnd;
2451
2452 if ((HWND) wParam == m_StartButton.m_hWnd)
2453 {
2454 /* Make sure we can't track the context menu if the start
2455 menu is currently being shown */
2456 if (!(m_StartButton.SendMessage(BM_GETSTATE, 0, 0) & BST_PUSHED))
2457 {
2458 CComPtr<IContextMenu> ctxMenu;
2459 StartMenuBtnCtxMenuCreator(this, m_hWnd, &ctxMenu);
2460 TrackCtxMenu(ctxMenu, ppt, hWndExclude, m_Position == ABE_BOTTOM, this);
2461 }
2462 }
2463 else
2464 {
2465 /* See if the context menu should be handled by the task band site */
2466 if (ppt != NULL && m_TrayBandSite != NULL)
2467 {
2468 HWND hWndAtPt;
2469 POINT ptClient = *ppt;
2470
2471 /* Convert the coordinates to client-coordinates */
2472 MapWindowPoints(NULL, m_hWnd, &ptClient, 1);
2473
2474 hWndAtPt = ChildWindowFromPoint(m_hWnd, ptClient);
2475 if (hWndAtPt != NULL &&
2476 (hWndAtPt == m_Rebar || IsChild(m_Rebar, hWndAtPt)))
2477 {
2478 /* Check if the user clicked on the task switch window */
2479 ptClient = *ppt;
2480 MapWindowPoints(NULL, m_Rebar, &ptClient, 1);
2481
2482 hWndAtPt = ChildWindowFromPointEx(m_Rebar, ptClient, CWP_SKIPINVISIBLE | CWP_SKIPDISABLED);
2483 if (hWndAtPt == m_TaskSwitch)
2484 goto HandleTrayContextMenu;
2485
2486 /* Forward the message to the task band site */
2487 m_TrayBandSite->ProcessMessage(m_hWnd, uMsg, wParam, lParam, &Ret);
2488 }
2489 else
2490 goto HandleTrayContextMenu;
2491 }
2492 else
2493 {
2494 HandleTrayContextMenu:
2495 /* Tray the default tray window context menu */
2496 CComPtr<IContextMenu> ctxMenu;
2497 TrayWindowCtxMenuCreator(this, m_hWnd, &ctxMenu);
2498 TrackCtxMenu(ctxMenu, ppt, NULL, FALSE, this);
2499 }
2500 }
2501 return Ret;
2502 }
2503
2504 LRESULT OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2505 {
2506 LRESULT Ret = FALSE;
2507 /* FIXME: We can't check with IsChild whether the hwnd is somewhere inside
2508 the rebar control! But we shouldn't forward messages that the band
2509 site doesn't handle, such as other controls (start button, tray window */
2510
2511 HRESULT hr = E_FAIL;
2512
2513 if (m_TrayBandSite)
2514 {
2515 hr = m_TrayBandSite->ProcessMessage(m_hWnd, uMsg, wParam, lParam, &Ret);
2516 if (SUCCEEDED(hr))
2517 return Ret;
2518 }
2519
2520 if (m_TrayBandSite == NULL || FAILED(hr))
2521 {
2522 const NMHDR *nmh = (const NMHDR *) lParam;
2523
2524 if (nmh->hwndFrom == m_TrayNotify)
2525 {
2526 switch (nmh->code)
2527 {
2528 case NTNWM_REALIGN:
2529 /* Cause all controls to be aligned */
2530 PostMessage(m_hWnd, WM_SIZE, SIZE_RESTORED, 0);
2531 break;
2532 }
2533 }
2534 }
2535 return Ret;
2536 }
2537
2538 LRESULT OnNcLButtonDblClick(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2539 {
2540 /* We "handle" this message so users can't cause a weird maximize/restore
2541 window animation when double-clicking the tray window! */
2542
2543 /* We should forward mouse messages to child windows here.
2544 Right now, this is only clock double-click */
2545 RECT rcClock;
2546 if (TrayNotify_GetClockRect(m_TrayNotifyInstance, &rcClock))
2547 {
2548 POINT ptClick;
2549 ptClick.x = MAKEPOINTS(lParam).x;
2550 ptClick.y = MAKEPOINTS(lParam).y;
2551 if (PtInRect(&rcClock, ptClick))
2552 {
2553 //FIXME: use SHRunControlPanel
2554 ShellExecuteW(m_hWnd, NULL, L"timedate.cpl", NULL, NULL, SW_NORMAL);
2555 }
2556 }
2557 return TRUE;
2558 }
2559
2560 LRESULT OnAppTrayDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2561 {
2562 DestroyWindow();
2563 return TRUE;
2564 }
2565
2566 LRESULT OnOpenStartMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2567 {
2568 HWND hwndStartMenu;
2569 HRESULT hr = IUnknown_GetWindow(m_StartMenuPopup, &hwndStartMenu);
2570 if (FAILED_UNEXPECTEDLY(hr))
2571 return FALSE;
2572
2573 if (IsWindowVisible(hwndStartMenu))
2574 {
2575 m_StartMenuPopup->OnSelect(MPOS_CANCELLEVEL);
2576 }
2577 else
2578 {
2579 PopupStartMenu();
2580 }
2581
2582 return TRUE;
2583 }
2584
2585 LRESULT DoExitWindows()
2586 {
2587 ExitWindowsDialog(m_hWnd);
2588 return 0;
2589 }
2590
2591 LRESULT OnDoExitWindows(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2592 {
2593 /*
2594 * TWM_DOEXITWINDOWS is send by the CDesktopBrowserr to us to
2595 * show the shutdown dialog
2596 */
2597 return DoExitWindows();
2598 }
2599
2600 LRESULT OnSysCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2601 {
2602 if (wParam == SC_CLOSE)
2603 {
2604 return DoExitWindows();
2605 }
2606
2607 bHandled = FALSE;
2608 return TRUE;
2609 }
2610
2611 HRESULT ExecResourceCmd(int id)
2612 {
2613 WCHAR szCommand[256];
2614 WCHAR *pszParameters;
2615
2616 if (!LoadString(hExplorerInstance,
2617 id,
2618 szCommand,
2619 sizeof(szCommand) / sizeof(szCommand[0])))
2620 {
2621 return E_FAIL;
2622 }
2623
2624 pszParameters = wcschr(szCommand, L'>');
2625 if (!pszParameters)
2626 return E_FAIL;
2627
2628 *pszParameters = 0;
2629 pszParameters++;
2630
2631 ShellExecuteW(m_hWnd, NULL, szCommand, pszParameters, NULL, 0);
2632 return S_OK;
2633 }
2634
2635 LRESULT OnHotkey(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2636 {
2637 switch (wParam)
2638 {
2639 case IDHK_RUN:
2640 DisplayRunFileDlg();
2641 break;
2642 case IDHK_HELP:
2643 ExecResourceCmd(IDS_HELP_COMMAND);
2644 break;
2645 case IDHK_EXPLORE:
2646 ShellExecuteW(0, L"explore", NULL, NULL, NULL, 1);
2647 break;
2648 case IDHK_FIND:
2649 SHFindFiles(NULL, NULL);
2650 break;
2651 case IDHK_FIND_COMPUTER:
2652 SHFindComputer(NULL, NULL);
2653 break;
2654 case IDHK_SYS_PROPERTIES:
2655 //FIXME: Use SHRunControlPanel
2656 ShellExecuteW(m_hWnd, NULL, L"sysdm.cpl", NULL, NULL, SW_NORMAL);
2657 break;
2658 case IDHK_NEXT_TASK:
2659 break;
2660 case IDHK_PREV_TASK:
2661 break;
2662 case IDHK_MINIMIZE_ALL:
2663 break;
2664 case IDHK_RESTORE_ALL:
2665 break;
2666 case IDHK_DESKTOP:
2667 break;
2668 case IDHK_PAGER:
2669 break;
2670 }
2671
2672 return 0;
2673 }
2674
2675 LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2676 {
2677 LRESULT Ret = FALSE;
2678
2679 if ((HWND) lParam == m_StartButton.m_hWnd)
2680 {
2681 PopupStartMenu();
2682 return FALSE;
2683 }
2684
2685 if (m_TrayBandSite == NULL || FAILED_UNEXPECTEDLY(m_TrayBandSite->ProcessMessage(m_hWnd, uMsg, wParam, lParam, &Ret)))
2686 {
2687 switch (LOWORD(wParam))
2688 {
2689 /* FIXME: Handle these commands as well */
2690 case IDM_TASKBARANDSTARTMENU:
2691 DisplayProperties();
2692 break;
2693
2694 case IDM_SEARCH:
2695 SHFindFiles(NULL, NULL);
2696 break;
2697
2698 case IDM_HELPANDSUPPORT:
2699 ExecResourceCmd(IDS_HELP_COMMAND);
2700 break;
2701
2702 case IDM_RUN:
2703 DisplayRunFileDlg();
2704 break;
2705
2706 /* FIXME: Handle these commands as well */
2707 case IDM_SYNCHRONIZE:
2708 case IDM_LOGOFF:
2709 case IDM_DISCONNECT:
2710 case IDM_UNDOCKCOMPUTER:
2711 break;
2712
2713 case IDM_SHUTDOWN:
2714 DoExitWindows();
2715 break;
2716 }
2717 }
2718 return Ret;
2719 }
2720
2721 LRESULT OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2722 {
2723 if (AutoHide)
2724 {
2725 SetTimer(TIMER_ID_MOUSETRACK, MOUSETRACK_INTERVAL, NULL);
2726 }
2727
2728 return TRUE;
2729 }
2730
2731 LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2732 {
2733 if (wParam == TIMER_ID_MOUSETRACK)
2734 {
2735 ProcessMouseTracking();
2736 }
2737 else if (wParam == TIMER_ID_AUTOHIDE)
2738 {
2739 ProcessAutoHide();
2740 }
2741
2742 bHandled = FALSE;
2743 return TRUE;
2744 }
2745
2746 LRESULT OnRebarAutoSize(INT code, LPNMHDR nmhdr, BOOL& bHandled)
2747 {
2748 #if 0
2749 LPNMRBAUTOSIZE as = (LPNMRBAUTOSIZE) nmhdr;
2750
2751 if (!as->fChanged)
2752 return 0;
2753
2754 RECT rc;
2755 ::GetWindowRect(m_hWnd, &rc);
2756
2757 SIZE szWindow = {
2758 rc.right - rc.left,
2759 rc.bottom - rc.top };
2760 SIZE szTarget = {
2761 as->rcTarget.right - as->rcTarget.left,
2762 as->rcTarget.bottom - as->rcTarget.top };
2763 SIZE szActual = {
2764 as->rcActual.right - as->rcActual.left,
2765 as->rcActual.bottom - as->rcActual.top };
2766
2767 SIZE borders = {
2768 szWindow.cx - szTarget.cx,
2769 szWindow.cy - szTarget.cx,
2770 };
2771
2772 switch (m_Position)
2773 {
2774 case ABE_LEFT:
2775 szWindow.cx = szActual.cx + borders.cx;
2776 break;
2777 case ABE_TOP:
2778 szWindow.cy = szActual.cy + borders.cy;
2779 break;
2780 case ABE_RIGHT:
2781 szWindow.cx = szActual.cx + borders.cx;
2782 rc.left = rc.right - szWindow.cy;
2783 break;
2784 case ABE_BOTTOM:
2785 szWindow.cy = szActual.cy + borders.cy;
2786 rc.top = rc.bottom - szWindow.cy;
2787 break;
2788 }
2789
2790 SetWindowPos(NULL, rc.left, rc.top, szWindow.cx, szWindow.cy, SWP_NOACTIVATE | SWP_NOZORDER);
2791 #else
2792 bHandled = FALSE;
2793 #endif
2794 return 0;
2795 }
2796
2797 DECLARE_WND_CLASS_EX(szTrayWndClass, CS_DBLCLKS, COLOR_3DFACE)
2798
2799 BEGIN_MSG_MAP(CTrayWindow)
2800 if (m_StartMenuBand != NULL)
2801 {
2802 MSG Msg;
2803 LRESULT lRet;
2804
2805 Msg.hwnd = m_hWnd;
2806 Msg.message = uMsg;
2807 Msg.wParam = wParam;
2808 Msg.lParam = lParam;
2809
2810 if (m_StartMenuBand->TranslateMenuMessage(&Msg, &lRet) == S_OK)
2811 {
2812 return lRet;
2813 }
2814
2815 wParam = Msg.wParam;
2816 lParam = Msg.lParam;
2817 }
2818 MESSAGE_HANDLER(WM_THEMECHANGED, OnThemeChanged)
2819 NOTIFY_CODE_HANDLER(RBN_AUTOSIZE, OnRebarAutoSize) // Doesn't quite work ;P
2820 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
2821 MESSAGE_HANDLER(WM_SIZE, OnSize)
2822 MESSAGE_HANDLER(WM_CREATE, OnCreate)
2823 /*MESSAGE_HANDLER(WM_DESTROY, OnDestroy)*/
2824 MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTest)
2825 MESSAGE_HANDLER(WM_COMMAND, OnCommand)
2826 MESSAGE_HANDLER(WM_SYSCOMMAND, OnSysCommand)
2827 MESSAGE_HANDLER(WM_NOTIFY, OnNotify)
2828 MESSAGE_HANDLER(WM_CONTEXTMENU, OnContextMenu)
2829 MESSAGE_HANDLER(WM_TIMER, OnTimer)
2830 MESSAGE_HANDLER(WM_DISPLAYCHANGE, OnDisplayChange)
2831 MESSAGE_HANDLER(WM_COPYDATA, OnCopyData)
2832 MESSAGE_HANDLER(WM_NCPAINT, OnNcPaint)
2833 MESSAGE_HANDLER(WM_CTLCOLORBTN, OnCtlColorBtn)
2834 MESSAGE_HANDLER(WM_MOVING, OnMoving)
2835 MESSAGE_HANDLER(WM_SIZING, OnSizing)
2836 MESSAGE_HANDLER(WM_WINDOWPOSCHANGING, OnWindowPosChange)
2837 MESSAGE_HANDLER(WM_ENTERSIZEMOVE, OnEnterSizeMove)
2838 MESSAGE_HANDLER(WM_EXITSIZEMOVE, OnExitSizeMove)
2839 MESSAGE_HANDLER(WM_SYSCHAR, OnSysChar)
2840 MESSAGE_HANDLER(WM_NCRBUTTONUP, OnNcRButtonUp)
2841 MESSAGE_HANDLER(WM_NCLBUTTONDBLCLK, OnNcLButtonDblClick)
2842 MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
2843 MESSAGE_HANDLER(WM_NCMOUSEMOVE, OnMouseMove)
2844 MESSAGE_HANDLER(WM_APP_TRAYDESTROY, OnAppTrayDestroy)
2845 MESSAGE_HANDLER(TWM_OPENSTARTMENU, OnOpenStartMenu)
2846 MESSAGE_HANDLER(TWM_DOEXITWINDOWS, OnDoExitWindows)
2847 MESSAGE_HANDLER(WM_HOTKEY, OnHotkey)
2848 ALT_MSG_MAP(1)
2849 END_MSG_MAP()
2850
2851 /*****************************************************************************/
2852
2853 VOID TrayProcessMessages()
2854 {
2855 MSG Msg;
2856
2857 /* FIXME: We should keep a reference here... */
2858
2859 while (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE))
2860 {
2861 if (Msg.message == WM_QUIT)
2862 break;
2863
2864 if (m_StartMenuBand == NULL ||
2865 m_StartMenuBand->IsMenuMessage(&Msg) != S_OK)
2866 {
2867 TranslateMessage(&Msg);
2868 DispatchMessage(&Msg);
2869 }
2870 }
2871 }
2872
2873 VOID TrayMessageLoop()
2874 {
2875 MSG Msg;
2876 BOOL Ret;
2877
2878 /* FIXME: We should keep a reference here... */
2879
2880 while (true)
2881 {
2882 Ret = GetMessage(&Msg, NULL, 0, 0);
2883
2884 if (!Ret || Ret == -1)
2885 break;
2886
2887 if (m_StartMenuBand == NULL ||
2888 m_StartMenuBand->IsMenuMessage(&Msg) != S_OK)
2889 {
2890 TranslateMessage(&Msg);
2891 DispatchMessage(&Msg);
2892 }
2893 }
2894 }
2895
2896 /*
2897 * IShellDesktopTray
2898 *
2899 * NOTE: this is a very windows-specific COM interface used by SHCreateDesktop()!
2900 * These are the calls I observed, it may be wrong/incomplete/buggy!!!
2901 * The reason we implement it is because we have to use SHCreateDesktop() so
2902 * that the shell provides the desktop window and all the features that come
2903 * with it (especially positioning of desktop icons)
2904 */
2905
2906 virtual ULONG STDMETHODCALLTYPE GetState()
2907 {
2908 /* FIXME: Return ABS_ flags? */
2909 TRACE("IShellDesktopTray::GetState() unimplemented!\n");
2910 return 0;
2911 }
2912
2913 virtual HRESULT STDMETHODCALLTYPE GetTrayWindow(OUT HWND *phWndTray)
2914 {
2915 TRACE("IShellDesktopTray::GetTrayWindow(0x%p)\n", phWndTray);
2916 *phWndTray = m_hWnd;
2917 return S_OK;
2918 }
2919
2920 virtual HRESULT STDMETHODCALLTYPE RegisterDesktopWindow(IN HWND hWndDesktop)
2921 {
2922 TRACE("IShellDesktopTray::RegisterDesktopWindow(0x%p)\n", hWndDesktop);
2923
2924 m_DesktopWnd = hWndDesktop;
2925 return S_OK;
2926 }
2927
2928 virtual HRESULT STDMETHODCALLTYPE Unknown(IN DWORD dwUnknown1, IN DWORD dwUnknown2)
2929 {
2930 TRACE("IShellDesktopTray::Unknown(%u,%u) unimplemented!\n", dwUnknown1, dwUnknown2);
2931 return S_OK;
2932 }
2933
2934 virtual HRESULT RaiseStartButton()
2935 {
2936 m_StartButton.SendMessageW(BM_SETSTATE, FALSE, 0);
2937 return S_OK;
2938 }
2939
2940 void _Init()
2941 {
2942 m_Position = (DWORD) -1;
2943 }
2944
2945 DECLARE_NOT_AGGREGATABLE(CTrayWindow)
2946
2947 DECLARE_PROTECT_FINAL_CONSTRUCT()
2948 BEGIN_COM_MAP(CTrayWindow)
2949 /*COM_INTERFACE_ENTRY_IID(IID_ITrayWindow, ITrayWindow)*/
2950 COM_INTERFACE_ENTRY_IID(IID_IShellDesktopTray, IShellDesktopTray)
2951 END_COM_MAP()
2952 };
2953
2954 class CTrayWindowCtxMenu :
2955 public CComCoClass<CTrayWindowCtxMenu>,
2956 public CComObjectRootEx<CComMultiThreadModelNoCS>,
2957 public IContextMenu
2958 {
2959 HWND hWndOwner;
2960 CComPtr<CTrayWindow> TrayWnd;
2961 CComPtr<IContextMenu> pcm;
2962
2963 public:
2964 HRESULT Initialize(ITrayWindow * pTrayWnd, IN HWND hWndOwner)
2965 {
2966 this->TrayWnd = (CTrayWindow *) pTrayWnd;
2967 this->hWndOwner = hWndOwner;
2968 return S_OK;
2969 }
2970
2971 virtual HRESULT STDMETHODCALLTYPE
2972 QueryContextMenu(HMENU hPopup,
2973 UINT indexMenu,
2974 UINT idCmdFirst,
2975 UINT idCmdLast,
2976 UINT uFlags)
2977 {
2978 HMENU menubase = LoadPopupMenu(hExplorerInstance, MAKEINTRESOURCE(IDM_TRAYWND));
2979
2980 if (!menubase)
2981 return HRESULT_FROM_WIN32(GetLastError());
2982
2983 int count = ::GetMenuItemCount(menubase);
2984
2985 for (int i = 0; i < count; i++)
2986 {
2987 WCHAR label[128];
2988
2989 MENUITEMINFOW mii = { 0 };
2990 mii.cbSize = sizeof(mii);
2991 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_SUBMENU | MIIM_CHECKMARKS
2992 | MIIM_DATA | MIIM_STRING | MIIM_BITMAP | MIIM_FTYPE;
2993 mii.dwTypeData = label;
2994 mii.cch = _countof(label);
2995 ::GetMenuItemInfoW(menubase, i, TRUE, &mii);
2996
2997 TRACE("Adding item %d label %S type %d\n", mii.wID, mii.dwTypeData, mii.fType);
2998
2999 mii.fType |= MFT_RADIOCHECK;
3000
3001 ::InsertMenuItemW(hPopup, i + 1, TRUE, &mii);
3002 }
3003
3004 ::DestroyMenu(menubase);
3005
3006 if (SHRestricted(REST_CLASSICSHELL) != 0)
3007 {
3008 DeleteMenu(hPopup,
3009 ID_LOCKTASKBAR,
3010 MF_BYCOMMAND);
3011 }
3012
3013 CheckMenuItem(hPopup,
3014 ID_LOCKTASKBAR,
3015 MF_BYCOMMAND | (TrayWnd->Locked ? MF_CHECKED : MF_UNCHECKED));
3016
3017 if (TrayWnd->m_TrayBandSite != NULL)
3018 {
3019 if (FAILED(TrayWnd->m_TrayBandSite->AddContextMenus(
3020 hPopup,
3021 0,
3022 ID_SHELL_CMD_FIRST,
3023 ID_SHELL_CMD_LAST,
3024 CMF_NORMAL,
3025 &pcm)))
3026 {
3027 WARN("AddContextMenus failed.\n");
3028 pcm = NULL;
3029 }
3030 }
3031
3032 return S_OK;
3033 }
3034
3035 virtual HRESULT STDMETHODCALLTYPE
3036 InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
3037 {
3038 UINT uiCmdId = (UINT) lpici->lpVerb;
3039 if (uiCmdId != 0)
3040 {
3041 if (uiCmdId >= ID_SHELL_CMD_FIRST && uiCmdId <= ID_SHELL_CMD_LAST)
3042 {
3043 CMINVOKECOMMANDINFO cmici = { 0 };
3044
3045 if (pcm != NULL)
3046 {
3047 /* Setup and invoke the shell command */
3048 cmici.cbSize = sizeof(cmici);
3049 cmici.hwnd = hWndOwner;
3050 cmici.lpVerb = (LPCSTR) MAKEINTRESOURCE(uiCmdId - ID_SHELL_CMD_FIRST);
3051 cmici.nShow = SW_NORMAL;
3052
3053 pcm->InvokeCommand(&cmici);
3054 }
3055 }
3056 else
3057 {
3058 TrayWnd->ExecContextMenuCmd(uiCmdId);
3059 }
3060 }
3061
3062 return S_OK;
3063 }
3064
3065 virtual HRESULT STDMETHODCALLTYPE
3066 GetCommandString(UINT_PTR idCmd,
3067 UINT uType,
3068 UINT *pwReserved,
3069 LPSTR pszName,
3070 UINT cchMax)
3071 {
3072 return E_NOTIMPL;
3073 }
3074
3075 CTrayWindowCtxMenu()
3076 {
3077 }
3078
3079 virtual ~CTrayWindowCtxMenu()
3080 {
3081 }
3082
3083 BEGIN_COM_MAP(CTrayWindowCtxMenu)
3084 COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu)
3085 END_COM_MAP()
3086 };
3087
3088 HRESULT TrayWindowCtxMenuCreator(ITrayWindow * TrayWnd, IN HWND hWndOwner, IContextMenu ** ppCtxMenu)
3089 {
3090 CTrayWindowCtxMenu * mnu = new CComObject<CTrayWindowCtxMenu>();
3091 mnu->Initialize(TrayWnd, hWndOwner);
3092 *ppCtxMenu = mnu;
3093 return S_OK;
3094 }
3095
3096 HRESULT CreateTrayWindow(ITrayWindow ** ppTray)
3097 {
3098 CComPtr<CTrayWindow> Tray = new CComObject<CTrayWindow>();
3099 if (Tray == NULL)
3100 return E_OUTOFMEMORY;
3101
3102 Tray->_Init();
3103 Tray->Open();
3104
3105 *ppTray = (ITrayWindow *) Tray;
3106
3107 return S_OK;
3108 }
3109
3110 HRESULT
3111 Tray_OnStartMenuDismissed(ITrayWindow* Tray)
3112 {
3113 CTrayWindow * TrayWindow = static_cast<CTrayWindow *>(Tray);
3114 return TrayWindow->RaiseStartButton();
3115 }
3116
3117 VOID TrayProcessMessages(ITrayWindow *Tray)
3118 {
3119 CTrayWindow * TrayWindow = static_cast<CTrayWindow *>(Tray);
3120 TrayWindow->TrayProcessMessages();
3121 }
3122
3123 VOID TrayMessageLoop(ITrayWindow *Tray)
3124 {
3125 CTrayWindow * TrayWindow = static_cast<CTrayWindow *>(Tray);
3126 TrayWindow->TrayMessageLoop();
3127 }