e89dd1b41dcfb1d5021b0dfbda6da551654ecdc0
[blender.git] / intern / ghost / intern / GHOST_WindowCocoa.mm
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s):      Maarten Gribnau 05/2001
24                                         Damien Plisson 10/2009
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 #include <Cocoa/Cocoa.h>
30
31 #if MAC_OS_X_VERSION_MIN_REQUIRED <= 1050
32 //Use of the SetSystemUIMode function (64bit compatible)
33 #include <Carbon/Carbon.h>
34 #endif
35
36 #include <OpenGL/gl.h>
37 #include <OpenGL/CGLRenderers.h>
38 /***** Multithreaded opengl code : uncomment for enabling
39 #include <OpenGL/OpenGL.h>
40 */
41
42  
43 #include "GHOST_WindowCocoa.h"
44 #include "GHOST_SystemCocoa.h"
45 #include "GHOST_Debug.h"
46
47
48 #pragma mark Cocoa window delegate object
49 /* live resize ugly patch
50 extern "C" {
51         struct bContext;
52         typedef struct bContext bContext;
53         bContext* ghostC;
54         extern int wm_window_timer(const bContext *C);
55         extern void wm_window_process_events(const bContext *C);
56         extern void wm_event_do_handlers(bContext *C);
57         extern void wm_event_do_notifiers(bContext *C);
58         extern void wm_draw_update(bContext *C);
59 };*/
60 @interface CocoaWindowDelegate : NSObject
61 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
62 <NSWindowDelegate>
63 #endif
64 {
65         GHOST_SystemCocoa *systemCocoa;
66         GHOST_WindowCocoa *associatedWindow;
67 }
68
69 - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa;
70 - (void)windowDidBecomeKey:(NSNotification *)notification;
71 - (void)windowDidResignKey:(NSNotification *)notification;
72 - (void)windowDidExpose:(NSNotification *)notification;
73 - (void)windowDidResize:(NSNotification *)notification;
74 - (void)windowDidMove:(NSNotification *)notification;
75 - (void)windowWillMove:(NSNotification *)notification;
76 - (BOOL)windowShouldClose:(id)sender;   
77 @end
78
79 @implementation CocoaWindowDelegate : NSObject
80 - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa
81 {
82         systemCocoa = sysCocoa;
83         associatedWindow = winCocoa;
84 }
85
86 - (void)windowDidBecomeKey:(NSNotification *)notification
87 {
88         systemCocoa->handleWindowEvent(GHOST_kEventWindowActivate, associatedWindow);
89 }
90
91 - (void)windowDidResignKey:(NSNotification *)notification
92 {
93         systemCocoa->handleWindowEvent(GHOST_kEventWindowDeactivate, associatedWindow);
94 }
95
96 - (void)windowDidExpose:(NSNotification *)notification
97 {
98         systemCocoa->handleWindowEvent(GHOST_kEventWindowUpdate, associatedWindow);
99 }
100
101 - (void)windowDidMove:(NSNotification *)notification
102 {
103         systemCocoa->handleWindowEvent(GHOST_kEventWindowMove, associatedWindow);
104 }
105
106 - (void)windowWillMove:(NSNotification *)notification
107 {
108         systemCocoa->handleWindowEvent(GHOST_kEventWindowMove, associatedWindow);
109 }
110
111 - (void)windowDidResize:(NSNotification *)notification
112 {
113 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
114         //if (![[notification object] inLiveResize]) {
115                 //Send event only once, at end of resize operation (when user has released mouse button)
116 #endif
117                 systemCocoa->handleWindowEvent(GHOST_kEventWindowSize, associatedWindow);
118 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
119         //}
120 #endif
121         /* Live resize ugly patch. Needed because live resize runs in a modal loop, not letting main loop run
122          if ([[notification object] inLiveResize]) {
123                 systemCocoa->dispatchEvents();
124                 wm_window_timer(ghostC);
125                 wm_event_do_handlers(ghostC);
126                 wm_event_do_notifiers(ghostC);
127                 wm_draw_update(ghostC);
128         }*/
129 }
130
131 - (BOOL)windowShouldClose:(id)sender;
132 {
133         //Let Blender close the window rather than closing immediately
134         systemCocoa->handleWindowEvent(GHOST_kEventWindowClose, associatedWindow);
135         return false;
136 }
137
138 @end
139
140 #pragma mark NSWindow subclass
141 //We need to subclass it to tell that even borderless (fullscreen), it can become key (receive user events)
142 @interface CocoaWindow: NSWindow
143 {
144         GHOST_SystemCocoa *systemCocoa;
145         GHOST_WindowCocoa *associatedWindow;
146         GHOST_TDragnDropTypes m_draggedObjectType;
147 }
148 - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa;
149 - (GHOST_SystemCocoa*)systemCocoa;
150 @end
151 @implementation CocoaWindow
152 - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa
153 {
154         systemCocoa = sysCocoa;
155         associatedWindow = winCocoa;
156 }
157 - (GHOST_SystemCocoa*)systemCocoa
158 {
159         return systemCocoa;
160 }
161
162 -(BOOL)canBecomeKeyWindow
163 {
164         return YES;
165 }
166
167 //The drag'n'drop dragging destination methods
168 - (NSDragOperation)draggingEntered:(id < NSDraggingInfo >)sender
169 {
170         NSPoint mouseLocation = [sender draggingLocation];
171         NSPasteboard *draggingPBoard = [sender draggingPasteboard];
172         
173         if ([[draggingPBoard types] containsObject:NSTIFFPboardType]) m_draggedObjectType = GHOST_kDragnDropTypeBitmap;
174         else if ([[draggingPBoard types] containsObject:NSFilenamesPboardType]) m_draggedObjectType = GHOST_kDragnDropTypeFilenames;
175         else if ([[draggingPBoard types] containsObject:NSStringPboardType]) m_draggedObjectType = GHOST_kDragnDropTypeString;
176         else return NSDragOperationNone;
177         
178         associatedWindow->setAcceptDragOperation(TRUE); //Drag operation is accepted by default
179         systemCocoa->handleDraggingEvent(GHOST_kEventDraggingEntered, m_draggedObjectType, associatedWindow, mouseLocation.x, mouseLocation.y, nil);
180         return NSDragOperationCopy;
181 }
182
183 - (BOOL)wantsPeriodicDraggingUpdates
184 {
185         return NO; //No need to overflow blender event queue. Events shall be sent only on changes
186 }
187
188 - (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender
189 {
190         NSPoint mouseLocation = [sender draggingLocation];
191         
192         systemCocoa->handleDraggingEvent(GHOST_kEventDraggingUpdated, m_draggedObjectType, associatedWindow, mouseLocation.x, mouseLocation.y, nil);
193         return associatedWindow->canAcceptDragOperation() ? NSDragOperationCopy : NSDragOperationNone;
194 }
195
196 - (void)draggingExited:(id < NSDraggingInfo >)sender
197 {
198         systemCocoa->handleDraggingEvent(GHOST_kEventDraggingExited, m_draggedObjectType, associatedWindow, 0, 0, nil);
199         m_draggedObjectType = GHOST_kDragnDropTypeUnknown;
200 }
201
202 - (BOOL)prepareForDragOperation:(id < NSDraggingInfo >)sender
203 {
204         if (associatedWindow->canAcceptDragOperation())
205                 return YES;
206         else
207                 return NO;
208 }
209
210 - (BOOL)performDragOperation:(id < NSDraggingInfo >)sender
211 {
212         NSPoint mouseLocation = [sender draggingLocation];
213         NSPasteboard *draggingPBoard = [sender draggingPasteboard];
214         NSImage *droppedImg;
215         id data;
216         
217         switch (m_draggedObjectType) {
218                 case GHOST_kDragnDropTypeBitmap:
219                         if ([NSImage canInitWithPasteboard:draggingPBoard]) {
220                                 droppedImg = [[NSImage alloc]initWithPasteboard:draggingPBoard];
221                                 data = droppedImg; //[draggingPBoard dataForType:NSTIFFPboardType];
222                         }
223                         else return NO;
224                         break;
225                 case GHOST_kDragnDropTypeFilenames:
226                         data = [draggingPBoard propertyListForType:NSFilenamesPboardType];
227                         break;
228                 case GHOST_kDragnDropTypeString:
229                         data = [draggingPBoard stringForType:NSStringPboardType];
230                         break;
231                 default:
232                         return NO;
233                         break;
234         }
235         systemCocoa->handleDraggingEvent(GHOST_kEventDraggingDropDone, m_draggedObjectType, associatedWindow, mouseLocation.x, mouseLocation.y, (void*)data);
236         return YES;
237 }
238
239 @end
240
241
242
243 #pragma mark NSOpenGLView subclass
244 //We need to subclass it in order to give Cocoa the feeling key events are trapped
245 @interface CocoaOpenGLView : NSOpenGLView <NSTextInput>
246 {
247         GHOST_SystemCocoa *systemCocoa;
248         GHOST_WindowCocoa *associatedWindow;
249
250         bool composing;
251         NSString *composing_text;
252 }
253 - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa;
254 @end
255 @implementation CocoaOpenGLView
256
257 - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa
258 {
259         systemCocoa = sysCocoa;
260         associatedWindow = winCocoa;
261
262         composing = false;
263         composing_text = nil;
264 }
265
266 - (BOOL)acceptsFirstResponder
267 {
268         return YES;
269 }
270
271 // The trick to prevent Cocoa from complaining (beeping)
272 - (void)keyDown:(NSEvent *)event
273 {
274         /* Start or continue composing? */
275         if ([[event characters] length] == 0  ||
276             [[event charactersIgnoringModifiers] length] == 0 ||
277             composing)
278         {
279                 composing = YES;
280
281                 // interpret event to call insertText
282                 NSMutableArray *events;
283                 events = [[NSMutableArray alloc] initWithCapacity:1];
284                 [events addObject:event];
285                 [self interpretKeyEvents:events]; // calls insertText
286                 [events removeObject:event];
287                 [events release];
288
289                 return;
290         }
291 }
292
293 #if MAC_OS_X_VERSION_MIN_REQUIRED <= 1040
294 //Cmd+key are handled differently before 10.5
295 - (BOOL)performKeyEquivalent:(NSEvent *)theEvent
296 {
297         NSString *chars = [theEvent charactersIgnoringModifiers];
298         
299         if ([chars length] <1) 
300                 return NO;
301         
302         //Let cocoa handle menu shortcuts
303         switch ([chars characterAtIndex:0]) {
304                 case 'q':
305                 case 'w':
306                 case 'h':
307                 case 'm':
308                 case '<':
309                 case '>':
310                 case '~':
311                 case '`':
312                         return NO;
313                 default:
314                         return YES;
315         }
316 }
317 #endif
318
319 - (BOOL)isOpaque
320 {
321         return YES;
322 }
323
324 - (void) drawRect:(NSRect)rect
325 {
326         if ([self inLiveResize]) {
327                 /* Don't redraw while in live resize */
328         }
329         else {
330                 [super drawRect:rect];
331                 systemCocoa->handleWindowEvent(GHOST_kEventWindowUpdate, associatedWindow);
332         }
333 }
334
335 // Text input
336
337 - (void)composing_free
338 {
339         composing = NO;
340
341         if (composing_text) {
342                 [composing_text release];
343                 composing_text = nil;
344         }
345 }
346
347 - (void)insertText:(id)chars
348 {
349         [self composing_free];
350 }
351
352 - (void)setMarkedText:(id)chars selectedRange:(NSRange)range
353 {
354         [self composing_free];
355         if ([chars length] == 0)
356                 return;
357
358         // start composing
359         composing = YES;
360         composing_text = [chars copy];
361
362         // if empty, cancel
363         if ([composing_text length] == 0)
364                 [self composing_free];
365 }
366
367 - (void)unmarkText
368 {
369         [self composing_free];
370 }
371
372 - (BOOL)hasMarkedText
373 {
374         return (composing) ? YES : NO;
375 }
376
377 - (void)doCommandBySelector:(SEL)selector
378 {
379 }
380
381 - (BOOL)isComposing
382 {
383         return composing;
384 }
385
386 - (NSInteger)conversationIdentifier
387 {
388         return (NSInteger)self;
389 }
390
391 - (NSAttributedString *)attributedSubstringFromRange:(NSRange)range
392 {
393         return [NSAttributedString new]; // XXX does this leak?
394 }
395
396 - (NSRange)markedRange
397 {
398         unsigned int length = (composing_text) ? [composing_text length] : 0;
399
400         if (composing)
401                 return NSMakeRange(0, length);
402
403         return NSMakeRange(NSNotFound, 0);
404 }
405
406 - (NSRange)selectedRange
407 {
408         unsigned int length = (composing_text) ? [composing_text length] : 0;
409         return NSMakeRange(0, length);
410 }
411
412 - (NSRect)firstRectForCharacterRange:(NSRange)range
413 {
414         return NSZeroRect;
415 }
416
417 - (NSUInteger)characterIndexForPoint:(NSPoint)point
418 {
419         return NSNotFound;
420 }
421
422 - (NSArray*)validAttributesForMarkedText
423 {
424         return [NSArray array]; // XXX does this leak?
425 }
426
427 @end
428
429 #pragma mark initialization / finalization
430
431 #if MAC_OS_X_VERSION_MAX_ALLOWED < 1070
432 @interface NSView (NSOpenGLSurfaceResolution)
433 - (BOOL)wantsBestResolutionOpenGLSurface;
434 - (void)setWantsBestResolutionOpenGLSurface:(BOOL)flag;
435 - (NSRect)convertRectToBacking:(NSRect)bounds;
436 @end
437 #endif
438
439 NSOpenGLContext* GHOST_WindowCocoa::s_firstOpenGLcontext = nil;
440
441 GHOST_WindowCocoa::GHOST_WindowCocoa(
442         GHOST_SystemCocoa *systemCocoa,
443         const STR_String& title,
444         GHOST_TInt32 left,
445         GHOST_TInt32 bottom,
446         GHOST_TUns32 width,
447         GHOST_TUns32 height,
448         GHOST_TWindowState state,
449         GHOST_TDrawingContextType type,
450         const bool stereoVisual, const GHOST_TUns16 numOfAASamples
451 ) :
452         GHOST_Window(width, height, state, GHOST_kDrawingContextTypeNone, stereoVisual, numOfAASamples),
453         m_customCursor(0)
454 {
455         NSOpenGLPixelFormatAttribute pixelFormatAttrsWindow[40];
456         NSOpenGLPixelFormat *pixelFormat = nil;
457         int i;
458                 
459         m_systemCocoa = systemCocoa;
460         m_fullScreen = false;
461         
462         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
463         
464         //Creates the window
465         NSRect rect;
466         NSSize  minSize;
467         
468         rect.origin.x = left;
469         rect.origin.y = bottom;
470         rect.size.width = width;
471         rect.size.height = height;
472         
473         m_window = [[CocoaWindow alloc] initWithContentRect:rect
474                                                                                    styleMask:NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask | NSMiniaturizableWindowMask
475                                                                                          backing:NSBackingStoreBuffered defer:NO];
476         if (m_window == nil) {
477                 [pool drain];
478                 return;
479         }
480         
481         [m_window setSystemAndWindowCocoa:systemCocoa windowCocoa:this];
482         
483         //Forbid to resize the window below the blender defined minimum one
484         minSize.width = 640;
485         minSize.height = 480;
486         [m_window setContentMinSize:minSize];
487         
488         setTitle(title);
489         
490         
491         // Pixel Format Attributes for the windowed NSOpenGLContext
492         i=0;
493         pixelFormatAttrsWindow[i++] = NSOpenGLPFADoubleBuffer;
494         
495         // Guarantees the back buffer contents to be valid after a call to NSOpenGLContext object's flushBuffer
496         // needed for 'Draw Overlap' drawing method
497         pixelFormatAttrsWindow[i++] = NSOpenGLPFABackingStore; 
498         
499         // Force software OpenGL, for debugging
500         if (getenv("BLENDER_SOFTWAREGL")) {
501                 pixelFormatAttrsWindow[i++] = NSOpenGLPFARendererID;
502                 pixelFormatAttrsWindow[i++] = kCGLRendererGenericID;
503         }
504         else
505                 pixelFormatAttrsWindow[i++] = NSOpenGLPFAAccelerated;
506
507         //pixelFormatAttrsWindow[i++] = NSOpenGLPFAAllowOfflineRenderers,;   // Removed to allow 10.4 builds, and 2 GPUs rendering is not used anyway
508
509         pixelFormatAttrsWindow[i++] = NSOpenGLPFADepthSize;
510         pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) 32;
511         
512         
513         if (stereoVisual) pixelFormatAttrsWindow[i++] = NSOpenGLPFAStereo;
514         
515         if (numOfAASamples > 0) {
516                 // Multisample anti-aliasing
517                 pixelFormatAttrsWindow[i++] = NSOpenGLPFAMultisample;
518                 
519                 pixelFormatAttrsWindow[i++] = NSOpenGLPFASampleBuffers;
520                 pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) 1;
521                 
522                 pixelFormatAttrsWindow[i++] = NSOpenGLPFASamples;
523                 pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) numOfAASamples;
524                 
525                 pixelFormatAttrsWindow[i++] = NSOpenGLPFANoRecovery;
526         }
527         
528         pixelFormatAttrsWindow[i] = (NSOpenGLPixelFormatAttribute) 0;
529         
530         pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:pixelFormatAttrsWindow];
531         
532         
533         //Fall back to no multisampling if Antialiasing init failed
534         if (pixelFormat == nil) {
535                 i=0;
536                 pixelFormatAttrsWindow[i++] = NSOpenGLPFADoubleBuffer;
537                 
538                 // Guarantees the back buffer contents to be valid after a call to NSOpenGLContext object's flushBuffer
539                 // needed for 'Draw Overlap' drawing method
540                 pixelFormatAttrsWindow[i++] = NSOpenGLPFABackingStore;
541                 
542                 // Force software OpenGL, for debugging
543                 if (getenv("BLENDER_SOFTWAREGL")) {
544                         pixelFormatAttrsWindow[i++] = NSOpenGLPFARendererID;
545                         pixelFormatAttrsWindow[i++] = kCGLRendererGenericID;
546                 }
547                 else
548                         pixelFormatAttrsWindow[i++] = NSOpenGLPFAAccelerated;
549
550                 //pixelFormatAttrsWindow[i++] = NSOpenGLPFAAllowOfflineRenderers,;   // Removed to allow 10.4 builds, and 2 GPUs rendering is not used anyway
551                 
552                 pixelFormatAttrsWindow[i++] = NSOpenGLPFADepthSize;
553                 pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) 32;
554                 
555                 if (stereoVisual) pixelFormatAttrsWindow[i++] = NSOpenGLPFAStereo;
556                 
557                 pixelFormatAttrsWindow[i] = (NSOpenGLPixelFormatAttribute) 0;
558                 
559                 pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:pixelFormatAttrsWindow];
560                 
561         }
562         
563         if (numOfAASamples > 0) { //Set m_numOfAASamples to the actual value
564                 GLint gli;
565                 [pixelFormat getValues:&gli forAttribute:NSOpenGLPFASamples forVirtualScreen:0];
566                 if (m_numOfAASamples != (GHOST_TUns16)gli) {
567                         m_numOfAASamples = (GHOST_TUns16)gli;
568                         printf("GHOST_Window could be created with anti-aliasing of only %i samples\n",m_numOfAASamples);
569                 }
570         }
571                 
572         //Creates the OpenGL View inside the window
573         m_openGLView = [[CocoaOpenGLView alloc] initWithFrame:rect
574                                                                                                  pixelFormat:pixelFormat];
575
576         [m_openGLView setSystemAndWindowCocoa:systemCocoa windowCocoa:this];
577         
578         [pixelFormat release];
579         
580         m_openGLContext = [m_openGLView openGLContext]; //This context will be replaced by the proper one just after
581         
582         [m_window setContentView:m_openGLView];
583         [m_window setInitialFirstResponder:m_openGLView];
584         
585         [m_window makeKeyAndOrderFront:nil];
586         
587         setDrawingContextType(type);
588         updateDrawingContext();
589         activateDrawingContext();
590
591         if (m_systemCocoa->m_nativePixel) {
592                 if ([m_openGLView respondsToSelector:@selector(setWantsBestResolutionOpenGLSurface:)]) {
593                         [m_openGLView setWantsBestResolutionOpenGLSurface:YES];
594                 
595                         NSRect backingBounds = [m_openGLView convertRectToBacking:[m_openGLView bounds]];
596                         m_systemCocoa->m_nativePixelSize = (float)backingBounds.size.width / (float)rect.size.width;
597                 }
598         }
599         
600         m_tablet.Active = GHOST_kTabletModeNone;
601         
602         CocoaWindowDelegate *windowDelegate = [[CocoaWindowDelegate alloc] init];
603         [windowDelegate setSystemAndWindowCocoa:systemCocoa windowCocoa:this];
604         [m_window setDelegate:windowDelegate];
605         
606         [m_window setAcceptsMouseMovedEvents:YES];
607         
608 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
609         NSView *view = [m_window contentView];
610         [view setAcceptsTouchEvents:YES];
611 #endif
612         
613         [m_window registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
614                                                                                   NSStringPboardType, NSTIFFPboardType, nil]];
615                                                                                   
616         if (state == GHOST_kWindowStateFullScreen)
617                 setState(GHOST_kWindowStateFullScreen);
618                 
619         [pool drain];
620 }
621
622
623 GHOST_WindowCocoa::~GHOST_WindowCocoa()
624 {
625         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
626
627         if (m_customCursor) {
628                 [m_customCursor release];
629                 m_customCursor = nil;
630         }
631
632         [m_openGLView release];
633         
634         if (m_window) {
635                 [m_window close];
636         }
637         
638         // Check for other blender opened windows and make the frontmost key
639         // Note: for some reason the closed window is still in the list
640         NSArray *windowsList = [NSApp orderedWindows];
641         for (int a = 0; a < [windowsList count]; a++) {
642                 if (m_window != (CocoaWindow *)[windowsList objectAtIndex:a]) {
643                         [[windowsList objectAtIndex:a] makeKeyWindow];
644                         break;
645                 }
646         }
647         m_window = nil;
648
649         [pool drain];
650 }
651
652 #pragma mark accessors
653
654 bool GHOST_WindowCocoa::getValid() const
655 {
656         return (m_window != 0);
657 }
658
659 void* GHOST_WindowCocoa::getOSWindow() const
660 {
661         return (void*)m_window;
662 }
663
664 void GHOST_WindowCocoa::setTitle(const STR_String& title)
665 {
666         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setTitle(): window invalid");
667         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
668
669         NSString *windowTitle = [[NSString alloc] initWithCString:title encoding:NSUTF8StringEncoding];
670         
671         //Set associated file if applicable
672         if (windowTitle && [windowTitle hasPrefix:@"Blender"]) {
673                 NSRange fileStrRange;
674                 NSString *associatedFileName;
675                 int len;
676                 
677                 fileStrRange.location = [windowTitle rangeOfString:@"["].location+1;
678                 len = [windowTitle rangeOfString:@"]"].location - fileStrRange.location;
679         
680                 if (len > 0) {
681                         fileStrRange.length = len;
682                         associatedFileName = [windowTitle substringWithRange:fileStrRange];
683                         [m_window setTitle:[associatedFileName lastPathComponent]];
684
685                         //Blender used file open/save functions converte file names into legal URL ones
686                         associatedFileName = [associatedFileName stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
687                         @try {
688                                 [m_window setRepresentedFilename:associatedFileName];
689                         }
690                         @catch (NSException * e) {
691                                 printf("\nInvalid file path given in window title");
692                         }
693                 }
694                 else {
695                         [m_window setTitle:windowTitle];
696                         [m_window setRepresentedFilename:@""];
697                 }
698
699         }
700         else {
701                 [m_window setTitle:windowTitle];
702                 [m_window setRepresentedFilename:@""];
703         }
704
705         
706         [windowTitle release];
707         [pool drain];
708 }
709
710
711 void GHOST_WindowCocoa::getTitle(STR_String& title) const
712 {
713         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getTitle(): window invalid");
714
715         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
716
717         NSString *windowTitle = [m_window title];
718
719         if (windowTitle != nil) {
720                 title = [windowTitle UTF8String];
721         }
722         
723         [pool drain];
724 }
725
726
727 void GHOST_WindowCocoa::getWindowBounds(GHOST_Rect& bounds) const
728 {
729         NSRect rect;
730         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getWindowBounds(): window invalid");
731
732         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
733         
734         NSRect screenSize = [[m_window screen] visibleFrame];
735
736         rect = [m_window frame];
737
738         bounds.m_b = screenSize.size.height - (rect.origin.y -screenSize.origin.y);
739         bounds.m_l = rect.origin.x -screenSize.origin.x;
740         bounds.m_r = rect.origin.x-screenSize.origin.x + rect.size.width;
741         bounds.m_t = screenSize.size.height - (rect.origin.y + rect.size.height -screenSize.origin.y);
742         
743         [pool drain];
744 }
745
746
747 void GHOST_WindowCocoa::getClientBounds(GHOST_Rect& bounds) const
748 {
749         NSRect rect;
750         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getClientBounds(): window invalid");
751         
752         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
753         
754         if (!m_fullScreen) {
755                 NSRect screenSize = [[m_window screen] visibleFrame];
756
757                 //Max window contents as screen size (excluding title bar...)
758                 NSRect contentRect = [CocoaWindow contentRectForFrameRect:screenSize
759                                                                                                          styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)];
760
761                 rect = [m_window contentRectForFrameRect:[m_window frame]];
762                 
763                 bounds.m_b = contentRect.size.height - (rect.origin.y -contentRect.origin.y);
764                 bounds.m_l = rect.origin.x -contentRect.origin.x;
765                 bounds.m_r = rect.origin.x-contentRect.origin.x + rect.size.width;
766                 bounds.m_t = contentRect.size.height - (rect.origin.y + rect.size.height -contentRect.origin.y);
767         }
768         else {
769                 NSRect screenSize = [[m_window screen] frame];
770                 
771                 bounds.m_b = screenSize.origin.y + screenSize.size.height;
772                 bounds.m_l = screenSize.origin.x;
773                 bounds.m_r = screenSize.origin.x + screenSize.size.width;
774                 bounds.m_t = screenSize.origin.y;
775         }
776         [pool drain];
777 }
778
779
780 GHOST_TSuccess GHOST_WindowCocoa::setClientWidth(GHOST_TUns32 width)
781 {
782         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientWidth(): window invalid");
783         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
784         GHOST_Rect cBnds, wBnds;
785         getClientBounds(cBnds);
786         if (((GHOST_TUns32)cBnds.getWidth()) != width) {
787                 NSSize size;
788                 size.width=width;
789                 size.height=cBnds.getHeight();
790                 [m_window setContentSize:size];
791         }
792         [pool drain];
793         return GHOST_kSuccess;
794 }
795
796
797 GHOST_TSuccess GHOST_WindowCocoa::setClientHeight(GHOST_TUns32 height)
798 {
799         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientHeight(): window invalid");
800         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
801         GHOST_Rect cBnds, wBnds;
802         getClientBounds(cBnds);
803         if (((GHOST_TUns32)cBnds.getHeight()) != height) {
804                 NSSize size;
805                 size.width=cBnds.getWidth();
806                 size.height=height;
807                 [m_window setContentSize:size];
808         }
809         [pool drain];
810         return GHOST_kSuccess;
811 }
812
813
814 GHOST_TSuccess GHOST_WindowCocoa::setClientSize(GHOST_TUns32 width, GHOST_TUns32 height)
815 {
816         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientSize(): window invalid");
817         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
818         GHOST_Rect cBnds, wBnds;
819         getClientBounds(cBnds);
820         if ((((GHOST_TUns32)cBnds.getWidth())  != width) ||
821             (((GHOST_TUns32)cBnds.getHeight()) != height))
822         {
823                 NSSize size;
824                 size.width=width;
825                 size.height=height;
826                 [m_window setContentSize:size];
827         }
828         [pool drain];
829         return GHOST_kSuccess;
830 }
831
832
833 GHOST_TWindowState GHOST_WindowCocoa::getState() const
834 {
835         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getState(): window invalid");
836         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
837         GHOST_TWindowState state;
838         if (m_fullScreen) {
839                 state = GHOST_kWindowStateFullScreen;
840         } 
841         else if ([m_window isMiniaturized]) {
842                 state = GHOST_kWindowStateMinimized;
843         }
844         else if ([m_window isZoomed]) {
845                 state = GHOST_kWindowStateMaximized;
846         }
847         else {
848                 state = GHOST_kWindowStateNormal;
849         }
850         [pool drain];
851         return state;
852 }
853
854
855 void GHOST_WindowCocoa::screenToClient(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
856 {
857         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::screenToClient(): window invalid");
858
859         screenToClientIntern(inX, inY, outX, outY);
860
861         /* switch y to match ghost convention */
862         GHOST_Rect cBnds;
863         getClientBounds(cBnds);
864         outY = (cBnds.getHeight() - 1) - outY;
865 }
866
867
868 void GHOST_WindowCocoa::clientToScreen(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
869 {
870         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::clientToScreen(): window invalid");
871
872         /* switch y to match ghost convention */
873         GHOST_Rect cBnds;
874         getClientBounds(cBnds);
875         inY = (cBnds.getHeight() - 1) - inY;
876
877         clientToScreenIntern(inX, inY, outX, outY);
878 }
879
880 void GHOST_WindowCocoa::screenToClientIntern(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
881 {
882         NSPoint screenCoord;
883         NSPoint baseCoord;
884         
885         screenCoord.x = inX;
886         screenCoord.y = inY;
887         
888         baseCoord = [m_window convertScreenToBase:screenCoord];
889         
890         outX = baseCoord.x;
891         outY = baseCoord.y;
892 }
893
894 void GHOST_WindowCocoa::clientToScreenIntern(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
895 {
896         NSPoint screenCoord;
897         NSPoint baseCoord;
898         
899         baseCoord.x = inX;
900         baseCoord.y = inY;
901         
902         screenCoord = [m_window convertBaseToScreen:baseCoord];
903         
904         outX = screenCoord.x;
905         outY = screenCoord.y;
906 }
907
908
909 NSScreen* GHOST_WindowCocoa::getScreen()
910 {
911         return [m_window screen];
912 }
913
914
915 /**
916  * \note Fullscreen switch is not actual fullscreen with display capture.
917  * As this capture removes all OS X window manager features.
918  *
919  * Instead, the menu bar and the dock are hidden, and the window is made borderless and enlarged.
920  * Thus, process switch, exposé, spaces, ... still work in fullscreen mode
921  */
922 GHOST_TSuccess GHOST_WindowCocoa::setState(GHOST_TWindowState state)
923 {
924         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setState(): window invalid");
925         switch (state) {
926                 case GHOST_kWindowStateMinimized:
927                         [m_window miniaturize:nil];
928                         break;
929                 case GHOST_kWindowStateMaximized:
930                         [m_window zoom:nil];
931                         break;
932                 
933                 case GHOST_kWindowStateFullScreen:
934                         if (!m_fullScreen) {
935                                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
936                         
937                                 /* This status change needs to be done before Cocoa call to enter fullscreen mode
938                                  * to give window delegate hint not to forward its deactivation to ghost wm that
939                                  * doesn't know view/window difference. */
940                                 m_fullScreen = true;
941
942 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
943                                 //10.6 provides Cocoa functions to autoshow menu bar, and to change a window style
944                                 //Hide menu & dock if needed
945                                 if ([[m_window screen] isEqual:[[NSScreen screens] objectAtIndex:0]]) {
946                                         [NSApp setPresentationOptions:(NSApplicationPresentationHideDock | NSApplicationPresentationAutoHideMenuBar)];
947                                 }
948                                 //Make window borderless and enlarge it
949                                 [m_window setStyleMask:NSBorderlessWindowMask];
950                                 [m_window setFrame:[[m_window screen] frame] display:YES];
951                                 [m_window makeFirstResponder:m_openGLView];
952 #else
953                                 //With 10.5, we need to create a new window to change its style to borderless
954                                 //Hide menu & dock if needed
955                                 if ([[m_window screen] isEqual:[[NSScreen screens] objectAtIndex:0]]) {
956                                         //Cocoa function in 10.5 does not allow to set the menu bar in auto-show mode [NSMenu setMenuBarVisible:NO];
957                                         //One of the very few 64bit compatible Carbon function
958                                         SetSystemUIMode(kUIModeAllHidden,kUIOptionAutoShowMenuBar);
959                                 }
960                                 //Create a fullscreen borderless window
961                                 CocoaWindow *tmpWindow = [[CocoaWindow alloc]
962                                                                                   initWithContentRect:[[m_window screen] frame]
963                                                                                   styleMask:NSBorderlessWindowMask
964                                                                                   backing:NSBackingStoreBuffered
965                                                                                   defer:YES];
966                                 //Copy current window parameters
967                                 [tmpWindow setTitle:[m_window title]];
968                                 [tmpWindow setRepresentedFilename:[m_window representedFilename]];
969                                 [tmpWindow setAcceptsMouseMovedEvents:YES];
970                                 [tmpWindow setDelegate:[m_window delegate]];
971                                 [tmpWindow setSystemAndWindowCocoa:[m_window systemCocoa] windowCocoa:this];
972                                 [tmpWindow registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
973                                                                     NSStringPboardType, NSTIFFPboardType, nil]];
974                                 
975                                 //Assign the openGL view to the new window
976                                 [tmpWindow setContentView:m_openGLView];
977                                 
978                                 //Show the new window
979                                 [tmpWindow makeKeyAndOrderFront:m_openGLView];
980                                 //Close and release old window
981                                 [m_window setDelegate:nil]; // To avoid the notification of "window closed" event
982                                 [m_window close];
983                                 [m_window release];
984                                 m_window = tmpWindow;
985 #endif
986                         
987                                 //Tell WM of view new size
988                                 m_systemCocoa->handleWindowEvent(GHOST_kEventWindowSize, this);
989                                 
990                                 [pool drain];
991                                 }
992                         break;
993                 case GHOST_kWindowStateNormal:
994                 default:
995                         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
996                         if (m_fullScreen) {
997                                 m_fullScreen = false;
998
999                                 //Exit fullscreen
1000 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
1001                                 //Show again menu & dock if needed
1002                                 if ([[m_window screen] isEqual:[NSScreen mainScreen]]) {
1003                                         [NSApp setPresentationOptions:NSApplicationPresentationDefault];
1004                                 }
1005                                 //Make window normal and resize it
1006                                 [m_window setStyleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)];
1007                                 [m_window setFrame:[[m_window screen] visibleFrame] display:YES];
1008                                 //TODO for 10.6 only : window title is forgotten after the style change
1009                                 [m_window makeFirstResponder:m_openGLView];
1010 #else
1011                                 //With 10.5, we need to create a new window to change its style to borderless
1012                                 //Show menu & dock if needed
1013                                 if ([[m_window screen] isEqual:[NSScreen mainScreen]]) {
1014                                         //Cocoa function in 10.5 does not allow to set the menu bar in auto-show mode [NSMenu setMenuBarVisible:YES];
1015                                         SetSystemUIMode(kUIModeNormal, 0); //One of the very few 64bit compatible Carbon function
1016                                 }
1017                                 //Create a fullscreen borderless window
1018                                 CocoaWindow *tmpWindow = [[CocoaWindow alloc]
1019                                                                                   initWithContentRect:[[m_window screen] frame]
1020                                                                                                         styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)
1021                                                                                                           backing:NSBackingStoreBuffered
1022                                                                                                                 defer:YES];
1023                                 //Copy current window parameters
1024                                 [tmpWindow setTitle:[m_window title]];
1025                                 [tmpWindow setRepresentedFilename:[m_window representedFilename]];
1026                                 [tmpWindow setAcceptsMouseMovedEvents:YES];
1027                                 [tmpWindow setDelegate:[m_window delegate]];
1028                                 [tmpWindow setSystemAndWindowCocoa:[m_window systemCocoa] windowCocoa:this];
1029                                 [tmpWindow registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
1030                                                                                                    NSStringPboardType, NSTIFFPboardType, nil]];
1031                                 //Forbid to resize the window below the blender defined minimum one
1032                                 [tmpWindow setContentMinSize:NSMakeSize(640, 480)];
1033                                 
1034                                 //Assign the openGL view to the new window
1035                                 [tmpWindow setContentView:m_openGLView];
1036                                 
1037                                 //Show the new window
1038                                 [tmpWindow makeKeyAndOrderFront:nil];
1039                                 //Close and release old window
1040                                 [m_window setDelegate:nil]; // To avoid the notification of "window closed" event
1041                                 [m_window close];
1042                                 [m_window release];
1043                                 m_window = tmpWindow;
1044 #endif
1045                         
1046                                 //Tell WM of view new size
1047                                 m_systemCocoa->handleWindowEvent(GHOST_kEventWindowSize, this);
1048                         }
1049                         else if ([m_window isMiniaturized])
1050                                 [m_window deminiaturize:nil];
1051                         else if ([m_window isZoomed])
1052                                 [m_window zoom:nil];
1053                         [pool drain];
1054                         break;
1055         }
1056
1057         return GHOST_kSuccess;
1058 }
1059
1060 GHOST_TSuccess GHOST_WindowCocoa::setModifiedState(bool isUnsavedChanges)
1061 {
1062         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1063         
1064         [m_window setDocumentEdited:isUnsavedChanges];
1065         
1066         [pool drain];
1067         return GHOST_Window::setModifiedState(isUnsavedChanges);
1068 }
1069
1070
1071
1072 GHOST_TSuccess GHOST_WindowCocoa::setOrder(GHOST_TWindowOrder order)
1073 {
1074         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1075         
1076         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setOrder(): window invalid");
1077         if (order == GHOST_kWindowOrderTop) {
1078                 [m_window makeKeyAndOrderFront:nil];
1079         }
1080         else {
1081                 NSArray *windowsList;
1082                 
1083                 [m_window orderBack:nil];
1084                 
1085                 //Check for other blender opened windows and make the frontmost key
1086                 windowsList = [NSApp orderedWindows];
1087                 if ([windowsList count]) {
1088                         [[windowsList objectAtIndex:0] makeKeyAndOrderFront:nil];
1089                 }
1090         }
1091         
1092         [pool drain];
1093         return GHOST_kSuccess;
1094 }
1095
1096 #pragma mark Drawing context
1097
1098 /*#define  WAIT_FOR_VSYNC 1*/
1099
1100 GHOST_TSuccess GHOST_WindowCocoa::swapBuffers()
1101 {
1102         if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
1103                 if (m_openGLContext != nil) {
1104                         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1105                         [m_openGLContext flushBuffer];
1106                         [pool drain];
1107                         return GHOST_kSuccess;
1108                 }
1109         }
1110         return GHOST_kFailure;
1111 }
1112
1113 GHOST_TSuccess GHOST_WindowCocoa::updateDrawingContext()
1114 {
1115         if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
1116                 if (m_openGLContext != nil) {
1117                         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1118                         [m_openGLContext update];
1119                         [pool drain];
1120                         return GHOST_kSuccess;
1121                 }
1122         }
1123         return GHOST_kFailure;
1124 }
1125
1126 GHOST_TSuccess GHOST_WindowCocoa::activateDrawingContext()
1127 {
1128         if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
1129                 if (m_openGLContext != nil) {
1130                         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1131                         [m_openGLContext makeCurrentContext];
1132                         
1133                         // Disable AA by default
1134                         if (m_numOfAASamples > 0) glDisable(GL_MULTISAMPLE_ARB);
1135                         [pool drain];
1136                         return GHOST_kSuccess;
1137                 }
1138         }
1139         return GHOST_kFailure;
1140 }
1141
1142
1143 GHOST_TSuccess GHOST_WindowCocoa::installDrawingContext(GHOST_TDrawingContextType type)
1144 {
1145         GHOST_TSuccess success = GHOST_kFailure;
1146         
1147         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1148         
1149         NSOpenGLPixelFormat *pixelFormat;
1150         NSOpenGLContext *tmpOpenGLContext;
1151         
1152         /***** Multithreaded opengl code : uncomment for enabling
1153         CGLContextObj cglCtx;
1154         */
1155          
1156         switch (type) {
1157                 case GHOST_kDrawingContextTypeOpenGL:
1158                         if (!getValid()) break;
1159
1160                         pixelFormat = [m_openGLView pixelFormat];
1161                         tmpOpenGLContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat
1162                                                                                                                           shareContext:s_firstOpenGLcontext];
1163                         if (tmpOpenGLContext == nil) {
1164                                 success = GHOST_kFailure;
1165                                 break;
1166                         }
1167                         
1168                         //Switch openGL to multhreaded mode
1169                         /******* Multithreaded opengl code : uncomment for enabling
1170                         cglCtx = (CGLContextObj)[tmpOpenGLContext CGLContextObj];
1171                         if (CGLEnable(cglCtx, kCGLCEMPEngine) == kCGLNoError)
1172                                 printf("\nSwitched openGL to multithreaded mode\n");
1173                          */
1174                         
1175                         if (!s_firstOpenGLcontext) s_firstOpenGLcontext = tmpOpenGLContext;
1176 #ifdef WAIT_FOR_VSYNC
1177                         {
1178                                 GLint swapInt = 1;
1179                                 /* wait for vsync, to avoid tearing artifacts */
1180                                 [tmpOpenGLContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
1181                         }
1182 #endif
1183                         [m_openGLView setOpenGLContext:tmpOpenGLContext];
1184                         [tmpOpenGLContext setView:m_openGLView];
1185                         
1186                         m_openGLContext = tmpOpenGLContext;
1187                         break;
1188         
1189                 case GHOST_kDrawingContextTypeNone:
1190                         success = GHOST_kSuccess;
1191                         break;
1192                 
1193                 default:
1194                         break;
1195         }
1196         [pool drain];
1197         return success;
1198 }
1199
1200
1201 GHOST_TSuccess GHOST_WindowCocoa::removeDrawingContext()
1202 {
1203         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1204         switch (m_drawingContextType) {
1205                 case GHOST_kDrawingContextTypeOpenGL:
1206                         if (m_openGLContext) {
1207                                 [m_openGLView clearGLContext];
1208                                 if (s_firstOpenGLcontext == m_openGLContext) s_firstOpenGLcontext = nil;
1209                                 m_openGLContext = nil;
1210                         }
1211                         [pool drain];
1212                         return GHOST_kSuccess;
1213                 case GHOST_kDrawingContextTypeNone:
1214                         [pool drain];
1215                         return GHOST_kSuccess;
1216                         break;
1217                 default:
1218                         [pool drain];
1219                         return GHOST_kFailure;
1220         }
1221 }
1222
1223
1224 GHOST_TSuccess GHOST_WindowCocoa::invalidate()
1225 {
1226         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::invalidate(): window invalid");
1227         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1228         [m_openGLView setNeedsDisplay:YES];
1229         [pool drain];
1230         return GHOST_kSuccess;
1231 }
1232
1233 #pragma mark Progress bar
1234
1235 GHOST_TSuccess GHOST_WindowCocoa::setProgressBar(float progress)
1236 {
1237         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1238         
1239         if ((progress >=0.0) && (progress <=1.0)) {
1240                 NSImage* dockIcon = [[NSImage alloc] initWithSize:NSMakeSize(128,128)];
1241                 
1242                 [dockIcon lockFocus];
1243                 NSRect progressBox = {{4, 4}, {120, 16}};
1244
1245                 [[NSImage imageNamed:@"NSApplicationIcon"] drawAtPoint:NSZeroPoint fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
1246
1247                 // Track & Outline
1248                 [[NSColor blackColor] setFill];
1249                 NSRectFill(progressBox);
1250
1251                 [[NSColor whiteColor] set];
1252                 NSFrameRect(progressBox);
1253
1254                 // Progress fill
1255                 progressBox = NSInsetRect(progressBox, 1, 1);
1256
1257                 progressBox.size.width = progressBox.size.width * progress;
1258                 NSGradient *gradient = [[NSGradient alloc] initWithStartingColor:[NSColor darkGrayColor] endingColor:[NSColor lightGrayColor]];
1259                 [gradient drawInRect:progressBox angle:90];
1260                 [gradient release];
1261                 
1262                 [dockIcon unlockFocus];
1263                 
1264                 [NSApp setApplicationIconImage:dockIcon];
1265                 [dockIcon release];
1266                 
1267                 m_progressBarVisible = true;
1268         }
1269         
1270         [pool drain];
1271         return GHOST_kSuccess;
1272 }
1273
1274
1275 GHOST_TSuccess GHOST_WindowCocoa::endProgressBar()
1276 {
1277         if (!m_progressBarVisible) return GHOST_kFailure;
1278         m_progressBarVisible = false;
1279         
1280         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1281         
1282         NSImage* dockIcon = [[NSImage alloc] initWithSize:NSMakeSize(128,128)];
1283         [dockIcon lockFocus];
1284         [[NSImage imageNamed:@"NSApplicationIcon"] drawAtPoint:NSZeroPoint fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
1285         [dockIcon unlockFocus];
1286         [NSApp setApplicationIconImage:dockIcon];
1287         [dockIcon release];
1288         
1289         [pool drain];
1290         return GHOST_kSuccess;
1291 }
1292
1293
1294
1295 #pragma mark Cursor handling
1296
1297 void GHOST_WindowCocoa::loadCursor(bool visible, GHOST_TStandardCursor cursor) const
1298 {
1299         static bool systemCursorVisible = true;
1300         
1301         NSCursor *tmpCursor =nil;
1302         
1303         if (visible != systemCursorVisible) {
1304                 if (visible) {
1305                         [NSCursor unhide];
1306                         systemCursorVisible = true;
1307                 }
1308                 else {
1309                         [NSCursor hide];
1310                         systemCursorVisible = false;
1311                 }
1312         }
1313
1314         if (cursor == GHOST_kStandardCursorCustom && m_customCursor) {
1315                 tmpCursor = m_customCursor;
1316         }
1317         else {
1318                 switch (cursor) {
1319                         case GHOST_kStandardCursorDestroy:
1320                                 tmpCursor = [NSCursor disappearingItemCursor];
1321                                 break;
1322                         case GHOST_kStandardCursorText:
1323                                 tmpCursor = [NSCursor IBeamCursor];
1324                                 break;
1325                         case GHOST_kStandardCursorCrosshair:
1326                                 tmpCursor = [NSCursor crosshairCursor];
1327                                 break;
1328                         case GHOST_kStandardCursorUpDown:
1329                                 tmpCursor = [NSCursor resizeUpDownCursor];
1330                                 break;
1331                         case GHOST_kStandardCursorLeftRight:
1332                                 tmpCursor = [NSCursor resizeLeftRightCursor];
1333                                 break;
1334                         case GHOST_kStandardCursorTopSide:
1335                                 tmpCursor = [NSCursor resizeUpCursor];
1336                                 break;
1337                         case GHOST_kStandardCursorBottomSide:
1338                                 tmpCursor = [NSCursor resizeDownCursor];
1339                                 break;
1340                         case GHOST_kStandardCursorLeftSide:
1341                                 tmpCursor = [NSCursor resizeLeftCursor];
1342                                 break;
1343                         case GHOST_kStandardCursorRightSide:
1344                                 tmpCursor = [NSCursor resizeRightCursor];
1345                                 break;
1346                         case GHOST_kStandardCursorRightArrow:
1347                         case GHOST_kStandardCursorInfo:
1348                         case GHOST_kStandardCursorLeftArrow:
1349                         case GHOST_kStandardCursorHelp:
1350                         case GHOST_kStandardCursorCycle:
1351                         case GHOST_kStandardCursorSpray:
1352                         case GHOST_kStandardCursorWait:
1353                         case GHOST_kStandardCursorTopLeftCorner:
1354                         case GHOST_kStandardCursorTopRightCorner:
1355                         case GHOST_kStandardCursorBottomRightCorner:
1356                         case GHOST_kStandardCursorBottomLeftCorner:
1357                         case GHOST_kStandardCursorCopy:
1358                         case GHOST_kStandardCursorDefault:
1359                         default:
1360                                 tmpCursor = [NSCursor arrowCursor];
1361                                 break;
1362                 };
1363         }
1364         [tmpCursor set];
1365 }
1366
1367
1368
1369 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorVisibility(bool visible)
1370 {
1371         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
1372         
1373         if ([m_window isVisible]) {
1374                 loadCursor(visible, getCursorShape());
1375         }
1376         
1377         [pool drain];
1378         return GHOST_kSuccess;
1379 }
1380
1381
1382 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorGrab(GHOST_TGrabCursorMode mode)
1383 {
1384         GHOST_TSuccess err = GHOST_kSuccess;
1385         
1386         if (mode != GHOST_kGrabDisable) {
1387                 //No need to perform grab without warp as it is always on in OS X
1388                 if (mode != GHOST_kGrabNormal) {
1389                         GHOST_TInt32 x_old,y_old;
1390                         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1391
1392                         m_systemCocoa->getCursorPosition(x_old,y_old);
1393                         screenToClientIntern(x_old, y_old, m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]);
1394                         //Warp position is stored in client (window base) coordinates
1395                         setCursorGrabAccum(0, 0);
1396                         
1397                         if (mode == GHOST_kGrabHide) {
1398                                 setWindowCursorVisibility(false);
1399                         }
1400                         
1401                         //Make window key if it wasn't to get the mouse move events
1402                         [m_window makeKeyWindow];
1403                         
1404                         //Dissociate cursor position even for warp mode, to allow mouse acceleration to work even when warping the cursor
1405                         err = CGAssociateMouseAndMouseCursorPosition(false) == kCGErrorSuccess ? GHOST_kSuccess : GHOST_kFailure;
1406                         
1407                         [pool drain];
1408                 }
1409         }
1410         else {
1411                 if (m_cursorGrab==GHOST_kGrabHide) {
1412                         //No need to set again cursor position, as it has not changed for Cocoa
1413                         setWindowCursorVisibility(true);
1414                 }
1415                 
1416                 err = CGAssociateMouseAndMouseCursorPosition(true) == kCGErrorSuccess ? GHOST_kSuccess : GHOST_kFailure;
1417                 /* Almost works without but important otherwise the mouse GHOST location can be incorrect on exit */
1418                 setCursorGrabAccum(0, 0);
1419                 m_cursorGrabBounds.m_l= m_cursorGrabBounds.m_r= -1; /* disable */
1420         }
1421         return err;
1422 }
1423         
1424 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorShape(GHOST_TStandardCursor shape)
1425 {
1426         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1427
1428         if (m_customCursor) {
1429                 [m_customCursor release];
1430                 m_customCursor = nil;
1431         }
1432
1433         if ([m_window isVisible]) {
1434                 loadCursor(getCursorVisibility(), shape);
1435         }
1436         
1437         [pool drain];
1438         return GHOST_kSuccess;
1439 }
1440
1441 /** Reverse the bits in a GHOST_TUns8
1442 static GHOST_TUns8 uns8ReverseBits(GHOST_TUns8 ch)
1443 {
1444         ch= ((ch >> 1) & 0x55) | ((ch << 1) & 0xAA);
1445         ch= ((ch >> 2) & 0x33) | ((ch << 2) & 0xCC);
1446         ch= ((ch >> 4) & 0x0F) | ((ch << 4) & 0xF0);
1447         return ch;
1448 }
1449 */
1450
1451
1452 /** Reverse the bits in a GHOST_TUns16 */
1453 static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
1454 {
1455         shrt = ((shrt >> 1) & 0x5555) | ((shrt << 1) & 0xAAAA);
1456         shrt = ((shrt >> 2) & 0x3333) | ((shrt << 2) & 0xCCCC);
1457         shrt = ((shrt >> 4) & 0x0F0F) | ((shrt << 4) & 0xF0F0);
1458         shrt = ((shrt >> 8) & 0x00FF) | ((shrt << 8) & 0xFF00);
1459         return shrt;
1460 }
1461
1462 GHOST_TSuccess GHOST_WindowCocoa::setWindowCustomCursorShape(GHOST_TUns8 *bitmap, GHOST_TUns8 *mask,
1463                                                              int sizex, int sizey, int hotX, int hotY, int fg_color, int bg_color)
1464 {
1465         int y,nbUns16;
1466         NSPoint hotSpotPoint;
1467         NSBitmapImageRep *cursorImageRep;
1468         NSImage *cursorImage;
1469         NSSize imSize;
1470         GHOST_TUns16 *cursorBitmap;
1471         
1472         
1473         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1474         
1475         if (m_customCursor) {
1476                 [m_customCursor release];
1477                 m_customCursor = nil;
1478         }
1479         
1480
1481         cursorImageRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil
1482                                                                                                                          pixelsWide:sizex
1483                                                                                                                          pixelsHigh:sizey
1484                                                                                                                   bitsPerSample:1 
1485                                                                                                                 samplesPerPixel:2
1486                                                                                                                            hasAlpha:YES
1487                                                                                                                            isPlanar:YES
1488                                                                                                                  colorSpaceName:NSDeviceWhiteColorSpace
1489                                                                                                                         bytesPerRow:(sizex/8 + (sizex%8 >0 ?1:0))
1490                                                                                                                    bitsPerPixel:1];
1491         
1492         
1493         cursorBitmap = (GHOST_TUns16*)[cursorImageRep bitmapData];
1494         nbUns16 = [cursorImageRep bytesPerPlane]/2;
1495         
1496         for (y=0; y<nbUns16; y++) {
1497 #if !defined(__LITTLE_ENDIAN__)
1498                 cursorBitmap[y] = ~uns16ReverseBits((bitmap[2*y]<<0) | (bitmap[2*y+1]<<8));
1499                 cursorBitmap[nbUns16+y] = uns16ReverseBits((mask[2*y]<<0) | (mask[2*y+1]<<8));
1500 #else
1501                 cursorBitmap[y] = ~uns16ReverseBits((bitmap[2*y+1]<<0) | (bitmap[2*y]<<8));
1502                 cursorBitmap[nbUns16+y] = uns16ReverseBits((mask[2*y+1]<<0) | (mask[2*y]<<8));
1503 #endif
1504                 
1505         }
1506         
1507         
1508         imSize.width = sizex;
1509         imSize.height= sizey;
1510         cursorImage = [[NSImage alloc] initWithSize:imSize];
1511         [cursorImage addRepresentation:cursorImageRep];
1512         
1513         hotSpotPoint.x = hotX;
1514         hotSpotPoint.y = hotY;
1515         
1516         //foreground and background color parameter is not handled for now (10.6)
1517         m_customCursor = [[NSCursor alloc] initWithImage:cursorImage
1518                                                                                          hotSpot:hotSpotPoint];
1519         
1520         [cursorImageRep release];
1521         [cursorImage release];
1522         
1523         if ([m_window isVisible]) {
1524                 loadCursor(getCursorVisibility(), GHOST_kStandardCursorCustom);
1525         }
1526         [pool drain];
1527         return GHOST_kSuccess;
1528 }
1529
1530 GHOST_TSuccess GHOST_WindowCocoa::setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2], 
1531                                                              GHOST_TUns8 mask[16][2], int hotX, int hotY)
1532 {
1533         return setWindowCustomCursorShape((GHOST_TUns8*)bitmap, (GHOST_TUns8*) mask, 16, 16, hotX, hotY, 0, 1);
1534 }