1 /* 2 * Copyright (c) 2011, 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 29 #import "CTrayIcon.h" 30 #import "ThreadUtilities.h" 31 #include "GeomUtilities.h" 32 33 #define kImageInset 4.0 34 35 /** 36 * If the image of the specified size won't fit into the status bar, 37 * then scale it down proprtionally. Otherwise, leave it as is. 38 */ 39 static NSSize ScaledImageSizeForStatusBar(NSSize imageSize) { 40 NSRect imageRect = NSMakeRect(0.0, 0.0, imageSize.width, imageSize.height); 41 42 // There is a black line at the bottom of the status bar 43 // that we don't want to cover with image pixels. 44 CGFloat desiredHeight = [[NSStatusBar systemStatusBar] thickness] - 1.0; 45 CGFloat scaleFactor = MIN(1.0, desiredHeight/imageSize.height); 46 47 imageRect.size.width *= scaleFactor; 48 imageRect.size.height *= scaleFactor; 49 imageRect = NSIntegralRect(imageRect); 50 51 return imageRect.size; 52 } 53 54 @implementation AWTTrayIcon 55 56 - (id) initWithPeer:(jobject)thePeer { 57 if (!(self = [super init])) return nil; 58 59 peer = thePeer; 60 61 theItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength]; 62 [theItem retain]; 63 64 view = [[AWTTrayIconView alloc] initWithTrayIcon:self]; 65 [theItem setView:view]; 66 67 return self; 68 } 69 70 -(void) dealloc { 71 JNIEnv *env = [ThreadUtilities getJNIEnvUncached]; 72 JNFDeleteGlobalRef(env, peer); 73 74 [[NSStatusBar systemStatusBar] removeStatusItem: theItem]; 75 76 // Its a bad idea to force the item to release our view by setting 77 // the item's view to nil: it can lead to a crash in some scenarios. 78 // The item will release the view later on, so just set the view's image 79 // to nil since we are done with it. 80 [view setImage: nil]; 81 [view release]; 82 83 [theItem release]; 84 85 [super dealloc]; 86 } 87 88 - (void) setTooltip:(NSString *) tooltip{ 89 [view setToolTip:tooltip]; 90 } 91 92 -(NSStatusItem *) theItem{ 93 return theItem; 94 } 95 96 - (jobject) peer{ 97 return peer; 98 } 99 100 - (void) setImage:(NSImage *) imagePtr sizing:(BOOL)autosize{ 101 NSSize imageSize = [imagePtr size]; 102 NSSize scaledSize = ScaledImageSizeForStatusBar(imageSize); 103 if (imageSize.width != scaledSize.width || 104 imageSize.height != scaledSize.height) { 105 [imagePtr setSize: scaledSize]; 106 } 107 108 CGFloat itemLength = scaledSize.width + 2.0*kImageInset; 109 [theItem setLength:itemLength]; 110 111 [view setImage:imagePtr]; 112 } 113 114 - (NSPoint) getLocationOnScreen { 115 return [[view window] convertBaseToScreen: NSZeroPoint]; 116 } 117 118 @end //AWTTrayIcon 119 //================================================ 120 121 @implementation AWTTrayIconView 122 123 -(id)initWithTrayIcon:(AWTTrayIcon *)theTrayIcon { 124 self = [super initWithFrame:NSMakeRect(0, 0, 1, 1)]; 125 126 trayIcon = theTrayIcon; 127 isHighlighted = NO; 128 image = nil; 129 130 return self; 131 } 132 133 -(void) dealloc { 134 [image release]; 135 [super dealloc]; 136 } 137 138 - (void)setHighlighted:(BOOL)aFlag 139 { 140 if (isHighlighted != aFlag) { 141 isHighlighted = aFlag; 142 [self setNeedsDisplay:YES]; 143 } 144 } 145 146 - (void)setImage:(NSImage*)anImage { 147 [anImage retain]; 148 [image release]; 149 image = anImage; 150 151 if (image != nil) { 152 [self setNeedsDisplay:YES]; 153 } 154 } 155 156 - (void)menuWillOpen:(NSMenu *)menu 157 { 158 [self setHighlighted:YES]; 159 } 160 161 - (void)menuDidClose:(NSMenu *)menu 162 { 163 [menu setDelegate:nil]; 164 [self setHighlighted:NO]; 165 } 166 167 - (void)drawRect:(NSRect)dirtyRect 168 { 169 if (image == nil) { 170 return; 171 } 172 173 NSRect bounds = [self bounds]; 174 NSSize imageSize = [image size]; 175 176 NSRect drawRect = {{ (bounds.size.width - imageSize.width) / 2.0, 177 (bounds.size.height - imageSize.height) / 2.0 }, imageSize}; 178 179 // don't cover bottom pixels of the status bar with the image 180 if (drawRect.origin.y < 1.0) { 181 drawRect.origin.y = 1.0; 182 } 183 drawRect = NSIntegralRect(drawRect); 184 185 [trayIcon.theItem drawStatusBarBackgroundInRect:bounds 186 withHighlight:isHighlighted]; 187 [image drawInRect:drawRect 188 fromRect:NSZeroRect 189 operation:NSCompositeSourceOver 190 fraction:1.0 191 ]; 192 } 193 194 - (void) mouseDown:(NSEvent *)e { 195 //find CTrayIcon.getPopupMenuModel method and call it to get popup menu ptr. 196 JNIEnv *env = [ThreadUtilities getJNIEnv]; 197 static JNF_CLASS_CACHE(jc_CTrayIcon, "sun/lwawt/macosx/CTrayIcon"); 198 static JNF_MEMBER_CACHE(jm_getPopupMenuModel, jc_CTrayIcon, "getPopupMenuModel", "()J"); 199 static JNF_MEMBER_CACHE(jm_performAction, jc_CTrayIcon, "performAction", "()V"); 200 jlong res = JNFCallLongMethod(env, trayIcon.peer, jm_getPopupMenuModel); 201 if (res != 0) { 202 CPopupMenu *cmenu = jlong_to_ptr(res); 203 NSMenu* menu = [cmenu menu]; 204 [menu setDelegate:self]; 205 [trayIcon.theItem popUpStatusItemMenu:menu]; 206 [self setNeedsDisplay:YES]; 207 } else { 208 JNFCallVoidMethod(env, trayIcon.peer, jm_performAction); 209 } 210 } 211 212 - (void) rightMouseDown:(NSEvent *)e { 213 // Call CTrayIcon.performAction() method on right mouse press 214 JNIEnv *env = [ThreadUtilities getJNIEnv]; 215 static JNF_CLASS_CACHE(jc_CTrayIcon, "sun/lwawt/macosx/CTrayIcon"); 216 static JNF_MEMBER_CACHE(jm_performAction, jc_CTrayIcon, "performAction", "()V"); 217 JNFCallVoidMethod(env, trayIcon.peer, jm_performAction); 218 } 219 220 221 @end //AWTTrayIconView 222 //================================================ 223 224 /* 225 * Class: sun_lwawt_macosx_CTrayIcon 226 * Method: nativeCreate 227 * Signature: ()J 228 */ 229 JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CTrayIcon_nativeCreate 230 (JNIEnv *env, jobject peer) { 231 __block AWTTrayIcon *trayIcon = nil; 232 233 JNF_COCOA_ENTER(env); 234 AWT_ASSERT_NOT_APPKIT_THREAD; 235 236 jobject thePeer = JNFNewGlobalRef(env, peer); 237 [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ 238 trayIcon = [[AWTTrayIcon alloc] initWithPeer:thePeer]; 239 }]; 240 241 JNF_COCOA_EXIT(env); 242 243 return ptr_to_jlong(trayIcon); 244 } 245 246 247 /* 248 * Class: java_awt_TrayIcon 249 * Method: initIDs 250 * Signature: ()V 251 */ 252 JNIEXPORT void JNICALL Java_java_awt_TrayIcon_initIDs 253 (JNIEnv *env, jclass cls) { 254 //Do nothing. 255 } 256 257 /* 258 * Class: sun_lwawt_macosx_CTrayIcon 259 * Method: nativeSetToolTip 260 * Signature: (JLjava/lang/String;)V 261 */ 262 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CTrayIcon_nativeSetToolTip 263 (JNIEnv *env, jobject self, jlong model, jstring jtooltip) { 264 JNF_COCOA_ENTER(env); 265 AWT_ASSERT_NOT_APPKIT_THREAD; 266 267 AWTTrayIcon *icon = jlong_to_ptr(model); 268 NSString *tooltip = JNFJavaToNSString(env, jtooltip); 269 [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ 270 [icon setTooltip:tooltip]; 271 }]; 272 273 JNF_COCOA_EXIT(env); 274 } 275 276 /* 277 * Class: sun_lwawt_macosx_CTrayIcon 278 * Method: setNativeImage 279 * Signature: (JJZ)V 280 */ 281 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CTrayIcon_setNativeImage 282 (JNIEnv *env, jobject self, jlong model, jlong imagePtr, jboolean autosize) { 283 JNF_COCOA_ENTER(env); 284 AWT_ASSERT_NOT_APPKIT_THREAD; 285 286 AWTTrayIcon *icon = jlong_to_ptr(model); 287 [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ 288 [icon setImage:jlong_to_ptr(imagePtr) sizing:autosize]; 289 }]; 290 291 JNF_COCOA_EXIT(env); 292 } 293 294 JNIEXPORT jobject JNICALL 295 Java_sun_lwawt_macosx_CTrayIcon_nativeGetIconLocation 296 (JNIEnv *env, jobject self, jlong model) { 297 jobject jpt = NULL; 298 299 JNF_COCOA_ENTER(env); 300 AWT_ASSERT_NOT_APPKIT_THREAD; 301 302 __block NSPoint pt = NSZeroPoint; 303 AWTTrayIcon *icon = jlong_to_ptr(model); 304 [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ 305 AWT_ASSERT_APPKIT_THREAD; 306 307 NSPoint loc = [icon getLocationOnScreen]; 308 pt = ConvertNSScreenPoint(env, loc); 309 }]; 310 311 jpt = NSToJavaPoint(env, pt); 312 313 JNF_COCOA_EXIT(env); 314 315 return jpt; 316 }