1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/widget/cocoa/nsMacDockSupport.mm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,178 @@ 1.4 +/* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#import <Cocoa/Cocoa.h> 1.10 + 1.11 +#include "nsComponentManagerUtils.h" 1.12 +#include "nsMacDockSupport.h" 1.13 +#include "nsObjCExceptions.h" 1.14 + 1.15 +NS_IMPL_ISUPPORTS(nsMacDockSupport, nsIMacDockSupport, nsITaskbarProgress) 1.16 + 1.17 +nsMacDockSupport::nsMacDockSupport() 1.18 +: mAppIcon(nil) 1.19 +, mProgressBackground(nil) 1.20 +, mProgressState(STATE_NO_PROGRESS) 1.21 +, mProgressFraction(0.0) 1.22 +{ 1.23 + mProgressTimer = do_CreateInstance(NS_TIMER_CONTRACTID); 1.24 +} 1.25 + 1.26 +nsMacDockSupport::~nsMacDockSupport() 1.27 +{ 1.28 + if (mAppIcon) { 1.29 + [mAppIcon release]; 1.30 + mAppIcon = nil; 1.31 + } 1.32 + if (mProgressBackground) { 1.33 + [mProgressBackground release]; 1.34 + mProgressBackground = nil; 1.35 + } 1.36 + if (mProgressTimer) { 1.37 + mProgressTimer->Cancel(); 1.38 + mProgressTimer = nullptr; 1.39 + } 1.40 +} 1.41 + 1.42 +NS_IMETHODIMP 1.43 +nsMacDockSupport::GetDockMenu(nsIStandaloneNativeMenu ** aDockMenu) 1.44 +{ 1.45 + *aDockMenu = nullptr; 1.46 + 1.47 + if (mDockMenu) 1.48 + return mDockMenu->QueryInterface(NS_GET_IID(nsIStandaloneNativeMenu), 1.49 + reinterpret_cast<void **>(aDockMenu)); 1.50 + return NS_OK; 1.51 +} 1.52 + 1.53 +NS_IMETHODIMP 1.54 +nsMacDockSupport::SetDockMenu(nsIStandaloneNativeMenu * aDockMenu) 1.55 +{ 1.56 + nsresult rv; 1.57 + mDockMenu = do_QueryInterface(aDockMenu, &rv); 1.58 + return rv; 1.59 +} 1.60 + 1.61 +NS_IMETHODIMP 1.62 +nsMacDockSupport::ActivateApplication(bool aIgnoreOtherApplications) 1.63 +{ 1.64 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; 1.65 + 1.66 + [[NSApplication sharedApplication] activateIgnoringOtherApps:aIgnoreOtherApplications]; 1.67 + return NS_OK; 1.68 + 1.69 + NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; 1.70 +} 1.71 + 1.72 +NS_IMETHODIMP 1.73 +nsMacDockSupport::SetBadgeText(const nsAString& aBadgeText) 1.74 +{ 1.75 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; 1.76 + 1.77 + NSDockTile *tile = [[NSApplication sharedApplication] dockTile]; 1.78 + mBadgeText = aBadgeText; 1.79 + if (aBadgeText.IsEmpty()) 1.80 + [tile setBadgeLabel: nil]; 1.81 + else 1.82 + [tile setBadgeLabel:[NSString stringWithCharacters:reinterpret_cast<const unichar*>(mBadgeText.get()) 1.83 + length:mBadgeText.Length()]]; 1.84 + return NS_OK; 1.85 + 1.86 + NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; 1.87 +} 1.88 + 1.89 +NS_IMETHODIMP 1.90 +nsMacDockSupport::GetBadgeText(nsAString& aBadgeText) 1.91 +{ 1.92 + aBadgeText = mBadgeText; 1.93 + return NS_OK; 1.94 +} 1.95 + 1.96 +NS_IMETHODIMP 1.97 +nsMacDockSupport::SetProgressState(nsTaskbarProgressState aState, 1.98 + uint64_t aCurrentValue, 1.99 + uint64_t aMaxValue) 1.100 +{ 1.101 + NS_ENSURE_ARG_RANGE(aState, 0, STATE_PAUSED); 1.102 + if (aState == STATE_NO_PROGRESS || aState == STATE_INDETERMINATE) { 1.103 + NS_ENSURE_TRUE(aCurrentValue == 0, NS_ERROR_INVALID_ARG); 1.104 + NS_ENSURE_TRUE(aMaxValue == 0, NS_ERROR_INVALID_ARG); 1.105 + } 1.106 + if (aCurrentValue > aMaxValue) { 1.107 + return NS_ERROR_ILLEGAL_VALUE; 1.108 + } 1.109 + 1.110 + mProgressState = aState; 1.111 + if (aMaxValue == 0) { 1.112 + mProgressFraction = 0; 1.113 + } else { 1.114 + mProgressFraction = (double)aCurrentValue / aMaxValue; 1.115 + } 1.116 + 1.117 + if (mProgressState == STATE_NORMAL || mProgressState == STATE_INDETERMINATE) { 1.118 + int perSecond = 8; // Empirically determined, see bug 848792 1.119 + mProgressTimer->InitWithFuncCallback(RedrawIconCallback, this, 1000 / perSecond, 1.120 + nsITimer::TYPE_REPEATING_SLACK); 1.121 + return NS_OK; 1.122 + } else { 1.123 + mProgressTimer->Cancel(); 1.124 + return RedrawIcon(); 1.125 + } 1.126 +} 1.127 + 1.128 +// static 1.129 +void nsMacDockSupport::RedrawIconCallback(nsITimer* aTimer, void* aClosure) 1.130 +{ 1.131 + static_cast<nsMacDockSupport*>(aClosure)->RedrawIcon(); 1.132 +} 1.133 + 1.134 +// Return whether to draw progress 1.135 +bool nsMacDockSupport::InitProgress() 1.136 +{ 1.137 + if (mProgressState != STATE_NORMAL && mProgressState != STATE_INDETERMINATE) { 1.138 + return false; 1.139 + } 1.140 + 1.141 + if (!mAppIcon) { 1.142 + mProgressTimer = do_CreateInstance(NS_TIMER_CONTRACTID); 1.143 + mAppIcon = [[NSImage imageNamed:@"NSApplicationIcon"] retain]; 1.144 + mProgressBackground = [mAppIcon copyWithZone:nil]; 1.145 + mTheme = new nsNativeThemeCocoa(); 1.146 + 1.147 + NSSize sz = [mProgressBackground size]; 1.148 + mProgressBounds = CGRectMake(sz.width * 1/32, sz.height * 3/32, 1.149 + sz.width * 30/32, sz.height * 4/32); 1.150 + [mProgressBackground lockFocus]; 1.151 + [[NSColor whiteColor] set]; 1.152 + NSRectFill(NSRectFromCGRect(mProgressBounds)); 1.153 + [mProgressBackground unlockFocus]; 1.154 + } 1.155 + return true; 1.156 +} 1.157 + 1.158 +nsresult 1.159 +nsMacDockSupport::RedrawIcon() 1.160 +{ 1.161 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; 1.162 + 1.163 + if (InitProgress()) { 1.164 + // TODO: - Implement ERROR and PAUSED states? 1.165 + NSImage *icon = [mProgressBackground copyWithZone:nil]; 1.166 + bool isIndeterminate = (mProgressState != STATE_NORMAL); 1.167 + 1.168 + [icon lockFocus]; 1.169 + CGContextRef ctx = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; 1.170 + mTheme->DrawProgress(ctx, mProgressBounds, isIndeterminate, 1.171 + true, mProgressFraction, 1.0, NULL); 1.172 + [icon unlockFocus]; 1.173 + [NSApp setApplicationIconImage:icon]; 1.174 + [icon release]; 1.175 + } else { 1.176 + [NSApp setApplicationIconImage:mAppIcon]; 1.177 + } 1.178 + 1.179 + return NS_OK; 1.180 + NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; 1.181 +}