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;
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:^(){
|
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 #import "LWCToolkit.h"
33
34 #define kImageInset 4.0
35
36 /**
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 */
40 static NSSize ScaledImageSizeForStatusBar(NSSize imageSize) {
41 NSRect imageRect = NSMakeRect(0.0, 0.0, imageSize.width, imageSize.height);
42
43 // There is a black line at the bottom of the status bar
44 // that we don't want to cover with image pixels.
45 CGFloat desiredHeight = [[NSStatusBar systemStatusBar] thickness] - 1.0;
46 CGFloat scaleFactor = MIN(1.0, desiredHeight/imageSize.height);
47
48 imageRect.size.width *= scaleFactor;
49 imageRect.size.height *= scaleFactor;
50 imageRect = NSIntegralRect(imageRect);
51
52 return imageRect.size;
60 peer = thePeer;
61
62 theItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
63 [theItem retain];
64
65 view = [[AWTTrayIconView alloc] initWithTrayIcon:self];
66 [theItem setView:view];
67
68 return self;
69 }
70
71 -(void) dealloc {
72 JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
73 JNFDeleteGlobalRef(env, peer);
74
75 [[NSStatusBar systemStatusBar] removeStatusItem: theItem];
76
77 // Its a bad idea to force the item to release our view by setting
78 // the item's view to nil: it can lead to a crash in some scenarios.
79 // The item will release the view later on, so just set the view's image
80 // and tray icon to nil since we are done with it.
81 [view setImage: nil];
82 [view setTrayIcon: nil];
83 [view release];
84
85 [theItem release];
86
87 [super dealloc];
88 }
89
90 - (void) setTooltip:(NSString *) tooltip{
91 [view setToolTip:tooltip];
92 }
93
94 -(NSStatusItem *) theItem{
95 return theItem;
96 }
97
98 - (jobject) peer{
99 return peer;
100 }
101
102 - (void) setImage:(NSImage *) imagePtr sizing:(BOOL)autosize{
103 NSSize imageSize = [imagePtr size];
104 NSSize scaledSize = ScaledImageSizeForStatusBar(imageSize);
105 if (imageSize.width != scaledSize.width ||
106 imageSize.height != scaledSize.height) {
107 [imagePtr setSize: scaledSize];
108 }
109
110 CGFloat itemLength = scaledSize.width + 2.0*kImageInset;
111 [theItem setLength:itemLength];
112
113 [view setImage:imagePtr];
114 }
115
116 - (NSPoint) getLocationOnScreen {
117 return [[view window] convertBaseToScreen: NSZeroPoint];
118 }
119
120 -(void) deliverJavaMouseEvent: (NSEvent *) event {
121 [AWTToolkit eventCountPlusPlus];
122
123 JNIEnv *env = [ThreadUtilities getJNIEnv];
124
125 NSPoint eventLocation = [event locationInWindow];
126 NSPoint localPoint = [view convertPoint: eventLocation fromView: nil];
127 localPoint.y = [view bounds].size.height - localPoint.y;
128
129 NSPoint absP = [NSEvent mouseLocation];
130 NSEventType type = [event type];
131
132 NSRect screenRect = [[NSScreen mainScreen] frame];
133 absP.y = screenRect.size.height - absP.y;
134 jint clickCount;
135
136 clickCount = [event clickCount];
137
138 static JNF_CLASS_CACHE(jc_NSEvent, "sun/lwawt/macosx/event/NSEvent");
139 static JNF_CTOR_CACHE(jctor_NSEvent, jc_NSEvent, "(IIIIIIIIDD)V");
140 jobject jEvent = JNFNewObject(env, jctor_NSEvent,
141 [event type],
142 [event modifierFlags],
143 clickCount,
144 [event buttonNumber],
145 (jint)localPoint.x, (jint)localPoint.y,
146 (jint)absP.x, (jint)absP.y,
147 [event deltaY],
148 [event deltaX]);
149 if (jEvent == nil) {
150 // Unable to create event by some reason.
151 return;
152 }
153
154 static JNF_CLASS_CACHE(jc_TrayIcon, "sun/lwawt/macosx/CTrayIcon");
155 static JNF_MEMBER_CACHE(jm_handleMouseEvent, jc_TrayIcon, "handleMouseEvent", "(Lsun/lwawt/macosx/event/NSEvent;)V");
156 JNFCallVoidMethod(env, peer, jm_handleMouseEvent, jEvent);
157 }
158
159 @end //AWTTrayIcon
160 //================================================
161
162 @implementation AWTTrayIconView
163
164 -(id)initWithTrayIcon:(AWTTrayIcon *)theTrayIcon {
165 self = [super initWithFrame:NSMakeRect(0, 0, 1, 1)];
166
167 [self setTrayIcon: theTrayIcon];
168 isHighlighted = NO;
169 image = nil;
170
171 return self;
172 }
173
174 -(void) dealloc {
175 [image release];
176 [super dealloc];
177 }
178
179 - (void)setHighlighted:(BOOL)aFlag
180 {
181 if (isHighlighted != aFlag) {
182 isHighlighted = aFlag;
183 [self setNeedsDisplay:YES];
184 }
185 }
186
187 - (void)setImage:(NSImage*)anImage {
188 [anImage retain];
189 [image release];
190 image = anImage;
191
192 if (image != nil) {
193 [self setNeedsDisplay:YES];
194 }
195 }
196
197 -(void)setTrayIcon:(AWTTrayIcon*)theTrayIcon {
198 trayIcon = theTrayIcon;
199 }
200
201 - (void)menuWillOpen:(NSMenu *)menu
202 {
203 [self setHighlighted:YES];
204 }
205
206 - (void)menuDidClose:(NSMenu *)menu
207 {
208 [menu setDelegate:nil];
209 [self setHighlighted:NO];
210 }
211
212 - (void)drawRect:(NSRect)dirtyRect
213 {
214 if (image == nil) {
215 return;
216 }
217
218 NSRect bounds = [self bounds];
219 NSSize imageSize = [image size];
220
221 NSRect drawRect = {{ (bounds.size.width - imageSize.width) / 2.0,
222 (bounds.size.height - imageSize.height) / 2.0 }, imageSize};
223
224 // don't cover bottom pixels of the status bar with the image
225 if (drawRect.origin.y < 1.0) {
226 drawRect.origin.y = 1.0;
227 }
228 drawRect = NSIntegralRect(drawRect);
229
230 [trayIcon.theItem drawStatusBarBackgroundInRect:bounds
231 withHighlight:isHighlighted];
232 [image drawInRect:drawRect
233 fromRect:NSZeroRect
234 operation:NSCompositeSourceOver
235 fraction:1.0
236 ];
237 }
238
239 - (void)mouseDown:(NSEvent *)event {
240 [trayIcon deliverJavaMouseEvent: event];
241
242 // don't show the menu on ctrl+click: it triggers ACTION event, like right click
243 if (([event modifierFlags] & NSControlKeyMask) == 0) {
244 //find CTrayIcon.getPopupMenuModel method and call it to get popup menu ptr.
245 JNIEnv *env = [ThreadUtilities getJNIEnv];
246 static JNF_CLASS_CACHE(jc_CTrayIcon, "sun/lwawt/macosx/CTrayIcon");
247 static JNF_MEMBER_CACHE(jm_getPopupMenuModel, jc_CTrayIcon, "getPopupMenuModel", "()J");
248 jlong res = JNFCallLongMethod(env, trayIcon.peer, jm_getPopupMenuModel);
249
250 if (res != 0) {
251 CPopupMenu *cmenu = jlong_to_ptr(res);
252 NSMenu* menu = [cmenu menu];
253 [menu setDelegate:self];
254 [trayIcon.theItem popUpStatusItemMenu:menu];
255 [self setNeedsDisplay:YES];
256 }
257 }
258 }
259
260 - (void) mouseUp:(NSEvent *)event {
261 [trayIcon deliverJavaMouseEvent: event];
262 }
263
264 - (void) mouseDragged:(NSEvent *)event {
265 [trayIcon deliverJavaMouseEvent: event];
266 }
267
268 - (void) rightMouseDown:(NSEvent *)event {
269 [trayIcon deliverJavaMouseEvent: event];
270 }
271
272 - (void) rightMouseUp:(NSEvent *)event {
273 [trayIcon deliverJavaMouseEvent: event];
274 }
275
276 - (void) rightMouseDragged:(NSEvent *)event {
277 [trayIcon deliverJavaMouseEvent: event];
278 }
279
280 - (void) otherMouseDown:(NSEvent *)event {
281 [trayIcon deliverJavaMouseEvent: event];
282 }
283
284 - (void) otherMouseUp:(NSEvent *)event {
285 [trayIcon deliverJavaMouseEvent: event];
286 }
287
288 - (void) otherMouseDragged:(NSEvent *)event {
289 [trayIcon deliverJavaMouseEvent: event];
290 }
291
292
293 @end //AWTTrayIconView
294 //================================================
295
296 /*
297 * Class: sun_lwawt_macosx_CTrayIcon
298 * Method: nativeCreate
299 * Signature: ()J
300 */
301 JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CTrayIcon_nativeCreate
302 (JNIEnv *env, jobject peer) {
303 __block AWTTrayIcon *trayIcon = nil;
304
305 JNF_COCOA_ENTER(env);
306 AWT_ASSERT_NOT_APPKIT_THREAD;
307
308 jobject thePeer = JNFNewGlobalRef(env, peer);
309 [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){
|