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 WindowHook &hook = GetWindowHook();
63 if (!CanMakeTaskbarCalls())
64 hook.AddMonitor(nsAppShell::GetTaskbarButtonCreatedMessage(),
65 TaskbarWindowHook, this);
66 }
67
~TaskbarWindowPreview()68 TaskbarWindowPreview::~TaskbarWindowPreview() {
69 if (mOverlayIcon) {
70 ::DestroyIcon(mOverlayIcon);
71 mOverlayIcon = nullptr;
72 }
73
74 if (IsWindowAvailable()) {
75 DetachFromNSWindow();
76 } else {
77 mWnd = nullptr;
78 }
79 }
80
ShowActive(bool active)81 nsresult TaskbarWindowPreview::ShowActive(bool active) {
82 return FAILED(mTaskbar->ActivateTab(active ? mWnd : nullptr))
83 ? NS_ERROR_FAILURE
84 : NS_OK;
85 }
86
PreviewWindow()87 HWND &TaskbarWindowPreview::PreviewWindow() { return mWnd; }
88
GetButton(uint32_t index,nsITaskbarPreviewButton ** _retVal)89 nsresult TaskbarWindowPreview::GetButton(uint32_t index,
90 nsITaskbarPreviewButton **_retVal) {
91 if (index >= nsITaskbarWindowPreview::NUM_TOOLBAR_BUTTONS)
92 return NS_ERROR_INVALID_ARG;
93
94 nsCOMPtr<nsITaskbarPreviewButton> button(
95 do_QueryReferent(mWeakButtons[index]));
96
97 if (!button) {
98 // Lost reference
99 button = new TaskbarPreviewButton(this, index);
100 if (!button) {
101 return NS_ERROR_OUT_OF_MEMORY;
102 }
103 mWeakButtons[index] = do_GetWeakReference(button);
104 }
105
106 if (!mHaveButtons) {
107 mHaveButtons = true;
108
109 WindowHook &hook = GetWindowHook();
110 (void)hook.AddHook(WM_COMMAND, WindowHookProc, this);
111
112 if (mVisible && FAILED(mTaskbar->ThumbBarAddButtons(
113 mWnd, nsITaskbarWindowPreview::NUM_TOOLBAR_BUTTONS,
114 mThumbButtons))) {
115 return NS_ERROR_FAILURE;
116 }
117 }
118 button.forget(_retVal);
119 return NS_OK;
120 }
121
122 NS_IMETHODIMP
SetEnableCustomDrawing(bool aEnable)123 TaskbarWindowPreview::SetEnableCustomDrawing(bool aEnable) {
124 if (aEnable == mCustomDrawing) return NS_OK;
125 mCustomDrawing = aEnable;
126 TaskbarPreview::EnableCustomDrawing(mWnd, aEnable);
127
128 WindowHook &hook = GetWindowHook();
129 if (aEnable) {
130 (void)hook.AddHook(WM_DWMSENDICONICTHUMBNAIL, WindowHookProc, this);
131 (void)hook.AddHook(WM_DWMSENDICONICLIVEPREVIEWBITMAP, WindowHookProc, this);
132 } else {
133 (void)hook.RemoveHook(WM_DWMSENDICONICLIVEPREVIEWBITMAP, WindowHookProc,
134 this);
135 (void)hook.RemoveHook(WM_DWMSENDICONICTHUMBNAIL, WindowHookProc, this);
136 }
137 return NS_OK;
138 }
139
140 NS_IMETHODIMP
GetEnableCustomDrawing(bool * aEnable)141 TaskbarWindowPreview::GetEnableCustomDrawing(bool *aEnable) {
142 *aEnable = mCustomDrawing;
143 return NS_OK;
144 }
145
146 NS_IMETHODIMP
SetProgressState(nsTaskbarProgressState aState,uint64_t aCurrentValue,uint64_t aMaxValue)147 TaskbarWindowPreview::SetProgressState(nsTaskbarProgressState aState,
148 uint64_t aCurrentValue,
149 uint64_t aMaxValue) {
150 NS_ENSURE_ARG_RANGE(aState, nsTaskbarProgressState(0),
151 nsTaskbarProgressState(ArrayLength(sNativeStates) - 1));
152
153 TBPFLAG nativeState = sNativeStates[aState];
154 if (nativeState == TBPF_NOPROGRESS || nativeState == TBPF_INDETERMINATE) {
155 NS_ENSURE_TRUE(aCurrentValue == 0, NS_ERROR_INVALID_ARG);
156 NS_ENSURE_TRUE(aMaxValue == 0, NS_ERROR_INVALID_ARG);
157 }
158
159 if (aCurrentValue > aMaxValue) return NS_ERROR_ILLEGAL_VALUE;
160
161 mState = nativeState;
162 mCurrentValue = aCurrentValue;
163 mMaxValue = aMaxValue;
164
165 // Only update if we can
166 return CanMakeTaskbarCalls() ? UpdateTaskbarProgress() : NS_OK;
167 }
168
169 NS_IMETHODIMP
SetOverlayIcon(imgIContainer * aStatusIcon,const nsAString & aStatusDescription)170 TaskbarWindowPreview::SetOverlayIcon(imgIContainer *aStatusIcon,
171 const nsAString &aStatusDescription) {
172 nsresult rv;
173 if (aStatusIcon) {
174 // The image shouldn't be animated
175 bool isAnimated;
176 rv = aStatusIcon->GetAnimated(&isAnimated);
177 NS_ENSURE_SUCCESS(rv, rv);
178 NS_ENSURE_FALSE(isAnimated, NS_ERROR_INVALID_ARG);
179 }
180
181 HICON hIcon = nullptr;
182 if (aStatusIcon) {
183 rv = nsWindowGfx::CreateIcon(
184 aStatusIcon, false, 0, 0,
185 nsWindowGfx::GetIconMetrics(nsWindowGfx::kSmallIcon), &hIcon);
186 NS_ENSURE_SUCCESS(rv, rv);
187 }
188
189 if (mOverlayIcon) ::DestroyIcon(mOverlayIcon);
190 mOverlayIcon = hIcon;
191 mIconDescription = aStatusDescription;
192
193 // Only update if we can
194 return CanMakeTaskbarCalls() ? UpdateOverlayIcon() : NS_OK;
195 }
196
UpdateTaskbarProperties()197 nsresult TaskbarWindowPreview::UpdateTaskbarProperties() {
198 if (mHaveButtons) {
199 if (FAILED(mTaskbar->ThumbBarAddButtons(
200 mWnd, nsITaskbarWindowPreview::NUM_TOOLBAR_BUTTONS, mThumbButtons)))
201 return NS_ERROR_FAILURE;
202 }
203 nsresult rv = UpdateTaskbarProgress();
204 NS_ENSURE_SUCCESS(rv, rv);
205 rv = UpdateOverlayIcon();
206 NS_ENSURE_SUCCESS(rv, rv);
207 return TaskbarPreview::UpdateTaskbarProperties();
208 }
209
UpdateTaskbarProgress()210 nsresult TaskbarWindowPreview::UpdateTaskbarProgress() {
211 HRESULT hr = mTaskbar->SetProgressState(mWnd, mState);
212 if (SUCCEEDED(hr) && mState != TBPF_NOPROGRESS &&
213 mState != TBPF_INDETERMINATE)
214 hr = mTaskbar->SetProgressValue(mWnd, mCurrentValue, mMaxValue);
215
216 return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE;
217 }
218
UpdateOverlayIcon()219 nsresult TaskbarWindowPreview::UpdateOverlayIcon() {
220 HRESULT hr =
221 mTaskbar->SetOverlayIcon(mWnd, mOverlayIcon, mIconDescription.get());
222 return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE;
223 }
224
225 LRESULT
WndProc(UINT nMsg,WPARAM wParam,LPARAM lParam)226 TaskbarWindowPreview::WndProc(UINT nMsg, WPARAM wParam, LPARAM lParam) {
227 RefPtr<TaskbarWindowPreview> kungFuDeathGrip(this);
228 switch (nMsg) {
229 case WM_COMMAND: {
230 uint32_t id = LOWORD(wParam);
231 uint32_t index = id;
232 nsCOMPtr<nsITaskbarPreviewButton> button;
233 nsresult rv = GetButton(index, getter_AddRefs(button));
234 if (NS_SUCCEEDED(rv)) mController->OnClick(button);
235 }
236 return 0;
237 }
238 return TaskbarPreview::WndProc(nMsg, wParam, lParam);
239 }
240
241 /* static */
TaskbarWindowHook(void * aContext,HWND hWnd,UINT nMsg,WPARAM wParam,LPARAM lParam,LRESULT * aResult)242 bool TaskbarWindowPreview::TaskbarWindowHook(void *aContext, HWND hWnd,
243 UINT nMsg, WPARAM wParam,
244 LPARAM lParam, LRESULT *aResult) {
245 NS_ASSERTION(nMsg == nsAppShell::GetTaskbarButtonCreatedMessage(),
246 "Window hook proc called with wrong message");
247 TaskbarWindowPreview *preview =
248 reinterpret_cast<TaskbarWindowPreview *>(aContext);
249 // Now we can make all the calls to mTaskbar
250 preview->UpdateTaskbarProperties();
251 return false;
252 }
253
Enable()254 nsresult TaskbarWindowPreview::Enable() {
255 nsresult rv = TaskbarPreview::Enable();
256 NS_ENSURE_SUCCESS(rv, rv);
257
258 return FAILED(mTaskbar->AddTab(mWnd)) ? NS_ERROR_FAILURE : NS_OK;
259 }
260
Disable()261 nsresult TaskbarWindowPreview::Disable() {
262 nsresult rv = TaskbarPreview::Disable();
263 NS_ENSURE_SUCCESS(rv, rv);
264
265 return FAILED(mTaskbar->DeleteTab(mWnd)) ? NS_ERROR_FAILURE : NS_OK;
266 }
267
DetachFromNSWindow()268 void TaskbarWindowPreview::DetachFromNSWindow() {
269 // Remove the hooks we have for drawing
270 SetEnableCustomDrawing(false);
271
272 WindowHook &hook = GetWindowHook();
273 (void)hook.RemoveHook(WM_COMMAND, WindowHookProc, this);
274 (void)hook.RemoveMonitor(nsAppShell::GetTaskbarButtonCreatedMessage(),
275 TaskbarWindowHook, this);
276
277 TaskbarPreview::DetachFromNSWindow();
278 }
279
UpdateButtons()280 nsresult TaskbarWindowPreview::UpdateButtons() {
281 NS_ASSERTION(mVisible, "UpdateButtons called on invisible preview");
282
283 if (FAILED(mTaskbar->ThumbBarUpdateButtons(
284 mWnd, nsITaskbarWindowPreview::NUM_TOOLBAR_BUTTONS, mThumbButtons)))
285 return NS_ERROR_FAILURE;
286 return NS_OK;
287 }
288
UpdateButton(uint32_t index)289 nsresult TaskbarWindowPreview::UpdateButton(uint32_t index) {
290 if (index >= nsITaskbarWindowPreview::NUM_TOOLBAR_BUTTONS)
291 return NS_ERROR_INVALID_ARG;
292 if (mVisible) {
293 if (FAILED(mTaskbar->ThumbBarUpdateButtons(mWnd, 1, &mThumbButtons[index])))
294 return NS_ERROR_FAILURE;
295 }
296 return NS_OK;
297 }
298
299 } // namespace widget
300 } // namespace mozilla
301