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_ISUPPORTS(TaskbarTabPreview
, nsITaskbarTabPreview
)
21 const wchar_t* const kWindowClass
= L
"MozillaTaskbarPreviewClass";
23 TaskbarTabPreview::TaskbarTabPreview(ITaskbarList4
* aTaskbar
,
24 nsITaskbarPreviewController
* aController
,
25 HWND aHWND
, nsIDocShell
* aShell
)
26 : TaskbarPreview(aTaskbar
, aController
, aHWND
, aShell
),
27 mProxyWindow(nullptr),
31 TaskbarTabPreview::~TaskbarTabPreview() {
37 // We need to ensure that proxy window disappears or else Bad Things happen.
38 if (mProxyWindow
) Disable();
40 NS_ASSERTION(!mProxyWindow
, "Taskbar proxy window was not destroyed!");
42 if (IsWindowAvailable()) {
49 nsresult
TaskbarTabPreview::Init() {
50 nsresult rv
= TaskbarPreview::Init();
55 WindowHook
* hook
= GetWindowHook();
57 return NS_ERROR_NOT_AVAILABLE
;
60 return hook
->AddMonitor(WM_WINDOWPOSCHANGED
, MainWindowHook
, this);
63 nsresult
TaskbarTabPreview::ShowActive(bool active
) {
64 NS_ASSERTION(mVisible
&& CanMakeTaskbarCalls(),
65 "ShowActive called on invisible window or before taskbar calls "
66 "can be made for this window");
68 mTaskbar
->SetTabActive(active
? mProxyWindow
: nullptr, mWnd
, 0))
73 HWND
& TaskbarTabPreview::PreviewWindow() { return mProxyWindow
; }
75 nativeWindow
TaskbarTabPreview::GetHWND() { return mProxyWindow
; }
77 void TaskbarTabPreview::EnsureRegistration() {
78 NS_ASSERTION(mVisible
&& CanMakeTaskbarCalls(),
79 "EnsureRegistration called when it is not safe to do so");
81 (void)UpdateTaskbarProperties();
85 TaskbarTabPreview::GetTitle(nsAString
& aTitle
) {
91 TaskbarTabPreview::SetTitle(const nsAString
& aTitle
) {
93 return mVisible
? UpdateTitle() : NS_OK
;
97 TaskbarTabPreview::SetIcon(imgIContainer
* icon
) {
98 HICON hIcon
= nullptr;
101 rv
= nsWindowGfx::CreateIcon(
102 icon
, false, LayoutDeviceIntPoint(),
103 nsWindowGfx::GetIconMetrics(nsWindowGfx::kSmallIcon
), &hIcon
);
104 NS_ENSURE_SUCCESS(rv
, rv
);
107 if (mIcon
) ::DestroyIcon(mIcon
);
110 return mVisible
? UpdateIcon() : NS_OK
;
114 TaskbarTabPreview::GetIcon(imgIContainer
** icon
) {
115 NS_IF_ADDREF(*icon
= mIconImage
);
120 TaskbarTabPreview::Move(nsITaskbarTabPreview
* aNext
) {
121 if (aNext
== this) return NS_ERROR_INVALID_ARG
;
123 return CanMakeTaskbarCalls() ? UpdateNext() : NS_OK
;
126 nsresult
TaskbarTabPreview::UpdateTaskbarProperties() {
127 if (mRegistered
) return NS_OK
;
129 if (FAILED(mTaskbar
->RegisterTab(mProxyWindow
, mWnd
)))
130 return NS_ERROR_FAILURE
;
132 nsresult rv
= UpdateNext();
133 NS_ENSURE_SUCCESS(rv
, rv
);
134 rv
= TaskbarPreview::UpdateTaskbarProperties();
140 TaskbarTabPreview::WndProc(UINT nMsg
, WPARAM wParam
, LPARAM lParam
) {
141 RefPtr
<TaskbarTabPreview
> kungFuDeathGrip(this);
144 TaskbarPreview::EnableCustomDrawing(mProxyWindow
, true);
147 mController
->OnClose();
150 if (LOWORD(wParam
) == WA_ACTIVE
) {
151 // Activate the tab the user selected then restore the main window,
152 // keeping normal/max window state intact.
154 nsresult rv
= mController
->OnActivate(&activateWindow
);
155 if (NS_SUCCEEDED(rv
) && activateWindow
) {
156 nsWindow
* win
= WinUtils::GetNSWindowPtr(mWnd
);
158 nsWindow
* parent
= win
->GetTopLevelWindow(true);
167 return (LRESULT
)mIcon
;
169 // Send activation events to the top level window and select the proper
170 // tab through the controller.
171 if (wParam
== SC_RESTORE
|| wParam
== SC_MAXIMIZE
) {
173 nsresult rv
= mController
->OnActivate(&activateWindow
);
174 if (NS_SUCCEEDED(rv
) && activateWindow
) {
175 // Note, restoring an iconic, maximized window here will only
176 // activate the maximized window. This is not a bug, it's default
178 ::SendMessageW(mWnd
, WM_SYSCOMMAND
, wParam
, lParam
);
182 // Forward everything else to the top level window. Do not forward
183 // close since that's intended for the tab. When the preview proxy
184 // closes, we'll close the tab above.
185 return wParam
== SC_CLOSE
186 ? ::DefWindowProcW(mProxyWindow
, WM_SYSCOMMAND
, wParam
, lParam
)
187 : ::SendMessageW(mWnd
, WM_SYSCOMMAND
, wParam
, lParam
);
189 return TaskbarPreview::WndProc(nMsg
, wParam
, lParam
);
193 LRESULT CALLBACK
TaskbarTabPreview::GlobalWndProc(HWND hWnd
, UINT nMsg
,
196 TaskbarTabPreview
* preview(nullptr);
197 if (nMsg
== WM_CREATE
) {
198 CREATESTRUCT
* cs
= reinterpret_cast<CREATESTRUCT
*>(lParam
);
199 preview
= reinterpret_cast<TaskbarTabPreview
*>(cs
->lpCreateParams
);
200 if (!::SetPropW(hWnd
, TASKBARPREVIEW_HWNDID
, preview
))
201 NS_ERROR("Could not associate native window with tab preview");
202 preview
->mProxyWindow
= hWnd
;
204 preview
= reinterpret_cast<TaskbarTabPreview
*>(
205 ::GetPropW(hWnd
, TASKBARPREVIEW_HWNDID
));
206 if (nMsg
== WM_DESTROY
) ::RemovePropW(hWnd
, TASKBARPREVIEW_HWNDID
);
209 if (preview
) return preview
->WndProc(nMsg
, wParam
, lParam
);
210 return ::DefWindowProcW(hWnd
, nMsg
, wParam
, lParam
);
213 nsresult
TaskbarTabPreview::Enable() {
215 HINSTANCE module
= GetModuleHandle(nullptr);
217 if (!GetClassInfoW(module
, kWindowClass
, &wc
)) {
219 wc
.lpfnWndProc
= GlobalWndProc
;
222 wc
.hInstance
= module
;
224 wc
.hCursor
= nullptr;
225 wc
.hbrBackground
= (HBRUSH
) nullptr;
226 wc
.lpszMenuName
= (LPCWSTR
) nullptr;
227 wc
.lpszClassName
= kWindowClass
;
230 ::CreateWindowW(kWindowClass
, L
"TaskbarPreviewWindow",
231 WS_CAPTION
| WS_SYSMENU
, 0, 0, 200, 60, nullptr, nullptr,
233 // GlobalWndProc will set mProxyWindow so that WM_CREATE can have a valid HWND
234 if (!mProxyWindow
) return NS_ERROR_INVALID_ARG
;
236 UpdateProxyWindowStyle();
238 nsresult rv
= TaskbarPreview::Enable();
240 rvUpdate
= UpdateTitle();
241 if (NS_FAILED(rvUpdate
)) rv
= rvUpdate
;
243 rvUpdate
= UpdateIcon();
244 if (NS_FAILED(rvUpdate
)) rv
= rvUpdate
;
249 nsresult
TaskbarTabPreview::Disable() {
250 // TaskbarPreview::Disable assumes that mWnd is valid but this method can be
251 // called when it is null iff the nsWindow has already been destroyed and we
252 // are still visible for some reason during object destruction.
253 if (mWnd
) TaskbarPreview::Disable();
255 if (FAILED(mTaskbar
->UnregisterTab(mProxyWindow
))) return NS_ERROR_FAILURE
;
258 // TaskbarPreview::WndProc will set mProxyWindow to null
259 if (!DestroyWindow(mProxyWindow
)) return NS_ERROR_FAILURE
;
260 mProxyWindow
= nullptr;
264 void TaskbarTabPreview::DetachFromNSWindow() {
265 (void)SetVisible(false);
266 if (WindowHook
* hook
= GetWindowHook()) {
267 hook
->RemoveMonitor(WM_WINDOWPOSCHANGED
, MainWindowHook
, this);
269 TaskbarPreview::DetachFromNSWindow();
273 bool TaskbarTabPreview::MainWindowHook(void* aContext
, HWND hWnd
, UINT nMsg
,
274 WPARAM wParam
, LPARAM lParam
,
276 if (nMsg
== WM_WINDOWPOSCHANGED
) {
277 TaskbarTabPreview
* preview
= reinterpret_cast<TaskbarTabPreview
*>(aContext
);
278 WINDOWPOS
* pos
= reinterpret_cast<WINDOWPOS
*>(lParam
);
279 if (SWP_FRAMECHANGED
== (pos
->flags
& SWP_FRAMECHANGED
))
280 preview
->UpdateProxyWindowStyle();
282 MOZ_ASSERT_UNREACHABLE(
283 "Style changed hook fired on non-style changed "
289 void TaskbarTabPreview::UpdateProxyWindowStyle() {
290 if (!mProxyWindow
) return;
292 DWORD minMaxMask
= WS_MINIMIZE
| WS_MAXIMIZE
;
293 DWORD windowStyle
= GetWindowLongW(mWnd
, GWL_STYLE
);
295 DWORD proxyStyle
= GetWindowLongW(mProxyWindow
, GWL_STYLE
);
296 proxyStyle
&= ~minMaxMask
;
297 proxyStyle
|= windowStyle
& minMaxMask
;
298 SetWindowLongW(mProxyWindow
, GWL_STYLE
, proxyStyle
);
301 (WS_MAXIMIZE
== (windowStyle
& WS_MAXIMIZE
)) ? WS_EX_TOOLWINDOW
: 0;
302 SetWindowLongW(mProxyWindow
, GWL_EXSTYLE
, exStyle
);
305 nsresult
TaskbarTabPreview::UpdateTitle() {
306 NS_ASSERTION(mVisible
, "UpdateTitle called on invisible preview");
308 if (!::SetWindowTextW(mProxyWindow
, mTitle
.get())) return NS_ERROR_FAILURE
;
312 nsresult
TaskbarTabPreview::UpdateIcon() {
313 NS_ASSERTION(mVisible
, "UpdateIcon called on invisible preview");
315 ::SendMessageW(mProxyWindow
, WM_SETICON
, ICON_SMALL
, (LPARAM
)mIcon
);
320 nsresult
TaskbarTabPreview::UpdateNext() {
321 NS_ASSERTION(CanMakeTaskbarCalls() && mVisible
,
322 "UpdateNext called on invisible tab preview");
323 HWND hNext
= nullptr;
326 nsresult rv
= mNext
->GetVisible(&visible
);
328 NS_ENSURE_SUCCESS(rv
, rv
);
330 // Can only move next to enabled previews
331 if (!visible
) return NS_ERROR_FAILURE
;
333 hNext
= (HWND
)mNext
->GetHWND();
335 // hNext must be registered with the taskbar if the call is to succeed
336 mNext
->EnsureRegistration();
338 if (FAILED(mTaskbar
->SetTabOrder(mProxyWindow
, hNext
)))
339 return NS_ERROR_FAILURE
;
343 } // namespace widget
344 } // namespace mozilla