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