1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/widget/windows/TaskbarTabPreview.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,357 @@ 1.4 +/* vim: se cin sw=2 ts=2 et : */ 1.5 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.6 + * 1.7 + * This Source Code Form is subject to the terms of the Mozilla Public 1.8 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.9 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.10 + 1.11 +#include "TaskbarTabPreview.h" 1.12 +#include "nsWindowGfx.h" 1.13 +#include "nsUXThemeData.h" 1.14 +#include "WinUtils.h" 1.15 +#include <nsITaskbarPreviewController.h> 1.16 + 1.17 +#define TASKBARPREVIEW_HWNDID L"TaskbarTabPreviewHwnd" 1.18 + 1.19 +namespace mozilla { 1.20 +namespace widget { 1.21 + 1.22 +NS_IMPL_ISUPPORTS(TaskbarTabPreview, nsITaskbarTabPreview) 1.23 + 1.24 +const wchar_t *const kWindowClass = L"MozillaTaskbarPreviewClass"; 1.25 + 1.26 +TaskbarTabPreview::TaskbarTabPreview(ITaskbarList4 *aTaskbar, nsITaskbarPreviewController *aController, HWND aHWND, nsIDocShell *aShell) 1.27 + : TaskbarPreview(aTaskbar, aController, aHWND, aShell), 1.28 + mProxyWindow(nullptr), 1.29 + mIcon(nullptr), 1.30 + mRegistered(false) 1.31 +{ 1.32 + WindowHook &hook = GetWindowHook(); 1.33 + hook.AddMonitor(WM_WINDOWPOSCHANGED, MainWindowHook, this); 1.34 +} 1.35 + 1.36 +TaskbarTabPreview::~TaskbarTabPreview() { 1.37 + if (mIcon) { 1.38 + ::DestroyIcon(mIcon); 1.39 + mIcon = nullptr; 1.40 + } 1.41 + 1.42 + // We need to ensure that proxy window disappears or else Bad Things happen. 1.43 + if (mProxyWindow) 1.44 + Disable(); 1.45 + 1.46 + NS_ASSERTION(!mProxyWindow, "Taskbar proxy window was not destroyed!"); 1.47 + 1.48 + if (IsWindowAvailable()) { 1.49 + DetachFromNSWindow(); 1.50 + } else { 1.51 + mWnd = nullptr; 1.52 + } 1.53 +} 1.54 + 1.55 +nsresult 1.56 +TaskbarTabPreview::ShowActive(bool active) { 1.57 + NS_ASSERTION(mVisible && CanMakeTaskbarCalls(), "ShowActive called on invisible window or before taskbar calls can be made for this window"); 1.58 + return FAILED(mTaskbar->SetTabActive(active ? mProxyWindow : nullptr, 1.59 + mWnd, 0)) ? NS_ERROR_FAILURE : NS_OK; 1.60 +} 1.61 + 1.62 +HWND & 1.63 +TaskbarTabPreview::PreviewWindow() { 1.64 + return mProxyWindow; 1.65 +} 1.66 + 1.67 +nativeWindow 1.68 +TaskbarTabPreview::GetHWND() { 1.69 + return mProxyWindow; 1.70 +} 1.71 + 1.72 +void 1.73 +TaskbarTabPreview::EnsureRegistration() { 1.74 + NS_ASSERTION(mVisible && CanMakeTaskbarCalls(), "EnsureRegistration called when it is not safe to do so"); 1.75 + 1.76 + (void) UpdateTaskbarProperties(); 1.77 +} 1.78 + 1.79 +NS_IMETHODIMP 1.80 +TaskbarTabPreview::GetTitle(nsAString &aTitle) { 1.81 + aTitle = mTitle; 1.82 + return NS_OK; 1.83 +} 1.84 + 1.85 +NS_IMETHODIMP 1.86 +TaskbarTabPreview::SetTitle(const nsAString &aTitle) { 1.87 + mTitle = aTitle; 1.88 + return mVisible ? UpdateTitle() : NS_OK; 1.89 +} 1.90 + 1.91 +NS_IMETHODIMP 1.92 +TaskbarTabPreview::SetIcon(imgIContainer *icon) { 1.93 + HICON hIcon = nullptr; 1.94 + if (icon) { 1.95 + nsresult rv; 1.96 + rv = nsWindowGfx::CreateIcon(icon, false, 0, 0, 1.97 + nsWindowGfx::GetIconMetrics(nsWindowGfx::kSmallIcon), 1.98 + &hIcon); 1.99 + NS_ENSURE_SUCCESS(rv, rv); 1.100 + } 1.101 + 1.102 + if (mIcon) 1.103 + ::DestroyIcon(mIcon); 1.104 + mIcon = hIcon; 1.105 + mIconImage = icon; 1.106 + return mVisible ? UpdateIcon() : NS_OK; 1.107 +} 1.108 + 1.109 +NS_IMETHODIMP 1.110 +TaskbarTabPreview::GetIcon(imgIContainer **icon) { 1.111 + NS_IF_ADDREF(*icon = mIconImage); 1.112 + return NS_OK; 1.113 +} 1.114 + 1.115 +NS_IMETHODIMP 1.116 +TaskbarTabPreview::Move(nsITaskbarTabPreview *aNext) { 1.117 + if (aNext == this) 1.118 + return NS_ERROR_INVALID_ARG; 1.119 + mNext = aNext; 1.120 + return CanMakeTaskbarCalls() ? UpdateNext() : NS_OK; 1.121 +} 1.122 + 1.123 +nsresult 1.124 +TaskbarTabPreview::UpdateTaskbarProperties() { 1.125 + if (mRegistered) 1.126 + return NS_OK; 1.127 + 1.128 + if (FAILED(mTaskbar->RegisterTab(mProxyWindow, mWnd))) 1.129 + return NS_ERROR_FAILURE; 1.130 + 1.131 + nsresult rv = UpdateNext(); 1.132 + NS_ENSURE_SUCCESS(rv, rv); 1.133 + rv = TaskbarPreview::UpdateTaskbarProperties(); 1.134 + mRegistered = true; 1.135 + return rv; 1.136 +} 1.137 + 1.138 +LRESULT 1.139 +TaskbarTabPreview::WndProc(UINT nMsg, WPARAM wParam, LPARAM lParam) { 1.140 + nsRefPtr<TaskbarTabPreview> kungFuDeathGrip(this); 1.141 + switch (nMsg) { 1.142 + case WM_CREATE: 1.143 + TaskbarPreview::EnableCustomDrawing(mProxyWindow, true); 1.144 + return 0; 1.145 + case WM_CLOSE: 1.146 + mController->OnClose(); 1.147 + return 0; 1.148 + case WM_ACTIVATE: 1.149 + if (LOWORD(wParam) == WA_ACTIVE) { 1.150 + // Activate the tab the user selected then restore the main window, 1.151 + // keeping normal/max window state intact. 1.152 + bool activateWindow; 1.153 + nsresult rv = mController->OnActivate(&activateWindow); 1.154 + if (NS_SUCCEEDED(rv) && activateWindow) { 1.155 + nsWindow* win = WinUtils::GetNSWindowPtr(mWnd); 1.156 + if (win) { 1.157 + nsWindow * parent = win->GetTopLevelWindow(true); 1.158 + if (parent) { 1.159 + parent->Show(true); 1.160 + } 1.161 + } 1.162 + } 1.163 + } 1.164 + return 0; 1.165 + case WM_GETICON: 1.166 + return (LRESULT)mIcon; 1.167 + case WM_SYSCOMMAND: 1.168 + // Send activation events to the top level window and select the proper 1.169 + // tab through the controller. 1.170 + if (wParam == SC_RESTORE || wParam == SC_MAXIMIZE) { 1.171 + bool activateWindow; 1.172 + nsresult rv = mController->OnActivate(&activateWindow); 1.173 + if (NS_SUCCEEDED(rv) && activateWindow) { 1.174 + // Note, restoring an iconic, maximized window here will only 1.175 + // activate the maximized window. This is not a bug, it's default 1.176 + // windows behavior. 1.177 + ::SendMessageW(mWnd, WM_SYSCOMMAND, wParam, lParam); 1.178 + } 1.179 + return 0; 1.180 + } 1.181 + // Forward everything else to the top level window. Do not forward 1.182 + // close since that's intended for the tab. When the preview proxy 1.183 + // closes, we'll close the tab above. 1.184 + return wParam == SC_CLOSE 1.185 + ? ::DefWindowProcW(mProxyWindow, WM_SYSCOMMAND, wParam, lParam) 1.186 + : ::SendMessageW(mWnd, WM_SYSCOMMAND, wParam, lParam); 1.187 + return 0; 1.188 + } 1.189 + return TaskbarPreview::WndProc(nMsg, wParam, lParam); 1.190 +} 1.191 + 1.192 +/* static */ 1.193 +LRESULT CALLBACK 1.194 +TaskbarTabPreview::GlobalWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam) { 1.195 + TaskbarTabPreview *preview(nullptr); 1.196 + if (nMsg == WM_CREATE) { 1.197 + CREATESTRUCT *cs = reinterpret_cast<CREATESTRUCT*>(lParam); 1.198 + preview = reinterpret_cast<TaskbarTabPreview*>(cs->lpCreateParams); 1.199 + if (!::SetPropW(hWnd, TASKBARPREVIEW_HWNDID, preview)) 1.200 + NS_ERROR("Could not associate native window with tab preview"); 1.201 + preview->mProxyWindow = hWnd; 1.202 + } else { 1.203 + preview = reinterpret_cast<TaskbarTabPreview*>(::GetPropW(hWnd, TASKBARPREVIEW_HWNDID)); 1.204 + if (nMsg == WM_DESTROY) 1.205 + ::RemovePropW(hWnd, TASKBARPREVIEW_HWNDID); 1.206 + } 1.207 + 1.208 + if (preview) 1.209 + return preview->WndProc(nMsg, wParam, lParam); 1.210 + return ::DefWindowProcW(hWnd, nMsg, wParam, lParam); 1.211 +} 1.212 + 1.213 +nsresult 1.214 +TaskbarTabPreview::Enable() { 1.215 + WNDCLASSW wc; 1.216 + HINSTANCE module = GetModuleHandle(nullptr); 1.217 + 1.218 + if (!GetClassInfoW(module, kWindowClass, &wc)) { 1.219 + wc.style = 0; 1.220 + wc.lpfnWndProc = GlobalWndProc; 1.221 + wc.cbClsExtra = 0; 1.222 + wc.cbWndExtra = 0; 1.223 + wc.hInstance = module; 1.224 + wc.hIcon = nullptr; 1.225 + wc.hCursor = nullptr; 1.226 + wc.hbrBackground = (HBRUSH) nullptr; 1.227 + wc.lpszMenuName = (LPCWSTR) nullptr; 1.228 + wc.lpszClassName = kWindowClass; 1.229 + RegisterClassW(&wc); 1.230 + } 1.231 + ::CreateWindowW(kWindowClass, L"TaskbarPreviewWindow", 1.232 + WS_CAPTION | WS_SYSMENU, 0, 0, 200, 60, 1.233 + nullptr, nullptr, module, this); 1.234 + // GlobalWndProc will set mProxyWindow so that WM_CREATE can have a valid HWND 1.235 + if (!mProxyWindow) 1.236 + return NS_ERROR_INVALID_ARG; 1.237 + 1.238 + UpdateProxyWindowStyle(); 1.239 + 1.240 + nsresult rv = TaskbarPreview::Enable(); 1.241 + nsresult rvUpdate; 1.242 + rvUpdate = UpdateTitle(); 1.243 + if (NS_FAILED(rvUpdate)) 1.244 + rv = rvUpdate; 1.245 + 1.246 + rvUpdate = UpdateIcon(); 1.247 + if (NS_FAILED(rvUpdate)) 1.248 + rv = rvUpdate; 1.249 + 1.250 + return rv; 1.251 +} 1.252 + 1.253 +nsresult 1.254 +TaskbarTabPreview::Disable() { 1.255 + // TaskbarPreview::Disable assumes that mWnd is valid but this method can be 1.256 + // called when it is null iff the nsWindow has already been destroyed and we 1.257 + // are still visible for some reason during object destruction. 1.258 + if (mWnd) 1.259 + TaskbarPreview::Disable(); 1.260 + 1.261 + if (FAILED(mTaskbar->UnregisterTab(mProxyWindow))) 1.262 + return NS_ERROR_FAILURE; 1.263 + mRegistered = false; 1.264 + 1.265 + // TaskbarPreview::WndProc will set mProxyWindow to null 1.266 + if (!DestroyWindow(mProxyWindow)) 1.267 + return NS_ERROR_FAILURE; 1.268 + mProxyWindow = nullptr; 1.269 + return NS_OK; 1.270 +} 1.271 + 1.272 +void 1.273 +TaskbarTabPreview::DetachFromNSWindow() { 1.274 + (void) SetVisible(false); 1.275 + WindowHook &hook = GetWindowHook(); 1.276 + hook.RemoveMonitor(WM_WINDOWPOSCHANGED, MainWindowHook, this); 1.277 + 1.278 + TaskbarPreview::DetachFromNSWindow(); 1.279 +} 1.280 + 1.281 +/* static */ 1.282 +bool 1.283 +TaskbarTabPreview::MainWindowHook(void *aContext, 1.284 + HWND hWnd, UINT nMsg, 1.285 + WPARAM wParam, LPARAM lParam, 1.286 + LRESULT *aResult) { 1.287 + if (nMsg == WM_WINDOWPOSCHANGED) { 1.288 + TaskbarTabPreview *preview = reinterpret_cast<TaskbarTabPreview*>(aContext); 1.289 + WINDOWPOS *pos = reinterpret_cast<WINDOWPOS*>(lParam); 1.290 + if (SWP_FRAMECHANGED == (pos->flags & SWP_FRAMECHANGED)) 1.291 + preview->UpdateProxyWindowStyle(); 1.292 + } else { 1.293 + NS_NOTREACHED("Style changed hook fired on non-style changed message"); 1.294 + } 1.295 + return false; 1.296 +} 1.297 + 1.298 +void 1.299 +TaskbarTabPreview::UpdateProxyWindowStyle() { 1.300 + if (!mProxyWindow) 1.301 + return; 1.302 + 1.303 + DWORD minMaxMask = WS_MINIMIZE | WS_MAXIMIZE; 1.304 + DWORD windowStyle = GetWindowLongW(mWnd, GWL_STYLE); 1.305 + 1.306 + DWORD proxyStyle = GetWindowLongW(mProxyWindow, GWL_STYLE); 1.307 + proxyStyle &= ~minMaxMask; 1.308 + proxyStyle |= windowStyle & minMaxMask; 1.309 + SetWindowLongW(mProxyWindow, GWL_STYLE, proxyStyle); 1.310 + 1.311 + DWORD exStyle = (WS_MAXIMIZE == (windowStyle & WS_MAXIMIZE)) ? WS_EX_TOOLWINDOW : 0; 1.312 + SetWindowLongW(mProxyWindow, GWL_EXSTYLE, exStyle); 1.313 +} 1.314 + 1.315 +nsresult 1.316 +TaskbarTabPreview::UpdateTitle() { 1.317 + NS_ASSERTION(mVisible, "UpdateTitle called on invisible preview"); 1.318 + 1.319 + if (!::SetWindowTextW(mProxyWindow, mTitle.get())) 1.320 + return NS_ERROR_FAILURE; 1.321 + return NS_OK; 1.322 +} 1.323 + 1.324 +nsresult 1.325 +TaskbarTabPreview::UpdateIcon() { 1.326 + NS_ASSERTION(mVisible, "UpdateIcon called on invisible preview"); 1.327 + 1.328 + ::SendMessageW(mProxyWindow, WM_SETICON, ICON_SMALL, (LPARAM)mIcon); 1.329 + 1.330 + return NS_OK; 1.331 +} 1.332 + 1.333 +nsresult 1.334 +TaskbarTabPreview::UpdateNext() { 1.335 + NS_ASSERTION(CanMakeTaskbarCalls() && mVisible, "UpdateNext called on invisible tab preview"); 1.336 + HWND hNext = nullptr; 1.337 + if (mNext) { 1.338 + bool visible; 1.339 + nsresult rv = mNext->GetVisible(&visible); 1.340 + 1.341 + NS_ENSURE_SUCCESS(rv, rv); 1.342 + 1.343 + // Can only move next to enabled previews 1.344 + if (!visible) 1.345 + return NS_ERROR_FAILURE; 1.346 + 1.347 + hNext = (HWND)mNext->GetHWND(); 1.348 + 1.349 + // hNext must be registered with the taskbar if the call is to succeed 1.350 + mNext->EnsureRegistration(); 1.351 + } 1.352 + if (FAILED(mTaskbar->SetTabOrder(mProxyWindow, hNext))) 1.353 + return NS_ERROR_FAILURE; 1.354 + return NS_OK; 1.355 +} 1.356 + 1.357 + 1.358 +} // namespace widget 1.359 +} // namespace mozilla 1.360 +