Bumping manifests a=b2g-bump
[gecko.git] / widget / windows / TaskbarTabPreview.cpp
blobd14c9ea99ad04618c881f6b3182a7726ad55c3af
1 /* vim: se cin sw=2 ts=2 et : */
2 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "TaskbarTabPreview.h"
9 #include "nsWindowGfx.h"
10 #include "nsUXThemeData.h"
11 #include "WinUtils.h"
12 #include <nsITaskbarPreviewController.h>
14 #define TASKBARPREVIEW_HWNDID L"TaskbarTabPreviewHwnd"
16 namespace mozilla {
17 namespace widget {
19 NS_IMPL_ISUPPORTS(TaskbarTabPreview, nsITaskbarTabPreview)
21 const wchar_t *const kWindowClass = L"MozillaTaskbarPreviewClass";
23 TaskbarTabPreview::TaskbarTabPreview(ITaskbarList4 *aTaskbar, nsITaskbarPreviewController *aController, HWND aHWND, nsIDocShell *aShell)
24 : TaskbarPreview(aTaskbar, aController, aHWND, aShell),
25 mProxyWindow(nullptr),
26 mIcon(nullptr),
27 mRegistered(false)
29 WindowHook &hook = GetWindowHook();
30 hook.AddMonitor(WM_WINDOWPOSCHANGED, MainWindowHook, this);
33 TaskbarTabPreview::~TaskbarTabPreview() {
34 if (mIcon) {
35 ::DestroyIcon(mIcon);
36 mIcon = nullptr;
39 // We need to ensure that proxy window disappears or else Bad Things happen.
40 if (mProxyWindow)
41 Disable();
43 NS_ASSERTION(!mProxyWindow, "Taskbar proxy window was not destroyed!");
45 if (IsWindowAvailable()) {
46 DetachFromNSWindow();
47 } else {
48 mWnd = nullptr;
52 nsresult
53 TaskbarTabPreview::ShowActive(bool active) {
54 NS_ASSERTION(mVisible && CanMakeTaskbarCalls(), "ShowActive called on invisible window or before taskbar calls can be made for this window");
55 return FAILED(mTaskbar->SetTabActive(active ? mProxyWindow : nullptr,
56 mWnd, 0)) ? NS_ERROR_FAILURE : NS_OK;
59 HWND &
60 TaskbarTabPreview::PreviewWindow() {
61 return mProxyWindow;
64 nativeWindow
65 TaskbarTabPreview::GetHWND() {
66 return mProxyWindow;
69 void
70 TaskbarTabPreview::EnsureRegistration() {
71 NS_ASSERTION(mVisible && CanMakeTaskbarCalls(), "EnsureRegistration called when it is not safe to do so");
73 (void) UpdateTaskbarProperties();
76 NS_IMETHODIMP
77 TaskbarTabPreview::GetTitle(nsAString &aTitle) {
78 aTitle = mTitle;
79 return NS_OK;
82 NS_IMETHODIMP
83 TaskbarTabPreview::SetTitle(const nsAString &aTitle) {
84 mTitle = aTitle;
85 return mVisible ? UpdateTitle() : NS_OK;
88 NS_IMETHODIMP
89 TaskbarTabPreview::SetIcon(imgIContainer *icon) {
90 HICON hIcon = nullptr;
91 if (icon) {
92 nsresult rv;
93 rv = nsWindowGfx::CreateIcon(icon, false, 0, 0,
94 nsWindowGfx::GetIconMetrics(nsWindowGfx::kSmallIcon),
95 &hIcon);
96 NS_ENSURE_SUCCESS(rv, rv);
99 if (mIcon)
100 ::DestroyIcon(mIcon);
101 mIcon = hIcon;
102 mIconImage = icon;
103 return mVisible ? UpdateIcon() : NS_OK;
106 NS_IMETHODIMP
107 TaskbarTabPreview::GetIcon(imgIContainer **icon) {
108 NS_IF_ADDREF(*icon = mIconImage);
109 return NS_OK;
112 NS_IMETHODIMP
113 TaskbarTabPreview::Move(nsITaskbarTabPreview *aNext) {
114 if (aNext == this)
115 return NS_ERROR_INVALID_ARG;
116 mNext = aNext;
117 return CanMakeTaskbarCalls() ? UpdateNext() : NS_OK;
120 nsresult
121 TaskbarTabPreview::UpdateTaskbarProperties() {
122 if (mRegistered)
123 return NS_OK;
125 if (FAILED(mTaskbar->RegisterTab(mProxyWindow, mWnd)))
126 return NS_ERROR_FAILURE;
128 nsresult rv = UpdateNext();
129 NS_ENSURE_SUCCESS(rv, rv);
130 rv = TaskbarPreview::UpdateTaskbarProperties();
131 mRegistered = true;
132 return rv;
135 LRESULT
136 TaskbarTabPreview::WndProc(UINT nMsg, WPARAM wParam, LPARAM lParam) {
137 nsRefPtr<TaskbarTabPreview> kungFuDeathGrip(this);
138 switch (nMsg) {
139 case WM_CREATE:
140 TaskbarPreview::EnableCustomDrawing(mProxyWindow, true);
141 return 0;
142 case WM_CLOSE:
143 mController->OnClose();
144 return 0;
145 case WM_ACTIVATE:
146 if (LOWORD(wParam) == WA_ACTIVE) {
147 // Activate the tab the user selected then restore the main window,
148 // keeping normal/max window state intact.
149 bool activateWindow;
150 nsresult rv = mController->OnActivate(&activateWindow);
151 if (NS_SUCCEEDED(rv) && activateWindow) {
152 nsWindow* win = WinUtils::GetNSWindowPtr(mWnd);
153 if (win) {
154 nsWindow * parent = win->GetTopLevelWindow(true);
155 if (parent) {
156 parent->Show(true);
161 return 0;
162 case WM_GETICON:
163 return (LRESULT)mIcon;
164 case WM_SYSCOMMAND:
165 // Send activation events to the top level window and select the proper
166 // tab through the controller.
167 if (wParam == SC_RESTORE || wParam == SC_MAXIMIZE) {
168 bool activateWindow;
169 nsresult rv = mController->OnActivate(&activateWindow);
170 if (NS_SUCCEEDED(rv) && activateWindow) {
171 // Note, restoring an iconic, maximized window here will only
172 // activate the maximized window. This is not a bug, it's default
173 // windows behavior.
174 ::SendMessageW(mWnd, WM_SYSCOMMAND, wParam, lParam);
176 return 0;
178 // Forward everything else to the top level window. Do not forward
179 // close since that's intended for the tab. When the preview proxy
180 // closes, we'll close the tab above.
181 return wParam == SC_CLOSE
182 ? ::DefWindowProcW(mProxyWindow, WM_SYSCOMMAND, wParam, lParam)
183 : ::SendMessageW(mWnd, WM_SYSCOMMAND, wParam, lParam);
184 return 0;
186 return TaskbarPreview::WndProc(nMsg, wParam, lParam);
189 /* static */
190 LRESULT CALLBACK
191 TaskbarTabPreview::GlobalWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam) {
192 TaskbarTabPreview *preview(nullptr);
193 if (nMsg == WM_CREATE) {
194 CREATESTRUCT *cs = reinterpret_cast<CREATESTRUCT*>(lParam);
195 preview = reinterpret_cast<TaskbarTabPreview*>(cs->lpCreateParams);
196 if (!::SetPropW(hWnd, TASKBARPREVIEW_HWNDID, preview))
197 NS_ERROR("Could not associate native window with tab preview");
198 preview->mProxyWindow = hWnd;
199 } else {
200 preview = reinterpret_cast<TaskbarTabPreview*>(::GetPropW(hWnd, TASKBARPREVIEW_HWNDID));
201 if (nMsg == WM_DESTROY)
202 ::RemovePropW(hWnd, TASKBARPREVIEW_HWNDID);
205 if (preview)
206 return preview->WndProc(nMsg, wParam, lParam);
207 return ::DefWindowProcW(hWnd, nMsg, wParam, lParam);
210 nsresult
211 TaskbarTabPreview::Enable() {
212 WNDCLASSW wc;
213 HINSTANCE module = GetModuleHandle(nullptr);
215 if (!GetClassInfoW(module, kWindowClass, &wc)) {
216 wc.style = 0;
217 wc.lpfnWndProc = GlobalWndProc;
218 wc.cbClsExtra = 0;
219 wc.cbWndExtra = 0;
220 wc.hInstance = module;
221 wc.hIcon = nullptr;
222 wc.hCursor = nullptr;
223 wc.hbrBackground = (HBRUSH) nullptr;
224 wc.lpszMenuName = (LPCWSTR) nullptr;
225 wc.lpszClassName = kWindowClass;
226 RegisterClassW(&wc);
228 ::CreateWindowW(kWindowClass, L"TaskbarPreviewWindow",
229 WS_CAPTION | WS_SYSMENU, 0, 0, 200, 60,
230 nullptr, nullptr, module, this);
231 // GlobalWndProc will set mProxyWindow so that WM_CREATE can have a valid HWND
232 if (!mProxyWindow)
233 return NS_ERROR_INVALID_ARG;
235 UpdateProxyWindowStyle();
237 nsresult rv = TaskbarPreview::Enable();
238 nsresult rvUpdate;
239 rvUpdate = UpdateTitle();
240 if (NS_FAILED(rvUpdate))
241 rv = rvUpdate;
243 rvUpdate = UpdateIcon();
244 if (NS_FAILED(rvUpdate))
245 rv = rvUpdate;
247 return rv;
250 nsresult
251 TaskbarTabPreview::Disable() {
252 // TaskbarPreview::Disable assumes that mWnd is valid but this method can be
253 // called when it is null iff the nsWindow has already been destroyed and we
254 // are still visible for some reason during object destruction.
255 if (mWnd)
256 TaskbarPreview::Disable();
258 if (FAILED(mTaskbar->UnregisterTab(mProxyWindow)))
259 return NS_ERROR_FAILURE;
260 mRegistered = false;
262 // TaskbarPreview::WndProc will set mProxyWindow to null
263 if (!DestroyWindow(mProxyWindow))
264 return NS_ERROR_FAILURE;
265 mProxyWindow = nullptr;
266 return NS_OK;
269 void
270 TaskbarTabPreview::DetachFromNSWindow() {
271 (void) SetVisible(false);
272 WindowHook &hook = GetWindowHook();
273 hook.RemoveMonitor(WM_WINDOWPOSCHANGED, MainWindowHook, this);
275 TaskbarPreview::DetachFromNSWindow();
278 /* static */
279 bool
280 TaskbarTabPreview::MainWindowHook(void *aContext,
281 HWND hWnd, UINT nMsg,
282 WPARAM wParam, LPARAM lParam,
283 LRESULT *aResult) {
284 if (nMsg == WM_WINDOWPOSCHANGED) {
285 TaskbarTabPreview *preview = reinterpret_cast<TaskbarTabPreview*>(aContext);
286 WINDOWPOS *pos = reinterpret_cast<WINDOWPOS*>(lParam);
287 if (SWP_FRAMECHANGED == (pos->flags & SWP_FRAMECHANGED))
288 preview->UpdateProxyWindowStyle();
289 } else {
290 NS_NOTREACHED("Style changed hook fired on non-style changed message");
292 return false;
295 void
296 TaskbarTabPreview::UpdateProxyWindowStyle() {
297 if (!mProxyWindow)
298 return;
300 DWORD minMaxMask = WS_MINIMIZE | WS_MAXIMIZE;
301 DWORD windowStyle = GetWindowLongW(mWnd, GWL_STYLE);
303 DWORD proxyStyle = GetWindowLongW(mProxyWindow, GWL_STYLE);
304 proxyStyle &= ~minMaxMask;
305 proxyStyle |= windowStyle & minMaxMask;
306 SetWindowLongW(mProxyWindow, GWL_STYLE, proxyStyle);
308 DWORD exStyle = (WS_MAXIMIZE == (windowStyle & WS_MAXIMIZE)) ? WS_EX_TOOLWINDOW : 0;
309 SetWindowLongW(mProxyWindow, GWL_EXSTYLE, exStyle);
312 nsresult
313 TaskbarTabPreview::UpdateTitle() {
314 NS_ASSERTION(mVisible, "UpdateTitle called on invisible preview");
316 if (!::SetWindowTextW(mProxyWindow, mTitle.get()))
317 return NS_ERROR_FAILURE;
318 return NS_OK;
321 nsresult
322 TaskbarTabPreview::UpdateIcon() {
323 NS_ASSERTION(mVisible, "UpdateIcon called on invisible preview");
325 ::SendMessageW(mProxyWindow, WM_SETICON, ICON_SMALL, (LPARAM)mIcon);
327 return NS_OK;
330 nsresult
331 TaskbarTabPreview::UpdateNext() {
332 NS_ASSERTION(CanMakeTaskbarCalls() && mVisible, "UpdateNext called on invisible tab preview");
333 HWND hNext = nullptr;
334 if (mNext) {
335 bool visible;
336 nsresult rv = mNext->GetVisible(&visible);
338 NS_ENSURE_SUCCESS(rv, rv);
340 // Can only move next to enabled previews
341 if (!visible)
342 return NS_ERROR_FAILURE;
344 hNext = (HWND)mNext->GetHWND();
346 // hNext must be registered with the taskbar if the call is to succeed
347 mNext->EnsureRegistration();
349 if (FAILED(mTaskbar->SetTabOrder(mProxyWindow, hNext)))
350 return NS_ERROR_FAILURE;
351 return NS_OK;
355 } // namespace widget
356 } // namespace mozilla