Drag & drop implementation at GHOST level (only OSX for now)
[blender-staging.git] / intern / ghost / intern / GHOST_WindowCocoa.mm
1 /**
2  * $Id$
3  * ***** BEGIN GPL LICENSE BLOCK *****
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software Foundation,
17  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18  *
19  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
20  * All rights reserved.
21  *
22  * The Original Code is: all of this file.
23  *
24  * Contributor(s):      Maarten Gribnau 05/2001
25                                         Damien Plisson 10/2009
26  *
27  * ***** END GPL LICENSE BLOCK *****
28  */
29
30 #include <Cocoa/Cocoa.h>
31
32 #ifndef MAC_OS_X_VERSION_10_6
33 //Use of the SetSystemUIMode function (64bit compatible)
34 #include <Carbon/Carbon.h>
35 #endif
36
37 /***** Multithreaded opengl code : uncomment for enabling
38 #include <OpenGL/OpenGL.h>
39 */
40  
41 #include "GHOST_WindowCocoa.h"
42 #include "GHOST_SystemCocoa.h"
43 #include "GHOST_Debug.h"
44
45
46 #pragma mark Cocoa window delegate object
47 /* live resize ugly patch
48 extern "C" {
49         struct bContext;
50         typedef struct bContext bContext;
51         bContext* ghostC;
52         extern int wm_window_timer(const bContext *C);
53         extern void wm_window_process_events(const bContext *C);
54         extern void wm_event_do_handlers(bContext *C);
55         extern void wm_event_do_notifiers(bContext *C);
56         extern void wm_draw_update(bContext *C);
57 };*/
58 @interface CocoaWindowDelegate : NSObject
59 #ifdef MAC_OS_X_VERSION_10_6
60 <NSWindowDelegate>
61 #endif
62 {
63         GHOST_SystemCocoa *systemCocoa;
64         GHOST_WindowCocoa *associatedWindow;
65 }
66
67 - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa;
68 - (void)windowWillClose:(NSNotification *)notification;
69 - (void)windowDidBecomeKey:(NSNotification *)notification;
70 - (void)windowDidResignKey:(NSNotification *)notification;
71 - (void)windowDidExpose:(NSNotification *)notification;
72 - (void)windowDidResize:(NSNotification *)notification;
73 @end
74
75 @implementation CocoaWindowDelegate : NSObject
76 - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa
77 {
78         systemCocoa = sysCocoa;
79         associatedWindow = winCocoa;
80 }
81
82 - (void)windowWillClose:(NSNotification *)notification
83 {
84         systemCocoa->handleWindowEvent(GHOST_kEventWindowClose, associatedWindow);
85 }
86
87 - (void)windowDidBecomeKey:(NSNotification *)notification
88 {
89         systemCocoa->handleWindowEvent(GHOST_kEventWindowActivate, associatedWindow);
90 }
91
92 - (void)windowDidResignKey:(NSNotification *)notification
93 {
94         systemCocoa->handleWindowEvent(GHOST_kEventWindowDeactivate, associatedWindow);
95 }
96
97 - (void)windowDidExpose:(NSNotification *)notification
98 {
99         systemCocoa->handleWindowEvent(GHOST_kEventWindowUpdate, associatedWindow);
100 }
101
102 - (void)windowDidResize:(NSNotification *)notification
103 {
104 #ifdef MAC_OS_X_VERSION_10_6
105         //if (![[notification object] inLiveResize]) {
106                 //Send event only once, at end of resize operation (when user has released mouse button)
107 #endif
108                 systemCocoa->handleWindowEvent(GHOST_kEventWindowSize, associatedWindow);
109 #ifdef MAC_OS_X_VERSION_10_6
110         //}
111 #endif
112         /* Live resize ugly patch. Needed because live resize runs in a modal loop, not letting main loop run
113          if ([[notification object] inLiveResize]) {
114                 systemCocoa->dispatchEvents();
115                 wm_window_timer(ghostC);
116                 wm_event_do_handlers(ghostC);
117                 wm_event_do_notifiers(ghostC);
118                 wm_draw_update(ghostC);
119         }*/
120 }
121 @end
122
123 #pragma mark NSWindow subclass
124 //We need to subclass it to tell that even borderless (fullscreen), it can become key (receive user events)
125 @interface CocoaWindow: NSWindow
126 {
127         GHOST_SystemCocoa *systemCocoa;
128         GHOST_WindowCocoa *associatedWindow;
129         GHOST_TDragnDropTypes m_draggedObjectType;
130 }
131 - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa;
132 @end
133 @implementation CocoaWindow
134 - (void)setSystemAndWindowCocoa:(GHOST_SystemCocoa *)sysCocoa windowCocoa:(GHOST_WindowCocoa *)winCocoa
135 {
136         systemCocoa = sysCocoa;
137         associatedWindow = winCocoa;
138 }
139
140 -(BOOL)canBecomeKeyWindow
141 {
142         return YES;
143 }
144
145 //The drag'n'drop dragging destination methods
146 - (NSDragOperation)draggingEntered:(id < NSDraggingInfo >)sender
147 {
148         NSPoint mouseLocation = [sender draggingLocation];
149         NSPasteboard *draggingPBoard = [sender draggingPasteboard];
150         
151         if ([[draggingPBoard types] containsObject:NSTIFFPboardType]) m_draggedObjectType = GHOST_kDragnDropTypeBitmap;
152         else if ([[draggingPBoard types] containsObject:NSFilenamesPboardType]) m_draggedObjectType = GHOST_kDragnDropTypeFilenames;
153         else if ([[draggingPBoard types] containsObject:NSStringPboardType]) m_draggedObjectType = GHOST_kDragnDropTypeString;
154         else return NSDragOperationNone;
155         
156         systemCocoa->handleDraggingEvent(GHOST_kEventDraggingEntered, m_draggedObjectType, associatedWindow, mouseLocation.x, mouseLocation.y, nil);
157         return NSDragOperationCopy;
158 }
159
160
161 - (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender
162 {
163         NSPoint mouseLocation = [sender draggingLocation];
164         
165         systemCocoa->handleDraggingEvent(GHOST_kEventDraggingUpdated, m_draggedObjectType, associatedWindow, mouseLocation.x, mouseLocation.y, nil);
166         return NSDragOperationCopy;
167 }
168
169 - (void)draggingExited:(id < NSDraggingInfo >)sender
170 {
171         systemCocoa->handleDraggingEvent(GHOST_kEventDraggingExited, m_draggedObjectType, associatedWindow, 0, 0, nil);
172         m_draggedObjectType = GHOST_kDragnDropTypeUnknown;
173 }
174
175 - (BOOL)prepareForDragOperation:(id < NSDraggingInfo >)sender
176 {
177         if (systemCocoa->canAcceptDragOperation())
178                 return YES;
179         else
180                 return NO;
181 }
182
183 - (BOOL)performDragOperation:(id < NSDraggingInfo >)sender
184 {
185         NSPoint mouseLocation = [sender draggingLocation];
186         NSPasteboard *draggingPBoard = [sender draggingPasteboard];
187         id data;
188         
189         switch (m_draggedObjectType) {
190                 case GHOST_kDragnDropTypeBitmap:
191                         data = [draggingPBoard dataForType:NSTIFFPboardType];
192                         break;
193                 case GHOST_kDragnDropTypeFilenames:
194                         data = [draggingPBoard propertyListForType:NSFilenamesPboardType];
195                         break;
196                 case GHOST_kDragnDropTypeString:
197                         data = [draggingPBoard stringForType:@"public.utf8-plain-text"];
198                         break;
199                 default:
200                         return NO;
201                         break;
202         }
203         systemCocoa->handleDraggingEvent(GHOST_kEventDraggingDropDone, m_draggedObjectType, associatedWindow, mouseLocation.x, mouseLocation.y, (void*)data);
204         return YES;
205 }
206
207 @end
208
209
210
211 #pragma mark NSOpenGLView subclass
212 //We need to subclass it in order to give Cocoa the feeling key events are trapped
213 @interface CocoaOpenGLView : NSOpenGLView
214 {
215 }
216 @end
217 @implementation CocoaOpenGLView
218
219 - (BOOL)acceptsFirstResponder
220 {
221     return YES;
222 }
223
224 //The trick to prevent Cocoa from complaining (beeping)
225 - (void)keyDown:(NSEvent *)theEvent
226 {}
227
228 - (BOOL)isOpaque
229 {
230     return YES;
231 }
232
233 @end
234
235
236 #pragma mark initialization / finalization
237
238 NSOpenGLContext* GHOST_WindowCocoa::s_firstOpenGLcontext = nil;
239
240 GHOST_WindowCocoa::GHOST_WindowCocoa(
241         GHOST_SystemCocoa *systemCocoa,
242         const STR_String& title,
243         GHOST_TInt32 left,
244         GHOST_TInt32 top,
245         GHOST_TUns32 width,
246         GHOST_TUns32 height,
247         GHOST_TWindowState state,
248         GHOST_TDrawingContextType type,
249         const bool stereoVisual
250 ) :
251         GHOST_Window(title, left, top, width, height, state, GHOST_kDrawingContextTypeNone, stereoVisual),
252         m_customCursor(0)
253 {
254         NSOpenGLPixelFormatAttribute pixelFormatAttrsWindow[40];
255         int i;
256         
257         m_systemCocoa = systemCocoa;
258         m_fullScreen = false;
259         
260         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
261         
262
263         //Creates the window
264         NSRect rect;
265         NSSize  minSize;
266         
267         rect.origin.x = left;
268         rect.origin.y = top;
269         rect.size.width = width;
270         rect.size.height = height;
271         
272         m_window = [[CocoaWindow alloc] initWithContentRect:rect
273                                                                                    styleMask:NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask | NSMiniaturizableWindowMask
274                                                                                          backing:NSBackingStoreBuffered defer:NO];
275         if (m_window == nil) {
276                 [pool drain];
277                 return;
278         }
279         
280         [m_window setSystemAndWindowCocoa:systemCocoa windowCocoa:this];
281         
282         //Forbid to resize the window below the blender defined minimum one
283         minSize.width = 320;
284         minSize.height = 240;
285         [m_window setContentMinSize:minSize];
286         
287         setTitle(title);
288         
289         
290         // Pixel Format Attributes for the windowed NSOpenGLContext
291         i=0;
292         pixelFormatAttrsWindow[i++] = NSOpenGLPFADoubleBuffer;
293         pixelFormatAttrsWindow[i++] = NSOpenGLPFAAccelerated;
294         //pixelFormatAttrsWindow[i++] = NSOpenGLPFAAllowOfflineRenderers,;   // Removed to allow 10.4 builds, and 2 GPUs rendering is not used anyway
295         
296         pixelFormatAttrsWindow[i++] = NSOpenGLPFADepthSize;
297         pixelFormatAttrsWindow[i++] = (NSOpenGLPixelFormatAttribute) 32;
298         
299         if (stereoVisual) pixelFormatAttrsWindow[i++] = NSOpenGLPFAStereo;
300         
301         pixelFormatAttrsWindow[i] = (NSOpenGLPixelFormatAttribute) 0;
302         
303
304         //Creates the OpenGL View inside the window
305         NSOpenGLPixelFormat *pixelFormat =
306         [[NSOpenGLPixelFormat alloc] initWithAttributes:pixelFormatAttrsWindow];
307         
308         m_openGLView = [[CocoaOpenGLView alloc] initWithFrame:rect
309                                                                                                  pixelFormat:pixelFormat];
310         
311         [pixelFormat release];
312         
313         m_openGLContext = [m_openGLView openGLContext]; //This context will be replaced by the proper one just after
314         
315         [m_window setContentView:m_openGLView];
316         [m_window setInitialFirstResponder:m_openGLView];
317         
318         [m_window setReleasedWhenClosed:NO]; //To avoid bad pointer exception in case of user closing the window
319         
320         [m_window makeKeyAndOrderFront:nil];
321         
322         setDrawingContextType(type);
323         updateDrawingContext();
324         activateDrawingContext();
325         
326         m_tablet.Active = GHOST_kTabletModeNone;
327         
328         CocoaWindowDelegate *windowDelegate = [[CocoaWindowDelegate alloc] init];
329         [windowDelegate setSystemAndWindowCocoa:systemCocoa windowCocoa:this];
330         [m_window setDelegate:windowDelegate];
331         
332         [m_window setAcceptsMouseMovedEvents:YES];
333         
334         [m_window registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
335                                                                                   NSStringPboardType, NSTIFFPboardType, nil]];
336                                                                                   
337         if (state == GHOST_kWindowStateFullScreen)
338                 setState(GHOST_kWindowStateFullScreen);
339                 
340         [pool drain];
341 }
342
343
344 GHOST_WindowCocoa::~GHOST_WindowCocoa()
345 {
346         if (m_customCursor) delete m_customCursor;
347
348     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
349         [m_openGLView release];
350         
351         if (m_window) {
352                 [m_window close];
353                 [[m_window delegate] release];
354                 [m_window release];
355                 m_window = nil;
356         }
357         
358         //Check for other blender opened windows and make the frontmost key
359         NSArray *windowsList = [NSApp orderedWindows];
360         if ([windowsList count]) {
361                 [[windowsList objectAtIndex:0] makeKeyAndOrderFront:nil];
362         }
363         [pool drain];
364 }
365
366 #pragma mark accessors
367
368 bool GHOST_WindowCocoa::getValid() const
369 {
370     bool valid;
371     if (!m_fullScreen) {
372         valid = (m_window != 0); //&& ::IsValidWindowPtr(m_windowRef);
373     }
374     else {
375         valid = true;
376     }
377     return valid;
378 }
379
380
381 void GHOST_WindowCocoa::setTitle(const STR_String& title)
382 {
383     GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setTitle(): window invalid")
384         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
385
386         NSString *windowTitle = [[NSString alloc] initWithUTF8String:title];
387         
388         //Set associated file if applicable
389         if ([windowTitle hasPrefix:@"Blender"])
390         {
391                 NSRange fileStrRange;
392                 NSString *associatedFileName;
393                 int len;
394                 
395                 fileStrRange.location = [windowTitle rangeOfString:@"["].location+1;
396                 len = [windowTitle rangeOfString:@"]"].location - fileStrRange.location;
397         
398                 if (len >0)
399                 {
400                         fileStrRange.length = len;
401                         associatedFileName = [windowTitle substringWithRange:fileStrRange];
402                         @try {
403                                 [m_window setRepresentedFilename:associatedFileName];
404                         }
405                         @catch (NSException * e) {
406                                 printf("\nInvalid file path given in window title");
407                         }
408                         [m_window setTitle:[associatedFileName lastPathComponent]];
409                 }
410                 else {
411                         [m_window setTitle:windowTitle];
412                         [m_window setRepresentedFilename:@""];
413                 }
414
415         } else {
416                 [m_window setTitle:windowTitle];
417                 [m_window setRepresentedFilename:@""];
418         }
419
420         
421         [windowTitle release];
422         [pool drain];
423 }
424
425
426 void GHOST_WindowCocoa::getTitle(STR_String& title) const
427 {
428     GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getTitle(): window invalid")
429
430         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
431
432         NSString *windowTitle = [m_window title];
433
434         if (windowTitle != nil) {
435                 title = [windowTitle UTF8String];               
436         }
437         
438         [pool drain];
439 }
440
441
442 void GHOST_WindowCocoa::getWindowBounds(GHOST_Rect& bounds) const
443 {
444         NSRect rect;
445         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getWindowBounds(): window invalid")
446
447         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
448         
449         NSRect screenSize = [[m_window screen] visibleFrame];
450
451         rect = [m_window frame];
452
453         bounds.m_b = screenSize.size.height - (rect.origin.y -screenSize.origin.y);
454         bounds.m_l = rect.origin.x -screenSize.origin.x;
455         bounds.m_r = rect.origin.x-screenSize.origin.x + rect.size.width;
456         bounds.m_t = screenSize.size.height - (rect.origin.y + rect.size.height -screenSize.origin.y);
457         
458         [pool drain];
459 }
460
461
462 void GHOST_WindowCocoa::getClientBounds(GHOST_Rect& bounds) const
463 {
464         NSRect rect;
465         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getClientBounds(): window invalid")
466         
467         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
468         
469         if (!m_fullScreen)
470         {
471                 NSRect screenSize = [[m_window screen] visibleFrame];
472
473                 //Max window contents as screen size (excluding title bar...)
474                 NSRect contentRect = [CocoaWindow contentRectForFrameRect:screenSize
475                                                                                                          styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)];
476
477                 rect = [m_window contentRectForFrameRect:[m_window frame]];
478                 
479                 bounds.m_b = contentRect.size.height - (rect.origin.y -contentRect.origin.y);
480                 bounds.m_l = rect.origin.x -contentRect.origin.x;
481                 bounds.m_r = rect.origin.x-contentRect.origin.x + rect.size.width;
482                 bounds.m_t = contentRect.size.height - (rect.origin.y + rect.size.height -contentRect.origin.y);
483         }
484         else {
485                 NSRect screenSize = [[m_window screen] frame];
486                 
487                 bounds.m_b = screenSize.origin.y + screenSize.size.height;
488                 bounds.m_l = screenSize.origin.x;
489                 bounds.m_r = screenSize.origin.x + screenSize.size.width;
490                 bounds.m_t = screenSize.origin.y;
491         }
492         [pool drain];
493 }
494
495
496 GHOST_TSuccess GHOST_WindowCocoa::setClientWidth(GHOST_TUns32 width)
497 {
498         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientWidth(): window invalid")
499         GHOST_Rect cBnds, wBnds;
500         getClientBounds(cBnds);
501         if (((GHOST_TUns32)cBnds.getWidth()) != width) {
502                 NSSize size;
503                 size.width=width;
504                 size.height=cBnds.getHeight();
505                 [m_window setContentSize:size];
506         }
507         return GHOST_kSuccess;
508 }
509
510
511 GHOST_TSuccess GHOST_WindowCocoa::setClientHeight(GHOST_TUns32 height)
512 {
513         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientHeight(): window invalid")
514         GHOST_Rect cBnds, wBnds;
515         getClientBounds(cBnds);
516         if (((GHOST_TUns32)cBnds.getHeight()) != height) {
517                 NSSize size;
518                 size.width=cBnds.getWidth();
519                 size.height=height;
520                 [m_window setContentSize:size];
521         }
522         return GHOST_kSuccess;
523 }
524
525
526 GHOST_TSuccess GHOST_WindowCocoa::setClientSize(GHOST_TUns32 width, GHOST_TUns32 height)
527 {
528         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setClientSize(): window invalid")
529         GHOST_Rect cBnds, wBnds;
530         getClientBounds(cBnds);
531         if ((((GHOST_TUns32)cBnds.getWidth()) != width) ||
532             (((GHOST_TUns32)cBnds.getHeight()) != height)) {
533                 NSSize size;
534                 size.width=width;
535                 size.height=height;
536                 [m_window setContentSize:size];
537         }
538         return GHOST_kSuccess;
539 }
540
541
542 GHOST_TWindowState GHOST_WindowCocoa::getState() const
543 {
544         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getState(): window invalid")
545         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
546         GHOST_TWindowState state;
547         if (m_fullScreen) {
548                 state = GHOST_kWindowStateFullScreen;
549         } 
550         else if ([m_window isMiniaturized]) {
551                 state = GHOST_kWindowStateMinimized;
552         }
553         else if ([m_window isZoomed]) {
554                 state = GHOST_kWindowStateMaximized;
555         }
556         else {
557                 state = GHOST_kWindowStateNormal;
558         }
559         [pool drain];
560         return state;
561 }
562
563
564 void GHOST_WindowCocoa::screenToClient(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
565 {
566         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::screenToClient(): window invalid")
567         
568         NSPoint screenCoord;
569         NSPoint baseCoord;
570         
571         screenCoord.x = inX;
572         screenCoord.y = inY;
573         
574         baseCoord = [m_window convertScreenToBase:screenCoord];
575         
576         outX = baseCoord.x;
577         outY = baseCoord.y;
578 }
579
580
581 void GHOST_WindowCocoa::clientToScreen(GHOST_TInt32 inX, GHOST_TInt32 inY, GHOST_TInt32& outX, GHOST_TInt32& outY) const
582 {
583         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::clientToScreen(): window invalid")
584         
585         NSPoint screenCoord;
586         NSPoint baseCoord;
587         
588         baseCoord.x = inX;
589         baseCoord.y = inY;
590         
591         screenCoord = [m_window convertBaseToScreen:baseCoord];
592         
593         outX = screenCoord.x;
594         outY = screenCoord.y;
595 }
596
597
598 NSScreen* GHOST_WindowCocoa::getScreen()
599 {
600         return [m_window screen];
601 }
602
603
604 /**
605  * @note Fullscreen switch is not actual fullscreen with display capture. As this capture removes all OS X window manager features.
606  * Instead, the menu bar and the dock are hidden, and the window is made borderless and enlarged.
607  * Thus, process switch, exposé, spaces, ... still work in fullscreen mode
608  */
609 GHOST_TSuccess GHOST_WindowCocoa::setState(GHOST_TWindowState state)
610 {
611         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setState(): window invalid")
612     switch (state) {
613                 case GHOST_kWindowStateMinimized:
614             [m_window miniaturize:nil];
615             break;
616                 case GHOST_kWindowStateMaximized:
617                         [m_window zoom:nil];
618                         break;
619                 
620                 case GHOST_kWindowStateFullScreen:
621                         if (!m_fullScreen)
622                         {
623                                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
624                         
625                                 //This status change needs to be done before Cocoa call to enter fullscreen mode
626                                 //to give window delegate hint not to forward its deactivation to ghost wm that doesn't know view/window difference
627                                 m_fullScreen = true;
628
629 #ifdef MAC_OS_X_VERSION_10_6
630                                 //10.6 provides Cocoa functions to autoshow menu bar, and to change a window style
631                                 //Hide menu & dock if needed
632                                 if ([[m_window screen] isEqual:[NSScreen mainScreen]])
633                                 {
634                                         [NSApp setPresentationOptions:(NSApplicationPresentationHideDock | NSApplicationPresentationAutoHideMenuBar)];
635                                 }
636                                 //Make window borderless and enlarge it
637                                 [m_window setStyleMask:NSBorderlessWindowMask];
638                                 [m_window setFrame:[[m_window screen] frame] display:YES];
639                                 [m_window makeFirstResponder:m_openGLView];
640 #else
641                                 //With 10.5, we need to create a new window to change its style to borderless
642                                 //Hide menu & dock if needed
643                                 if ([[m_window screen] isEqual:[NSScreen mainScreen]])
644                                 {
645                                         //Cocoa function in 10.5 does not allow to set the menu bar in auto-show mode [NSMenu setMenuBarVisible:NO];
646                                         //One of the very few 64bit compatible Carbon function
647                                         SetSystemUIMode(kUIModeAllHidden,kUIOptionAutoShowMenuBar);
648                                 }
649                                 //Create a fullscreen borderless window
650                                 CocoaWindow *tmpWindow = [[CocoaWindow alloc]
651                                                                                   initWithContentRect:[[m_window screen] frame]
652                                                                                   styleMask:NSBorderlessWindowMask
653                                                                                   backing:NSBackingStoreBuffered
654                                                                                   defer:YES];
655                                 //Copy current window parameters
656                                 [tmpWindow setTitle:[m_window title]];
657                                 [tmpWindow setRepresentedFilename:[m_window representedFilename]];
658                                 [tmpWindow setReleasedWhenClosed:NO];
659                                 [tmpWindow setAcceptsMouseMovedEvents:YES];
660                                 [tmpWindow setDelegate:[m_window delegate]];
661                                 
662                                 //Assign the openGL view to the new window
663                                 [tmpWindow setContentView:m_openGLView];
664                                 
665                                 //Show the new window
666                                 [tmpWindow makeKeyAndOrderFront:nil];
667                                 //Close and release old window
668                                 [m_window setDelegate:nil]; // To avoid the notification of "window closed" event
669                                 [m_window close];
670                                 [m_window release];
671                                 m_window = tmpWindow;
672 #endif
673                         
674                                 //Tell WM of view new size
675                                 m_systemCocoa->handleWindowEvent(GHOST_kEventWindowSize, this);
676                                 
677                                 [pool drain];
678                                 }
679                         break;
680                 case GHOST_kWindowStateNormal:
681         default:
682                         if (m_fullScreen)
683                         {
684                                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
685                                 m_fullScreen = false;
686
687                                 //Exit fullscreen
688 #ifdef MAC_OS_X_VERSION_10_6
689                                 //Show again menu & dock if needed
690                                 if ([[m_window screen] isEqual:[NSScreen mainScreen]])
691                                 {
692                                         [NSApp setPresentationOptions:NSApplicationPresentationDefault];
693                                 }
694                                 //Make window normal and resize it
695                                 [m_window setStyleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)];
696                                 [m_window setFrame:[[m_window screen] visibleFrame] display:YES];
697                                 //TODO for 10.6 only : window title is forgotten after the style change
698                                 [m_window makeFirstResponder:m_openGLView];
699 #else
700                                 //With 10.5, we need to create a new window to change its style to borderless
701                                 //Show menu & dock if needed
702                                 if ([[m_window screen] isEqual:[NSScreen mainScreen]])
703                                 {
704                                         //Cocoa function in 10.5 does not allow to set the menu bar in auto-show mode [NSMenu setMenuBarVisible:YES];
705                                         SetSystemUIMode(kUIModeNormal, 0); //One of the very few 64bit compatible Carbon function
706                                 }
707                                 //Create a fullscreen borderless window
708                                 CocoaWindow *tmpWindow = [[CocoaWindow alloc]
709                                                                                   initWithContentRect:[[m_window screen] frame]
710                                                                                                         styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)
711                                                                                                           backing:NSBackingStoreBuffered
712                                                                                                                 defer:YES];
713                                 //Copy current window parameters
714                                 [tmpWindow setTitle:[m_window title]];
715                                 [tmpWindow setRepresentedFilename:[m_window representedFilename]];
716                                 [tmpWindow setReleasedWhenClosed:NO];
717                                 [tmpWindow setAcceptsMouseMovedEvents:YES];
718                                 [tmpWindow setDelegate:[m_window delegate]];
719                                 
720                                 //Assign the openGL view to the new window
721                                 [tmpWindow setContentView:m_openGLView];
722                                 
723                                 //Show the new window
724                                 [tmpWindow makeKeyAndOrderFront:nil];
725                                 //Close and release old window
726                                 [m_window setDelegate:nil]; // To avoid the notification of "window closed" event
727                                 [m_window close];
728                                 [m_window release];
729                                 m_window = tmpWindow;
730 #endif
731                         
732                                 //Tell WM of view new size
733                                 m_systemCocoa->handleWindowEvent(GHOST_kEventWindowSize, this);
734                                 
735                                 [pool drain];
736                         }
737             else if ([m_window isMiniaturized])
738                                 [m_window deminiaturize:nil];
739                         else if ([m_window isZoomed])
740                                 [m_window zoom:nil];
741             break;
742     }
743     return GHOST_kSuccess;
744 }
745
746 GHOST_TSuccess GHOST_WindowCocoa::setModifiedState(bool isUnsavedChanges)
747 {
748         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
749         
750         [m_window setDocumentEdited:isUnsavedChanges];
751         
752         [pool drain];
753         return GHOST_Window::setModifiedState(isUnsavedChanges);
754 }
755
756
757
758 GHOST_TSuccess GHOST_WindowCocoa::setOrder(GHOST_TWindowOrder order)
759 {
760         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setOrder(): window invalid")
761     if (order == GHOST_kWindowOrderTop) {
762                 [m_window makeKeyAndOrderFront:nil];
763     }
764     else {
765                 NSArray *windowsList;
766                 
767                 [m_window orderBack:nil];
768                 
769                 //Check for other blender opened windows and make the frontmost key
770                 windowsList = [NSApp orderedWindows];
771                 if ([windowsList count]) {
772                         [[windowsList objectAtIndex:0] makeKeyAndOrderFront:nil];
773                 }
774     }
775     return GHOST_kSuccess;
776 }
777
778 #pragma mark Drawing context
779
780 /*#define  WAIT_FOR_VSYNC 1*/
781
782 GHOST_TSuccess GHOST_WindowCocoa::swapBuffers()
783 {
784     if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
785         if (m_openGLContext != nil) {
786                         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
787                         [m_openGLContext flushBuffer];
788                         [pool drain];
789             return GHOST_kSuccess;
790         }
791     }
792     return GHOST_kFailure;
793 }
794
795 GHOST_TSuccess GHOST_WindowCocoa::updateDrawingContext()
796 {
797         if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
798                 if (m_openGLContext != nil) {
799                         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
800                         [m_openGLContext update];
801                         [pool drain];
802                         return GHOST_kSuccess;
803                 }
804         }
805         return GHOST_kFailure;
806 }
807
808 GHOST_TSuccess GHOST_WindowCocoa::activateDrawingContext()
809 {
810         if (m_drawingContextType == GHOST_kDrawingContextTypeOpenGL) {
811                 if (m_openGLContext != nil) {
812                         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
813                         [m_openGLContext makeCurrentContext];
814                         [pool drain];
815                         return GHOST_kSuccess;
816                 }
817         }
818         return GHOST_kFailure;
819 }
820
821
822 GHOST_TSuccess GHOST_WindowCocoa::installDrawingContext(GHOST_TDrawingContextType type)
823 {
824         GHOST_TSuccess success = GHOST_kFailure;
825         
826         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
827         
828         NSOpenGLPixelFormat *pixelFormat;
829         NSOpenGLContext *tmpOpenGLContext;
830         
831         /***** Multithreaded opengl code : uncomment for enabling
832         CGLContextObj cglCtx;
833         */
834          
835         switch (type) {
836                 case GHOST_kDrawingContextTypeOpenGL:
837                         if (!getValid()) break;
838                                         
839                         pixelFormat = [m_openGLView pixelFormat];
840                         tmpOpenGLContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat
841                                                                                                                           shareContext:s_firstOpenGLcontext];
842                         if (tmpOpenGLContext == nil) {
843                                 success = GHOST_kFailure;
844                                 break;
845                         }
846                         
847                         //Switch openGL to multhreaded mode
848                         /******* Multithreaded opengl code : uncomment for enabling
849                         cglCtx = (CGLContextObj)[tmpOpenGLContext CGLContextObj];
850                         if (CGLEnable(cglCtx, kCGLCEMPEngine) == kCGLNoError)
851                                 printf("\nSwitched openGL to multithreaded mode");
852                          */
853                         
854                         if (!s_firstOpenGLcontext) s_firstOpenGLcontext = tmpOpenGLContext;
855 #ifdef WAIT_FOR_VSYNC
856                                 /* wait for vsync, to avoid tearing artifacts */
857                                 [tmpOpenGLContext setValues:1 forParameter:NSOpenGLCPSwapInterval];
858 #endif
859                                 [m_openGLView setOpenGLContext:tmpOpenGLContext];
860                                 [tmpOpenGLContext setView:m_openGLView];
861                                 
862                                 m_openGLContext = tmpOpenGLContext;
863                         break;
864                 
865                 case GHOST_kDrawingContextTypeNone:
866                         success = GHOST_kSuccess;
867                         break;
868                 
869                 default:
870                         break;
871         }
872         [pool drain];
873         return success;
874 }
875
876
877 GHOST_TSuccess GHOST_WindowCocoa::removeDrawingContext()
878 {
879         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
880         switch (m_drawingContextType) {
881                 case GHOST_kDrawingContextTypeOpenGL:
882                         if (m_openGLContext)
883                         {
884                                 [m_openGLView clearGLContext];
885                                 if (s_firstOpenGLcontext == m_openGLContext) s_firstOpenGLcontext = nil;
886                                 m_openGLContext = nil;
887                         }
888                         [pool drain];
889                         return GHOST_kSuccess;
890                 case GHOST_kDrawingContextTypeNone:
891                         [pool drain];
892                         return GHOST_kSuccess;
893                         break;
894                 default:
895                         [pool drain];
896                         return GHOST_kFailure;
897         }
898 }
899
900
901 GHOST_TSuccess GHOST_WindowCocoa::invalidate()
902 {
903         GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::invalidate(): window invalid")
904         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
905         [m_openGLView setNeedsDisplay:YES];
906         [pool drain];
907         return GHOST_kSuccess;
908 }
909
910 #pragma mark Cursor handling
911
912 void GHOST_WindowCocoa::loadCursor(bool visible, GHOST_TStandardCursor cursor) const
913 {
914         static bool systemCursorVisible = true;
915         
916         NSAutoreleasePool *pool =[[NSAutoreleasePool alloc] init];
917
918         NSCursor *tmpCursor =nil;
919         
920         if (visible != systemCursorVisible) {
921                 if (visible) {
922                         [NSCursor unhide];
923                         systemCursorVisible = true;
924                 }
925                 else {
926                         [NSCursor hide];
927                         systemCursorVisible = false;
928                 }
929         }
930
931         if (cursor == GHOST_kStandardCursorCustom && m_customCursor) {
932                 tmpCursor = m_customCursor;
933         } else {
934                 switch (cursor) {
935                         case GHOST_kStandardCursorDestroy:
936                                 tmpCursor = [NSCursor disappearingItemCursor];
937                                 break;
938                         case GHOST_kStandardCursorText:
939                                 tmpCursor = [NSCursor IBeamCursor];
940                                 break;
941                         case GHOST_kStandardCursorCrosshair:
942                                 tmpCursor = [NSCursor crosshairCursor];
943                                 break;
944                         case GHOST_kStandardCursorUpDown:
945                                 tmpCursor = [NSCursor resizeUpDownCursor];
946                                 break;
947                         case GHOST_kStandardCursorLeftRight:
948                                 tmpCursor = [NSCursor resizeLeftRightCursor];
949                                 break;
950                         case GHOST_kStandardCursorTopSide:
951                                 tmpCursor = [NSCursor resizeUpCursor];
952                                 break;
953                         case GHOST_kStandardCursorBottomSide:
954                                 tmpCursor = [NSCursor resizeDownCursor];
955                                 break;
956                         case GHOST_kStandardCursorLeftSide:
957                                 tmpCursor = [NSCursor resizeLeftCursor];
958                                 break;
959                         case GHOST_kStandardCursorRightSide:
960                                 tmpCursor = [NSCursor resizeRightCursor];
961                                 break;
962                         case GHOST_kStandardCursorRightArrow:
963                         case GHOST_kStandardCursorInfo:
964                         case GHOST_kStandardCursorLeftArrow:
965                         case GHOST_kStandardCursorHelp:
966                         case GHOST_kStandardCursorCycle:
967                         case GHOST_kStandardCursorSpray:
968                         case GHOST_kStandardCursorWait:
969                         case GHOST_kStandardCursorTopLeftCorner:
970                         case GHOST_kStandardCursorTopRightCorner:
971                         case GHOST_kStandardCursorBottomRightCorner:
972                         case GHOST_kStandardCursorBottomLeftCorner:
973                         case GHOST_kStandardCursorDefault:
974                         default:
975                                 tmpCursor = [NSCursor arrowCursor];
976                                 break;
977                 };
978         }
979         [tmpCursor set];
980         [pool drain];
981 }
982
983
984
985 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorVisibility(bool visible)
986 {
987         if ([m_window isVisible]) {
988                 loadCursor(visible, getCursorShape());
989         }
990         
991         return GHOST_kSuccess;
992 }
993
994
995 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorGrab(GHOST_TGrabCursorMode mode)
996 {
997         GHOST_TSuccess err = GHOST_kSuccess;
998         
999         if (mode != GHOST_kGrabDisable)
1000         {
1001                 //No need to perform grab without warp as it is always on in OS X
1002                 if(mode != GHOST_kGrabNormal) {
1003                         GHOST_TInt32 x_old,y_old;
1004
1005                         m_systemCocoa->getCursorPosition(x_old,y_old);
1006                         screenToClient(x_old, y_old, m_cursorGrabInitPos[0], m_cursorGrabInitPos[1]);
1007                         //Warp position is stored in client (window base) coordinates
1008                         setCursorGrabAccum(0, 0);
1009                         
1010                         if(mode == GHOST_kGrabHide) {
1011                                 setWindowCursorVisibility(false);
1012                         }
1013                         
1014                         //Dissociate cursor position even for warp mode, to allow mouse acceleration to work even when warping the cursor
1015                         err = CGAssociateMouseAndMouseCursorPosition(false) == kCGErrorSuccess ? GHOST_kSuccess : GHOST_kFailure;
1016                 }
1017         }
1018         else {
1019                 if(m_cursorGrab==GHOST_kGrabHide)
1020                 {
1021                         //No need to set again cursor position, as it has not changed for Cocoa
1022                         setWindowCursorVisibility(true);
1023                 }
1024                 
1025                 err = CGAssociateMouseAndMouseCursorPosition(true) == kCGErrorSuccess ? GHOST_kSuccess : GHOST_kFailure;
1026                 /* Almost works without but important otherwise the mouse GHOST location can be incorrect on exit */
1027                 setCursorGrabAccum(0, 0);
1028                 m_cursorGrabBounds.m_l= m_cursorGrabBounds.m_r= -1; /* disable */
1029         }
1030         return err;
1031 }
1032         
1033 GHOST_TSuccess GHOST_WindowCocoa::setWindowCursorShape(GHOST_TStandardCursor shape)
1034 {
1035         if (m_customCursor) {
1036                 [m_customCursor release];
1037                 m_customCursor = nil;
1038         }
1039
1040         if ([m_window isVisible]) {
1041                 loadCursor(getCursorVisibility(), shape);
1042         }
1043         
1044         return GHOST_kSuccess;
1045 }
1046
1047 /** Reverse the bits in a GHOST_TUns8
1048 static GHOST_TUns8 uns8ReverseBits(GHOST_TUns8 ch)
1049 {
1050         ch= ((ch>>1)&0x55) | ((ch<<1)&0xAA);
1051         ch= ((ch>>2)&0x33) | ((ch<<2)&0xCC);
1052         ch= ((ch>>4)&0x0F) | ((ch<<4)&0xF0);
1053         return ch;
1054 }
1055 */
1056
1057
1058 /** Reverse the bits in a GHOST_TUns16 */
1059 static GHOST_TUns16 uns16ReverseBits(GHOST_TUns16 shrt)
1060 {
1061         shrt= ((shrt>>1)&0x5555) | ((shrt<<1)&0xAAAA);
1062         shrt= ((shrt>>2)&0x3333) | ((shrt<<2)&0xCCCC);
1063         shrt= ((shrt>>4)&0x0F0F) | ((shrt<<4)&0xF0F0);
1064         shrt= ((shrt>>8)&0x00FF) | ((shrt<<8)&0xFF00);
1065         return shrt;
1066 }
1067
1068 GHOST_TSuccess GHOST_WindowCocoa::setWindowCustomCursorShape(GHOST_TUns8 *bitmap, GHOST_TUns8 *mask,
1069                                         int sizex, int sizey, int hotX, int hotY, int fg_color, int bg_color)
1070 {
1071         int y,nbUns16;
1072         NSPoint hotSpotPoint;
1073         NSBitmapImageRep *cursorImageRep;
1074         NSImage *cursorImage;
1075         NSSize imSize;
1076         GHOST_TUns16 *cursorBitmap;
1077         
1078         
1079         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1080         
1081         if (m_customCursor) {
1082                 [m_customCursor release];
1083                 m_customCursor = nil;
1084         }
1085         
1086
1087         cursorImageRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil
1088                                                                                                                          pixelsWide:sizex
1089                                                                                                                          pixelsHigh:sizey
1090                                                                                                                   bitsPerSample:1 
1091                                                                                                                 samplesPerPixel:2
1092                                                                                                                            hasAlpha:YES
1093                                                                                                                            isPlanar:YES
1094                                                                                                                  colorSpaceName:NSDeviceWhiteColorSpace
1095                                                                                                                         bytesPerRow:(sizex/8 + (sizex%8 >0 ?1:0))
1096                                                                                                                    bitsPerPixel:1];
1097         
1098         
1099         cursorBitmap = (GHOST_TUns16*)[cursorImageRep bitmapData];
1100         nbUns16 = [cursorImageRep bytesPerPlane]/2;
1101         
1102         for (y=0; y<nbUns16; y++) {
1103 #if !defined(__LITTLE_ENDIAN__)
1104                 cursorBitmap[y] = ~uns16ReverseBits((bitmap[2*y]<<0) | (bitmap[2*y+1]<<8));
1105                 cursorBitmap[nbUns16+y] = uns16ReverseBits((mask[2*y]<<0) | (mask[2*y+1]<<8));
1106 #else
1107                 cursorBitmap[y] = ~uns16ReverseBits((bitmap[2*y+1]<<0) | (bitmap[2*y]<<8));
1108                 cursorBitmap[nbUns16+y] = uns16ReverseBits((mask[2*y+1]<<0) | (mask[2*y]<<8));
1109 #endif
1110                 
1111         }
1112         
1113         
1114         imSize.width = sizex;
1115         imSize.height= sizey;
1116         cursorImage = [[NSImage alloc] initWithSize:imSize];
1117         [cursorImage addRepresentation:cursorImageRep];
1118         
1119         hotSpotPoint.x = hotX;
1120         hotSpotPoint.y = hotY;
1121         
1122         //foreground and background color parameter is not handled for now (10.6)
1123         m_customCursor = [[NSCursor alloc] initWithImage:cursorImage
1124                                                                                          hotSpot:hotSpotPoint];
1125         
1126         [cursorImageRep release];
1127         [cursorImage release];
1128         
1129         if ([m_window isVisible]) {
1130                 loadCursor(getCursorVisibility(), GHOST_kStandardCursorCustom);
1131         }
1132         [pool drain];
1133         return GHOST_kSuccess;
1134 }
1135
1136 GHOST_TSuccess GHOST_WindowCocoa::setWindowCustomCursorShape(GHOST_TUns8 bitmap[16][2], 
1137                                                                                                 GHOST_TUns8 mask[16][2], int hotX, int hotY)
1138 {
1139         return setWindowCustomCursorShape((GHOST_TUns8*)bitmap, (GHOST_TUns8*) mask, 16, 16, hotX, hotY, 0, 1);
1140 }