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