1 /* 2 * ReactOS Explorer 3 * 4 * Copyright 2006 - 2007 Thomas Weidenmueller <w3seek@reactos.org> 5 * Copyright 2018 Ged Murphy <gedmurphy@reactos.org> 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 */ 21 22 #include "precomp.h" 23 #include <commoncontrols.h> 24 25 static const WCHAR szTrayNotifyWndClass[] = L"TrayNotifyWnd"; 26 27 #define TRAY_NOTIFY_WND_SPACING_X 1 28 #define TRAY_NOTIFY_WND_SPACING_Y 1 29 #define CLOCK_TEXT_HACK 4 30 31 /* 32 * TrayNotifyWnd 33 */ 34 35 class CTrayNotifyWnd : 36 public CComCoClass<CTrayNotifyWnd>, 37 public CComObjectRootEx<CComMultiThreadModelNoCS>, 38 public CWindowImpl < CTrayNotifyWnd, CWindow, CControlWinTraits >, 39 public IOleWindow 40 { 41 CComPtr<IUnknown> m_clock; 42 CTrayShowDesktopButton m_ShowDesktopButton; 43 CComPtr<IUnknown> m_pager; 44 45 HWND m_hwndClock; 46 HWND m_hwndShowDesktop; 47 HWND m_hwndPager; 48 49 HTHEME TrayTheme; 50 SIZE trayClockMinSize; 51 SIZE trayShowDesktopSize; 52 SIZE trayNotifySize; 53 MARGINS ContentMargin; 54 BOOL IsHorizontal; 55 56 public: 57 CTrayNotifyWnd() : 58 m_hwndClock(NULL), 59 m_hwndPager(NULL), 60 TrayTheme(NULL), 61 IsHorizontal(FALSE) 62 { 63 ZeroMemory(&trayClockMinSize, sizeof(trayClockMinSize)); 64 ZeroMemory(&trayShowDesktopSize, sizeof(trayShowDesktopSize)); 65 ZeroMemory(&trayNotifySize, sizeof(trayNotifySize)); 66 ZeroMemory(&ContentMargin, sizeof(ContentMargin)); 67 } 68 ~CTrayNotifyWnd() { } 69 70 LRESULT OnThemeChanged() 71 { 72 if (TrayTheme) 73 CloseThemeData(TrayTheme); 74 75 if (IsThemeActive()) 76 TrayTheme = OpenThemeData(m_hWnd, L"TrayNotify"); 77 else 78 TrayTheme = NULL; 79 80 if (TrayTheme) 81 { 82 SetWindowExStyle(m_hWnd, WS_EX_STATICEDGE, 0); 83 84 GetThemeMargins(TrayTheme, 85 NULL, 86 TNP_BACKGROUND, 87 0, 88 TMT_CONTENTMARGINS, 89 NULL, 90 &ContentMargin); 91 } 92 else 93 { 94 SetWindowExStyle(m_hWnd, WS_EX_STATICEDGE, WS_EX_STATICEDGE); 95 96 ContentMargin.cxLeftWidth = 2; 97 ContentMargin.cxRightWidth = 2; 98 ContentMargin.cyTopHeight = 2; 99 ContentMargin.cyBottomHeight = 2; 100 } 101 102 return TRUE; 103 } 104 105 LRESULT OnThemeChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 106 { 107 return OnThemeChanged(); 108 } 109 110 LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 111 { 112 HRESULT hr; 113 114 hr = CTrayClockWnd_CreateInstance(m_hWnd, IID_PPV_ARG(IUnknown, &m_clock)); 115 if (FAILED_UNEXPECTEDLY(hr)) 116 return FALSE; 117 118 hr = IUnknown_GetWindow(m_clock, &m_hwndClock); 119 if (FAILED_UNEXPECTEDLY(hr)) 120 return FALSE; 121 122 hr = CSysPagerWnd_CreateInstance(m_hWnd, IID_PPV_ARG(IUnknown, &m_pager)); 123 if (FAILED_UNEXPECTEDLY(hr)) 124 return FALSE; 125 126 hr = IUnknown_GetWindow(m_pager, &m_hwndPager); 127 if (FAILED_UNEXPECTEDLY(hr)) 128 return FALSE; 129 130 /* Create the 'Show Desktop' button */ 131 m_ShowDesktopButton.DoCreate(m_hWnd); 132 m_hwndShowDesktop = m_ShowDesktopButton.m_hWnd; 133 134 return TRUE; 135 } 136 137 BOOL GetMinimumSize(IN OUT PSIZE pSize) 138 { 139 SIZE clockSize = { 0, 0 }; 140 SIZE traySize = { 0, 0 }; 141 SIZE showDesktopSize = { 0, 0 }; 142 143 if (!g_TaskbarSettings.sr.HideClock) 144 { 145 if (IsHorizontal) 146 { 147 clockSize.cy = pSize->cy; 148 if (clockSize.cy <= 0) 149 goto NoClock; 150 } 151 else 152 { 153 clockSize.cx = pSize->cx; 154 if (clockSize.cx <= 0) 155 goto NoClock; 156 } 157 158 ::SendMessage(m_hwndClock, TNWM_GETMINIMUMSIZE, (WPARAM) IsHorizontal, (LPARAM) &clockSize); 159 160 trayClockMinSize = clockSize; 161 } 162 else 163 NoClock: 164 trayClockMinSize = clockSize; 165 166 if (IsHorizontal) 167 { 168 traySize.cy = pSize->cy - 2 * TRAY_NOTIFY_WND_SPACING_Y; 169 } 170 else 171 { 172 traySize.cx = pSize->cx - 2 * TRAY_NOTIFY_WND_SPACING_X; 173 } 174 175 ::SendMessage(m_hwndPager, TNWM_GETMINIMUMSIZE, (WPARAM) IsHorizontal, (LPARAM) &traySize); 176 177 trayNotifySize = traySize; 178 179 INT showDesktopButtonExtent = 0; 180 if (g_TaskbarSettings.bShowDesktopButton) 181 { 182 showDesktopButtonExtent = m_ShowDesktopButton.WidthOrHeight(); 183 if (IsHorizontal) 184 { 185 showDesktopSize.cx = showDesktopButtonExtent; 186 showDesktopSize.cy = pSize->cy; 187 } 188 else 189 { 190 showDesktopSize.cx = pSize->cx; 191 showDesktopSize.cy = showDesktopButtonExtent; 192 } 193 } 194 trayShowDesktopSize = showDesktopSize; 195 196 if (IsHorizontal) 197 { 198 pSize->cx = 2 * TRAY_NOTIFY_WND_SPACING_X; 199 200 if (!g_TaskbarSettings.sr.HideClock) 201 pSize->cx += TRAY_NOTIFY_WND_SPACING_X + trayClockMinSize.cx; 202 203 if (g_TaskbarSettings.bShowDesktopButton) 204 pSize->cx += showDesktopButtonExtent; 205 206 pSize->cx += traySize.cx; 207 pSize->cx += ContentMargin.cxLeftWidth + ContentMargin.cxRightWidth; 208 } 209 else 210 { 211 pSize->cy = 2 * TRAY_NOTIFY_WND_SPACING_Y; 212 213 if (!g_TaskbarSettings.sr.HideClock) 214 pSize->cy += TRAY_NOTIFY_WND_SPACING_Y + trayClockMinSize.cy; 215 216 if (g_TaskbarSettings.bShowDesktopButton) 217 pSize->cy += showDesktopButtonExtent; 218 219 pSize->cy += traySize.cy; 220 pSize->cy += ContentMargin.cyTopHeight + ContentMargin.cyBottomHeight; 221 } 222 223 return TRUE; 224 } 225 226 VOID Size(IN OUT SIZE *pszClient) 227 { 228 RECT rcClient = {0, 0, pszClient->cx, pszClient->cy}; 229 AlignControls(&rcClient); 230 pszClient->cx = rcClient.right - rcClient.left; 231 pszClient->cy = rcClient.bottom - rcClient.top; 232 } 233 234 VOID AlignControls(IN CONST PRECT prcClient OPTIONAL) 235 { 236 RECT rcClient; 237 if (prcClient != NULL) 238 rcClient = *prcClient; 239 else 240 GetClientRect(&rcClient); 241 242 rcClient.left += ContentMargin.cxLeftWidth; 243 rcClient.top += ContentMargin.cyTopHeight; 244 rcClient.right -= ContentMargin.cxRightWidth; 245 rcClient.bottom -= ContentMargin.cyBottomHeight; 246 247 CONST UINT swpFlags = SWP_DRAWFRAME | SWP_NOCOPYBITS | SWP_NOZORDER; 248 249 if (g_TaskbarSettings.bShowDesktopButton) 250 { 251 POINT ptShowDesktop = 252 { 253 rcClient.left, 254 rcClient.top 255 }; 256 SIZE showDesktopSize = 257 { 258 rcClient.right - rcClient.left, 259 rcClient.bottom - rcClient.top 260 }; 261 262 INT cxyShowDesktop = m_ShowDesktopButton.WidthOrHeight(); 263 if (IsHorizontal) 264 { 265 if (!TrayTheme) 266 { 267 ptShowDesktop.y -= ContentMargin.cyTopHeight; 268 showDesktopSize.cy += ContentMargin.cyTopHeight + ContentMargin.cyBottomHeight; 269 } 270 271 rcClient.right -= (cxyShowDesktop - ContentMargin.cxRightWidth); 272 273 ptShowDesktop.x = rcClient.right; 274 showDesktopSize.cx = cxyShowDesktop; 275 276 // HACK: Clock has layout problems - remove this once addressed. 277 rcClient.right -= CLOCK_TEXT_HACK; 278 } 279 else 280 { 281 if (!TrayTheme) 282 { 283 ptShowDesktop.x -= ContentMargin.cxLeftWidth; 284 showDesktopSize.cx += ContentMargin.cxLeftWidth + ContentMargin.cxRightWidth; 285 } 286 287 rcClient.bottom -= (cxyShowDesktop - ContentMargin.cyBottomHeight); 288 289 ptShowDesktop.y = rcClient.bottom; 290 showDesktopSize.cy = cxyShowDesktop; 291 292 // HACK: Clock has layout problems - remove this once addressed. 293 rcClient.bottom -= CLOCK_TEXT_HACK; 294 } 295 296 /* Resize and reposition the button */ 297 ::SetWindowPos(m_hwndShowDesktop, 298 NULL, 299 ptShowDesktop.x, 300 ptShowDesktop.y, 301 showDesktopSize.cx, 302 showDesktopSize.cy, 303 swpFlags); 304 } 305 306 if (!g_TaskbarSettings.sr.HideClock) 307 { 308 POINT ptClock = { rcClient.left, rcClient.top }; 309 SIZE clockSize = { rcClient.right - rcClient.left, rcClient.bottom - rcClient.top }; 310 311 if (IsHorizontal) 312 { 313 rcClient.right -= trayClockMinSize.cx; 314 315 ptClock.x = rcClient.right; 316 clockSize.cx = trayClockMinSize.cx; 317 } 318 else 319 { 320 rcClient.bottom -= trayClockMinSize.cy; 321 322 ptClock.y = rcClient.bottom; 323 clockSize.cy = trayClockMinSize.cy; 324 } 325 326 ::SetWindowPos(m_hwndClock, 327 NULL, 328 ptClock.x, 329 ptClock.y, 330 clockSize.cx, 331 clockSize.cy, 332 swpFlags); 333 } 334 335 POINT ptPager; 336 if (IsHorizontal) 337 { 338 ptPager.x = ContentMargin.cxLeftWidth; 339 ptPager.y = ((rcClient.bottom - rcClient.top) - trayNotifySize.cy) / 2; 340 if (g_TaskbarSettings.UseCompactTrayIcons()) 341 ptPager.y += ContentMargin.cyTopHeight; 342 } 343 else 344 { 345 ptPager.x = ((rcClient.right - rcClient.left) - trayNotifySize.cx) / 2; 346 if (g_TaskbarSettings.UseCompactTrayIcons()) 347 ptPager.x += ContentMargin.cxLeftWidth; 348 ptPager.y = ContentMargin.cyTopHeight; 349 } 350 351 ::SetWindowPos(m_hwndPager, 352 NULL, 353 ptPager.x, 354 ptPager.y, 355 trayNotifySize.cx, 356 trayNotifySize.cy, 357 swpFlags); 358 359 if (prcClient != NULL) 360 { 361 prcClient->left = rcClient.left - ContentMargin.cxLeftWidth; 362 prcClient->top = rcClient.top - ContentMargin.cyTopHeight; 363 prcClient->right = rcClient.right + ContentMargin.cxRightWidth; 364 prcClient->bottom = rcClient.bottom + ContentMargin.cyBottomHeight; 365 } 366 } 367 368 LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 369 { 370 HDC hdc = (HDC) wParam; 371 372 if (!TrayTheme) 373 { 374 bHandled = FALSE; 375 return 0; 376 } 377 378 RECT rect; 379 GetClientRect(&rect); 380 if (IsThemeBackgroundPartiallyTransparent(TrayTheme, TNP_BACKGROUND, 0)) 381 DrawThemeParentBackground(m_hWnd, hdc, &rect); 382 383 DrawThemeBackground(TrayTheme, hdc, TNP_BACKGROUND, 0, &rect, 0); 384 385 return TRUE; 386 } 387 388 LRESULT OnGetMinimumSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 389 { 390 BOOL Horizontal = (BOOL) wParam; 391 392 if (Horizontal != IsHorizontal) 393 IsHorizontal = Horizontal; 394 395 SetWindowTheme(m_hWnd, 396 IsHorizontal ? L"TrayNotifyHoriz" : L"TrayNotifyVert", 397 NULL); 398 m_ShowDesktopButton.m_bHorizontal = Horizontal; 399 400 return (LRESULT)GetMinimumSize((PSIZE)lParam); 401 } 402 403 LRESULT OnGetShowDesktopButton(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 404 { 405 if (wParam == NULL) 406 return 0; 407 408 CTrayShowDesktopButton** ptr = (CTrayShowDesktopButton**)wParam; 409 if (!m_ShowDesktopButton) 410 { 411 *ptr = NULL; 412 return 0; 413 } 414 415 *ptr = &m_ShowDesktopButton; 416 bHandled = TRUE; 417 return 0; 418 } 419 420 LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 421 { 422 SIZE clientSize; 423 424 clientSize.cx = LOWORD(lParam); 425 clientSize.cy = HIWORD(lParam); 426 427 Size(&clientSize); 428 429 return TRUE; 430 } 431 432 LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 433 { 434 POINT pt; 435 pt.x = GET_X_LPARAM(lParam); 436 pt.y = GET_Y_LPARAM(lParam); 437 438 if (m_ShowDesktopButton && m_ShowDesktopButton.PtInButton(&pt)) 439 return HTCLIENT; 440 441 return HTTRANSPARENT; 442 } 443 444 LRESULT OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 445 { 446 POINT pt; 447 ::GetCursorPos(&pt); 448 449 if (m_ShowDesktopButton && m_ShowDesktopButton.PtInButton(&pt)) 450 m_ShowDesktopButton.StartHovering(); 451 452 return TRUE; 453 } 454 455 LRESULT OnCtxMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 456 { 457 bHandled = TRUE; 458 459 if (reinterpret_cast<HWND>(wParam) == m_hwndClock) 460 return GetParent().SendMessage(uMsg, wParam, lParam); 461 else 462 return 0; 463 } 464 465 LRESULT OnClockMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 466 { 467 return SendMessageW(m_hwndClock, uMsg, wParam, lParam); 468 } 469 470 LRESULT OnTaskbarSettingsChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 471 { 472 TaskbarSettings* newSettings = (TaskbarSettings*)lParam; 473 474 /* Toggle show desktop button */ 475 if (newSettings->bShowDesktopButton != g_TaskbarSettings.bShowDesktopButton) 476 { 477 g_TaskbarSettings.bShowDesktopButton = newSettings->bShowDesktopButton; 478 ::ShowWindow(m_hwndShowDesktop, g_TaskbarSettings.bShowDesktopButton ? SW_SHOW : SW_HIDE); 479 480 /* Ask the parent to resize */ 481 NMHDR nmh = {m_hWnd, 0, NTNWM_REALIGN}; 482 SendMessage(WM_NOTIFY, 0, (LPARAM) &nmh); 483 } 484 485 return OnClockMessage(uMsg, wParam, lParam, bHandled); 486 } 487 488 LRESULT OnPagerMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 489 { 490 return SendMessageW(m_hwndPager, uMsg, wParam, lParam); 491 } 492 493 LRESULT OnRealign(INT uCode, LPNMHDR hdr, BOOL& bHandled) 494 { 495 hdr->hwndFrom = m_hWnd; 496 return GetParent().SendMessage(WM_NOTIFY, 0, (LPARAM)hdr); 497 } 498 499 HRESULT WINAPI GetWindow(HWND* phwnd) 500 { 501 if (!phwnd) 502 return E_INVALIDARG; 503 *phwnd = m_hWnd; 504 return S_OK; 505 } 506 507 HRESULT WINAPI ContextSensitiveHelp(BOOL fEnterMode) 508 { 509 return E_NOTIMPL; 510 } 511 512 HRESULT Initialize(IN HWND hwndParent) 513 { 514 const DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; 515 Create(hwndParent, 0, NULL, dwStyle, WS_EX_STATICEDGE); 516 return m_hWnd ? S_OK : E_FAIL; 517 } 518 519 DECLARE_NOT_AGGREGATABLE(CTrayNotifyWnd) 520 521 DECLARE_PROTECT_FINAL_CONSTRUCT() 522 BEGIN_COM_MAP(CTrayNotifyWnd) 523 COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow) 524 END_COM_MAP() 525 526 DECLARE_WND_CLASS_EX(szTrayNotifyWndClass, CS_DBLCLKS, COLOR_3DFACE) 527 528 BEGIN_MSG_MAP(CTrayNotifyWnd) 529 MESSAGE_HANDLER(WM_CREATE, OnCreate) 530 MESSAGE_HANDLER(WM_THEMECHANGED, OnThemeChanged) 531 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground) 532 MESSAGE_HANDLER(WM_SIZE, OnSize) 533 MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTest) 534 MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove) 535 MESSAGE_HANDLER(WM_NCMOUSEMOVE, OnMouseMove) 536 MESSAGE_HANDLER(WM_CONTEXTMENU, OnCtxMenu) 537 MESSAGE_HANDLER(WM_NCLBUTTONDBLCLK, OnClockMessage) 538 MESSAGE_HANDLER(WM_SETFONT, OnClockMessage) 539 MESSAGE_HANDLER(WM_SETTINGCHANGE, OnPagerMessage) 540 MESSAGE_HANDLER(WM_COPYDATA, OnPagerMessage) 541 MESSAGE_HANDLER(TWM_SETTINGSCHANGED, OnTaskbarSettingsChanged) 542 NOTIFY_CODE_HANDLER(NTNWM_REALIGN, OnRealign) 543 MESSAGE_HANDLER(TNWM_GETMINIMUMSIZE, OnGetMinimumSize) 544 MESSAGE_HANDLER(TNWM_GETSHOWDESKTOPBUTTON, OnGetShowDesktopButton) 545 END_MSG_MAP() 546 }; 547 548 HRESULT CTrayNotifyWnd_CreateInstance(HWND hwndParent, REFIID riid, void **ppv) 549 { 550 return ShellObjectCreatorInit<CTrayNotifyWnd>(hwndParent, riid, ppv); 551 } 552