4632N/A/*
4632N/A * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
4632N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4632N/A *
4632N/A * This code is free software; you can redistribute it and/or modify it
4632N/A * under the terms of the GNU General Public License version 2 only, as
4632N/A * published by the Free Software Foundation. Oracle designates this
4632N/A * particular file as subject to the "Classpath" exception as provided
4632N/A * by Oracle in the LICENSE file that accompanied this code.
4632N/A *
4632N/A * This code is distributed in the hope that it will be useful, but WITHOUT
4632N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
4632N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
4632N/A * version 2 for more details (a copy is included in the LICENSE file that
4632N/A * accompanied this code).
4632N/A *
4632N/A * You should have received a copy of the GNU General Public License version
4632N/A * 2 along with this work; if not, write to the Free Software Foundation,
4632N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
4632N/A *
4632N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
4632N/A * or visit www.oracle.com if you need additional information or have any
4632N/A * questions.
4632N/A */
4632N/A
4632N/A#import <AppKit/AppKit.h>
4632N/A#import <JavaNativeFoundation/JavaNativeFoundation.h>
4632N/A
4632N/A#import "CTrayIcon.h"
4632N/A#import "ThreadUtilities.h"
4639N/A#include "GeomUtilities.h"
4876N/A#import "LWCToolkit.h"
4632N/A
4639N/A#define kImageInset 4.0
4639N/A
4639N/A/**
4639N/A * If the image of the specified size won't fit into the status bar,
4639N/A * then scale it down proprtionally. Otherwise, leave it as is.
4639N/A */
4639N/Astatic NSSize ScaledImageSizeForStatusBar(NSSize imageSize) {
4639N/A NSRect imageRect = NSMakeRect(0.0, 0.0, imageSize.width, imageSize.height);
4730N/A
4730N/A // There is a black line at the bottom of the status bar
4639N/A // that we don't want to cover with image pixels.
4639N/A CGFloat desiredHeight = [[NSStatusBar systemStatusBar] thickness] - 1.0;
4639N/A CGFloat scaleFactor = MIN(1.0, desiredHeight/imageSize.height);
4730N/A
4639N/A imageRect.size.width *= scaleFactor;
4639N/A imageRect.size.height *= scaleFactor;
4639N/A imageRect = NSIntegralRect(imageRect);
4730N/A
4639N/A return imageRect.size;
4639N/A}
4632N/A
4632N/A@implementation AWTTrayIcon
4632N/A
4632N/A- (id) initWithPeer:(jobject)thePeer {
4632N/A if (!(self = [super init])) return nil;
4632N/A
4632N/A peer = thePeer;
4632N/A
4730N/A theItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
4632N/A [theItem retain];
4632N/A
4639N/A view = [[AWTTrayIconView alloc] initWithTrayIcon:self];
4639N/A [theItem setView:view];
4632N/A
4632N/A return self;
4632N/A}
4632N/A
4632N/A-(void) dealloc {
4632N/A JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
4632N/A JNFDeleteGlobalRef(env, peer);
4632N/A
4644N/A [[NSStatusBar systemStatusBar] removeStatusItem: theItem];
4644N/A
4644N/A // Its a bad idea to force the item to release our view by setting
4644N/A // the item's view to nil: it can lead to a crash in some scenarios.
4644N/A // The item will release the view later on, so just set the view's image
4876N/A // and tray icon to nil since we are done with it.
4644N/A [view setImage: nil];
4876N/A [view setTrayIcon: nil];
4644N/A [view release];
4644N/A
4632N/A [theItem release];
4632N/A
4632N/A [super dealloc];
4632N/A}
4632N/A
4632N/A- (void) setTooltip:(NSString *) tooltip{
4639N/A [view setToolTip:tooltip];
4632N/A}
4632N/A
4632N/A-(NSStatusItem *) theItem{
4632N/A return theItem;
4632N/A}
4632N/A
4632N/A- (jobject) peer{
4632N/A return peer;
4632N/A}
4632N/A
4639N/A- (void) setImage:(NSImage *) imagePtr sizing:(BOOL)autosize{
4639N/A NSSize imageSize = [imagePtr size];
4639N/A NSSize scaledSize = ScaledImageSizeForStatusBar(imageSize);
4730N/A if (imageSize.width != scaledSize.width ||
4639N/A imageSize.height != scaledSize.height) {
4639N/A [imagePtr setSize: scaledSize];
4639N/A }
4730N/A
4644N/A CGFloat itemLength = scaledSize.width + 2.0*kImageInset;
4644N/A [theItem setLength:itemLength];
4644N/A
4639N/A [view setImage:imagePtr];
4632N/A}
4632N/A
4639N/A- (NSPoint) getLocationOnScreen {
4639N/A return [[view window] convertBaseToScreen: NSZeroPoint];
4632N/A}
4632N/A
4876N/A-(void) deliverJavaMouseEvent: (NSEvent *) event {
4876N/A [AWTToolkit eventCountPlusPlus];
4876N/A
4876N/A JNIEnv *env = [ThreadUtilities getJNIEnv];
4876N/A
4876N/A NSPoint eventLocation = [event locationInWindow];
4876N/A NSPoint localPoint = [view convertPoint: eventLocation fromView: nil];
4876N/A localPoint.y = [view bounds].size.height - localPoint.y;
4876N/A
4876N/A NSPoint absP = [NSEvent mouseLocation];
4876N/A NSEventType type = [event type];
4876N/A
4876N/A NSRect screenRect = [[NSScreen mainScreen] frame];
4876N/A absP.y = screenRect.size.height - absP.y;
4876N/A jint clickCount;
4876N/A
4876N/A clickCount = [event clickCount];
4876N/A
4876N/A static JNF_CLASS_CACHE(jc_NSEvent, "sun/lwawt/macosx/event/NSEvent");
4876N/A static JNF_CTOR_CACHE(jctor_NSEvent, jc_NSEvent, "(IIIIIIIIDD)V");
4876N/A jobject jEvent = JNFNewObject(env, jctor_NSEvent,
4876N/A [event type],
4876N/A [event modifierFlags],
4876N/A clickCount,
4876N/A [event buttonNumber],
4876N/A (jint)localPoint.x, (jint)localPoint.y,
4876N/A (jint)absP.x, (jint)absP.y,
4876N/A [event deltaY],
4876N/A [event deltaX]);
4876N/A if (jEvent == nil) {
4876N/A // Unable to create event by some reason.
4876N/A return;
4876N/A }
4876N/A
4876N/A static JNF_CLASS_CACHE(jc_TrayIcon, "sun/lwawt/macosx/CTrayIcon");
4876N/A static JNF_MEMBER_CACHE(jm_handleMouseEvent, jc_TrayIcon, "handleMouseEvent", "(Lsun/lwawt/macosx/event/NSEvent;)V");
4876N/A JNFCallVoidMethod(env, peer, jm_handleMouseEvent, jEvent);
4876N/A}
4876N/A
4632N/A@end //AWTTrayIcon
4632N/A//================================================
4632N/A
4639N/A@implementation AWTTrayIconView
4632N/A
4730N/A-(id)initWithTrayIcon:(AWTTrayIcon *)theTrayIcon {
4639N/A self = [super initWithFrame:NSMakeRect(0, 0, 1, 1)];
4632N/A
4876N/A [self setTrayIcon: theTrayIcon];
4639N/A isHighlighted = NO;
4730N/A image = nil;
4730N/A
4639N/A return self;
4639N/A}
4632N/A
4639N/A-(void) dealloc {
4639N/A [image release];
4639N/A [super dealloc];
4639N/A}
4639N/A
4730N/A- (void)setHighlighted:(BOOL)aFlag
4639N/A{
4639N/A if (isHighlighted != aFlag) {
4639N/A isHighlighted = aFlag;
4639N/A [self setNeedsDisplay:YES];
4639N/A }
4639N/A}
4639N/A
4639N/A- (void)setImage:(NSImage*)anImage {
4639N/A [anImage retain];
4730N/A [image release];
4639N/A image = anImage;
4639N/A
4644N/A if (image != nil) {
4644N/A [self setNeedsDisplay:YES];
4644N/A }
4639N/A}
4632N/A
4876N/A-(void)setTrayIcon:(AWTTrayIcon*)theTrayIcon {
4876N/A trayIcon = theTrayIcon;
4876N/A}
4876N/A
4730N/A- (void)menuWillOpen:(NSMenu *)menu
4639N/A{
4639N/A [self setHighlighted:YES];
4639N/A}
4639N/A
4730N/A- (void)menuDidClose:(NSMenu *)menu
4639N/A{
4639N/A [menu setDelegate:nil];
4639N/A [self setHighlighted:NO];
4639N/A}
4639N/A
4639N/A- (void)drawRect:(NSRect)dirtyRect
4639N/A{
4644N/A if (image == nil) {
4644N/A return;
4644N/A }
4644N/A
4639N/A NSRect bounds = [self bounds];
4639N/A NSSize imageSize = [image size];
4639N/A
4730N/A NSRect drawRect = {{ (bounds.size.width - imageSize.width) / 2.0,
4639N/A (bounds.size.height - imageSize.height) / 2.0 }, imageSize};
4639N/A
4730N/A // don't cover bottom pixels of the status bar with the image
4639N/A if (drawRect.origin.y < 1.0) {
4639N/A drawRect.origin.y = 1.0;
4639N/A }
4730N/A drawRect = NSIntegralRect(drawRect);
4730N/A
4639N/A [trayIcon.theItem drawStatusBarBackgroundInRect:bounds
4730N/A withHighlight:isHighlighted];
4730N/A [image drawInRect:drawRect
4730N/A fromRect:NSZeroRect
4730N/A operation:NSCompositeSourceOver
4639N/A fraction:1.0
4639N/A ];
4632N/A}
4632N/A
4876N/A- (void)mouseDown:(NSEvent *)event {
4876N/A [trayIcon deliverJavaMouseEvent: event];
4876N/A
4876N/A // don't show the menu on ctrl+click: it triggers ACTION event, like right click
4876N/A if (([event modifierFlags] & NSControlKeyMask) == 0) {
4876N/A //find CTrayIcon.getPopupMenuModel method and call it to get popup menu ptr.
4876N/A JNIEnv *env = [ThreadUtilities getJNIEnv];
4876N/A static JNF_CLASS_CACHE(jc_CTrayIcon, "sun/lwawt/macosx/CTrayIcon");
4876N/A static JNF_MEMBER_CACHE(jm_getPopupMenuModel, jc_CTrayIcon, "getPopupMenuModel", "()J");
4876N/A jlong res = JNFCallLongMethod(env, trayIcon.peer, jm_getPopupMenuModel);
4876N/A
4876N/A if (res != 0) {
4876N/A CPopupMenu *cmenu = jlong_to_ptr(res);
4876N/A NSMenu* menu = [cmenu menu];
4876N/A [menu setDelegate:self];
4876N/A [trayIcon.theItem popUpStatusItemMenu:menu];
4876N/A [self setNeedsDisplay:YES];
4876N/A }
4632N/A }
4632N/A}
4632N/A
4876N/A- (void) mouseUp:(NSEvent *)event {
4876N/A [trayIcon deliverJavaMouseEvent: event];
4876N/A}
4876N/A
4876N/A- (void) mouseDragged:(NSEvent *)event {
4876N/A [trayIcon deliverJavaMouseEvent: event];
4876N/A}
4876N/A
4876N/A- (void) rightMouseDown:(NSEvent *)event {
4876N/A [trayIcon deliverJavaMouseEvent: event];
4876N/A}
4876N/A
4876N/A- (void) rightMouseUp:(NSEvent *)event {
4876N/A [trayIcon deliverJavaMouseEvent: event];
4876N/A}
4876N/A
4876N/A- (void) rightMouseDragged:(NSEvent *)event {
4876N/A [trayIcon deliverJavaMouseEvent: event];
4876N/A}
4876N/A
4876N/A- (void) otherMouseDown:(NSEvent *)event {
4876N/A [trayIcon deliverJavaMouseEvent: event];
4876N/A}
4876N/A
4876N/A- (void) otherMouseUp:(NSEvent *)event {
4876N/A [trayIcon deliverJavaMouseEvent: event];
4876N/A}
4876N/A
4876N/A- (void) otherMouseDragged:(NSEvent *)event {
4876N/A [trayIcon deliverJavaMouseEvent: event];
4632N/A}
4632N/A
4632N/A
4639N/A@end //AWTTrayIconView
4632N/A//================================================
4632N/A
4632N/A/*
4632N/A * Class: sun_lwawt_macosx_CTrayIcon
4632N/A * Method: nativeCreate
4632N/A * Signature: ()J
4632N/A */
4632N/AJNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CTrayIcon_nativeCreate
4632N/A(JNIEnv *env, jobject peer) {
4632N/A __block AWTTrayIcon *trayIcon = nil;
4632N/A
4632N/AJNF_COCOA_ENTER(env);
4632N/A
4632N/A jobject thePeer = JNFNewGlobalRef(env, peer);
6055N/A [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
4632N/A trayIcon = [[AWTTrayIcon alloc] initWithPeer:thePeer];
4632N/A }];
4632N/A
4632N/AJNF_COCOA_EXIT(env);
4632N/A
4632N/A return ptr_to_jlong(trayIcon);
4632N/A}
4632N/A
4632N/A
4632N/A/*
4632N/A * Class: java_awt_TrayIcon
4632N/A * Method: initIDs
4632N/A * Signature: ()V
4632N/A */
4632N/AJNIEXPORT void JNICALL Java_java_awt_TrayIcon_initIDs
4632N/A(JNIEnv *env, jclass cls) {
4632N/A //Do nothing.
4632N/A}
4632N/A
4632N/A/*
4632N/A * Class: sun_lwawt_macosx_CTrayIcon
4632N/A * Method: nativeSetToolTip
4632N/A * Signature: (JLjava/lang/String;)V
4632N/A */
4632N/AJNIEXPORT void JNICALL Java_sun_lwawt_macosx_CTrayIcon_nativeSetToolTip
4632N/A(JNIEnv *env, jobject self, jlong model, jstring jtooltip) {
4632N/AJNF_COCOA_ENTER(env);
4632N/A
4632N/A AWTTrayIcon *icon = jlong_to_ptr(model);
4632N/A NSString *tooltip = JNFJavaToNSString(env, jtooltip);
6055N/A [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
4632N/A [icon setTooltip:tooltip];
4632N/A }];
4632N/A
4632N/AJNF_COCOA_EXIT(env);
4632N/A}
4632N/A
4632N/A/*
4632N/A * Class: sun_lwawt_macosx_CTrayIcon
4632N/A * Method: setNativeImage
4632N/A * Signature: (JJZ)V
4632N/A */
4632N/AJNIEXPORT void JNICALL Java_sun_lwawt_macosx_CTrayIcon_setNativeImage
4632N/A(JNIEnv *env, jobject self, jlong model, jlong imagePtr, jboolean autosize) {
4632N/AJNF_COCOA_ENTER(env);
4632N/A
4632N/A AWTTrayIcon *icon = jlong_to_ptr(model);
6055N/A [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
4632N/A [icon setImage:jlong_to_ptr(imagePtr) sizing:autosize];
4632N/A }];
4632N/A
4632N/AJNF_COCOA_EXIT(env);
4632N/A}
4632N/A
4639N/AJNIEXPORT jobject JNICALL
4639N/AJava_sun_lwawt_macosx_CTrayIcon_nativeGetIconLocation
4639N/A(JNIEnv *env, jobject self, jlong model) {
4639N/A jobject jpt = NULL;
4639N/A
4639N/AJNF_COCOA_ENTER(env);
4730N/A
4639N/A __block NSPoint pt = NSZeroPoint;
4639N/A AWTTrayIcon *icon = jlong_to_ptr(model);
6055N/A [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
4730N/A NSPoint loc = [icon getLocationOnScreen];
4639N/A pt = ConvertNSScreenPoint(env, loc);
4639N/A }];
4639N/A
4639N/A jpt = NSToJavaPoint(env, pt);
4730N/A
4639N/AJNF_COCOA_EXIT(env);
4730N/A
4639N/A return jpt;
4639N/A}