Fix #33644: rendering to a new window and then closing it would not completely
[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 NSOpenGLContext* GHOST_WindowCocoa::s_firstOpenGLcontext = nil;
432
433 GHOST_WindowCocoa::GHOST_WindowCocoa(
434         GHOST_SystemCocoa *systemCocoa,
435         const STR_String& title,
436         GHOST_TInt32 left,
437         GHOST_TInt32 bottom,
438         GHOST_TUns32 width,
439         GHOST_TUns32 height,
440         GHOST_TWindowState state,
441         GHOST_TDrawingContextType type,
442         const bool stereoVisual, const GHOST_TUns16 numOfAASamples
443 ) :
444         GHOST_Window(width, height, state, GHOST_kDrawingContextTypeNone, stereoVisual, numOfAASamples),
445         m_customCursor(0)
446 {
447         NSOpenGLPixelFormatAttribute pixelFormatAttrsWindow[40];
448         NSOpenGLPixelFormat *pixelFormat = nil;
449         int i;
450                 
451         m_systemCocoa = systemCocoa;
452         m_fullScreen = false;
453         
454         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
455         
456         //Creates the window
457         NSRect rect;
458         NSSize  minSize;
459         
460         rect.origin.x = left;
461         rect.origin.y = bottom;
462         rect.size.width = width;
463         rect.size.height = height;
464         
465         m_window = [[CocoaWindow alloc] initWithContentRect:rect
466                                                                                    styleMask:NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask | NSMiniaturizableWindowMask
467                                                                                          backing:NSBackingStoreBuffered defer:NO];
468         if (m_window == nil) {
469                 [pool drain];
470                 return;
471         }
472         
473         [m_window setSystemAndWindowCocoa:systemCocoa windowCocoa:this];
474         
475         //Forbid to resize the window below the blender defined minimum one
476         minSize.width = 640;
477         minSize.height = 480;
478         [m_window setContentMinSize:minSize];
479         
480         setTitle(title);
481         
482         
483         // Pixel Format Attributes for the windowed NSOpenGLContext
484         i=0;
485         pixelFormatAttrsWindow[i++] = NSOpenGLPFADoubleBuffer;
486         
487         // Guarantees the back buffer contents to be valid after a call to NSOpenGLContext object's flushBuffer
488         // needed for 'Draw Overlap' drawing method
489         pixelFormatAttrsWindow[i++] = NSOpenGLPFABackingStore; 
490         
491         // Force software OpenGL, for debugging
492         if (getenv("BLENDER_SOFTWAREGL")) {
493                 pixelFormatAttrsWindow[i++] = NSOpenGLPFARendererID;
494                 pixelFormatAttrsWindow[i++] = kCGLRendererGenericID;
495         }
496         else
497                 pixelFormatAttrsWindow[i++] = NSOpenGLPFAAccelerated;
498
499         //pixelFormatAttrsWindow[i++] = NSOpenGLPFAAllowOfflineRenderers,;   // Removed to allow 10.4 builds, and 2 GPUs rendering is not used anyway
500
501         pixelFormatAttrsWindow[i++] = NSOpenGLPFADepthSize;
502         pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) 32;
503         
504         
505         if (stereoVisual) pixelFormatAttrsWindow[i++] = NSOpenGLPFAStereo;
506         
507         if (numOfAASamples > 0) {
508                 // Multisample anti-aliasing
509                 pixelFormatAttrsWindow[i++] = NSOpenGLPFAMultisample;
510                 
511                 pixelFormatAttrsWindow[i++] = NSOpenGLPFASampleBuffers;
512                 pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) 1;
513                 
514                 pixelFormatAttrsWindow[i++] = NSOpenGLPFASamples;
515                 pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) numOfAASamples;
516                 
517                 pixelFormatAttrsWindow[i++] = NSOpenGLPFANoRecovery;
518         }
519         
520         pixelFormatAttrsWindow[i] = (NSOpenGLPixelFormatAttribute) 0;
521         
522         pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:pixelFormatAttrsWindow];
523         
524         
525         //Fall back to no multisampling if Antialiasing init failed
526         if (pixelFormat == nil) {
527                 i=0;
528                 pixelFormatAttrsWindow[i++] = NSOpenGLPFADoubleBuffer;
529                 
530                 // Guarantees the back buffer contents to be valid after a call to NSOpenGLContext object's flushBuffer
531                 // needed for 'Draw Overlap' drawing method
532                 pixelFormatAttrsWindow[i++] = NSOpenGLPFABackingStore;
533                 
534                 // Force software OpenGL, for debugging
535                 if (getenv("BLENDER_SOFTWAREGL")) {
536                         pixelFormatAttrsWindow[i++] = NSOpenGLPFARendererID;
537                         pixelFormatAttrsWindow[i++] = kCGLRendererGenericID;
538                 }
539                 else
540                         pixelFormatAttrsWindow[i++] = NSOpenGLPFAAccelerated;
541
542                 //pixelFormatAttrsWindow[i++] = NSOpenGLPFAAllowOfflineRenderers,;   // Removed to allow 10.4 builds, and 2 GPUs rendering is not used anyway
543                 
544                 pixelFormatAttrsWindow[i++] = NSOpenGLPFADepthSize;
545                 pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) 32;
546                 
547                 if (stereoVisual) pixelFormatAttrsWindow[i++] = NSOpenGLPFAStereo;
548                 
549                 pixelFormatAttrsWindow[i] = (NSOpenGLPixelFormatAttribute) 0;
550                 
551                 pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:pixelFormatAttrsWindow];
552                 
553         }
554         
555         if (numOfAASamples > 0) { //Set m_numOfAASamples to the actual value
556                 GLint gli;
557                 [pixelFormat getValues:&gli forAttribute:NSOpenGLPFASamples forVirtualScreen:0];
558                 if (m_numOfAASamples != (GHOST_TUns16)gli) {
559                         m_numOfAASamples = (GHOST_TUns16)gli;
560                         printf("GHOST_Window could be created with anti-aliasing of only %i samples\n",m_numOfAASamples);
561                 }
562         }
563                 
564         //Creates the OpenGL View inside the window
565         m_openGLView = [[CocoaOpenGLView alloc] initWithFrame:rect
566                                                                                                  pixelFormat:pixelFormat];
567
568         [m_openGLView setSystemAndWindowCocoa:systemCocoa windowCocoa:this];
569         
570         [pixelFormat release];
571         
572         m_openGLContext = [m_openGLView openGLContext]; //This context will be replaced by the proper one just after
573         
574         [m_window setContentView:m_openGLView];
575         [m_window setInitialFirstResponder:m_openGLView];
576         
577         [m_window makeKeyAndOrderFront:nil];
578         
579         setDrawingContextType(type);
580         updateDrawingContext();
581         activateDrawingContext();
582 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1070       // retina support started with 10.7.4 afaik
583         if (m_systemCocoa->m_nativePixel) {
584                 [m_openGLView setWantsBestResolutionOpenGLSurface:YES];
585                 NSRect backingBounds = [m_openGLView convertRectToBacking:[m_openGLView bounds]];
586                 m_systemCocoa->m_nativePixelSize = (float)backingBounds.size.width / (float)rect.size.width;
587         }
588 #endif
589         
590         m_tablet.Active = GHOST_kTabletModeNone;
591         
592         CocoaWindowDelegate *windowDelegate = [[CocoaWindowDelegate alloc] init];
593         [windowDelegate setSystemAndWindowCocoa:systemCocoa windowCocoa:this];
594         [m_window setDelegate:windowDelegate];
595         
596         [m_window setAcceptsMouseMovedEvents:YES];
597         
598         [m_window registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
599                                                                                   NSStringPboardType, NSTIFFPboardType, nil]];
600                                                                                   
601         if (state == GHOST_kWindowStateFullScreen)
602                 setState(GHOST_kWindowStateFullScreen);
603                 
604         [pool drain];
605 }
606
607
608 GHOST_WindowCocoa::~GHOST_WindowCocoa()
609 {
610         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
611
612         if (m_customCursor) {
613                 [m_customCursor release];
614                 m_customCursor = nil;
615         }
616
617         [m_openGLView release];
618         
619         if (m_window) {
620                 [m_window close];
621         }
622         
623         // Check for other blender opened windows and make the frontmost key
624         // Note: for some reason the closed window is still in the list
625         NSArray *windowsList = [NSApp orderedWindows];
626         for (int a = 0; a < [windowsList count]; a++) {
627                 if (m_window != (CocoaWindow *)[windowsList objectAtIndex:a]) {
628                         [[windowsList objectAtIndex:a] makeKeyWindow];
629                         break;
630                 }
631         }
632         m_window = nil;
633
634         [pool drain];
635 }
636
637 #pragma mark accessors
638
639 bool GHOST_WindowCocoa::getValid() const
640 {
641         return (m_window != 0);
642 }
643
644 void* GHOST_WindowCocoa::getOSWindow() const
645 {
646         return (void*)m_window;
647 }
648
649 void GHOST_WindowCocoa::setTitle(const STR_String& title)
650 {
651         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setTitle(): window invalid");
652         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
653
654         NSString *windowTitle = [[NSString alloc] initWithCString:title encoding:NSUTF8StringEncoding];
655         
656         //Set associated file if applicable
657         if (windowTitle && [windowTitle hasPrefix:@"Blender"]) {
658                 NSRange fileStrRange;
659                 NSString *associatedFileName;
660                 int len;
661                 
662                 fileStrRange.location = [windowTitle rangeOfString:@"["].location+1;
663                 len = [windowTitle rangeOfString:@"]"].location - fileStrRange.location;
664         
665                 if (len > 0) {
666                         fileStrRange.length = len;
667                         associatedFileName = [windowTitle substringWithRange:fileStrRange];
668                         [m_window setTitle:[associatedFileName lastPathComponent]];
669
670                         //Blender used file open/save functions converte file names into legal URL ones
671                         associatedFileName = [associatedFileName stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
672                         @try {
673                                 [m_window setRepresentedFilename:associatedFileName];
674                         }
675                         @catch (NSException * e) {
676                                 printf("\nInvalid file path given in window title");
677                         }
678                 }
679                 else {
680                         [m_window setTitle:windowTitle];
681                         [m_window setRepresentedFilename:@""];
682                 }
683
684         }
685         else {
686                 [m_window setTitle:windowTitle];
687                 [m_window setRepresentedFilename:@""];
688         }
689
690         
691         [windowTitle release];
692         [pool drain];
693 }
694
695
696 void GHOST_WindowCocoa::getTitle(STR_String& title) const
697 {
698         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getTitle(): window invalid");
699
700         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
701
702         NSString *windowTitle = [m_window title];
703
704         if (windowTitle != nil) {
705                 title = [windowTitle UTF8String];
706         }
707         
708         [pool drain];
709 }
710
711
712 void GHOST_WindowCocoa::getWindowBounds(GHOST_Rect& bounds) const
713 {
714         NSRect rect;
715         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getWindowBounds(): window invalid");
716
717         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
718         
719         NSRect screenSize = [[m_window screen] visibleFrame];
720
721         rect = [m_window frame];
722
723         bounds.m_b = screenSize.size.height - (rect.origin.y -screenSize.origin.y);
724         bounds.m_l = rect.origin.x -screenSize.origin.x;
725         bounds.m_r = rect.origin.x-screenSize.origin.x + rect.size.width;
726         bounds.m_t = screenSize.size.height - (rect.origin.y + rect.size.height -screenSize.origin.y);
727         
728         [pool drain];
729 }
730
731
732 void GHOST_WindowCocoa::getClientBounds(GHOST_Rect& bounds) const
733 {
734         NSRect rect;
735         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getClientBounds(): window invalid");
736         
737         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
738         
739         if (!m_fullScreen) {
740                 NSRect screenSize = [[m_window screen] visibleFrame];
741
742                 //Max window contents as screen size (excluding title bar...)
743                 NSRect contentRect = [CocoaWindow contentRectForFrameRect:screenSize
744                                                                                                          styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)];
745
746                 rect = [m_window contentRectForFrameRect:[m_window frame]];
747                 
748                 bounds.m_b = contentRect.size.height - (rect.origin.y -contentRect.origin.y);
749                 bounds.m_l = rect.origin.x -contentRect.origin.x;
750                 bounds.m_r = rect.origin.x-contentRect.origin.x + rect.size.width;
751                 bounds.m_t = contentRect.size.height - (rect.origin.y + rect.size.height -contentRect.origin.y);
752         }
753         else {
754                 NSRect screenSize = [[m_window screen] frame];
755                 
756                 bounds.m_b = screenSize.origin.y + screenSize.size.height;
757                 bounds.m_l = screenSize.origin.x;
758                 bounds.m_r = screenSize.origin.x + screenSize.size.width;
759                 bounds.m_t = screenSize.origin.y;
760         }
761         [pool drain];
762 }
763
764
765 GHOST_TSuccess GHOST_WindowCocoa::setClientWidth(GHOST_TUns32 width)
766 {
767         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientWidth(): window invalid");
768         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
769         GHOST_Rect cBnds, wBnds;
770         getClientBounds(cBnds);
771         if (((GHOST_TUns32)cBnds.getWidth()) != width) {
772                 NSSize size;
773                 size.width=width;
774                 size.height=cBnds.getHeight();
775                 [m_window setContentSize:size];
776         }
777         [pool drain];
778         return GHOST_kSuccess;
779 }
780
781
782 GHOST_TSuccess GHOST_WindowCocoa::setClientHeight(GHOST_TUns32 height)
783 {
784         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientHeight(): window invalid");
785         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
786         GHOST_Rect cBnds, wBnds;
787         getClientBounds(cBnds);
788         if (((GHOST_TUns32)cBnds.getHeight()) != height) {
789                 NSSize size;
790                 size.width=cBnds.getWidth();
791                 size.height=height;
792                 [m_window setContentSize:size];
793         }
794         [pool drain];
795         return GHOST_kSuccess;
796 }
797
798
799 GHOST_TSuccess GHOST_WindowCocoa::setClientSize(GHOST_TUns32 width, GHOST_TUns32 height)
800 {
801         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientSize(): window invalid");
802         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
803         GHOST_Rect cBnds, wBnds;
804         getClientBounds(cBnds);
805         if ((((GHOST_TUns32)cBnds.getWidth())  != width) ||
806             (((GHOST_TUns32)cBnds.getHeight()) != height))
807         {
808                 NSSize size;
809                 size.width=width;
810                 size.height=height;
811                 [m_window setContentSize:size];
812         }
813         [pool drain];
814         return GHOST_kSuccess;
815 }
816
817
818 GHOST_TWindowState GHOST_WindowCocoa::getState() const
819 {
820         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getState(): window invalid");
821         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
822         GHOST_TWindowState state;
823         if (m_fullScreen) {
824                 state = GHOST_kWindowStateFullScreen;
825         } 
826         else if ([m_window isMiniaturized]) {
827                 state = GHOST_kWindowStateMinimized;
828         }
829         else if ([m_window isZoomed]) {
830                 state = GHOST_kWindowStateMaximized;
831         }
832         else {
833                 state = GHOST_kWindowStateNormal;
834         }
835         [pool drain];
836         return state;
837 }
838
839
840 void GHOST_WindowCocoa::screenToClient(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
841 {
842         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::screenToClient(): window invalid");
843
844         screenToClientIntern(inX, inY, outX, outY);
845
846         /* switch y to match ghost convention */
847         GHOST_Rect cBnds;
848         getClientBounds(cBnds);
849         outY = (cBnds.getHeight() - 1) - outY;
850 }
851
852
853 void GHOST_WindowCocoa::clientToScreen(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
854 {
855         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::clientToScreen(): window invalid");
856
857         /* switch y to match ghost convention */
858         GHOST_Rect cBnds;
859         getClientBounds(cBnds);
860         inY = (cBnds.getHeight() - 1) - inY;
861
862         clientToScreenIntern(inX, inY, outX, outY);
863 }
864
865 void GHOST_WindowCocoa::screenToClientIntern(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
866 {
867         NSPoint screenCoord;
868         NSPoint baseCoord;
869         
870         screenCoord.x = inX;
871         screenCoord.y = inY;
872         
873         baseCoord = [m_window convertScreenToBase:screenCoord];
874         
875         outX = baseCoord.x;
876         outY = baseCoord.y;
877 }
878
879 void GHOST_WindowCocoa::clientToScreenIntern(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
880 {
881         NSPoint screenCoord;
882         NSPoint baseCoord;
883         
884         baseCoord.x = inX;
885         baseCoord.y = inY;
886         
887         screenCoord = [m_window convertBaseToScreen:baseCoord];
888         
889         outX = screenCoord.x;
890         outY = screenCoord.y;
891 }
892
893
894 NSScreen* GHOST_WindowCocoa::getScreen()
895 {
896         return [m_window screen];
897 }
898
899
900 /**
901  * \note Fullscreen switch is not actual fullscreen with display capture.
902  * As this capture removes all OS X window manager features.
903  *
904  * Instead, the menu bar and the dock are hidden, and the window is made borderless and enlarged.
905  * Thus, process switch, exposé, spaces, ... still work in fullscreen mode
906  */
907 GHOST_TSuccess GHOST_WindowCocoa::setState(GHOST_TWindowState state)
908 {
909         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setState(): window invalid");
910         switch (state) {
911                 case GHOST_kWindowStateMinimized:
912                         [m_window miniaturize:nil];
913                         break;
914                 case GHOST_kWindowStateMaximized:
915                         [m_window zoom:nil];
916                         break;
917                 
918                 case GHOST_kWindowStateFullScreen:
919                         if (!m_fullScreen) {
920                                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
921                         
922                                 /* This status change needs to be done before Cocoa call to enter fullscreen mode
923                                  * to give window delegate hint not to forward its deactivation to ghost wm that
924                                  * doesn't know view/window difference. */
925                                 m_fullScreen = true;
926
927 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
928                                 //10.6 provides Cocoa functions to autoshow menu bar, and to change a window style
929                                 //Hide menu & dock if needed
930                                 if ([[m_window screen] isEqual:[[NSScreen screens] objectAtIndex:0]]) {
931                                         [NSApp setPresentationOptions:(NSApplicationPresentationHideDock | NSApplicationPresentationAutoHideMenuBar)];
932                                 }
933                                 //Make window borderless and enlarge it
934                                 [m_window setStyleMask:NSBorderlessWindowMask];
935                                 [m_window setFrame:[[m_window screen] frame] display:YES];
936                                 [m_window makeFirstResponder:m_openGLView];
937 #else
938                                 //With 10.5, we need to create a new window to change its style to borderless
939                                 //Hide menu & dock if needed
940                                 if ([[m_window screen] isEqual:[[NSScreen screens] objectAtIndex:0]]) {
941                                         //Cocoa function in 10.5 does not allow to set the menu bar in auto-show mode [NSMenu setMenuBarVisible:NO];
942                                         //One of the very few 64bit compatible Carbon function
943                                         SetSystemUIMode(kUIModeAllHidden,kUIOptionAutoShowMenuBar);
944                                 }
945                                 //Create a fullscreen borderless window
946                                 CocoaWindow *tmpWindow = [[CocoaWindow alloc]
947                                                                                   initWithContentRect:[[m_window screen] frame]
948                                                                                   styleMask:NSBorderlessWindowMask
949                                                                                   backing:NSBackingStoreBuffered
950                                                                                   defer:YES];
951                                 //Copy current window parameters
952                                 [tmpWindow setTitle:[m_window title]];
953                                 [tmpWindow setRepresentedFilename:[m_window representedFilename]];
954                                 [tmpWindow setAcceptsMouseMovedEvents:YES];
955                                 [tmpWindow setDelegate:[m_window delegate]];
956                                 [tmpWindow setSystemAndWindowCocoa:[m_window systemCocoa] windowCocoa:this];
957                                 [tmpWindow registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
958                                                                     NSStringPboardType, NSTIFFPboardType, nil]];
959                                 
960                                 //Assign the openGL view to the new window
961                                 [tmpWindow setContentView:m_openGLView];
962                                 
963                                 //Show the new window
964                                 [tmpWindow makeKeyAndOrderFront:m_openGLView];
965                                 //Close and release old window
966                                 [m_window setDelegate:nil]; // To avoid the notification of "window closed" event
967                                 [m_window close];
968                                 [m_window release];
969                                 m_window = tmpWindow;
970 #endif
971                         
972                                 //Tell WM of view new size
973                                 m_systemCocoa->handleWindowEvent(GHOST_kEventWindowSize, this);
974                                 
975                                 [pool drain];
976                                 }
977                         break;
978                 case GHOST_kWindowStateNormal:
979                 default:
980                         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
981                         if (m_fullScreen) {
982                                 m_fullScreen = false;
983
984                                 //Exit fullscreen
985 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
986                                 //Show again menu & dock if needed
987                                 if ([[m_window screen] isEqual:[NSScreen mainScreen]]) {
988                                         [NSApp setPresentationOptions:NSApplicationPresentationDefault];
989                                 }
990                                 //Make window normal and resize it
991                                 [m_window setStyleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)];
992                                 [m_window setFrame:[[m_window screen] visibleFrame] display:YES];
993                                 //TODO for 10.6 only : window title is forgotten after the style change
994                                 [m_window makeFirstResponder:m_openGLView];
995 #else
996                                 //With 10.5, we need to create a new window to change its style to borderless
997                                 //Show menu & dock if needed
998                                 if ([[m_window screen] isEqual:[NSScreen mainScreen]]) {
999                                         //Cocoa function in 10.5 does not allow to set the menu bar in auto-show mode [NSMenu setMenuBarVisible:YES];
1000                                         SetSystemUIMode(kUIModeNormal, 0); //One of the very few 64bit compatible Carbon function
1001                                 }
1002                                 //Create a fullscreen borderless window
1003                                 CocoaWindow *tmpWindow = [[CocoaWindow alloc]
1004                                                                                   initWithContentRect:[[m_window screen] frame]
1005                                                                                                         styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)
1006                                                                                                           backing:NSBackingStoreBuffered
1007                                                                                                                 defer:YES];
1008                                 //Copy current window parameters
1009                                 [tmpWindow setTitle:[m_window title]];
1010                                 [tmpWindow setRepresentedFilename:[m_window representedFilename]];
1011                                 [tmpWindow setAcceptsMouseMovedEvents:YES];
1012                                 [tmpWindow setDelegate:[m_window delegate]];
1013                                 [tmpWindow setSystemAndWindowCocoa:[m_window systemCocoa] windowCocoa:this];
1014                                 [tmpWindow registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
1015                                                                                                    NSStringPboardType, NSTIFFPboardType, nil]];
1016                                 //Forbid to resize the window below the blender defined minimum one
1017                                 [tmpWindow setContentMinSize:NSMakeSize(640, 480)];
1018                                 
1019                                 //Assign the openGL view to the new window
1020                                 [tmpWindow setContentView:m_openGLView];
1021                                 
1022                                 //Show the new window
1023                                 [tmpWindow makeKeyAndOrderFront:nil];
1024                                 //Close and release old window
1025                                 [m_window setDelegate:nil]; // To avoid the notification of "window closed" event
1026                                 [m_window close];
1027                                 [m_window release];
1028                                 m_window = tmpWindow;
1029 #endif
1030                         
1031                                 //Tell WM of view new size
1032                                 m_systemCocoa->handleWindowEvent(GHOST_kEventWindowSize, this);
1033                         }
1034                         else if ([m_window isMiniaturized])
1035                                 [m_window deminiaturize:nil];
1036                         else if ([m_window isZoomed])
1037                                 [m_window zoom:nil];
1038                         [pool drain];
1039                         break;
1040         }
1041
1042         return GHOST_kSuccess;
1043 }
1044
1045 GHOST_TSuccess GHOST_WindowCocoa::setModifiedState(bool isUnsavedChanges)
1046 {
1047         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1048         
1049         [m_window setDocumentEdited:isUnsavedChanges];
1050         
1051         [pool drain];
1052         return GHOST_Window::setModifiedState(isUnsavedChanges);
1053 }
1054
1055
1056
1057 GHOST_TSuccess GHOST_WindowCocoa::setOrder(GHOST_TWindowOrder order)
1058 {
1059         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1060         
1061         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setOrder(): window invalid");
1062         if (order == GHOST_kWindowOrderTop) {
1063                 [m_window makeKeyAndOrderFront:nil];
1064         }
1065         else {
1066                 NSArray *windowsList;
1067                 
1068                 [m_window orderBack:nil];
1069                 
1070                 //Check for other blender opened windows and make the frontmost key
1071                 windowsList = [NSApp orderedWindows];
1072                 if ([windowsList count]) {
1073                         [[windowsList objectAtIndex:0] makeKeyAndOrderFront:nil];
1074                 }
1075         }
1076         
1077         [pool drain];
1078         return GHOST_kSuccess;
1079 }
1080
1081 #pragma mark Drawing context
1082
1083 /*#define  WAIT_FOR_VSYNC 1*/
1084
1085 GHOST_TSuccess GHOST_WindowCocoa::swapBuffers()
1086 {
1087         if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
1088                 if (m_openGLContext != nil) {
1089                         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1090                         [m_openGLContext flushBuffer];
1091                         [pool drain];
1092                         return GHOST_kSuccess;
1093                 }
1094         }
1095         return GHOST_kFailure;
1096 }
1097
1098 GHOST_TSuccess GHOST_WindowCocoa::updateDrawingContext()
1099 {
1100         if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
1101                 if (m_openGLContext != nil) {
1102                         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1103                         [m_openGLContext update];
1104                         [pool drain];
1105                         return GHOST_kSuccess;
1106                 }
1107         }
1108         return GHOST_kFailure;
1109 }
1110
1111 GHOST_TSuccess GHOST_WindowCocoa::activateDrawingContext()
1112 {
1113         if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
1114                 if (m_openGLContext != nil) {
1115                         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1116                         [m_openGLContext makeCurrentContext];
1117                         
1118                         // Disable AA by default
1119                         if (m_numOfAASamples > 0) glDisable(GL_MULTISAMPLE_ARB);
1120                         [pool drain];
1121                         return GHOST_kSuccess;
1122                 }
1123         }
1124         return GHOST_kFailure;
1125 }
1126
1127
1128 GHOST_TSuccess GHOST_WindowCocoa::installDrawingContext(GHOST_TDrawingContextType type)
1129 {
1130         GHOST_TSuccess success = GHOST_kFailure;
1131         
1132         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1133         
1134         NSOpenGLPixelFormat *pixelFormat;
1135         NSOpenGLContext *tmpOpenGLContext;
1136         
1137         /***** Multithreaded opengl code : uncomment for enabling
1138         CGLContextObj cglCtx;
1139         */
1140          
1141         switch (type) {
1142                 case GHOST_kDrawingContextTypeOpenGL:
1143                         if (!getValid()) break;
1144
1145                         pixelFormat = [m_openGLView pixelFormat];
1146                         tmpOpenGLContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat
1147                                                                                                                           shareContext:s_firstOpenGLcontext];
1148                         if (tmpOpenGLContext == nil) {
1149                                 success = GHOST_kFailure;
1150                                 break;
1151                         }
1152                         
1153                         //Switch openGL to multhreaded mode
1154                         /******* Multithreaded opengl code : uncomment for enabling
1155                         cglCtx = (CGLContextObj)[tmpOpenGLContext CGLContextObj];
1156                         if (CGLEnable(cglCtx, kCGLCEMPEngine) == kCGLNoError)
1157                                 printf("\nSwitched openGL to multithreaded mode\n");
1158                          */
1159                         
1160                         if (!s_firstOpenGLcontext) s_firstOpenGLcontext = tmpOpenGLContext;
1161 #ifdef WAIT_FOR_VSYNC
1162                         {
1163                                 GLint swapInt = 1;
1164                                 /* wait for vsync, to avoid tearing artifacts */
1165                                 [tmpOpenGLContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
1166                         }
1167 #endif
1168                         [m_openGLView setOpenGLContext:tmpOpenGLContext];
1169                         [tmpOpenGLContext setView:m_openGLView];
1170                         
1171                         m_openGLContext = tmpOpenGLContext;
1172                         break;
1173         
1174                 case GHOST_kDrawingContextTypeNone:
1175                         success = GHOST_kSuccess;
1176                         break;
1177                 
1178                 default:
1179                         break;
1180         }
1181         [pool drain];
1182         return success;
1183 }
1184
1185
1186 GHOST_TSuccess GHOST_WindowCocoa::removeDrawingContext()
1187 {
1188         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1189         switch (m_drawingContextType) {
1190                 case GHOST_kDrawingContextTypeOpenGL:
1191                         if (m_openGLContext) {
1192                                 [m_openGLView clearGLContext];
1193                                 if (s_firstOpenGLcontext == m_openGLContext) s_firstOpenGLcontext = nil;
1194                                 m_openGLContext = nil;
1195                         }
1196                         [pool drain];
1197                         return GHOST_kSuccess;
1198                 case GHOST_kDrawingContextTypeNone:
1199                         [pool drain];
1200                         return GHOST_kSuccess;
1201                         break;
1202                 default:
1203                         [pool drain];
1204                         return GHOST_kFailure;
1205         }
1206 }
1207
1208
1209 GHOST_TSuccess GHOST_WindowCocoa::invalidate()
1210 {
1211         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::invalidate(): window invalid");
1212         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1213         [m_openGLView setNeedsDisplay:YES];
1214         [pool drain];
1215         return GHOST_kSuccess;
1216 }
1217
1218 #pragma mark Progress bar
1219
1220 GHOST_TSuccess GHOST_WindowCocoa::setProgressBar(float progress)
1221 {
1222         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1223         
1224         if ((progress >=0.0) && (progress <=1.0)) {
1225                 NSImage* dockIcon = [[NSImage alloc] initWithSize:NSMakeSize(128,128)];
1226                 
1227                 [dockIcon lockFocus];
1228                 NSRect progressBox = {{4, 4}, {120, 16}};
1229
1230                 [[NSImage imageNamed:@"NSApplicationIcon"] drawAtPoint:NSZeroPoint fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
1231
1232                 // Track & Outline
1233                 [[NSColor blackColor] setFill];
1234                 NSRectFill(progressBox);
1235
1236                 [[NSColor whiteColor] set];
1237                 NSFrameRect(progressBox);
1238
1239                 // Progress fill
1240                 progressBox = NSInsetRect(progressBox, 1, 1);
1241
1242                 progressBox.size.width = progressBox.size.width * progress;
1243                 NSGradient *gradient = [[NSGradient alloc] initWithStartingColor:[NSColor darkGrayColor] endingColor:[NSColor lightGrayColor]];
1244                 [gradient drawInRect:progressBox angle:90];
1245                 [gradient release];
1246                 
1247                 [dockIcon unlockFocus];
1248                 
1249                 [NSApp setApplicationIconImage:dockIcon];
1250                 [dockIcon release];
1251                 
1252                 m_progressBarVisible = true;
1253         }
1254         
1255         [pool drain];
1256         return GHOST_kSuccess;
1257 }
1258
1259
1260 GHOST_TSuccess GHOST_WindowCocoa::endProgressBar()
1261 {
1262         if (!m_progressBarVisible) return GHOST_kFailure;
1263         m_progressBarVisible = false;
1264         
1265         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1266         
1267         NSImage* dockIcon = [[NSImage alloc] initWithSize:NSMakeSize(128,128)];
1268         [dockIcon lockFocus];
1269         [[NSImage imageNamed:@"NSApplicationIcon"] drawAtPoint:NSZeroPoint fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
1270         [dockIcon unlockFocus];
1271         [NSApp setApplicationIconImage:dockIcon];
1272         [dockIcon release];
1273         
1274         [pool drain];
1275         return GHOST_kSuccess;
1276 }
1277
1278
1279
1280 #pragma mark Cursor handling
1281
1282 void GHOST_WindowCocoa::loadCursor(bool visible, GHOST_TStandardCursor cursor) const
1283 {
1284         static bool systemCursorVisible = true;
1285         
1286         NSCursor *tmpCursor =nil;
1287         
1288         if (visible != systemCursorVisible) {
1289                 if (visible) {
1290                         [NSCursor unhide];
1291                         systemCursorVisible = true;
1292                 }
1293                 else {
1294                         [NSCursor hide];
1295                         systemCursorVisible = false;
1296                 }
1297         }
1298
1299         if (cursor == GHOST_kStandardCursorCustom && m_customCursor) {
1300                 tmpCursor = m_customCursor;
1301         }
1302         else {
1303                 switch (cursor) {
1304                         case GHOST_kStandardCursorDestroy:
1305                                 tmpCursor = [NSCursor disappearingItemCursor];
1306                                 break;
1307                         case GHOST_kStandardCursorText:
1308                                 tmpCursor = [NSCursor IBeamCursor];
1309                                 break;
1310                         case GHOST_kStandardCursorCrosshair:
1311                                 tmpCursor = [NSCursor crosshairCursor];
1312                                 break;
1313                         case GHOST_kStandardCursorUpDown:
1314                                 tmpCursor = [NSCursor resizeUpDownCursor];
1315                                 break;
1316                         case GHOST_kStandardCursorLeftRight:
1317                                 tmpCursor = [NSCursor resizeLeftRightCursor];
1318                                 break;
1319                         case GHOST_kStandardCursorTopSide:
1320                                 tmpCursor = [NSCursor resizeUpCursor];
1321                                 break;
1322                         case GHOST_kStandardCursorBottomSide:
1323                                 tmpCursor = [NSCursor resizeDownCursor];
1324                                 break;
1325                         case GHOST_kStandardCursorLeftSide:
1326                                 tmpCursor = [NSCursor resizeLeftCursor];
1327                                 break;
1328                         case GHOST_kStandardCursorRightSide:
1329                                 tmpCursor = [NSCursor resizeRightCursor];
1330                                 break;
1331                         case GHOST_kStandardCursorRightArrow:
1332                         case GHOST_kStandardCursorInfo:
1333                         case GHOST_kStandardCursorLeftArrow:
1334                         case GHOST_kStandardCursorHelp:
1335                         case GHOST_kStandardCursorCycle:
1336                         case GHOST_kStandardCursorSpray:
1337                         case GHOST_kStandardCursorWait:
1338                         case GHOST_kStandardCursorTopLeftCorner:
1339                         case GHOST_kStandardCursorTopRightCorner:
1340                         case GHOST_kStandardCursorBottomRightCorner:
1341                         case GHOST_kStandardCursorBottomLeftCorner:
1342                         case GHOST_kStandardCursorCopy:
1343                         case GHOST_kStandardCursorDefault:
1344                         default:
1345                                 tmpCursor = [NSCursor arrowCursor];
1346                                 break;
1347                 };
1348         }
1349         [tmpCursor set];
1350 }
1351
1352
1353
1354 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorVisibility(bool visible)
1355 {
1356         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
1357         
1358         if ([m_window isVisible]) {
1359                 loadCursor(visible, getCursorShape());
1360         }
1361         
1362         [pool drain];
1363         return GHOST_kSuccess;
1364 }
1365
1366
1367 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorGrab(GHOST_TGrabCursorMode mode)
1368 {
1369         GHOST_TSuccess err = GHOST_kSuccess;
1370         
1371         if (mode != GHOST_kGrabDisable) {
1372                 //No need to perform grab without warp as it is always on in OS X
1373                 if (mode != GHOST_kGrabNormal) {
1374                         GHOST_TInt32 x_old,y_old;
1375                         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1376
1377                         m_systemCocoa->getCursorPosition(x_old,y_old);
1378                         screenToClientIntern(x_old, y_old, m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]);
1379                         //Warp position is stored in client (window base) coordinates
1380                         setCursorGrabAccum(0, 0);
1381                         
1382                         if (mode == GHOST_kGrabHide) {
1383                                 setWindowCursorVisibility(false);
1384                         }
1385                         
1386                         //Make window key if it wasn't to get the mouse move events
1387                         [m_window makeKeyWindow];
1388                         
1389                         //Dissociate cursor position even for warp mode, to allow mouse acceleration to work even when warping the cursor
1390                         err = CGAssociateMouseAndMouseCursorPosition(false) == kCGErrorSuccess ? GHOST_kSuccess : GHOST_kFailure;
1391                         
1392                         [pool drain];
1393                 }
1394         }
1395         else {
1396                 if (m_cursorGrab==GHOST_kGrabHide) {
1397                         //No need to set again cursor position, as it has not changed for Cocoa
1398                         setWindowCursorVisibility(true);
1399                 }
1400                 
1401                 err = CGAssociateMouseAndMouseCursorPosition(true) == kCGErrorSuccess ? GHOST_kSuccess : GHOST_kFailure;
1402                 /* Almost works without but important otherwise the mouse GHOST location can be incorrect on exit */
1403                 setCursorGrabAccum(0, 0);
1404                 m_cursorGrabBounds.m_l= m_cursorGrabBounds.m_r= -1; /* disable */
1405         }
1406         return err;
1407 }
1408         
1409 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorShape(GHOST_TStandardCursor shape)
1410 {
1411         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1412
1413         if (m_customCursor) {
1414                 [m_customCursor release];
1415                 m_customCursor = nil;
1416         }
1417
1418         if ([m_window isVisible]) {
1419                 loadCursor(getCursorVisibility(), shape);
1420         }
1421         
1422         [pool drain];
1423         return GHOST_kSuccess;
1424 }
1425
1426 /** Reverse the bits in a GHOST_TUns8
1427 static GHOST_TUns8 uns8ReverseBits(GHOST_TUns8 ch)
1428 {
1429         ch= ((ch >> 1) & 0x55) | ((ch << 1) & 0xAA);
1430         ch= ((ch >> 2) & 0x33) | ((ch << 2) & 0xCC);
1431         ch= ((ch >> 4) & 0x0F) | ((ch << 4) & 0xF0);
1432         return ch;
1433 }
1434 */
1435
1436
1437 /** Reverse the bits in a GHOST_TUns16 */
1438 static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
1439 {
1440         shrt = ((shrt >> 1) & 0x5555) | ((shrt << 1) & 0xAAAA);
1441         shrt = ((shrt >> 2) & 0x3333) | ((shrt << 2) & 0xCCCC);
1442         shrt = ((shrt >> 4) & 0x0F0F) | ((shrt << 4) & 0xF0F0);
1443         shrt = ((shrt >> 8) & 0x00FF) | ((shrt << 8) & 0xFF00);
1444         return shrt;
1445 }
1446
1447 GHOST_TSuccess GHOST_WindowCocoa::setWindowCustomCursorShape(GHOST_TUns8 *bitmap, GHOST_TUns8 *mask,
1448                                                              int sizex, int sizey, int hotX, int hotY, int fg_color, int bg_color)
1449 {
1450         int y,nbUns16;
1451         NSPoint hotSpotPoint;
1452         NSBitmapImageRep *cursorImageRep;
1453         NSImage *cursorImage;
1454         NSSize imSize;
1455         GHOST_TUns16 *cursorBitmap;
1456         
1457         
1458         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1459         
1460         if (m_customCursor) {
1461                 [m_customCursor release];
1462                 m_customCursor = nil;
1463         }
1464         
1465
1466         cursorImageRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil
1467                                                                                                                          pixelsWide:sizex
1468                                                                                                                          pixelsHigh:sizey
1469                                                                                                                   bitsPerSample:1 
1470                                                                                                                 samplesPerPixel:2
1471                                                                                                                            hasAlpha:YES
1472                                                                                                                            isPlanar:YES
1473                                                                                                                  colorSpaceName:NSDeviceWhiteColorSpace
1474                                                                                                                         bytesPerRow:(sizex/8 + (sizex%8 >0 ?1:0))
1475                                                                                                                    bitsPerPixel:1];
1476         
1477         
1478         cursorBitmap = (GHOST_TUns16*)[cursorImageRep bitmapData];
1479         nbUns16 = [cursorImageRep bytesPerPlane]/2;
1480         
1481         for (y=0; y<nbUns16; y++) {
1482 #if !defined(__LITTLE_ENDIAN__)
1483                 cursorBitmap[y] = ~uns16ReverseBits((bitmap[2*y]<<0) | (bitmap[2*y+1]<<8));
1484                 cursorBitmap[nbUns16+y] = uns16ReverseBits((mask[2*y]<<0) | (mask[2*y+1]<<8));
1485 #else
1486                 cursorBitmap[y] = ~uns16ReverseBits((bitmap[2*y+1]<<0) | (bitmap[2*y]<<8));
1487                 cursorBitmap[nbUns16+y] = uns16ReverseBits((mask[2*y+1]<<0) | (mask[2*y]<<8));
1488 #endif
1489                 
1490         }
1491         
1492         
1493         imSize.width = sizex;
1494         imSize.height= sizey;
1495         cursorImage = [[NSImage alloc] initWithSize:imSize];
1496         [cursorImage addRepresentation:cursorImageRep];
1497         
1498         hotSpotPoint.x = hotX;
1499         hotSpotPoint.y = hotY;
1500         
1501         //foreground and background color parameter is not handled for now (10.6)
1502         m_customCursor = [[NSCursor alloc] initWithImage:cursorImage
1503                                                                                          hotSpot:hotSpotPoint];
1504         
1505         [cursorImageRep release];
1506         [cursorImage release];
1507         
1508         if ([m_window isVisible]) {
1509                 loadCursor(getCursorVisibility(), GHOST_kStandardCursorCustom);
1510         }
1511         [pool drain];
1512         return GHOST_kSuccess;
1513 }
1514
1515 GHOST_TSuccess GHOST_WindowCocoa::setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2], 
1516                                                              GHOST_TUns8 mask[16][2], int hotX, int hotY)
1517 {
1518         return setWindowCustomCursorShape((GHOST_TUns8*)bitmap, (GHOST_TUns8*) mask, 16, 16, hotX, hotY, 0, 1);
1519 }