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