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