|
1 /* |
|
2 * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. |
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 * |
|
5 * This code is free software; you can redistribute it and/or modify it |
|
6 * under the terms of the GNU General Public License version 2 only, as |
|
7 * published by the Free Software Foundation. Oracle designates this |
|
8 * particular file as subject to the "Classpath" exception as provided |
|
9 * by Oracle in the LICENSE file that accompanied this code. |
|
10 * |
|
11 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 * version 2 for more details (a copy is included in the LICENSE file that |
|
15 * accompanied this code). |
|
16 * |
|
17 * You should have received a copy of the GNU General Public License version |
|
18 * 2 along with this work; if not, write to the Free Software Foundation, |
|
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 * |
|
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
22 * or visit www.oracle.com if you need additional information or have any |
|
23 * questions. |
|
24 */ |
|
25 |
|
26 #import <AppKit/AppKit.h> |
|
27 #import <JavaNativeFoundation/JavaNativeFoundation.h> |
|
28 #import "jni_util.h" |
|
29 |
|
30 #import "CTrayIcon.h" |
|
31 #import "ThreadUtilities.h" |
|
32 #include "GeomUtilities.h" |
|
33 #import "LWCToolkit.h" |
|
34 |
|
35 #define kImageInset 4.0 |
|
36 |
|
37 /** |
|
38 * If the image of the specified size won't fit into the status bar, |
|
39 * then scale it down proprtionally. Otherwise, leave it as is. |
|
40 */ |
|
41 static NSSize ScaledImageSizeForStatusBar(NSSize imageSize) { |
|
42 NSRect imageRect = NSMakeRect(0.0, 0.0, imageSize.width, imageSize.height); |
|
43 |
|
44 // There is a black line at the bottom of the status bar |
|
45 // that we don't want to cover with image pixels. |
|
46 CGFloat desiredHeight = [[NSStatusBar systemStatusBar] thickness] - 1.0; |
|
47 CGFloat scaleFactor = MIN(1.0, desiredHeight/imageSize.height); |
|
48 |
|
49 imageRect.size.width *= scaleFactor; |
|
50 imageRect.size.height *= scaleFactor; |
|
51 imageRect = NSIntegralRect(imageRect); |
|
52 |
|
53 return imageRect.size; |
|
54 } |
|
55 |
|
56 @implementation AWTTrayIcon |
|
57 |
|
58 - (id) initWithPeer:(jobject)thePeer { |
|
59 if (!(self = [super init])) return nil; |
|
60 |
|
61 peer = thePeer; |
|
62 |
|
63 theItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength]; |
|
64 [theItem retain]; |
|
65 |
|
66 view = [[AWTTrayIconView alloc] initWithTrayIcon:self]; |
|
67 [theItem setView:view]; |
|
68 |
|
69 return self; |
|
70 } |
|
71 |
|
72 -(void) dealloc { |
|
73 JNIEnv *env = [ThreadUtilities getJNIEnvUncached]; |
|
74 JNFDeleteGlobalRef(env, peer); |
|
75 |
|
76 [[NSStatusBar systemStatusBar] removeStatusItem: theItem]; |
|
77 |
|
78 // Its a bad idea to force the item to release our view by setting |
|
79 // the item's view to nil: it can lead to a crash in some scenarios. |
|
80 // The item will release the view later on, so just set the view's image |
|
81 // and tray icon to nil since we are done with it. |
|
82 [view setImage: nil]; |
|
83 [view setTrayIcon: nil]; |
|
84 [view release]; |
|
85 |
|
86 [theItem release]; |
|
87 |
|
88 [super dealloc]; |
|
89 } |
|
90 |
|
91 - (void) setTooltip:(NSString *) tooltip{ |
|
92 [view setToolTip:tooltip]; |
|
93 } |
|
94 |
|
95 -(NSStatusItem *) theItem{ |
|
96 return theItem; |
|
97 } |
|
98 |
|
99 - (jobject) peer{ |
|
100 return peer; |
|
101 } |
|
102 |
|
103 - (void) setImage:(NSImage *) imagePtr sizing:(BOOL)autosize{ |
|
104 NSSize imageSize = [imagePtr size]; |
|
105 NSSize scaledSize = ScaledImageSizeForStatusBar(imageSize); |
|
106 if (imageSize.width != scaledSize.width || |
|
107 imageSize.height != scaledSize.height) { |
|
108 [imagePtr setSize: scaledSize]; |
|
109 } |
|
110 |
|
111 CGFloat itemLength = scaledSize.width + 2.0*kImageInset; |
|
112 [theItem setLength:itemLength]; |
|
113 |
|
114 [view setImage:imagePtr]; |
|
115 } |
|
116 |
|
117 - (NSPoint) getLocationOnScreen { |
|
118 return [[view window] convertBaseToScreen: NSZeroPoint]; |
|
119 } |
|
120 |
|
121 -(void) deliverJavaMouseEvent: (NSEvent *) event { |
|
122 [AWTToolkit eventCountPlusPlus]; |
|
123 |
|
124 JNIEnv *env = [ThreadUtilities getJNIEnv]; |
|
125 |
|
126 NSPoint eventLocation = [event locationInWindow]; |
|
127 NSPoint localPoint = [view convertPoint: eventLocation fromView: nil]; |
|
128 localPoint.y = [view bounds].size.height - localPoint.y; |
|
129 |
|
130 NSPoint absP = [NSEvent mouseLocation]; |
|
131 NSEventType type = [event type]; |
|
132 |
|
133 NSRect screenRect = [[NSScreen mainScreen] frame]; |
|
134 absP.y = screenRect.size.height - absP.y; |
|
135 jint clickCount; |
|
136 |
|
137 clickCount = [event clickCount]; |
|
138 |
|
139 static JNF_CLASS_CACHE(jc_NSEvent, "sun/lwawt/macosx/NSEvent"); |
|
140 static JNF_CTOR_CACHE(jctor_NSEvent, jc_NSEvent, "(IIIIIIIIDD)V"); |
|
141 jobject jEvent = JNFNewObject(env, jctor_NSEvent, |
|
142 [event type], |
|
143 [event modifierFlags], |
|
144 clickCount, |
|
145 [event buttonNumber], |
|
146 (jint)localPoint.x, (jint)localPoint.y, |
|
147 (jint)absP.x, (jint)absP.y, |
|
148 [event deltaY], |
|
149 [event deltaX]); |
|
150 CHECK_NULL(jEvent); |
|
151 |
|
152 static JNF_CLASS_CACHE(jc_TrayIcon, "sun/lwawt/macosx/CTrayIcon"); |
|
153 static JNF_MEMBER_CACHE(jm_handleMouseEvent, jc_TrayIcon, "handleMouseEvent", "(Lsun/lwawt/macosx/NSEvent;)V"); |
|
154 JNFCallVoidMethod(env, peer, jm_handleMouseEvent, jEvent); |
|
155 (*env)->DeleteLocalRef(env, jEvent); |
|
156 } |
|
157 |
|
158 @end //AWTTrayIcon |
|
159 //================================================ |
|
160 |
|
161 @implementation AWTTrayIconView |
|
162 |
|
163 -(id)initWithTrayIcon:(AWTTrayIcon *)theTrayIcon { |
|
164 self = [super initWithFrame:NSMakeRect(0, 0, 1, 1)]; |
|
165 |
|
166 [self setTrayIcon: theTrayIcon]; |
|
167 isHighlighted = NO; |
|
168 image = nil; |
|
169 |
|
170 return self; |
|
171 } |
|
172 |
|
173 -(void) dealloc { |
|
174 [image release]; |
|
175 [super dealloc]; |
|
176 } |
|
177 |
|
178 - (void)setHighlighted:(BOOL)aFlag |
|
179 { |
|
180 if (isHighlighted != aFlag) { |
|
181 isHighlighted = aFlag; |
|
182 [self setNeedsDisplay:YES]; |
|
183 } |
|
184 } |
|
185 |
|
186 - (void)setImage:(NSImage*)anImage { |
|
187 [anImage retain]; |
|
188 [image release]; |
|
189 image = anImage; |
|
190 |
|
191 if (image != nil) { |
|
192 [self setNeedsDisplay:YES]; |
|
193 } |
|
194 } |
|
195 |
|
196 -(void)setTrayIcon:(AWTTrayIcon*)theTrayIcon { |
|
197 trayIcon = theTrayIcon; |
|
198 } |
|
199 |
|
200 - (void)menuWillOpen:(NSMenu *)menu |
|
201 { |
|
202 [self setHighlighted:YES]; |
|
203 } |
|
204 |
|
205 - (void)menuDidClose:(NSMenu *)menu |
|
206 { |
|
207 [menu setDelegate:nil]; |
|
208 [self setHighlighted:NO]; |
|
209 } |
|
210 |
|
211 - (void)drawRect:(NSRect)dirtyRect |
|
212 { |
|
213 if (image == nil) { |
|
214 return; |
|
215 } |
|
216 |
|
217 NSRect bounds = [self bounds]; |
|
218 NSSize imageSize = [image size]; |
|
219 |
|
220 NSRect drawRect = {{ (bounds.size.width - imageSize.width) / 2.0, |
|
221 (bounds.size.height - imageSize.height) / 2.0 }, imageSize}; |
|
222 |
|
223 // don't cover bottom pixels of the status bar with the image |
|
224 if (drawRect.origin.y < 1.0) { |
|
225 drawRect.origin.y = 1.0; |
|
226 } |
|
227 drawRect = NSIntegralRect(drawRect); |
|
228 |
|
229 [trayIcon.theItem drawStatusBarBackgroundInRect:bounds |
|
230 withHighlight:isHighlighted]; |
|
231 [image drawInRect:drawRect |
|
232 fromRect:NSZeroRect |
|
233 operation:NSCompositeSourceOver |
|
234 fraction:1.0 |
|
235 ]; |
|
236 } |
|
237 |
|
238 - (void)mouseDown:(NSEvent *)event { |
|
239 [trayIcon deliverJavaMouseEvent: event]; |
|
240 |
|
241 // don't show the menu on ctrl+click: it triggers ACTION event, like right click |
|
242 if (([event modifierFlags] & NSControlKeyMask) == 0) { |
|
243 //find CTrayIcon.getPopupMenuModel method and call it to get popup menu ptr. |
|
244 JNIEnv *env = [ThreadUtilities getJNIEnv]; |
|
245 static JNF_CLASS_CACHE(jc_CTrayIcon, "sun/lwawt/macosx/CTrayIcon"); |
|
246 static JNF_MEMBER_CACHE(jm_getPopupMenuModel, jc_CTrayIcon, "getPopupMenuModel", "()J"); |
|
247 jlong res = JNFCallLongMethod(env, trayIcon.peer, jm_getPopupMenuModel); |
|
248 |
|
249 if (res != 0) { |
|
250 CPopupMenu *cmenu = jlong_to_ptr(res); |
|
251 NSMenu* menu = [cmenu menu]; |
|
252 [menu setDelegate:self]; |
|
253 [trayIcon.theItem popUpStatusItemMenu:menu]; |
|
254 [self setNeedsDisplay:YES]; |
|
255 } |
|
256 } |
|
257 } |
|
258 |
|
259 - (void) mouseUp:(NSEvent *)event { |
|
260 [trayIcon deliverJavaMouseEvent: event]; |
|
261 } |
|
262 |
|
263 - (void) mouseDragged:(NSEvent *)event { |
|
264 [trayIcon deliverJavaMouseEvent: event]; |
|
265 } |
|
266 |
|
267 - (void) rightMouseDown:(NSEvent *)event { |
|
268 [trayIcon deliverJavaMouseEvent: event]; |
|
269 } |
|
270 |
|
271 - (void) rightMouseUp:(NSEvent *)event { |
|
272 [trayIcon deliverJavaMouseEvent: event]; |
|
273 } |
|
274 |
|
275 - (void) rightMouseDragged:(NSEvent *)event { |
|
276 [trayIcon deliverJavaMouseEvent: event]; |
|
277 } |
|
278 |
|
279 - (void) otherMouseDown:(NSEvent *)event { |
|
280 [trayIcon deliverJavaMouseEvent: event]; |
|
281 } |
|
282 |
|
283 - (void) otherMouseUp:(NSEvent *)event { |
|
284 [trayIcon deliverJavaMouseEvent: event]; |
|
285 } |
|
286 |
|
287 - (void) otherMouseDragged:(NSEvent *)event { |
|
288 [trayIcon deliverJavaMouseEvent: event]; |
|
289 } |
|
290 |
|
291 |
|
292 @end //AWTTrayIconView |
|
293 //================================================ |
|
294 |
|
295 /* |
|
296 * Class: sun_lwawt_macosx_CTrayIcon |
|
297 * Method: nativeCreate |
|
298 * Signature: ()J |
|
299 */ |
|
300 JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CTrayIcon_nativeCreate |
|
301 (JNIEnv *env, jobject peer) { |
|
302 __block AWTTrayIcon *trayIcon = nil; |
|
303 |
|
304 JNF_COCOA_ENTER(env); |
|
305 |
|
306 jobject thePeer = JNFNewGlobalRef(env, peer); |
|
307 [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ |
|
308 trayIcon = [[AWTTrayIcon alloc] initWithPeer:thePeer]; |
|
309 }]; |
|
310 |
|
311 JNF_COCOA_EXIT(env); |
|
312 |
|
313 return ptr_to_jlong(trayIcon); |
|
314 } |
|
315 |
|
316 |
|
317 /* |
|
318 * Class: java_awt_TrayIcon |
|
319 * Method: initIDs |
|
320 * Signature: ()V |
|
321 */ |
|
322 JNIEXPORT void JNICALL Java_java_awt_TrayIcon_initIDs |
|
323 (JNIEnv *env, jclass cls) { |
|
324 //Do nothing. |
|
325 } |
|
326 |
|
327 /* |
|
328 * Class: sun_lwawt_macosx_CTrayIcon |
|
329 * Method: nativeSetToolTip |
|
330 * Signature: (JLjava/lang/String;)V |
|
331 */ |
|
332 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CTrayIcon_nativeSetToolTip |
|
333 (JNIEnv *env, jobject self, jlong model, jstring jtooltip) { |
|
334 JNF_COCOA_ENTER(env); |
|
335 |
|
336 AWTTrayIcon *icon = jlong_to_ptr(model); |
|
337 NSString *tooltip = JNFJavaToNSString(env, jtooltip); |
|
338 [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ |
|
339 [icon setTooltip:tooltip]; |
|
340 }]; |
|
341 |
|
342 JNF_COCOA_EXIT(env); |
|
343 } |
|
344 |
|
345 /* |
|
346 * Class: sun_lwawt_macosx_CTrayIcon |
|
347 * Method: setNativeImage |
|
348 * Signature: (JJZ)V |
|
349 */ |
|
350 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CTrayIcon_setNativeImage |
|
351 (JNIEnv *env, jobject self, jlong model, jlong imagePtr, jboolean autosize) { |
|
352 JNF_COCOA_ENTER(env); |
|
353 |
|
354 AWTTrayIcon *icon = jlong_to_ptr(model); |
|
355 [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ |
|
356 [icon setImage:jlong_to_ptr(imagePtr) sizing:autosize]; |
|
357 }]; |
|
358 |
|
359 JNF_COCOA_EXIT(env); |
|
360 } |
|
361 |
|
362 JNIEXPORT jobject JNICALL |
|
363 Java_sun_lwawt_macosx_CTrayIcon_nativeGetIconLocation |
|
364 (JNIEnv *env, jobject self, jlong model) { |
|
365 jobject jpt = NULL; |
|
366 |
|
367 JNF_COCOA_ENTER(env); |
|
368 |
|
369 __block NSPoint pt = NSZeroPoint; |
|
370 AWTTrayIcon *icon = jlong_to_ptr(model); |
|
371 [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ |
|
372 NSPoint loc = [icon getLocationOnScreen]; |
|
373 pt = ConvertNSScreenPoint(env, loc); |
|
374 }]; |
|
375 |
|
376 jpt = NSToJavaPoint(env, pt); |
|
377 |
|
378 JNF_COCOA_EXIT(env); |
|
379 |
|
380 return jpt; |
|
381 } |