|
1 /* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #import <Cocoa/Cocoa.h> |
|
7 |
|
8 #include "nsComponentManagerUtils.h" |
|
9 #include "nsMacDockSupport.h" |
|
10 #include "nsObjCExceptions.h" |
|
11 |
|
12 NS_IMPL_ISUPPORTS(nsMacDockSupport, nsIMacDockSupport, nsITaskbarProgress) |
|
13 |
|
14 nsMacDockSupport::nsMacDockSupport() |
|
15 : mAppIcon(nil) |
|
16 , mProgressBackground(nil) |
|
17 , mProgressState(STATE_NO_PROGRESS) |
|
18 , mProgressFraction(0.0) |
|
19 { |
|
20 mProgressTimer = do_CreateInstance(NS_TIMER_CONTRACTID); |
|
21 } |
|
22 |
|
23 nsMacDockSupport::~nsMacDockSupport() |
|
24 { |
|
25 if (mAppIcon) { |
|
26 [mAppIcon release]; |
|
27 mAppIcon = nil; |
|
28 } |
|
29 if (mProgressBackground) { |
|
30 [mProgressBackground release]; |
|
31 mProgressBackground = nil; |
|
32 } |
|
33 if (mProgressTimer) { |
|
34 mProgressTimer->Cancel(); |
|
35 mProgressTimer = nullptr; |
|
36 } |
|
37 } |
|
38 |
|
39 NS_IMETHODIMP |
|
40 nsMacDockSupport::GetDockMenu(nsIStandaloneNativeMenu ** aDockMenu) |
|
41 { |
|
42 *aDockMenu = nullptr; |
|
43 |
|
44 if (mDockMenu) |
|
45 return mDockMenu->QueryInterface(NS_GET_IID(nsIStandaloneNativeMenu), |
|
46 reinterpret_cast<void **>(aDockMenu)); |
|
47 return NS_OK; |
|
48 } |
|
49 |
|
50 NS_IMETHODIMP |
|
51 nsMacDockSupport::SetDockMenu(nsIStandaloneNativeMenu * aDockMenu) |
|
52 { |
|
53 nsresult rv; |
|
54 mDockMenu = do_QueryInterface(aDockMenu, &rv); |
|
55 return rv; |
|
56 } |
|
57 |
|
58 NS_IMETHODIMP |
|
59 nsMacDockSupport::ActivateApplication(bool aIgnoreOtherApplications) |
|
60 { |
|
61 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; |
|
62 |
|
63 [[NSApplication sharedApplication] activateIgnoringOtherApps:aIgnoreOtherApplications]; |
|
64 return NS_OK; |
|
65 |
|
66 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; |
|
67 } |
|
68 |
|
69 NS_IMETHODIMP |
|
70 nsMacDockSupport::SetBadgeText(const nsAString& aBadgeText) |
|
71 { |
|
72 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; |
|
73 |
|
74 NSDockTile *tile = [[NSApplication sharedApplication] dockTile]; |
|
75 mBadgeText = aBadgeText; |
|
76 if (aBadgeText.IsEmpty()) |
|
77 [tile setBadgeLabel: nil]; |
|
78 else |
|
79 [tile setBadgeLabel:[NSString stringWithCharacters:reinterpret_cast<const unichar*>(mBadgeText.get()) |
|
80 length:mBadgeText.Length()]]; |
|
81 return NS_OK; |
|
82 |
|
83 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; |
|
84 } |
|
85 |
|
86 NS_IMETHODIMP |
|
87 nsMacDockSupport::GetBadgeText(nsAString& aBadgeText) |
|
88 { |
|
89 aBadgeText = mBadgeText; |
|
90 return NS_OK; |
|
91 } |
|
92 |
|
93 NS_IMETHODIMP |
|
94 nsMacDockSupport::SetProgressState(nsTaskbarProgressState aState, |
|
95 uint64_t aCurrentValue, |
|
96 uint64_t aMaxValue) |
|
97 { |
|
98 NS_ENSURE_ARG_RANGE(aState, 0, STATE_PAUSED); |
|
99 if (aState == STATE_NO_PROGRESS || aState == STATE_INDETERMINATE) { |
|
100 NS_ENSURE_TRUE(aCurrentValue == 0, NS_ERROR_INVALID_ARG); |
|
101 NS_ENSURE_TRUE(aMaxValue == 0, NS_ERROR_INVALID_ARG); |
|
102 } |
|
103 if (aCurrentValue > aMaxValue) { |
|
104 return NS_ERROR_ILLEGAL_VALUE; |
|
105 } |
|
106 |
|
107 mProgressState = aState; |
|
108 if (aMaxValue == 0) { |
|
109 mProgressFraction = 0; |
|
110 } else { |
|
111 mProgressFraction = (double)aCurrentValue / aMaxValue; |
|
112 } |
|
113 |
|
114 if (mProgressState == STATE_NORMAL || mProgressState == STATE_INDETERMINATE) { |
|
115 int perSecond = 8; // Empirically determined, see bug 848792 |
|
116 mProgressTimer->InitWithFuncCallback(RedrawIconCallback, this, 1000 / perSecond, |
|
117 nsITimer::TYPE_REPEATING_SLACK); |
|
118 return NS_OK; |
|
119 } else { |
|
120 mProgressTimer->Cancel(); |
|
121 return RedrawIcon(); |
|
122 } |
|
123 } |
|
124 |
|
125 // static |
|
126 void nsMacDockSupport::RedrawIconCallback(nsITimer* aTimer, void* aClosure) |
|
127 { |
|
128 static_cast<nsMacDockSupport*>(aClosure)->RedrawIcon(); |
|
129 } |
|
130 |
|
131 // Return whether to draw progress |
|
132 bool nsMacDockSupport::InitProgress() |
|
133 { |
|
134 if (mProgressState != STATE_NORMAL && mProgressState != STATE_INDETERMINATE) { |
|
135 return false; |
|
136 } |
|
137 |
|
138 if (!mAppIcon) { |
|
139 mProgressTimer = do_CreateInstance(NS_TIMER_CONTRACTID); |
|
140 mAppIcon = [[NSImage imageNamed:@"NSApplicationIcon"] retain]; |
|
141 mProgressBackground = [mAppIcon copyWithZone:nil]; |
|
142 mTheme = new nsNativeThemeCocoa(); |
|
143 |
|
144 NSSize sz = [mProgressBackground size]; |
|
145 mProgressBounds = CGRectMake(sz.width * 1/32, sz.height * 3/32, |
|
146 sz.width * 30/32, sz.height * 4/32); |
|
147 [mProgressBackground lockFocus]; |
|
148 [[NSColor whiteColor] set]; |
|
149 NSRectFill(NSRectFromCGRect(mProgressBounds)); |
|
150 [mProgressBackground unlockFocus]; |
|
151 } |
|
152 return true; |
|
153 } |
|
154 |
|
155 nsresult |
|
156 nsMacDockSupport::RedrawIcon() |
|
157 { |
|
158 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; |
|
159 |
|
160 if (InitProgress()) { |
|
161 // TODO: - Implement ERROR and PAUSED states? |
|
162 NSImage *icon = [mProgressBackground copyWithZone:nil]; |
|
163 bool isIndeterminate = (mProgressState != STATE_NORMAL); |
|
164 |
|
165 [icon lockFocus]; |
|
166 CGContextRef ctx = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; |
|
167 mTheme->DrawProgress(ctx, mProgressBounds, isIndeterminate, |
|
168 true, mProgressFraction, 1.0, NULL); |
|
169 [icon unlockFocus]; |
|
170 [NSApp setApplicationIconImage:icon]; |
|
171 [icon release]; |
|
172 } else { |
|
173 [NSApp setApplicationIconImage:mAppIcon]; |
|
174 } |
|
175 |
|
176 return NS_OK; |
|
177 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; |
|
178 } |