1 /*
   2  * Copyright (c) 2011, 2015, 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 "common.h"
  27 #import "com_sun_glass_events_ViewEvent.h"
  28 #import "com_sun_glass_events_MouseEvent.h"
  29 #import "com_sun_glass_events_KeyEvent.h"
  30 #import "com_sun_glass_events_DndEvent.h"
  31 #import "com_sun_glass_events_SwipeGesture.h"
  32 #import "com_sun_glass_ui_Clipboard.h"
  33 #import "com_sun_glass_ui_mac_MacGestureSupport.h"
  34 
  35 #import "GlassMacros.h"
  36 #import "GlassViewDelegate.h"
  37 #import "GlassKey.h"
  38 #import "GlassScreen.h"
  39 #import "GlassWindow.h"
  40 #import "GlassApplication.h"
  41 #import "GlassLayer3D.h"
  42 #import "GlassNSEvent.h"
  43 #import "GlassPasteboard.h"
  44 #import "GlassHelper.h"
  45 #import "GlassStatics.h"
  46 #import "GlassPasteboard.h"
  47 #import "GlassTouches.h"
  48 
  49 //#define VERBOSE
  50 #ifndef VERBOSE
  51     #define LOG(MSG, ...)
  52 #else
  53     #define LOG(MSG, ...) GLASS_LOG(MSG, ## __VA_ARGS__);
  54 #endif
  55 
  56 //#define DNDVERBOSE
  57 #ifndef DNDVERBOSE
  58     #define DNDLOG(MSG, ...)
  59 #else
  60     #define DNDLOG(MSG, ...) GLASS_LOG(MSG, ## __VA_ARGS__);
  61 #endif
  62 
  63 // used Safari as a reference while dragging large images
  64 #define MAX_DRAG_SIZE 400
  65 
  66 // explicitly set image size
  67 #define DEFAULT_DRAG_SIZE 64
  68 
  69 // Tracks pressed modifier keys
  70 static NSUInteger s_modifierFlags = 0;
  71 
  72 // Extracted from class-dump utility output for NSEvent class
  73 @interface NSEvent (hidden)
  74 
  75 - (long long)_scrollPhase;
  76 - (unsigned long long)momentumPhase;
  77 @end
  78 
  79 
  80 static jboolean isInertialScroll(NSEvent *theEvent)
  81 {
  82     enum
  83     {
  84         SelectorNotSet,
  85         MomentumPhaseSelector,
  86         ScrollPhaseSelector,
  87         SelectorNotAvailable
  88     };
  89 
  90     static int selector = SelectorNotSet;
  91 
  92     switch (selector)
  93     {
  94         case SelectorNotSet:
  95             if ([theEvent respondsToSelector:@selector(momentumPhase)])
  96             {   // Available from OS X 10.7
  97                 selector = MomentumPhaseSelector;
  98             }
  99             else if ([theEvent respondsToSelector:@selector(_scrollPhase)])
 100             {   // Available in OS X 10.6 and earlier. Deprecated in OS X 10.7
 101                 selector = ScrollPhaseSelector;
 102             }
 103             else
 104             {
 105                 selector = SelectorNotAvailable;
 106             }
 107             return isInertialScroll(theEvent);
 108 
 109         case MomentumPhaseSelector:
 110             return ([theEvent momentumPhase] != 0);
 111 
 112         case ScrollPhaseSelector:
 113             return ([theEvent _scrollPhase] != 0);
 114     }
 115 
 116     return JNI_FALSE;
 117 }
 118 
 119 
 120 static jint getSwipeDirFromEvent(NSEvent *theEvent)
 121 {
 122     if ([theEvent deltaX] < 0) {
 123         return com_sun_glass_events_SwipeGesture_DIR_RIGHT;
 124     }
 125     if ([theEvent deltaX] > 0) {
 126         return com_sun_glass_events_SwipeGesture_DIR_LEFT;
 127     }
 128     if ([theEvent deltaY] > 0) {
 129         return com_sun_glass_events_SwipeGesture_DIR_UP;
 130     }
 131     if ([theEvent deltaY] < 0) {
 132         return com_sun_glass_events_SwipeGesture_DIR_DOWN;
 133     }
 134     return 0;
 135 }
 136 
 137 
 138 @implementation GlassViewDelegate
 139 
 140 - (id)initWithView:(NSView*)view withJview:(jobject)jview
 141 {
 142     self = [super init];
 143     if (self != nil)
 144     {
 145         GET_MAIN_JENV;
 146 
 147         self->nsView = view;
 148         self->jView = (*env)->NewGlobalRef(env, jview);
 149         self->mouseIsOver = NO;
 150         self->mouseDownMask = 0;
 151 
 152         self->gestureInProgress = NO;
 153 
 154         self->nativeFullScreenModeWindow = nil;
 155 
 156         // optimization
 157         [self->nsView allocateGState];
 158 
 159                 // register for drag and drop
 160                 [self->nsView registerForDraggedTypes:[NSArray arrayWithObjects:        NSPasteboardTypeString,
 161                                                                                 NSPasteboardTypeTIFF,
 162                                                                                    NSPasteboardTypeRTF,
 163                                                                                    NSPasteboardTypeTabularText,
 164                                                                                    NSPasteboardTypeFont,
 165                                                                                    NSPasteboardTypeRuler,
 166                                                                                    NSPasteboardTypeColor,
 167                                                                                    NSPasteboardTypeRTFD,
 168                                                                                    NSPasteboardTypeHTML,
 169                                                                                    NSPasteboardTypePDF,
 170                                                                                    NSPasteboardTypeMultipleTextSelection,
 171                                                                                    (NSString*)kUTTypeURL,
 172                                                                                    (NSString*)kUTTypeFileURL,
 173                                                                                    (NSString*)@"public.mime-type",
 174                                                                             nil]];
 175     }
 176     return self;
 177 }
 178 
 179 - (void)dealloc
 180 {
 181     [self->lastEvent release];
 182     self->lastEvent = nil;
 183 
 184     [self->parentHost release];
 185     self->parentHost = nil;
 186 
 187     [self->parentWindow release];
 188     self->parentWindow = nil;
 189 
 190     [self->fullscreenWindow release];
 191     self->fullscreenWindow = nil;
 192 
 193     [GlassTouches stopTracking:self];
 194 
 195     GET_MAIN_JENV;
 196     if (env != NULL)
 197     {
 198         (*env)->DeleteGlobalRef(env, self->jView);
 199     }
 200     self->jView = NULL;
 201 
 202     [super dealloc];
 203 }
 204 
 205 - (jobject)jView
 206 {
 207     return self->jView;
 208 }
 209 
 210 - (void)viewDidMoveToWindow
 211 {
 212     //        NSLog(@"viewDidMoveToWindow");
 213     //        NSLog(@"        self: %@", self);
 214     //        NSLog(@"        [self superview]: %@", [self superview]);
 215     GET_MAIN_JENV;
 216     if ([self->nsView window] != nil)
 217     {
 218         if (self->parentHost == nil)
 219         {
 220             self->parentHost = (GlassHostView*)[[self->nsView superview] retain];
 221         }
 222         if (self->parentWindow == nil)
 223         {
 224             self->parentWindow = [[self->nsView window] retain];
 225         }
 226 
 227         [[self->nsView window] setAcceptsMouseMovedEvents:YES];
 228         (*env)->CallVoidMethod(env, self->jView, jViewNotifyEvent, com_sun_glass_events_ViewEvent_ADD);
 229     }
 230     else
 231     {
 232         if (self->parentWindow != nil)
 233         {
 234             [self->parentWindow release];
 235             self->parentWindow = nil;
 236         }
 237         (*env)->CallVoidMethod(env, self->jView, jViewNotifyEvent, com_sun_glass_events_ViewEvent_REMOVE);
 238     }
 239     GLASS_CHECK_EXCEPTION(env);
 240 }
 241 
 242 - (void)setFrameOrigin:(NSPoint)newOrigin
 243 {
 244 
 245 }
 246 
 247 - (void)setFrameSize:(NSSize)newSize
 248 {
 249     LOG("GlassViewDelegate setFrameSize %fx%f", newSize.width, newSize.height);
 250 
 251     //NSLog(@"GlassViewDelegate setFrameSize: %dx%d", (int)newSize.width, (int)newSize.height);
 252     // TODO: listen for resize view's notifications
 253     GET_MAIN_JENV;
 254     (*env)->CallVoidMethod(env, self->jView, jViewNotifyResize, (int)newSize.width, (int)newSize.height);
 255     GLASS_CHECK_EXCEPTION(env);
 256 
 257     [self->nsView removeTrackingRect:self->trackingRect];
 258     self->trackingRect = [self->nsView addTrackingRect:[self->nsView bounds] owner:self->nsView userData:nil assumeInside:NO];
 259 }
 260 
 261 - (void)setFrame:(NSRect)frameRect
 262 {
 263     LOG("GlassViewDelegate setFrame %fx%f", frameRect.size.width, frameRect.size.height);
 264 
 265     //NSLog(@"GlassViewDelegate setFrame: %d,%d %dx%d", (int)frameRect.origin.x, (int)frameRect.origin.y, (int)frameRect.size.width, (int)frameRect.size.height);
 266     // TODO: listen for resize view's notifications
 267     GET_MAIN_JENV;
 268     (*env)->CallVoidMethod(env, self->jView, jViewNotifyResize, (int)frameRect.size.width, (int)frameRect.size.height);
 269     GLASS_CHECK_EXCEPTION(env);
 270 
 271     [self->nsView removeTrackingRect:self->trackingRect];
 272     self->trackingRect = [self->nsView addTrackingRect:[self->nsView bounds] owner:self->nsView userData:nil assumeInside:NO];
 273 }
 274 
 275 - (void)updateTrackingAreas
 276 {
 277     [self->nsView removeTrackingRect:self->trackingRect];
 278     self->trackingRect = [self->nsView addTrackingRect:[self->nsView bounds] owner:self->nsView userData:nil assumeInside:NO];
 279 }
 280 
 281 - (void)drawRect:(NSRect)dirtyRect
 282 {
 283     //NSLog(@"BEGIN View:drawRect %@: ", self);
 284     //NSLog(@"        [self frame]: %f,%f %fx%f", [self->nsView frame].origin.x, [self->nsView frame].origin.y, [self->nsView frame].size.width, [self->nsView frame].size.height);
 285     GET_MAIN_JENV;
 286     jint x = (jint)[self->nsView frame].origin.x;
 287     jint y = (jint)[self->nsView frame].origin.y;
 288     jint w = (jint)[self->nsView frame].size.width;
 289     jint h = (jint)[self->nsView frame].size.height;
 290     (*env)->CallVoidMethod(env, self->jView, jViewNotifyRepaint, x, y, w, h);
 291     GLASS_CHECK_EXCEPTION(env);
 292     //NSLog(@"END drawRect");
 293 }
 294 
 295 - (void)sendJavaMenuEvent:(NSEvent *)theEvent
 296 {
 297 //    NSLog(@"sendJavaMenuEvent");
 298     NSWindow * nswindow = [nsView window];
 299     if (nswindow && [[nswindow delegate] isKindOfClass: [GlassWindow class]]) {
 300         GlassWindow *window = (GlassWindow*)[nswindow delegate];
 301         if (!window->isEnabled) {
 302             return;
 303         }
 304     }
 305     NSPoint viewPoint = [nsView convertPoint:[theEvent locationInWindow] fromView:nil]; // convert from window coordinates to view coordinates
 306     CGPoint basePoint = CGEventGetLocation([theEvent CGEvent]);
 307 
 308     GET_MAIN_JENV;
 309     jboolean isKeyboardTrigger = JNI_FALSE;
 310     (*env)->CallVoidMethod(env, self->jView, jViewNotifyMenu,
 311                             (jint)viewPoint.x, (jint)viewPoint.y, (jint)basePoint.x, (jint)basePoint.y, isKeyboardTrigger);
 312     GLASS_CHECK_EXCEPTION(env);
 313 }
 314 
 315 - (void)sendJavaMouseEvent:(NSEvent *)theEvent
 316 {
 317     NSWindow * nswindow = [nsView window];
 318     if (nswindow && [[nswindow delegate] isKindOfClass: [GlassWindow class]]) {
 319         GlassWindow *window = (GlassWindow*)[nswindow delegate];
 320         if (!window->isEnabled) {
 321             return;
 322         }
 323     }
 324 
 325     int type = 0;
 326     int button = com_sun_glass_events_MouseEvent_BUTTON_NONE;
 327     switch ([theEvent type])
 328     {
 329         case NSLeftMouseDown:
 330             type = com_sun_glass_events_MouseEvent_DOWN;
 331             button = com_sun_glass_events_MouseEvent_BUTTON_LEFT;
 332             break;
 333         case NSRightMouseDown:
 334             type = com_sun_glass_events_MouseEvent_DOWN;
 335             button = com_sun_glass_events_MouseEvent_BUTTON_RIGHT;
 336             break;
 337         case NSOtherMouseDown:
 338             type = com_sun_glass_events_MouseEvent_DOWN;
 339             button = com_sun_glass_events_MouseEvent_BUTTON_OTHER;
 340             break;
 341 
 342         case NSLeftMouseUp:
 343             type = com_sun_glass_events_MouseEvent_UP;
 344             button = com_sun_glass_events_MouseEvent_BUTTON_LEFT;
 345             break;
 346         case NSRightMouseUp:
 347             type = com_sun_glass_events_MouseEvent_UP;
 348             button = com_sun_glass_events_MouseEvent_BUTTON_RIGHT;
 349             break;
 350         case NSOtherMouseUp:
 351             type = com_sun_glass_events_MouseEvent_UP;
 352             button = com_sun_glass_events_MouseEvent_BUTTON_OTHER;
 353             break;
 354 
 355         case NSLeftMouseDragged:
 356             type = com_sun_glass_events_MouseEvent_DRAG;
 357             button = com_sun_glass_events_MouseEvent_BUTTON_LEFT;
 358             break;
 359         case NSRightMouseDragged:
 360             type = com_sun_glass_events_MouseEvent_DRAG;
 361             button = com_sun_glass_events_MouseEvent_BUTTON_RIGHT;
 362             break;
 363         case NSOtherMouseDragged:
 364             type = com_sun_glass_events_MouseEvent_DRAG;
 365             button = com_sun_glass_events_MouseEvent_BUTTON_OTHER;
 366             break;
 367 
 368         case NSMouseMoved:
 369             type = com_sun_glass_events_MouseEvent_MOVE;
 370             break;
 371 
 372         case NSMouseEntered:
 373             type = com_sun_glass_events_MouseEvent_ENTER;
 374             [GlassTouches startTracking:self];
 375             self->lastTrackingNumber = [theEvent trackingNumber];
 376             break;
 377 
 378         case NSMouseExited:
 379             type = com_sun_glass_events_MouseEvent_EXIT;
 380             [GlassTouches stopTracking:self];
 381             self->lastTrackingNumber = [theEvent trackingNumber];
 382             break;
 383 
 384         case NSScrollWheel:
 385             type = com_sun_glass_events_MouseEvent_WHEEL;
 386             break;
 387     }
 388 
 389     NSPoint viewPoint = [nsView convertPoint:[theEvent locationInWindow] fromView:nil]; // convert from window coordinates to view coordinates
 390     CGPoint basePoint = CGEventGetLocation([theEvent CGEvent]);
 391 
 392     if (type == com_sun_glass_events_MouseEvent_MOVE)
 393     {
 394         NSRect frame = [nsView frame];
 395 
 396         if (viewPoint.x < 0 || viewPoint.y < 0 ||
 397                 viewPoint.x >= frame.size.width ||
 398                 viewPoint.y >= frame.size.height)
 399         {
 400             // The MOVE events happening outside of the view must be ignored
 401             return;
 402         }
 403 
 404         // Check if the event is a duplicate
 405         if (self->lastEvent)
 406         {
 407             CGPoint oldBasePoint = CGEventGetLocation([self->lastEvent CGEvent]);
 408 
 409             if (basePoint.x == oldBasePoint.x && basePoint.y == oldBasePoint.y)
 410             {
 411                 return;
 412             }
 413         }
 414     }
 415 
 416         //    NSLog(@"Event location: in window %@, in view %@, in base coordinates %d,%d",
 417         //          NSStringFromPoint([theEvent locationInWindow]),
 418         //          NSStringFromPoint(viewPoint),
 419         //          (jint)basePoint.x, (jint)basePoint.y);
 420 
 421     jdouble rotationX = 0.0;
 422     jdouble rotationY = 0.0;
 423     if (type == com_sun_glass_events_MouseEvent_WHEEL)
 424     {
 425         rotationX = (jdouble)[theEvent deltaX];
 426         rotationY = (jdouble)[theEvent deltaY];
 427 
 428         //XXX: check for equality for doubles???
 429         if (rotationX == 0.0 && rotationY == 0.0)
 430         {
 431             return;
 432         }
 433     }
 434 
 435     BOOL block = NO;
 436     {
 437         // RT-5892
 438         if ((type == com_sun_glass_events_MouseEvent_ENTER) || (type == com_sun_glass_events_MouseEvent_EXIT))
 439         {
 440             // userData indicates if this is a synthesized EXIT event that MUST pass through
 441             // Note: userData is only valid for ENTER/EXIT events!
 442             if (self->mouseIsDown == YES && [theEvent userData] != self)
 443             {
 444                 block = [self suppressMouseEnterExitOnMouseDown];
 445             }
 446         }
 447         else
 448         {
 449             // for the mouse supression we can not look at the mouse down state during ENTER/EXIT events
 450             // as they always report mouse up regardless of the actual state, so we need to store it
 451             // based on the events other than ENTER/EXIT
 452             self->mouseIsDown = (button != com_sun_glass_events_MouseEvent_BUTTON_NONE);
 453         }
 454     }
 455     if (block == NO)
 456     {
 457         if (!self->mouseIsOver &&
 458                 type != com_sun_glass_events_MouseEvent_ENTER &&
 459                 type != com_sun_glass_events_MouseEvent_EXIT)
 460         {
 461             // OS X didn't send mouseEnter. Synthesize it here.
 462             NSEvent *eeEvent = [NSEvent enterExitEventWithType:NSMouseEntered
 463                                                       location:[theEvent locationInWindow]
 464                                                  modifierFlags:[theEvent modifierFlags]
 465                                                      timestamp:[theEvent timestamp]
 466                                                   windowNumber:[theEvent windowNumber]
 467                                                        context:[theEvent context]
 468                                                    eventNumber:0
 469                                                 trackingNumber:self->lastTrackingNumber
 470                                                       userData:self];
 471             [self sendJavaMouseEvent:eeEvent];
 472         }
 473 
 474         jint modifiers = GetJavaKeyModifiers(theEvent);
 475         if (type != com_sun_glass_events_MouseEvent_ENTER &&
 476             type != com_sun_glass_events_MouseEvent_EXIT) {
 477             modifiers |= GetJavaMouseModifiers([NSEvent pressedMouseButtons]);
 478             if (type != com_sun_glass_events_MouseEvent_UP)
 479             {
 480                 switch (button)
 481                 {
 482                     case com_sun_glass_events_MouseEvent_BUTTON_LEFT:
 483                         modifiers |= com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_PRIMARY;
 484                         break;
 485                     case com_sun_glass_events_MouseEvent_BUTTON_RIGHT:
 486                         modifiers |= com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_SECONDARY;
 487                         break;
 488                     case com_sun_glass_events_MouseEvent_BUTTON_OTHER:
 489                         modifiers |= com_sun_glass_events_KeyEvent_MODIFIER_BUTTON_MIDDLE;
 490                         break;
 491                 }
 492             }
 493         }
 494 
 495         jboolean isSynthesized = JNI_FALSE;
 496 
 497         jboolean isPopupTrigger = JNI_FALSE;
 498         if (type == com_sun_glass_events_MouseEvent_DOWN) {
 499             if (button == com_sun_glass_events_MouseEvent_BUTTON_RIGHT) {
 500                 isPopupTrigger = JNI_TRUE;
 501             }
 502             if (button == com_sun_glass_events_MouseEvent_BUTTON_LEFT &&
 503                 (modifiers & com_sun_glass_events_KeyEvent_MODIFIER_CONTROL))
 504             {
 505                 isPopupTrigger = JNI_TRUE;
 506             }
 507         }
 508 
 509         [self->lastEvent release];
 510         self->lastEvent = nil;
 511         switch (type) {
 512             // prepare GlassDragSource for possible drag,
 513             case com_sun_glass_events_MouseEvent_DOWN:
 514                 switch (button) {
 515                     case com_sun_glass_events_MouseEvent_BUTTON_LEFT:  self->mouseDownMask |= 1 << 0; break;
 516                     case com_sun_glass_events_MouseEvent_BUTTON_RIGHT: self->mouseDownMask |= 1 << 1; break;
 517                     case com_sun_glass_events_MouseEvent_BUTTON_OTHER: self->mouseDownMask |= 1 << 2; break;
 518                 }
 519                 //fall through
 520             case com_sun_glass_events_MouseEvent_DRAG:
 521                 [GlassDragSource setDelegate:self];
 522                 // fall through to save the lastEvent
 523             // or for filtering out duplicate MOVE events
 524             case com_sun_glass_events_MouseEvent_MOVE:
 525                 self->lastEvent = [theEvent retain];
 526                 break;
 527             case com_sun_glass_events_MouseEvent_UP:
 528                 switch (button) {
 529                     case com_sun_glass_events_MouseEvent_BUTTON_LEFT:  self->mouseDownMask &= ~(1 << 0); break;
 530                     case com_sun_glass_events_MouseEvent_BUTTON_RIGHT: self->mouseDownMask &= ~(1 << 1); break;
 531                     case com_sun_glass_events_MouseEvent_BUTTON_OTHER: self->mouseDownMask &= ~(1 << 2); break;
 532                 }
 533                 break;
 534 
 535 
 536 
 537             // Track whether the mouse is over the view
 538             case com_sun_glass_events_MouseEvent_ENTER:
 539                 self->mouseIsOver = YES;
 540                 break;
 541             case com_sun_glass_events_MouseEvent_EXIT:
 542                 self->mouseIsOver = NO;
 543                 break;
 544         }
 545 
 546         GET_MAIN_JENV;
 547         if (type == com_sun_glass_events_MouseEvent_WHEEL) {
 548             // Detect mouse wheel event sender.
 549             // Can be inertia from scroll gesture,
 550             // scroll gesture or mouse wheel itself
 551             //
 552             // RT-22388, RT-25269
 553             jint sender = com_sun_glass_ui_mac_MacGestureSupport_SCROLL_SRC_WHEEL;
 554             if (isInertialScroll(theEvent))
 555             {
 556                 sender = com_sun_glass_ui_mac_MacGestureSupport_SCROLL_SRC_INERTIA;
 557             }
 558             else if (self->gestureInProgress == YES)
 559             {
 560                 sender = com_sun_glass_ui_mac_MacGestureSupport_SCROLL_SRC_GESTURE;
 561             }
 562 
 563             const jclass jGestureSupportClass = [GlassHelper ClassForName:"com.sun.glass.ui.mac.MacGestureSupport"
 564                                                                   withEnv:env];
 565             if (jGestureSupportClass)
 566             {
 567                 (*env)->CallStaticVoidMethod(env, jGestureSupportClass,
 568                                              javaIDs.GestureSupport.scrollGesturePerformed,
 569                                              self->jView, modifiers, sender,
 570                                              (jint)viewPoint.x, (jint)viewPoint.y,
 571                                              (jint)basePoint.x, (jint)basePoint.y,
 572                                              rotationX, rotationY);
 573             }
 574         } else {
 575             (*env)->CallVoidMethod(env, self->jView, jViewNotifyMouse, type, button,
 576                     (jint)viewPoint.x, (jint)viewPoint.y, (jint)basePoint.x, (jint)basePoint.y,
 577                     modifiers, isPopupTrigger, isSynthesized);
 578         }
 579         GLASS_CHECK_EXCEPTION(env);
 580 
 581         if (isPopupTrigger) {
 582             [self sendJavaMenuEvent:theEvent];
 583         }
 584     }
 585 }
 586 
 587 - (void)resetMouseTracking
 588 {
 589     if (self->mouseIsOver) {
 590         // Nothing of the parameters really matters for the EXIT event, except userData
 591         NSEvent* theEvent = [NSEvent
 592             enterExitEventWithType:NSMouseExited
 593                           location:[NSEvent mouseLocation]
 594                      modifierFlags:0
 595                          timestamp:[NSDate timeIntervalSinceReferenceDate]
 596                       windowNumber:[[self->nsView window] windowNumber]
 597                            context:[NSGraphicsContext currentContext]
 598                        eventNumber:0
 599                     trackingNumber:self->lastTrackingNumber
 600                           userData:self]; // indicates that this is a synthesized event
 601 
 602         [self sendJavaMouseEvent:theEvent];
 603     }
 604 }
 605 
 606 // RT-11707: zero out the keycode for TYPED events
 607 #define SEND_KEY_EVENT(type) \
 608     (*env)->CallVoidMethod(env, self->jView, jViewNotifyKey, (type), \
 609             (type) == com_sun_glass_events_KeyEvent_TYPED ? 0 : jKeyCode, \
 610             jKeyChars, jModifiers); \
 611     GLASS_CHECK_EXCEPTION(env);
 612 
 613 - (void)sendJavaKeyEvent:(NSEvent *)theEvent isDown:(BOOL)isDown
 614 {
 615     if (theEvent == s_lastKeyEvent) {
 616         // this must be a keyDown: generated by performKeyEquivalent: which returns NO by design
 617         return;
 618     }
 619     [s_lastKeyEvent release];
 620     s_lastKeyEvent = [theEvent retain];
 621 
 622     GET_MAIN_JENV;
 623 
 624     jint jKeyCode = GetJavaKeyCode(theEvent);
 625     jcharArray jKeyChars = GetJavaKeyChars(env, theEvent);
 626     jint jModifiers = GetJavaModifiers(theEvent);
 627 
 628     // Short circuit here: If this is a synthetic key-typed from a text event
 629     // post it and return.
 630     if ([theEvent isKindOfClass:[GlassNSEvent class]]) {
 631         if ([(GlassNSEvent *)theEvent isSyntheticKeyTyped]) {
 632             SEND_KEY_EVENT(com_sun_glass_events_KeyEvent_TYPED);
 633             (*env)->DeleteLocalRef(env, jKeyChars);
 634             return;
 635         }
 636     }
 637 
 638     if (!isDown)
 639     {
 640         SEND_KEY_EVENT(com_sun_glass_events_KeyEvent_RELEASE);
 641     }
 642     else
 643     {
 644         SEND_KEY_EVENT(com_sun_glass_events_KeyEvent_PRESS);
 645 
 646         // In the applet case, FireFox always sends a text input event after every
 647         // key-pressed, which gets turned into a TYPED event for simple key strokes.
 648         // The NPAPI support code will send a boolean to let us know if we need to
 649         // generate the TYPED, or if we should expect the input method support to do it.
 650         BOOL sendKeyTyped = YES;
 651 
 652         if ([theEvent isKindOfClass:[GlassNSEvent class]]) {
 653             sendKeyTyped = [(GlassNSEvent *)theEvent needsKeyTyped];
 654         }
 655 
 656         // TYPED events should only be sent for printable characters. Thus we avoid
 657         // sending them for navigation keys. Perhaps this logic could be enhanced.
 658         if (sendKeyTyped) {
 659             if (jKeyCode < com_sun_glass_events_KeyEvent_VK_PAGE_UP ||
 660                 jKeyCode > com_sun_glass_events_KeyEvent_VK_DOWN)
 661             {
 662                 SEND_KEY_EVENT(com_sun_glass_events_KeyEvent_TYPED);
 663             }
 664 
 665             // Quirk in Firefox: If we have to generate a key-typed and this
 666             // event is a repeat we will also need to generate a fake RELEASE event
 667             // because we won't see a key-release.
 668             if ([theEvent isARepeat] &&
 669                 [[self->nsView window] isKindOfClass:[GlassEmbeddedWindow class]]) {
 670                 SEND_KEY_EVENT(com_sun_glass_events_KeyEvent_RELEASE);
 671             }
 672         }
 673 
 674         // Mac doesn't send keyUp for Cmd+<> key combinations (including Shift+Cmd+<>, etc.)
 675         // So we synthesize the event
 676         if (jModifiers & com_sun_glass_events_KeyEvent_MODIFIER_COMMAND)
 677         {
 678             SEND_KEY_EVENT(com_sun_glass_events_KeyEvent_RELEASE);
 679         }
 680     }
 681 
 682     (*env)->DeleteLocalRef(env, jKeyChars);
 683     GLASS_CHECK_EXCEPTION(env);
 684 }
 685 
 686 #define SEND_MODIFIER_KEY_EVENT_WITH_TYPE(type, vkCode) \
 687         (*env)->CallVoidMethod(env, self->jView, jViewNotifyKey, \
 688                 (type), \
 689                 (vkCode), \
 690                 jKeyChars, jModifiers);
 691 
 692 #define SEND_MODIFIER_KEY_EVENT(mask, vkCode) \
 693     if (changedFlags & (mask)) { \
 694         SEND_MODIFIER_KEY_EVENT_WITH_TYPE(currentFlags & (mask) ? com_sun_glass_events_KeyEvent_PRESS : com_sun_glass_events_KeyEvent_RELEASE, vkCode); \
 695         GLASS_CHECK_EXCEPTION(env); \
 696     }
 697 
 698 - (void)sendJavaModifierKeyEvent:(NSEvent *)theEvent
 699 {
 700     NSUInteger currentFlags = [theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask;
 701     NSUInteger changedFlags = currentFlags ^ s_modifierFlags;
 702 
 703     jint jModifiers = GetJavaModifiers(theEvent);
 704 
 705     GET_MAIN_JENV;
 706     jcharArray jKeyChars = (*env)->NewCharArray(env, 0);
 707 
 708     SEND_MODIFIER_KEY_EVENT(NSShiftKeyMask,       com_sun_glass_events_KeyEvent_VK_SHIFT);
 709     SEND_MODIFIER_KEY_EVENT(NSControlKeyMask,     com_sun_glass_events_KeyEvent_VK_CONTROL);
 710     SEND_MODIFIER_KEY_EVENT(NSAlternateKeyMask,   com_sun_glass_events_KeyEvent_VK_ALT);
 711     SEND_MODIFIER_KEY_EVENT(NSCommandKeyMask,     com_sun_glass_events_KeyEvent_VK_COMMAND);
 712 
 713     // For CapsLock both PRESS and RELEASE should be synthesized each time
 714     if (changedFlags & NSAlphaShiftKeyMask) {
 715         SEND_MODIFIER_KEY_EVENT_WITH_TYPE(com_sun_glass_events_KeyEvent_PRESS, com_sun_glass_events_KeyEvent_VK_CAPS_LOCK);
 716         SEND_MODIFIER_KEY_EVENT_WITH_TYPE(com_sun_glass_events_KeyEvent_RELEASE, com_sun_glass_events_KeyEvent_VK_CAPS_LOCK);
 717     }
 718 
 719     (*env)->DeleteLocalRef(env, jKeyChars);
 720     GLASS_CHECK_EXCEPTION(env);
 721 
 722     s_modifierFlags = currentFlags;
 723 }
 724 
 725 - (void)sendJavaGestureEvent:(NSEvent *)theEvent type:(int)type
 726 {
 727     NSPoint viewPoint = [nsView convertPoint:[theEvent locationInWindow] fromView:nil]; // convert from window coordinates to view coordinates
 728     CGPoint basePoint = CGEventGetLocation([theEvent CGEvent]);
 729 
 730     jint modifiers = GetJavaModifiers(theEvent);
 731 
 732     GET_MAIN_JENV;
 733     const jclass jGestureSupportClass = [GlassHelper ClassForName:"com.sun.glass.ui.mac.MacGestureSupport"
 734                                                           withEnv:env];
 735     if (jGestureSupportClass)
 736     {
 737         switch (type)
 738         {
 739             case com_sun_glass_ui_mac_MacGestureSupport_GESTURE_ROTATE:
 740                 (*env)->CallStaticVoidMethod(env, jGestureSupportClass,
 741                                              javaIDs.GestureSupport.rotateGesturePerformed,
 742                                              self->jView, modifiers,
 743                                              (jint)viewPoint.x, (jint)viewPoint.y,
 744                                              (jint)basePoint.x, (jint)basePoint.y,
 745                                              (jfloat)[theEvent rotation]);
 746                 break;
 747             case com_sun_glass_ui_mac_MacGestureSupport_GESTURE_SWIPE:
 748                 (*env)->CallStaticVoidMethod(env, jGestureSupportClass,
 749                                              javaIDs.GestureSupport.swipeGesturePerformed,
 750                                              self->jView, modifiers,
 751                                              getSwipeDirFromEvent(theEvent),
 752                                              (jint)viewPoint.x, (jint)viewPoint.y,
 753                                              (jint)basePoint.x, (jint)basePoint.y);
 754                 break;
 755             case com_sun_glass_ui_mac_MacGestureSupport_GESTURE_MAGNIFY:
 756                 (*env)->CallStaticVoidMethod(env, jGestureSupportClass,
 757                                              javaIDs.GestureSupport.magnifyGesturePerformed,
 758                                              self->jView, modifiers,
 759                                              (jint)viewPoint.x, (jint)viewPoint.y,
 760                                              (jint)basePoint.x, (jint)basePoint.y,
 761                                              (jfloat)[theEvent magnification]);
 762                 break;
 763         }
 764     }
 765     GLASS_CHECK_EXCEPTION(env);
 766 }
 767 
 768 - (void)sendJavaGestureBeginEvent:(NSEvent *)theEvent
 769 {
 770     self->gestureInProgress = YES;
 771 }
 772 
 773 - (void)sendJavaGestureEndEvent:(NSEvent *)theEvent
 774 {
 775     self->gestureInProgress = NO;
 776 
 777     NSPoint viewPoint = [nsView convertPoint:[theEvent locationInWindow] fromView:nil]; // convert from window coordinates to view coordinates
 778     CGPoint basePoint = CGEventGetLocation([theEvent CGEvent]);
 779 
 780     jint modifiers = GetJavaModifiers(theEvent);
 781 
 782     GET_MAIN_JENV;
 783     const jclass jGestureSupportClass = [GlassHelper ClassForName:"com.sun.glass.ui.mac.MacGestureSupport"
 784                                                           withEnv:env];
 785     if (jGestureSupportClass)
 786     {
 787         (*env)->CallStaticVoidMethod(env, jGestureSupportClass,
 788                                      javaIDs.GestureSupport.gestureFinished,
 789                                      self->jView, modifiers,
 790                                      (jint)viewPoint.x, (jint)viewPoint.y,
 791                                      (jint)basePoint.x, (jint)basePoint.y);
 792 
 793     }
 794     GLASS_CHECK_EXCEPTION(env);
 795 }
 796 
 797 - (NSDragOperation)sendJavaDndEvent:(id <NSDraggingInfo>)info type:(jint)type
 798 {
 799     GET_MAIN_JENV;
 800 
 801     NSPoint draggingLocation = [nsView convertPoint:[info draggingLocation] fromView:nil];
 802     int x = (int)draggingLocation.x;
 803     int y = (int)draggingLocation.y;
 804 
 805     int xAbs = (int)([info draggingLocation].x + [self->nsView window].frame.origin.x);
 806     int yAbs = (int)([[self->nsView window] screen].frame.size.height - [self->nsView window].frame.origin.y
 807                      - [info draggingLocation].y);
 808 
 809     int mask;
 810     NSDragOperation operation = [info draggingSourceOperationMask];
 811 
 812     [GlassDragSource setSupportedActions:[GlassDragSource mapNsOperationToJavaMask:operation]];
 813 
 814     jint recommendedAction = [GlassDragSource getRecommendedActionForMask:operation];
 815     switch (type)
 816     {
 817         case com_sun_glass_events_DndEvent_ENTER:
 818             DNDLOG("com_sun_glass_events_DndEvent_ENTER");
 819             copyToDragPasteboardIfNeeded(info);
 820             mask = (*env)->CallIntMethod(env, self->jView, jViewNotifyDragEnter, x, y, xAbs, yAbs, recommendedAction);
 821             break;
 822         case com_sun_glass_events_DndEvent_UPDATE:
 823             DNDLOG("com_sun_glass_events_DndEvent_UPDATE");
 824             mask = (*env)->CallIntMethod(env, self->jView, jViewNotifyDragOver, x, y, xAbs, yAbs, recommendedAction);
 825             break;
 826         case com_sun_glass_events_DndEvent_PERFORM:
 827             DNDLOG("com_sun_glass_events_DndEvent_PERFORM");
 828             mask = (*env)->CallIntMethod(env, self->jView, jViewNotifyDragDrop, x, y, xAbs, yAbs, recommendedAction);
 829             break;
 830         case com_sun_glass_events_DndEvent_EXIT:
 831             DNDLOG("com_sun_glass_events_DndEvent_EXIT");
 832             (*env)->CallVoidMethod(env, self->jView, jViewNotifyDragLeave);
 833             mask = com_sun_glass_ui_Clipboard_ACTION_NONE;
 834             break;
 835         default:
 836             mask = com_sun_glass_ui_Clipboard_ACTION_NONE;
 837             break;
 838     }
 839     [GlassDragSource setMask:mask];
 840 
 841     GLASS_CHECK_EXCEPTION(env);
 842 
 843     return [GlassDragSource mapJavaMaskToNsOperation:[GlassDragSource getMask]];
 844 }
 845 
 846 - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal
 847 {
 848     return self->dragOperation;
 849 }
 850 
 851 // called from Java layer drag handler, triggered by DnD Pasteboard flush
 852 - (void)startDrag:(NSDragOperation)operation
 853 {
 854     DNDLOG("startDrag");
 855     self->dragOperation = operation;
 856     {
 857         NSPoint dragPoint = [self->nsView convertPoint:[self->lastEvent locationInWindow] fromView:nil];
 858         NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
 859         NSImage *image = nil;
 860 
 861         if ([[pasteboard types] containsObject:DRAG_IMAGE_MIME]) {
 862             //Try to init with drag image specified by the user
 863             image = [[NSImage alloc] initWithData:[pasteboard dataForType:DRAG_IMAGE_MIME]];
 864         }
 865 
 866         if (image == nil && [NSImage canInitWithPasteboard:pasteboard] == YES)
 867         {
 868             // ask the Pasteboard for ist own image representation of its contents
 869             image = [[NSImage alloc] initWithPasteboard:pasteboard];
 870         }
 871 
 872         if (image != nil)
 873         {
 874             // check the drag image size and scale it down as needed using Safari behavior (sizes) as reference
 875             CGFloat width = [image size].width;
 876             CGFloat height = [image size].height;
 877             if ((width > MAX_DRAG_SIZE) || (height > MAX_DRAG_SIZE))
 878             {
 879                 if (width >= height)
 880                 {
 881                     CGFloat ratio = height/width;
 882                     width = MIN(width, MAX_DRAG_SIZE);
 883                     height = ratio * width;
 884                     [image setSize:NSMakeSize(width, height)];
 885                 }
 886                 else
 887                 {
 888                     CGFloat ratio = width/height;
 889                     height = MIN(height, MAX_DRAG_SIZE);
 890                     width = ratio * height;
 891                     [image setSize:NSMakeSize(width, height)];
 892                 }
 893             }
 894         } else {
 895             NSArray *items = [pasteboard pasteboardItems];
 896             if ([items count] == 1)
 897             {
 898                 image = [[NSImage alloc] initWithContentsOfFile:@"/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/GenericDocumentIcon.icns"];
 899             }
 900 
 901             if (image == nil)
 902             {
 903                 image = [[NSImage imageNamed:NSImageNameMultipleDocuments] retain];
 904             }
 905 
 906             [image setSize:NSMakeSize(DEFAULT_DRAG_SIZE, DEFAULT_DRAG_SIZE)];
 907         }
 908 
 909         if (image != nil)
 910         {
 911             // select the center of the image as the drag origin
 912             // TODO http://javafx-jira.kenai.com/browse/RT-17629
 913             // would be nice to get this info from the Java layer,
 914             // so that we could adjust the drag image origin based on where in the src it was clicked on
 915             dragPoint.x -= ([image size].width/2.0f);
 916             dragPoint.y += ([image size].height/2.0f);
 917 
 918             NSString *offsetString = [pasteboard stringForType:DRAG_IMAGE_OFFSET];
 919             if (offsetString != nil) {
 920                 NSPoint offset = NSPointFromString(offsetString);
 921                 //Adjust offset to the image size
 922                 float imageHalfX = [image size].width/2.0f;
 923                 float imageHalfY = [image size].height/2.0f;
 924 
 925                 if (offset.x > imageHalfX || offset.x < -imageHalfX) {
 926                     offset.x = imageHalfX * (offset.x > 0 ? 1 : -1);
 927                 }
 928                 if (offset.y > imageHalfY || offset.y < -imageHalfY) {
 929                     offset.y = imageHalfY * (offset.y > 0 ? 1 : -1);
 930                 }
 931 
 932                 dragPoint.x += offset.x;
 933                 dragPoint.y -= offset.y;
 934             }
 935         }
 936         else
 937         {
 938             // last resource: "empty" image
 939             image = [[NSImage alloc] initWithSize:NSMakeSize(1.0f, 1.0f)];
 940         }
 941         [self->nsView dragImage:image at:dragPoint offset:NSZeroSize event:self->lastEvent pasteboard:pasteboard source:self->nsView slideBack:YES];
 942 
 943         // main thread blocked here until drag completes
 944 
 945         [GlassDragSource setDelegate:nil];
 946 
 947         [image release];
 948     }
 949     self->dragOperation = NSDragOperationNone;
 950 }
 951 
 952 - (void)synthesizeMouseUp:(NSEventType)type
 953 {
 954     NSEvent* theEvent = [NSEvent
 955         mouseEventWithType:type
 956                   location:[NSEvent mouseLocation]
 957              modifierFlags:0
 958                  timestamp:[NSDate timeIntervalSinceReferenceDate]
 959               windowNumber:[[self->nsView window] windowNumber]
 960                    context:[NSGraphicsContext currentContext]
 961                eventNumber:0
 962                 clickCount:0
 963                   pressure:0.0];
 964 
 965     [self sendJavaMouseEvent:theEvent];
 966 }
 967 
 968 - (void)draggingEnded
 969 {
 970     GET_MAIN_JENV;
 971     (*env)->CallVoidMethod(env, self->jView, jViewNotifyDragEnd,  [GlassDragSource getMask]);
 972     GLASS_CHECK_EXCEPTION(env);
 973 
 974     // RT-36038: OS X won't send mouseUp after DnD is complete, so we synthesize them
 975     if (self->mouseDownMask & 1 << 0) [self synthesizeMouseUp:NSLeftMouseUp];
 976     if (self->mouseDownMask & 1 << 1) [self synthesizeMouseUp:NSRightMouseUp];
 977     if (self->mouseDownMask & 1 << 2) [self synthesizeMouseUp:NSOtherMouseUp];
 978 }
 979 
 980 - (BOOL)suppressMouseEnterExitOnMouseDown
 981 {
 982     return YES;
 983 }
 984 
 985 - (void)notifyInputMethod:(id) aString attr:(int)attr length:(int)length cursor:(int)cursor  selectedRange:(NSRange)selectionRange
 986 {
 987     if ([NSThread isMainThread] == YES)
 988     {
 989         GET_MAIN_JENV;
 990         jstring jStr = (*env)->NewStringUTF(env, [aString UTF8String]);
 991         (*env)->CallVoidMethod(env, self->jView, jViewNotifyInputMethodMac, jStr, attr, length, cursor, selectionRange.location, selectionRange.length);
 992         GLASS_CHECK_EXCEPTION(env);
 993     }
 994 }
 995 
 996 - (NSRect)getInputMethodCandidatePosRequest:(int)pos
 997 {
 998     NSRect retVal = NSMakeRect(0.0, 0.0, 0.0, 0.0);
 999     if ([NSThread isMainThread] == YES)
1000     {
1001         // TODO: For some reason result is not always converted to the screen coordinates,
1002         // and when we call this method before we set text to updated we get the
1003         // IndexOutOfBoundsException
1004         // In this case we return an empty rectangle so suggestion window is shown at the
1005         // bottom left corner of the main screen.
1006         GET_MAIN_JENV;
1007         jdoubleArray theArray =
1008             (jdoubleArray) (*env)->CallObjectMethod(env,
1009                                                     self->jView,
1010                                                     jViewNotifyInputMethodCandidatePosRequest,
1011                                                     pos);
1012         GLASS_CHECK_EXCEPTION(env);
1013         if (theArray != NULL) {
1014             jint n = (*env)->GetArrayLength(env, theArray);
1015             if (n == 2) {
1016                 jboolean isCopy;
1017                 jdouble *elems = (*env)->GetDoubleArrayElements(env, theArray, &isCopy);
1018                 retVal = NSMakeRect((CGFloat)elems[0], (CGFloat)elems[1], 0, 0);
1019                 (*env)->ReleaseDoubleArrayElements(env, theArray, elems, 0);
1020                 (*env)->DeleteLocalRef(env, theArray);
1021             }
1022         }
1023         GLASS_CHECK_EXCEPTION(env);
1024     }
1025     return retVal;
1026 }
1027 
1028 - (void)sendJavaFullScreenEvent:(BOOL)entered withNativeWidget:(BOOL)isNative
1029 {
1030     if (isNative) {
1031         // Must be done before sending the event to Java since the event handler
1032         // may re-request the operation.
1033         if (entered) {
1034             self->nativeFullScreenModeWindow = [[self->nsView window] retain];
1035         } else {
1036             [self->nativeFullScreenModeWindow release];
1037             self->nativeFullScreenModeWindow = nil;
1038         }
1039     }
1040 
1041     GET_MAIN_JENV;
1042     (*env)->CallVoidMethod(env, self->jView, jViewNotifyEvent,
1043             entered ? com_sun_glass_events_ViewEvent_FULLSCREEN_ENTER : com_sun_glass_events_ViewEvent_FULLSCREEN_EXIT);
1044     GLASS_CHECK_EXCEPTION(env);
1045 }
1046 
1047 /*
1048  The hierarchy for our view is view -> superview (host) -> window
1049 
1050  1. create superview (new host) for our view
1051  2. create fullscreen window with the new superview
1052  3. create the background window (for fading out the desktop)
1053  4. remove our view from the window superview and insert it into the fullscreen window superview
1054  5. show our fullscreen window (and hide the original window)
1055  6. attach to it our background window (which will show it as well)
1056  7. zoom out our fullscreen window and at the same time animate the background window transparency
1057  8. enter fullscreen
1058  */
1059 - (void)enterFullscreenWithAnimate:(BOOL)animate withKeepRatio:(BOOL)keepRatio withHideCursor:(BOOL)hideCursor
1060 {
1061     LOG("GlassViewDelegate enterFullscreenWithAnimate:%d withKeepRatio:%d withHideCursor:%d", animate, keepRatio, hideCursor);
1062 
1063     if ([[self->nsView window] isKindOfClass:[GlassEmbeddedWindow class]] == NO)
1064     {
1065         [[self->nsView window] toggleFullScreen:self];
1066         // wait until the operation is complete
1067         [GlassApplication enterFullScreenExitingLoop];
1068         return;
1069     }
1070 
1071     NSScreen *screen = [[self->nsView window] screen];
1072 
1073     NSRect frameInWindowScreenCoords = [self->nsView bounds];
1074     frameInWindowScreenCoords = [self->parentWindow convertRectToScreen:frameInWindowScreenCoords];
1075     NSPoint pointInPrimaryScreenCoords = frameInWindowScreenCoords.origin;
1076 
1077     // Convert to local screen
1078     frameInWindowScreenCoords.origin.x -= screen.frame.origin.x;
1079     frameInWindowScreenCoords.origin.y -= screen.frame.origin.y;
1080 
1081     @try
1082     {
1083         // 0. Retain the view while it's in the FS mode
1084         [self->nsView retain];
1085 
1086         // 1.
1087         self->fullscreenHost = [[GlassHostView alloc] initWithFrame:[self->nsView bounds]];
1088         [self->fullscreenHost setAutoresizesSubviews:YES];
1089 
1090         // 2.
1091         self->fullscreenWindow = [[GlassFullscreenWindow alloc] initWithContentRect:frameInWindowScreenCoords
1092                                                                        withHostView:self->fullscreenHost
1093                                                                            withView:self->nsView withScreen:screen
1094                                                                           withPoint:pointInPrimaryScreenCoords];
1095 
1096         // 3.
1097 
1098         [self->parentWindow disableFlushWindow];
1099         {
1100             // handle plugin case
1101             if ([[self->nsView window] isKindOfClass:[GlassEmbeddedWindow class]] == YES)
1102             {
1103                 GlassEmbeddedWindow *window = (GlassEmbeddedWindow*)self->parentWindow;
1104                 [window setFullscreenWindow:self->fullscreenWindow];
1105             }
1106 
1107             // 4.
1108             [self->nsView retain];
1109             {
1110                 [self->nsView removeFromSuperviewWithoutNeedingDisplay];
1111                 [self->fullscreenHost addSubview:self->nsView];
1112             }
1113             [self->nsView release];
1114 
1115             if ([[self->parentWindow delegate] isKindOfClass:[GlassWindow class]] == YES)
1116             {
1117                 GlassWindow *window = (GlassWindow*)[self->parentWindow delegate];
1118                 [window setFullscreenWindow:self->fullscreenWindow];
1119             }
1120 
1121             // 5.
1122             [self->fullscreenWindow setInitialFirstResponder:self->nsView];
1123             [self->fullscreenWindow makeFirstResponder:self->nsView];
1124 
1125             // This trick allows an applet to display a focused window. This is harmless otherwise.
1126             // If we don't do this, we end up with a literally empty full screen background and no content shown whatsoever.
1127             [[NSRunningApplication currentApplication] activateWithOptions:(NSApplicationActivateIgnoringOtherApps | NSApplicationActivateAllWindows)];
1128 
1129             [self->fullscreenWindow makeKeyAndOrderFront:self->nsView];
1130             [self->fullscreenWindow orderFrontRegardless];
1131             [self->fullscreenWindow makeMainWindow];
1132         }
1133 
1134         // 6.
1135 
1136         NSRect screenFrame = [screen frame];
1137         NSRect fullscreenFrame = [screen frame];
1138         if (keepRatio == YES)
1139         {
1140             CGFloat ratioWidth = (frameInWindowScreenCoords.size.width/screenFrame.size.width);
1141             CGFloat ratioHeight = (frameInWindowScreenCoords.size.height/screenFrame.size.height);
1142             if (ratioWidth > ratioHeight)
1143             {
1144                 CGFloat ratio = (frameInWindowScreenCoords.size.width/frameInWindowScreenCoords.size.height);
1145                 fullscreenFrame.size.height = fullscreenFrame.size.width / ratio;
1146                 fullscreenFrame.origin.y += (screenFrame.size.height - fullscreenFrame.size.height) / 2.0f;
1147             }
1148             else
1149             {
1150                 CGFloat ratio = (frameInWindowScreenCoords.size.height/frameInWindowScreenCoords.size.width);
1151                 fullscreenFrame.size.width = fullscreenFrame.size.height / ratio;
1152                 fullscreenFrame.origin.x += (screenFrame.size.width - fullscreenFrame.size.width) / 2.0f;
1153             }
1154         }
1155 
1156         // 7.
1157         //[self->fullscreenWindow setBackgroundColor:[NSColor whiteColor]]; // debug
1158         [self->fullscreenWindow setFrame:frameInWindowScreenCoords display:YES animate:animate];
1159 
1160         // 8.
1161         [self->fullscreenWindow toggleFullScreen:self->fullscreenWindow];
1162     }
1163     @catch (NSException *e)
1164     {
1165         NSLog(@"enterFullscreenWithAnimate caught exception: %@", e);
1166     }
1167 
1168     [self sendJavaFullScreenEvent:YES withNativeWidget:NO];
1169 }
1170 
1171 - (void)exitFullscreenWithAnimate:(BOOL)animate
1172 {
1173     LOG("GlassViewDelegate exitFullscreenWithAnimate");
1174 
1175     @try
1176     {
1177         if (self->nativeFullScreenModeWindow)
1178         {
1179             [self->nativeFullScreenModeWindow performSelector:@selector(toggleFullScreen:) withObject:nil];
1180             // wait until the operation is complete
1181             [GlassApplication enterFullScreenExitingLoop];
1182             return;
1183         }
1184 
1185         [self->fullscreenWindow toggleFullScreen:self->fullscreenWindow];
1186 
1187         NSRect frame = [self->parentHost bounds];
1188         frame.origin = [self->fullscreenWindow point];
1189         [self->fullscreenWindow setFrame:frame display:YES animate:animate];
1190 
1191         [self->fullscreenWindow disableFlushWindow];
1192         {
1193             [self->nsView retain];
1194             {
1195                 [self->nsView removeFromSuperviewWithoutNeedingDisplay];
1196                 [self->parentHost addSubview:self->nsView];
1197             }
1198             [self->nsView release];
1199 
1200             // handle plugin case
1201             if ([[self->nsView window] isKindOfClass:[GlassEmbeddedWindow class]] == YES)
1202             {
1203                 GlassEmbeddedWindow *window = (GlassEmbeddedWindow*)[self->nsView window];
1204                 [window setFullscreenWindow:nil];
1205             }
1206 
1207             [self->parentWindow setInitialFirstResponder:self->nsView];
1208             [self->parentWindow makeFirstResponder:self->nsView];
1209 
1210             if ([[self->parentWindow delegate] isKindOfClass:[GlassWindow class]])
1211             {
1212                 GlassWindow *window = (GlassWindow*)[self->parentWindow delegate];
1213                 [window setFullscreenWindow: nil];
1214             }
1215         }
1216         [self->fullscreenWindow enableFlushWindow];
1217         [self->parentWindow enableFlushWindow];
1218 
1219         [self->fullscreenWindow orderOut:nil];
1220         [self->fullscreenWindow close];
1221         self->fullscreenWindow = nil;
1222 
1223         // It was retained upon entering the FS mode
1224         [self->nsView release];
1225     }
1226     @catch (NSException *e)
1227     {
1228         NSLog(@"exitFullscreenWithAnimate caught exception: %@", e);
1229     }
1230 
1231     [self sendJavaFullScreenEvent:NO withNativeWidget:NO];
1232 }
1233 
1234 - (GlassAccessible*)getAccessible
1235 {
1236     GET_MAIN_JENV;
1237     jlong accessible = (*env)->CallLongMethod(env, self->jView, jViewGetAccessible);
1238     GLASS_CHECK_EXCEPTION(env);
1239     return (GlassAccessible*)jlong_to_ptr(accessible);
1240 }
1241 
1242 @end