michael@0: /* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- */ 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: #import michael@0: michael@0: #include "nsComponentManagerUtils.h" michael@0: #include "nsMacDockSupport.h" michael@0: #include "nsObjCExceptions.h" michael@0: michael@0: NS_IMPL_ISUPPORTS(nsMacDockSupport, nsIMacDockSupport, nsITaskbarProgress) michael@0: michael@0: nsMacDockSupport::nsMacDockSupport() michael@0: : mAppIcon(nil) michael@0: , mProgressBackground(nil) michael@0: , mProgressState(STATE_NO_PROGRESS) michael@0: , mProgressFraction(0.0) michael@0: { michael@0: mProgressTimer = do_CreateInstance(NS_TIMER_CONTRACTID); michael@0: } michael@0: michael@0: nsMacDockSupport::~nsMacDockSupport() michael@0: { michael@0: if (mAppIcon) { michael@0: [mAppIcon release]; michael@0: mAppIcon = nil; michael@0: } michael@0: if (mProgressBackground) { michael@0: [mProgressBackground release]; michael@0: mProgressBackground = nil; michael@0: } michael@0: if (mProgressTimer) { michael@0: mProgressTimer->Cancel(); michael@0: mProgressTimer = nullptr; michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMacDockSupport::GetDockMenu(nsIStandaloneNativeMenu ** aDockMenu) michael@0: { michael@0: *aDockMenu = nullptr; michael@0: michael@0: if (mDockMenu) michael@0: return mDockMenu->QueryInterface(NS_GET_IID(nsIStandaloneNativeMenu), michael@0: reinterpret_cast(aDockMenu)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMacDockSupport::SetDockMenu(nsIStandaloneNativeMenu * aDockMenu) michael@0: { michael@0: nsresult rv; michael@0: mDockMenu = do_QueryInterface(aDockMenu, &rv); michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMacDockSupport::ActivateApplication(bool aIgnoreOtherApplications) michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; michael@0: michael@0: [[NSApplication sharedApplication] activateIgnoringOtherApps:aIgnoreOtherApplications]; michael@0: return NS_OK; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMacDockSupport::SetBadgeText(const nsAString& aBadgeText) michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; michael@0: michael@0: NSDockTile *tile = [[NSApplication sharedApplication] dockTile]; michael@0: mBadgeText = aBadgeText; michael@0: if (aBadgeText.IsEmpty()) michael@0: [tile setBadgeLabel: nil]; michael@0: else michael@0: [tile setBadgeLabel:[NSString stringWithCharacters:reinterpret_cast(mBadgeText.get()) michael@0: length:mBadgeText.Length()]]; michael@0: return NS_OK; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMacDockSupport::GetBadgeText(nsAString& aBadgeText) michael@0: { michael@0: aBadgeText = mBadgeText; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMacDockSupport::SetProgressState(nsTaskbarProgressState aState, michael@0: uint64_t aCurrentValue, michael@0: uint64_t aMaxValue) michael@0: { michael@0: NS_ENSURE_ARG_RANGE(aState, 0, STATE_PAUSED); michael@0: if (aState == STATE_NO_PROGRESS || aState == STATE_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: if (aCurrentValue > aMaxValue) { michael@0: return NS_ERROR_ILLEGAL_VALUE; michael@0: } michael@0: michael@0: mProgressState = aState; michael@0: if (aMaxValue == 0) { michael@0: mProgressFraction = 0; michael@0: } else { michael@0: mProgressFraction = (double)aCurrentValue / aMaxValue; michael@0: } michael@0: michael@0: if (mProgressState == STATE_NORMAL || mProgressState == STATE_INDETERMINATE) { michael@0: int perSecond = 8; // Empirically determined, see bug 848792 michael@0: mProgressTimer->InitWithFuncCallback(RedrawIconCallback, this, 1000 / perSecond, michael@0: nsITimer::TYPE_REPEATING_SLACK); michael@0: return NS_OK; michael@0: } else { michael@0: mProgressTimer->Cancel(); michael@0: return RedrawIcon(); michael@0: } michael@0: } michael@0: michael@0: // static michael@0: void nsMacDockSupport::RedrawIconCallback(nsITimer* aTimer, void* aClosure) michael@0: { michael@0: static_cast(aClosure)->RedrawIcon(); michael@0: } michael@0: michael@0: // Return whether to draw progress michael@0: bool nsMacDockSupport::InitProgress() michael@0: { michael@0: if (mProgressState != STATE_NORMAL && mProgressState != STATE_INDETERMINATE) { michael@0: return false; michael@0: } michael@0: michael@0: if (!mAppIcon) { michael@0: mProgressTimer = do_CreateInstance(NS_TIMER_CONTRACTID); michael@0: mAppIcon = [[NSImage imageNamed:@"NSApplicationIcon"] retain]; michael@0: mProgressBackground = [mAppIcon copyWithZone:nil]; michael@0: mTheme = new nsNativeThemeCocoa(); michael@0: michael@0: NSSize sz = [mProgressBackground size]; michael@0: mProgressBounds = CGRectMake(sz.width * 1/32, sz.height * 3/32, michael@0: sz.width * 30/32, sz.height * 4/32); michael@0: [mProgressBackground lockFocus]; michael@0: [[NSColor whiteColor] set]; michael@0: NSRectFill(NSRectFromCGRect(mProgressBounds)); michael@0: [mProgressBackground unlockFocus]; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: nsresult michael@0: nsMacDockSupport::RedrawIcon() michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; michael@0: michael@0: if (InitProgress()) { michael@0: // TODO: - Implement ERROR and PAUSED states? michael@0: NSImage *icon = [mProgressBackground copyWithZone:nil]; michael@0: bool isIndeterminate = (mProgressState != STATE_NORMAL); michael@0: michael@0: [icon lockFocus]; michael@0: CGContextRef ctx = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; michael@0: mTheme->DrawProgress(ctx, mProgressBounds, isIndeterminate, michael@0: true, mProgressFraction, 1.0, NULL); michael@0: [icon unlockFocus]; michael@0: [NSApp setApplicationIconImage:icon]; michael@0: [icon release]; michael@0: } else { michael@0: [NSApp setApplicationIconImage:mAppIcon]; michael@0: } michael@0: michael@0: return NS_OK; michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; michael@0: }