vtkCocoaGLView.mm 16.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkCocoaGLView.mm

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/

16 17 18
#import <Cocoa/Cocoa.h>
#import "vtkCocoaMacOSXSDKCompatibility.h" // Needed to support old SDKs

19
#import "vtkCocoaGLView.h"
20 21 22
#import "vtkCocoaRenderWindow.h"
#import "vtkCocoaRenderWindowInteractor.h"
#import "vtkCommand.h"
23

24 25 26 27 28
//----------------------------------------------------------------------------
// Private
@interface vtkCocoaGLView()
@property(readwrite, retain, nonatomic) NSTrackingArea *rolloverTrackingArea;
@end
29

30
@implementation vtkCocoaGLView
31

32
//----------------------------------------------------------------------------
33
// Private
34 35 36 37 38
- (void)emptyMethod:(id)sender
{
  (void)sender;
}

39 40 41
//----------------------------------------------------------------------------
@synthesize rolloverTrackingArea = _rolloverTrackingArea;

42
//----------------------------------------------------------------------------
43
// Overridden (from NSView).
44 45 46 47 48 49 50 51 52 53 54
// designated initializer
- (id)initWithFrame:(NSRect)frameRect
{
  self = [super initWithFrame:frameRect];
  if (self)
    {
    // Force Cocoa into "multi threaded mode" because VTK spawns pthreads.
    // Apple's docs say: "If you intend to use Cocoa calls, you must force
    // Cocoa into its multithreaded mode before detaching any POSIX threads.
    // To do this, simply detach an NSThread and have it promptly exit.
    // This is enough to ensure that the locks needed by the Cocoa
55
    // frameworks are put in place"
56 57 58
    if ([NSThread isMultiThreaded] == NO)
      {
      [NSThread detachNewThreadSelector:@selector(emptyMethod:)
59 60
                               toTarget:self
                             withObject:nil];
61 62 63 64 65
      }
    }
  return self;
}

66 67 68
//----------------------------------------------------------------------------
- (vtkCocoaRenderWindow *)getVTKRenderWindow
{
69
  return _myVTKRenderWindow;
70 71
}

72 73 74
//----------------------------------------------------------------------------
- (void)setVTKRenderWindow:(vtkCocoaRenderWindow *)theVTKRenderWindow
{
75
  _myVTKRenderWindow = theVTKRenderWindow;
76 77
}

78
//----------------------------------------------------------------------------
79
- (vtkCocoaRenderWindowInteractor *)getInteractor
80
{
81
  if (_myVTKRenderWindow)
82
    {
83
    return (vtkCocoaRenderWindowInteractor *)_myVTKRenderWindow->GetInteractor();
84 85 86 87 88
    }
  else
    {
    return NULL;
    }
89 90
}

91
//----------------------------------------------------------------------------
92
// Overridden (from NSView).
93 94
- (void)drawRect:(NSRect)theRect
{
95
  (void)theRect;
96

97
  if (_myVTKRenderWindow && _myVTKRenderWindow->GetMapped())
98
    {
99
    _myVTKRenderWindow->Render();
100
    }
101 102
}

103
//----------------------------------------------------------------------------
104 105
// Overridden (from NSView).
- (void)updateTrackingAreas
106
{
107 108 109
  //clear out the old tracking area
  NSTrackingArea *trackingArea = [self rolloverTrackingArea];
  if (trackingArea)
110
    {
111
    [self removeTrackingArea:trackingArea];
112 113
    }

114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
  //create a new tracking area
  NSRect rect = [self visibleRect];
  NSTrackingAreaOptions opts = (NSTrackingMouseEnteredAndExited |
                                NSTrackingMouseMoved |
                                NSTrackingActiveAlways);
  trackingArea = [[NSTrackingArea alloc] initWithRect:rect
                                              options:opts
                                                owner:self
                                             userInfo:nil];
  [self addTrackingArea:trackingArea];
  [self setRolloverTrackingArea:trackingArea];
#if !VTK_OBJC_IS_ARC
  [trackingArea release];
#endif

  [super updateTrackingAreas];
130 131
}

