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