Path: blob/master/src/java.desktop/macosx/native/libawt_lwawt/awt/CTrayIcon.m
41152 views
/*1* Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425#import "jni_util.h"2627#import <AppKit/AppKit.h>2829#import "CTrayIcon.h"30#import "ThreadUtilities.h"31#include "GeomUtilities.h"32#import "LWCToolkit.h"3334#define kImageInset 4.03536/**37* If the image of the specified size won't fit into the status bar,38* then scale it down proprtionally. Otherwise, leave it as is.39*/40static NSSize ScaledImageSizeForStatusBar(NSSize imageSize, BOOL autosize) {41NSRect imageRect = NSMakeRect(0.0, 0.0, imageSize.width, imageSize.height);4243// There is a black line at the bottom of the status bar44// that we don't want to cover with image pixels.45CGFloat desiredSize = [[NSStatusBar systemStatusBar] thickness] - 1.0;46if (autosize) {47imageRect.size.width = desiredSize;48imageRect.size.height = desiredSize;49} else {50CGFloat scaleFactor = MIN(1.0, desiredSize/imageSize.height);51imageRect.size.width *= scaleFactor;52imageRect.size.height *= scaleFactor;53}54imageRect = NSIntegralRect(imageRect);55return imageRect.size;56}5758@implementation AWTTrayIcon5960- (id) initWithPeer:(jobject)thePeer {61if (!(self = [super init])) return nil;6263peer = thePeer;6465theItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];66[theItem retain];6768view = [[AWTTrayIconView alloc] initWithTrayIcon:self];69[theItem setView:view];70[[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:self];7172return self;73}7475-(void) dealloc {76[[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:nil];7778JNIEnv *env = [ThreadUtilities getJNIEnvUncached];79(*env)->DeleteGlobalRef(env, peer);8081[[NSStatusBar systemStatusBar] removeStatusItem: theItem];8283// Its a bad idea to force the item to release our view by setting84// the item's view to nil: it can lead to a crash in some scenarios.85// The item will release the view later on, so just set the view's image86// and tray icon to nil since we are done with it.87[view setImage: nil];88[view setTrayIcon: nil];89[view release];9091[theItem release];9293[super dealloc];94}9596- (void) setTooltip:(NSString *) tooltip{97[view setToolTip:tooltip];98}99100-(NSStatusItem *) theItem{101return theItem;102}103104- (jobject) peer{105return peer;106}107108- (void) setImage:(NSImage *) imagePtr sizing:(BOOL)autosize template:(BOOL)isTemplate {109NSSize imageSize = [imagePtr size];110NSSize scaledSize = ScaledImageSizeForStatusBar(imageSize, autosize);111if (imageSize.width != scaledSize.width ||112imageSize.height != scaledSize.height) {113[imagePtr setSize: scaledSize];114}115116CGFloat itemLength = scaledSize.width + 2.0*kImageInset;117[theItem setLength:itemLength];118119[imagePtr setTemplate: isTemplate];120[view setImage:imagePtr];121}122123- (NSPoint) getLocationOnScreen {124return [[view window] convertBaseToScreen: NSZeroPoint];125}126127-(void) deliverJavaMouseEvent: (NSEvent *) event {128[AWTToolkit eventCountPlusPlus];129130JNIEnv *env = [ThreadUtilities getJNIEnv];131132NSPoint eventLocation = [event locationInWindow];133NSPoint localPoint = [view convertPoint: eventLocation fromView: nil];134localPoint.y = [view bounds].size.height - localPoint.y;135136NSPoint absP = [NSEvent mouseLocation];137NSEventType type = [event type];138139absP = ConvertNSScreenPoint(NULL, absP);140jint clickCount;141142clickCount = [event clickCount];143144jdouble deltaX = [event deltaX];145jdouble deltaY = [event deltaY];146if ([AWTToolkit hasPreciseScrollingDeltas: event]) {147deltaX = [event scrollingDeltaX] * 0.1;148deltaY = [event scrollingDeltaY] * 0.1;149}150151DECLARE_CLASS(jc_NSEvent, "sun/lwawt/macosx/NSEvent");152DECLARE_METHOD(jctor_NSEvent, jc_NSEvent, "<init>", "(IIIIIIIIDDI)V");153jobject jEvent = (*env)->NewObject(env, jc_NSEvent, jctor_NSEvent,154[event type],155[event modifierFlags],156clickCount,157[event buttonNumber],158(jint)localPoint.x, (jint)localPoint.y,159(jint)absP.x, (jint)absP.y,160deltaY,161deltaX,162[AWTToolkit scrollStateWithEvent: event]);163CHECK_NULL(jEvent);164165DECLARE_CLASS(jc_TrayIcon, "sun/lwawt/macosx/CTrayIcon");166DECLARE_METHOD(jm_handleMouseEvent, jc_TrayIcon, "handleMouseEvent", "(Lsun/lwawt/macosx/NSEvent;)V");167(*env)->CallVoidMethod(env, peer, jm_handleMouseEvent, jEvent);168CHECK_EXCEPTION();169(*env)->DeleteLocalRef(env, jEvent);170}171172- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center173shouldPresentNotification:(NSUserNotification *)notification174{175return YES; // We always show notifications to the user176}177178@end //AWTTrayIcon179//================================================180181@implementation AWTTrayIconView182183-(id)initWithTrayIcon:(AWTTrayIcon *)theTrayIcon {184self = [super initWithFrame:NSMakeRect(0, 0, 1, 1)];185186[self setTrayIcon: theTrayIcon];187[self setImage: nil];188isHighlighted = NO;189trackingArea = nil;190191[self addTrackingArea];192193return self;194}195196- (void)addTrackingArea {197NSTrackingAreaOptions options = NSTrackingMouseMoved |198NSTrackingInVisibleRect |199NSTrackingActiveAlways;200trackingArea = [[NSTrackingArea alloc] initWithRect: CGRectZero201options: options202owner: self203userInfo: nil];204[self addTrackingArea:trackingArea];205}206207-(void) dealloc {208[trackingArea release];209[super dealloc];210}211212- (void)setHighlighted:(BOOL)aFlag213{214if (isHighlighted != aFlag) {215isHighlighted = aFlag;216[self setNeedsDisplay:YES];217}218}219220-(void)setTrayIcon:(AWTTrayIcon*)theTrayIcon {221trayIcon = theTrayIcon;222}223224- (void)menuWillOpen:(NSMenu *)menu225{226[self setHighlighted:YES];227}228229- (void)menuDidClose:(NSMenu *)menu230{231[menu setDelegate:nil];232[self setHighlighted:NO];233}234235- (void)drawRect:(NSRect)dirtyRect236{237if (self.image == nil) {238return;239}240241NSRect bounds = [self bounds];242[trayIcon.theItem drawStatusBarBackgroundInRect:bounds243withHighlight:isHighlighted];244245[super drawRect: dirtyRect];246}247248- (void)mouseDown:(NSEvent *)event {249[trayIcon deliverJavaMouseEvent: event];250251// don't show the menu on ctrl+click: it triggers ACTION event, like right click252if (([event modifierFlags] & NSControlKeyMask) == 0) {253//find CTrayIcon.getPopupMenuModel method and call it to get popup menu ptr.254JNIEnv *env = [ThreadUtilities getJNIEnv];255DECLARE_CLASS(jc_CTrayIcon, "sun/lwawt/macosx/CTrayIcon");256DECLARE_METHOD(jm_getPopupMenuModel, jc_CTrayIcon, "getPopupMenuModel", "()J");257jlong res = (*env)->CallLongMethod(env, trayIcon.peer, jm_getPopupMenuModel);258CHECK_EXCEPTION();259260if (res != 0) {261CPopupMenu *cmenu = jlong_to_ptr(res);262NSMenu* menu = [cmenu menu];263[menu setDelegate:self];264[trayIcon.theItem popUpStatusItemMenu:menu];265[self setNeedsDisplay:YES];266}267}268}269270- (void) mouseUp:(NSEvent *)event {271[trayIcon deliverJavaMouseEvent: event];272}273274- (void) mouseDragged:(NSEvent *)event {275[trayIcon deliverJavaMouseEvent: event];276}277278- (void) mouseMoved: (NSEvent *)event {279[trayIcon deliverJavaMouseEvent: event];280}281282- (void) rightMouseDown:(NSEvent *)event {283[trayIcon deliverJavaMouseEvent: event];284}285286- (void) rightMouseUp:(NSEvent *)event {287[trayIcon deliverJavaMouseEvent: event];288}289290- (void) rightMouseDragged:(NSEvent *)event {291[trayIcon deliverJavaMouseEvent: event];292}293294- (void) otherMouseDown:(NSEvent *)event {295[trayIcon deliverJavaMouseEvent: event];296}297298- (void) otherMouseUp:(NSEvent *)event {299[trayIcon deliverJavaMouseEvent: event];300}301302- (void) otherMouseDragged:(NSEvent *)event {303[trayIcon deliverJavaMouseEvent: event];304}305306307@end //AWTTrayIconView308//================================================309310/*311* Class: sun_lwawt_macosx_CTrayIcon312* Method: nativeCreate313* Signature: ()J314*/315JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CTrayIcon_nativeCreate316(JNIEnv *env, jobject peer) {317__block AWTTrayIcon *trayIcon = nil;318319JNI_COCOA_ENTER(env);320321jobject thePeer = (*env)->NewGlobalRef(env, peer);322[ThreadUtilities performOnMainThreadWaiting:YES block:^(){323trayIcon = [[AWTTrayIcon alloc] initWithPeer:thePeer];324}];325326JNI_COCOA_EXIT(env);327328return ptr_to_jlong(trayIcon);329}330331332/*333* Class: java_awt_TrayIcon334* Method: initIDs335* Signature: ()V336*/337JNIEXPORT void JNICALL Java_java_awt_TrayIcon_initIDs338(JNIEnv *env, jclass cls) {339//Do nothing.340}341342/*343* Class: sun_lwawt_macosx_CTrayIcon344* Method: nativeSetToolTip345* Signature: (JLjava/lang/String;)V346*/347JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CTrayIcon_nativeSetToolTip348(JNIEnv *env, jobject self, jlong model, jstring jtooltip) {349JNI_COCOA_ENTER(env);350351AWTTrayIcon *icon = jlong_to_ptr(model);352NSString *tooltip = JavaStringToNSString(env, jtooltip);353[ThreadUtilities performOnMainThreadWaiting:NO block:^(){354[icon setTooltip:tooltip];355}];356357JNI_COCOA_EXIT(env);358}359360/*361* Class: sun_lwawt_macosx_CTrayIcon362* Method: setNativeImage363* Signature: (JJZZ)V364*/365JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CTrayIcon_setNativeImage366(JNIEnv *env, jobject self, jlong model, jlong imagePtr, jboolean autosize, jboolean isTemplate) {367JNI_COCOA_ENTER(env);368369AWTTrayIcon *icon = jlong_to_ptr(model);370[ThreadUtilities performOnMainThreadWaiting:YES block:^(){371[icon setImage:jlong_to_ptr(imagePtr) sizing:autosize template:isTemplate];372}];373374JNI_COCOA_EXIT(env);375}376377JNIEXPORT jobject JNICALL378Java_sun_lwawt_macosx_CTrayIcon_nativeGetIconLocation379(JNIEnv *env, jobject self, jlong model) {380jobject jpt = NULL;381382JNI_COCOA_ENTER(env);383384__block NSPoint pt = NSZeroPoint;385AWTTrayIcon *icon = jlong_to_ptr(model);386[ThreadUtilities performOnMainThreadWaiting:YES block:^(){387NSPoint loc = [icon getLocationOnScreen];388pt = ConvertNSScreenPoint(env, loc);389}];390391jpt = NSToJavaPoint(env, pt);392393JNI_COCOA_EXIT(env);394395return jpt;396}397398JNIEXPORT void JNICALL399Java_sun_lwawt_macosx_CTrayIcon_nativeShowNotification400(JNIEnv *env, jobject self, jlong model, jobject jcaption, jobject jtext,401long nsimage) {402JNI_COCOA_ENTER(env);403404AWTTrayIcon *icon = jlong_to_ptr(model);405NSString *caption = JavaStringToNSString(env, jcaption);406NSString *text = JavaStringToNSString(env, jtext);407NSImage * contentImage = jlong_to_ptr(nsimage);408409[ThreadUtilities performOnMainThreadWaiting:NO block:^(){410NSUserNotification *notification = [[NSUserNotification alloc] init];411notification.title = caption;412notification.informativeText = text;413notification.contentImage = contentImage;414notification.soundName = NSUserNotificationDefaultSoundName;415416[[NSUserNotificationCenter defaultUserNotificationCenter]417deliverNotification:notification];418}];419420JNI_COCOA_EXIT(env);421}422423424