132
//----------------------------------------------------------------------------
133
// Overridden (from NSResponder).
134 135 136 137 138
- (BOOL)acceptsFirstResponder
{
  return YES;
}

139 140 141
//----------------------------------------------------------------------------
// For generating keysyms that are compatible with other VTK interactors
static const char *vtkMacCharCodeToKeySymTable[128] = {
142 143 144 145 146
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  "space", "exclam", "quotedbl", "numbersign",
  "dollar", "percent", "ampersand", "quoteright",
  "parenleft", "parenright", "asterisk", "plus",
147
  "comma", "minus", "period", "slash",
148
  "0", "1", "2", "3", "4", "5", "6", "7",
149
  "8", "9", "colon", "semicolon", "less", "equal", "greater", "question",
150
  "at", "A", "B", "C", "D", "E", "F", "G",
151 152
  "H", "I", "J", "K", "L", "M", "N", "O",
  "P", "Q", "R", "S", "T", "U", "V", "W",
153
  "X", "Y", "Z", "bracketleft",
154 155 156 157 158 159 160
  "backslash", "bracketright", "asciicircum", "underscore",
  "quoteleft", "a", "b", "c", "d", "e", "f", "g",
  "h", "i", "j", "k", "l", "m", "n", "o",
  "p", "q", "r", "s", "t", "u", "v", "w",
  "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde", "Delete",
};

161
//----------------------------------------------------------------------------
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
// For generating keysyms that are compatible with other VTK interactors
static const char *vtkMacKeyCodeToKeySymTable[128] = {
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, "Return", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  "Tab", 0, 0, "Backspace", 0, "Escape", 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,
  0, "period", 0, "asterisk", 0, "plus", 0, "Clear",
  0, 0, 0, "slash", "KP_Enter", 0, "minus", 0,
  0, 0, "KP_0", "KP_1", "KP_2", "KP_3", "KP_4", "KP_5",
  "KP_6", "KP_7", 0, "KP_8", "KP_9", 0, 0, 0,
  "F5", "F6", "F7", "F3", "F8", 0, 0, 0,
  0, "Snapshot", 0, 0, 0, 0, 0, 0,
  0, 0, "Help", "Home", "Prior", "Delete", "F4", "End",
  "F2", "Next", "F1", "Left", "Right", "Down", "Up", 0,
};

179
//----------------------------------------------------------------------------
180 181 182
// Convert a Cocoa key event into a VTK key event
- (void)invokeVTKKeyEvent:(unsigned long)theEventId
               cocoaEvent:(NSEvent *)theEvent
