1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/widget/windows/TaskbarWindowPreview.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,326 @@ 1.4 +/* vim: se cin sw=2 ts=2 et : */ 1.5 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.6 + * 1.7 + * This Source Code Form is subject to the terms of the Mozilla Public 1.8 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.9 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.10 + 1.11 +#include "mozilla/ArrayUtils.h" 1.12 + 1.13 +#include <nsITaskbarPreviewController.h> 1.14 +#include "TaskbarWindowPreview.h" 1.15 +#include "WindowHook.h" 1.16 +#include "nsUXThemeData.h" 1.17 +#include "TaskbarPreviewButton.h" 1.18 +#include "nsWindow.h" 1.19 +#include "nsWindowGfx.h" 1.20 + 1.21 +namespace mozilla { 1.22 +namespace widget { 1.23 + 1.24 +namespace { 1.25 +bool WindowHookProc(void *aContext, HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam, LRESULT *aResult) 1.26 +{ 1.27 + TaskbarWindowPreview *preview = reinterpret_cast<TaskbarWindowPreview*>(aContext); 1.28 + *aResult = preview->WndProc(nMsg, wParam, lParam); 1.29 + return true; 1.30 +} 1.31 +} 1.32 + 1.33 +NS_IMPL_ISUPPORTS(TaskbarWindowPreview, nsITaskbarWindowPreview, 1.34 + nsITaskbarProgress, nsITaskbarOverlayIconController, 1.35 + nsISupportsWeakReference) 1.36 + 1.37 +/** 1.38 + * These correspond directly to the states defined in nsITaskbarProgress.idl, so 1.39 + * they should be kept in sync. 1.40 + */ 1.41 +static TBPFLAG sNativeStates[] = 1.42 +{ 1.43 + TBPF_NOPROGRESS, 1.44 + TBPF_INDETERMINATE, 1.45 + TBPF_NORMAL, 1.46 + TBPF_ERROR, 1.47 + TBPF_PAUSED 1.48 +}; 1.49 + 1.50 +TaskbarWindowPreview::TaskbarWindowPreview(ITaskbarList4 *aTaskbar, nsITaskbarPreviewController *aController, HWND aHWND, nsIDocShell *aShell) 1.51 + : TaskbarPreview(aTaskbar, aController, aHWND, aShell), 1.52 + mCustomDrawing(false), 1.53 + mHaveButtons(false), 1.54 + mState(TBPF_NOPROGRESS), 1.55 + mCurrentValue(0), 1.56 + mMaxValue(0), 1.57 + mOverlayIcon(nullptr) 1.58 +{ 1.59 + // Window previews are visible by default 1.60 + (void) SetVisible(true); 1.61 + 1.62 + memset(mThumbButtons, 0, sizeof mThumbButtons); 1.63 + for (int32_t i = 0; i < nsITaskbarWindowPreview::NUM_TOOLBAR_BUTTONS; i++) { 1.64 + mThumbButtons[i].dwMask = THB_FLAGS | THB_ICON | THB_TOOLTIP; 1.65 + mThumbButtons[i].iId = i; 1.66 + mThumbButtons[i].dwFlags = THBF_HIDDEN; 1.67 + } 1.68 + 1.69 + WindowHook &hook = GetWindowHook(); 1.70 + if (!CanMakeTaskbarCalls()) 1.71 + hook.AddMonitor(nsAppShell::GetTaskbarButtonCreatedMessage(), 1.72 + TaskbarWindowHook, this); 1.73 +} 1.74 + 1.75 +TaskbarWindowPreview::~TaskbarWindowPreview() { 1.76 + if (mOverlayIcon) { 1.77 + ::DestroyIcon(mOverlayIcon); 1.78 + mOverlayIcon = nullptr; 1.79 + } 1.80 + 1.81 + if (IsWindowAvailable()) { 1.82 + DetachFromNSWindow(); 1.83 + } else { 1.84 + mWnd = nullptr; 1.85 + } 1.86 +} 1.87 + 1.88 +nsresult 1.89 +TaskbarWindowPreview::ShowActive(bool active) { 1.90 + return FAILED(mTaskbar->ActivateTab(active ? mWnd : nullptr)) 1.91 + ? NS_ERROR_FAILURE 1.92 + : NS_OK; 1.93 + 1.94 +} 1.95 + 1.96 +HWND & 1.97 +TaskbarWindowPreview::PreviewWindow() { 1.98 + return mWnd; 1.99 +} 1.100 + 1.101 +nsresult 1.102 +TaskbarWindowPreview::GetButton(uint32_t index, nsITaskbarPreviewButton **_retVal) { 1.103 + if (index >= nsITaskbarWindowPreview::NUM_TOOLBAR_BUTTONS) 1.104 + return NS_ERROR_INVALID_ARG; 1.105 + 1.106 + nsCOMPtr<nsITaskbarPreviewButton> button(do_QueryReferent(mWeakButtons[index])); 1.107 + 1.108 + if (!button) { 1.109 + // Lost reference 1.110 + button = new TaskbarPreviewButton(this, index); 1.111 + if (!button) { 1.112 + return NS_ERROR_OUT_OF_MEMORY; 1.113 + } 1.114 + mWeakButtons[index] = do_GetWeakReference(button); 1.115 + } 1.116 + 1.117 + if (!mHaveButtons) { 1.118 + mHaveButtons = true; 1.119 + 1.120 + WindowHook &hook = GetWindowHook(); 1.121 + (void) hook.AddHook(WM_COMMAND, WindowHookProc, this); 1.122 + 1.123 + if (mVisible && FAILED(mTaskbar->ThumbBarAddButtons(mWnd, nsITaskbarWindowPreview::NUM_TOOLBAR_BUTTONS, mThumbButtons))) { 1.124 + return NS_ERROR_FAILURE; 1.125 + } 1.126 + } 1.127 + button.forget(_retVal); 1.128 + return NS_OK; 1.129 +} 1.130 + 1.131 +NS_IMETHODIMP 1.132 +TaskbarWindowPreview::SetEnableCustomDrawing(bool aEnable) { 1.133 + if (aEnable == mCustomDrawing) 1.134 + return NS_OK; 1.135 + mCustomDrawing = aEnable; 1.136 + TaskbarPreview::EnableCustomDrawing(mWnd, aEnable); 1.137 + 1.138 + WindowHook &hook = GetWindowHook(); 1.139 + if (aEnable) { 1.140 + (void) hook.AddHook(WM_DWMSENDICONICTHUMBNAIL, WindowHookProc, this); 1.141 + (void) hook.AddHook(WM_DWMSENDICONICLIVEPREVIEWBITMAP, WindowHookProc, this); 1.142 + } else { 1.143 + (void) hook.RemoveHook(WM_DWMSENDICONICLIVEPREVIEWBITMAP, WindowHookProc, this); 1.144 + (void) hook.RemoveHook(WM_DWMSENDICONICTHUMBNAIL, WindowHookProc, this); 1.145 + } 1.146 + return NS_OK; 1.147 +} 1.148 + 1.149 +NS_IMETHODIMP 1.150 +TaskbarWindowPreview::GetEnableCustomDrawing(bool *aEnable) { 1.151 + *aEnable = mCustomDrawing; 1.152 + return NS_OK; 1.153 +} 1.154 + 1.155 +NS_IMETHODIMP 1.156 +TaskbarWindowPreview::SetProgressState(nsTaskbarProgressState aState, 1.157 + uint64_t aCurrentValue, 1.158 + uint64_t aMaxValue) 1.159 +{ 1.160 + NS_ENSURE_ARG_RANGE(aState, 1.161 + nsTaskbarProgressState(0), 1.162 + nsTaskbarProgressState(ArrayLength(sNativeStates) - 1)); 1.163 + 1.164 + TBPFLAG nativeState = sNativeStates[aState]; 1.165 + if (nativeState == TBPF_NOPROGRESS || nativeState == TBPF_INDETERMINATE) { 1.166 + NS_ENSURE_TRUE(aCurrentValue == 0, NS_ERROR_INVALID_ARG); 1.167 + NS_ENSURE_TRUE(aMaxValue == 0, NS_ERROR_INVALID_ARG); 1.168 + } 1.169 + 1.170 + if (aCurrentValue > aMaxValue) 1.171 + return NS_ERROR_ILLEGAL_VALUE; 1.172 + 1.173 + mState = nativeState; 1.174 + mCurrentValue = aCurrentValue; 1.175 + mMaxValue = aMaxValue; 1.176 + 1.177 + // Only update if we can 1.178 + return CanMakeTaskbarCalls() ? UpdateTaskbarProgress() : NS_OK; 1.179 +} 1.180 + 1.181 +NS_IMETHODIMP 1.182 +TaskbarWindowPreview::SetOverlayIcon(imgIContainer* aStatusIcon, 1.183 + const nsAString& aStatusDescription) { 1.184 + nsresult rv; 1.185 + if (aStatusIcon) { 1.186 + // The image shouldn't be animated 1.187 + bool isAnimated; 1.188 + rv = aStatusIcon->GetAnimated(&isAnimated); 1.189 + NS_ENSURE_SUCCESS(rv, rv); 1.190 + NS_ENSURE_FALSE(isAnimated, NS_ERROR_INVALID_ARG); 1.191 + } 1.192 + 1.193 + HICON hIcon = nullptr; 1.194 + if (aStatusIcon) { 1.195 + rv = nsWindowGfx::CreateIcon(aStatusIcon, false, 0, 0, 1.196 + nsWindowGfx::GetIconMetrics(nsWindowGfx::kSmallIcon), 1.197 + &hIcon); 1.198 + NS_ENSURE_SUCCESS(rv, rv); 1.199 + } 1.200 + 1.201 + if (mOverlayIcon) 1.202 + ::DestroyIcon(mOverlayIcon); 1.203 + mOverlayIcon = hIcon; 1.204 + mIconDescription = aStatusDescription; 1.205 + 1.206 + // Only update if we can 1.207 + return CanMakeTaskbarCalls() ? UpdateOverlayIcon() : NS_OK; 1.208 +} 1.209 + 1.210 +nsresult 1.211 +TaskbarWindowPreview::UpdateTaskbarProperties() { 1.212 + if (mHaveButtons) { 1.213 + if (FAILED(mTaskbar->ThumbBarAddButtons(mWnd, nsITaskbarWindowPreview::NUM_TOOLBAR_BUTTONS, mThumbButtons))) 1.214 + return NS_ERROR_FAILURE; 1.215 + } 1.216 + nsresult rv = UpdateTaskbarProgress(); 1.217 + NS_ENSURE_SUCCESS(rv, rv); 1.218 + rv = UpdateOverlayIcon(); 1.219 + NS_ENSURE_SUCCESS(rv, rv); 1.220 + return TaskbarPreview::UpdateTaskbarProperties(); 1.221 +} 1.222 + 1.223 +nsresult 1.224 +TaskbarWindowPreview::UpdateTaskbarProgress() { 1.225 + HRESULT hr = mTaskbar->SetProgressState(mWnd, mState); 1.226 + if (SUCCEEDED(hr) && mState != TBPF_NOPROGRESS && 1.227 + mState != TBPF_INDETERMINATE) 1.228 + hr = mTaskbar->SetProgressValue(mWnd, mCurrentValue, mMaxValue); 1.229 + 1.230 + return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE; 1.231 +} 1.232 + 1.233 +nsresult 1.234 +TaskbarWindowPreview::UpdateOverlayIcon() { 1.235 + HRESULT hr = mTaskbar->SetOverlayIcon(mWnd, mOverlayIcon, 1.236 + mIconDescription.get()); 1.237 + return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE; 1.238 +} 1.239 + 1.240 +LRESULT 1.241 +TaskbarWindowPreview::WndProc(UINT nMsg, WPARAM wParam, LPARAM lParam) { 1.242 + nsRefPtr<TaskbarWindowPreview> kungFuDeathGrip(this); 1.243 + switch (nMsg) { 1.244 + case WM_COMMAND: 1.245 + { 1.246 + uint32_t id = LOWORD(wParam); 1.247 + uint32_t index = id; 1.248 + nsCOMPtr<nsITaskbarPreviewButton> button; 1.249 + nsresult rv = GetButton(index, getter_AddRefs(button)); 1.250 + if (NS_SUCCEEDED(rv)) 1.251 + mController->OnClick(button); 1.252 + } 1.253 + return 0; 1.254 + } 1.255 + return TaskbarPreview::WndProc(nMsg, wParam, lParam); 1.256 +} 1.257 + 1.258 +/* static */ 1.259 +bool 1.260 +TaskbarWindowPreview::TaskbarWindowHook(void *aContext, 1.261 + HWND hWnd, UINT nMsg, 1.262 + WPARAM wParam, LPARAM lParam, 1.263 + LRESULT *aResult) 1.264 +{ 1.265 + NS_ASSERTION(nMsg == nsAppShell::GetTaskbarButtonCreatedMessage(), 1.266 + "Window hook proc called with wrong message"); 1.267 + TaskbarWindowPreview *preview = 1.268 + reinterpret_cast<TaskbarWindowPreview*>(aContext); 1.269 + // Now we can make all the calls to mTaskbar 1.270 + preview->UpdateTaskbarProperties(); 1.271 + return false; 1.272 +} 1.273 + 1.274 +nsresult 1.275 +TaskbarWindowPreview::Enable() { 1.276 + nsresult rv = TaskbarPreview::Enable(); 1.277 + NS_ENSURE_SUCCESS(rv, rv); 1.278 + 1.279 + return FAILED(mTaskbar->AddTab(mWnd)) 1.280 + ? NS_ERROR_FAILURE 1.281 + : NS_OK; 1.282 +} 1.283 + 1.284 +nsresult 1.285 +TaskbarWindowPreview::Disable() { 1.286 + nsresult rv = TaskbarPreview::Disable(); 1.287 + NS_ENSURE_SUCCESS(rv, rv); 1.288 + 1.289 + return FAILED(mTaskbar->DeleteTab(mWnd)) 1.290 + ? NS_ERROR_FAILURE 1.291 + : NS_OK; 1.292 +} 1.293 + 1.294 +void 1.295 +TaskbarWindowPreview::DetachFromNSWindow() { 1.296 + // Remove the hooks we have for drawing 1.297 + SetEnableCustomDrawing(false); 1.298 + 1.299 + WindowHook &hook = GetWindowHook(); 1.300 + (void) hook.RemoveHook(WM_COMMAND, WindowHookProc, this); 1.301 + (void) hook.RemoveMonitor(nsAppShell::GetTaskbarButtonCreatedMessage(), 1.302 + TaskbarWindowHook, this); 1.303 + 1.304 + TaskbarPreview::DetachFromNSWindow(); 1.305 +} 1.306 + 1.307 +nsresult 1.308 +TaskbarWindowPreview::UpdateButtons() { 1.309 + NS_ASSERTION(mVisible, "UpdateButtons called on invisible preview"); 1.310 + 1.311 + if (FAILED(mTaskbar->ThumbBarUpdateButtons(mWnd, nsITaskbarWindowPreview::NUM_TOOLBAR_BUTTONS, mThumbButtons))) 1.312 + return NS_ERROR_FAILURE; 1.313 + return NS_OK; 1.314 +} 1.315 + 1.316 +nsresult 1.317 +TaskbarWindowPreview::UpdateButton(uint32_t index) { 1.318 + if (index >= nsITaskbarWindowPreview::NUM_TOOLBAR_BUTTONS) 1.319 + return NS_ERROR_INVALID_ARG; 1.320 + if (mVisible) { 1.321 + if (FAILED(mTaskbar->ThumbBarUpdateButtons(mWnd, 1, &mThumbButtons[index]))) 1.322 + return NS_ERROR_FAILURE; 1.323 + } 1.324 + return NS_OK; 1.325 +} 1.326 + 1.327 +} // namespace widget 1.328 +} // namespace mozilla 1.329 +