diff --git a/examples/osgsimpleviewerCocoa/English.lproj/InfoPlist.strings b/examples/osgsimpleviewerCocoa/English.lproj/InfoPlist.strings
new file mode 100644
index 000000000..be1baf725
Binary files /dev/null and b/examples/osgsimpleviewerCocoa/English.lproj/InfoPlist.strings differ
diff --git a/examples/osgsimpleviewerCocoa/Info.plist b/examples/osgsimpleviewerCocoa/Info.plist
new file mode 100644
index 000000000..e90fc6f1b
--- /dev/null
+++ b/examples/osgsimpleviewerCocoa/Info.plist
@@ -0,0 +1,28 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ English
+ CFBundleExecutable
+ ${EXECUTABLE_NAME}
+ CFBundleIconFile
+
+ CFBundleIdentifier
+ org.openscenegraph.osgsimpleviewerCocoa
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ ${PRODUCT_NAME}
+ CFBundlePackageType
+ APPL
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1.0
+ NSMainNibFile
+ MainMenu
+ NSPrincipalClass
+ NSApplication
+
+
diff --git a/examples/osgsimpleviewerCocoa/SimpleViewerCocoa.h b/examples/osgsimpleviewerCocoa/SimpleViewerCocoa.h
new file mode 100644
index 000000000..c33185400
--- /dev/null
+++ b/examples/osgsimpleviewerCocoa/SimpleViewerCocoa.h
@@ -0,0 +1,133 @@
+//
+// SimpleViewerCocoa.h
+// osgsimpleviewerCocoa
+//
+// Created by Eric Wing on 11/12/06.
+// Copyright 2006. All rights reserved.
+//
+/* This is the class interface for a custom NSView that interfaces with an osgViewer.
+ * Because Cocoa is written in Objective-C, but OSG is written in C++, we rely on
+ * Objective-C++ to make the integration easy.
+ *
+ * One thing to remember is that any Objective-C files that include this header
+ * must also be compiled as Objective-C++ because there are C++ constructs in
+ * this file (such as namespaces) which the normal Objective-C compiler
+ * won't understand. (The easy way to do this is rename the .m files to .mm.)
+ *
+ * In the event that you have a large, pre-existing code base written in
+ * pure Objective-C and you find that the header include propagates to a
+ * large number of your files, forcing you to mark many files to be compiled as
+ * Objective-C++, and you find that you don't want to change these files because
+ * they are shared with other pure Obj-C projects, you might consider further
+ * wrapping the C++ code so there are only C or Obj-C constructs in
+ * this header. There are several different techniques ranging from, wrapping
+ * the C++ code in pure C interfaces, to simply using void pointers in this
+ * file for any C++ pointers and casting appropriately in the implementation
+ * file.
+ */
+
+
+#import
+
+namespace osgViewer
+{
+ // Just a forward declaration so I don't need the #include in the header.
+ class SimpleViewer;
+}
+
+// Subclass NSOpenGLView. We could subclass NSView instead, but NSOpenGLView is easy.
+@interface SimpleViewerCocoa : NSOpenGLView
+{
+ // Note: In Objective-C++, if you use objects instead of pointers as
+ // member instance variables, you MUST turn on "Call C++ Default Ctors/Dtors in Objective-C".
+ // -fobjc-call-cxx-cdtors
+ // This option makes sure constructors and destructors are run.
+ // This option is only available for gcc 4.0+ (Mac OS X 10.4+)
+
+ // Is SimpleViewer supposed use ref_ptr? (Doesn't look like it to me.)
+ // If so, remember ref_ptr is an object on the stack and the cdtors option must be activated.
+ // We could also make simpleViewer an object instead of a pointer, but again, turn on the option.
+ osgViewer::SimpleViewer* simpleViewer;
+
+ // This timer is used to trigger animation callbacks since everything is event driven.
+ NSTimer* animationTimer;
+
+ // Flags to help track whether ctrl-clicking or option-clicking is being used
+ BOOL isUsingCtrlClick;
+ BOOL isUsingOptionClick;
+}
+
+// My custom static method to create a basic pixel format
++ (NSOpenGLPixelFormat*) basicPixelFormat;
+
+// Official init methods
+- (id) initWithFrame:(NSRect)frame_rect pixelFormat:(NSOpenGLPixelFormat*)pixel_format;
+- (id) initWithCoder:(NSCoder*)the_coder;
+
+// Private init helper methods
+- (void) commonInit;
+- (void) initOSGViewer;
+- (void) initAnimationTimer;
+
+// Official/Special NSOpenGLView method that gets called for you to prepare your OpenGL state.
+- (void) prepareOpenGL;
+// Class dealloc method
+- (void) dealloc;
+
+// Official mouse event methods
+- (void) mouseDown:(NSEvent*)the_event;
+- (void) mouseDragged:(NSEvent*)the_event;
+- (void) mouseUp:(NSEvent*)the_event;
+- (void) rightMouseDown:(NSEvent*)the_event;
+- (void) rightMouseDragged:(NSEvent*)the_event;
+- (void) rightMouseUp:(NSEvent*)the_event;
+- (void) otherMouseDown:(NSEvent*)the_event;
+- (void) otherMouseDragged:(NSEvent*)the_event;
+- (void) otherMouseUp:(NSEvent*)the_event;
+
+// Private setter/getter methods to track ctrl/option-clicking
+- (void) setIsUsingCtrlClick:(BOOL)is_using_ctrl_click;
+- (BOOL) isUsingCtrlClick;
+- (void) setIsUsingOptionClick:(BOOL)is_using_option_click;
+- (BOOL) isUsingOptionClick;
+// Private helper methods to help deal with mouse events
+- (void) doLeftMouseButtonDown:(NSEvent*)the_event;
+- (void) doLeftMouseButtonUp:(NSEvent*)the_event;
+- (void) doRightMouseButtonDown:(NSEvent*)the_event;
+- (void) doRightMouseButtonUp:(NSEvent*)the_event;
+- (void) doMiddleMouseButtonDown:(NSEvent*)the_event;
+- (void) doExtraMouseButtonDown:(NSEvent*)the_event buttonNumber:(int)button_number;
+- (void) doMiddleMouseButtonUp:(NSEvent*)the_event;
+- (void) doExtraMouseButtonUp:(NSEvent*)the_event buttonNumber:(int)button_number;
+
+// Official scrollWheel (scrollball) method
+- (void) scrollWheel:(NSEvent*)the_event;
+
+// Official methods for keyboard events
+- (BOOL) acceptsFirstResponder;
+- (void) keyDown:(NSEvent*)the_event;
+- (void) keyUp:(NSEvent*)the_event;
+
+// My custom method to handle timer callbacks
+- (void) animationCallback;
+
+// Official methods for view stuff and drawing
+- (BOOL) isOpaque;
+- (void) resizeViewport;
+- (void) reshape;
+- (void) drawRect:(NSRect)the_rect;
+
+// Official methods for drag and drop (view as target)
+- (unsigned int) draggingEntered:(id )the_sender;
+- (void) draggingExited:(id )the_sender;
+- (BOOL) prepareForDragOperation:(id )the_sender;
+- (BOOL) performDragOperation:(id )the_sender;
+- (void) concludeDragOperation:(id )the_sender;
+
+
+// Examples of providing an action to connect to.
+- (IBAction) resetPosition:(id)the_sender;
+- (IBAction) takeBackgroundColorFrom:(id)the_sender;
+
+@end
+
diff --git a/examples/osgsimpleviewerCocoa/SimpleViewerCocoa.mm b/examples/osgsimpleviewerCocoa/SimpleViewerCocoa.mm
new file mode 100644
index 000000000..d636982d2
--- /dev/null
+++ b/examples/osgsimpleviewerCocoa/SimpleViewerCocoa.mm
@@ -0,0 +1,757 @@
+//
+// SimpleViewerCocoa.mm
+// osgsimpleviewerCocoa
+//
+// Created by Eric Wing on 11/12/06.
+// Copyright 2006. All rights reserved.
+//
+/* This class demonstrates how to subclass NSOpenGLView to integrate with the
+ * osgViewer::SimpleViewer.
+ * This class demonstrates:
+ * Objective-C++
+ * How to subclass NSOpenGLView
+ * Specifying OpenGL pixel formats
+ * V-sync
+ * Timer based animation
+ * One button and multibutton mice.
+ * Scroll events (Mighty Mouse, Two-finger trackpads)
+ * Keyboard events
+ * Drag and drop (as target)
+ * Resolution Independent UI (maybe, not Leopard tested, only Tiger Quartz Debug)
+ * Target-Action (for other widgets to invoke actions on this view)
+ *
+ * Things not demonstrated by this view or application example (but would be interesting):
+ * Cocoa Bindings (highly recommended)
+ * Core Data (works great with Cocoa Bindings)
+ * Custom Interface Builder palette with Inspector configurable options
+ * Shared OpenGLContexts
+ * More PixelFormat options
+ * Fullscreen mode
+ * Low-level CGL access.
+ * Delegates for your view
+ * Multithreading
+ * Drag-and-drop as a source (similar to the target code)
+ * Copy, Cut, Paste (very similar to drag-and-drop)
+ * Creating (updating) images of the window for the minimized view in the Dock
+ * Printing support
+ * Non-view stuff (Application Delegates, models, controllers)
+ * Launching by double-clicking a model or drag-and-drop onto Application Icon (non-view)
+ * Launching via commandline with parameters (non-view)
+ */
+
+/*
+ * Coding conventions:
+ * My coding style is slightly different than what you normally see in Cocoa.
+ * So here is the cheat sheet:
+ * I hate Hungarian (Microsoft) notation. And prefixed underscore _variables
+ * are technically reserved by the compiler which is why I avoid them. So...
+ * Member variables use camelCase (Pascal/Java)
+ * Local variables use under_scores (Ada)
+ * For methods, I follow standard Cocoa conventions.
+ * I tend to keep * with the type (e.g. NSView* foo) instead of with the variable (NSView *foo).
+ * (I tend to think of the pointer as part of the type.)
+ * For Obj-C named parameters, I tend to keep the namedParameter and the value
+ * together instead of separated by spaces
+ * (e.g. [self initWithX:x_val yVal:y_val zVal:z_val].
+ * (When I was first learning Objective-C, this made it easier for me to
+ * figure out which things * were paired.)
+ */
+#import "SimpleViewerCocoa.h"
+
+#include
+#include
+// Needed to explicitly typecast keys to the OSG type
+#include
+// Needed to load models
+#include
+
+// Used so I can change the background (clear) color
+#include
+#include
+
+// For debugging
+//#include
+
+// osgText is used only to announce that you can use drag-and-drop to load models.
+// osgViewer itself does not have a dependency on osgText.
+#include
+#include
+#include
+
+@implementation SimpleViewerCocoa
+
+// My simple pixel format definition
++ (NSOpenGLPixelFormat*) basicPixelFormat
+{
+ NSOpenGLPixelFormatAttribute pixel_attributes[] =
+ {
+ NSOpenGLPFAWindow,
+ NSOpenGLPFADoubleBuffer, // double buffered
+ NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute)32, // depth buffer size in bits
+ (NSOpenGLPixelFormatAttribute)nil
+ };
+ return [[[NSOpenGLPixelFormat alloc] initWithAttributes:pixel_attributes] autorelease];
+}
+
+////////////////////////////////////////////////////////////////////////
+/////////////////////////// Init Stuff /////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+
+/* This is the designated initializer for an NSOpenGLView. However, since I'm
+ * using Interface Builder to help, initWithCoder: is the initializer that gets called.
+ * But for completeness, I implement this method here.
+ */
+- (id) initWithFrame:(NSRect)frame_rect pixelFormat:(NSOpenGLPixelFormat*)pixel_format
+{
+ self = [super initWithFrame:frame_rect pixelFormat:pixel_format];
+ if(self)
+ {
+ [self commonInit];
+ }
+ return self;
+}
+
+/* Going through the IB palette, this initializer is calling instead of the designated initializer
+ * initWithFrame:pixelFormat:
+ * But for some reason, the pixel format set in IB selected seems to be either ignored or is missing
+ * a value I need. (The depth buffer looks too shallow to me and glErrors are triggered.)
+ * So I explicitly set the pixel format inside here (overriding the IB palette options).
+ * This probably should be investigated, but since IB is getting an overhaul for Leopard,
+ * I'll wait on this for now.
+ */
+- (id) initWithCoder:(NSCoder*)the_coder
+{
+ self = [super initWithCoder:the_coder];
+ if(self)
+ {
+ NSOpenGLPixelFormat* pixel_format = [SimpleViewerCocoa basicPixelFormat];
+ [self setPixelFormat:pixel_format];
+ [self commonInit];
+ }
+ return self;
+}
+
+// My custom methods to centralize common init stuff
+- (void) commonInit
+{
+ isUsingCtrlClick = NO;
+ isUsingOptionClick = NO;
+
+
+ [self initOSGViewer];
+ [self initAnimationTimer];
+
+ // Register for Drag and Drop
+ [self registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, NSURLPboardType, nil]];
+}
+
+// Allocate a SimpleViewer and do basic initialization. No assumption about having an
+// a valid OpenGL context is made by this function.
+- (void) initOSGViewer
+{
+// osg::setNotifyLevel( osg::DEBUG_FP );
+ simpleViewer = new osgViewer::SimpleViewer;
+ // Cocoa follows the same coordinate convention as OpenGL. osgViewer's default is inverted.
+ simpleViewer->getEventQueue()->getCurrentEventState()->setMouseYOrientation(osgGA::GUIEventAdapter::Y_INCREASING_UPWARDS);
+ // Use a trackball manipulator...matches nicely with the Mighty Mouse Scrollball.
+ simpleViewer->setCameraManipulator(new osgGA::TrackballManipulator);
+}
+
+- (void) initAnimationTimer
+{
+ // Cocoa is event driven, so by default, there is nothing to trigger redraws for animation.
+ // The easiest way to animate is to set a repeating NSTimer which triggers a redraw.
+ SEL the_selector;
+ NSMethodSignature* a_signature;
+ NSInvocation* an_invocation;
+ // animationCallback is my animation callback method
+ the_selector = @selector( animationCallback );
+ a_signature = [SimpleViewerCocoa instanceMethodSignatureForSelector:the_selector];
+ an_invocation = [NSInvocation invocationWithMethodSignature:a_signature] ;
+ [an_invocation setSelector:the_selector];
+ [an_invocation setTarget:self];
+
+ animationTimer = [NSTimer
+ scheduledTimerWithTimeInterval:1.0/60.0 // fps
+ invocation:an_invocation
+ repeats:YES];
+ [animationTimer retain];
+
+ // For single threaded apps like this one,
+ // Cocoa seems to block timers or events sometimes. This can be seen
+ // when I'm animating (via a timer) and you open an popup box or move a slider.
+ // Apparently, sheets and dialogs can also block (try printing).
+ // To work around this, Cocoa provides different run-loop modes. I need to
+ // specify the modes to avoid the blockage.
+ // NSDefaultRunLoopMode seems to be the default. I don't think I need to explicitly
+ // set this one, but just in case, I will set it anyway.
+ [[NSRunLoop currentRunLoop] addTimer:animationTimer forMode:NSDefaultRunLoopMode];
+ // This seems to be the one for preventing blocking on other events (popup box, slider, etc)
+ [[NSRunLoop currentRunLoop] addTimer:animationTimer forMode:NSEventTrackingRunLoopMode];
+ // This seems to be the one for dialogs.
+ [[NSRunLoop currentRunLoop] addTimer:animationTimer forMode:NSModalPanelRunLoopMode];
+}
+
+- (void) dealloc
+{
+ [animationTimer invalidate];
+ [animationTimer release];
+ delete simpleViewer;
+ [super dealloc];
+}
+
+/* NSOpenGLView defines this method to be called (only once) after the OpenGL
+ * context is created and made the current context. It is intended to be used to setup
+ * your initial OpenGL state. This seems like a good place to initialize the
+ * OSG stuff. This method exists in 10.3 and later. If you are running pre-10.3, you
+ * must manually call this method sometime after the OpenGL context is created and
+ * made current, or refactor this code.
+ */
+- (void) prepareOpenGL
+{
+ [super prepareOpenGL];
+
+ // The NSOpenGLCPSwapInterval seems to be vsync. If 1, buffers are swapped with vertical refresh.
+ // If 0, flushBuffer will execute as soon as possible.
+ long swap_interval = 1 ;
+ [[self openGLContext] setValues:&swap_interval forParameter:NSOpenGLCPSwapInterval];
+
+ // This is also might be a good place to setup OpenGL state that OSG doesn't control.
+ glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
+ glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
+ glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
+ glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
+
+ // We need to tell the osgViewer what the viewport size is
+ [self resizeViewport];
+
+
+ // This is optional:
+ // This is to setup some default text in the OpenGL view so the
+ // user knows that they should drag and drop a model into the view.
+ osg::ref_ptr default_text = new osgText::Text;
+ default_text->setAlignment(osgText::Text::CENTER_CENTER);
+ default_text->setBackdropType(osgText::Text::OUTLINE);
+ default_text->setAxisAlignment(osgText::Text::XZ_PLANE);
+ default_text->setText("Drag-and-Drop\nyour .osg model here!");
+ osg::ref_ptr the_geode = new osg::Geode;
+ the_geode->addDrawable(default_text.get());
+ simpleViewer->setSceneData(the_geode.get());
+
+}
+
+////////////////////////////////////////////////////////////////////////
+/////////////////////////// End Init Stuff /////////////////////////////
+////////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////////
+/////////////////////////// Mouse Stuff ////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+
+- (void) mouseDown:(NSEvent*)the_event
+{
+ // Because many Mac users have only a 1-button mouse, we should provide ways
+ // to access the button 2 and 3 actions of osgViewer.
+ // I will use the Ctrl modifer to represent right-clicking
+ // and Option modifier to represent middle clicking.
+ if([the_event modifierFlags] & NSControlKeyMask)
+ {
+ [self setIsUsingCtrlClick:YES];
+ [self doRightMouseButtonDown:the_event];
+ }
+ else if([the_event modifierFlags] & NSAlternateKeyMask)
+ {
+ [self setIsUsingOptionClick:YES];
+ [self doMiddleMouseButtonDown:the_event];
+ }
+ else
+ {
+ [self doLeftMouseButtonDown:the_event];
+ }
+}
+
+- (void) mouseDragged:(NSEvent*)the_event
+{
+ // We must convert the mouse event locations from the window coordinate system to the
+ // local view coordinate system.
+ NSPoint the_point = [the_event locationInWindow];
+ NSPoint converted_point = [self convertPoint:the_point fromView:nil];
+
+ simpleViewer->getEventQueue()->mouseMotion(converted_point.x, converted_point.y);
+ [self setNeedsDisplay:YES];
+}
+
+- (void) mouseUp:(NSEvent*)the_event
+{
+ // Because many Mac users have only a 1-button mouse, we should provide ways
+ // to access the button 2 and 3 actions of osgViewer.
+ // I will use the Ctrl modifer to represent right-clicking
+ // and Option modifier to represent middle clicking.
+ if([self isUsingCtrlClick] == YES)
+ {
+ [self setIsUsingCtrlClick:NO];
+ [self doRightMouseButtonUp:the_event];
+ }
+ if([self isUsingOptionClick] == YES)
+ {
+ [self setIsUsingOptionClick:NO];
+ [self doMiddleMouseButtonUp:the_event];
+ }
+ else
+ {
+ [self doLeftMouseButtonUp:the_event];
+ }
+}
+
+- (void) rightMouseDown:(NSEvent*)the_event
+{
+ [self doRightMouseButtonDown:the_event];
+}
+
+- (void) rightMouseDragged:(NSEvent*)the_event
+{
+ // We must convert the mouse event locations from the window coordinate system to the
+ // local view coordinate system.
+ NSPoint the_point = [the_event locationInWindow];
+ NSPoint converted_point = [self convertPoint:the_point fromView:nil];
+
+ simpleViewer->getEventQueue()->mouseMotion(converted_point.x, converted_point.y);
+ [self setNeedsDisplay:YES];
+}
+
+- (void) rightMouseUp:(NSEvent*)the_event
+{
+ [self doRightMouseButtonUp:the_event];
+}
+
+// "otherMouse" seems to capture middle button and any other buttons beyond (4th, etc).
+- (void) otherMouseDown:(NSEvent*)the_event
+{
+ // We must convert the mouse event locations from the window coordinate system to the
+ // local view coordinate system.
+ NSPoint the_point = [the_event locationInWindow];
+ NSPoint converted_point = [self convertPoint:the_point fromView:nil];
+ // Button 0 is left
+ // Button 1 is right
+ // Button 2 is middle
+ // Button 3 keeps going
+ // osgViewer expects 1 for left, 3 for right, 2 for middle
+ // osgViewer has a reversed number mapping for right and middle compared to Cocoa
+ if([the_event buttonNumber] == 2)
+ {
+ [self doMiddleMouseButtonDown:the_event];
+ }
+ else // buttonNumber should be 3,4,5,etc; must map to 4,5,6,etc in osgViewer
+ {
+ [self doExtraMouseButtonDown:the_event buttonNumber:[the_event buttonNumber]];
+ }
+}
+
+- (void) otherMouseDragged:(NSEvent*)the_event
+{
+ // We must convert the mouse event locations from the window coordinate system to the
+ // local view coordinate system.
+ NSPoint the_point = [the_event locationInWindow];
+ NSPoint converted_point = [self convertPoint:the_point fromView:nil];
+
+ simpleViewer->getEventQueue()->mouseMotion(converted_point.x, converted_point.y);
+ [self setNeedsDisplay:YES];
+}
+
+// "otherMouse" seems to capture middle button and any other buttons beyond (4th, etc).
+- (void) otherMouseUp:(NSEvent*)the_event
+{
+ // Button 0 is left
+ // Button 1 is right
+ // Button 2 is middle
+ // Button 3 keeps going
+ // osgViewer expects 1 for left, 3 for right, 2 for middle
+ // osgViewer has a reversed number mapping for right and middle compared to Cocoa
+ if([the_event buttonNumber] == 2)
+ {
+ [self doMiddleMouseButtonUp:the_event];
+ }
+ else // buttonNumber should be 3,4,5,etc; must map to 4,5,6,etc in osgViewer
+ {
+ // I don't think osgViewer does anything for these additional buttons,
+ // but just in case, pass them along. But as a Cocoa programmer, you might
+ // think about things you can do natively here instead of passing the buck.
+ } [self doExtraMouseButtonUp:the_event buttonNumber:[the_event buttonNumber]];
+}
+
+- (void) setIsUsingCtrlClick:(BOOL)is_using_ctrl_click
+{
+ isUsingCtrlClick = is_using_ctrl_click;
+}
+
+- (BOOL) isUsingCtrlClick
+{
+ return isUsingCtrlClick;
+}
+
+- (void) setIsUsingOptionClick:(BOOL)is_using_option_click
+{
+ isUsingOptionClick = is_using_option_click;
+}
+
+- (BOOL) isUsingOptionClick
+{
+ return isUsingOptionClick;
+}
+
+- (void) doLeftMouseButtonDown:(NSEvent*)the_event
+{
+ // We must convert the mouse event locations from the window coordinate system to the
+ // local view coordinate system.
+ NSPoint the_point = [the_event locationInWindow];
+ NSPoint converted_point = [self convertPoint:the_point fromView:nil];
+ if([the_event clickCount] == 1)
+ {
+ simpleViewer->getEventQueue()->mouseButtonPress(converted_point.x, converted_point.y, 1);
+ }
+ else
+ {
+ simpleViewer->getEventQueue()->mouseDoubleButtonPress(converted_point.x, converted_point.y, 1);
+ }
+ [self setNeedsDisplay:YES];
+}
+
+- (void) doLeftMouseButtonUp:(NSEvent*)the_event
+{
+ // We must convert the mouse event locations from the window coordinate system to the
+ // local view coordinate system.
+ NSPoint the_point = [the_event locationInWindow];
+ NSPoint converted_point = [self convertPoint:the_point fromView:nil];
+
+ simpleViewer->getEventQueue()->mouseButtonRelease(converted_point.x, converted_point.y, 1);
+ [self setNeedsDisplay:YES];
+}
+
+- (void) doRightMouseButtonDown:(NSEvent*)the_event
+{
+ // We must convert the mouse event locations from the window coordinate system to the
+ // local view coordinate system.
+ NSPoint the_point = [the_event locationInWindow];
+ NSPoint converted_point = [self convertPoint:the_point fromView:nil];
+ if([the_event clickCount] == 1)
+ {
+ simpleViewer->getEventQueue()->mouseButtonPress(converted_point.x, converted_point.y, 3);
+ }
+ else
+ {
+ simpleViewer->getEventQueue()->mouseDoubleButtonPress(converted_point.x, converted_point.y, 3);
+ }
+ [self setNeedsDisplay:YES];
+}
+
+
+- (void) doRightMouseButtonUp:(NSEvent*)the_event
+{
+ // We must convert the mouse event locations from the window coordinate system to the
+ // local view coordinate system.
+ NSPoint the_point = [the_event locationInWindow];
+ NSPoint converted_point = [self convertPoint:the_point fromView:nil];
+
+ simpleViewer->getEventQueue()->mouseButtonRelease(converted_point.x, converted_point.y, 3);
+ [self setNeedsDisplay:YES];
+}
+
+- (void) doMiddleMouseButtonDown:(NSEvent*)the_event
+{
+ // We must convert the mouse event locations from the window coordinate system to the
+ // local view coordinate system.
+ NSPoint the_point = [the_event locationInWindow];
+ NSPoint converted_point = [self convertPoint:the_point fromView:nil];
+
+ if([the_event clickCount] == 1)
+ {
+ simpleViewer->getEventQueue()->mouseButtonPress(converted_point.x, converted_point.y, 2);
+ }
+ else
+ {
+ simpleViewer->getEventQueue()->mouseDoubleButtonPress(converted_point.x, converted_point.y, 2);
+ }
+ [self setNeedsDisplay:YES];
+}
+
+- (void) doExtraMouseButtonDown:(NSEvent*)the_event buttonNumber:(int)button_number
+{
+ // We must convert the mouse event locations from the window coordinate system to the
+ // local view coordinate system.
+ NSPoint the_point = [the_event locationInWindow];
+ NSPoint converted_point = [self convertPoint:the_point fromView:nil];
+
+ if([the_event clickCount] == 1)
+ {
+ simpleViewer->getEventQueue()->mouseButtonPress(converted_point.x, converted_point.y, button_number+1);
+ }
+ else
+ {
+ simpleViewer->getEventQueue()->mouseDoubleButtonPress(converted_point.x, converted_point.y, button_number+1);
+ }
+ [self setNeedsDisplay:YES];
+}
+
+
+- (void) doMiddleMouseButtonUp:(NSEvent*)the_event
+{
+ // We must convert the mouse event locations from the window coordinate system to the
+ // local view coordinate system. NSPoint the_point = [the_event locationInWindow];
+ NSPoint the_point = [the_event locationInWindow];
+ NSPoint converted_point = [self convertPoint:the_point fromView:nil];
+ simpleViewer->getEventQueue()->mouseButtonRelease(converted_point.x, converted_point.y, 2);
+ [self setNeedsDisplay:YES];
+}
+
+- (void) doExtraMouseButtonUp:(NSEvent*)the_event buttonNumber:(int)button_number
+{
+ // We must convert the mouse event locations from the window coordinate system to the
+ // local view coordinate system. NSPoint the_point = [the_event locationInWindow];
+ NSPoint the_point = [the_event locationInWindow];
+ NSPoint converted_point = [self convertPoint:the_point fromView:nil];
+ simpleViewer->getEventQueue()->mouseButtonRelease(converted_point.x, converted_point.y, button_number+1);
+
+ [self setNeedsDisplay:YES];
+}
+
+// This is a job for Mighty Mouse!
+// For the most fluid experience turn on 360 degree mode availble in 10.4.8+.
+// With your Mighty Mouse plugged in,
+// open 'Keyboard & Mouse' in 'System Preferences'.
+// Select the 'Mouse' tab.
+// Under 'Scrolling Options' select '360 degree'.
+// That should improve diagonal scrolling.
+// You should also be able to use 'two-finger scrolling' on newer laptops.
+- (void) scrollWheel:(NSEvent*)the_event
+{
+ // Unfortunately, it turns out mouseScroll2D doesn't actually do anything.
+ // The camera manipulators don't seem to implement any code that utilize the scroll values.
+ // This this call does nothing.
+// simpleViewer->getEventQueue()->mouseScroll2D([the_event deltaX], [the_event deltaY]);
+
+ // With the absense of a useful mouseScroll2D API, we can manually simulate the desired effect.
+ NSPoint the_point = [the_event locationInWindow];
+ NSPoint converted_point = [self convertPoint:the_point fromView:nil];
+ simpleViewer->getEventQueue()->mouseButtonPress(converted_point.x, converted_point.y, 1);
+ simpleViewer->getEventQueue()->mouseMotion(converted_point.x + -[the_event deltaX], converted_point.y + [the_event deltaY]);
+ simpleViewer->getEventQueue()->mouseButtonRelease(converted_point.x + -[the_event deltaX], converted_point.y + [the_event deltaY], 1);
+
+ [self setNeedsDisplay:YES];
+}
+
+////////////////////////////////////////////////////////////////////////
+/////////////////////////// End Mouse Stuff ////////////////////////////
+////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////
+/////////////////////////// Keyboard Stuff /////////////////////////////
+////////////////////////////////////////////////////////////////////////
+// Needed to accept keyboard events
+- (BOOL) acceptsFirstResponder
+{
+ return YES;
+}
+
+- (void) keyDown:(NSEvent*)the_event
+{
+ // Do you want characters or charactersIgnoringModifiers?
+ NSString* event_characters = [the_event characters];
+// NSString* event_characters = [the_event charactersIgnoringModifiers];
+
+ unichar unicode_character = [event_characters characterAtIndex:0];
+// NSLog(@"unicode_character: %d", unicode_character);
+ simpleViewer->getEventQueue()->keyPress(static_cast(unicode_character));
+
+ [self setNeedsDisplay:YES];
+}
+
+- (void) keyUp:(NSEvent*)the_event
+{
+ // Do you want characters or charactersIgnoringModifiers?
+ NSString* event_characters = [the_event characters];
+// NSString* event_characters = [the_event charactersIgnoringModifiers];
+ unichar unicode_character = [event_characters characterAtIndex:0];
+ simpleViewer->getEventQueue()->keyRelease(static_cast(unicode_character));
+ [self setNeedsDisplay:YES];
+}
+
+////////////////////////////////////////////////////////////////////////
+/////////////////////////// End Keyboard Stuff /////////////////////////
+////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////
+/////////////////////////// View and Draw Stuff ////////////////////////
+////////////////////////////////////////////////////////////////////////
+
+// This method is periodically called by my timer.
+- (void) animationCallback
+{
+ // Simply notify Cocoa that a drawRect needs to take place.
+ // Potential optimization is to query the OSG stuff to find out if a redraw is actually necessary.
+ [self setNeedsDisplay:YES];
+}
+
+// This is an optional optimization. This states you don't have a transparent view/window.
+// Obviously don't use this or set it to NO if you intend for your view to be see-through.
+- (BOOL) isOpaque
+{
+ return YES;
+}
+
+// Resolution Independent UI is coming... (Tiger's Quartz Debug already has the tool.)
+// We must think in 'point sizes', not pixel sizes, so a conversion is needed for OpenGL.
+- (void) resizeViewport
+{
+ NSSize size_in_points = [self bounds].size;
+ // This coordinate system conversion seems to make things work with Quartz Debug.
+ NSSize size_in_window_coordinates = [self convertSize:size_in_points toView:nil];
+ simpleViewer->getEventQueue()->windowResize(0, 0, size_in_window_coordinates.width, size_in_window_coordinates.height);
+}
+
+// For window resize
+- (void) reshape
+{
+ [super reshape];
+ [self resizeViewport];
+}
+
+// This is the code that actually draws.
+// Remember you shouldn't call drawRect: directly and should use setNeedsDisplay:YES
+// This is so the operating system can optimize when a draw is actually needed.
+// (e.g. No sense drawing when the application is hidden.)
+- (void) drawRect:(NSRect)the_rect
+{
+ [[self openGLContext] makeCurrentContext];
+ simpleViewer->frame();
+ [[self openGLContext] flushBuffer];
+}
+////////////////////////////////////////////////////////////////////////
+/////////////////////////// End View and Draw Stuff ////////////////////
+////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////
+/////////////////////////// For drag and drop //////////////////////////
+////////////////////////////////////////////////////////////////////////
+- (unsigned int) draggingEntered:(id )the_sender
+{
+ if([the_sender draggingSource] != self)
+ {
+ NSPasteboard* paste_board = [the_sender draggingPasteboard];
+ // I respond to filename types or URL types
+ NSArray* supported_types = [NSArray arrayWithObjects:NSFilenamesPboardType, NSURLPboardType, nil];
+ // If any of the supported types are being dragged in, activate the copy operation
+ NSString* first_type = [paste_board availableTypeFromArray:supported_types];
+ if(first_type != nil)
+ {
+ return NSDragOperationCopy;
+ }
+ }
+ // Means we don't support this type
+ return NSDragOperationNone;
+}
+
+// We're not using this method, but here it is as an example.
+- (void) draggingExited:(id )the_sender
+{
+}
+
+- (BOOL) prepareForDragOperation:(id )the_sender
+{
+ return YES;
+}
+
+- (BOOL) performDragOperation:(id )the_sender
+{
+ NSPasteboard* paste_board = [the_sender draggingPasteboard];
+
+
+ if([[paste_board types] containsObject:NSFilenamesPboardType])
+ {
+ NSArray* file_names = [paste_board propertyListForType:NSFilenamesPboardType];
+// int number_of_files = [file_names count];
+ // Exercise for the reader: Try loading all files in the array
+ NSString* single_file = [file_names objectAtIndex:0];
+ osg::ref_ptr loaded_model = osgDB::readNodeFile([single_file UTF8String]);
+ if(!loaded_model)
+ {
+ NSLog(@"File: %@ failed to load", single_file);
+ return NO;
+ }
+ simpleViewer->setSceneData(loaded_model.get());
+ return YES;
+ }
+ else if([[paste_board types] containsObject:NSURLPboardType])
+ {
+ NSURL* file_url = [NSURL URLFromPasteboard:paste_board];
+ // See if the URL is valid file path
+ if(![file_url isFileURL])
+ {
+ NSLog(@"URL: %@ needs to be a file for readNodeFile()", file_url);
+ return NO;
+ }
+ NSString* file_path = [file_url path];
+ osg::ref_ptr loaded_model = osgDB::readNodeFile([file_path UTF8String]);
+ if(!loaded_model)
+ {
+ NSLog(@"URL: %@ failed to load, %@", file_url, file_path);
+ return NO;
+ }
+ simpleViewer->setSceneData(loaded_model.get());
+ return YES;
+ }
+ return NO;
+}
+
+// This method isn't really needed (I could move setNeedsDisplay up), but is here as an example
+- (void) concludeDragOperation:(id )the_sender
+{
+ [self setNeedsDisplay:YES];
+}
+
+////////////////////////////////////////////////////////////////////////
+/////////////////////////// End of drag and drop ///////////////////////
+////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////
+/////////////////////////// IBAction examples /////////////////////////
+////////////////////////////////////////////////////////////////////////
+
+// Connect a button to this to stop and reset the position.
+- (IBAction) resetPosition:(id)the_sender
+{
+ // osgGA::MatrixManipulator* camera_manipulator = simpleViewer->getCameraManipulator();
+ // This only resets the position
+ // camera_manipulator->home(0.0);
+
+ // There is no external API from SimpleViewer that I can see that will stop movement.
+ // So fake the 'spacebar' to stop things and reset.
+ simpleViewer->getEventQueue()->keyPress(static_cast(0x20));
+ [self setNeedsDisplay:YES];
+}
+
+// Connect a NSColorWell to this to change color.
+// A better way to do this is use Cocoa Bindings because it will automatically
+// synchronize between your model and view, but since this demo lacks a model and controller
+// aspect it wouldn't do much good.
+- (IBAction) takeBackgroundColorFrom:(id)the_sender
+{
+ NSColor* the_color = [the_sender color];
+ osgUtil::SceneView* scene_view = simpleViewer->getSceneView();
+
+ scene_view->setClearColor(
+ osg::Vec4(
+ [the_color redComponent],
+ [the_color greenComponent],
+ [the_color blueComponent],
+ [the_color alphaComponent]
+ )
+ );
+ [self setNeedsDisplay:YES];
+}
+
+////////////////////////////////////////////////////////////////////////
+/////////////////////////// End IBAction examples /////////////////////
+////////////////////////////////////////////////////////////////////////
+
+@end
diff --git a/examples/osgsimpleviewerCocoa/main.m b/examples/osgsimpleviewerCocoa/main.m
new file mode 100644
index 000000000..ebebb4fb7
--- /dev/null
+++ b/examples/osgsimpleviewerCocoa/main.m
@@ -0,0 +1,14 @@
+//
+// main.m
+// osgsimpleviewerCocoa
+//
+// Created by Eric Wing on 11/13/06.
+// Copyright __MyCompanyName__ 2006. All rights reserved.
+//
+
+#import
+
+int main(int argc, char *argv[])
+{
+ return NSApplicationMain(argc, (const char **) argv);
+}