Yves Starreveld's avatar
Yves Starreveld committed
183
{
184
  vtkCocoaRenderWindowInteractor *interactor = [self getInteractor];
185
  vtkCocoaRenderWindow *renWin =
186 187
    vtkCocoaRenderWindow::SafeDownCast([self getVTKRenderWindow]);

188
  if (!interactor || !renWin)
189
    {
190
    return;
191
    }
192

193
  // Get the location of the mouse event relative to this NSView's bottom
194
  // left corner.  Since this is NOT a mouse event, we can not use
195 196 197 198 199 200 201
  // locationInWindow.  Instead we get the mouse location at this instant,
  // which may not be the exact location of the mouse at the time of the
  // keypress, but should be quite close.  There seems to be no better way.
  // And, yes, vtk does sometimes need the mouse location even for key
  // events, example: pressing 'p' to pick the actor under the mouse.
  // Also note that 'mouseLoc' may have nonsense values if a key is pressed
  // while the mouse in not actually in the vtk view but the view is
202 203 204 205
  // first responder.
  NSPoint mouseLoc = [[self window] mouseLocationOutsideOfEventStream];
  mouseLoc = [self convertPoint:mouseLoc fromView:nil];

206 207
  NSUInteger flags = [theEvent modifierFlags];
  int shiftDown = ((flags & NSShiftKeyMask) != 0);
208 209
  int controlDown = ((flags & (NSControlKeyMask | NSCommandKeyMask)) != 0);
  int altDown = ((flags & NSAlternateKeyMask) != 0);
210 211

  unsigned char charCode = '\0';
212
  const char *keySym = 0;
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235

  NSEventType type = [theEvent type];
  BOOL isPress = (type == NSKeyDown);

  if (type == NSKeyUp || type == NSKeyDown)
    {
    // Get the characters associated with the key event as a utf8 string.
    // This pointer is only valid for the duration of the current autorelease
    // context!
    const char* keyedChars = [[theEvent characters] UTF8String];
    // Since vtk only supports ASCII, we just blindly use the first element
    // of the above string, hoping it's ASCII.
    charCode = (unsigned char)keyedChars[0];
    // Get the virtual key code and convert it to a keysym as best we can.
    unsigned short macKeyCode = [theEvent keyCode];
    if (macKeyCode < 128)
      {
      keySym = vtkMacKeyCodeToKeySymTable[macKeyCode];
      }
    if (keySym == 0 && charCode < 128)
      {
      keySym = vtkMacCharCodeToKeySymTable[charCode];
      }
236
    }
237
  else if (type == NSFlagsChanged)
238
    {
239
    // Check to see what modifier flag changed.
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
    if (controlDown != interactor->GetControlKey())
      {
      keySym = "Control_L";
      isPress = (controlDown != 0);
      }
    else if (shiftDown != interactor->GetShiftKey())
      {
      keySym = "Shift_L";
      isPress = (shiftDown != 0);
      }
    else if (altDown != interactor->GetAltKey())
      {
      keySym = "Alt_L";
      isPress = (altDown != 0);
      }
    else
      {
      return;
      }

    theEventId = (isPress ?
                  vtkCommand::KeyPressEvent :
                  vtkCommand::KeyReleaseEvent);
263
    }
264 265 266 267
  else // No info from which to generate a VTK key event!
    {
    return;
    }
268

269 270 271 272 273
  if (keySym == 0)
    {
    keySym = "None";
    }

274 275
  interactor->SetEventInformation(static_cast<int>(round(mouseLoc.x)),
                                  static_cast<int>(round(mouseLoc.y)),
276 277 278
                                  controlDown, shiftDown,
                                  charCode, 1, keySym);
  interactor->SetAltKey(altDown);
279

280 281
  interactor->InvokeEvent(theEventId, NULL);
  if (isPress && charCode != '\0')
282 283 284
    {
    interactor->InvokeEvent(vtkCommand::CharEvent, NULL);
    }
285
}
286 287

//----------------------------------------------------------------------------
288 289 290
// Convert a Cocoa motion event into a VTK button event
- (void)invokeVTKMoveEvent:(unsigned long)theEventId
                cocoaEvent:(NSEvent *)theEvent
291
{
292
  vtkCocoaRenderWindowInteractor *interactor = [self getInteractor];
293
  vtkCocoaRenderWindow *renWin =
294 295
    vtkCocoaRenderWindow::SafeDownCast([self getVTKRenderWindow]);

296
  if (!interactor || !renWin)
297
    {
298
    return;
299
    }
300

301
  // Get the location of the mouse event relative to this NSView's bottom
302 303 304
  // left corner. Since this is a mouse event, we can use locationInWindow.
  NSPoint mouseLoc =
    [self convertPoint:[theEvent locationInWindow] fromView:nil];
305

306 307
  NSUInteger flags = [theEvent modifierFlags];
  int shiftDown = ((flags & NSShiftKeyMask) != 0);
308 309
  int controlDown = ((flags & (NSControlKeyMask | NSCommandKeyMask)) != 0);
  int altDown = ((flags & NSAlternateKeyMask) != 0);
310

311 312 313
  interactor->SetEventInformation(static_cast<int>(round(mouseLoc.x)),
                                  static_cast<int>(round(mouseLoc.y)),
                                  controlDown, shiftDown);
314
  interactor->SetAltKey(altDown);
315
  interactor->InvokeEvent(theEventId, NULL);
316 317
}

