/* vim: se cin sw=2 ts=2 et : */ /* -*- Mode: C++; 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 * Mozilla Foundation. * Portions created by the Initial Developer are Copyright (C) 2009 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Rob Arnold * * 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 ***** */ #if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_WIN7 #include "TaskbarPreview.h" #include #include #include #include #include #include #include #include #include "nsUXThemeData.h" #include "nsWindow.h" #include "nsAppShell.h" #include "TaskbarPreviewButton.h" #include #include #include #include #include // Defined in dwmapi in a header that needs a higher numbered _WINNT #define #define DWM_SIT_DISPLAYFRAME 0x1 namespace mozilla { namespace widget { namespace { /* Helper method to create a canvas rendering context backed by the given surface * * @param shell The docShell used by the canvas context for text settings and other * misc things. * @param surface The gfxSurface backing the context * @param width The width of the given surface * @param height The height of the given surface * @param aCtx Out-param - a canvas context backed by the given surface */ nsresult CreateRenderingContext(nsIDocShell *shell, gfxASurface *surface, PRUint32 width, PRUint32 height, nsICanvasRenderingContextInternal **aCtx) { nsresult rv; nsCOMPtr ctx(do_CreateInstance( "@mozilla.org/content/canvas-rendering-context;1?id=2d", &rv)); NS_ENSURE_SUCCESS(rv, rv); rv = ctx->InitializeWithSurface(shell, surface, width, height); NS_ENSURE_SUCCESS(rv, rv); NS_ADDREF(*aCtx = ctx); return NS_OK; } } TaskbarPreview::TaskbarPreview(ITaskbarList4 *aTaskbar, nsITaskbarPreviewController *aController, HWND aHWND, nsIDocShell *aShell) : mTaskbar(aTaskbar), mController(aController), mWnd(aHWND), mVisible(PR_FALSE), mDocShell(do_GetWeakReference(aShell)) { // TaskbarPreview may outlive the WinTaskbar that created it ::CoInitialize(NULL); WindowHook &hook = GetWindowHook(); hook.AddMonitor(WM_DESTROY, MainWindowHook, this); } TaskbarPreview::~TaskbarPreview() { // Avoid dangling pointer if (sActivePreview == this) sActivePreview = nsnull; // Here we remove the hook since this preview is dying before the nsWindow if (mWnd) DetachFromNSWindow(PR_TRUE); // Make sure to release before potentially uninitializing COM mTaskbar = NULL; ::CoUninitialize(); } NS_IMETHODIMP TaskbarPreview::SetController(nsITaskbarPreviewController *aController) { NS_ENSURE_ARG(aController); mController = aController; return NS_OK; } NS_IMETHODIMP TaskbarPreview::GetController(nsITaskbarPreviewController **aController) { NS_ADDREF(*aController = mController); return NS_OK; } NS_IMETHODIMP TaskbarPreview::GetTooltip(nsAString &aTooltip) { aTooltip = mTooltip; return NS_OK; } NS_IMETHODIMP TaskbarPreview::SetTooltip(const nsAString &aTooltip) { return NS_OK; mTooltip = aTooltip; return CanMakeTaskbarCalls() ? UpdateTooltip() : NS_OK; } NS_IMETHODIMP TaskbarPreview::SetVisible(PRBool visible) { if (mVisible == visible) return NS_OK; mVisible = visible; return visible ? Enable() : Disable(); } NS_IMETHODIMP TaskbarPreview::GetVisible(PRBool *visible) { *visible = mVisible; return NS_OK; } NS_IMETHODIMP TaskbarPreview::SetActive(PRBool active) { if (active) sActivePreview = this; else if (sActivePreview == this) sActivePreview = NULL; return CanMakeTaskbarCalls() ? ShowActive(active) : NS_OK; } NS_IMETHODIMP TaskbarPreview::GetActive(PRBool *active) { *active = sActivePreview == this; return NS_OK; } NS_IMETHODIMP TaskbarPreview::Invalidate() { if (!mVisible) return NS_ERROR_FAILURE; // DWM Composition is required for previews if (!nsUXThemeData::CheckForCompositor()) return NS_OK; HWND previewWindow = PreviewWindow(); return FAILED(nsUXThemeData::dwmInvalidateIconicBitmapsPtr(previewWindow)) ? NS_ERROR_FAILURE : NS_OK; } nsresult TaskbarPreview::UpdateTaskbarProperties() { nsresult rv = UpdateTooltip(); // If we are the active preview and our window is the active window, restore // our active state - otherwise some other non-preview window is now active // and should be displayed as so. if (sActivePreview == this) { if (mWnd == ::GetActiveWindow()) { nsresult rvActive = ShowActive(PR_TRUE); if (NS_FAILED(rvActive)) rv = rvActive; } else { sActivePreview = nsnull; } } return rv; } nsresult TaskbarPreview::Enable() { nsresult rv = NS_OK; if (CanMakeTaskbarCalls()) { rv = UpdateTaskbarProperties(); } else { WindowHook &hook = GetWindowHook(); hook.AddMonitor(nsAppShell::GetTaskbarButtonCreatedMessage(), MainWindowHook, this); } return rv; } nsresult TaskbarPreview::Disable() { WindowHook &hook = GetWindowHook(); (void) hook.RemoveMonitor(nsAppShell::GetTaskbarButtonCreatedMessage(), MainWindowHook, this); return NS_OK; } void TaskbarPreview::DetachFromNSWindow(PRBool windowIsAlive) { if (windowIsAlive) { WindowHook &hook = GetWindowHook(); hook.RemoveMonitor(WM_DESTROY, MainWindowHook, this); } mWnd = NULL; } LRESULT TaskbarPreview::WndProc(UINT nMsg, WPARAM wParam, LPARAM lParam) { switch (nMsg) { case WM_DWMSENDICONICTHUMBNAIL: { PRUint32 width = HIWORD(lParam); PRUint32 height = LOWORD(lParam); float aspectRatio = width/float(height); nsresult rv; float preferredAspectRatio; rv = mController->GetThumbnailAspectRatio(&preferredAspectRatio); if (NS_FAILED(rv)) break; PRUint32 thumbnailWidth = width; PRUint32 thumbnailHeight = height; if (aspectRatio > preferredAspectRatio) { thumbnailWidth = PRUint32(thumbnailHeight * preferredAspectRatio); } else { thumbnailHeight = PRUint32(thumbnailWidth / preferredAspectRatio); } DrawBitmap(thumbnailWidth, thumbnailHeight, PR_FALSE); } break; case WM_DWMSENDICONICLIVEPREVIEWBITMAP: { PRUint32 width, height; nsresult rv; rv = mController->GetWidth(&width); if (NS_FAILED(rv)) break; rv = mController->GetHeight(&height); if (NS_FAILED(rv)) break; DrawBitmap(width, height, PR_TRUE); } break; } return ::DefWindowProcW(PreviewWindow(), nMsg, wParam, lParam); } PRBool TaskbarPreview::CanMakeTaskbarCalls() { nsWindow *window = nsWindow::GetNSWindowPtr(mWnd); NS_ASSERTION(window, "Cannot use taskbar previews in an embedded context!"); return mVisible && window->HasTaskbarIconBeenCreated(); } WindowHook& TaskbarPreview::GetWindowHook() { nsWindow *window = nsWindow::GetNSWindowPtr(mWnd); NS_ASSERTION(window, "Cannot use taskbar previews in an embedded context!"); return window->GetWindowHook(); } void TaskbarPreview::EnableCustomDrawing(HWND aHWND, PRBool aEnable) { nsUXThemeData::dwmSetWindowAttributePtr( aHWND, DWMWA_FORCE_ICONIC_REPRESENTATION, &aEnable, sizeof(aEnable)); nsUXThemeData::dwmSetWindowAttributePtr( aHWND, DWMWA_HAS_ICONIC_BITMAP, &aEnable, sizeof(aEnable)); } nsresult TaskbarPreview::UpdateTooltip() { NS_ASSERTION(CanMakeTaskbarCalls() && mVisible, "UpdateTooltip called on invisible tab preview"); if (FAILED(mTaskbar->SetThumbnailTooltip(PreviewWindow(), mTooltip.get()))) return NS_ERROR_FAILURE; return NS_OK; } void TaskbarPreview::DrawBitmap(PRUint32 width, PRUint32 height, PRBool isPreview) { nsresult rv; nsRefPtr surface = new gfxWindowsSurface(gfxIntSize(width, height), gfxASurface::ImageFormatARGB32); nsCOMPtr shell = do_QueryReferent(mDocShell); if (!shell) return; nsCOMPtr ctxI; rv = CreateRenderingContext(shell, surface, width, height, getter_AddRefs(ctxI)); nsCOMPtr ctx = do_QueryInterface(ctxI); PRBool drawFrame = PR_FALSE; if (NS_SUCCEEDED(rv) && ctx) { if (isPreview) rv = mController->DrawPreview(ctx, &drawFrame); else rv = mController->DrawThumbnail(ctx, width, height, &drawFrame); } if (NS_FAILED(rv)) return; HDC hDC = surface->GetDC(); HBITMAP hBitmap = (HBITMAP)GetCurrentObject(hDC, OBJ_BITMAP); DWORD flags = drawFrame ? DWM_SIT_DISPLAYFRAME : 0; POINT pptClient = { 0, 0 }; if (isPreview) nsUXThemeData::dwmSetIconicLivePreviewBitmapPtr(PreviewWindow(), hBitmap, &pptClient, flags); else nsUXThemeData::dwmSetIconicThumbnailPtr(PreviewWindow(), hBitmap, flags); } /* static */ PRBool TaskbarPreview::MainWindowHook(void *aContext, HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam, LRESULT *aResult) { NS_ASSERTION(nMsg == nsAppShell::GetTaskbarButtonCreatedMessage() || nMsg == WM_DESTROY, "Window hook proc called with wrong message"); TaskbarPreview *preview = reinterpret_cast(aContext); if (nMsg == WM_DESTROY) { // nsWindow is being destroyed // Don't remove the hook since it is currently in dispatch // and the window is being destroyed preview->DetachFromNSWindow(PR_FALSE); } else { nsWindow *window = nsWindow::GetNSWindowPtr(preview->mWnd); NS_ASSERTION(window, "Cannot use taskbar previews in an embedded context!"); window->SetHasTaskbarIconBeenCreated(); if (preview->mVisible) preview->UpdateTaskbarProperties(); } return PR_FALSE; } TaskbarPreview * TaskbarPreview::sActivePreview = nsnull; } // namespace widget } // namespace mozilla #endif // MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_WIN7