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 24 /* 25 * TrayNotifyWnd 26 */ 27 28 static const WCHAR szTrayNotifyWndClass [] = TEXT("TrayNotifyWnd"); 29 30 #define TRAY_NOTIFY_WND_SPACING_X 1 31 #define TRAY_NOTIFY_WND_SPACING_Y 1 32 33 class CTrayNotifyWnd : 34 public CComObjectRootEx<CComMultiThreadModelNoCS>, 35 public CWindowImpl < CTrayNotifyWnd, CWindow, CControlWinTraits > 36 { 37 HWND hWndNotify; 38 39 CSysPagerWnd * m_pager; 40 CTrayClockWnd * m_clock; 41 42 CComPtr<ITrayWindow> TrayWindow; 43 44 HTHEME TrayTheme; 45 SIZE szTrayClockMin; 46 SIZE szTrayNotify; 47 MARGINS ContentMargin; 48 BOOL IsHorizontal; 49 50 public: 51 CTrayNotifyWnd() : 52 hWndNotify(NULL), 53 m_pager(NULL), 54 m_clock(NULL), 55 TrayTheme(NULL), 56 IsHorizontal(FALSE) 57 { 58 ZeroMemory(&szTrayClockMin, sizeof(szTrayClockMin)); 59 ZeroMemory(&szTrayNotify, sizeof(szTrayNotify)); 60 ZeroMemory(&ContentMargin, sizeof(ContentMargin)); 61 } 62 virtual ~CTrayNotifyWnd() { } 63 64 LRESULT OnThemeChanged() 65 { 66 if (TrayTheme) 67 CloseThemeData(TrayTheme); 68 69 if (IsThemeActive()) 70 TrayTheme = OpenThemeData(m_hWnd, L"TrayNotify"); 71 else 72 TrayTheme = NULL; 73 74 if (TrayTheme) 75 { 76 SetWindowExStyle(m_hWnd, WS_EX_STATICEDGE, 0); 77 78 GetThemeMargins(TrayTheme, 79 NULL, 80 TNP_BACKGROUND, 81 0, 82 TMT_CONTENTMARGINS, 83 NULL, 84 &ContentMargin); 85 } 86 else 87 { 88 SetWindowExStyle(m_hWnd, WS_EX_STATICEDGE, WS_EX_STATICEDGE); 89 90 ContentMargin.cxLeftWidth = 2; 91 ContentMargin.cxRightWidth = 2; 92 ContentMargin.cyTopHeight = 2; 93 ContentMargin.cyBottomHeight = 2; 94 } 95 96 return TRUE; 97 } 98 99 LRESULT OnThemeChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 100 { 101 return OnThemeChanged(); 102 } 103 104 LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 105 { 106 m_clock = new CTrayClockWnd(); 107 m_clock->_Init(m_hWnd, !g_TaskbarSettings.sr.HideClock); 108 109 m_pager = new CSysPagerWnd(); 110 m_pager->_Init(m_hWnd, !g_TaskbarSettings.sr.HideClock); 111 112 return TRUE; 113 } 114 115 BOOL GetMinimumSize(IN OUT PSIZE pSize) 116 { 117 SIZE szClock = { 0, 0 }; 118 SIZE szTray = { 0, 0 }; 119 120 if (!g_TaskbarSettings.sr.HideClock) 121 { 122 if (IsHorizontal) 123 { 124 szClock.cy = pSize->cy - 2 * TRAY_NOTIFY_WND_SPACING_Y; 125 if (szClock.cy <= 0) 126 goto NoClock; 127 } 128 else 129 { 130 szClock.cx = pSize->cx - 2 * TRAY_NOTIFY_WND_SPACING_X; 131 if (szClock.cx <= 0) 132 goto NoClock; 133 } 134 135 m_clock->SendMessage(TCWM_GETMINIMUMSIZE, (WPARAM) IsHorizontal, (LPARAM) &szClock); 136 137 szTrayClockMin = szClock; 138 } 139 else 140 NoClock: 141 szTrayClockMin = szClock; 142 143 if (IsHorizontal) 144 { 145 szTray.cy = pSize->cy - 2 * TRAY_NOTIFY_WND_SPACING_Y; 146 } 147 else 148 { 149 szTray.cx = pSize->cx - 2 * TRAY_NOTIFY_WND_SPACING_X; 150 } 151 152 m_pager->GetSize(IsHorizontal, &szTray); 153 154 szTrayNotify = szTray; 155 156 if (IsHorizontal) 157 { 158 pSize->cx = 2 * TRAY_NOTIFY_WND_SPACING_X; 159 160 if (!g_TaskbarSettings.sr.HideClock) 161 pSize->cx += TRAY_NOTIFY_WND_SPACING_X + szTrayClockMin.cx; 162 163 pSize->cx += szTray.cx; 164 } 165 else 166 { 167 pSize->cy = 2 * TRAY_NOTIFY_WND_SPACING_Y; 168 169 if (!g_TaskbarSettings.sr.HideClock) 170 pSize->cy += TRAY_NOTIFY_WND_SPACING_Y + szTrayClockMin.cy; 171 172 pSize->cy += szTray.cy; 173 } 174 175 pSize->cy += ContentMargin.cyTopHeight + ContentMargin.cyBottomHeight; 176 pSize->cx += ContentMargin.cxLeftWidth + ContentMargin.cxRightWidth; 177 178 return TRUE; 179 } 180 181 VOID Size(IN const SIZE *pszClient) 182 { 183 if (!g_TaskbarSettings.sr.HideClock) 184 { 185 POINT ptClock; 186 SIZE szClock; 187 188 if (IsHorizontal) 189 { 190 ptClock.x = pszClient->cx - szTrayClockMin.cx - ContentMargin.cxRightWidth; 191 ptClock.y = ContentMargin.cyTopHeight; 192 szClock.cx = szTrayClockMin.cx; 193 szClock.cy = pszClient->cy - ContentMargin.cyTopHeight - ContentMargin.cyBottomHeight; 194 } 195 else 196 { 197 ptClock.x = ContentMargin.cxLeftWidth; 198 ptClock.y = pszClient->cy - szTrayClockMin.cy; 199 szClock.cx = pszClient->cx - ContentMargin.cxLeftWidth - ContentMargin.cxRightWidth; 200 szClock.cy = szTrayClockMin.cy; 201 } 202 203 m_clock->SetWindowPos( 204 NULL, 205 ptClock.x, 206 ptClock.y, 207 szClock.cx, 208 szClock.cy, 209 SWP_NOZORDER); 210 211 POINT ptPager; 212 213 if (IsHorizontal) 214 { 215 ptPager.x = ContentMargin.cxLeftWidth; 216 ptPager.y = (pszClient->cy - szTrayNotify.cy)/2; 217 } 218 else 219 { 220 ptPager.x = (pszClient->cx - szTrayNotify.cx)/2; 221 ptPager.y = ContentMargin.cyTopHeight; 222 } 223 224 m_pager->SetWindowPos( 225 NULL, 226 ptPager.x, 227 ptPager.y, 228 szTrayNotify.cx, 229 szTrayNotify.cy, 230 SWP_NOZORDER); 231 } 232 } 233 234 LRESULT DrawBackground(HDC hdc) 235 { 236 HRESULT res; 237 RECT rect; 238 239 GetClientRect(&rect); 240 241 if (TrayTheme) 242 { 243 if (IsThemeBackgroundPartiallyTransparent(TrayTheme, TNP_BACKGROUND, 0)) 244 { 245 DrawThemeParentBackground(m_hWnd, hdc, &rect); 246 } 247 248 res = DrawThemeBackground(TrayTheme, hdc, TNP_BACKGROUND, 0, &rect, 0); 249 } 250 251 return res; 252 } 253 254 LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 255 { 256 HDC hdc = (HDC) wParam; 257 258 if (!TrayTheme) 259 { 260 bHandled = FALSE; 261 return 0; 262 } 263 264 return DrawBackground(hdc); 265 } 266 267 BOOL NotifyIconCmd(WPARAM wParam, LPARAM lParam) 268 { 269 if (m_pager) 270 { 271 return m_pager->NotifyIconCmd(wParam, lParam); 272 } 273 274 return TRUE; 275 } 276 277 BOOL GetClockRect(OUT PRECT rcClock) 278 { 279 if (!m_clock->IsWindowVisible()) 280 return FALSE; 281 282 return m_clock->GetWindowRect(rcClock); 283 } 284 285 LRESULT OnGetMinimumSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 286 { 287 BOOL Horizontal = (BOOL) wParam; 288 289 if (Horizontal != IsHorizontal) 290 { 291 IsHorizontal = Horizontal; 292 if (IsHorizontal) 293 SetWindowTheme(m_hWnd, L"TrayNotifyHoriz", NULL); 294 else 295 SetWindowTheme(m_hWnd, L"TrayNotifyVert", NULL); 296 } 297 298 return (LRESULT) GetMinimumSize((PSIZE) lParam); 299 } 300 301 LRESULT OnUpdateTime(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 302 { 303 if (m_clock != NULL) 304 { 305 /* Forward the message to the tray clock window procedure */ 306 return m_clock->OnUpdateTime(uMsg, wParam, lParam, bHandled); 307 } 308 return FALSE; 309 } 310 311 LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 312 { 313 SIZE szClient; 314 315 szClient.cx = LOWORD(lParam); 316 szClient.cy = HIWORD(lParam); 317 318 Size(&szClient); 319 320 return TRUE; 321 } 322 323 LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 324 { 325 return HTTRANSPARENT; 326 } 327 328 LRESULT OnShowClock(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 329 { 330 BOOL PrevHidden = g_TaskbarSettings.sr.HideClock; 331 g_TaskbarSettings.sr.HideClock = (wParam == 0); 332 333 if (m_clock != NULL && PrevHidden != g_TaskbarSettings.sr.HideClock) 334 { 335 m_clock->ShowWindow(g_TaskbarSettings.sr.HideClock ? SW_HIDE : SW_SHOW); 336 } 337 338 return (LRESULT) (!PrevHidden); 339 } 340 341 LRESULT OnTaskbarSettingsChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 342 { 343 TaskbarSettings* newSettings = (TaskbarSettings*)lParam; 344 if (newSettings->bShowSeconds != g_TaskbarSettings.bShowSeconds) 345 { 346 g_TaskbarSettings.bShowSeconds = newSettings->bShowSeconds; 347 /* TODO: Toggle showing seconds */ 348 } 349 350 if (newSettings->sr.HideClock != g_TaskbarSettings.sr.HideClock) 351 { 352 g_TaskbarSettings.sr.HideClock = newSettings->sr.HideClock; 353 /* TODO: Toggle hiding the clock */ 354 } 355 356 return 0; 357 } 358 359 LRESULT OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 360 { 361 const NMHDR *nmh = (const NMHDR *) lParam; 362 363 if (nmh->hwndFrom == m_clock->m_hWnd) 364 { 365 /* Pass down notifications */ 366 return m_clock->SendMessage(WM_NOTIFY, wParam, lParam); 367 } 368 369 return FALSE; 370 } 371 372 LRESULT OnSetFont(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 373 { 374 if (m_clock != NULL) 375 { 376 m_clock->SendMessageW(WM_SETFONT, wParam, lParam); 377 } 378 379 bHandled = FALSE; 380 return FALSE; 381 } 382 383 LRESULT OnCtxMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 384 { 385 bHandled = TRUE; 386 return 0; 387 } 388 389 LRESULT OnSettingChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 390 { 391 if (wParam == SPI_SETNONCLIENTMETRICS) 392 { 393 m_pager->ResizeImagelist(); 394 } 395 return 0; 396 } 397 398 DECLARE_WND_CLASS_EX(szTrayNotifyWndClass, CS_DBLCLKS, COLOR_3DFACE) 399 400 BEGIN_MSG_MAP(CTrayNotifyWnd) 401 MESSAGE_HANDLER(WM_CREATE, OnCreate) 402 MESSAGE_HANDLER(WM_THEMECHANGED, OnThemeChanged) 403 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground) 404 MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChanged) 405 MESSAGE_HANDLER(WM_SIZE, OnSize) 406 MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTest) 407 MESSAGE_HANDLER(WM_NOTIFY, OnNotify) 408 MESSAGE_HANDLER(WM_SETFONT, OnSetFont) 409 MESSAGE_HANDLER(WM_CONTEXTMENU, OnCtxMenu) // FIXME: This handler is not necessary in Windows 410 MESSAGE_HANDLER(TNWM_GETMINIMUMSIZE, OnGetMinimumSize) 411 MESSAGE_HANDLER(TNWM_UPDATETIME, OnUpdateTime) 412 MESSAGE_HANDLER(TNWM_SHOWCLOCK, OnShowClock) 413 MESSAGE_HANDLER(TWM_SETTINGSCHANGED, OnTaskbarSettingsChanged) 414 END_MSG_MAP() 415 416 HWND _Init(IN OUT ITrayWindow *TrayWindow) 417 { 418 HWND hWndTrayWindow; 419 420 hWndTrayWindow = TrayWindow->GetHWND(); 421 if (hWndTrayWindow == NULL) 422 return NULL; 423 424 this->TrayWindow = TrayWindow; 425 this->hWndNotify = hWndTrayWindow; 426 427 DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; 428 return Create(hWndTrayWindow, 0, NULL, dwStyle, WS_EX_STATICEDGE); 429 } 430 }; 431 432 HWND CreateTrayNotifyWnd(IN OUT ITrayWindow *Tray, CTrayNotifyWnd** ppinstance) 433 { 434 CTrayNotifyWnd * pTrayNotify = new CTrayNotifyWnd(); 435 // TODO: Destroy after the window is destroyed 436 *ppinstance = pTrayNotify; 437 438 return pTrayNotify->_Init(Tray); 439 } 440 441 BOOL 442 TrayNotify_NotifyIconCmd(CTrayNotifyWnd* pTrayNotify, WPARAM wParam, LPARAM lParam) 443 { 444 return pTrayNotify->NotifyIconCmd(wParam, lParam); 445 } 446 447 BOOL 448 TrayNotify_GetClockRect(CTrayNotifyWnd* pTrayNotify, OUT PRECT rcClock) 449 { 450 return pTrayNotify->GetClockRect(rcClock); 451 } 452