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 <Cocoa/Cocoa.h>
  27 #import <JavaNativeFoundation/JavaNativeFoundation.h>
  28 #import <JavaRuntimeSupport/JavaRuntimeSupport.h>
  29 
  30 #import "sun_lwawt_macosx_CPlatformWindow.h"
  31 #import "com_apple_eawt_event_GestureHandler.h"
  32 #import "com_apple_eawt_FullScreenHandler.h"
  33 
  34 #import "AWTWindow.h"
  35 #import "AWTView.h"
  36 #import "CMenu.h"
  37 #import "CMenuBar.h"
  38 #import "LWCToolkit.h"
  39 #import "GeomUtilities.h"
  40 #import "ThreadUtilities.h"
  41 #import "OSVersion.h"
  42 
  43 
  44 #define MASK(KEY) \
  45     (sun_lwawt_macosx_CPlatformWindow_ ## KEY)
  46 
  47 #define IS(BITS, KEY) \
  48     ((BITS & MASK(KEY)) != 0)
  49 
  50 #define SET(BITS, KEY, VALUE) \
  51     BITS = VALUE ? BITS | MASK(KEY) : BITS & ~MASK(KEY)
  52 
  53 
  54 static JNF_CLASS_CACHE(jc_CPlatformWindow, "sun/lwawt/macosx/CPlatformWindow");
  55 
  56 @interface JavaResizeGrowBoxOverlayWindow : NSWindow { }
  57 
  58 @end
  59 
  60 @implementation JavaResizeGrowBoxOverlayWindow
  61 
  62 - (BOOL) accessibilityIsIgnored
  63 {
  64     return YES;
  65 }
  66 
  67 - (NSArray *)accessibilityChildrenAttribute
  68 {
  69     return nil;
  70 }
  71 @end
  72 
  73 @implementation AWTWindow
  74 
  75 @synthesize javaPlatformWindow;
  76 @synthesize javaMenuBar;
  77 @synthesize growBoxWindow;
  78 @synthesize javaMinSize;
  79 @synthesize javaMaxSize;
  80 @synthesize styleBits;
  81 
  82 - (void) updateMinMaxSize:(BOOL)resizable {
  83     if (resizable) {
  84         [self setMinSize:self.javaMinSize];
  85         [self setMaxSize:self.javaMaxSize];
  86     } else {
  87         NSRect currentFrame = [self frame];
  88         [self setMinSize:currentFrame.size];
  89         [self setMaxSize:currentFrame.size];
  90     }
  91 }
  92 
  93 // creates a new NSWindow style mask based on the _STYLE_PROP_BITMASK bits
  94 + (NSUInteger) styleMaskForStyleBits:(jint)styleBits {
  95     NSUInteger type = 0;
  96     if (IS(styleBits, DECORATED)) {
  97         type |= NSTitledWindowMask;
  98         if (IS(styleBits, CLOSEABLE))   type |= NSClosableWindowMask;
  99         if (IS(styleBits, MINIMIZABLE)) type |= NSMiniaturizableWindowMask;
 100         if (IS(styleBits, RESIZABLE))   type |= NSResizableWindowMask;
 101     } else {
 102         type |= NSBorderlessWindowMask;
 103     }
 104 
 105     if (IS(styleBits, TEXTURED))    type |= NSTexturedBackgroundWindowMask;
 106     if (IS(styleBits, UNIFIED))     type |= NSUnifiedTitleAndToolbarWindowMask;
 107     if (IS(styleBits, UTILITY))     type |= NSUtilityWindowMask;
 108     if (IS(styleBits, HUD))         type |= NSHUDWindowMask;
 109     if (IS(styleBits, SHEET))       type |= NSDocModalWindowMask;
 110 
 111     return type;
 112 }
 113 
 114 // updates _METHOD_PROP_BITMASK based properties on the window
 115 - (void) setPropertiesForStyleBits:(jint)bits mask:(jint)mask {
 116     if (IS(mask, RESIZABLE)) {
 117         BOOL resizable = IS(bits, RESIZABLE);
 118         [self updateMinMaxSize:resizable];
 119         [self setShowsResizeIndicator:resizable];
 120     }
 121 
 122     if (IS(mask, HAS_SHADOW)) {
 123         [self setHasShadow:IS(bits, HAS_SHADOW)];
 124     }
 125 
 126     if (IS(mask, ZOOMABLE)) {
 127         [[self standardWindowButton:NSWindowZoomButton] setEnabled:IS(bits, ZOOMABLE)];
 128     }
 129 
 130     if (IS(mask, ALWAYS_ON_TOP)) {
 131         [self setLevel:IS(bits, ALWAYS_ON_TOP) ? NSFloatingWindowLevel : NSNormalWindowLevel];
 132     }
 133 
 134     if (IS(mask, HIDES_ON_DEACTIVATE)) {
 135         [self setHidesOnDeactivate:IS(bits, HIDES_ON_DEACTIVATE)];
 136     }
 137 
 138     if (IS(mask, DRAGGABLE_BACKGROUND)) {
 139         [self setMovableByWindowBackground:IS(bits, DRAGGABLE_BACKGROUND)];
 140     }
 141 
 142     if (IS(mask, DOCUMENT_MODIFIED)) {
 143         [self setDocumentEdited:IS(bits, DOCUMENT_MODIFIED)];
 144     }
 145     
 146     if ([self respondsToSelector:@selector(toggleFullScreen:)]) {
 147         if (IS(mask, FULLSCREENABLE)) {
 148             [self setCollectionBehavior:(1 << 7) /*NSWindowCollectionBehaviorFullScreenPrimary*/];
 149         } else {
 150             [self setCollectionBehavior:NSWindowCollectionBehaviorDefault];
 151         }
 152     }
 153 
 154 }
 155 
 156 - (BOOL) shouldShowGrowBox {
 157     return isSnowLeopardOrLower() && IS(self.styleBits, RESIZABLE);
 158 }
 159 
 160 - (NSImage *) createGrowBoxImage {
 161     NSImage *image = [[NSImage alloc] initWithSize:NSMakeSize(12, 12)];
 162     JRSUIControlRef growBoxWidget = JRSUIControlCreate(FALSE);
 163     JRSUIControlSetWidget(growBoxWidget, kJRSUI_Widget_growBoxTextured);
 164     JRSUIControlSetWindowType(growBoxWidget, kJRSUI_WindowType_utility);
 165     JRSUIRendererRef renderer = JRSUIRendererCreate();
 166     [image lockFocus]; // sets current graphics context to that of the image
 167     JRSUIControlDraw(renderer, growBoxWidget, [[NSGraphicsContext currentContext] graphicsPort], CGRectMake(0, 1, 11, 11));
 168     [image unlockFocus];
 169     JRSUIRendererRelease(renderer);
 170     JRSUIControlRelease(growBoxWidget);
 171     return image;
 172 }
 173 
 174 - (id) initWithPlatformWindow:(JNFWeakJObjectWrapper *)platformWindow
 175                     styleBits:(jint)bits
 176                     frameRect:(NSRect)rect
 177                   contentView:(NSView *)view
 178 {
 179 AWT_ASSERT_APPKIT_THREAD;
 180 
 181     NSUInteger styleMask = [AWTWindow styleMaskForStyleBits:bits];
 182     NSRect contentRect = rect; //[NSWindow contentRectForFrameRect:rect styleMask:styleMask];
 183     if (contentRect.size.width <= 0.0) {
 184         contentRect.size.width = 1.0;
 185     }
 186     if (contentRect.size.height <= 0.0) {
 187         contentRect.size.height = 1.0;
 188     }
 189 
 190     self = [super initWithContentRect:contentRect
 191                             styleMask:styleMask
 192                               backing:NSBackingStoreBuffered
 193                                 defer:NO];
 194 
 195     if (self == nil) return nil; // no hope
 196 
 197     self.javaPlatformWindow = platformWindow;
 198     self.styleBits = bits;
 199     [self setPropertiesForStyleBits:styleBits mask:MASK(_METHOD_PROP_BITMASK)];
 200 
 201     [self setDelegate:self];
 202     [self setContentView:view];
 203     [self setInitialFirstResponder:view];
 204     [self setReleasedWhenClosed:NO];
 205     [self setPreservesContentDuringLiveResize:YES];
 206 
 207     if ([self shouldShowGrowBox]) {
 208         NSImage *growBoxImage = [self createGrowBoxImage];
 209         growBoxWindow = [[JavaResizeGrowBoxOverlayWindow alloc] initWithContentRect:NSMakeRect(0, 0, [growBoxImage size].width, [growBoxImage size].height) styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
 210         [self.growBoxWindow setIgnoresMouseEvents:YES];
 211         [self.growBoxWindow setOpaque:NO];
 212         [self.growBoxWindow setBackgroundColor:[NSColor clearColor]];
 213         [self.growBoxWindow setHasShadow:NO];
 214         [self.growBoxWindow setReleasedWhenClosed:NO];
 215 
 216         NSImageView *imageView = [[NSImageView alloc] initWithFrame:[self.growBoxWindow frame]];
 217         [imageView setEditable:NO];
 218         [imageView setAnimates:NO];
 219         [imageView setAllowsCutCopyPaste:NO];
 220         [self.growBoxWindow setContentView:imageView];
 221         [imageView setImage:growBoxImage];
 222         [growBoxImage release];
 223         [imageView release];
 224 
 225         [self addChildWindow:self.growBoxWindow ordered:NSWindowAbove];
 226         [self adjustGrowBoxWindow];
 227     } else growBoxWindow = nil;
 228 
 229     return self;
 230 }
 231 
 232 - (void) dealloc {
 233 AWT_ASSERT_APPKIT_THREAD;
 234 
 235     JNIEnv *env = [ThreadUtilities getJNIEnv];
 236     [self.javaPlatformWindow setJObject:nil withEnv:env];
 237     self.growBoxWindow = nil;
 238 
 239     [super dealloc];
 240 }
 241 
 242 
 243 // NSWindow overrides
 244 - (BOOL) canBecomeKeyWindow {
 245 AWT_ASSERT_APPKIT_THREAD;
 246     return IS(self.styleBits, SHOULD_BECOME_KEY);
 247 }
 248 
 249 - (BOOL) canBecomeMainWindow {
 250 AWT_ASSERT_APPKIT_THREAD;
 251     return IS(self.styleBits, SHOULD_BECOME_MAIN);
 252 }
 253 
 254 - (BOOL) worksWhenModal {
 255 AWT_ASSERT_APPKIT_THREAD;
 256     return IS(self.styleBits, MODAL_EXCLUDED);
 257 }
 258 
 259 
 260 // Gesture support
 261 - (void)postGesture:(NSEvent *)event as:(jint)type a:(jdouble)a b:(jdouble)b {
 262 AWT_ASSERT_APPKIT_THREAD;
 263 
 264     JNIEnv *env = [ThreadUtilities getJNIEnv];
 265     jobject platformWindow = [self.javaPlatformWindow jObjectWithEnv:env];
 266     if (platformWindow != NULL) {
 267         // extract the target AWT Window object out of the CPlatformWindow
 268         static JNF_MEMBER_CACHE(jf_target, jc_CPlatformWindow, "target", "Ljava/awt/Window;");
 269         jobject awtWindow = JNFGetObjectField(env, platformWindow, jf_target);
 270         if (awtWindow != NULL) {
 271             // translate the point into Java coordinates
 272             NSPoint loc = [event locationInWindow];
 273             loc.y = [self frame].size.height - loc.y;
 274 
 275             // send up to the GestureHandler to recursively dispatch on the AWT event thread
 276             static JNF_CLASS_CACHE(jc_GestureHandler, "com/apple/eawt/event/GestureHandler");
 277             static JNF_STATIC_MEMBER_CACHE(sjm_handleGestureFromNative, jc_GestureHandler, "handleGestureFromNative", "(Ljava/awt/Window;IDDDD)V");
 278             JNFCallStaticVoidMethod(env, sjm_handleGestureFromNative, awtWindow, type, (jdouble)loc.x, (jdouble)loc.y, (jdouble)a, (jdouble)b);
 279             (*env)->DeleteLocalRef(env, awtWindow); 
 280         }
 281         (*env)->DeleteLocalRef(env, platformWindow);
 282     }
 283 }
 284 
 285 - (void)beginGestureWithEvent:(NSEvent *)event {
 286     [self postGesture:event
 287                    as:com_apple_eawt_event_GestureHandler_PHASE
 288                     a:-1.0
 289                     b:0.0];
 290 }
 291 
 292 - (void)endGestureWithEvent:(NSEvent *)event {
 293     [self postGesture:event
 294                    as:com_apple_eawt_event_GestureHandler_PHASE
 295                     a:1.0
 296                     b:0.0];
 297 }
 298 
 299 - (void)magnifyWithEvent:(NSEvent *)event {
 300     [self postGesture:event
 301                    as:com_apple_eawt_event_GestureHandler_MAGNIFY
 302                     a:[event magnification]
 303                     b:0.0];
 304 }
 305 
 306 - (void)rotateWithEvent:(NSEvent *)event {
 307     [self postGesture:event
 308                    as:com_apple_eawt_event_GestureHandler_ROTATE
 309                     a:[event rotation]
 310                     b:0.0];
 311 }
 312 
 313 - (void)swipeWithEvent:(NSEvent *)event {
 314     [self postGesture:event
 315                    as:com_apple_eawt_event_GestureHandler_SWIPE
 316                     a:[event deltaX]
 317                     b:[event deltaY]];
 318 }
 319 
 320 
 321 // NSWindowDelegate methods
 322 
 323 - (void) adjustGrowBoxWindow {
 324     if (self.growBoxWindow != nil) {
 325         NSRect parentRect = [self frame];
 326         parentRect.origin.x += (parentRect.size.width - [self.growBoxWindow frame].size.width);
 327         [self.growBoxWindow setFrameOrigin:parentRect.origin];
 328     }
 329 }
 330 
 331 - (void) _deliverMoveResizeEvent {
 332 AWT_ASSERT_APPKIT_THREAD;
 333 
 334     // deliver the event if this is a user-initiated live resize or as a side-effect
 335     // of a Java initiated resize, because AppKit can override the bounds and force
 336     // the bounds of the window to avoid the Dock or remain on screen.
 337     [AWTToolkit eventCountPlusPlus];
 338     JNIEnv *env = [ThreadUtilities getJNIEnv];
 339     jobject platformWindow = [self.javaPlatformWindow jObjectWithEnv:env];
 340     if (platformWindow == NULL) {
 341         // TODO: create generic AWT assert
 342     }
 343 
 344     [self adjustGrowBoxWindow];
 345 
 346     NSRect frame = ConvertNSScreenRect(env, [self frame]);
 347 
 348     static JNF_MEMBER_CACHE(jm_deliverMoveResizeEvent, jc_CPlatformWindow, "deliverMoveResizeEvent", "(IIII)V");
 349     JNFCallVoidMethod(env, platformWindow, jm_deliverMoveResizeEvent,
 350                       (jint)frame.origin.x,
 351                       (jint)frame.origin.y,
 352                       (jint)frame.size.width,
 353                       (jint)frame.size.height);
 354     (*env)->DeleteLocalRef(env, platformWindow);
 355 }
 356 
 357 - (void)windowDidMove:(NSNotification *)notification {
 358 AWT_ASSERT_APPKIT_THREAD;
 359 
 360     [self _deliverMoveResizeEvent];
 361 }
 362 
 363 - (void)windowDidResize:(NSNotification *)notification {
 364 AWT_ASSERT_APPKIT_THREAD;
 365 
 366     [self _deliverMoveResizeEvent];
 367 }
 368 
 369 - (void)windowDidExpose:(NSNotification *)notification {
 370 AWT_ASSERT_APPKIT_THREAD;
 371 
 372     [AWTToolkit eventCountPlusPlus];
 373     // TODO: don't see this callback invoked anytime so we track
 374     // window exposing in _setVisible:(BOOL)
 375 }
 376 
 377 - (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)proposedFrame {
 378 AWT_ASSERT_APPKIT_THREAD;
 379 
 380     [AWTToolkit eventCountPlusPlus];
 381     JNIEnv *env = [ThreadUtilities getJNIEnv];
 382     jobject platformWindow = [self.javaPlatformWindow jObjectWithEnv:env];
 383     if (platformWindow != NULL) {
 384         static JNF_MEMBER_CACHE(jm_deliverZoom, jc_CPlatformWindow, "deliverZoom", "(Z)V");
 385         JNFCallVoidMethod(env, platformWindow, jm_deliverZoom, ![window isZoomed]);
 386         (*env)->DeleteLocalRef(env, platformWindow);
 387     }
 388     return YES;
 389 }
 390 
 391 - (void) _deliverIconify:(BOOL)iconify {
 392 AWT_ASSERT_APPKIT_THREAD;
 393 
 394     [AWTToolkit eventCountPlusPlus];
 395     JNIEnv *env = [ThreadUtilities getJNIEnv];
 396     jobject platformWindow = [self.javaPlatformWindow jObjectWithEnv:env];
 397     if (platformWindow != NULL) {
 398         static JNF_MEMBER_CACHE(jm_deliverIconify, jc_CPlatformWindow, "deliverIconify", "(Z)V");
 399         JNFCallVoidMethod(env, platformWindow, jm_deliverIconify, iconify);
 400         (*env)->DeleteLocalRef(env, platformWindow);
 401     }
 402 }
 403 
 404 - (void)windowDidMiniaturize:(NSNotification *)notification {
 405 AWT_ASSERT_APPKIT_THREAD;
 406 
 407     [self _deliverIconify:JNI_TRUE];
 408 }
 409 
 410 - (void)windowDidDeminiaturize:(NSNotification *)notification {
 411 AWT_ASSERT_APPKIT_THREAD;
 412 
 413     [self _deliverIconify:JNI_FALSE];
 414 }
 415 
 416 - (void) _deliverWindowFocusEvent:(BOOL)focused {
 417 //AWT_ASSERT_APPKIT_THREAD;
 418 
 419     JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
 420     jobject platformWindow = [self.javaPlatformWindow jObjectWithEnv:env];
 421     if (platformWindow != NULL) {
 422         static JNF_MEMBER_CACHE(jm_deliverWindowFocusEvent, jc_CPlatformWindow, "deliverWindowFocusEvent", "(Z)V");
 423         JNFCallVoidMethod(env, platformWindow, jm_deliverWindowFocusEvent, (jboolean)focused);
 424         (*env)->DeleteLocalRef(env, platformWindow);
 425     }
 426 }
 427 
 428 
 429 - (void) windowDidBecomeKey: (NSNotification *) notification {
 430 AWT_ASSERT_APPKIT_THREAD;       
 431     [AWTToolkit eventCountPlusPlus];
 432     [CMenuBar activate:self.javaMenuBar modallyDisabled:NO];
 433     [self _deliverWindowFocusEvent:YES];
 434 }
 435 
 436 - (void) windowDidResignKey: (NSNotification *) notification {
 437     // TODO: check why sometimes at start is invoked *not* on AppKit main thread.
 438 AWT_ASSERT_APPKIT_THREAD;
 439     [AWTToolkit eventCountPlusPlus];
 440     [self.javaMenuBar deactivate];
 441     [self _deliverWindowFocusEvent:NO];
 442 }
 443 
 444 - (void) windowDidBecomeMain: (NSNotification *) notification {
 445 AWT_ASSERT_APPKIT_THREAD;
 446     [AWTToolkit eventCountPlusPlus];
 447 
 448     JNIEnv *env = [ThreadUtilities getJNIEnv];
 449     jobject platformWindow = [self.javaPlatformWindow jObjectWithEnv:env];
 450     if (platformWindow != NULL) {
 451         static JNF_MEMBER_CACHE(jm_windowDidBecomeMain, jc_CPlatformWindow, "windowDidBecomeMain", "()V");
 452         JNFCallVoidMethod(env, platformWindow, jm_windowDidBecomeMain);
 453         (*env)->DeleteLocalRef(env, platformWindow);
 454     }
 455 }
 456 
 457 - (BOOL)windowShouldClose:(id)sender {
 458 AWT_ASSERT_APPKIT_THREAD;
 459     [AWTToolkit eventCountPlusPlus];
 460     JNIEnv *env = [ThreadUtilities getJNIEnv];
 461     jobject platformWindow = [self.javaPlatformWindow jObjectWithEnv:env];
 462     if (platformWindow != NULL) {
 463         static JNF_MEMBER_CACHE(jm_deliverWindowClosingEvent, jc_CPlatformWindow, "deliverWindowClosingEvent", "()V");
 464         JNFCallVoidMethod(env, platformWindow, jm_deliverWindowClosingEvent);    
 465         (*env)->DeleteLocalRef(env, platformWindow);
 466     }
 467     // The window will be closed (if allowed) as result of sending Java event
 468     return NO;
 469 }
 470 
 471 
 472 - (void)_notifyFullScreenOp:(jint)op withEnv:(JNIEnv *)env {
 473     static JNF_CLASS_CACHE(jc_FullScreenHandler, "com/apple/eawt/FullScreenHandler");
 474     static JNF_STATIC_MEMBER_CACHE(jm_notifyFullScreenOperation, jc_FullScreenHandler, "handleFullScreenEventFromNative", "(Ljava/awt/Window;I)V");
 475     static JNF_MEMBER_CACHE(jf_target, jc_CPlatformWindow, "target", "Ljava/awt/Window;");
 476     jobject platformWindow = [self.javaPlatformWindow jObjectWithEnv:env];
 477     if (platformWindow != NULL) {
 478         jobject awtWindow = JNFGetObjectField(env, platformWindow, jf_target);
 479         if (awtWindow != NULL) {
 480             JNFCallStaticVoidMethod(env, jm_notifyFullScreenOperation, awtWindow, op);
 481             (*env)->DeleteLocalRef(env, awtWindow);
 482         }
 483         (*env)->DeleteLocalRef(env, platformWindow);
 484     }
 485 }
 486 
 487 
 488 - (void)windowWillEnterFullScreen:(NSNotification *)notification {
 489     static JNF_MEMBER_CACHE(jm_windowWillEnterFullScreen, jc_CPlatformWindow, "windowWillEnterFullScreen", "()V");
 490     JNIEnv *env = [ThreadUtilities getJNIEnv];
 491     jobject platformWindow = [self.javaPlatformWindow jObjectWithEnv:env];
 492     if (platformWindow != NULL) {
 493         JNFCallVoidMethod(env, platformWindow, jm_windowWillEnterFullScreen);
 494         [self _notifyFullScreenOp:com_apple_eawt_FullScreenHandler_FULLSCREEN_WILL_ENTER withEnv:env];
 495         (*env)->DeleteLocalRef(env, platformWindow);
 496     }
 497 }
 498 
 499 - (void)windowDidEnterFullScreen:(NSNotification *)notification {
 500     static JNF_MEMBER_CACHE(jm_windowDidEnterFullScreen, jc_CPlatformWindow, "windowDidEnterFullScreen", "()V");
 501     JNIEnv *env = [ThreadUtilities getJNIEnv];
 502     jobject platformWindow = [self.javaPlatformWindow jObjectWithEnv:env];
 503     if (platformWindow != NULL) {
 504         JNFCallVoidMethod(env, platformWindow, jm_windowDidEnterFullScreen);
 505         [self _notifyFullScreenOp:com_apple_eawt_FullScreenHandler_FULLSCREEN_DID_ENTER withEnv:env];
 506         (*env)->DeleteLocalRef(env, platformWindow);
 507     }
 508 }
 509 
 510 - (void)windowWillExitFullScreen:(NSNotification *)notification {
 511     static JNF_MEMBER_CACHE(jm_windowWillExitFullScreen, jc_CPlatformWindow, "windowWillExitFullScreen", "()V");
 512     JNIEnv *env = [ThreadUtilities getJNIEnv];
 513     jobject platformWindow = [self.javaPlatformWindow jObjectWithEnv:env];
 514     if (platformWindow != NULL) {
 515         JNFCallVoidMethod(env, platformWindow, jm_windowWillExitFullScreen);
 516         [self _notifyFullScreenOp:com_apple_eawt_FullScreenHandler_FULLSCREEN_WILL_EXIT withEnv:env];
 517         (*env)->DeleteLocalRef(env, platformWindow);
 518     }
 519 }
 520 
 521 - (void)windowDidExitFullScreen:(NSNotification *)notification {
 522     static JNF_MEMBER_CACHE(jm_windowDidExitFullScreen, jc_CPlatformWindow, "windowDidExitFullScreen", "()V");
 523     JNIEnv *env = [ThreadUtilities getJNIEnv];
 524     jobject platformWindow = [self.javaPlatformWindow jObjectWithEnv:env];
 525     if (platformWindow != NULL) {
 526         JNFCallVoidMethod(env, platformWindow, jm_windowDidExitFullScreen);
 527         [self _notifyFullScreenOp:com_apple_eawt_FullScreenHandler_FULLSCREEN_DID_EXIT withEnv:env];
 528         (*env)->DeleteLocalRef(env, platformWindow);
 529     }
 530 }
 531 
 532 - (void)sendEvent:(NSEvent *)event {
 533         if ([event type] == NSLeftMouseDown || [event type] == NSRightMouseDown || [event type] == NSOtherMouseDown) {
 534                         
 535             NSPoint p = [NSEvent mouseLocation];
 536             NSRect frame = [self frame];
 537             NSRect contentRect = [self contentRectForFrameRect:frame];
 538                         
 539             // Check if the click happened in the non-client area (title bar) 
 540             if (p.y >= (frame.origin.y + contentRect.size.height)) {                         
 541                 JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
 542                 jobject platformWindow = [self.javaPlatformWindow jObjectWithEnv:env];
 543                 // Currently, no need to deliver the whole NSEvent.
 544                 static JNF_MEMBER_CACHE(jm_deliverNCMouseDown, jc_CPlatformWindow, "deliverNCMouseDown", "()V");
 545                 JNFCallVoidMethod(env, platformWindow, jm_deliverNCMouseDown);
 546             }
 547         }
 548         [super sendEvent:event];
 549 }
 550 @end // AWTWindow
 551 
 552 
 553 /*
 554  * Class:     sun_lwawt_macosx_CPlatformWindow
 555  * Method:    nativeCreateNSWindow
 556  * Signature: (JJIIII)J
 557  */
 558 JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeCreateNSWindow
 559 (JNIEnv *env, jobject obj, jlong contentViewPtr, jlong styleBits, jdouble x, jdouble y, jdouble w, jdouble h)
 560 {
 561     __block AWTWindow *window = nil;
 562 
 563 JNF_COCOA_ENTER(env);
 564 AWT_ASSERT_NOT_APPKIT_THREAD;
 565 
 566     JNFWeakJObjectWrapper *platformWindow = [JNFWeakJObjectWrapper wrapperWithJObject:obj withEnv:env];
 567     NSView *contentView = OBJC(contentViewPtr);
 568     NSRect frameRect = NSMakeRect(x, y, w, h);
 569 
 570     [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){
 571         AWT_ASSERT_APPKIT_THREAD;
 572 
 573         window = [[AWTWindow alloc] initWithPlatformWindow:platformWindow
 574                                                   styleBits:styleBits
 575                                                   frameRect:frameRect
 576                                                 contentView:contentView];
 577 
 578         if (window) CFRetain(window);
 579         [window release]; // GC
 580     }];
 581 
 582 JNF_COCOA_EXIT(env);
 583 
 584     return ptr_to_jlong(window);
 585 }
 586 
 587 /*
 588  * Class:     sun_lwawt_macosx_CPlatformWindow
 589  * Method:    nativeSetNSWindowStyleBits
 590  * Signature: (JII)V
 591  */
 592 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeSetNSWindowStyleBits
 593 (JNIEnv *env, jclass clazz, jlong windowPtr, jint mask, jint bits)
 594 {
 595 JNF_COCOA_ENTER(env);
 596 AWT_ASSERT_NOT_APPKIT_THREAD;
 597 
 598     AWTWindow *window = OBJC(windowPtr);
 599     [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){
 600         AWT_ASSERT_APPKIT_THREAD;
 601 
 602         // scans the bit field, and only updates the values requested by the mask
 603         // (this implicity handles the _CALLBACK_PROP_BITMASK case, since those are passive reads)
 604         jint newBits = window.styleBits & ~mask | bits & mask;
 605 
 606         // resets the NSWindow's style mask if the mask intersects any of those bits
 607         if (mask & MASK(_STYLE_PROP_BITMASK)) {
 608             [window setStyleMask:[AWTWindow styleMaskForStyleBits:newBits]];
 609         }
 610 
 611         // calls methods on NSWindow to change other properties, based on the mask
 612         if (mask & MASK(_METHOD_PROP_BITMASK)) {
 613             [window setPropertiesForStyleBits:bits mask:mask];
 614         }
 615 
 616         window.styleBits = newBits;
 617     }];
 618 
 619 JNF_COCOA_EXIT(env);
 620 }
 621 
 622 /*
 623  * Class:     sun_lwawt_macosx_CPlatformWindow
 624  * Method:    nativeSetNSWindowMenuBar
 625  * Signature: (JJ)V
 626  */
 627 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeSetNSWindowMenuBar
 628 (JNIEnv *env, jclass clazz, jlong windowPtr, jlong menuBarPtr)
 629 {
 630 JNF_COCOA_ENTER(env);
 631 AWT_ASSERT_NOT_APPKIT_THREAD;
 632 
 633     AWTWindow *window = OBJC(windowPtr);
 634     CMenuBar *menuBar = OBJC(menuBarPtr);
 635     [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){
 636         AWT_ASSERT_APPKIT_THREAD;
 637 
 638         if ([window isKeyWindow]) [window.javaMenuBar deactivate];
 639         window.javaMenuBar = menuBar;
 640 
 641         // if ([self isKeyWindow]) {
 642         [CMenuBar activate:window.javaMenuBar modallyDisabled:NO];
 643         // }
 644     }];
 645 
 646 JNF_COCOA_EXIT(env);
 647 }
 648 
 649 /*
 650  * Class:     sun_lwawt_macosx_CPlatformWindow
 651  * Method:    nativeGetNSWindowInsets
 652  * Signature: (J)Ljava/awt/Insets;
 653  */
 654 JNIEXPORT jobject JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeGetNSWindowInsets
 655 (JNIEnv *env, jclass clazz, jlong windowPtr)
 656 {
 657     jobject ret = NULL;
 658 
 659 JNF_COCOA_ENTER(env);
 660 AWT_ASSERT_NOT_APPKIT_THREAD;
 661 
 662     AWTWindow *window = OBJC(windowPtr);
 663     __block NSRect contentRect = NSZeroRect;
 664     __block NSRect frame = NSZeroRect;
 665 
 666     [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){
 667         AWT_ASSERT_APPKIT_THREAD;
 668 
 669         frame = [window frame];
 670         contentRect = [NSWindow contentRectForFrameRect:frame styleMask:[window styleMask]];
 671     }];
 672 
 673     jint top = (jint)(frame.size.height - contentRect.size.height);
 674     jint left = (jint)(contentRect.origin.x - frame.origin.x);
 675     jint bottom = (jint)(contentRect.origin.y - frame.origin.y);
 676     jint right = (jint)(frame.size.width - (contentRect.size.width + left));
 677 
 678     static JNF_CLASS_CACHE(jc_Insets, "java/awt/Insets");
 679     static JNF_CTOR_CACHE(jc_Insets_ctor, jc_Insets, "(IIII)V");
 680     ret = JNFNewObject(env, jc_Insets_ctor, top, left, bottom, right);
 681 
 682 JNF_COCOA_EXIT(env);
 683     return ret;
 684 }
 685 
 686 /*
 687  * Class:     sun_lwawt_macosx_CPlatformWindow
 688  * Method:    nativeSetNSWindowBounds
 689  * Signature: (JDDDD)V
 690  */
 691 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeSetNSWindowBounds
 692 (JNIEnv *env, jclass clazz, jlong windowPtr, jdouble originX, jdouble originY, jdouble width, jdouble height)
 693 {
 694 JNF_COCOA_ENTER(env);
 695 AWT_ASSERT_NOT_APPKIT_THREAD;
 696 
 697     NSRect jrect = NSMakeRect(originX, originY, width, height);
 698 
 699     // TODO: not sure we need displayIfNeeded message in our view
 700     AWTWindow *window = OBJC(windowPtr);
 701     [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){
 702         AWT_ASSERT_APPKIT_THREAD;
 703 
 704         NSRect rect = ConvertNSScreenRect(NULL, jrect);
 705         [window setFrame:rect display:YES];
 706 
 707         // only start tracking events if pointer is above the toplevel
 708         // TODO: should post an Entered event if YES.
 709         NSPoint mLocation = [NSEvent mouseLocation];
 710         [window setAcceptsMouseMovedEvents:NSPointInRect(mLocation, rect)];
 711 
 712         // ensure we repaint the whole window after the resize operation
 713         // (this will also re-enable screen updates, which were disabled above)
 714         // TODO: send PaintEvent
 715     }];
 716 
 717 JNF_COCOA_EXIT(env);
 718 }
 719 
 720 /*
 721  * Class:     sun_lwawt_macosx_CPlatformWindow
 722  * Method:    nativeSetNSWindowMinMax
 723  * Signature: (JDDDD)V
 724  */
 725 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeSetNSWindowMinMax
 726 (JNIEnv *env, jclass clazz, jlong windowPtr, jdouble minW, jdouble minH, jdouble maxW, jdouble maxH)
 727 {
 728 JNF_COCOA_ENTER(env);
 729 AWT_ASSERT_NOT_APPKIT_THREAD;
 730 
 731     if (minW < 1) minW = 1;
 732     if (minH < 1) minH = 1;
 733     if (maxW < 1) maxW = 1;
 734     if (maxH < 1) maxH = 1;
 735 
 736     NSSize min = { minW, minH };
 737     NSSize max = { maxW, maxH };
 738 
 739     AWTWindow *window = OBJC(windowPtr);
 740     [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){
 741         AWT_ASSERT_APPKIT_THREAD;
 742 
 743         window.javaMinSize = min;
 744         window.javaMaxSize = max;
 745         [window updateMinMaxSize:IS(window.styleBits, RESIZABLE)];
 746     }];
 747 
 748 JNF_COCOA_EXIT(env);
 749 }
 750 
 751 /*
 752  * Class:     sun_lwawt_macosx_CPlatformWindow
 753  * Method:    nativePushNSWindowToBack
 754  * Signature: (J)V
 755  */
 756 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativePushNSWindowToBack
 757 (JNIEnv *env, jclass clazz, jlong windowPtr)
 758 {
 759 JNF_COCOA_ENTER(env);
 760 AWT_ASSERT_NOT_APPKIT_THREAD;
 761 
 762     AWTWindow *window = OBJC(windowPtr);
 763     [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){
 764         AWT_ASSERT_APPKIT_THREAD;
 765 
 766         [window orderBack:nil];
 767     }];
 768 
 769 JNF_COCOA_EXIT(env);
 770 }
 771 
 772 /*
 773  * Class:     sun_lwawt_macosx_CPlatformWindow
 774  * Method:    nativePushNSWindowToFront
 775  * Signature: (J)V
 776  */
 777 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativePushNSWindowToFront
 778 (JNIEnv *env, jclass clazz, jlong windowPtr)
 779 {
 780 JNF_COCOA_ENTER(env);
 781 AWT_ASSERT_NOT_APPKIT_THREAD;
 782 
 783     AWTWindow *window = OBJC(windowPtr);
 784     [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){
 785         AWT_ASSERT_APPKIT_THREAD;
 786 
 787         if (![window isKeyWindow]) {
 788             [window makeKeyAndOrderFront:window];
 789         } else {
 790             [window orderFront:window];
 791         }
 792     }];
 793 
 794 JNF_COCOA_EXIT(env);
 795 }
 796 
 797 /*
 798  * Class:     sun_lwawt_macosx_CPlatformWindow
 799  * Method:    nativeSetNSWindowTitle
 800  * Signature: (JLjava/lang/String;)V
 801  */
 802 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeSetNSWindowTitle
 803 (JNIEnv *env, jclass clazz, jlong windowPtr, jstring jtitle)
 804 {
 805 JNF_COCOA_ENTER(env);
 806 AWT_ASSERT_NOT_APPKIT_THREAD;
 807 
 808     AWTWindow *window = OBJC(windowPtr);
 809     [window performSelectorOnMainThread:@selector(setTitle:)
 810                               withObject:JNFJavaToNSString(env, jtitle)
 811                            waitUntilDone:NO];
 812 
 813 JNF_COCOA_EXIT(env);
 814 }
 815 
 816 /*
 817  * Class:     sun_lwawt_macosx_CPlatformWindow
 818  * Method:    nativeSetNSWindowAlpha
 819  * Signature: (JF)V
 820  */
 821 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeSetNSWindowAlpha
 822 (JNIEnv *env, jclass clazz, jlong windowPtr, jfloat alpha)
 823 {
 824 JNF_COCOA_ENTER(env);
 825 AWT_ASSERT_NOT_APPKIT_THREAD;
 826 
 827     AWTWindow *window = OBJC(windowPtr);
 828     [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){
 829         AWT_ASSERT_APPKIT_THREAD;
 830 
 831         [window setAlphaValue:alpha];
 832         [window.growBoxWindow setAlphaValue:alpha];
 833     }];
 834 
 835 JNF_COCOA_EXIT(env);
 836 }
 837 
 838 /*
 839  * Class:     sun_lwawt_macosx_CPlatformWindow
 840  * Method:    nativeRevalidateNSWindowShadow
 841  * Signature: (J)V
 842  */
 843 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeRevalidateNSWindowShadow
 844 (JNIEnv *env, jclass clazz, jlong windowPtr)
 845 {
 846 JNF_COCOA_ENTER(env);
 847 AWT_ASSERT_NOT_APPKIT_THREAD;
 848 
 849     AWTWindow *window = OBJC(windowPtr);
 850     [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){
 851         AWT_ASSERT_APPKIT_THREAD;
 852 
 853         [window invalidateShadow];
 854     }];
 855 
 856 JNF_COCOA_EXIT(env);
 857 }
 858 
 859 /*
 860  * Class:     sun_lwawt_macosx_CPlatformWindow
 861  * Method:    nativeScreenOn_AppKitThread
 862  * Signature: (J)I
 863  */
 864 JNIEXPORT jint JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeScreenOn_1AppKitThread
 865 (JNIEnv *env, jclass clazz, jlong windowPtr)
 866 {
 867     jint ret = 0;
 868 
 869 JNF_COCOA_ENTER(env);
 870 AWT_ASSERT_APPKIT_THREAD;
 871 
 872     AWTWindow *window = OBJC(windowPtr);
 873     NSDictionary *props = [[window screen] deviceDescription];
 874     ret = [[props objectForKey:@"NSScreenNumber"] intValue];
 875 
 876 JNF_COCOA_EXIT(env);
 877 
 878     return ret;
 879 }
 880 
 881 /*
 882  * Class:     sun_lwawt_macosx_CPlatformWindow
 883  * Method:    nativeSetNSWindowMinimizedIcon
 884  * Signature: (JJ)V
 885  */
 886 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeSetNSWindowMinimizedIcon
 887 (JNIEnv *env, jclass clazz, jlong windowPtr, jlong nsImagePtr)
 888 {
 889 JNF_COCOA_ENTER(env);
 890 AWT_ASSERT_NOT_APPKIT_THREAD;
 891 
 892     AWTWindow *window = OBJC(windowPtr);
 893     NSImage *image = OBJC(nsImagePtr);
 894     [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){
 895         AWT_ASSERT_APPKIT_THREAD;
 896 
 897         [window setMiniwindowImage:image];
 898     }];
 899 
 900 JNF_COCOA_EXIT(env);
 901 }
 902 
 903 /*
 904  * Class:     sun_lwawt_macosx_CPlatformWindow
 905  * Method:    nativeSetNSWindowRepresentedFilename
 906  * Signature: (JLjava/lang/String;)V
 907  */
 908 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeSetNSWindowRepresentedFilename
 909 (JNIEnv *env, jclass clazz, jlong windowPtr, jstring filename)
 910 {
 911 JNF_COCOA_ENTER(env);
 912 AWT_ASSERT_NOT_APPKIT_THREAD;
 913 
 914     AWTWindow *window = OBJC(windowPtr);
 915     NSURL *url = (filename == NULL) ? nil : [NSURL fileURLWithPath:JNFNormalizedNSStringForPath(env, filename)];
 916     [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){
 917         AWT_ASSERT_APPKIT_THREAD;
 918 
 919         [window setRepresentedURL:url];
 920     }];
 921 
 922 JNF_COCOA_EXIT(env);
 923 }
 924 
 925 /*
 926  * Class:     sun_lwawt_macosx_CPlatformWindow
 927  * Method:    nativeSetNSWindowSecurityWarningPositioning
 928  * Signature: (JDDFF)V
 929  */
 930 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeSetNSWindowSecurityWarningPositioning
 931 (JNIEnv *env, jclass clazz, jlong windowPtr, jdouble x, jdouble y, jfloat biasX, jfloat biasY)
 932 {
 933 JNF_COCOA_ENTER(env);
 934 AWT_ASSERT_NOT_APPKIT_THREAD;
 935 
 936     [JNFException raise:env as:kRuntimeException reason:"unimplemented"];
 937 
 938 JNF_COCOA_EXIT(env);
 939 }
 940 
 941 /*
 942  * Class:     sun_lwawt_macosx_CPlatformWindow
 943  * Method:    nativeGetScreenNSWindowIsOn_AppKitThread
 944  * Signature: (J)I
 945  */
 946 JNIEXPORT jint JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeGetScreenNSWindowIsOn_1AppKitThread
 947 (JNIEnv *env, jclass clazz, jlong windowPtr)
 948 {
 949     jint index = -1;
 950 
 951 JNF_COCOA_ENTER(env);
 952 AWT_ASSERT_APPKIT_THREAD;
 953 
 954     AWTWindow *window = OBJC(windowPtr);
 955     NSScreen* screen = [window screen];
 956 
 957     //+++gdb NOTE: This is using a linear search of the screens. If it should
 958     //  prove to be a bottleneck, this can definitely be improved. However,
 959     //  many screens should prove to be the exception, rather than the rule.
 960     NSArray* screens = [NSScreen screens];
 961     NSUInteger i;
 962     for (i = 0; i < [screens count]; i++)
 963     {
 964         if ([[screens objectAtIndex:i] isEqualTo:screen])
 965         {
 966             index = i;
 967             break;
 968         }
 969     }
 970 
 971 JNF_COCOA_EXIT(env);
 972     return 1;
 973 }
 974 
 975 
 976 /*
 977  * Class:     sun_lwawt_macosx_CPlatformWindow
 978  * Method:    _toggleFullScreenMode
 979  * Signature: (J)V
 980  */
 981 JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow__1toggleFullScreenMode
 982 (JNIEnv *env, jobject peer, jlong windowPtr)
 983 {
 984 JNF_COCOA_ENTER(env);
 985 
 986     AWTWindow *window = OBJC(windowPtr);
 987     SEL toggleFullScreenSelector = @selector(toggleFullScreen:);
 988     if (![window respondsToSelector:toggleFullScreenSelector]) return;
 989 
 990     [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){
 991         [window performSelector:toggleFullScreenSelector withObject:nil];
 992     }];
 993 
 994 JNF_COCOA_EXIT(env);
 995 }
 996 
 997 JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_CMouseInfoPeer_nativeIsWindowUnderMouse
 998 (JNIEnv *env, jclass clazz, jlong windowPtr)
 999 {
1000     __block jboolean underMouse = JNI_FALSE;
1001 
1002 JNF_COCOA_ENTER(env);
1003 AWT_ASSERT_NOT_APPKIT_THREAD;
1004 
1005     AWTWindow *aWindow = OBJC(windowPtr);
1006     [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^() {
1007         AWT_ASSERT_APPKIT_THREAD;
1008 
1009         NSPoint pt = [aWindow mouseLocationOutsideOfEventStream];
1010         underMouse = [[aWindow contentView] hitTest:pt] != nil;
1011     }];
1012 
1013 JNF_COCOA_EXIT(env);
1014 
1015     return underMouse;
1016 }