1 /* vim: se cin sw=2 ts=2 et : */
2 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 *
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/. */
7
8 #include "mozilla/ArrayUtils.h"
9
10 #include <nsITaskbarPreviewController.h>
11 #include "TaskbarWindowPreview.h"
12 #include "WindowHook.h"
13 #include "nsUXThemeData.h"
14 #include "TaskbarPreviewButton.h"
15 #include "nsWindow.h"
16 #include "nsWindowGfx.h"
17
18 namespace mozilla {
19 namespace widget {
20
21 namespace {
WindowHookProc(void * aContext,HWND hWnd,UINT nMsg,WPARAM wParam,LPARAM lParam,LRESULT * aResult)22 bool WindowHookProc(void* aContext, HWND hWnd, UINT nMsg, WPARAM wParam,
23 LPARAM lParam, LRESULT* aResult) {
24 TaskbarWindowPreview* preview =
25 reinterpret_cast<TaskbarWindowPreview*>(aContext);
26 *aResult = preview->WndProc(nMsg, wParam, lParam);
27 return true;
28 }
29 } // namespace
30
31 NS_IMPL_ISUPPORTS(TaskbarWindowPreview, nsITaskbarWindowPreview,
32 nsITaskbarProgress, nsITaskbarOverlayIconController,
33 nsISupportsWeakReference)
34
35 /**
36 * These correspond directly to the states defined in nsITaskbarProgress.idl, so
37 * they should be kept in sync.
38 */
39 static TBPFLAG sNativeStates[] = {TBPF_NOPROGRESS, TBPF_INDETERMINATE,
40 TBPF_NORMAL, TBPF_ERROR, TBPF_PAUSED};
41
TaskbarWindowPreview(ITaskbarList4 * aTaskbar,nsITaskbarPreviewController * aController,HWND aHWND,nsIDocShell * aShell)42 TaskbarWindowPreview::TaskbarWindowPreview(
43 ITaskbarList4* aTaskbar, nsITaskbarPreviewController* aController,
44 HWND aHWND, nsIDocShell* aShell)
45 : TaskbarPreview(aTaskbar, aController, aHWND, aShell),
46 mCustomDrawing(false),
47 mHaveButtons(false),
48 mState(TBPF_NOPROGRESS),
49 mCurrentValue(0),
50 mMaxValue(0),
51 mOverlayIcon(nullptr) {
52 // Window previews are visible by default
53 (void)SetVisible(true);
54
55 memset(mThumbButtons, 0, sizeof mThumbButtons);
56 for (int32_t i = 0; i < nsITaskbarWindowPreview::NUM_TOOLBAR_BUTTONS; i++) {
57 mThumbButtons[i].dwMask = THB_FLAGS | THB_ICON | THB_TOOLTIP;
58 mThumbButtons[i].iId = i;
59 mThumbButtons[i].dwFlags = THBF_HIDDEN;
60 }
61 }
62
~TaskbarWindowPreview()63 TaskbarWindowPreview::~TaskbarWindowPreview() {
64 if (mOverlayIcon) {
65 ::DestroyIcon(mOverlayIcon);
66 mOverlayIcon = nullptr;
67 }
68
69 // We need to clean up a hook associated with the "this" pointer.
70 SetVisible(false);
71
72 if (IsWindowAvailable()) {
73 DetachFromNSWindow();
74 } else {
75 mWnd = nullptr;
76 }
77 }
78
Init()79 nsresult TaskbarWindowPreview::Init() {
80 nsresult rv = TaskbarPreview::Init();
81 if (NS_FAILED(rv)) {
82 return rv;
83 }
84
85 if (CanMakeTaskbarCalls()) {
86 return NS_OK;
87 }
88
89 WindowHook* hook = GetWindowHook();
90 if (!hook) {
91 return NS_ERROR_NOT_AVAILABLE;
92 }
93
94 return hook->AddMonitor(nsAppShell::GetTaskbarButtonCreatedMessage(),
95 TaskbarWindowHook, this);
96 }
97
ShowActive(bool active)98 nsresult TaskbarWindowPreview::ShowActive(bool active) {
99 return FAILED(mTaskbar->ActivateTab(active ? mWnd : nullptr))
100 ? NS_ERROR_FAILURE
101 : NS_OK;
102 }
103
PreviewWindow()104 HWND& TaskbarWindowPreview::PreviewWindow() { return mWnd; }
105
GetButton(uint32_t index,nsITaskbarPreviewButton ** _retVal)106 nsresult TaskbarWindowPreview::GetButton(uint32_t index,
107 nsITaskbarPreviewButton** _retVal) {
108 if (index >= nsITaskbarWindowPreview::NUM_TOOLBAR_BUTTONS)
109 return NS_ERROR_INVALID_ARG;
110
111 nsCOMPtr<nsITaskbarPreviewButton> button(
112 do_QueryReferent(mWeakButtons[index]));
113
114 if (!button) {
115 // Lost reference
116 button = new TaskbarPreviewButton(this, index);
117 if (!button) {
118 return NS_ERROR_OUT_OF_MEMORY;
119 }
120 mWeakButtons[index] = do_GetWeakReference(button);
121 }
122
123 if (!mHaveButtons) {
124 mHaveButtons = true;
125
126 WindowHook* hook = GetWindowHook();
127 if (!hook) {
128 return NS_ERROR_NOT_AVAILABLE;
129 }
130 (void)hook->AddHook(WM_COMMAND, WindowHookProc, this);
131
132 if (mVisible && FAILED(mTaskbar->ThumbBarAddButtons(
133 mWnd, nsITaskbarWindowPreview::NUM_TOOLBAR_BUTTONS,
134 mThumbButtons))) {
135 return NS_ERROR_FAILURE;
136 }
137 }
138 button.forget(_retVal);
139 return NS_OK;
140 }
141
142 NS_IMETHODIMP
SetEnableCustomDrawing(bool aEnable)143 TaskbarWindowPreview::SetEnableCustomDrawing(bool aEnable) {
144 if (aEnable == mCustomDrawing) return NS_OK;
145
146 WindowHook* hook = GetWindowHook();
147 if (!hook) {
148 return NS_ERROR_NOT_AVAILABLE;
149 }
150
151 mCustomDrawing = aEnable;
152 TaskbarPreview::EnableCustomDrawing(mWnd, aEnable);
153
154 if (aEnable) {
155 (void)hook->AddHook(WM_DWMSENDICONICTHUMBNAIL, WindowHookProc, this);
156 (void)hook->AddHook(WM_DWMSENDICONICLIVEPREVIEWBITMAP, WindowHookProc,
157 this);
158 } else {
159 (void)hook->RemoveHook(WM_DWMSENDICONICLIVEPREVIEWBITMAP, WindowHookProc,
160 this);
161 (void)hook->RemoveHook(WM_DWMSENDICONICTHUMBNAIL, WindowHookProc, this);
162 }
163 return NS_OK;
164 }
165
166 NS_IMETHODIMP
GetEnableCustomDrawing(bool * aEnable)167 TaskbarWindowPreview::GetEnableCustomDrawing(bool* aEnable) {
168 *aEnable = mCustomDrawing;
169 return NS_OK;
170 }
171
172 NS_IMETHODIMP
SetProgressState(nsTaskbarProgressState aState,uint64_t aCurrentValue,uint64_t aMaxValue)173 TaskbarWindowPreview::SetProgressState(nsTaskbarProgressState aState,
174 uint64_t aCurrentValue,
175 uint64_t aMaxValue) {
176 NS_ENSURE_ARG_RANGE(aState, nsTaskbarProgressState(0),
177 nsTaskbarProgressState(ArrayLength(sNativeStates) - 1));
178
179 TBPFLAG nativeState = sNativeStates[aState];
180 if (nativeState == TBPF_NOPROGRESS || nativeState == TBPF_INDETERMINATE) {
181 NS_ENSURE_TRUE(aCurrentValue == 0, NS_ERROR_INVALID_ARG);
182 NS_ENSURE_TRUE(aMaxValue == 0, NS_ERROR_INVALID_ARG);
183 }
184
185 if (aCurrentValue > aMaxValue) return NS_ERROR_ILLEGAL_VALUE;
186
187 mState = nativeState;
188 mCurrentValue = aCurrentValue;
189 mMaxValue = aMaxValue;
190
191 // Only update if we can
192 return CanMakeTaskbarCalls() ? UpdateTaskbarProgress() : NS_OK;
193 }
194
195 NS_IMETHODIMP
SetOverlayIcon(imgIContainer * aStatusIcon,const nsAString & aStatusDescription)196 TaskbarWindowPreview::SetOverlayIcon(imgIContainer* aStatusIcon,
197 const nsAString& aStatusDescription) {
198 nsresult rv;
199 if (aStatusIcon) {
200 // The image shouldn't be animated
201 bool isAnimated;
202 rv = aStatusIcon->GetAnimated(&isAnimated);
203 NS_ENSURE_SUCCESS(rv, rv);
204 NS_ENSURE_FALSE(isAnimated, NS_ERROR_INVALID_ARG);
205 }
206
207 HICON hIcon = nullptr;
208 if (aStatusIcon) {
209 rv = nsWindowGfx::CreateIcon(
210 aStatusIcon, false, LayoutDeviceIntPoint(),
211 nsWindowGfx::GetIconMetrics(nsWindowGfx::kSmallIcon), &hIcon);
212 NS_ENSURE_SUCCESS(rv, rv);
213 }
214
215 if (mOverlayIcon) ::DestroyIcon(mOverlayIcon);
216 mOverlayIcon = hIcon;
217 mIconDescription = aStatusDescription;
218
219 // Only update if we can
220 return CanMakeTaskbarCalls() ? UpdateOverlayIcon() : NS_OK;
221 }
222
UpdateTaskbarProperties()223 nsresult TaskbarWindowPreview::UpdateTaskbarProperties() {
224 if (mHaveButtons) {
225 if (FAILED(mTaskbar->ThumbBarAddButtons(
226 mWnd, nsITaskbarWindowPreview::NUM_TOOLBAR_BUTTONS, mThumbButtons)))
227 return NS_ERROR_FAILURE;
228 }
229 nsresult rv = UpdateTaskbarProgress();
230 NS_ENSURE_SUCCESS(rv, rv);
231 rv = UpdateOverlayIcon();
232 NS_ENSURE_SUCCESS(rv, rv);
233 return TaskbarPreview::UpdateTaskbarProperties();
234 }
235
UpdateTaskbarProgress()236 nsresult TaskbarWindowPreview::UpdateTaskbarProgress() {
237 HRESULT hr = mTaskbar->SetProgressState(mWnd, mState);
238 if (SUCCEEDED(hr) && mState != TBPF_NOPROGRESS &&
239 mState != TBPF_INDETERMINATE)
240 hr = mTaskbar->SetProgressValue(mWnd, mCurrentValue, mMaxValue);
241
242 return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE;
243 }
244
UpdateOverlayIcon()245 nsresult TaskbarWindowPreview::UpdateOverlayIcon() {
246 HRESULT hr =
247 mTaskbar->SetOverlayIcon(mWnd, mOverlayIcon, mIconDescription.get());
248 return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE;
249 }
250
251 LRESULT
WndProc(UINT nMsg,WPARAM wParam,LPARAM lParam)252 TaskbarWindowPreview::WndProc(UINT nMsg, WPARAM wParam, LPARAM lParam) {
253 RefPtr<TaskbarWindowPreview> kungFuDeathGrip(this);
254 switch (nMsg) {
255 case WM_COMMAND: {
256 uint32_t id = LOWORD(wParam);
257 uint32_t index = id;
258 nsCOMPtr<nsITaskbarPreviewButton> button;
259 nsresult rv = GetButton(index, getter_AddRefs(button));
260 if (NS_SUCCEEDED(rv)) mController->OnClick(button);
261 }
262 return 0;
263 }
264 return TaskbarPreview::WndProc(nMsg, wParam, lParam);
265 }
266
267 /* static */
TaskbarWindowHook(void * aContext,HWND hWnd,UINT nMsg,WPARAM wParam,LPARAM lParam,LRESULT * aResult)268 bool TaskbarWindowPreview::TaskbarWindowHook(void* aContext, HWND hWnd,
269 UINT nMsg, WPARAM wParam,
270 LPARAM lParam, LRESULT* aResult) {
271 NS_ASSERTION(nMsg == nsAppShell::GetTaskbarButtonCreatedMessage(),
272 "Window hook proc called with wrong message");
273 TaskbarWindowPreview* preview =
274 reinterpret_cast<TaskbarWindowPreview*>(aContext);
275 // Now we can make all the calls to mTaskbar
276 preview->UpdateTaskbarProperties();
277 return false;
278 }
279
Enable()280 nsresult TaskbarWindowPreview::Enable() {
281 nsresult rv = TaskbarPreview::Enable();
282 NS_ENSURE_SUCCESS(rv, rv);
283
284 return FAILED(mTaskbar->AddTab(mWnd)) ? NS_ERROR_FAILURE : NS_OK;
285 }
286
Disable()287 nsresult TaskbarWindowPreview::Disable() {
288 nsresult rv = TaskbarPreview::Disable();
289 NS_ENSURE_SUCCESS(rv, rv);
290
291 return FAILED(mTaskbar->DeleteTab(mWnd)) ? NS_ERROR_FAILURE : NS_OK;
292 }
293
DetachFromNSWindow()294 void TaskbarWindowPreview::DetachFromNSWindow() {
295 // Remove the hooks we have for drawing
296 SetEnableCustomDrawing(false);
297
298 if (WindowHook* hook = GetWindowHook()) {
299 (void)hook->RemoveHook(WM_COMMAND, WindowHookProc, this);
300 (void)hook->RemoveMonitor(nsAppShell::GetTaskbarButtonCreatedMessage(),
301 TaskbarWindowHook, this);
302 }
303 TaskbarPreview::DetachFromNSWindow();
304 }
305
UpdateButtons()306 nsresult TaskbarWindowPreview::UpdateButtons() {
307 NS_ASSERTION(mVisible, "UpdateButtons called on invisible preview");
308
309 if (FAILED(mTaskbar->ThumbBarUpdateButtons(
310 mWnd, nsITaskbarWindowPreview::NUM_TOOLBAR_BUTTONS, mThumbButtons)))
311 return NS_ERROR_FAILURE;
312 return NS_OK;
313 }
314
UpdateButton(uint32_t index)315 nsresult TaskbarWindowPreview::UpdateButton(uint32_t index) {
316 if (index >= nsITaskbarWindowPreview::NUM_TOOLBAR_BUTTONS)
317 return NS_ERROR_INVALID_ARG;
318 if (mVisible) {
319 if (FAILED(mTaskbar->ThumbBarUpdateButtons(mWnd, 1, &mThumbButtons[index])))
320 return NS_ERROR_FAILURE;
321 }
322 return NS_OK;
323 }
324
325 } // namespace widget
326 } // namespace mozilla
327