From 79c05b8d5ca3f7834f5467cad3e114b24b021f1e Mon Sep 17 00:00:00 2001 From: Siddharth Agarwal Date: Thu, 29 Sep 2011 00:06:43 +0530 Subject: [PATCH] Bug 515907 - Support taskbar icon overlays in Windows 7. Original patch by Tim Miller . r=jimm, sr=roc --- widget/public/Makefile.in | 1 + .../nsITaskbarOverlayIconController.idl | 72 +++++++++++++++++++ widget/public/nsIWinTaskbar.idl | 14 +++- widget/src/windows/TaskbarPreviewButton.cpp | 4 +- widget/src/windows/TaskbarTabPreview.cpp | 4 +- widget/src/windows/TaskbarWindowPreview.cpp | 66 ++++++++++++++--- widget/src/windows/TaskbarWindowPreview.h | 18 +++-- widget/src/windows/WinTaskbar.cpp | 10 +++ widget/src/windows/nsWindow.cpp | 4 +- widget/src/windows/nsWindowGfx.cpp | 60 +++++++++++++++- widget/src/windows/nsWindowGfx.h | 7 +- 11 files changed, 238 insertions(+), 22 deletions(-) create mode 100644 widget/public/nsITaskbarOverlayIconController.idl diff --git a/widget/public/Makefile.in b/widget/public/Makefile.in index bafb5893adf0..33756e3f2593 100644 --- a/widget/public/Makefile.in +++ b/widget/public/Makefile.in @@ -140,6 +140,7 @@ XPIDLSRCS += nsIPrintSettingsWin.idl \ nsITaskbarPreviewButton.idl \ nsITaskbarProgress.idl \ nsITaskbarPreviewButton.idl \ + nsITaskbarOverlayIconController.idl \ nsIJumpListBuilder.idl \ nsIJumpListItem.idl \ $(NULL) diff --git a/widget/public/nsITaskbarOverlayIconController.idl b/widget/public/nsITaskbarOverlayIconController.idl new file mode 100644 index 000000000000..59fc8a21df42 --- /dev/null +++ b/widget/public/nsITaskbarOverlayIconController.idl @@ -0,0 +1,72 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Tim Miller . + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Siddharth Agarwal + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsISupports.idl" + +interface imgIContainer; + +/** + * Starting in Windows 7, applications can display an overlay on the icon in + * the taskbar. This class wraps around the native functionality to do this. + */ +[scriptable, uuid(b1858889-a698-428a-a14b-b5d60cff6de2)] +interface nsITaskbarOverlayIconController : nsISupports +{ + /** + * Sets the overlay icon and its corresponding alt text. + * + * @param statusIcon The handle to the overlay icon. The icon will be scaled + * to the small icon size (16x16 at 96 dpi). Can be null, in + * which case if the taskbar button represents a single window + * the icon is removed. + * @param statusDescription The alt text version of the information + * conveyed by the overlay, for accessibility + * purposes. + * + * @note The behavior for window groups is managed by Windows. + * - If an overlay icon is set for any window in a window group and another + * overlay icon is already applied to the corresponding taskbar button, that + * existing overlay is replaced. + * - If null is passed in to replace the overlay currently being displayed, + * and if a previous overlay set for a different window in the group is + * still available, then that previous overlay is displayed. + */ + void setOverlayIcon(in imgIContainer statusIcon, + in AString statusDescription); +}; diff --git a/widget/public/nsIWinTaskbar.idl b/widget/public/nsIWinTaskbar.idl index ebebb188fd43..e0e88a5a8b63 100644 --- a/widget/public/nsIWinTaskbar.idl +++ b/widget/public/nsIWinTaskbar.idl @@ -48,6 +48,7 @@ interface nsITaskbarTabPreview; interface nsITaskbarWindowPreview; interface nsITaskbarPreviewController; interface nsITaskbarProgress; +interface nsITaskbarOverlayIconController; interface nsIJumpListBuilder; interface nsIDOMWindow; @@ -83,7 +84,7 @@ interface nsIDOMWindow; * See nsIJumpListBuilder for more information. */ -[scriptable, uuid(9fc572db-1089-4d43-9121-f4833d77a2df)] +[scriptable, uuid(3232f40a-e94b-432d-9496-096abf1387bd)] interface nsIWinTaskbar : nsISupports { /** @@ -132,6 +133,17 @@ interface nsIWinTaskbar : nsISupports */ nsITaskbarProgress getTaskbarProgress(in nsIDocShell shell); + /** + * Taskbar icon overlay + */ + + /** + * Gets the taskbar icon overlay controller for a window. The docshell is used + * to find the toplevel window. See the documentation in + * nsITaskbarOverlayIconController for more details. + */ + nsITaskbarOverlayIconController getOverlayIconController(in nsIDocShell shell); + /** * Taskbar and start menu jump list management */ diff --git a/widget/src/windows/TaskbarPreviewButton.cpp b/widget/src/windows/TaskbarPreviewButton.cpp index 645d1348a735..d23353f322bb 100644 --- a/widget/src/windows/TaskbarPreviewButton.cpp +++ b/widget/src/windows/TaskbarPreviewButton.cpp @@ -140,7 +140,9 @@ TaskbarPreviewButton::SetImage(imgIContainer *img) { ::DestroyIcon(Button().hIcon); if (img) { nsresult rv; - rv = nsWindowGfx::CreateIcon(img, PR_FALSE, 0, 0, &Button().hIcon); + rv = nsWindowGfx::CreateIcon(img, PR_FALSE, 0, 0, + nsWindowGfx::GetIconMetrics(nsWindowGfx::kRegularIcon), + &Button().hIcon); NS_ENSURE_SUCCESS(rv, rv); } else { Button().hIcon = NULL; diff --git a/widget/src/windows/TaskbarTabPreview.cpp b/widget/src/windows/TaskbarTabPreview.cpp index c7a3aa996504..1fb11d3c47d0 100644 --- a/widget/src/windows/TaskbarTabPreview.cpp +++ b/widget/src/windows/TaskbarTabPreview.cpp @@ -125,7 +125,9 @@ TaskbarTabPreview::SetIcon(imgIContainer *icon) { HICON hIcon = NULL; if (icon) { nsresult rv; - rv = nsWindowGfx::CreateIcon(icon, PR_FALSE, 0, 0, &hIcon); + rv = nsWindowGfx::CreateIcon(icon, PR_FALSE, 0, 0, + nsWindowGfx::GetIconMetrics(nsWindowGfx::kSmallIcon), + &hIcon); NS_ENSURE_SUCCESS(rv, rv); } diff --git a/widget/src/windows/TaskbarWindowPreview.cpp b/widget/src/windows/TaskbarWindowPreview.cpp index d31ab35182f8..0eae47455610 100644 --- a/widget/src/windows/TaskbarWindowPreview.cpp +++ b/widget/src/windows/TaskbarWindowPreview.cpp @@ -47,6 +47,7 @@ #include "nsUXThemeData.h" #include "TaskbarPreviewButton.h" #include "nsWindow.h" +#include "nsWindowGfx.h" namespace mozilla { namespace widget { @@ -60,8 +61,9 @@ PRBool WindowHookProc(void *aContext, HWND hWnd, UINT nMsg, WPARAM wParam, LPARA } } -NS_IMPL_ISUPPORTS3(TaskbarWindowPreview, nsITaskbarWindowPreview, - nsITaskbarProgress, nsISupportsWeakReference) +NS_IMPL_ISUPPORTS4(TaskbarWindowPreview, nsITaskbarWindowPreview, + nsITaskbarProgress, nsITaskbarOverlayIconController, + nsISupportsWeakReference) /** * These correspond directly to the states defined in nsITaskbarProgress.idl, so @@ -82,7 +84,8 @@ TaskbarWindowPreview::TaskbarWindowPreview(ITaskbarList4 *aTaskbar, nsITaskbarPr mHaveButtons(PR_FALSE), mState(TBPF_NOPROGRESS), mCurrentValue(0), - mMaxValue(0) + mMaxValue(0), + mOverlayIcon(NULL) { // Window previews are visible by default (void) SetVisible(PR_TRUE); @@ -97,10 +100,15 @@ TaskbarWindowPreview::TaskbarWindowPreview(ITaskbarList4 *aTaskbar, nsITaskbarPr WindowHook &hook = GetWindowHook(); if (!CanMakeTaskbarCalls()) hook.AddMonitor(nsAppShell::GetTaskbarButtonCreatedMessage(), - TaskbarProgressWindowHook, this); + TaskbarWindowHook, this); } TaskbarWindowPreview::~TaskbarWindowPreview() { + if (mOverlayIcon) { + ::DestroyIcon(mOverlayIcon); + mOverlayIcon = NULL; + } + if (IsWindowAvailable()) { DetachFromNSWindow(); } else { @@ -199,6 +207,35 @@ TaskbarWindowPreview::SetProgressState(nsTaskbarProgressState aState, return CanMakeTaskbarCalls() ? UpdateTaskbarProgress() : NS_OK; } +NS_IMETHODIMP +TaskbarWindowPreview::SetOverlayIcon(imgIContainer* aStatusIcon, + const nsAString& aStatusDescription) { + nsresult rv; + if (aStatusIcon) { + // The image shouldn't be animated + PRBool isAnimated; + rv = aStatusIcon->GetAnimated(&isAnimated); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_FALSE(isAnimated, NS_ERROR_INVALID_ARG); + } + + HICON hIcon = NULL; + if (aStatusIcon) { + rv = nsWindowGfx::CreateIcon(aStatusIcon, false, 0, 0, + nsWindowGfx::GetIconMetrics(nsWindowGfx::kSmallIcon), + &hIcon); + NS_ENSURE_SUCCESS(rv, rv); + } + + if (mOverlayIcon) + ::DestroyIcon(mOverlayIcon); + mOverlayIcon = hIcon; + mIconDescription = aStatusDescription; + + // Only update if we can + return CanMakeTaskbarCalls() ? UpdateOverlayIcon() : NS_OK; +} + nsresult TaskbarWindowPreview::UpdateTaskbarProperties() { if (mHaveButtons) { @@ -207,6 +244,8 @@ TaskbarWindowPreview::UpdateTaskbarProperties() { } nsresult rv = UpdateTaskbarProgress(); NS_ENSURE_SUCCESS(rv, rv); + rv = UpdateOverlayIcon(); + NS_ENSURE_SUCCESS(rv, rv); return TaskbarPreview::UpdateTaskbarProperties(); } @@ -220,6 +259,13 @@ TaskbarWindowPreview::UpdateTaskbarProgress() { return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE; } +nsresult +TaskbarWindowPreview::UpdateOverlayIcon() { + HRESULT hr = mTaskbar->SetOverlayIcon(mWnd, mOverlayIcon, + mIconDescription.get()); + return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE; +} + LRESULT TaskbarWindowPreview::WndProc(UINT nMsg, WPARAM wParam, LPARAM lParam) { nsRefPtr kungFuDeathGrip(this); @@ -240,17 +286,17 @@ TaskbarWindowPreview::WndProc(UINT nMsg, WPARAM wParam, LPARAM lParam) { /* static */ PRBool -TaskbarWindowPreview::TaskbarProgressWindowHook(void *aContext, - HWND hWnd, UINT nMsg, - WPARAM wParam, LPARAM lParam, - LRESULT *aResult) +TaskbarWindowPreview::TaskbarWindowHook(void *aContext, + HWND hWnd, UINT nMsg, + WPARAM wParam, LPARAM lParam, + LRESULT *aResult) { NS_ASSERTION(nMsg == nsAppShell::GetTaskbarButtonCreatedMessage(), "Window hook proc called with wrong message"); TaskbarWindowPreview *preview = reinterpret_cast(aContext); // Now we can make all the calls to mTaskbar - preview->UpdateTaskbarProgress(); + preview->UpdateTaskbarProperties(); return PR_FALSE; } @@ -282,7 +328,7 @@ TaskbarWindowPreview::DetachFromNSWindow() { WindowHook &hook = GetWindowHook(); (void) hook.RemoveHook(WM_COMMAND, WindowHookProc, this); (void) hook.RemoveMonitor(nsAppShell::GetTaskbarButtonCreatedMessage(), - TaskbarProgressWindowHook, this); + TaskbarWindowHook, this); TaskbarPreview::DetachFromNSWindow(); } diff --git a/widget/src/windows/TaskbarWindowPreview.h b/widget/src/windows/TaskbarWindowPreview.h index 7c0b3865ad1f..465ab876a320 100644 --- a/widget/src/windows/TaskbarWindowPreview.h +++ b/widget/src/windows/TaskbarWindowPreview.h @@ -46,6 +46,7 @@ #include "nsITaskbarWindowPreview.h" #include "nsITaskbarProgress.h" +#include "nsITaskbarOverlayIconController.h" #include "TaskbarPreview.h" #include @@ -56,6 +57,7 @@ class TaskbarPreviewButton; class TaskbarWindowPreview : public TaskbarPreview, public nsITaskbarWindowPreview, public nsITaskbarProgress, + public nsITaskbarOverlayIconController, public nsSupportsWeakReference { public: @@ -65,6 +67,7 @@ public: NS_DECL_ISUPPORTS NS_DECL_NSITASKBARWINDOWPREVIEW NS_DECL_NSITASKBARPROGRESS + NS_DECL_NSITASKBAROVERLAYICONCONTROLLER NS_FORWARD_NSITASKBARPREVIEW(TaskbarPreview::) virtual LRESULT WndProc(UINT nMsg, WPARAM wParam, LPARAM lParam); @@ -90,17 +93,22 @@ private: // Called to update ITaskbarList4 dependent properties nsresult UpdateTaskbarProgress(); + nsresult UpdateOverlayIcon(); // The taskbar progress TBPFLAG mState; ULONGLONG mCurrentValue; ULONGLONG mMaxValue; - // WindowHook procedure for hooking mWnd for taskbar progress stuff - static PRBool TaskbarProgressWindowHook(void *aContext, - HWND hWnd, UINT nMsg, - WPARAM wParam, LPARAM lParam, - LRESULT *aResult); + // Taskbar overlay icon + HICON mOverlayIcon; + nsString mIconDescription; + + // WindowHook procedure for hooking mWnd for taskbar progress and icon stuff + static PRBool TaskbarWindowHook(void *aContext, + HWND hWnd, UINT nMsg, + WPARAM wParam, LPARAM lParam, + LRESULT *aResult); friend class TaskbarPreviewButton; }; diff --git a/widget/src/windows/WinTaskbar.cpp b/widget/src/windows/WinTaskbar.cpp index eef10bb54521..7871e7d18cd3 100644 --- a/widget/src/windows/WinTaskbar.cpp +++ b/widget/src/windows/WinTaskbar.cpp @@ -416,6 +416,16 @@ WinTaskbar::GetTaskbarProgress(nsIDocShell *shell, nsITaskbarProgress **_retval) return CallQueryInterface(preview, _retval); } +NS_IMETHODIMP +WinTaskbar::GetOverlayIconController(nsIDocShell *shell, + nsITaskbarOverlayIconController **_retval) { + nsCOMPtr preview; + nsresult rv = GetTaskbarWindowPreview(shell, getter_AddRefs(preview)); + NS_ENSURE_SUCCESS(rv, rv); + + return CallQueryInterface(preview, _retval); +} + /* nsIJumpListBuilder createJumpListBuilder(); */ NS_IMETHODIMP WinTaskbar::CreateJumpListBuilder(nsIJumpListBuilder * *aJumpListBuilder) { diff --git a/widget/src/windows/nsWindow.cpp b/widget/src/windows/nsWindow.cpp index 7027ebfe9f56..c49d01bea512 100644 --- a/widget/src/windows/nsWindow.cpp +++ b/widget/src/windows/nsWindow.cpp @@ -2453,7 +2453,9 @@ NS_IMETHODIMP nsWindow::SetCursor(imgIContainer* aCursor, return NS_ERROR_NOT_AVAILABLE; HCURSOR cursor; - rv = nsWindowGfx::CreateIcon(aCursor, PR_TRUE, aHotspotX, aHotspotY, &cursor); + // No scaling + gfxIntSize size(0, 0); + rv = nsWindowGfx::CreateIcon(aCursor, PR_TRUE, aHotspotX, aHotspotY, size, &cursor); NS_ENSURE_SUCCESS(rv, rv); mCursor = nsCursor(-1); diff --git a/widget/src/windows/nsWindowGfx.cpp b/widget/src/windows/nsWindowGfx.cpp index 1dec926f8c8a..cc0301f04fb5 100644 --- a/widget/src/windows/nsWindowGfx.cpp +++ b/widget/src/windows/nsWindowGfx.cpp @@ -103,6 +103,18 @@ using namespace mozilla::layers; static nsAutoPtr sSharedSurfaceData; static gfxIntSize sSharedSurfaceSize; +struct IconMetrics { + PRInt32 xMetric; + PRInt32 yMetric; + PRInt32 defaultSize; +}; + +// Corresponds 1:1 to the IconSizeType enum +static IconMetrics sIconMetrics[] = { + {SM_CXSMICON, SM_CYSMICON, 16}, // small icon + {SM_CXICON, SM_CYICON, 32} // regular icon +}; + /************************************************************** ************************************************************** ** @@ -613,10 +625,22 @@ PRBool nsWindow::OnPaint(HDC aDC, PRUint32 aNestingLevel) return result; } +gfxIntSize nsWindowGfx::GetIconMetrics(IconSizeType aSizeType) { + PRInt32 width = ::GetSystemMetrics(sIconMetrics[aSizeType].xMetric); + PRInt32 height = ::GetSystemMetrics(sIconMetrics[aSizeType].yMetric); + + if (width == 0 || height == 0) { + width = height = sIconMetrics[aSizeType].defaultSize; + } + + return gfxIntSize(width, height); +} + nsresult nsWindowGfx::CreateIcon(imgIContainer *aContainer, PRBool aIsCursor, PRUint32 aHotspotX, PRUint32 aHotspotY, + gfxIntSize aScaledSize, HICON *aIcon) { // Get the image data @@ -627,10 +651,42 @@ nsresult nsWindowGfx::CreateIcon(imgIContainer *aContainer, if (!frame) return NS_ERROR_NOT_AVAILABLE; - PRUint8 *data = frame->Data(); - PRInt32 width = frame->Width(); PRInt32 height = frame->Height(); + if (!width || !height) + return NS_ERROR_FAILURE; + + PRUint8 *data; + if ((aScaledSize.width == 0 && aScaledSize.height == 0) || + (aScaledSize.width == width && aScaledSize.height == height)) { + // We're not scaling the image. The data is simply what's in the frame. + data = frame->Data(); + } + else { + NS_ENSURE_ARG(aScaledSize.width > 0); + NS_ENSURE_ARG(aScaledSize.height > 0); + // Draw a scaled version of the image to a temporary surface + nsRefPtr dest = new gfxImageSurface(aScaledSize, + gfxASurface::ImageFormatARGB32); + if (!dest) + return NS_ERROR_OUT_OF_MEMORY; + + gfxContext ctx(dest); + + // Set scaling + gfxFloat sw = (double) aScaledSize.width / width; + gfxFloat sh = (double) aScaledSize.height / height; + ctx.Scale(sw, sh); + + // Paint a scaled image + ctx.SetOperator(gfxContext::OPERATOR_SOURCE); + ctx.SetSource(frame); + ctx.Paint(); + + data = dest->Data(); + width = aScaledSize.width; + height = aScaledSize.height; + } HBITMAP bmp = DataToBitmap(data, width, -height, 32); PRUint8* a1data = Data32BitTo1Bit(data, width, height); diff --git a/widget/src/windows/nsWindowGfx.h b/widget/src/windows/nsWindowGfx.h index 2fb957b5a31d..896a89eb39f8 100644 --- a/widget/src/windows/nsWindowGfx.h +++ b/widget/src/windows/nsWindowGfx.h @@ -60,7 +60,12 @@ public: static nsIntRegion ConvertHRGNToRegion(HRGN aRgn); - static nsresult CreateIcon(imgIContainer *aContainer, PRBool aIsCursor, PRUint32 aHotspotX, PRUint32 aHotspotY, HICON *aIcon); + enum IconSizeType { + kSmallIcon, + kRegularIcon + }; + static gfxIntSize GetIconMetrics(IconSizeType aSizeType); + static nsresult CreateIcon(imgIContainer *aContainer, PRBool aIsCursor, PRUint32 aHotspotX, PRUint32 aHotspotY, gfxIntSize aScaledSize, HICON *aIcon); private: /**