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