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