michael@0: /* vim: se cin sw=2 ts=2 et : */ michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/ArrayUtils.h" michael@0: michael@0: #include michael@0: #include "TaskbarWindowPreview.h" michael@0: #include "WindowHook.h" michael@0: #include "nsUXThemeData.h" michael@0: #include "TaskbarPreviewButton.h" michael@0: #include "nsWindow.h" michael@0: #include "nsWindowGfx.h" michael@0: michael@0: namespace mozilla { michael@0: namespace widget { michael@0: michael@0: namespace { michael@0: bool WindowHookProc(void *aContext, HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam, LRESULT *aResult) michael@0: { michael@0: TaskbarWindowPreview *preview = reinterpret_cast(aContext); michael@0: *aResult = preview->WndProc(nMsg, wParam, lParam); michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(TaskbarWindowPreview, nsITaskbarWindowPreview, michael@0: nsITaskbarProgress, nsITaskbarOverlayIconController, michael@0: nsISupportsWeakReference) michael@0: michael@0: /** michael@0: * These correspond directly to the states defined in nsITaskbarProgress.idl, so michael@0: * they should be kept in sync. michael@0: */ michael@0: static TBPFLAG sNativeStates[] = michael@0: { michael@0: TBPF_NOPROGRESS, michael@0: TBPF_INDETERMINATE, michael@0: TBPF_NORMAL, michael@0: TBPF_ERROR, michael@0: TBPF_PAUSED michael@0: }; michael@0: michael@0: TaskbarWindowPreview::TaskbarWindowPreview(ITaskbarList4 *aTaskbar, nsITaskbarPreviewController *aController, HWND aHWND, nsIDocShell *aShell) michael@0: : TaskbarPreview(aTaskbar, aController, aHWND, aShell), michael@0: mCustomDrawing(false), michael@0: mHaveButtons(false), michael@0: mState(TBPF_NOPROGRESS), michael@0: mCurrentValue(0), michael@0: mMaxValue(0), michael@0: mOverlayIcon(nullptr) michael@0: { michael@0: // Window previews are visible by default michael@0: (void) SetVisible(true); michael@0: michael@0: memset(mThumbButtons, 0, sizeof mThumbButtons); michael@0: for (int32_t i = 0; i < nsITaskbarWindowPreview::NUM_TOOLBAR_BUTTONS; i++) { michael@0: mThumbButtons[i].dwMask = THB_FLAGS | THB_ICON | THB_TOOLTIP; michael@0: mThumbButtons[i].iId = i; michael@0: mThumbButtons[i].dwFlags = THBF_HIDDEN; michael@0: } michael@0: michael@0: WindowHook &hook = GetWindowHook(); michael@0: if (!CanMakeTaskbarCalls()) michael@0: hook.AddMonitor(nsAppShell::GetTaskbarButtonCreatedMessage(), michael@0: TaskbarWindowHook, this); michael@0: } michael@0: michael@0: TaskbarWindowPreview::~TaskbarWindowPreview() { michael@0: if (mOverlayIcon) { michael@0: ::DestroyIcon(mOverlayIcon); michael@0: mOverlayIcon = nullptr; michael@0: } michael@0: michael@0: if (IsWindowAvailable()) { michael@0: DetachFromNSWindow(); michael@0: } else { michael@0: mWnd = nullptr; michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: TaskbarWindowPreview::ShowActive(bool active) { michael@0: return FAILED(mTaskbar->ActivateTab(active ? mWnd : nullptr)) michael@0: ? NS_ERROR_FAILURE michael@0: : NS_OK; michael@0: michael@0: } michael@0: michael@0: HWND & michael@0: TaskbarWindowPreview::PreviewWindow() { michael@0: return mWnd; michael@0: } michael@0: michael@0: nsresult michael@0: TaskbarWindowPreview::GetButton(uint32_t index, nsITaskbarPreviewButton **_retVal) { michael@0: if (index >= nsITaskbarWindowPreview::NUM_TOOLBAR_BUTTONS) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: nsCOMPtr button(do_QueryReferent(mWeakButtons[index])); michael@0: michael@0: if (!button) { michael@0: // Lost reference michael@0: button = new TaskbarPreviewButton(this, index); michael@0: if (!button) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: mWeakButtons[index] = do_GetWeakReference(button); michael@0: } michael@0: michael@0: if (!mHaveButtons) { michael@0: mHaveButtons = true; michael@0: michael@0: WindowHook &hook = GetWindowHook(); michael@0: (void) hook.AddHook(WM_COMMAND, WindowHookProc, this); michael@0: michael@0: if (mVisible && FAILED(mTaskbar->ThumbBarAddButtons(mWnd, nsITaskbarWindowPreview::NUM_TOOLBAR_BUTTONS, mThumbButtons))) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: button.forget(_retVal); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: TaskbarWindowPreview::SetEnableCustomDrawing(bool aEnable) { michael@0: if (aEnable == mCustomDrawing) michael@0: return NS_OK; michael@0: mCustomDrawing = aEnable; michael@0: TaskbarPreview::EnableCustomDrawing(mWnd, aEnable); michael@0: michael@0: WindowHook &hook = GetWindowHook(); michael@0: if (aEnable) { michael@0: (void) hook.AddHook(WM_DWMSENDICONICTHUMBNAIL, WindowHookProc, this); michael@0: (void) hook.AddHook(WM_DWMSENDICONICLIVEPREVIEWBITMAP, WindowHookProc, this); michael@0: } else { michael@0: (void) hook.RemoveHook(WM_DWMSENDICONICLIVEPREVIEWBITMAP, WindowHookProc, this); michael@0: (void) hook.RemoveHook(WM_DWMSENDICONICTHUMBNAIL, WindowHookProc, this); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: TaskbarWindowPreview::GetEnableCustomDrawing(bool *aEnable) { michael@0: *aEnable = mCustomDrawing; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: TaskbarWindowPreview::SetProgressState(nsTaskbarProgressState aState, michael@0: uint64_t aCurrentValue, michael@0: uint64_t aMaxValue) michael@0: { michael@0: NS_ENSURE_ARG_RANGE(aState, michael@0: nsTaskbarProgressState(0), michael@0: nsTaskbarProgressState(ArrayLength(sNativeStates) - 1)); michael@0: michael@0: TBPFLAG nativeState = sNativeStates[aState]; michael@0: if (nativeState == TBPF_NOPROGRESS || nativeState == TBPF_INDETERMINATE) { michael@0: NS_ENSURE_TRUE(aCurrentValue == 0, NS_ERROR_INVALID_ARG); michael@0: NS_ENSURE_TRUE(aMaxValue == 0, NS_ERROR_INVALID_ARG); michael@0: } michael@0: michael@0: if (aCurrentValue > aMaxValue) michael@0: return NS_ERROR_ILLEGAL_VALUE; michael@0: michael@0: mState = nativeState; michael@0: mCurrentValue = aCurrentValue; michael@0: mMaxValue = aMaxValue; michael@0: michael@0: // Only update if we can michael@0: return CanMakeTaskbarCalls() ? UpdateTaskbarProgress() : NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: TaskbarWindowPreview::SetOverlayIcon(imgIContainer* aStatusIcon, michael@0: const nsAString& aStatusDescription) { michael@0: nsresult rv; michael@0: if (aStatusIcon) { michael@0: // The image shouldn't be animated michael@0: bool isAnimated; michael@0: rv = aStatusIcon->GetAnimated(&isAnimated); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: NS_ENSURE_FALSE(isAnimated, NS_ERROR_INVALID_ARG); michael@0: } michael@0: michael@0: HICON hIcon = nullptr; michael@0: if (aStatusIcon) { michael@0: rv = nsWindowGfx::CreateIcon(aStatusIcon, false, 0, 0, michael@0: nsWindowGfx::GetIconMetrics(nsWindowGfx::kSmallIcon), michael@0: &hIcon); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: if (mOverlayIcon) michael@0: ::DestroyIcon(mOverlayIcon); michael@0: mOverlayIcon = hIcon; michael@0: mIconDescription = aStatusDescription; michael@0: michael@0: // Only update if we can michael@0: return CanMakeTaskbarCalls() ? UpdateOverlayIcon() : NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: TaskbarWindowPreview::UpdateTaskbarProperties() { michael@0: if (mHaveButtons) { michael@0: if (FAILED(mTaskbar->ThumbBarAddButtons(mWnd, nsITaskbarWindowPreview::NUM_TOOLBAR_BUTTONS, mThumbButtons))) michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: nsresult rv = UpdateTaskbarProgress(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = UpdateOverlayIcon(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: return TaskbarPreview::UpdateTaskbarProperties(); michael@0: } michael@0: michael@0: nsresult michael@0: TaskbarWindowPreview::UpdateTaskbarProgress() { michael@0: HRESULT hr = mTaskbar->SetProgressState(mWnd, mState); michael@0: if (SUCCEEDED(hr) && mState != TBPF_NOPROGRESS && michael@0: mState != TBPF_INDETERMINATE) michael@0: hr = mTaskbar->SetProgressValue(mWnd, mCurrentValue, mMaxValue); michael@0: michael@0: return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsresult michael@0: TaskbarWindowPreview::UpdateOverlayIcon() { michael@0: HRESULT hr = mTaskbar->SetOverlayIcon(mWnd, mOverlayIcon, michael@0: mIconDescription.get()); michael@0: return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: LRESULT michael@0: TaskbarWindowPreview::WndProc(UINT nMsg, WPARAM wParam, LPARAM lParam) { michael@0: nsRefPtr kungFuDeathGrip(this); michael@0: switch (nMsg) { michael@0: case WM_COMMAND: michael@0: { michael@0: uint32_t id = LOWORD(wParam); michael@0: uint32_t index = id; michael@0: nsCOMPtr button; michael@0: nsresult rv = GetButton(index, getter_AddRefs(button)); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mController->OnClick(button); michael@0: } michael@0: return 0; michael@0: } michael@0: return TaskbarPreview::WndProc(nMsg, wParam, lParam); michael@0: } michael@0: michael@0: /* static */ michael@0: bool michael@0: TaskbarWindowPreview::TaskbarWindowHook(void *aContext, michael@0: HWND hWnd, UINT nMsg, michael@0: WPARAM wParam, LPARAM lParam, michael@0: LRESULT *aResult) michael@0: { michael@0: NS_ASSERTION(nMsg == nsAppShell::GetTaskbarButtonCreatedMessage(), michael@0: "Window hook proc called with wrong message"); michael@0: TaskbarWindowPreview *preview = michael@0: reinterpret_cast(aContext); michael@0: // Now we can make all the calls to mTaskbar michael@0: preview->UpdateTaskbarProperties(); michael@0: return false; michael@0: } michael@0: michael@0: nsresult michael@0: TaskbarWindowPreview::Enable() { michael@0: nsresult rv = TaskbarPreview::Enable(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return FAILED(mTaskbar->AddTab(mWnd)) michael@0: ? NS_ERROR_FAILURE michael@0: : NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: TaskbarWindowPreview::Disable() { michael@0: nsresult rv = TaskbarPreview::Disable(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return FAILED(mTaskbar->DeleteTab(mWnd)) michael@0: ? NS_ERROR_FAILURE michael@0: : NS_OK; michael@0: } michael@0: michael@0: void michael@0: TaskbarWindowPreview::DetachFromNSWindow() { michael@0: // Remove the hooks we have for drawing michael@0: SetEnableCustomDrawing(false); michael@0: michael@0: WindowHook &hook = GetWindowHook(); michael@0: (void) hook.RemoveHook(WM_COMMAND, WindowHookProc, this); michael@0: (void) hook.RemoveMonitor(nsAppShell::GetTaskbarButtonCreatedMessage(), michael@0: TaskbarWindowHook, this); michael@0: michael@0: TaskbarPreview::DetachFromNSWindow(); michael@0: } michael@0: michael@0: nsresult michael@0: TaskbarWindowPreview::UpdateButtons() { michael@0: NS_ASSERTION(mVisible, "UpdateButtons called on invisible preview"); michael@0: michael@0: if (FAILED(mTaskbar->ThumbBarUpdateButtons(mWnd, nsITaskbarWindowPreview::NUM_TOOLBAR_BUTTONS, mThumbButtons))) michael@0: return NS_ERROR_FAILURE; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: TaskbarWindowPreview::UpdateButton(uint32_t index) { michael@0: if (index >= nsITaskbarWindowPreview::NUM_TOOLBAR_BUTTONS) michael@0: return NS_ERROR_INVALID_ARG; michael@0: if (mVisible) { michael@0: if (FAILED(mTaskbar->ThumbBarUpdateButtons(mWnd, 1, &mThumbButtons[index]))) michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: } // namespace widget michael@0: } // namespace mozilla michael@0: