Cocoa/DND : added bitmap data type handling in drag'n'drop operations
[blender.git] / intern / ghost / intern / GHOST_SystemCocoa.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 09/2009
26  *
27  * ***** END GPL LICENSE BLOCK *****
28  */
29
30 #import <Cocoa/Cocoa.h>
31
32 #include <sys/time.h>
33 #include <sys/types.h>
34 #include <sys/sysctl.h>
35
36 #include "GHOST_SystemCocoa.h"
37
38 #include "GHOST_DisplayManagerCocoa.h"
39 #include "GHOST_EventKey.h"
40 #include "GHOST_EventButton.h"
41 #include "GHOST_EventCursor.h"
42 #include "GHOST_EventWheel.h"
43 #include "GHOST_EventNDOF.h"
44 #include "GHOST_EventTrackpad.h"
45 #include "GHOST_EventDragnDrop.h"
46 #include "GHOST_EventString.h"
47
48 #include "GHOST_TimerManager.h"
49 #include "GHOST_TimerTask.h"
50 #include "GHOST_WindowManager.h"
51 #include "GHOST_WindowCocoa.h"
52 #include "GHOST_NDOFManager.h"
53 #include "AssertMacros.h"
54
55 #pragma mark KeyMap, mouse converters
56
57
58 /* Keycodes from Carbon include file */
59 /*  
60  *  Summary:
61  *    Virtual keycodes
62  *  
63  *  Discussion:
64  *    These constants are the virtual keycodes defined originally in
65  *    Inside Mac Volume V, pg. V-191. They identify physical keys on a
66  *    keyboard. Those constants with "ANSI" in the name are labeled
67  *    according to the key position on an ANSI-standard US keyboard.
68  *    For example, kVK_ANSI_A indicates the virtual keycode for the key
69  *    with the letter 'A' in the US keyboard layout. Other keyboard
70  *    layouts may have the 'A' key label on a different physical key;
71  *    in this case, pressing 'A' will generate a different virtual
72  *    keycode.
73  */
74 enum {
75         kVK_ANSI_A                    = 0x00,
76         kVK_ANSI_S                    = 0x01,
77         kVK_ANSI_D                    = 0x02,
78         kVK_ANSI_F                    = 0x03,
79         kVK_ANSI_H                    = 0x04,
80         kVK_ANSI_G                    = 0x05,
81         kVK_ANSI_Z                    = 0x06,
82         kVK_ANSI_X                    = 0x07,
83         kVK_ANSI_C                    = 0x08,
84         kVK_ANSI_V                    = 0x09,
85         kVK_ANSI_B                    = 0x0B,
86         kVK_ANSI_Q                    = 0x0C,
87         kVK_ANSI_W                    = 0x0D,
88         kVK_ANSI_E                    = 0x0E,
89         kVK_ANSI_R                    = 0x0F,
90         kVK_ANSI_Y                    = 0x10,
91         kVK_ANSI_T                    = 0x11,
92         kVK_ANSI_1                    = 0x12,
93         kVK_ANSI_2                    = 0x13,
94         kVK_ANSI_3                    = 0x14,
95         kVK_ANSI_4                    = 0x15,
96         kVK_ANSI_6                    = 0x16,
97         kVK_ANSI_5                    = 0x17,
98         kVK_ANSI_Equal                = 0x18,
99         kVK_ANSI_9                    = 0x19,
100         kVK_ANSI_7                    = 0x1A,
101         kVK_ANSI_Minus                = 0x1B,
102         kVK_ANSI_8                    = 0x1C,
103         kVK_ANSI_0                    = 0x1D,
104         kVK_ANSI_RightBracket         = 0x1E,
105         kVK_ANSI_O                    = 0x1F,
106         kVK_ANSI_U                    = 0x20,
107         kVK_ANSI_LeftBracket          = 0x21,
108         kVK_ANSI_I                    = 0x22,
109         kVK_ANSI_P                    = 0x23,
110         kVK_ANSI_L                    = 0x25,
111         kVK_ANSI_J                    = 0x26,
112         kVK_ANSI_Quote                = 0x27,
113         kVK_ANSI_K                    = 0x28,
114         kVK_ANSI_Semicolon            = 0x29,
115         kVK_ANSI_Backslash            = 0x2A,
116         kVK_ANSI_Comma                = 0x2B,
117         kVK_ANSI_Slash                = 0x2C,
118         kVK_ANSI_N                    = 0x2D,
119         kVK_ANSI_M                    = 0x2E,
120         kVK_ANSI_Period               = 0x2F,
121         kVK_ANSI_Grave                = 0x32,
122         kVK_ANSI_KeypadDecimal        = 0x41,
123         kVK_ANSI_KeypadMultiply       = 0x43,
124         kVK_ANSI_KeypadPlus           = 0x45,
125         kVK_ANSI_KeypadClear          = 0x47,
126         kVK_ANSI_KeypadDivide         = 0x4B,
127         kVK_ANSI_KeypadEnter          = 0x4C,
128         kVK_ANSI_KeypadMinus          = 0x4E,
129         kVK_ANSI_KeypadEquals         = 0x51,
130         kVK_ANSI_Keypad0              = 0x52,
131         kVK_ANSI_Keypad1              = 0x53,
132         kVK_ANSI_Keypad2              = 0x54,
133         kVK_ANSI_Keypad3              = 0x55,
134         kVK_ANSI_Keypad4              = 0x56,
135         kVK_ANSI_Keypad5              = 0x57,
136         kVK_ANSI_Keypad6              = 0x58,
137         kVK_ANSI_Keypad7              = 0x59,
138         kVK_ANSI_Keypad8              = 0x5B,
139         kVK_ANSI_Keypad9              = 0x5C
140 };
141
142 /* keycodes for keys that are independent of keyboard layout*/
143 enum {
144         kVK_Return                    = 0x24,
145         kVK_Tab                       = 0x30,
146         kVK_Space                     = 0x31,
147         kVK_Delete                    = 0x33,
148         kVK_Escape                    = 0x35,
149         kVK_Command                   = 0x37,
150         kVK_Shift                     = 0x38,
151         kVK_CapsLock                  = 0x39,
152         kVK_Option                    = 0x3A,
153         kVK_Control                   = 0x3B,
154         kVK_RightShift                = 0x3C,
155         kVK_RightOption               = 0x3D,
156         kVK_RightControl              = 0x3E,
157         kVK_Function                  = 0x3F,
158         kVK_F17                       = 0x40,
159         kVK_VolumeUp                  = 0x48,
160         kVK_VolumeDown                = 0x49,
161         kVK_Mute                      = 0x4A,
162         kVK_F18                       = 0x4F,
163         kVK_F19                       = 0x50,
164         kVK_F20                       = 0x5A,
165         kVK_F5                        = 0x60,
166         kVK_F6                        = 0x61,
167         kVK_F7                        = 0x62,
168         kVK_F3                        = 0x63,
169         kVK_F8                        = 0x64,
170         kVK_F9                        = 0x65,
171         kVK_F11                       = 0x67,
172         kVK_F13                       = 0x69,
173         kVK_F16                       = 0x6A,
174         kVK_F14                       = 0x6B,
175         kVK_F10                       = 0x6D,
176         kVK_F12                       = 0x6F,
177         kVK_F15                       = 0x71,
178         kVK_Help                      = 0x72,
179         kVK_Home                      = 0x73,
180         kVK_PageUp                    = 0x74,
181         kVK_ForwardDelete             = 0x75,
182         kVK_F4                        = 0x76,
183         kVK_End                       = 0x77,
184         kVK_F2                        = 0x78,
185         kVK_PageDown                  = 0x79,
186         kVK_F1                        = 0x7A,
187         kVK_LeftArrow                 = 0x7B,
188         kVK_RightArrow                = 0x7C,
189         kVK_DownArrow                 = 0x7D,
190         kVK_UpArrow                   = 0x7E
191 };
192
193 /* ISO keyboards only*/
194 enum {
195         kVK_ISO_Section               = 0x0A
196 };
197
198 /* JIS keyboards only*/
199 enum {
200         kVK_JIS_Yen                   = 0x5D,
201         kVK_JIS_Underscore            = 0x5E,
202         kVK_JIS_KeypadComma           = 0x5F,
203         kVK_JIS_Eisu                  = 0x66,
204         kVK_JIS_Kana                  = 0x68
205 };
206
207
208 static GHOST_TButtonMask convertButton(int button)
209 {
210         switch (button) {
211                 case 0:
212                         return GHOST_kButtonMaskLeft;
213                 case 1:
214                         return GHOST_kButtonMaskRight;
215                 case 2:
216                         return GHOST_kButtonMaskMiddle;
217                 case 3:
218                         return GHOST_kButtonMaskButton4;
219                 case 4:
220                         return GHOST_kButtonMaskButton5;
221                 default:
222                         return GHOST_kButtonMaskLeft;
223         }
224 }
225
226 /**
227  * Converts Mac rawkey codes (same for Cocoa & Carbon)
228  * into GHOST key codes
229  * @param rawCode The raw physical key code
230  * @param recvChar the character ignoring modifiers (except for shift)
231  * @return Ghost key code
232  */
233 static GHOST_TKey convertKey(int rawCode, unichar recvChar) 
234 {       
235         
236         //printf("\nrecvchar %c 0x%x",recvChar,recvChar);
237         switch (rawCode) {
238                 /*Physical keycodes not used due to map changes in int'l keyboards
239                 case kVK_ANSI_A:        return GHOST_kKeyA;
240                 case kVK_ANSI_B:        return GHOST_kKeyB;
241                 case kVK_ANSI_C:        return GHOST_kKeyC;
242                 case kVK_ANSI_D:        return GHOST_kKeyD;
243                 case kVK_ANSI_E:        return GHOST_kKeyE;
244                 case kVK_ANSI_F:        return GHOST_kKeyF;
245                 case kVK_ANSI_G:        return GHOST_kKeyG;
246                 case kVK_ANSI_H:        return GHOST_kKeyH;
247                 case kVK_ANSI_I:        return GHOST_kKeyI;
248                 case kVK_ANSI_J:        return GHOST_kKeyJ;
249                 case kVK_ANSI_K:        return GHOST_kKeyK;
250                 case kVK_ANSI_L:        return GHOST_kKeyL;
251                 case kVK_ANSI_M:        return GHOST_kKeyM;
252                 case kVK_ANSI_N:        return GHOST_kKeyN;
253                 case kVK_ANSI_O:        return GHOST_kKeyO;
254                 case kVK_ANSI_P:        return GHOST_kKeyP;
255                 case kVK_ANSI_Q:        return GHOST_kKeyQ;
256                 case kVK_ANSI_R:        return GHOST_kKeyR;
257                 case kVK_ANSI_S:        return GHOST_kKeyS;
258                 case kVK_ANSI_T:        return GHOST_kKeyT;
259                 case kVK_ANSI_U:        return GHOST_kKeyU;
260                 case kVK_ANSI_V:        return GHOST_kKeyV;
261                 case kVK_ANSI_W:        return GHOST_kKeyW;
262                 case kVK_ANSI_X:        return GHOST_kKeyX;
263                 case kVK_ANSI_Y:        return GHOST_kKeyY;
264                 case kVK_ANSI_Z:        return GHOST_kKeyZ;*/
265                 
266                 /* Numbers keys mapped to handle some int'l keyboard (e.g. French)*/
267                 case kVK_ISO_Section: return    GHOST_kKeyUnknown;
268                 case kVK_ANSI_1:        return GHOST_kKey1;
269                 case kVK_ANSI_2:        return GHOST_kKey2;
270                 case kVK_ANSI_3:        return GHOST_kKey3;
271                 case kVK_ANSI_4:        return GHOST_kKey4;
272                 case kVK_ANSI_5:        return GHOST_kKey5;
273                 case kVK_ANSI_6:        return GHOST_kKey6;
274                 case kVK_ANSI_7:        return GHOST_kKey7;
275                 case kVK_ANSI_8:        return GHOST_kKey8;
276                 case kVK_ANSI_9:        return GHOST_kKey9;
277                 case kVK_ANSI_0:        return GHOST_kKey0;
278         
279                 case kVK_ANSI_Keypad0:                  return GHOST_kKeyNumpad0;
280                 case kVK_ANSI_Keypad1:                  return GHOST_kKeyNumpad1;
281                 case kVK_ANSI_Keypad2:                  return GHOST_kKeyNumpad2;
282                 case kVK_ANSI_Keypad3:                  return GHOST_kKeyNumpad3;
283                 case kVK_ANSI_Keypad4:                  return GHOST_kKeyNumpad4;
284                 case kVK_ANSI_Keypad5:                  return GHOST_kKeyNumpad5;
285                 case kVK_ANSI_Keypad6:                  return GHOST_kKeyNumpad6;
286                 case kVK_ANSI_Keypad7:                  return GHOST_kKeyNumpad7;
287                 case kVK_ANSI_Keypad8:                  return GHOST_kKeyNumpad8;
288                 case kVK_ANSI_Keypad9:                  return GHOST_kKeyNumpad9;
289                 case kVK_ANSI_KeypadDecimal:    return GHOST_kKeyNumpadPeriod;
290                 case kVK_ANSI_KeypadEnter:              return GHOST_kKeyNumpadEnter;
291                 case kVK_ANSI_KeypadPlus:               return GHOST_kKeyNumpadPlus;
292                 case kVK_ANSI_KeypadMinus:              return GHOST_kKeyNumpadMinus;
293                 case kVK_ANSI_KeypadMultiply:   return GHOST_kKeyNumpadAsterisk;
294                 case kVK_ANSI_KeypadDivide:     return GHOST_kKeyNumpadSlash;
295                 case kVK_ANSI_KeypadClear:              return GHOST_kKeyUnknown;
296
297                 case kVK_F1:                            return GHOST_kKeyF1;
298                 case kVK_F2:                            return GHOST_kKeyF2;
299                 case kVK_F3:                            return GHOST_kKeyF3;
300                 case kVK_F4:                            return GHOST_kKeyF4;
301                 case kVK_F5:                            return GHOST_kKeyF5;
302                 case kVK_F6:                            return GHOST_kKeyF6;
303                 case kVK_F7:                            return GHOST_kKeyF7;
304                 case kVK_F8:                            return GHOST_kKeyF8;
305                 case kVK_F9:                            return GHOST_kKeyF9;
306                 case kVK_F10:                           return GHOST_kKeyF10;
307                 case kVK_F11:                           return GHOST_kKeyF11;
308                 case kVK_F12:                           return GHOST_kKeyF12;
309                 case kVK_F13:                           return GHOST_kKeyF13;
310                 case kVK_F14:                           return GHOST_kKeyF14;
311                 case kVK_F15:                           return GHOST_kKeyF15;
312                 case kVK_F16:                           return GHOST_kKeyF16;
313                 case kVK_F17:                           return GHOST_kKeyF17;
314                 case kVK_F18:                           return GHOST_kKeyF18;
315                 case kVK_F19:                           return GHOST_kKeyF19;
316                 case kVK_F20:                           return GHOST_kKeyF20;
317                         
318                 case kVK_UpArrow:                       return GHOST_kKeyUpArrow;
319                 case kVK_DownArrow:                     return GHOST_kKeyDownArrow;
320                 case kVK_LeftArrow:                     return GHOST_kKeyLeftArrow;
321                 case kVK_RightArrow:            return GHOST_kKeyRightArrow;
322                         
323                 case kVK_Return:                        return GHOST_kKeyEnter;
324                 case kVK_Delete:                        return GHOST_kKeyBackSpace;
325                 case kVK_ForwardDelete:         return GHOST_kKeyDelete;
326                 case kVK_Escape:                        return GHOST_kKeyEsc;
327                 case kVK_Tab:                           return GHOST_kKeyTab;
328                 case kVK_Space:                         return GHOST_kKeySpace;
329                         
330                 case kVK_Home:                          return GHOST_kKeyHome;
331                 case kVK_End:                           return GHOST_kKeyEnd;
332                 case kVK_PageUp:                        return GHOST_kKeyUpPage;
333                 case kVK_PageDown:                      return GHOST_kKeyDownPage;
334                         
335                 /*case kVK_ANSI_Minus:          return GHOST_kKeyMinus;
336                 case kVK_ANSI_Equal:            return GHOST_kKeyEqual;
337                 case kVK_ANSI_Comma:            return GHOST_kKeyComma;
338                 case kVK_ANSI_Period:           return GHOST_kKeyPeriod;
339                 case kVK_ANSI_Slash:            return GHOST_kKeySlash;
340                 case kVK_ANSI_Semicolon:        return GHOST_kKeySemicolon;
341                 case kVK_ANSI_Quote:            return GHOST_kKeyQuote;
342                 case kVK_ANSI_Backslash:        return GHOST_kKeyBackslash;
343                 case kVK_ANSI_LeftBracket:      return GHOST_kKeyLeftBracket;
344                 case kVK_ANSI_RightBracket:     return GHOST_kKeyRightBracket;
345                 case kVK_ANSI_Grave:            return GHOST_kKeyAccentGrave;*/
346                         
347                 case kVK_VolumeUp:
348                 case kVK_VolumeDown:
349                 case kVK_Mute:
350                         return GHOST_kKeyUnknown;
351                         
352                 default:
353                         /*Then detect on character value for "remappable" keys in int'l keyboards*/
354                         if ((recvChar >= 'A') && (recvChar <= 'Z')) {
355                                 return (GHOST_TKey) (recvChar - 'A' + GHOST_kKeyA);
356                         } else if ((recvChar >= 'a') && (recvChar <= 'z')) {
357                                 return (GHOST_TKey) (recvChar - 'a' + GHOST_kKeyA);
358                         } else
359                         switch (recvChar) {
360                                 case '-':       return GHOST_kKeyMinus;
361                                 case '=':       return GHOST_kKeyEqual;
362                                 case ',':       return GHOST_kKeyComma;
363                                 case '.':       return GHOST_kKeyPeriod;
364                                 case '/':       return GHOST_kKeySlash;
365                                 case ';':       return GHOST_kKeySemicolon;
366                                 case '\'':      return GHOST_kKeyQuote;
367                                 case '\\':      return GHOST_kKeyBackslash;
368                                 case '[':       return GHOST_kKeyLeftBracket;
369                                 case ']':       return GHOST_kKeyRightBracket;
370                                 case '`':       return GHOST_kKeyAccentGrave;
371                                 default:
372                                         return GHOST_kKeyUnknown;
373                         }
374         }
375         return GHOST_kKeyUnknown;
376 }
377
378
379 #pragma mark defines for 10.6 api not documented in 10.5
380 #ifndef MAC_OS_X_VERSION_10_6
381 enum {
382         /* The following event types are available on some hardware on 10.5.2 and later */
383         NSEventTypeGesture          = 29,
384         NSEventTypeMagnify          = 30,
385         NSEventTypeSwipe            = 31,
386         NSEventTypeRotate           = 18,
387         NSEventTypeBeginGesture     = 19,
388         NSEventTypeEndGesture       = 20
389 };
390
391 @interface NSEvent(GestureEvents)
392 /* This message is valid for events of type NSEventTypeMagnify, on 10.5.2 or later */
393 #if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4
394 - (float)magnification;       // change in magnification.
395 #else
396 - (CGFloat)magnification;       // change in magnification.
397 #endif
398 @end 
399
400 @interface NSEvent(SnowLeopardEvents)
401 /* modifier keys currently down.  This returns the state of devices combined
402  with synthesized events at the moment, independent of which events
403  have been delivered via the event stream. */
404 #if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4
405 + (unsigned int)modifierFlags; //NSUInteger is defined only from 10.5
406 #else
407 + (NSUInteger)modifierFlags;
408 #endif
409 @end
410
411 #endif
412
413
414 #pragma mark Utility functions
415
416 #define FIRSTFILEBUFLG 512
417 static bool g_hasFirstFile = false;
418 static char g_firstFileBuf[512];
419
420 //TODO:Need to investigate this. Function called too early in creator.c to have g_hasFirstFile == true
421 extern "C" int GHOST_HACK_getFirstFile(char buf[FIRSTFILEBUFLG]) { 
422         if (g_hasFirstFile) {
423                 strncpy(buf, g_firstFileBuf, FIRSTFILEBUFLG - 1);
424                 buf[FIRSTFILEBUFLG - 1] = '\0';
425                 return 1;
426         } else {
427                 return 0; 
428         }
429 }
430
431 #if defined(WITH_QUICKTIME) && !defined(USE_QTKIT)
432 //Need to place this quicktime function in an ObjC file
433 //It is used to avoid memory leak when raising the quicktime "compression settings" standard dialog
434 extern "C" {
435         struct bContext;
436         struct wmOperator;
437         extern int fromcocoa_request_qtcodec_settings(bContext *C, wmOperator *op);
438
439
440 int cocoa_request_qtcodec_settings(bContext *C, wmOperator *op)
441 {
442         int result;
443         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
444         
445         result = fromcocoa_request_qtcodec_settings(C, op);
446         
447         [pool drain];
448         return result;
449 }
450 };
451 #endif
452
453
454 #pragma mark Cocoa objects
455
456 /**
457  * CocoaAppDelegate
458  * ObjC object to capture applicationShouldTerminate, and send quit event
459  **/
460 @interface CocoaAppDelegate : NSObject {
461         GHOST_SystemCocoa *systemCocoa;
462 }
463 - (void)setSystemCocoa:(GHOST_SystemCocoa *)sysCocoa;
464 - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename;
465 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender;
466 - (void)applicationWillTerminate:(NSNotification *)aNotification;
467 - (void)applicationWillBecomeActive:(NSNotification *)aNotification;
468 @end
469
470 @implementation CocoaAppDelegate : NSObject
471 -(void)setSystemCocoa:(GHOST_SystemCocoa *)sysCocoa
472 {
473         systemCocoa = sysCocoa;
474 }
475
476 - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
477 {
478         return systemCocoa->handleOpenDocumentRequest(filename);
479 }
480
481 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
482 {
483         //TODO: implement graceful termination through Cocoa mechanism to avoid session log off to be cancelled
484         //Note that Cmd+Q is already handled by keyhandler
485     if (systemCocoa->handleQuitRequest() == GHOST_kExitNow)
486                 return NSTerminateCancel;//NSTerminateNow;
487         else
488                 return NSTerminateCancel;
489 }
490
491 // To avoid cancelling a log off process, we must use Cocoa termination process
492 // And this function is the only chance to perform clean up
493 // So WM_exit needs to be called directly, as the event loop will never run before termination
494 - (void)applicationWillTerminate:(NSNotification *)aNotification
495 {
496         /*G.afbreek = 0; //Let Cocoa perform the termination at the end
497         WM_exit(C);*/
498 }
499
500 - (void)applicationWillBecomeActive:(NSNotification *)aNotification
501 {
502         systemCocoa->handleApplicationBecomeActiveEvent();
503 }
504 @end
505
506
507
508 #pragma mark initialization/finalization
509
510
511 GHOST_SystemCocoa::GHOST_SystemCocoa()
512 {
513         int mib[2];
514         struct timeval boottime;
515         size_t len;
516         char *rstring = NULL;
517         
518         m_modifierMask =0;
519         m_pressedMouseButtons =0;
520         m_isGestureInProgress = false;
521         m_cursorDelta_x=0;
522         m_cursorDelta_y=0;
523         m_outsideLoopEventProcessed = false;
524         m_needDelayedApplicationBecomeActiveEventProcessing = false;
525         m_displayManager = new GHOST_DisplayManagerCocoa ();
526         GHOST_ASSERT(m_displayManager, "GHOST_SystemCocoa::GHOST_SystemCocoa(): m_displayManager==0\n");
527         m_displayManager->initialize();
528
529         //NSEvent timeStamp is given in system uptime, state start date is boot time
530         mib[0] = CTL_KERN;
531         mib[1] = KERN_BOOTTIME;
532         len = sizeof(struct timeval);
533         
534         sysctl(mib, 2, &boottime, &len, NULL, 0);
535         m_start_time = ((boottime.tv_sec*1000)+(boottime.tv_usec/1000));
536         
537         //Detect multitouch trackpad
538         mib[0] = CTL_HW;
539         mib[1] = HW_MODEL;
540         sysctl( mib, 2, NULL, &len, NULL, 0 );
541         rstring = (char*)malloc( len );
542         sysctl( mib, 2, rstring, &len, NULL, 0 );
543         
544         //Hack on MacBook revision, as multitouch avail. function missing
545         if (strstr(rstring,"MacBookAir") ||
546                 (strstr(rstring,"MacBook") && (rstring[strlen(rstring)-3]>='5') && (rstring[strlen(rstring)-3]<='9')))
547                 m_hasMultiTouchTrackpad = true;
548         else m_hasMultiTouchTrackpad = false;
549         
550         free( rstring );
551         rstring = NULL;
552         
553         m_ignoreWindowSizedMessages = false;
554 }
555
556 GHOST_SystemCocoa::~GHOST_SystemCocoa()
557 {
558 }
559
560
561 GHOST_TSuccess GHOST_SystemCocoa::init()
562 {
563         
564     GHOST_TSuccess success = GHOST_System::init();
565     if (success) {
566                 //ProcessSerialNumber psn;
567                 
568                 //Carbon stuff to move window & menu to foreground
569                 /*if (!GetCurrentProcess(&psn)) {
570                         TransformProcessType(&psn, kProcessTransformToForegroundApplication);
571                         SetFrontProcess(&psn);
572                 }*/
573                 
574                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
575                 if (NSApp == nil) {
576                         [NSApplication sharedApplication];
577                         
578                         if ([NSApp mainMenu] == nil) {
579                                 NSMenu *mainMenubar = [[NSMenu alloc] init];
580                                 NSMenuItem *menuItem;
581                                 NSMenu *windowMenu;
582                                 NSMenu *appMenu;
583                                 
584                                 //Create the application menu
585                                 appMenu = [[NSMenu alloc] initWithTitle:@"Blender"];
586                                 
587                                 [appMenu addItemWithTitle:@"About Blender" action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
588                                 [appMenu addItem:[NSMenuItem separatorItem]];
589                                 
590                                 menuItem = [appMenu addItemWithTitle:@"Hide Blender" action:@selector(hide:) keyEquivalent:@"h"];
591                                 [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
592                                  
593                                 menuItem = [appMenu addItemWithTitle:@"Hide others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
594                                 [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask | NSCommandKeyMask)];
595                                 
596                                 [appMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
597                                 
598                                 menuItem = [appMenu addItemWithTitle:@"Quit Blender" action:@selector(terminate:) keyEquivalent:@"q"];
599                                 [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
600                                 
601                                 menuItem = [[NSMenuItem alloc] init];
602                                 [menuItem setSubmenu:appMenu];
603                                 
604                                 [mainMenubar addItem:menuItem];
605                                 [menuItem release];
606                                 [NSApp performSelector:@selector(setAppleMenu:) withObject:appMenu]; //Needed for 10.5
607                                 [appMenu release];
608                                 
609                                 //Create the window menu
610                                 windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
611                                 
612                                 menuItem = [windowMenu addItemWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
613                                 [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
614                                 
615                                 [windowMenu addItemWithTitle:@"Zoom" action:@selector(performZoom:) keyEquivalent:@""];
616                                 
617                                 menuItem = [windowMenu addItemWithTitle:@"Close" action:@selector(performClose:) keyEquivalent:@"w"];
618                                 [menuItem setKeyEquivalentModifierMask:NSCommandKeyMask];
619                                 
620                                 menuItem = [[NSMenuItem alloc] init];
621                                 [menuItem setSubmenu:windowMenu];
622                                 
623                                 [mainMenubar addItem:menuItem];
624                                 [menuItem release];
625                                 
626                                 [NSApp setMainMenu:mainMenubar];
627                                 [NSApp setWindowsMenu:windowMenu];
628                                 [windowMenu release];
629                         }
630                 }
631                 if ([NSApp delegate] == nil) {
632                         CocoaAppDelegate *appDelegate = [[CocoaAppDelegate alloc] init];
633                         [appDelegate setSystemCocoa:this];
634                         [NSApp setDelegate:appDelegate];
635                 }
636                 
637                 [NSApp finishLaunching];
638                 
639                 [pool drain];
640     }
641     return success;
642 }
643
644
645 #pragma mark window management
646
647 GHOST_TUns64 GHOST_SystemCocoa::getMilliSeconds() const
648 {
649         //Cocoa equivalent exists in 10.6 ([[NSProcessInfo processInfo] systemUptime])
650         struct timeval currentTime;
651         
652         gettimeofday(&currentTime, NULL);
653         
654         //Return timestamp of system uptime
655         
656         return ((currentTime.tv_sec*1000)+(currentTime.tv_usec/1000)-m_start_time);
657 }
658
659
660 GHOST_TUns8 GHOST_SystemCocoa::getNumDisplays() const
661 {
662         //Note that OS X supports monitor hot plug
663         // We do not support multiple monitors at the moment
664         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
665
666         GHOST_TUns8 count = [[NSScreen screens] count];
667
668         [pool drain];
669         return count;
670 }
671
672
673 void GHOST_SystemCocoa::getMainDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const
674 {
675         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
676         //Get visible frame, that is frame excluding dock and top menu bar
677         NSRect frame = [[NSScreen mainScreen] visibleFrame];
678         
679         //Returns max window contents (excluding title bar...)
680         NSRect contentRect = [NSWindow contentRectForFrameRect:frame
681                                                                                                  styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask)];
682         
683         width = contentRect.size.width;
684         height = contentRect.size.height;
685
686         [pool drain];
687 }
688
689
690 GHOST_IWindow* GHOST_SystemCocoa::createWindow(
691         const STR_String& title, 
692         GHOST_TInt32 left,
693         GHOST_TInt32 top,
694         GHOST_TUns32 width,
695         GHOST_TUns32 height,
696         GHOST_TWindowState state,
697         GHOST_TDrawingContextType type,
698         bool stereoVisual,
699         const GHOST_TUns16 numOfAASamples,
700         const GHOST_TEmbedderWindowID parentWindow
701 )
702 {
703     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
704         GHOST_IWindow* window = 0;
705         
706         //Get the available rect for including window contents
707         NSRect frame = [[NSScreen mainScreen] visibleFrame];
708         NSRect contentRect = [NSWindow contentRectForFrameRect:frame
709                                                                                                  styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask)];
710         
711         //Ensures window top left is inside this available rect
712         left = left > contentRect.origin.x ? left : contentRect.origin.x;
713         top = top > contentRect.origin.y ? top : contentRect.origin.y;
714         
715         window = new GHOST_WindowCocoa (this, title, left, top, width, height, state, type, stereoVisual, numOfAASamples);
716
717     if (window) {
718         if (window->getValid()) {
719             // Store the pointer to the window 
720             GHOST_ASSERT(m_windowManager, "m_windowManager not initialized");
721             m_windowManager->addWindow(window);
722             m_windowManager->setActiveWindow(window);
723                         //Need to tell window manager the new window is the active one (Cocoa does not send the event activate upon window creation)
724             pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window));
725                         pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window));
726
727         }
728         else {
729                         GHOST_PRINT("GHOST_SystemCocoa::createWindow(): window invalid\n");
730             delete window;
731             window = 0;
732         }
733     }
734         else {
735                 GHOST_PRINT("GHOST_SystemCocoa::createWindow(): could not create window\n");
736         }
737         [pool drain];
738     return window;
739 }
740
741 GHOST_TSuccess GHOST_SystemCocoa::beginFullScreen(const GHOST_DisplaySetting& setting, GHOST_IWindow** window, const bool stereoVisual)
742 {       
743         GHOST_IWindow* currentWindow = m_windowManager->getActiveWindow();
744
745         *window = currentWindow;
746         
747         return currentWindow->setState(GHOST_kWindowStateFullScreen);
748 }
749
750 GHOST_TSuccess GHOST_SystemCocoa::endFullScreen(void)
751 {       
752         GHOST_IWindow* currentWindow = m_windowManager->getActiveWindow();
753         
754         return currentWindow->setState(GHOST_kWindowStateNormal);
755 }
756
757
758         
759 /**
760  * @note : returns coordinates in Cocoa screen coordinates
761  */
762 GHOST_TSuccess GHOST_SystemCocoa::getCursorPosition(GHOST_TInt32& x, GHOST_TInt32& y) const
763 {
764     NSPoint mouseLoc = [NSEvent mouseLocation];
765         
766     // Returns the mouse location in screen coordinates
767     x = (GHOST_TInt32)mouseLoc.x;
768     y = (GHOST_TInt32)mouseLoc.y;
769     return GHOST_kSuccess;
770 }
771
772 /**
773  * @note : expect Cocoa screen coordinates
774  */
775 GHOST_TSuccess GHOST_SystemCocoa::setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y) const
776 {
777         float xf=(float)x, yf=(float)y;
778         GHOST_WindowCocoa* window = (GHOST_WindowCocoa*)m_windowManager->getActiveWindow();
779         NSScreen *windowScreen = window->getScreen();
780         NSRect screenRect = [windowScreen frame];
781         
782         //Set position relative to current screen
783         xf -= screenRect.origin.x;
784         yf -= screenRect.origin.y;
785         
786         //Quartz Display Services uses the old coordinates (top left origin)
787         yf = screenRect.size.height -yf;
788
789         CGDisplayMoveCursorToPoint((CGDirectDisplayID)[[[windowScreen deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue], CGPointMake(xf, yf));
790
791     return GHOST_kSuccess;
792 }
793
794
795 GHOST_TSuccess GHOST_SystemCocoa::getModifierKeys(GHOST_ModifierKeys& keys) const
796 {
797         keys.set(GHOST_kModifierKeyCommand, (m_modifierMask & NSCommandKeyMask) ? true : false);
798         keys.set(GHOST_kModifierKeyLeftAlt, (m_modifierMask & NSAlternateKeyMask) ? true : false);
799         keys.set(GHOST_kModifierKeyLeftShift, (m_modifierMask & NSShiftKeyMask) ? true : false);
800         keys.set(GHOST_kModifierKeyLeftControl, (m_modifierMask & NSControlKeyMask) ? true : false);
801         
802     return GHOST_kSuccess;
803 }
804
805 GHOST_TSuccess GHOST_SystemCocoa::getButtons(GHOST_Buttons& buttons) const
806 {
807         buttons.clear();
808     buttons.set(GHOST_kButtonMaskLeft, m_pressedMouseButtons & GHOST_kButtonMaskLeft);
809         buttons.set(GHOST_kButtonMaskRight, m_pressedMouseButtons & GHOST_kButtonMaskRight);
810         buttons.set(GHOST_kButtonMaskMiddle, m_pressedMouseButtons & GHOST_kButtonMaskMiddle);
811         buttons.set(GHOST_kButtonMaskButton4, m_pressedMouseButtons & GHOST_kButtonMaskButton4);
812         buttons.set(GHOST_kButtonMaskButton5, m_pressedMouseButtons & GHOST_kButtonMaskButton5);
813     return GHOST_kSuccess;
814 }
815
816
817
818 #pragma mark Event handlers
819
820 /**
821  * The event queue polling function
822  */
823 bool GHOST_SystemCocoa::processEvents(bool waitForEvent)
824 {
825         bool anyProcessed = false;
826         NSEvent *event;
827         
828         //      SetMouseCoalescingEnabled(false, NULL);
829         //TODO : implement timer ??
830         
831         /*do {
832                 GHOST_TimerManager* timerMgr = getTimerManager();
833                 
834                  if (waitForEvent) {
835                  GHOST_TUns64 next = timerMgr->nextFireTime();
836                  double timeOut;
837                  
838                  if (next == GHOST_kFireTimeNever) {
839                  timeOut = kEventDurationForever;
840                  } else {
841                  timeOut = (double)(next - getMilliSeconds())/1000.0;
842                  if (timeOut < 0.0)
843                  timeOut = 0.0;
844                  }
845                  
846                  ::ReceiveNextEvent(0, NULL, timeOut, false, &event);
847                  }
848                  
849                  if (timerMgr->fireTimers(getMilliSeconds())) {
850                  anyProcessed = true;
851                  }*/
852                 
853                 do {
854                         NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
855                         event = [NSApp nextEventMatchingMask:NSAnyEventMask
856                                                                            untilDate:[NSDate distantPast]
857                                                                                   inMode:NSDefaultRunLoopMode
858                                                                                  dequeue:YES];
859                         if (event==nil) {
860                                 [pool drain];
861                                 break;
862                         }
863                         
864                         anyProcessed = true;
865                         
866                         switch ([event type]) {
867                                 case NSKeyDown:
868                                 case NSKeyUp:
869                                 case NSFlagsChanged:
870                                         handleKeyEvent(event);
871                                         
872                                         /* Support system-wide keyboard shortcuts, like Expos√©, ...) =>included in always NSApp sendEvent */
873                                         /*              if (([event modifierFlags] & NSCommandKeyMask) || [event type] == NSFlagsChanged) {
874                                          [NSApp sendEvent:event];
875                                          }*/
876                                         break;
877                                         
878                                 case NSLeftMouseDown:
879                                 case NSLeftMouseUp:
880                                 case NSRightMouseDown:
881                                 case NSRightMouseUp:
882                                 case NSMouseMoved:
883                                 case NSLeftMouseDragged:
884                                 case NSRightMouseDragged:
885                                 case NSScrollWheel:
886                                 case NSOtherMouseDown:
887                                 case NSOtherMouseUp:
888                                 case NSOtherMouseDragged:
889                                 case NSEventTypeMagnify:
890                                 case NSEventTypeRotate:
891                                 case NSEventTypeBeginGesture:
892                                 case NSEventTypeEndGesture:
893                                         handleMouseEvent(event);
894                                         break;
895                                         
896                                 case NSTabletPoint:
897                                 case NSTabletProximity:
898                                         handleTabletEvent(event,[event type]);
899                                         break;
900                                         
901                                         /* Trackpad features, fired only from OS X 10.5.2
902                                          case NSEventTypeGesture:
903                                          case NSEventTypeSwipe:
904                                          break; */
905                                         
906                                         /*Unused events
907                                          NSMouseEntered       = 8,
908                                          NSMouseExited        = 9,
909                                          NSAppKitDefined      = 13,
910                                          NSSystemDefined      = 14,
911                                          NSApplicationDefined = 15,
912                                          NSPeriodic           = 16,
913                                          NSCursorUpdate       = 17,*/
914                                         
915                                 default:
916                                         break;
917                         }
918                         //Resend event to NSApp to ensure Mac wide events are handled
919                         [NSApp sendEvent:event];
920                         [pool drain];
921                 } while (event!= nil);          
922         //} while (waitForEvent && !anyProcessed); Needed only for timer implementation
923         
924         if (m_needDelayedApplicationBecomeActiveEventProcessing) handleApplicationBecomeActiveEvent();
925         
926         if (m_outsideLoopEventProcessed) {
927                 m_outsideLoopEventProcessed = false;
928                 return true;
929         }
930         
931     return anyProcessed;
932 }
933
934 //Note: called from NSApplication delegate
935 GHOST_TSuccess GHOST_SystemCocoa::handleApplicationBecomeActiveEvent()
936 {
937         //Update the modifiers key mask, as its status may have changed when the application was not active
938         //(that is when update events are sent to another application)
939         unsigned int modifiers;
940         GHOST_IWindow* window = m_windowManager->getActiveWindow();
941         
942         if (!window) {
943                 m_needDelayedApplicationBecomeActiveEventProcessing = true;
944                 return GHOST_kFailure;
945         }
946         else m_needDelayedApplicationBecomeActiveEventProcessing = false;
947
948 #ifdef MAC_OS_X_VERSION_10_6
949         modifiers = [NSEvent modifierFlags];
950 #else
951         //If build against an older SDK, check if running on 10.6 to use the correct function
952         if ([NSEvent respondsToSelector:@selector(modifierFlags)]) {
953                 modifiers = [NSEvent modifierFlags];
954         }
955         else {
956                 //TODO: need to find a better workaround for the missing cocoa "getModifierFlag" function in 10.4/10.5
957                 modifiers = 0;
958         }
959 #endif
960         
961         /* Discard erroneous 10.6 modifiers values reported when switching back from spaces */
962         if ((modifiers & NSDeviceIndependentModifierFlagsMask) == 0xb00000) modifiers = 0;
963         
964         if ((modifiers & NSShiftKeyMask) != (m_modifierMask & NSShiftKeyMask)) {
965                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & NSShiftKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftShift) );
966         }
967         if ((modifiers & NSControlKeyMask) != (m_modifierMask & NSControlKeyMask)) {
968                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & NSControlKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftControl) );
969         }
970         if ((modifiers & NSAlternateKeyMask) != (m_modifierMask & NSAlternateKeyMask)) {
971                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & NSAlternateKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftAlt) );
972         }
973         if ((modifiers & NSCommandKeyMask) != (m_modifierMask & NSCommandKeyMask)) {
974                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & NSCommandKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyCommand) );
975         }
976         
977         m_modifierMask = modifiers;
978         
979         m_outsideLoopEventProcessed = true;
980         return GHOST_kSuccess;
981 }
982
983 //Note: called from NSWindow delegate
984 GHOST_TSuccess GHOST_SystemCocoa::handleWindowEvent(GHOST_TEventType eventType, GHOST_WindowCocoa* window)
985 {
986         if (!validWindow(window)) {
987                 return GHOST_kFailure;
988         }
989                 switch(eventType) 
990                 {
991                         case GHOST_kEventWindowClose:
992                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, window) );
993                                 break;
994                         case GHOST_kEventWindowActivate:
995                                 m_windowManager->setActiveWindow(window);
996                                 window->loadCursor(window->getCursorVisibility(), window->getCursorShape());
997                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window) );
998                                 break;
999                         case GHOST_kEventWindowDeactivate:
1000                                 m_windowManager->setWindowInactive(window);
1001                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowDeactivate, window) );
1002                                 break;
1003                         case GHOST_kEventWindowUpdate:
1004                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window) );
1005                                 break;
1006                         case GHOST_kEventWindowMove:
1007                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowMove, window) );
1008                                 break;
1009                         case GHOST_kEventWindowSize:
1010                                 if (!m_ignoreWindowSizedMessages)
1011                                 {
1012                                         window->updateDrawingContext();
1013                                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window) );
1014                                 }
1015                                 break;
1016                         default:
1017                                 return GHOST_kFailure;
1018                                 break;
1019                 }
1020         
1021         m_outsideLoopEventProcessed = true;
1022         return GHOST_kSuccess;
1023 }
1024
1025 //Note: called from NSWindow subclass
1026 GHOST_TSuccess GHOST_SystemCocoa::handleDraggingEvent(GHOST_TEventType eventType, GHOST_TDragnDropTypes draggedObjectType,
1027                                                                    GHOST_WindowCocoa* window, int mouseX, int mouseY, void* data)
1028 {
1029         if (!validWindow(window)) {
1030                 return GHOST_kFailure;
1031         }
1032         switch(eventType) 
1033         {
1034                 case GHOST_kEventDraggingEntered:
1035                 case GHOST_kEventDraggingUpdated:
1036                 case GHOST_kEventDraggingExited:
1037                         pushEvent(new GHOST_EventDragnDrop(getMilliSeconds(),eventType,draggedObjectType,window,mouseX,mouseY,NULL));
1038                         break;
1039                         
1040                 case GHOST_kEventDraggingDropDone:
1041                 {
1042                         GHOST_TUns8 * temp_buff;
1043                         GHOST_TStringArray *strArray;
1044                         NSArray *droppedArray;
1045                         size_t pastedTextSize;  
1046                         NSString *droppedStr;
1047                         GHOST_TEventDataPtr eventData;
1048                         int i;
1049
1050                         if (!data) return GHOST_kFailure;
1051                         
1052                         switch (draggedObjectType) {
1053                                 case GHOST_kDragnDropTypeFilenames:
1054                                         droppedArray = (NSArray*)data;
1055                                         
1056                                         strArray = (GHOST_TStringArray*)malloc(sizeof(GHOST_TStringArray));
1057                                         if (!strArray) return GHOST_kFailure;
1058                                         
1059                                         strArray->count = [droppedArray count];
1060                                         if (strArray->count == 0) return GHOST_kFailure;
1061                                         
1062                                         strArray->strings = (GHOST_TUns8**) malloc(strArray->count*sizeof(GHOST_TUns8*));
1063                                         
1064                                         for (i=0;i<strArray->count;i++)
1065                                         {
1066                                                 droppedStr = [droppedArray objectAtIndex:i];
1067                                                 
1068                                                 pastedTextSize = [droppedStr lengthOfBytesUsingEncoding:NSISOLatin1StringEncoding];
1069                                                 temp_buff = (GHOST_TUns8*) malloc(pastedTextSize+1); 
1070                                         
1071                                                 if (!temp_buff) {
1072                                                         strArray->count = i;
1073                                                         break;
1074                                                 }
1075                                         
1076                                                 strncpy((char*)temp_buff, [droppedStr cStringUsingEncoding:NSISOLatin1StringEncoding], pastedTextSize);
1077                                                 temp_buff[pastedTextSize] = '\0';
1078                                                 
1079                                                 strArray->strings[i] = temp_buff;
1080                                         }
1081
1082                                         eventData = (GHOST_TEventDataPtr) strArray;     
1083                                         break;
1084                                         
1085                                 case GHOST_kDragnDropTypeString:
1086                                         droppedStr = (NSString*)data;
1087                                         pastedTextSize = [droppedStr lengthOfBytesUsingEncoding:NSISOLatin1StringEncoding];
1088                                         
1089                                         temp_buff = (GHOST_TUns8*) malloc(pastedTextSize+1); 
1090                                         
1091                                         if (temp_buff == NULL) {
1092                                                 return GHOST_kFailure;
1093                                         }
1094                                         
1095                                         strncpy((char*)temp_buff, [droppedStr cStringUsingEncoding:NSISOLatin1StringEncoding], pastedTextSize);
1096                                         
1097                                         temp_buff[pastedTextSize] = '\0';
1098                                         
1099                                         eventData = (GHOST_TEventDataPtr) temp_buff;
1100                                         break;
1101                                 
1102                                 case GHOST_kDragnDropTypeBitmap:
1103                                 {
1104                                         NSImage *droppedImg = (NSImage*)data;
1105                                         NSSize imgSize = [droppedImg size];
1106                                         ImBuf *ibuf = NULL;
1107                                         GHOST_TUns8 *rasterRGB = NULL;
1108                                         GHOST_TUns8 *rasterRGBA = NULL;
1109                                         GHOST_TUns8 *toIBuf = NULL;
1110                                         int x, y, to_i, from_i;
1111                                         NSBitmapImageRep *blBitmapFormatImageRGB,*blBitmapFormatImageRGBA,*bitmapImage=nil;
1112                                         NSEnumerator *enumerator;
1113                                         NSImageRep *representation;
1114                                         
1115                                         ibuf = IMB_allocImBuf (imgSize.width , imgSize.height, 32, IB_rect, 0);
1116                                         if (!ibuf) {
1117                                                 [droppedImg release];
1118                                                 return GHOST_kFailure;
1119                                         }
1120                                         
1121                                         /*Get the bitmap of the image*/
1122                                         enumerator = [[droppedImg representations] objectEnumerator];
1123                                         while ((representation = [enumerator nextObject])) {
1124                                                 if ([representation isKindOfClass:[NSBitmapImageRep class]]) {
1125                                                         bitmapImage = (NSBitmapImageRep *)representation;
1126                                                         break;
1127                                                 }
1128                                         }
1129                                         if (bitmapImage == nil) return GHOST_kFailure;
1130                                         
1131                                         if (([bitmapImage bitsPerPixel] == 32) && (([bitmapImage bitmapFormat] & 0x5) == 0)
1132                                                 && ![bitmapImage isPlanar]) {
1133                                                 /* Try a fast copy if the image is a meshed RGBA 32bit bitmap*/
1134                                                 toIBuf = (GHOST_TUns8*)ibuf->rect;
1135                                                 rasterRGB = (GHOST_TUns8*)[bitmapImage bitmapData];
1136                                                 for (y = 0; y < imgSize.height; y++) {
1137                                                         to_i = (imgSize.height-y-1)*imgSize.width;
1138                                                         from_i = y*imgSize.width;
1139                                                         memcpy(toIBuf+4*to_i, rasterRGB+4*from_i, 4*imgSize.width);
1140                                                 }
1141                                         }
1142                                         else {
1143                                                 /* Tell cocoa image resolution is same as current system one */
1144                                                 [bitmapImage setSize:imgSize];
1145                                                 
1146                                                 /* Convert the image in a RGBA 32bit format */
1147                                                 /* As Core Graphics does not support contextes with non premutliplied alpha,
1148                                                  we need to get alpha key values in a separate batch */
1149                                                 
1150                                                 /* First get RGB values w/o Alpha to avoid pre-multiplication, 32bit but last byte is unused */
1151                                                 blBitmapFormatImageRGB = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
1152                                                                                                                                                                                  pixelsWide:imgSize.width 
1153                                                                                                                                                                                  pixelsHigh:imgSize.height
1154                                                                                                                                                                           bitsPerSample:8 samplesPerPixel:3 hasAlpha:NO isPlanar:NO
1155                                                                                                                                                                          colorSpaceName:NSDeviceRGBColorSpace 
1156                                                                                                                                                                            bitmapFormat:(NSBitmapFormat)0
1157                                                                                                                                                                                 bytesPerRow:4*imgSize.width
1158                                                                                                                                                                            bitsPerPixel:32/*RGB format padded to 32bits*/];
1159                                                 
1160                                                 [NSGraphicsContext saveGraphicsState];
1161                                                 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:blBitmapFormatImageRGB]];
1162                                                 [bitmapImage draw];
1163                                                 [NSGraphicsContext restoreGraphicsState];
1164                                                 
1165                                                 rasterRGB = (GHOST_TUns8*)[blBitmapFormatImageRGB bitmapData];
1166                                                 if (rasterRGB == NULL) {
1167                                                         [bitmapImage release];
1168                                                         [blBitmapFormatImageRGB release];
1169                                                         [droppedImg release];
1170                                                         return GHOST_kFailure;
1171                                                 }
1172                                                 
1173                                                 /* Then get Alpha values by getting the RGBA image (that is premultiplied btw) */
1174                                                 blBitmapFormatImageRGBA = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
1175                                                                                                                                                                                   pixelsWide:imgSize.width
1176                                                                                                                                                                                   pixelsHigh:imgSize.height
1177                                                                                                                                                                            bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO
1178                                                                                                                                                                           colorSpaceName:NSDeviceRGBColorSpace
1179                                                                                                                                                                                 bitmapFormat:(NSBitmapFormat)0
1180                                                                                                                                                                                  bytesPerRow:4*imgSize.width
1181                                                                                                                                                                                 bitsPerPixel:32/* RGBA */];
1182                                                 
1183                                                 [NSGraphicsContext saveGraphicsState];
1184                                                 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:blBitmapFormatImageRGBA]];
1185                                                 [bitmapImage draw];
1186                                                 [NSGraphicsContext restoreGraphicsState];
1187                                                 
1188                                                 rasterRGBA = (GHOST_TUns8*)[blBitmapFormatImageRGBA bitmapData];
1189                                                 if (rasterRGBA == NULL) {
1190                                                         [bitmapImage release];
1191                                                         [blBitmapFormatImageRGB release];
1192                                                         [blBitmapFormatImageRGBA release];
1193                                                         [droppedImg release];
1194                                                         return GHOST_kFailure;
1195                                                 }
1196                                                 
1197                                                 /*Copy the image to ibuf, flipping it vertically*/
1198                                                 toIBuf = (GHOST_TUns8*)ibuf->rect;
1199                                                 for (y = 0; y < imgSize.height; y++) {
1200                                                         for (x = 0; x < imgSize.width; x++) {
1201                                                                 to_i = (imgSize.height-y-1)*imgSize.width + x;
1202                                                                 from_i = y*imgSize.width + x;
1203                                                                 
1204                                                                 toIBuf[4*to_i] = rasterRGB[4*from_i]; /* R */
1205                                                                 toIBuf[4*to_i+1] = rasterRGB[4*from_i+1]; /* G */
1206                                                                 toIBuf[4*to_i+2] = rasterRGB[4*from_i+2]; /* B */
1207                                                                 toIBuf[4*to_i+3] = rasterRGBA[4*from_i+3]; /* A */
1208                                                         }
1209                                                 }
1210                                                 
1211                                                 [blBitmapFormatImageRGB release];
1212                                                 [blBitmapFormatImageRGBA release];
1213                                                 [droppedImg release];
1214                                         }
1215                                         
1216                                         eventData = (GHOST_TEventDataPtr) ibuf;
1217                                 }
1218                                         break;
1219                                         
1220                                 default:
1221                                         return GHOST_kFailure;
1222                                         break;
1223                         }
1224                         pushEvent(new GHOST_EventDragnDrop(getMilliSeconds(),eventType,draggedObjectType,window,mouseX,mouseY,eventData));
1225                 }
1226                         break;
1227                 default:
1228                         return GHOST_kFailure;
1229         }
1230         m_outsideLoopEventProcessed = true;
1231         return GHOST_kSuccess;
1232 }
1233
1234
1235 GHOST_TUns8 GHOST_SystemCocoa::handleQuitRequest()
1236 {
1237         GHOST_Window* window = (GHOST_Window*)m_windowManager->getActiveWindow();
1238         
1239         //Discard quit event if we are in cursor grab sequence
1240         if (window && (window->getCursorGrabMode() != GHOST_kGrabDisable) && (window->getCursorGrabMode() != GHOST_kGrabNormal))
1241                 return GHOST_kExitCancel;
1242         
1243         //Check open windows if some changes are not saved
1244         if (m_windowManager->getAnyModifiedState())
1245         {
1246                 int shouldQuit = NSRunAlertPanel(@"Exit Blender", @"Some changes have not been saved.\nDo you really want to quit ?",
1247                                                                                  @"Cancel", @"Quit Anyway", nil);
1248                 if (shouldQuit == NSAlertAlternateReturn)
1249                 {
1250                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventQuit, NULL) );
1251                         return GHOST_kExitNow;
1252                 } else {
1253                         //Give back focus to the blender window if user selected cancel quit
1254                         NSArray *windowsList = [NSApp orderedWindows];
1255                         if ([windowsList count]) {
1256                                 [[windowsList objectAtIndex:0] makeKeyAndOrderFront:nil];
1257                         }
1258                 }
1259
1260         }
1261         else {
1262                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventQuit, NULL) );
1263                 m_outsideLoopEventProcessed = true;
1264                 return GHOST_kExitNow;
1265         }
1266         
1267         return GHOST_kExitCancel;
1268 }
1269
1270 bool GHOST_SystemCocoa::handleOpenDocumentRequest(void *filepathStr)
1271 {
1272         NSString *filepath = (NSString*)filepathStr;
1273         int confirmOpen = NSAlertAlternateReturn;
1274         NSArray *windowsList;
1275         char * temp_buff;
1276         size_t filenameTextSize;        
1277         GHOST_Window* window= (GHOST_Window*)m_windowManager->getActiveWindow();
1278         
1279         if (!window) {
1280                 return NO;
1281         }       
1282         
1283         //Discard event if we are in cursor grab sequence, it'll lead to "stuck cursor" situation if the alert panel is raised
1284         if (window && (window->getCursorGrabMode() != GHOST_kGrabDisable) && (window->getCursorGrabMode() != GHOST_kGrabNormal))
1285                 return GHOST_kExitCancel;
1286
1287         //Check open windows if some changes are not saved
1288         if (m_windowManager->getAnyModifiedState())
1289         {
1290                 confirmOpen = NSRunAlertPanel([NSString stringWithFormat:@"Opening %@",[filepath lastPathComponent]],
1291                                                                                  @"Current document has not been saved.\nDo you really want to proceed?",
1292                                                                                  @"Cancel", @"Open", nil);
1293         }
1294
1295         //Give back focus to the blender window
1296         windowsList = [NSApp orderedWindows];
1297         if ([windowsList count]) {
1298                 [[windowsList objectAtIndex:0] makeKeyAndOrderFront:nil];
1299         }
1300
1301         if (confirmOpen == NSAlertAlternateReturn)
1302         {
1303                 filenameTextSize = [filepath lengthOfBytesUsingEncoding:NSISOLatin1StringEncoding];
1304                 
1305                 temp_buff = (char*) malloc(filenameTextSize+1); 
1306                 
1307                 if (temp_buff == NULL) {
1308                         return GHOST_kFailure;
1309                 }
1310                 
1311                 strncpy(temp_buff, [filepath cStringUsingEncoding:NSISOLatin1StringEncoding], filenameTextSize);
1312                 
1313                 temp_buff[filenameTextSize] = '\0';
1314
1315                 pushEvent(new GHOST_EventString(getMilliSeconds(),GHOST_kEventOpenMainFile,window,(GHOST_TEventDataPtr) temp_buff));
1316
1317                 return YES;
1318         }
1319         else return NO;
1320 }
1321
1322 GHOST_TSuccess GHOST_SystemCocoa::handleTabletEvent(void *eventPtr, short eventType)
1323 {
1324         NSEvent *event = (NSEvent *)eventPtr;
1325         GHOST_IWindow* window = m_windowManager->getActiveWindow();
1326         
1327         if (!window) {
1328                 /* If no active window found, still tries to find the window associated with the event
1329                  This may happen when Cocoa continues to send some events after the window deactivate one */
1330                 window = m_windowManager->getWindowAssociatedWithOSWindow((void*)[event window]);
1331                 if (!window) {
1332                         //printf("\nW failure for event 0x%x",[event type]);
1333                         return GHOST_kFailure;
1334                 }
1335         }
1336         
1337         GHOST_TabletData& ct=((GHOST_WindowCocoa*)window)->GetCocoaTabletData();
1338         
1339         switch (eventType) {
1340                 case NSTabletPoint:
1341                         ct.Pressure = [event pressure];
1342                         ct.Xtilt = [event tilt].x;
1343                         ct.Ytilt = [event tilt].y;
1344                         break;
1345                 
1346                 case NSTabletProximity:
1347                         ct.Pressure = 0;
1348                         ct.Xtilt = 0;
1349                         ct.Ytilt = 0;
1350                         if ([event isEnteringProximity])
1351                         {
1352                                 //pointer is entering tablet area proximity
1353                                 switch ([event pointingDeviceType]) {
1354                                         case NSPenPointingDevice:
1355                                                 ct.Active = GHOST_kTabletModeStylus;
1356                                                 break;
1357                                         case NSEraserPointingDevice:
1358                                                 ct.Active = GHOST_kTabletModeEraser;
1359                                                 break;
1360                                         case NSCursorPointingDevice:
1361                                         case NSUnknownPointingDevice:
1362                                         default:
1363                                                 ct.Active = GHOST_kTabletModeNone;
1364                                                 break;
1365                                 }
1366                         } else {
1367                                 // pointer is leaving - return to mouse
1368                                 ct.Active = GHOST_kTabletModeNone;
1369                         }
1370                         break;
1371                 
1372                 default:
1373                         GHOST_ASSERT(FALSE,"GHOST_SystemCocoa::handleTabletEvent : unknown event received");
1374                         return GHOST_kFailure;
1375                         break;
1376         }
1377         return GHOST_kSuccess;
1378 }
1379
1380
1381 GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr)
1382 {
1383         NSEvent *event = (NSEvent *)eventPtr;
1384     GHOST_Window* window = (GHOST_Window*)m_windowManager->getActiveWindow();
1385         
1386         if (!window) {
1387                 /* If no active window found, still tries to find the window associated with the event
1388                  This may happen when Cocoa continues to send some events after the window deactivate one */
1389                 window = (GHOST_Window*)m_windowManager->getWindowAssociatedWithOSWindow((void*)[event window]);
1390                 if (!window) {
1391                         //printf("\nW failure for event 0x%x",[event type]);
1392                         return GHOST_kFailure;
1393                 }
1394         }
1395         
1396         switch ([event type])
1397     {
1398                 case NSLeftMouseDown:
1399                 case NSRightMouseDown:
1400                 case NSOtherMouseDown:
1401                         pushEvent(new GHOST_EventButton([event timestamp]*1000, GHOST_kEventButtonDown, window, convertButton([event buttonNumber])));
1402                         //Handle tablet events combined with mouse events
1403                         switch ([event subtype]) {
1404                                 case NX_SUBTYPE_TABLET_POINT:
1405                                         handleTabletEvent(eventPtr, NSTabletPoint);
1406                                         break;
1407                                 case NX_SUBTYPE_TABLET_PROXIMITY:
1408                                         handleTabletEvent(eventPtr, NSTabletProximity);
1409                                         break;
1410                                 default:
1411                                         //No tablet event included : do nothing
1412                                         break;
1413                         }
1414                         break;
1415                                                 
1416                 case NSLeftMouseUp:
1417                 case NSRightMouseUp:
1418                 case NSOtherMouseUp:
1419                         pushEvent(new GHOST_EventButton([event timestamp]*1000, GHOST_kEventButtonUp, window, convertButton([event buttonNumber])));
1420                         //Handle tablet events combined with mouse events
1421                         switch ([event subtype]) {
1422                                 case NX_SUBTYPE_TABLET_POINT:
1423                                         handleTabletEvent(eventPtr, NSTabletPoint);
1424                                         break;
1425                                 case NX_SUBTYPE_TABLET_PROXIMITY:
1426                                         handleTabletEvent(eventPtr, NSTabletProximity);
1427                                         break;
1428                                 default:
1429                                         //No tablet event included : do nothing
1430                                         break;
1431                         }
1432                         break;
1433                         
1434                 case NSLeftMouseDragged:
1435                 case NSRightMouseDragged:
1436                 case NSOtherMouseDragged:                               
1437                         //Handle tablet events combined with mouse events
1438                         switch ([event subtype]) {
1439                                 case NX_SUBTYPE_TABLET_POINT:
1440                                         handleTabletEvent(eventPtr, NSTabletPoint);
1441                                         break;
1442                                 case NX_SUBTYPE_TABLET_PROXIMITY:
1443                                         handleTabletEvent(eventPtr, NSTabletProximity);
1444                                         break;
1445                                 default:
1446                                         //No tablet event included : do nothing
1447                                         break;
1448                         }
1449                         
1450                 case NSMouseMoved:
1451                                 switch (window->getCursorGrabMode()) {
1452                                         case GHOST_kGrabHide: //Cursor hidden grab operation : no cursor move
1453                                         {
1454                                                 GHOST_TInt32 x_warp, y_warp, x_accum, y_accum;
1455                                                 
1456                                                 window->getCursorGrabInitPos(x_warp, y_warp);
1457                                                 
1458                                                 window->getCursorGrabAccum(x_accum, y_accum);
1459                                                 x_accum += [event deltaX];
1460                                                 y_accum += -[event deltaY]; //Strange Apple implementation (inverted coordinates for the deltaY) ...
1461                                                 window->setCursorGrabAccum(x_accum, y_accum);
1462                                                 
1463                                                 pushEvent(new GHOST_EventCursor([event timestamp]*1000, GHOST_kEventCursorMove, window, x_warp+x_accum, y_warp+y_accum));
1464                                         }
1465                                                 break;
1466                                         case GHOST_kGrabWrap: //Wrap cursor at area/window boundaries
1467                                         {
1468                                                 NSPoint mousePos = [event locationInWindow];
1469                                                 GHOST_TInt32 x_mouse= mousePos.x;
1470                                                 GHOST_TInt32 y_mouse= mousePos.y;
1471                                                 GHOST_TInt32 x_accum, y_accum, x_cur, y_cur;
1472                                                 GHOST_Rect bounds, windowBounds, correctedBounds;
1473                                                 
1474                                                 /* fallback to window bounds */
1475                                                 if(window->getCursorGrabBounds(bounds)==GHOST_kFailure)
1476                                                         window->getClientBounds(bounds);
1477                                                 
1478                                                 //Switch back to Cocoa coordinates orientation (y=0 at botton,the same as blender internal btw!), and to client coordinates
1479                                                 window->getClientBounds(windowBounds);
1480                                                 window->screenToClient(bounds.m_l,bounds.m_b, correctedBounds.m_l, correctedBounds.m_t);
1481                                                 window->screenToClient(bounds.m_r, bounds.m_t, correctedBounds.m_r, correctedBounds.m_b);
1482                                                 correctedBounds.m_b = (windowBounds.m_b - windowBounds.m_t) - correctedBounds.m_b;
1483                                                 correctedBounds.m_t = (windowBounds.m_b - windowBounds.m_t) - correctedBounds.m_t;
1484                                                 
1485                                                 //Update accumulation counts
1486                                                 window->getCursorGrabAccum(x_accum, y_accum);
1487                                                 x_accum += [event deltaX]-m_cursorDelta_x;
1488                                                 y_accum += -[event deltaY]-m_cursorDelta_y; //Strange Apple implementation (inverted coordinates for the deltaY) ...
1489                                                 window->setCursorGrabAccum(x_accum, y_accum);
1490                                                 
1491                                                 
1492                                                 //Warp mouse cursor if needed
1493                                                 x_mouse += [event deltaX]-m_cursorDelta_x;
1494                                                 y_mouse += -[event deltaY]-m_cursorDelta_y;
1495                                                 correctedBounds.wrapPoint(x_mouse, y_mouse, 2);
1496                                                 
1497                                                 //Compensate for mouse moved event taking cursor position set into account
1498                                                 m_cursorDelta_x = x_mouse-mousePos.x;
1499                                                 m_cursorDelta_y = y_mouse-mousePos.y;
1500                                                 
1501                                                 //Set new cursor position
1502                                                 window->clientToScreen(x_mouse, y_mouse, x_cur, y_cur);
1503                                                 setCursorPosition(x_cur, y_cur); /* wrap */
1504                                                 
1505                                                 //Post event
1506                                                 window->getCursorGrabInitPos(x_cur, y_cur);
1507                                                 pushEvent(new GHOST_EventCursor([event timestamp]*1000, GHOST_kEventCursorMove, window, x_cur + x_accum, y_cur + y_accum));
1508                                         }
1509                                                 break;
1510                                         default:
1511                                         {
1512                                                 //Normal cursor operation: send mouse position in window
1513                                                 NSPoint mousePos = [event locationInWindow];
1514                                                 pushEvent(new GHOST_EventCursor([event timestamp]*1000, GHOST_kEventCursorMove, window, mousePos.x, mousePos.y));
1515                                                 m_cursorDelta_x=0;
1516                                                 m_cursorDelta_y=0; //Mouse motion occured between two cursor warps, so we can reset the delta counter
1517                                         }
1518                                                 break;
1519                                 }
1520                                 break;
1521                         
1522                 case NSScrollWheel:
1523                         {
1524                                 /* Send trackpad event if inside a trackpad gesture, send wheel event otherwise */
1525                                 if (!m_hasMultiTouchTrackpad || !m_isGestureInProgress) {
1526                                         GHOST_TInt32 delta;
1527                                         
1528                                         double deltaF = [event deltaY];
1529                                         if (deltaF == 0.0) break; //discard trackpad delta=0 events
1530                                         
1531                                         delta = deltaF > 0.0 ? 1 : -1;
1532                                         pushEvent(new GHOST_EventWheel([event timestamp]*1000, window, delta));
1533                                 }
1534                                 else {
1535                                         NSPoint mousePos = [event locationInWindow];
1536                                         double dx = [event deltaX];
1537                                         double dy = -[event deltaY];
1538                                         
1539                                         const double deltaMax = 50.0;
1540                                         
1541                                         if ((dx == 0) && (dy == 0)) break;
1542                                         
1543                                         /* Quadratic acceleration */
1544                                         dx = dx*(fabs(dx)+0.5);
1545                                         if (dx<0.0) dx-=0.5; else dx+=0.5;
1546                                         if (dx< -deltaMax) dx= -deltaMax; else if (dx>deltaMax) dx=deltaMax;
1547                                         
1548                                         dy = dy*(fabs(dy)+0.5);
1549                                         if (dy<0.0) dy-=0.5; else dy+=0.5;
1550                                         if (dy< -deltaMax) dy= -deltaMax; else if (dy>deltaMax) dy=deltaMax;
1551
1552                                         pushEvent(new GHOST_EventTrackpad([event timestamp]*1000, window, GHOST_kTrackpadEventScroll, mousePos.x, mousePos.y, dx, dy));
1553                                 }
1554                         }
1555                         break;
1556                         
1557                 case NSEventTypeMagnify:
1558                         {
1559                                 NSPoint mousePos = [event locationInWindow];
1560                                 pushEvent(new GHOST_EventTrackpad([event timestamp]*1000, window, GHOST_kTrackpadEventMagnify, mousePos.x, mousePos.y,
1561                                                                                                   [event magnification]*250.0 + 0.1, 0));
1562                         }
1563                         break;
1564
1565                 case NSEventTypeRotate:
1566                         {
1567                                 NSPoint mousePos = [event locationInWindow];
1568                                 pushEvent(new GHOST_EventTrackpad([event timestamp]*1000, window, GHOST_kTrackpadEventRotate, mousePos.x, mousePos.y,
1569                                                                                                   -[event rotation] * 5.0, 0));
1570                         }
1571                 case NSEventTypeBeginGesture:
1572                         m_isGestureInProgress = true;
1573                         break;
1574                 case NSEventTypeEndGesture:
1575                         m_isGestureInProgress = false;
1576                         break;
1577                 default:
1578                         return GHOST_kFailure;
1579                         break;
1580                 }
1581         
1582         return GHOST_kSuccess;
1583 }
1584
1585
1586 GHOST_TSuccess GHOST_SystemCocoa::handleKeyEvent(void *eventPtr)
1587 {
1588         NSEvent *event = (NSEvent *)eventPtr;
1589         GHOST_IWindow* window = m_windowManager->getActiveWindow();
1590         unsigned int modifiers;
1591         NSString *characters;
1592         NSData *convertedCharacters;
1593         GHOST_TKey keyCode;
1594         unsigned char ascii;
1595         NSString* charsIgnoringModifiers;
1596
1597         if (!window) {
1598                 /* If no active window found, still tries to find the window associated with the event
1599                  This may happen when Cocoa continues to send some events after the window deactivate one */
1600                 window = m_windowManager->getWindowAssociatedWithOSWindow((void*)[event window]);
1601                 if (!window) {
1602                         //printf("\nW failure for event 0x%x",[event type]);
1603                         return GHOST_kFailure;
1604                 }
1605         }
1606         
1607         switch ([event type]) {
1608                 case NSKeyDown:
1609                 case NSKeyUp:
1610                         charsIgnoringModifiers = [event charactersIgnoringModifiers];
1611                         if ([charsIgnoringModifiers length]>0)
1612                                 keyCode = convertKey([event keyCode],
1613                                                                          [charsIgnoringModifiers characterAtIndex:0]);
1614                         else
1615                                 keyCode = convertKey([event keyCode],0);
1616
1617                                 
1618                         characters = [event characters];
1619                         if ([characters length]>0) { //Check for dead keys
1620                                 //Convert characters to iso latin 1 encoding
1621                                 convertedCharacters = [characters dataUsingEncoding:NSISOLatin1StringEncoding];
1622                                 if ([convertedCharacters length]>0)
1623                                         ascii =((char*)[convertedCharacters bytes])[0];
1624                                 else
1625                                         ascii = 0; //Character not available in iso latin 1 encoding
1626                         }
1627                         else
1628                                 ascii= 0;
1629                         
1630                         if ((keyCode == GHOST_kKeyQ) && (m_modifierMask & NSCommandKeyMask))
1631                                 break; //Cmd-Q is directly handled by Cocoa
1632
1633                         if ([event type] == NSKeyDown) {
1634                                 pushEvent( new GHOST_EventKey([event timestamp]*1000, GHOST_kEventKeyDown, window, keyCode, ascii) );
1635                                 //printf("\nKey pressed keyCode=%u ascii=%i %c",keyCode,ascii,ascii);
1636                         } else {
1637                                 pushEvent( new GHOST_EventKey([event timestamp]*1000, GHOST_kEventKeyUp, window, keyCode, ascii) );
1638                         }
1639                         break;
1640         
1641                 case NSFlagsChanged: 
1642                         modifiers = [event modifierFlags];
1643                         
1644                         if ((modifiers & NSShiftKeyMask) != (m_modifierMask & NSShiftKeyMask)) {
1645                                 pushEvent( new GHOST_EventKey([event timestamp]*1000, (modifiers & NSShiftKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftShift) );
1646                         }
1647                         if ((modifiers & NSControlKeyMask) != (m_modifierMask & NSControlKeyMask)) {
1648                                 pushEvent( new GHOST_EventKey([event timestamp]*1000, (modifiers & NSControlKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftControl) );
1649                         }
1650                         if ((modifiers & NSAlternateKeyMask) != (m_modifierMask & NSAlternateKeyMask)) {
1651                                 pushEvent( new GHOST_EventKey([event timestamp]*1000, (modifiers & NSAlternateKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftAlt) );
1652                         }
1653                         if ((modifiers & NSCommandKeyMask) != (m_modifierMask & NSCommandKeyMask)) {
1654                                 pushEvent( new GHOST_EventKey([event timestamp]*1000, (modifiers & NSCommandKeyMask)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyCommand) );
1655                         }
1656                         
1657                         m_modifierMask = modifiers;
1658                         break;
1659                         
1660                 default:
1661                         return GHOST_kFailure;
1662                         break;
1663         }
1664         
1665         return GHOST_kSuccess;
1666 }
1667
1668
1669
1670 #pragma mark Clipboard get/set
1671
1672 GHOST_TUns8* GHOST_SystemCocoa::getClipboard(bool selection) const
1673 {
1674         GHOST_TUns8 * temp_buff;
1675         size_t pastedTextSize;  
1676         
1677         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1678         
1679         NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
1680         
1681         if (pasteBoard == nil) {
1682                 [pool drain];
1683                 return NULL;
1684         }
1685         
1686         NSArray *supportedTypes =
1687                 [NSArray arrayWithObjects: NSStringPboardType, nil];
1688         
1689         NSString *bestType = [[NSPasteboard generalPasteboard]
1690                                                   availableTypeFromArray:supportedTypes];
1691         
1692         if (bestType == nil) {
1693                 [pool drain];
1694                 return NULL;
1695         }
1696         
1697         NSString * textPasted = [pasteBoard stringForType:NSStringPboardType];
1698
1699         if (textPasted == nil) {
1700                 [pool drain];
1701                 return NULL;
1702         }
1703         
1704         pastedTextSize = [textPasted lengthOfBytesUsingEncoding:NSISOLatin1StringEncoding];
1705         
1706         temp_buff = (GHOST_TUns8*) malloc(pastedTextSize+1); 
1707
1708         if (temp_buff == NULL) {
1709                 [pool drain];
1710                 return NULL;
1711         }
1712         
1713         strncpy((char*)temp_buff, [textPasted cStringUsingEncoding:NSISOLatin1StringEncoding], pastedTextSize);
1714         
1715         temp_buff[pastedTextSize] = '\0';
1716         
1717         [pool drain];
1718
1719         if(temp_buff) {
1720                 return temp_buff;
1721         } else {
1722                 return NULL;
1723         }
1724 }
1725
1726 void GHOST_SystemCocoa::putClipboard(GHOST_TInt8 *buffer, bool selection) const
1727 {
1728         NSString *textToCopy;
1729         
1730         if(selection) {return;} // for copying the selection, used on X11
1731
1732         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
1733                 
1734         NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
1735         
1736         if (pasteBoard == nil) {
1737                 [pool drain];
1738                 return;
1739         }
1740         
1741         NSArray *supportedTypes = [NSArray arrayWithObject:NSStringPboardType];
1742         
1743         [pasteBoard declareTypes:supportedTypes owner:nil];
1744         
1745         textToCopy = [NSString stringWithCString:buffer encoding:NSISOLatin1StringEncoding];
1746         
1747         [pasteBoard setString:textToCopy forType:NSStringPboardType];
1748         
1749         [pool drain];
1750 }