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"
12 #include <nsITaskbarPreviewController.h>
14 #define TASKBARPREVIEW_HWNDID L"TaskbarTabPreviewHwnd"
19 NS_IMPL_ISUPPORTS1(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),
29 WindowHook
&hook
= GetWindowHook();
30 hook
.AddMonitor(WM_WINDOWPOSCHANGED
, MainWindowHook
, this);
33 TaskbarTabPreview::~TaskbarTabPreview() {
39 // We need to ensure that proxy window disappears or else Bad Things happen.
43 NS_ASSERTION(!mProxyWindow
, "Taskbar proxy window was not destroyed!");
45 if (IsWindowAvailable()) {
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
;
60 TaskbarTabPreview::PreviewWindow() {
65 TaskbarTabPreview::GetHWND() {
70 TaskbarTabPreview::EnsureRegistration() {
71 NS_ASSERTION(mVisible
&& CanMakeTaskbarCalls(), "EnsureRegistration called when it is not safe to do so");
73 (void) UpdateTaskbarProperties();
77 TaskbarTabPreview::GetTitle(nsAString
&aTitle
) {
83 TaskbarTabPreview::SetTitle(const nsAString
&aTitle
) {
85 return mVisible
? UpdateTitle() : NS_OK
;
89 TaskbarTabPreview::SetIcon(imgIContainer
*icon
) {
90 HICON hIcon
= nullptr;
93 rv
= nsWindowGfx::CreateIcon(icon
, false, 0, 0,
94 nsWindowGfx::GetIconMetrics(nsWindowGfx::kSmallIcon
),
96 NS_ENSURE_SUCCESS(rv
, rv
);
100 ::DestroyIcon(mIcon
);
103 return mVisible
? UpdateIcon() : NS_OK
;
107 TaskbarTabPreview::GetIcon(imgIContainer
**icon
) {
108 NS_IF_ADDREF(*icon
= mIconImage
);
113 TaskbarTabPreview::Move(nsITaskbarTabPreview
*aNext
) {
115 return NS_ERROR_INVALID_ARG
;
117 return CanMakeTaskbarCalls() ? UpdateNext() : NS_OK
;
121 TaskbarTabPreview::UpdateTaskbarProperties() {
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();
136 TaskbarTabPreview::WndProc(UINT nMsg
, WPARAM wParam
, LPARAM lParam
) {
137 nsRefPtr
<TaskbarTabPreview
> kungFuDeathGrip(this);
140 TaskbarPreview::EnableCustomDrawing(mProxyWindow
, true);
143 mController
->OnClose();
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.
150 nsresult rv
= mController
->OnActivate(&activateWindow
);
151 if (NS_SUCCEEDED(rv
) && activateWindow
) {
152 nsWindow
* win
= WinUtils::GetNSWindowPtr(mWnd
);
154 nsWindow
* parent
= win
->GetTopLevelWindow(true);
163 return (LRESULT
)mIcon
;
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
) {
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
174 ::SendMessageW(mWnd
, WM_SYSCOMMAND
, wParam
, lParam
);
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
);
186 return TaskbarPreview::WndProc(nMsg
, wParam
, lParam
);
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
;
200 preview
= reinterpret_cast<TaskbarTabPreview
*>(::GetPropW(hWnd
, TASKBARPREVIEW_HWNDID
));
201 if (nMsg
== WM_DESTROY
)
202 ::RemovePropW(hWnd
, TASKBARPREVIEW_HWNDID
);
206 return preview
->WndProc(nMsg
, wParam
, lParam
);
207 return ::DefWindowProcW(hWnd
, nMsg
, wParam
, lParam
);
211 TaskbarTabPreview::Enable() {
213 HINSTANCE module
= GetModuleHandle(nullptr);
215 if (!GetClassInfoW(module
, kWindowClass
, &wc
)) {
217 wc
.lpfnWndProc
= GlobalWndProc
;
220 wc
.hInstance
= module
;
222 wc
.hCursor
= nullptr;
223 wc
.hbrBackground
= (HBRUSH
) nullptr;
224 wc
.lpszMenuName
= (LPCWSTR
) nullptr;
225 wc
.lpszClassName
= kWindowClass
;
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
233 return NS_ERROR_INVALID_ARG
;
235 UpdateProxyWindowStyle();
237 nsresult rv
= TaskbarPreview::Enable();
239 rvUpdate
= UpdateTitle();
240 if (NS_FAILED(rvUpdate
))
243 rvUpdate
= UpdateIcon();
244 if (NS_FAILED(rvUpdate
))
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.
256 TaskbarPreview::Disable();
258 if (FAILED(mTaskbar
->UnregisterTab(mProxyWindow
)))
259 return NS_ERROR_FAILURE
;
262 // TaskbarPreview::WndProc will set mProxyWindow to null
263 if (!DestroyWindow(mProxyWindow
))
264 return NS_ERROR_FAILURE
;
265 mProxyWindow
= nullptr;
270 TaskbarTabPreview::DetachFromNSWindow() {
271 (void) SetVisible(false);
272 WindowHook
&hook
= GetWindowHook();
273 hook
.RemoveMonitor(WM_WINDOWPOSCHANGED
, MainWindowHook
, this);
275 TaskbarPreview::DetachFromNSWindow();
280 TaskbarTabPreview::MainWindowHook(void *aContext
,
281 HWND hWnd
, UINT nMsg
,
282 WPARAM wParam
, LPARAM lParam
,
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();
290 NS_NOTREACHED("Style changed hook fired on non-style changed message");
296 TaskbarTabPreview::UpdateProxyWindowStyle() {
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
);
313 TaskbarTabPreview::UpdateTitle() {
314 NS_ASSERTION(mVisible
, "UpdateTitle called on invisible preview");
316 if (!::SetWindowTextW(mProxyWindow
, mTitle
.get()))
317 return NS_ERROR_FAILURE
;
322 TaskbarTabPreview::UpdateIcon() {
323 NS_ASSERTION(mVisible
, "UpdateIcon called on invisible preview");
325 ::SendMessageW(mProxyWindow
, WM_SETICON
, ICON_SMALL
, (LPARAM
)mIcon
);
331 TaskbarTabPreview::UpdateNext() {
332 NS_ASSERTION(CanMakeTaskbarCalls() && mVisible
, "UpdateNext called on invisible tab preview");
333 HWND hNext
= nullptr;
336 nsresult rv
= mNext
->GetVisible(&visible
);
338 NS_ENSURE_SUCCESS(rv
, rv
);
340 // Can only move next to enabled previews
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
;
355 } // namespace widget
356 } // namespace mozilla