318
//----------------------------------------------------------------------------
319 320 321
// Convert a Cocoa motion event into a VTK button event
- (void)invokeVTKButtonEvent:(unsigned long)theEventId
                  cocoaEvent:(NSEvent *)theEvent
322 323
{
  vtkCocoaRenderWindowInteractor *interactor = [self getInteractor];
324
  vtkCocoaRenderWindow *renWin =
325 326
    vtkCocoaRenderWindow::SafeDownCast([self getVTKRenderWindow]);

327
  if (!interactor || !renWin)
328 329 330
    {
    return;
    }
331

332
  // Get the location of the mouse event relative to this NSView's bottom
333 334 335
  // left corner. Since this is a mouseevent, we can use locationInWindow.
  NSPoint mouseLoc =
    [self convertPoint:[theEvent locationInWindow] fromView:nil];
336

337 338
  int clickCount = static_cast<int>([theEvent clickCount]);
  int repeatCount = ((clickCount > 1) ? clickCount - 1 : 0);
339

340 341
  NSUInteger flags = [theEvent modifierFlags];
  int shiftDown = ((flags & NSShiftKeyMask) != 0);
342 343
  int controlDown = ((flags & (NSControlKeyMask | NSCommandKeyMask)) != 0);
  int altDown = ((flags & NSAlternateKeyMask) != 0);
344

345 346 347 348 349 350 351
  interactor->SetEventInformation(static_cast<int>(round(mouseLoc.x)),
                                  static_cast<int>(round(mouseLoc.y)),
                                  controlDown, shiftDown,
                                  0, repeatCount);
  interactor->SetAltKey(altDown);
  interactor->InvokeEvent(theEventId, NULL);
}
352

353 354 355 356 357 358 359
//----------------------------------------------------------------------------
// Overridden (from NSResponder).
- (void)keyDown:(NSEvent *)theEvent
{
  [self invokeVTKKeyEvent:vtkCommand::KeyPressEvent
               cocoaEvent:theEvent];
}
360

361 362 363 364 365 366 367 368 369 370 371 372 373 374 375
//----------------------------------------------------------------------------
// Overridden (from NSResponder).
- (void)keyUp:(NSEvent *)theEvent
{
  [self invokeVTKKeyEvent:vtkCommand::KeyReleaseEvent
               cocoaEvent:theEvent];
}

//----------------------------------------------------------------------------
// Overridden (from NSResponder).
- (void)flagsChanged:(NSEvent *)theEvent
{
  // what kind of event it is will be decided by invokeVTKKeyEvent
  [self invokeVTKKeyEvent:vtkCommand::AnyEvent
               cocoaEvent:theEvent];
376
}
377

378
//----------------------------------------------------------------------------
379
// Overridden (from NSResponder).
380 381
- (void)mouseMoved:(NSEvent *)theEvent
{
382 383 384
  // Note: this method will only be called if this view's NSWindow
  // is set to receive mouse moved events.  See setAcceptsMouseMovedEvents:
  // An NSWindow created by vtk automatically does accept such events.
385

386
  // Ignore motion outside the view in order to mimic other interactors
David Gobbi's avatar
David Gobbi committed
387 388
  NSPoint mouseLoc =
    [self convertPoint:[theEvent locationInWindow] fromView:nil];
389
  if (NSPointInRect(mouseLoc, [self visibleRect]))
390
    {
391 392
    [self invokeVTKMoveEvent:vtkCommand::MouseMoveEvent
                  cocoaEvent:theEvent];
393
    }
394
}
395

396 397 398 399 400 401 402
//----------------------------------------------------------------------------
// Overridden (from NSResponder).
- (void)mouseDragged:(NSEvent *)theEvent
{
  [self invokeVTKMoveEvent:vtkCommand::MouseMoveEvent
                cocoaEvent:theEvent];
}
403

