Blender V2.61 - r43446

GHOST_WindowCocoa.mm

Go to the documentation of this file.
00001 /*
00002  * ***** BEGIN GPL LICENSE BLOCK *****
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License
00006  * as published by the Free Software Foundation; either version 2
00007  * of the License, or (at your option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software Foundation,
00016  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  *
00018  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
00019  * All rights reserved.
00020  *
00021  * The Original Code is: all of this file.
00022  *
00023  * Contributor(s):  Maarten Gribnau 05/2001
00024                     Damien Plisson 10/2009
00025  *
00026  * ***** END GPL LICENSE BLOCK *****
00027  */
00028 
00029 #include <Cocoa/Cocoa.h>
00030 
00031 #ifndef MAC_OS_X_VERSION_10_6
00032 //Use of the SetSystemUIMode function (64bit compatible)
00033 #include <Carbon/Carbon.h>
00034 #endif
00035 
00036 #include <OpenGL/gl.h>
00037 #include <OpenGL/CGLRenderers.h>
00038 /***** Multithreaded opengl code : uncomment for enabling
00039 #include <OpenGL/OpenGL.h>
00040 */
00041 
00042  
00043 #include "GHOST_WindowCocoa.h"
00044 #include "GHOST_SystemCocoa.h"
00045 #include "GHOST_Debug.h"
00046 
00047 
00048 #pragma mark Cocoa window delegate object
00049 /* live resize ugly patch
00050 extern "C" {
00051     struct bContext;
00052     typedef struct bContext bContext;
00053     bContext* ghostC;
00054     extern int wm_window_timer(const bContext *C);
00055     extern void wm_window_process_events(const bContext *C);
00056     extern void wm_event_do_handlers(bContext *C);
00057     extern void wm_event_do_notifiers(bContext *C);
00058     extern void wm_draw_update(bContext *C);
00059 };*/
00060 @interface CocoaWindowDelegate : NSObject
00061 #ifdef MAC_OS_X_VERSION_10_6
00062 <NSWindowDelegate>
00063 #endif
00064 {
00065     GHOST_SystemCocoa *systemCocoa;
00066     GHOST_WindowCocoa *associatedWindow;
00067 }
00068 
00069 - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa;
00070 - (void)windowWillClose:(NSNotification *)notification;
00071 - (void)windowDidBecomeKey:(NSNotification *)notification;
00072 - (void)windowDidResignKey:(NSNotification *)notification;
00073 - (void)windowDidExpose:(NSNotification *)notification;
00074 - (void)windowDidResize:(NSNotification *)notification;
00075 - (void)windowDidMove:(NSNotification *)notification;
00076 - (void)windowWillMove:(NSNotification *)notification;
00077 @end
00078 
00079 @implementation CocoaWindowDelegate : NSObject
00080 - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa
00081 {
00082     systemCocoa = sysCocoa;
00083     associatedWindow = winCocoa;
00084 }
00085 
00086 - (void)windowWillClose:(NSNotification *)notification
00087 {
00088     systemCocoa->handleWindowEvent(GHOST_kEventWindowClose, associatedWindow);
00089 }
00090 
00091 - (void)windowDidBecomeKey:(NSNotification *)notification
00092 {
00093     systemCocoa->handleWindowEvent(GHOST_kEventWindowActivate, associatedWindow);
00094 }
00095 
00096 - (void)windowDidResignKey:(NSNotification *)notification
00097 {
00098     systemCocoa->handleWindowEvent(GHOST_kEventWindowDeactivate, associatedWindow);
00099 }
00100 
00101 - (void)windowDidExpose:(NSNotification *)notification
00102 {
00103     systemCocoa->handleWindowEvent(GHOST_kEventWindowUpdate, associatedWindow);
00104 }
00105 
00106 - (void)windowDidMove:(NSNotification *)notification
00107 {
00108     systemCocoa->handleWindowEvent(GHOST_kEventWindowMove, associatedWindow);
00109 }
00110 
00111 - (void)windowWillMove:(NSNotification *)notification
00112 {
00113     systemCocoa->handleWindowEvent(GHOST_kEventWindowMove, associatedWindow);
00114 }
00115 
00116 - (void)windowDidResize:(NSNotification *)notification
00117 {
00118 #ifdef MAC_OS_X_VERSION_10_6
00119     //if (![[notification object] inLiveResize]) {
00120         //Send event only once, at end of resize operation (when user has released mouse button)
00121 #endif
00122         systemCocoa->handleWindowEvent(GHOST_kEventWindowSize, associatedWindow);
00123 #ifdef MAC_OS_X_VERSION_10_6
00124     //}
00125 #endif
00126     /* Live resize ugly patch. Needed because live resize runs in a modal loop, not letting main loop run
00127      if ([[notification object] inLiveResize]) {
00128         systemCocoa->dispatchEvents();
00129         wm_window_timer(ghostC);
00130         wm_event_do_handlers(ghostC);
00131         wm_event_do_notifiers(ghostC);
00132         wm_draw_update(ghostC);
00133     }*/
00134 }
00135 @end
00136 
00137 #pragma mark NSWindow subclass
00138 //We need to subclass it to tell that even borderless (fullscreen), it can become key (receive user events)
00139 @interface CocoaWindow: NSWindow
00140 {
00141     GHOST_SystemCocoa *systemCocoa;
00142     GHOST_WindowCocoa *associatedWindow;
00143     GHOST_TDragnDropTypes m_draggedObjectType;
00144 }
00145 - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa;
00146 - (GHOST_SystemCocoa*)systemCocoa;
00147 @end
00148 @implementation CocoaWindow
00149 - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa
00150 {
00151     systemCocoa = sysCocoa;
00152     associatedWindow = winCocoa;
00153 }
00154 - (GHOST_SystemCocoa*)systemCocoa
00155 {
00156     return systemCocoa;
00157 }
00158 
00159 -(BOOL)canBecomeKeyWindow
00160 {
00161     return YES;
00162 }
00163 
00164 //The drag'n'drop dragging destination methods
00165 - (NSDragOperation)draggingEntered:(id < NSDraggingInfo >)sender
00166 {
00167     NSPoint mouseLocation = [sender draggingLocation];
00168     NSPasteboard *draggingPBoard = [sender draggingPasteboard];
00169     
00170     if ([[draggingPBoard types] containsObject:NSTIFFPboardType]) m_draggedObjectType = GHOST_kDragnDropTypeBitmap;
00171     else if ([[draggingPBoard types] containsObject:NSFilenamesPboardType]) m_draggedObjectType = GHOST_kDragnDropTypeFilenames;
00172     else if ([[draggingPBoard types] containsObject:NSStringPboardType]) m_draggedObjectType = GHOST_kDragnDropTypeString;
00173     else return NSDragOperationNone;
00174     
00175     associatedWindow->setAcceptDragOperation(TRUE); //Drag operation is accepted by default
00176     systemCocoa->handleDraggingEvent(GHOST_kEventDraggingEntered, m_draggedObjectType, associatedWindow, mouseLocation.x, mouseLocation.y, nil);
00177     return NSDragOperationCopy;
00178 }
00179 
00180 - (BOOL)wantsPeriodicDraggingUpdates
00181 {
00182     return NO; //No need to overflow blender event queue. Events shall be sent only on changes
00183 }
00184 
00185 - (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender
00186 {
00187     NSPoint mouseLocation = [sender draggingLocation];
00188     
00189     systemCocoa->handleDraggingEvent(GHOST_kEventDraggingUpdated, m_draggedObjectType, associatedWindow, mouseLocation.x, mouseLocation.y, nil);
00190     return associatedWindow->canAcceptDragOperation()?NSDragOperationCopy:NSDragOperationNone;
00191 }
00192 
00193 - (void)draggingExited:(id < NSDraggingInfo >)sender
00194 {
00195     systemCocoa->handleDraggingEvent(GHOST_kEventDraggingExited, m_draggedObjectType, associatedWindow, 0, 0, nil);
00196     m_draggedObjectType = GHOST_kDragnDropTypeUnknown;
00197 }
00198 
00199 - (BOOL)prepareForDragOperation:(id < NSDraggingInfo >)sender
00200 {
00201     if (associatedWindow->canAcceptDragOperation())
00202         return YES;
00203     else
00204         return NO;
00205 }
00206 
00207 - (BOOL)performDragOperation:(id < NSDraggingInfo >)sender
00208 {
00209     NSPoint mouseLocation = [sender draggingLocation];
00210     NSPasteboard *draggingPBoard = [sender draggingPasteboard];
00211     NSImage *droppedImg;
00212     id data;
00213     
00214     switch (m_draggedObjectType) {
00215         case GHOST_kDragnDropTypeBitmap:
00216             if([NSImage canInitWithPasteboard:draggingPBoard]) {
00217                 droppedImg = [[NSImage alloc]initWithPasteboard:draggingPBoard];
00218                 data = droppedImg; //[draggingPBoard dataForType:NSTIFFPboardType];
00219             }
00220             else return NO;
00221             break;
00222         case GHOST_kDragnDropTypeFilenames:
00223             data = [draggingPBoard propertyListForType:NSFilenamesPboardType];
00224             break;
00225         case GHOST_kDragnDropTypeString:
00226             data = [draggingPBoard stringForType:NSStringPboardType];
00227             break;
00228         default:
00229             return NO;
00230             break;
00231     }
00232     systemCocoa->handleDraggingEvent(GHOST_kEventDraggingDropDone, m_draggedObjectType, associatedWindow, mouseLocation.x, mouseLocation.y, (void*)data);
00233     return YES;
00234 }
00235 
00236 @end
00237 
00238 
00239 
00240 #pragma mark NSOpenGLView subclass
00241 //We need to subclass it in order to give Cocoa the feeling key events are trapped
00242 @interface CocoaOpenGLView : NSOpenGLView <NSTextInput>
00243 {
00244     GHOST_SystemCocoa *systemCocoa;
00245     GHOST_WindowCocoa *associatedWindow;
00246 
00247        bool composing;
00248        NSString *composing_text;
00249 }
00250 - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa;
00251 @end
00252 @implementation CocoaOpenGLView
00253 
00254 - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa
00255 {
00256     systemCocoa = sysCocoa;
00257     associatedWindow = winCocoa;
00258 
00259        composing = false;
00260        composing_text = nil;
00261 }
00262 
00263 - (BOOL)acceptsFirstResponder
00264 {
00265     return YES;
00266 }
00267 
00268 // The trick to prevent Cocoa from complaining (beeping)
00269 - (void)keyDown:(NSEvent *)event
00270 {
00271        // Start or continue composing?
00272        if([[event characters] length] == 0  ||
00273           [[event charactersIgnoringModifiers] length] == 0 ||
00274           composing) {
00275                composing = YES;
00276  
00277                // interpret event to call insertText
00278                NSMutableArray *events;
00279                events = [[NSMutableArray alloc] initWithCapacity:1];
00280                [events addObject:event];
00281                [self interpretKeyEvents:events]; // calls insertText
00282                [events removeObject:event];
00283                [events release];
00284 
00285                return;
00286        }
00287 }
00288 
00289 #if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4
00290 //Cmd+key are handled differently before 10.5
00291 - (BOOL)performKeyEquivalent:(NSEvent *)theEvent
00292 {
00293     NSString *chars = [theEvent charactersIgnoringModifiers];
00294     
00295     if ([chars length] <1) 
00296         return NO;
00297     
00298     //Let cocoa handle menu shortcuts
00299     switch ([chars characterAtIndex:0]) {
00300         case 'q':
00301         case 'w':
00302         case 'h':
00303         case 'm':
00304         case '<':
00305         case '>':
00306         case '~':
00307         case '`':
00308             return NO;
00309         default:
00310             return YES;
00311     }
00312 }
00313 #endif
00314 
00315 - (BOOL)isOpaque
00316 {
00317     return YES;
00318 }
00319 
00320 - (void) drawRect:(NSRect)rect
00321 {
00322     if ([self inLiveResize])
00323     {
00324         //Don't redraw while in live resize
00325     }
00326     else
00327     {
00328         [super drawRect:rect];
00329         systemCocoa->handleWindowEvent(GHOST_kEventWindowUpdate, associatedWindow);
00330     }
00331 }
00332 
00333 // Text input
00334 
00335 - (void)composing_free
00336 {
00337        composing = NO;
00338 
00339        if(composing_text) {
00340                [composing_text release];
00341                composing_text = nil;
00342        }
00343 }
00344 
00345 - (void)insertText:(id)chars
00346 {
00347        [self composing_free];
00348 }
00349 
00350 - (void)setMarkedText:(id)chars selectedRange:(NSRange)range
00351 {
00352        [self composing_free];
00353        if([chars length] == 0)
00354                return;
00355        
00356        // start composing
00357        composing = YES;
00358        composing_text = [chars copy];
00359 
00360        // if empty, cancel
00361        if([composing_text length] == 0)
00362                [self composing_free];
00363 }
00364 
00365 - (void)unmarkText
00366 {
00367        [self composing_free];
00368 }
00369 
00370 - (BOOL)hasMarkedText
00371 {
00372        return (composing)? YES: NO;
00373 }
00374 
00375 - (void)doCommandBySelector:(SEL)selector
00376 {
00377 }
00378 
00379 - (BOOL)isComposing
00380 {
00381        return composing;
00382 }
00383 
00384 - (NSInteger)conversationIdentifier
00385 {
00386        return (NSInteger)self;
00387 }
00388 
00389 - (NSAttributedString *)attributedSubstringFromRange:(NSRange)range
00390 {
00391        return [NSAttributedString new]; // XXX does this leak?
00392 }
00393 
00394 - (NSRange)markedRange
00395 {
00396        unsigned int length = (composing_text)? [composing_text length]: 0;
00397 
00398        if(composing)
00399                return NSMakeRange(0, length);
00400 
00401        return NSMakeRange(NSNotFound, 0);
00402 }
00403 
00404 - (NSRange)selectedRange
00405 {
00406        unsigned int length = (composing_text)? [composing_text length]: 0;
00407        return NSMakeRange(0, length);
00408 }
00409 
00410 - (NSRect)firstRectForCharacterRange:(NSRange)range
00411 {
00412        return NSZeroRect;
00413 }
00414 
00415 - (NSUInteger)characterIndexForPoint:(NSPoint)point
00416 {
00417        return NSNotFound;
00418 }
00419 
00420 - (NSArray*)validAttributesForMarkedText
00421 {
00422        return [NSArray array]; // XXX does this leak?
00423 }
00424 
00425 @end
00426 
00427 #pragma mark initialization / finalization
00428 
00429 NSOpenGLContext* GHOST_WindowCocoa::s_firstOpenGLcontext = nil;
00430 
00431 GHOST_WindowCocoa::GHOST_WindowCocoa(
00432     GHOST_SystemCocoa *systemCocoa,
00433     const STR_String& title,
00434     GHOST_TInt32 left,
00435     GHOST_TInt32 bottom,
00436     GHOST_TUns32 width,
00437     GHOST_TUns32 height,
00438     GHOST_TWindowState state,
00439     GHOST_TDrawingContextType type,
00440     const bool stereoVisual, const GHOST_TUns16 numOfAASamples
00441 ) :
00442     GHOST_Window(width, height, state, GHOST_kDrawingContextTypeNone, stereoVisual, numOfAASamples),
00443     m_customCursor(0)
00444 {
00445     NSOpenGLPixelFormatAttribute pixelFormatAttrsWindow[40];
00446     NSOpenGLPixelFormat *pixelFormat = nil;
00447     int i;
00448         
00449     m_systemCocoa = systemCocoa;
00450     m_fullScreen = false;
00451     
00452     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
00453     
00454     //Creates the window
00455     NSRect rect;
00456     NSSize  minSize;
00457     
00458     rect.origin.x = left;
00459     rect.origin.y = bottom;
00460     rect.size.width = width;
00461     rect.size.height = height;
00462     
00463     m_window = [[CocoaWindow alloc] initWithContentRect:rect
00464                                            styleMask:NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask | NSMiniaturizableWindowMask
00465                                              backing:NSBackingStoreBuffered defer:NO];
00466     if (m_window == nil) {
00467         [pool drain];
00468         return;
00469     }
00470     
00471     [m_window setSystemAndWindowCocoa:systemCocoa windowCocoa:this];
00472     
00473     //Forbid to resize the window below the blender defined minimum one
00474     minSize.width = 320;
00475     minSize.height = 240;
00476     [m_window setContentMinSize:minSize];
00477     
00478     setTitle(title);
00479     
00480     
00481     // Pixel Format Attributes for the windowed NSOpenGLContext
00482     i=0;
00483     pixelFormatAttrsWindow[i++] = NSOpenGLPFADoubleBuffer;
00484     
00485     // Guarantees the back buffer contents to be valid after a call to NSOpenGLContext object’s flushBuffer
00486     // needed for 'Draw Overlap' drawing method
00487     pixelFormatAttrsWindow[i++] = NSOpenGLPFABackingStore; 
00488     
00489     // Force software OpenGL, for debugging
00490     if(getenv("BLENDER_SOFTWAREGL")) {
00491         pixelFormatAttrsWindow[i++] = NSOpenGLPFARendererID;
00492         pixelFormatAttrsWindow[i++] = kCGLRendererGenericID;
00493     }
00494     else
00495         pixelFormatAttrsWindow[i++] = NSOpenGLPFAAccelerated;
00496 
00497     //pixelFormatAttrsWindow[i++] = NSOpenGLPFAAllowOfflineRenderers,;   // Removed to allow 10.4 builds, and 2 GPUs rendering is not used anyway
00498 
00499     pixelFormatAttrsWindow[i++] = NSOpenGLPFADepthSize;
00500     pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) 32;
00501     
00502     
00503     if (stereoVisual) pixelFormatAttrsWindow[i++] = NSOpenGLPFAStereo;
00504     
00505     if (numOfAASamples>0) {
00506         // Multisample anti-aliasing
00507         pixelFormatAttrsWindow[i++] = NSOpenGLPFAMultisample;
00508         
00509         pixelFormatAttrsWindow[i++] = NSOpenGLPFASampleBuffers;
00510         pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) 1;
00511         
00512         pixelFormatAttrsWindow[i++] = NSOpenGLPFASamples;
00513         pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) numOfAASamples;
00514         
00515         pixelFormatAttrsWindow[i++] = NSOpenGLPFANoRecovery;
00516     }
00517     
00518     pixelFormatAttrsWindow[i] = (NSOpenGLPixelFormatAttribute) 0;
00519     
00520     pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:pixelFormatAttrsWindow];
00521     
00522     
00523     //Fall back to no multisampling if Antialiasing init failed
00524     if (pixelFormat == nil) {
00525         i=0;
00526         pixelFormatAttrsWindow[i++] = NSOpenGLPFADoubleBuffer;
00527         
00528         // Guarantees the back buffer contents to be valid after a call to NSOpenGLContext object’s flushBuffer
00529         // needed for 'Draw Overlap' drawing method
00530         pixelFormatAttrsWindow[i++] = NSOpenGLPFABackingStore;
00531         
00532         // Force software OpenGL, for debugging
00533         if(getenv("BLENDER_SOFTWAREGL")) {
00534             pixelFormatAttrsWindow[i++] = NSOpenGLPFARendererID;
00535             pixelFormatAttrsWindow[i++] = kCGLRendererGenericID;
00536         }
00537         else
00538             pixelFormatAttrsWindow[i++] = NSOpenGLPFAAccelerated;
00539 
00540         //pixelFormatAttrsWindow[i++] = NSOpenGLPFAAllowOfflineRenderers,;   // Removed to allow 10.4 builds, and 2 GPUs rendering is not used anyway
00541         
00542         pixelFormatAttrsWindow[i++] = NSOpenGLPFADepthSize;
00543         pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) 32;
00544         
00545         if (stereoVisual) pixelFormatAttrsWindow[i++] = NSOpenGLPFAStereo;
00546         
00547         pixelFormatAttrsWindow[i] = (NSOpenGLPixelFormatAttribute) 0;
00548         
00549         pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:pixelFormatAttrsWindow];
00550         
00551     }
00552     
00553     if (numOfAASamples>0) { //Set m_numOfAASamples to the actual value
00554         GLint gli;
00555         [pixelFormat getValues:&gli forAttribute:NSOpenGLPFASamples forVirtualScreen:0];
00556         if (m_numOfAASamples != (GHOST_TUns16)gli) {
00557             m_numOfAASamples = (GHOST_TUns16)gli;
00558             printf("GHOST_Window could be created with anti-aliasing of only %i samples\n",m_numOfAASamples);
00559         }
00560     }
00561         
00562     //Creates the OpenGL View inside the window
00563     m_openGLView = [[CocoaOpenGLView alloc] initWithFrame:rect
00564                                                  pixelFormat:pixelFormat];
00565 
00566     [m_openGLView setSystemAndWindowCocoa:systemCocoa windowCocoa:this];
00567     
00568     [pixelFormat release];
00569     
00570     m_openGLContext = [m_openGLView openGLContext]; //This context will be replaced by the proper one just after
00571     
00572     [m_window setContentView:m_openGLView];
00573     [m_window setInitialFirstResponder:m_openGLView];
00574     
00575     [m_window setReleasedWhenClosed:NO]; //To avoid bad pointer exception in case of user closing the window
00576     
00577     [m_window makeKeyAndOrderFront:nil];
00578     
00579     setDrawingContextType(type);
00580     updateDrawingContext();
00581     activateDrawingContext();
00582     
00583     m_tablet.Active = GHOST_kTabletModeNone;
00584     
00585     CocoaWindowDelegate *windowDelegate = [[CocoaWindowDelegate alloc] init];
00586     [windowDelegate setSystemAndWindowCocoa:systemCocoa windowCocoa:this];
00587     [m_window setDelegate:windowDelegate];
00588     
00589     [m_window setAcceptsMouseMovedEvents:YES];
00590     
00591     [m_window registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
00592                                           NSStringPboardType, NSTIFFPboardType, nil]];
00593                                           
00594     if (state == GHOST_kWindowStateFullScreen)
00595         setState(GHOST_kWindowStateFullScreen);
00596         
00597     [pool drain];
00598 }
00599 
00600 
00601 GHOST_WindowCocoa::~GHOST_WindowCocoa()
00602 {
00603     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
00604 
00605     if (m_customCursor) {
00606         [m_customCursor release];
00607         m_customCursor = nil;
00608     }
00609 
00610     [m_openGLView release];
00611     
00612     if (m_window) {
00613         [m_window close];
00614         [[m_window delegate] release];
00615         [m_window release];
00616         m_window = nil;
00617     }
00618     
00619     //Check for other blender opened windows and make the frontmost key
00620     NSArray *windowsList = [NSApp orderedWindows];
00621     if ([windowsList count]) {
00622         [[windowsList objectAtIndex:0] makeKeyAndOrderFront:nil];
00623     }
00624     [pool drain];
00625 }
00626 
00627 #pragma mark accessors
00628 
00629 bool GHOST_WindowCocoa::getValid() const
00630 {
00631     return (m_window != 0);
00632 }
00633 
00634 void* GHOST_WindowCocoa::getOSWindow() const
00635 {
00636     return (void*)m_window;
00637 }
00638 
00639 void GHOST_WindowCocoa::setTitle(const STR_String& title)
00640 {
00641     GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setTitle(): window invalid")
00642     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
00643 
00644     NSString *windowTitle = [[NSString alloc] initWithCString:title encoding:NSUTF8StringEncoding];
00645     
00646     //Set associated file if applicable
00647     if (windowTitle && [windowTitle hasPrefix:@"Blender"])
00648     {
00649         NSRange fileStrRange;
00650         NSString *associatedFileName;
00651         int len;
00652         
00653         fileStrRange.location = [windowTitle rangeOfString:@"["].location+1;
00654         len = [windowTitle rangeOfString:@"]"].location - fileStrRange.location;
00655     
00656         if (len >0)
00657         {
00658             fileStrRange.length = len;
00659             associatedFileName = [windowTitle substringWithRange:fileStrRange];
00660             [m_window setTitle:[associatedFileName lastPathComponent]];
00661 
00662             //Blender used file open/save functions converte file names into legal URL ones
00663             associatedFileName = [associatedFileName stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
00664             @try {
00665                 [m_window setRepresentedFilename:associatedFileName];
00666             }
00667             @catch (NSException * e) {
00668                 printf("\nInvalid file path given in window title");
00669             }
00670         }
00671         else {
00672             [m_window setTitle:windowTitle];
00673             [m_window setRepresentedFilename:@""];
00674         }
00675 
00676     } else {
00677         [m_window setTitle:windowTitle];
00678         [m_window setRepresentedFilename:@""];
00679     }
00680 
00681     
00682     [windowTitle release];
00683     [pool drain];
00684 }
00685 
00686 
00687 void GHOST_WindowCocoa::getTitle(STR_String& title) const
00688 {
00689     GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getTitle(): window invalid")
00690 
00691     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
00692 
00693     NSString *windowTitle = [m_window title];
00694 
00695     if (windowTitle != nil) {
00696         title = [windowTitle UTF8String];       
00697     }
00698     
00699     [pool drain];
00700 }
00701 
00702 
00703 void GHOST_WindowCocoa::getWindowBounds(GHOST_Rect& bounds) const
00704 {
00705     NSRect rect;
00706     GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getWindowBounds(): window invalid")
00707 
00708     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
00709     
00710     NSRect screenSize = [[m_window screen] visibleFrame];
00711 
00712     rect = [m_window frame];
00713 
00714     bounds.m_b = screenSize.size.height - (rect.origin.y -screenSize.origin.y);
00715     bounds.m_l = rect.origin.x -screenSize.origin.x;
00716     bounds.m_r = rect.origin.x-screenSize.origin.x + rect.size.width;
00717     bounds.m_t = screenSize.size.height - (rect.origin.y + rect.size.height -screenSize.origin.y);
00718     
00719     [pool drain];
00720 }
00721 
00722 
00723 void GHOST_WindowCocoa::getClientBounds(GHOST_Rect& bounds) const
00724 {
00725     NSRect rect;
00726     GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getClientBounds(): window invalid")
00727     
00728     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
00729     
00730     if (!m_fullScreen)
00731     {
00732         NSRect screenSize = [[m_window screen] visibleFrame];
00733 
00734         //Max window contents as screen size (excluding title bar...)
00735         NSRect contentRect = [CocoaWindow contentRectForFrameRect:screenSize
00736                                                      styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)];
00737 
00738         rect = [m_window contentRectForFrameRect:[m_window frame]];
00739         
00740         bounds.m_b = contentRect.size.height - (rect.origin.y -contentRect.origin.y);
00741         bounds.m_l = rect.origin.x -contentRect.origin.x;
00742         bounds.m_r = rect.origin.x-contentRect.origin.x + rect.size.width;
00743         bounds.m_t = contentRect.size.height - (rect.origin.y + rect.size.height -contentRect.origin.y);
00744     }
00745     else {
00746         NSRect screenSize = [[m_window screen] frame];
00747         
00748         bounds.m_b = screenSize.origin.y + screenSize.size.height;
00749         bounds.m_l = screenSize.origin.x;
00750         bounds.m_r = screenSize.origin.x + screenSize.size.width;
00751         bounds.m_t = screenSize.origin.y;
00752     }
00753     [pool drain];
00754 }
00755 
00756 
00757 GHOST_TSuccess GHOST_WindowCocoa::setClientWidth(GHOST_TUns32 width)
00758 {
00759     GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientWidth(): window invalid")
00760     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
00761     GHOST_Rect cBnds, wBnds;
00762     getClientBounds(cBnds);
00763     if (((GHOST_TUns32)cBnds.getWidth()) != width) {
00764         NSSize size;
00765         size.width=width;
00766         size.height=cBnds.getHeight();
00767         [m_window setContentSize:size];
00768     }
00769     [pool drain];
00770     return GHOST_kSuccess;
00771 }
00772 
00773 
00774 GHOST_TSuccess GHOST_WindowCocoa::setClientHeight(GHOST_TUns32 height)
00775 {
00776     GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientHeight(): window invalid")
00777     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
00778     GHOST_Rect cBnds, wBnds;
00779     getClientBounds(cBnds);
00780     if (((GHOST_TUns32)cBnds.getHeight()) != height) {
00781         NSSize size;
00782         size.width=cBnds.getWidth();
00783         size.height=height;
00784         [m_window setContentSize:size];
00785     }
00786     [pool drain];
00787     return GHOST_kSuccess;
00788 }
00789 
00790 
00791 GHOST_TSuccess GHOST_WindowCocoa::setClientSize(GHOST_TUns32 width, GHOST_TUns32 height)
00792 {
00793     GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientSize(): window invalid")
00794     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
00795     GHOST_Rect cBnds, wBnds;
00796     getClientBounds(cBnds);
00797     if ((((GHOST_TUns32)cBnds.getWidth()) != width) ||
00798         (((GHOST_TUns32)cBnds.getHeight()) != height)) {
00799         NSSize size;
00800         size.width=width;
00801         size.height=height;
00802         [m_window setContentSize:size];
00803     }
00804     [pool drain];
00805     return GHOST_kSuccess;
00806 }
00807 
00808 
00809 GHOST_TWindowState GHOST_WindowCocoa::getState() const
00810 {
00811     GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getState(): window invalid")
00812     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
00813     GHOST_TWindowState state;
00814     if (m_fullScreen) {
00815         state = GHOST_kWindowStateFullScreen;
00816     } 
00817     else if ([m_window isMiniaturized]) {
00818         state = GHOST_kWindowStateMinimized;
00819     }
00820     else if ([m_window isZoomed]) {
00821         state = GHOST_kWindowStateMaximized;
00822     }
00823     else {
00824         state = GHOST_kWindowStateNormal;
00825     }
00826     [pool drain];
00827     return state;
00828 }
00829 
00830 
00831 void GHOST_WindowCocoa::screenToClient(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
00832 {
00833     GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::screenToClient(): window invalid")
00834 
00835     screenToClientIntern(inX, inY, outX, outY);
00836 
00837     /* switch y to match ghost convention */
00838     GHOST_Rect cBnds;
00839     getClientBounds(cBnds);
00840     outY = (cBnds.getHeight() - 1) - outY;
00841 }
00842 
00843 
00844 void GHOST_WindowCocoa::clientToScreen(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
00845 {
00846     GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::clientToScreen(): window invalid")
00847 
00848     /* switch y to match ghost convention */
00849     GHOST_Rect cBnds;
00850     getClientBounds(cBnds);
00851     inY = (cBnds.getHeight() - 1) - inY;
00852 
00853     clientToScreenIntern(inX, inY, outX, outY);
00854 }
00855 
00856 void GHOST_WindowCocoa::screenToClientIntern(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
00857 {
00858     NSPoint screenCoord;
00859     NSPoint baseCoord;
00860     
00861     screenCoord.x = inX;
00862     screenCoord.y = inY;
00863     
00864     baseCoord = [m_window convertScreenToBase:screenCoord];
00865     
00866     outX = baseCoord.x;
00867     outY = baseCoord.y;
00868 }
00869 
00870 void GHOST_WindowCocoa::clientToScreenIntern(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
00871 {
00872     NSPoint screenCoord;
00873     NSPoint baseCoord;
00874     
00875     baseCoord.x = inX;
00876     baseCoord.y = inY;
00877     
00878     screenCoord = [m_window convertBaseToScreen:baseCoord];
00879     
00880     outX = screenCoord.x;
00881     outY = screenCoord.y;
00882 }
00883 
00884 
00885 NSScreen* GHOST_WindowCocoa::getScreen()
00886 {
00887     return [m_window screen];
00888 }
00889 
00890 
00896 GHOST_TSuccess GHOST_WindowCocoa::setState(GHOST_TWindowState state)
00897 {
00898     GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setState(): window invalid")
00899     switch (state) {
00900         case GHOST_kWindowStateMinimized:
00901             [m_window miniaturize:nil];
00902             break;
00903         case GHOST_kWindowStateMaximized:
00904             [m_window zoom:nil];
00905             break;
00906         
00907         case GHOST_kWindowStateFullScreen:
00908             if (!m_fullScreen)
00909             {
00910                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
00911             
00912                 //This status change needs to be done before Cocoa call to enter fullscreen mode
00913                 //to give window delegate hint not to forward its deactivation to ghost wm that doesn't know view/window difference
00914                 m_fullScreen = true;
00915 
00916 #ifdef MAC_OS_X_VERSION_10_6
00917                 //10.6 provides Cocoa functions to autoshow menu bar, and to change a window style
00918                 //Hide menu & dock if needed
00919                 if ([[m_window screen] isEqual:[[NSScreen screens] objectAtIndex:0]])
00920                 {
00921                     [NSApp setPresentationOptions:(NSApplicationPresentationHideDock | NSApplicationPresentationAutoHideMenuBar)];
00922                 }
00923                 //Make window borderless and enlarge it
00924                 [m_window setStyleMask:NSBorderlessWindowMask];
00925                 [m_window setFrame:[[m_window screen] frame] display:YES];
00926                 [m_window makeFirstResponder:m_openGLView];
00927 #else
00928                 //With 10.5, we need to create a new window to change its style to borderless
00929                 //Hide menu & dock if needed
00930                 if ([[m_window screen] isEqual:[[NSScreen screens] objectAtIndex:0]])
00931                 {
00932                     //Cocoa function in 10.5 does not allow to set the menu bar in auto-show mode [NSMenu setMenuBarVisible:NO];
00933                     //One of the very few 64bit compatible Carbon function
00934                     SetSystemUIMode(kUIModeAllHidden,kUIOptionAutoShowMenuBar);
00935                 }
00936                 //Create a fullscreen borderless window
00937                 CocoaWindow *tmpWindow = [[CocoaWindow alloc]
00938                                           initWithContentRect:[[m_window screen] frame]
00939                                           styleMask:NSBorderlessWindowMask
00940                                           backing:NSBackingStoreBuffered
00941                                           defer:YES];
00942                 //Copy current window parameters
00943                 [tmpWindow setTitle:[m_window title]];
00944                 [tmpWindow setRepresentedFilename:[m_window representedFilename]];
00945                 [tmpWindow setReleasedWhenClosed:NO];
00946                 [tmpWindow setAcceptsMouseMovedEvents:YES];
00947                 [tmpWindow setDelegate:[m_window delegate]];
00948                 [tmpWindow setSystemAndWindowCocoa:[m_window systemCocoa] windowCocoa:this];
00949                 [tmpWindow registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
00950                                                    NSStringPboardType, NSTIFFPboardType, nil]];
00951                 
00952                 //Assign the openGL view to the new window
00953                 [tmpWindow setContentView:m_openGLView];
00954                 
00955                 //Show the new window
00956                 [tmpWindow makeKeyAndOrderFront:m_openGLView];
00957                 //Close and release old window
00958                 [m_window setDelegate:nil]; // To avoid the notification of "window closed" event
00959                 [m_window close];
00960                 [m_window release];
00961                 m_window = tmpWindow;
00962 #endif
00963             
00964                 //Tell WM of view new size
00965                 m_systemCocoa->handleWindowEvent(GHOST_kEventWindowSize, this);
00966                 
00967                 [pool drain];
00968                 }
00969             break;
00970         case GHOST_kWindowStateNormal:
00971         default:
00972             NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
00973             if (m_fullScreen)
00974             {
00975                 m_fullScreen = false;
00976 
00977                 //Exit fullscreen
00978 #ifdef MAC_OS_X_VERSION_10_6
00979                 //Show again menu & dock if needed
00980                 if ([[m_window screen] isEqual:[NSScreen mainScreen]])
00981                 {
00982                     [NSApp setPresentationOptions:NSApplicationPresentationDefault];
00983                 }
00984                 //Make window normal and resize it
00985                 [m_window setStyleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)];
00986                 [m_window setFrame:[[m_window screen] visibleFrame] display:YES];
00987                 //TODO for 10.6 only : window title is forgotten after the style change
00988                 [m_window makeFirstResponder:m_openGLView];
00989 #else
00990                 //With 10.5, we need to create a new window to change its style to borderless
00991                 //Show menu & dock if needed
00992                 if ([[m_window screen] isEqual:[NSScreen mainScreen]])
00993                 {
00994                     //Cocoa function in 10.5 does not allow to set the menu bar in auto-show mode [NSMenu setMenuBarVisible:YES];
00995                     SetSystemUIMode(kUIModeNormal, 0); //One of the very few 64bit compatible Carbon function
00996                 }
00997                 //Create a fullscreen borderless window
00998                 CocoaWindow *tmpWindow = [[CocoaWindow alloc]
00999                                           initWithContentRect:[[m_window screen] frame]
01000                                                     styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)
01001                                                       backing:NSBackingStoreBuffered
01002                                                         defer:YES];
01003                 //Copy current window parameters
01004                 [tmpWindow setTitle:[m_window title]];
01005                 [tmpWindow setRepresentedFilename:[m_window representedFilename]];
01006                 [tmpWindow setReleasedWhenClosed:NO];
01007                 [tmpWindow setAcceptsMouseMovedEvents:YES];
01008                 [tmpWindow setDelegate:[m_window delegate]];
01009                 [tmpWindow setSystemAndWindowCocoa:[m_window systemCocoa] windowCocoa:this];
01010                 [tmpWindow registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
01011                                                    NSStringPboardType, NSTIFFPboardType, nil]];
01012                 //Forbid to resize the window below the blender defined minimum one
01013                 [tmpWindow setContentMinSize:NSMakeSize(320, 240)];
01014                 
01015                 //Assign the openGL view to the new window
01016                 [tmpWindow setContentView:m_openGLView];
01017                 
01018                 //Show the new window
01019                 [tmpWindow makeKeyAndOrderFront:nil];
01020                 //Close and release old window
01021                 [m_window setDelegate:nil]; // To avoid the notification of "window closed" event
01022                 [m_window close];
01023                 [m_window release];
01024                 m_window = tmpWindow;
01025 #endif
01026             
01027                 //Tell WM of view new size
01028                 m_systemCocoa->handleWindowEvent(GHOST_kEventWindowSize, this);
01029             }
01030             else if ([m_window isMiniaturized])
01031                 [m_window deminiaturize:nil];
01032             else if ([m_window isZoomed])
01033                 [m_window zoom:nil];
01034             [pool drain];
01035             break;
01036     }
01037 
01038     return GHOST_kSuccess;
01039 }
01040 
01041 GHOST_TSuccess GHOST_WindowCocoa::setModifiedState(bool isUnsavedChanges)
01042 {
01043     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
01044     
01045     [m_window setDocumentEdited:isUnsavedChanges];
01046     
01047     [pool drain];
01048     return GHOST_Window::setModifiedState(isUnsavedChanges);
01049 }
01050 
01051 
01052 
01053 GHOST_TSuccess GHOST_WindowCocoa::setOrder(GHOST_TWindowOrder order)
01054 {
01055     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
01056     
01057     GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setOrder(): window invalid")
01058     if (order == GHOST_kWindowOrderTop) {
01059         [m_window makeKeyAndOrderFront:nil];
01060     }
01061     else {
01062         NSArray *windowsList;
01063         
01064         [m_window orderBack:nil];
01065         
01066         //Check for other blender opened windows and make the frontmost key
01067         windowsList = [NSApp orderedWindows];
01068         if ([windowsList count]) {
01069             [[windowsList objectAtIndex:0] makeKeyAndOrderFront:nil];
01070         }
01071     }
01072     
01073     [pool drain];
01074     return GHOST_kSuccess;
01075 }
01076 
01077 #pragma mark Drawing context
01078 
01079 /*#define  WAIT_FOR_VSYNC 1*/
01080 
01081 GHOST_TSuccess GHOST_WindowCocoa::swapBuffers()
01082 {
01083     if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
01084         if (m_openGLContext != nil) {
01085             NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
01086             [m_openGLContext flushBuffer];
01087             [pool drain];
01088             return GHOST_kSuccess;
01089         }
01090     }
01091     return GHOST_kFailure;
01092 }
01093 
01094 GHOST_TSuccess GHOST_WindowCocoa::updateDrawingContext()
01095 {
01096     if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
01097         if (m_openGLContext != nil) {
01098             NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
01099             [m_openGLContext update];
01100             [pool drain];
01101             return GHOST_kSuccess;
01102         }
01103     }
01104     return GHOST_kFailure;
01105 }
01106 
01107 GHOST_TSuccess GHOST_WindowCocoa::activateDrawingContext()
01108 {
01109     if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
01110         if (m_openGLContext != nil) {
01111             NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
01112             [m_openGLContext makeCurrentContext];
01113             
01114             // Disable AA by default
01115             if (m_numOfAASamples > 0) glDisable(GL_MULTISAMPLE_ARB);
01116             [pool drain];
01117             return GHOST_kSuccess;
01118         }
01119     }
01120     return GHOST_kFailure;
01121 }
01122 
01123 
01124 GHOST_TSuccess GHOST_WindowCocoa::installDrawingContext(GHOST_TDrawingContextType type)
01125 {
01126     GHOST_TSuccess success = GHOST_kFailure;
01127     
01128     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
01129     
01130     NSOpenGLPixelFormat *pixelFormat;
01131     NSOpenGLContext *tmpOpenGLContext;
01132     
01133     /***** Multithreaded opengl code : uncomment for enabling
01134     CGLContextObj cglCtx;
01135     */
01136      
01137     switch (type) {
01138         case GHOST_kDrawingContextTypeOpenGL:
01139             if (!getValid()) break;
01140 
01141             pixelFormat = [m_openGLView pixelFormat];
01142             tmpOpenGLContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat
01143                                                               shareContext:s_firstOpenGLcontext];
01144             if (tmpOpenGLContext == nil) {
01145                 success = GHOST_kFailure;
01146                 break;
01147             }
01148             
01149             //Switch openGL to multhreaded mode
01150             /******* Multithreaded opengl code : uncomment for enabling
01151             cglCtx = (CGLContextObj)[tmpOpenGLContext CGLContextObj];
01152             if (CGLEnable(cglCtx, kCGLCEMPEngine) == kCGLNoError)
01153                 printf("\nSwitched openGL to multithreaded mode");
01154              */
01155             
01156             if (!s_firstOpenGLcontext) s_firstOpenGLcontext = tmpOpenGLContext;
01157 #ifdef WAIT_FOR_VSYNC
01158             {
01159                 GLint swapInt = 1;
01160                 /* wait for vsync, to avoid tearing artifacts */
01161                 [tmpOpenGLContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
01162             }
01163 #endif
01164             [m_openGLView setOpenGLContext:tmpOpenGLContext];
01165             [tmpOpenGLContext setView:m_openGLView];
01166             
01167             m_openGLContext = tmpOpenGLContext;
01168             break;
01169     
01170         case GHOST_kDrawingContextTypeNone:
01171             success = GHOST_kSuccess;
01172             break;
01173         
01174         default:
01175             break;
01176     }
01177     [pool drain];
01178     return success;
01179 }
01180 
01181 
01182 GHOST_TSuccess GHOST_WindowCocoa::removeDrawingContext()
01183 {
01184     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
01185     switch (m_drawingContextType) {
01186         case GHOST_kDrawingContextTypeOpenGL:
01187             if (m_openGLContext)
01188             {
01189                 [m_openGLView clearGLContext];
01190                 if (s_firstOpenGLcontext == m_openGLContext) s_firstOpenGLcontext = nil;
01191                 m_openGLContext = nil;
01192             }
01193             [pool drain];
01194             return GHOST_kSuccess;
01195         case GHOST_kDrawingContextTypeNone:
01196             [pool drain];
01197             return GHOST_kSuccess;
01198             break;
01199         default:
01200             [pool drain];
01201             return GHOST_kFailure;
01202     }
01203 }
01204 
01205 
01206 GHOST_TSuccess GHOST_WindowCocoa::invalidate()
01207 {
01208     GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::invalidate(): window invalid")
01209     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
01210     [m_openGLView setNeedsDisplay:YES];
01211     [pool drain];
01212     return GHOST_kSuccess;
01213 }
01214 
01215 #pragma mark Progress bar
01216 
01217 GHOST_TSuccess GHOST_WindowCocoa::setProgressBar(float progress)
01218 {
01219     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
01220     
01221     if ((progress >=0.0) && (progress <=1.0)) {
01222         NSImage* dockIcon = [[NSImage alloc] initWithSize:NSMakeSize(128,128)];
01223         
01224         [dockIcon lockFocus];
01225         NSRect progressBox = {{4, 4}, {120, 16}};
01226 
01227         [[NSImage imageNamed:@"NSApplicationIcon"] dissolveToPoint:NSZeroPoint fraction:1.0];
01228         
01229         // Track & Outline
01230         [[NSColor blackColor] setFill];
01231         NSRectFill(progressBox);
01232         
01233         [[NSColor whiteColor] set];
01234         NSFrameRect(progressBox);
01235         
01236         // Progress fill
01237         progressBox = NSInsetRect(progressBox, 1, 1);
01238         [[NSColor knobColor] setFill];
01239         progressBox.size.width = progressBox.size.width * progress;
01240         NSRectFill(progressBox);
01241         
01242         [dockIcon unlockFocus];
01243         
01244         [NSApp setApplicationIconImage:dockIcon];
01245         [dockIcon release];
01246         
01247         m_progressBarVisible = true;
01248     }
01249     
01250     [pool drain];
01251     return GHOST_kSuccess;
01252 }
01253 
01254 
01255 GHOST_TSuccess GHOST_WindowCocoa::endProgressBar()
01256 {
01257     if (!m_progressBarVisible) return GHOST_kFailure;
01258     m_progressBarVisible = false;
01259     
01260     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
01261     
01262     NSImage* dockIcon = [[NSImage alloc] initWithSize:NSMakeSize(128,128)];
01263     [dockIcon lockFocus];
01264     [[NSImage imageNamed:@"NSApplicationIcon"] dissolveToPoint:NSZeroPoint fraction:1.0];
01265     [dockIcon unlockFocus];
01266     [NSApp setApplicationIconImage:dockIcon];
01267     [dockIcon release];
01268     
01269     [pool drain];
01270     return GHOST_kSuccess;
01271 }
01272 
01273 
01274 
01275 #pragma mark Cursor handling
01276 
01277 void GHOST_WindowCocoa::loadCursor(bool visible, GHOST_TStandardCursor cursor) const
01278 {
01279     static bool systemCursorVisible = true;
01280     
01281     NSCursor *tmpCursor =nil;
01282     
01283     if (visible != systemCursorVisible) {
01284         if (visible) {
01285             [NSCursor unhide];
01286             systemCursorVisible = true;
01287         }
01288         else {
01289             [NSCursor hide];
01290             systemCursorVisible = false;
01291         }
01292     }
01293 
01294     if (cursor == GHOST_kStandardCursorCustom && m_customCursor) {
01295         tmpCursor = m_customCursor;
01296     } else {
01297         switch (cursor) {
01298             case GHOST_kStandardCursorDestroy:
01299                 tmpCursor = [NSCursor disappearingItemCursor];
01300                 break;
01301             case GHOST_kStandardCursorText:
01302                 tmpCursor = [NSCursor IBeamCursor];
01303                 break;
01304             case GHOST_kStandardCursorCrosshair:
01305                 tmpCursor = [NSCursor crosshairCursor];
01306                 break;
01307             case GHOST_kStandardCursorUpDown:
01308                 tmpCursor = [NSCursor resizeUpDownCursor];
01309                 break;
01310             case GHOST_kStandardCursorLeftRight:
01311                 tmpCursor = [NSCursor resizeLeftRightCursor];
01312                 break;
01313             case GHOST_kStandardCursorTopSide:
01314                 tmpCursor = [NSCursor resizeUpCursor];
01315                 break;
01316             case GHOST_kStandardCursorBottomSide:
01317                 tmpCursor = [NSCursor resizeDownCursor];
01318                 break;
01319             case GHOST_kStandardCursorLeftSide:
01320                 tmpCursor = [NSCursor resizeLeftCursor];
01321                 break;
01322             case GHOST_kStandardCursorRightSide:
01323                 tmpCursor = [NSCursor resizeRightCursor];
01324                 break;
01325             case GHOST_kStandardCursorRightArrow:
01326             case GHOST_kStandardCursorInfo:
01327             case GHOST_kStandardCursorLeftArrow:
01328             case GHOST_kStandardCursorHelp:
01329             case GHOST_kStandardCursorCycle:
01330             case GHOST_kStandardCursorSpray:
01331             case GHOST_kStandardCursorWait:
01332             case GHOST_kStandardCursorTopLeftCorner:
01333             case GHOST_kStandardCursorTopRightCorner:
01334             case GHOST_kStandardCursorBottomRightCorner:
01335             case GHOST_kStandardCursorBottomLeftCorner:
01336             case GHOST_kStandardCursorCopy:
01337             case GHOST_kStandardCursorDefault:
01338             default:
01339                 tmpCursor = [NSCursor arrowCursor];
01340                 break;
01341         };
01342     }
01343     [tmpCursor set];
01344 }
01345 
01346 
01347 
01348 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorVisibility(bool visible)
01349 {
01350     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
01351     
01352     if ([m_window isVisible]) {
01353         loadCursor(visible, getCursorShape());
01354     }
01355     
01356     [pool drain];
01357     return GHOST_kSuccess;
01358 }
01359 
01360 
01361 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorGrab(GHOST_TGrabCursorMode mode)
01362 {
01363     GHOST_TSuccess err = GHOST_kSuccess;
01364     
01365     if (mode != GHOST_kGrabDisable)
01366     {
01367         //No need to perform grab without warp as it is always on in OS X
01368         if(mode != GHOST_kGrabNormal) {
01369             GHOST_TInt32 x_old,y_old;
01370             NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
01371 
01372             m_systemCocoa->getCursorPosition(x_old,y_old);
01373             screenToClientIntern(x_old, y_old, m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]);
01374             //Warp position is stored in client (window base) coordinates
01375             setCursorGrabAccum(0, 0);
01376             
01377             if(mode == GHOST_kGrabHide) {
01378                 setWindowCursorVisibility(false);
01379             }
01380             
01381             //Make window key if it wasn't to get the mouse move events
01382             [m_window makeKeyWindow];
01383             
01384             //Dissociate cursor position even for warp mode, to allow mouse acceleration to work even when warping the cursor
01385             err = CGAssociateMouseAndMouseCursorPosition(false) == kCGErrorSuccess ? GHOST_kSuccess : GHOST_kFailure;
01386             
01387             [pool drain];
01388         }
01389     }
01390     else {
01391         if(m_cursorGrab==GHOST_kGrabHide)
01392         {
01393             //No need to set again cursor position, as it has not changed for Cocoa
01394             setWindowCursorVisibility(true);
01395         }
01396         
01397         err = CGAssociateMouseAndMouseCursorPosition(true) == kCGErrorSuccess ? GHOST_kSuccess : GHOST_kFailure;
01398         /* Almost works without but important otherwise the mouse GHOST location can be incorrect on exit */
01399         setCursorGrabAccum(0, 0);
01400         m_cursorGrabBounds.m_l= m_cursorGrabBounds.m_r= -1; /* disable */
01401     }
01402     return err;
01403 }
01404     
01405 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorShape(GHOST_TStandardCursor shape)
01406 {
01407     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
01408 
01409     if (m_customCursor) {
01410         [m_customCursor release];
01411         m_customCursor = nil;
01412     }
01413 
01414     if ([m_window isVisible]) {
01415         loadCursor(getCursorVisibility(), shape);
01416     }
01417     
01418     [pool drain];
01419     return GHOST_kSuccess;
01420 }
01421 
01434 static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
01435 {
01436     shrt= ((shrt>>1)&0x5555) | ((shrt<<1)&0xAAAA);
01437     shrt= ((shrt>>2)&0x3333) | ((shrt<<2)&0xCCCC);
01438     shrt= ((shrt>>4)&0x0F0F) | ((shrt<<4)&0xF0F0);
01439     shrt= ((shrt>>8)&0x00FF) | ((shrt<<8)&0xFF00);
01440     return shrt;
01441 }
01442 
01443 GHOST_TSuccess GHOST_WindowCocoa::setWindowCustomCursorShape(GHOST_TUns8 *bitmap, GHOST_TUns8 *mask,
01444                     int sizex, int sizey, int hotX, int hotY, int fg_color, int bg_color)
01445 {
01446     int y,nbUns16;
01447     NSPoint hotSpotPoint;
01448     NSBitmapImageRep *cursorImageRep;
01449     NSImage *cursorImage;
01450     NSSize imSize;
01451     GHOST_TUns16 *cursorBitmap;
01452     
01453     
01454     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
01455     
01456     if (m_customCursor) {
01457         [m_customCursor release];
01458         m_customCursor = nil;
01459     }
01460     
01461 
01462     cursorImageRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil
01463                                                              pixelsWide:sizex
01464                                                              pixelsHigh:sizey
01465                                                           bitsPerSample:1 
01466                                                         samplesPerPixel:2
01467                                                                hasAlpha:YES
01468                                                                isPlanar:YES
01469                                                          colorSpaceName:NSDeviceWhiteColorSpace
01470                                                             bytesPerRow:(sizex/8 + (sizex%8 >0 ?1:0))
01471                                                            bitsPerPixel:1];
01472     
01473     
01474     cursorBitmap = (GHOST_TUns16*)[cursorImageRep bitmapData];
01475     nbUns16 = [cursorImageRep bytesPerPlane]/2;
01476     
01477     for (y=0; y<nbUns16; y++) {
01478 #if !defined(__LITTLE_ENDIAN__)
01479         cursorBitmap[y] = ~uns16ReverseBits((bitmap[2*y]<<0) | (bitmap[2*y+1]<<8));
01480         cursorBitmap[nbUns16+y] = uns16ReverseBits((mask[2*y]<<0) | (mask[2*y+1]<<8));
01481 #else
01482         cursorBitmap[y] = ~uns16ReverseBits((bitmap[2*y+1]<<0) | (bitmap[2*y]<<8));
01483         cursorBitmap[nbUns16+y] = uns16ReverseBits((mask[2*y+1]<<0) | (mask[2*y]<<8));
01484 #endif
01485         
01486     }
01487     
01488     
01489     imSize.width = sizex;
01490     imSize.height= sizey;
01491     cursorImage = [[NSImage alloc] initWithSize:imSize];
01492     [cursorImage addRepresentation:cursorImageRep];
01493     
01494     hotSpotPoint.x = hotX;
01495     hotSpotPoint.y = hotY;
01496     
01497     //foreground and background color parameter is not handled for now (10.6)
01498     m_customCursor = [[NSCursor alloc] initWithImage:cursorImage
01499                                              hotSpot:hotSpotPoint];
01500     
01501     [cursorImageRep release];
01502     [cursorImage release];
01503     
01504     if ([m_window isVisible]) {
01505         loadCursor(getCursorVisibility(), GHOST_kStandardCursorCustom);
01506     }
01507     [pool drain];
01508     return GHOST_kSuccess;
01509 }
01510 
01511 GHOST_TSuccess GHOST_WindowCocoa::setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2], 
01512                                                 GHOST_TUns8 mask[16][2], int hotX, int hotY)
01513 {
01514     return setWindowCustomCursorShape((GHOST_TUns8*)bitmap, (GHOST_TUns8*) mask, 16, 16, hotX, hotY, 0, 1);
01515 }