404 405 406 407 408 409 410 411 412 413 414 415 416 417
//----------------------------------------------------------------------------
// Overridden (from NSResponder).
- (void)rightMouseDragged:(NSEvent *)theEvent
{
  [self invokeVTKMoveEvent:vtkCommand::MouseMoveEvent
                cocoaEvent:theEvent];
}

//----------------------------------------------------------------------------
// Overridden (from NSResponder).
- (void)otherMouseDragged:(NSEvent *)theEvent
{
  [self invokeVTKMoveEvent:vtkCommand::MouseMoveEvent
                cocoaEvent:theEvent];
418 419
}

420
//----------------------------------------------------------------------------
421
// Overridden (from NSResponder).
422 423
- (void)mouseEntered:(NSEvent *)theEvent
{
424
  // Note: the mouseEntered/mouseExited events depend on the maintenance of
425
  // the tracking area (updateTrackingAreas).
426 427
  [self invokeVTKMoveEvent:vtkCommand::EnterEvent
                cocoaEvent:theEvent];
428 429 430
}

//----------------------------------------------------------------------------
431 432
// Overridden (from NSResponder).
- (void)mouseExited:(NSEvent *)theEvent
433
{
434 435
  [self invokeVTKMoveEvent:vtkCommand::LeaveEvent
                cocoaEvent:theEvent];
436 437
}

438
//----------------------------------------------------------------------------
439
// Overridden (from NSResponder).
440 441
- (void)scrollWheel:(NSEvent *)theEvent
{
442
  CGFloat dy = [theEvent deltaY];
David Gobbi's avatar
David Gobbi committed
443

444 445 446
  unsigned long eventId = 0;

  if (dy > 0)
David Gobbi's avatar
David Gobbi committed
447
    {
448 449 450 451 452 453
    eventId = vtkCommand::MouseWheelForwardEvent;
    }
  else if (dy < 0)
    {
    eventId = vtkCommand::MouseWheelBackwardEvent;
    }
454

455 456
  if (eventId != 0)
    {
457 458
    [self invokeVTKMoveEvent:eventId
                  cocoaEvent:theEvent];
459
    }
460 461
}

462
//----------------------------------------------------------------------------
463
// Overridden (from NSResponder).
464 465
- (void)mouseDown:(NSEvent *)theEvent
{
466 467
  [self invokeVTKButtonEvent:vtkCommand::LeftButtonPressEvent
                  cocoaEvent:theEvent];
468 469
}

470
//----------------------------------------------------------------------------
471
// Overridden (from NSResponder).
472 473
- (void)rightMouseDown:(NSEvent *)theEvent
{
474 475
  [self invokeVTKButtonEvent:vtkCommand::RightButtonPressEvent
                  cocoaEvent:theEvent];
476 477
}

478
//----------------------------------------------------------------------------
479
// Overridden (from NSResponder).
480 481
- (void)otherMouseDown:(NSEvent *)theEvent
{
482 483 484
  [self invokeVTKButtonEvent:vtkCommand::MiddleButtonPressEvent
                  cocoaEvent:theEvent];
}
485

486 487 488 489 490 491 492
//----------------------------------------------------------------------------
// Overridden (from NSResponder).
- (void)mouseUp:(NSEvent *)theEvent
{
  [self invokeVTKButtonEvent:vtkCommand::LeftButtonReleaseEvent
                  cocoaEvent:theEvent];
}
493

494 495 496 497 498 499 500
//----------------------------------------------------------------------------
// Overridden (from NSResponder).
- (void)rightMouseUp:(NSEvent *)theEvent
{
  [self invokeVTKButtonEvent:vtkCommand::RightButtonReleaseEvent
                  cocoaEvent:theEvent];
}
501

502 503 504 505 506 507
//----------------------------------------------------------------------------
// Overridden (from NSResponder).
- (void)otherMouseUp:(NSEvent *)theEvent
{
  [self invokeVTKButtonEvent:vtkCommand::MiddleButtonReleaseEvent
                  cocoaEvent:theEvent];
508
}
509 510

@end