Fixed a stupid bug when exporting meshes with empty material slots.
[blender.git] / intern / ghost / intern / GHOST_SystemCarbon.cpp
1 /**
2  * $Id$
3  * ***** BEGIN GPL/BL DUAL 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. The Blender
9  * Foundation also sells licenses for use in proprietary software under
10  * the Blender License.  See http://www.blender.org/BL/ for information
11  * about this.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21  *
22  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
23  * All rights reserved.
24  *
25  * The Original Code is: all of this file.
26  *
27  * Contributor(s): none yet.
28  *
29  * ***** END GPL/BL DUAL LICENSE BLOCK *****
30  */
31
32 /**
33
34  * $Id$
35  * Copyright (C) 2001 NaN Technologies B.V.
36  * @author      Maarten Gribnau
37  * @date        May 7, 2001
38  */
39
40 #ifdef HAVE_CONFIG_H
41 #include <config.h>
42 #endif
43
44 #include "GHOST_SystemCarbon.h"
45
46 #include "GHOST_DisplayManagerCarbon.h"
47 #include "GHOST_EventKey.h"
48 #include "GHOST_EventButton.h"
49 #include "GHOST_EventCursor.h"
50 #include "GHOST_EventWheel.h"
51 #include "GHOST_TimerManager.h"
52 #include "GHOST_TimerTask.h"
53 #include "GHOST_WindowManager.h"
54 #include "GHOST_WindowCarbon.h"
55
56 #define GHOST_KEY_SWITCH(mac, ghost) { case (mac): ghostKey = (ghost); break; }
57
58 const EventTypeSpec     kEvents[] =
59 {
60         { kEventClassAppleEvent, kEventAppleEvent },
61         
62 /*
63         { kEventClassApplication, kEventAppActivated },
64         { kEventClassApplication, kEventAppDeactivated },
65 */      
66
67         { kEventClassKeyboard, kEventRawKeyDown },
68         { kEventClassKeyboard, kEventRawKeyRepeat },
69         { kEventClassKeyboard, kEventRawKeyUp },
70         { kEventClassKeyboard, kEventRawKeyModifiersChanged },
71         
72         { kEventClassMouse, kEventMouseDown },
73         { kEventClassMouse, kEventMouseUp },
74         { kEventClassMouse, kEventMouseMoved },
75         { kEventClassMouse, kEventMouseDragged },
76         { kEventClassMouse, kEventMouseWheelMoved },
77         
78         { kEventClassWindow, kEventWindowClickZoomRgn } ,  /* for new zoom behaviour */ 
79         { kEventClassWindow, kEventWindowZoom },  /* for new zoom behaviour */ 
80         { kEventClassWindow, kEventWindowExpand } ,  /* for new zoom behaviour */ 
81         { kEventClassWindow, kEventWindowExpandAll },  /* for new zoom behaviour */ 
82
83         { kEventClassWindow, kEventWindowClose },
84         { kEventClassWindow, kEventWindowActivated },
85         { kEventClassWindow, kEventWindowDeactivated },
86         { kEventClassWindow, kEventWindowUpdate },
87         { kEventClassWindow, kEventWindowBoundsChanged }
88         
89 };
90
91
92
93 static GHOST_TButtonMask convertButton(EventMouseButton button)
94 {
95         switch (button) {
96         case kEventMouseButtonPrimary:
97                 return GHOST_kButtonMaskLeft;
98         case kEventMouseButtonSecondary:
99                 return GHOST_kButtonMaskRight;
100         case kEventMouseButtonTertiary:
101         default:
102                 return GHOST_kButtonMaskMiddle;
103         }
104 }
105
106 static GHOST_TKey convertKey(int rawCode) 
107 {       
108                 /* This bit of magic converts the rawCode into a virtual
109                  * Mac key based on the current keyboard mapping, but
110                  * without regard to the modifiers (so we don't get 'a' 
111                  * and 'A' for example.
112                  */
113         static UInt32 dummy= 0;
114         Handle transData = (Handle) GetScriptManagerVariable(smKCHRCache);
115         unsigned char vk = KeyTranslate(transData, rawCode, &dummy);    
116                 /* Map numpad based on rawcodes first, otherwise they
117                  * look like non-numpad events.
118                  * Added too: mapping the number keys, for french keyboards etc (ton)
119                  */
120         // printf("GHOST: vk: %d %c raw: %d\n", vk, vk, rawCode);
121                  
122         switch (rawCode) {
123         case 18:        return GHOST_kKey1;
124         case 19:        return GHOST_kKey2;
125         case 20:        return GHOST_kKey3;
126         case 21:        return GHOST_kKey4;
127         case 23:        return GHOST_kKey5;
128         case 22:        return GHOST_kKey6;
129         case 26:        return GHOST_kKey7;
130         case 28:        return GHOST_kKey8;
131         case 25:        return GHOST_kKey9;
132         case 29:        return GHOST_kKey0;
133         
134         case 82:        return GHOST_kKeyNumpad0;
135         case 83:        return GHOST_kKeyNumpad1;
136         case 84:        return GHOST_kKeyNumpad2;
137         case 85:        return GHOST_kKeyNumpad3;
138         case 86:        return GHOST_kKeyNumpad4;
139         case 87:        return GHOST_kKeyNumpad5;
140         case 88:        return GHOST_kKeyNumpad6;
141         case 89:        return GHOST_kKeyNumpad7;
142         case 91:        return GHOST_kKeyNumpad8;
143         case 92:        return GHOST_kKeyNumpad9;
144         case 65:        return GHOST_kKeyNumpadPeriod;
145         case 76:        return GHOST_kKeyNumpadEnter;
146         case 69:        return GHOST_kKeyNumpadPlus;
147         case 78:        return GHOST_kKeyNumpadMinus;
148         case 67:        return GHOST_kKeyNumpadAsterisk;
149         case 75:        return GHOST_kKeyNumpadSlash;
150         }
151         
152         if ((vk >= 'a') && (vk <= 'z')) {
153                 return (GHOST_TKey) (vk - 'a' + GHOST_kKeyA);
154         } else if ((vk >= '0') && (vk <= '9')) {
155                 return (GHOST_TKey) (vk - '0' + GHOST_kKey0);
156         } else if (vk==16) {
157                 switch (rawCode) {
158                 case 122:       return GHOST_kKeyF1;
159                 case 120:       return GHOST_kKeyF2;
160                 case 99:        return GHOST_kKeyF3;
161                 case 118:       return GHOST_kKeyF4;
162                 case 96:        return GHOST_kKeyF5;
163                 case 97:        return GHOST_kKeyF6;
164                 case 98:        return GHOST_kKeyF7;
165                 case 100:       return GHOST_kKeyF8;
166                 case 101:       return GHOST_kKeyF9;
167                 case 109:       return GHOST_kKeyF10;
168                 case 103:       return GHOST_kKeyF11;
169                 case 111:       return GHOST_kKeyF12;  // Never get, is used for ejecting the CD! 
170                 }
171         } else {
172                 switch (vk) {
173                 case kUpArrowCharCode:          return GHOST_kKeyUpArrow;
174                 case kDownArrowCharCode:        return GHOST_kKeyDownArrow;
175                 case kLeftArrowCharCode:        return GHOST_kKeyLeftArrow;
176                 case kRightArrowCharCode:       return GHOST_kKeyRightArrow;
177
178                 case kReturnCharCode:           return GHOST_kKeyEnter;
179                 case kBackspaceCharCode:        return GHOST_kKeyBackSpace;
180                 case kDeleteCharCode:           return GHOST_kKeyDelete;
181                 case kEscapeCharCode:           return GHOST_kKeyEsc;
182                 case kTabCharCode:                      return GHOST_kKeyTab;
183                 case kSpaceCharCode:            return GHOST_kKeySpace;
184
185                 case kHomeCharCode:             return GHOST_kKeyHome;
186                 case kEndCharCode:                      return GHOST_kKeyEnd;
187                 case kPageUpCharCode:           return GHOST_kKeyUpPage;
188                 case kPageDownCharCode:         return GHOST_kKeyDownPage;
189
190                 case '-':       return GHOST_kKeyMinus;
191                 case '=':       return GHOST_kKeyEqual;
192                 case ',':       return GHOST_kKeyComma;
193                 case '.':       return GHOST_kKeyPeriod;
194                 case '/':       return GHOST_kKeySlash;
195                 case ';':       return GHOST_kKeySemicolon;
196                 case '\'':      return GHOST_kKeyQuote;
197                 case '\\':      return GHOST_kKeyBackslash;
198                 case '[':       return GHOST_kKeyLeftBracket;
199                 case ']':       return GHOST_kKeyRightBracket;
200                 case '`':       return GHOST_kKeyAccentGrave;
201                 }
202         }
203         
204         // printf("GHOST: unknown key: %d %d\n", vk, rawCode);
205         
206         return GHOST_kKeyUnknown;
207 }
208
209 /* MacOSX returns a Roman charset with kEventParamKeyMacCharCodes
210  * as defined here: http://developer.apple.com/documentation/mac/Text/Text-516.html
211  * I am not sure how international this works...
212  * For cross-platform convention, we'll use the Latin ascii set instead.
213  * As defined at: http://www.ramsch.org/martin/uni/fmi-hp/iso8859-1.html
214  * 
215  */
216 static unsigned char convertRomanToLatin(unsigned char ascii)
217 {
218
219         if(ascii<128) return ascii;
220         
221         switch(ascii) {
222         case 128:       return 142;
223         case 129:       return 143;
224         case 130:       return 128;
225         case 131:       return 201;
226         case 132:       return 209;
227         case 133:       return 214;
228         case 134:       return 220;
229         case 135:       return 225;
230         case 136:       return 224;
231         case 137:       return 226;
232         case 138:       return 228;
233         case 139:       return 227;
234         case 140:       return 229;
235         case 141:       return 231;
236         case 142:       return 233;
237         case 143:       return 232;
238         case 144:       return 234;
239         case 145:       return 235;
240         case 146:       return 237;
241         case 147:       return 236;
242         case 148:       return 238;
243         case 149:       return 239;
244         case 150:       return 241;
245         case 151:       return 243;
246         case 152:       return 242;
247         case 153:       return 244;
248         case 154:       return 246;
249         case 155:       return 245;
250         case 156:       return 250;
251         case 157:       return 249;
252         case 158:       return 251;
253         case 159:       return 252;
254         case 160:       return 0;
255         case 161:       return 176;
256         case 162:       return 162;
257         case 163:       return 163;
258         case 164:       return 167;
259         case 165:       return 183;
260         case 166:       return 182;
261         case 167:       return 223;
262         case 168:       return 174;
263         case 169:       return 169;
264         case 170:       return 174;
265         case 171:       return 180;
266         case 172:       return 168;
267         case 173:       return 0;
268         case 174:       return 198;
269         case 175:       return 216;
270         case 176:       return 0;
271         case 177:       return 177;
272         case 178:       return 0;
273         case 179:       return 0;
274         case 180:       return 165;
275         case 181:       return 181;
276         case 182:       return 0;
277         case 183:       return 0;
278         case 184:       return 215;
279         case 185:       return 0;
280         case 186:       return 0;
281         case 187:       return 170;
282         case 188:       return 186;
283         case 189:       return 0;
284         case 190:       return 230;
285         case 191:       return 248;
286         case 192:       return 191;
287         case 193:       return 161;
288         case 194:       return 172;
289         case 195:       return 0;
290         case 196:       return 0;
291         case 197:       return 0;
292         case 198:       return 0;
293         case 199:       return 171;
294         case 200:       return 187;
295         case 201:       return 201;
296         case 202:       return 0;
297         case 203:       return 192;
298         case 204:       return 195;
299         case 205:       return 213;
300         case 206:       return 0;
301         case 207:       return 0;
302         case 208:       return 0;
303         case 209:       return 0;
304         case 210:       return 0;
305         
306         case 214:       return 247;
307
308         case 229:       return 194;
309         case 230:       return 202;
310         case 231:       return 193;
311         case 232:       return 203;
312         case 233:       return 200;
313         case 234:       return 205;
314         case 235:       return 206;
315         case 236:       return 207;
316         case 237:       return 204;
317         case 238:       return 211;
318         case 239:       return 212;
319         case 240:       return 0;
320         case 241:       return 210;
321         case 242:       return 218;
322         case 243:       return 219;
323         case 244:       return 217;
324         case 245:       return 0;
325         case 246:       return 0;
326         case 247:       return 0;
327         case 248:       return 0;
328         case 249:       return 0;
329         case 250:       return 0;
330
331         
332                 default: return 0;
333         }
334
335 }
336
337
338 /***/
339
340 GHOST_SystemCarbon::GHOST_SystemCarbon() :
341         m_modifierMask(0)
342 {
343         m_displayManager = new GHOST_DisplayManagerCarbon ();
344         GHOST_ASSERT(m_displayManager, "GHOST_SystemCarbon::GHOST_SystemCarbon(): m_displayManager==0\n");
345         m_displayManager->initialize();
346
347         UnsignedWide micros;
348         ::Microseconds(&micros);
349         m_start_time = UnsignedWideToUInt64(micros)/1000;
350         m_ignoreWindowSizedMessages = false;
351 }
352
353 GHOST_SystemCarbon::~GHOST_SystemCarbon()
354 {
355 }
356
357
358 GHOST_TUns64 GHOST_SystemCarbon::getMilliSeconds() const
359 {
360         UnsignedWide micros;
361         ::Microseconds(&micros);
362         UInt64 millis;
363         millis = UnsignedWideToUInt64(micros);
364         return (millis / 1000) - m_start_time;
365 }
366
367
368 GHOST_TUns8 GHOST_SystemCarbon::getNumDisplays() const
369 {
370         // We do not support multiple monitors at the moment
371         return 1;
372 }
373
374
375 void GHOST_SystemCarbon::getMainDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const
376 {
377         BitMap screenBits;
378     Rect bnds = GetQDGlobalsScreenBits(&screenBits)->bounds;
379         width = bnds.right - bnds.left;
380         height = bnds.bottom - bnds.top;
381 }
382
383
384 GHOST_IWindow* GHOST_SystemCarbon::createWindow(
385         const STR_String& title, 
386         GHOST_TInt32 left,
387         GHOST_TInt32 top,
388         GHOST_TUns32 width,
389         GHOST_TUns32 height,
390         GHOST_TWindowState state,
391         GHOST_TDrawingContextType type,
392         bool stereoVisual
393 )
394 {
395     GHOST_IWindow* window = 0;
396
397         window = new GHOST_WindowCarbon (title, left, top, width, height, state, type);
398
399     if (window) {
400         if (window->getValid()) {
401             // Store the pointer to the window 
402             GHOST_ASSERT(m_windowManager, "m_windowManager not initialized");
403             m_windowManager->addWindow(window);
404             m_windowManager->setActiveWindow(window);
405             pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window));
406         }
407         else {
408                         GHOST_PRINT("GHOST_SystemCarbon::createWindow(): window invalid\n");
409             delete window;
410             window = 0;
411         }
412     }
413         else {
414                 GHOST_PRINT("GHOST_SystemCarbon::createWindow(): could not create window\n");
415         }
416     return window;
417 }
418
419
420 bool GHOST_SystemCarbon::processEvents(bool waitForEvent)
421 {
422         bool anyProcessed = false;
423         EventRef event;
424         
425         do {
426                 GHOST_TimerManager* timerMgr = getTimerManager();
427
428                 if (waitForEvent) {
429                         GHOST_TUns64 curtime = getMilliSeconds();
430                         GHOST_TUns64 next = timerMgr->nextFireTime();
431                         double timeOut;
432                         
433                         if (next == GHOST_kFireTimeNever) {
434                                 timeOut = kEventDurationForever;
435                         } else {
436                                 if (next<=curtime)
437                                         timeOut = 0.0;
438                                 else
439                                         timeOut = (double) (next - getMilliSeconds())/1000.0;
440                         }
441                         
442                         ::ReceiveNextEvent(0, NULL, timeOut, false, &event);
443                 }
444                 
445                 if (timerMgr->fireTimers(getMilliSeconds())) {
446                         anyProcessed = true;
447                 }
448
449                 if (getFullScreen()) {
450                         // Check if the full-screen window is dirty
451                         GHOST_IWindow* window = m_windowManager->getFullScreenWindow();
452                         if (((GHOST_WindowCarbon*)window)->getFullScreenDirty()) {
453                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window) );
454                                 anyProcessed = true;
455                         }
456                 }
457
458                 while (::ReceiveNextEvent(0, NULL, 0, true, &event)==noErr) {
459                         OSStatus status= ::SendEventToEventTarget(event, ::GetEventDispatcherTarget());
460                         if (status==noErr) {
461                                 anyProcessed = true;
462                         } else {
463                                 UInt32 i= ::GetEventClass(event);
464                                 
465                                         /* Ignore 'cgs ' class, no documentation on what they
466                                          * are, but we get a lot of them
467                                          */
468                                 if (i!='cgs ') {
469                                         //printf("Missed - Class: '%.4s', Kind: %d\n", &i, ::GetEventKind(event));
470                                 }
471                         }
472                         ::ReleaseEvent(event);
473                 }
474         } while (waitForEvent && !anyProcessed);
475         
476     return anyProcessed;
477 }
478         
479
480 GHOST_TSuccess GHOST_SystemCarbon::getCursorPosition(GHOST_TInt32& x, GHOST_TInt32& y) const
481 {
482     Point mouseLoc;
483     // Get the position of the mouse in the active port
484     ::GetGlobalMouse(&mouseLoc);
485     // Convert the coordinates to screen coordinates
486     x = (GHOST_TInt32)mouseLoc.h;
487     y = (GHOST_TInt32)mouseLoc.v;
488     return GHOST_kSuccess;
489 }
490
491
492 GHOST_TSuccess GHOST_SystemCarbon::setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y) const
493 {
494         float xf=(float)x, yf=(float)y;
495
496         CGWarpMouseCursorPosition(CGPointMake(xf, yf));
497         // this call below sends event, but empties other events (like shift)
498         // CGPostMouseEvent(CGPointMake(xf, yf), TRUE, 1, FALSE, 0);
499
500     return GHOST_kSuccess;
501 }
502
503
504 GHOST_TSuccess GHOST_SystemCarbon::getModifierKeys(GHOST_ModifierKeys& keys) const
505 {
506     UInt32 modifiers = ::GetCurrentKeyModifiers();
507
508     keys.set(GHOST_kModifierKeyCommand, (modifiers & cmdKey) ? true : false);
509     keys.set(GHOST_kModifierKeyLeftAlt, (modifiers & optionKey) ? true : false);
510     keys.set(GHOST_kModifierKeyLeftShift, (modifiers & shiftKey) ? true : false);
511     keys.set(GHOST_kModifierKeyLeftControl, (modifiers & controlKey) ? true : false);
512         
513     return GHOST_kSuccess;
514 }
515
516         /* XXX, incorrect for multibutton mice */
517 GHOST_TSuccess GHOST_SystemCarbon::getButtons(GHOST_Buttons& buttons) const
518 {
519     Boolean theOnlyButtonIsDown = ::Button();
520     buttons.clear();
521     buttons.set(GHOST_kButtonMaskLeft, theOnlyButtonIsDown);
522     return GHOST_kSuccess;
523 }
524
525 static bool g_hasFirstFile = false;
526 static char g_firstFileBuf[512];
527
528 extern "C" int GHOST_HACK_getFirstFile(char buf[512]) { 
529         if (g_hasFirstFile) {
530                 strcpy(buf, g_firstFileBuf);
531                 return 1;
532         } else {
533                 return 0; 
534         }
535 }
536
537 OSErr GHOST_SystemCarbon::sAEHandlerLaunch(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
538 {
539         //GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
540         
541         return noErr;
542 }
543
544 OSErr GHOST_SystemCarbon::sAEHandlerOpenDocs(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
545 {
546         //GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
547         AEDescList docs;
548         SInt32 ndocs;
549         OSErr err;
550
551         err = AEGetParamDesc(event, keyDirectObject, typeAEList, &docs);
552         if (err != noErr)  return err;
553
554         err = AECountItems(&docs, &ndocs);
555         if (err==noErr) {
556                 int i;
557         
558                 for (i=0; i<ndocs; i++) {
559                         FSSpec fss;
560                         AEKeyword kwd;
561                         DescType actType;
562                         Size actSize;
563                 
564                         err = AEGetNthPtr(&docs, i+1, typeFSS, &kwd, &actType, &fss, sizeof(fss), &actSize);
565                         if (err!=noErr)
566                                 break;
567                 
568                         if (i==0) {
569                                 FSRef fsref;
570                                 
571                                 if (FSpMakeFSRef(&fss, &fsref)!=noErr)
572                                         break;
573                                 if (FSRefMakePath(&fsref, (UInt8*) g_firstFileBuf, sizeof(g_firstFileBuf))!=noErr)
574                                         break;
575
576                                 g_hasFirstFile = true;
577                         }
578                 }
579         }
580         
581         AEDisposeDesc(&docs);
582         
583         return err;
584 }
585
586 OSErr GHOST_SystemCarbon::sAEHandlerPrintDocs(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
587 {
588         //GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
589         
590         return noErr;
591 }
592
593 OSErr GHOST_SystemCarbon::sAEHandlerQuit(const AppleEvent *event, AppleEvent *reply, SInt32 refCon)
594 {
595         GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) refCon;
596         
597         sys->pushEvent( new GHOST_Event(sys->getMilliSeconds(), GHOST_kEventQuit, NULL) );
598         
599         return noErr;
600 }
601
602
603 GHOST_TSuccess GHOST_SystemCarbon::init()
604 {
605     GHOST_TSuccess success = GHOST_System::init();
606     if (success) {
607                 /*
608          * Initialize the cursor to the standard arrow shape (so that we can change it later on).
609          * This initializes the cursor's visibility counter to 0.
610          */
611         ::InitCursor();
612
613                 MenuRef windMenu;
614                 ::CreateStandardWindowMenu(0, &windMenu);
615                 ::InsertMenu(windMenu, 0);
616                 ::DrawMenuBar();
617
618         ::InstallApplicationEventHandler(sEventHandlerProc, GetEventTypeCount(kEvents), kEvents, this, &m_handler);
619                 
620                 ::AEInstallEventHandler(kCoreEventClass, kAEOpenApplication, sAEHandlerLaunch, (SInt32) this, false);
621                 ::AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, sAEHandlerOpenDocs, (SInt32) this, false);
622                 ::AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments, sAEHandlerPrintDocs, (SInt32) this, false);
623                 ::AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, sAEHandlerQuit, (SInt32) this, false);
624     }
625     return success;
626 }
627
628
629 GHOST_TSuccess GHOST_SystemCarbon::exit()
630 {
631     return GHOST_System::exit();
632 }
633
634
635 OSStatus GHOST_SystemCarbon::handleWindowEvent(EventRef event)
636 {
637         WindowRef windowRef;
638         GHOST_WindowCarbon *window;
639         OSStatus err = eventNotHandledErr;
640         
641         // Check if the event was send to a GHOST window
642         ::GetEventParameter(event, kEventParamDirectObject, typeWindowRef, NULL, sizeof(WindowRef), NULL, &windowRef);
643         window = (GHOST_WindowCarbon*) ::GetWRefCon(windowRef);
644         if (!validWindow(window)) {
645                 return err;
646         }
647
648         //if (!getFullScreen()) {
649                 err = noErr;
650                 switch(::GetEventKind(event)) 
651                 {
652                         case kEventWindowClose:
653                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, window) );
654                                 break;
655                         case kEventWindowActivated:
656                                 m_windowManager->setActiveWindow(window);
657                                 window->loadCursor(window->getCursorVisibility(), window->getCursorShape());
658                                 window->updateDrawingContext();
659                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window) );
660                                 break;
661                         case kEventWindowDeactivated:
662                                 m_windowManager->setWindowInactive(window);
663                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowDeactivate, window) );
664                                 break;
665                         case kEventWindowUpdate:
666                                 //if (getFullScreen()) GHOST_PRINT("GHOST_SystemCarbon::handleWindowEvent(): full-screen update event\n");
667                                 window->updateDrawingContext();
668                                 pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window) );
669                                 break;
670                         case kEventWindowBoundsChanged:
671                                 if (!m_ignoreWindowSizedMessages)
672                                 {
673                                         window->updateDrawingContext();
674                                         pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window) );
675                                 }
676                                 break;
677                         default:
678                                 err = eventNotHandledErr;
679                                 break;
680                 }
681 //      }
682         //else {
683                 //window = (GHOST_WindowCarbon*) m_windowManager->getFullScreenWindow();
684                 //GHOST_PRINT("GHOST_SystemCarbon::handleWindowEvent(): full-screen window event, " << window << "\n");
685                 //::RemoveEventFromQueue(::GetMainEventQueue(), event);
686         //}
687         
688         return err;
689 }
690
691 OSStatus GHOST_SystemCarbon::handleMouseEvent(EventRef event)
692 {
693     OSStatus err = eventNotHandledErr;
694         GHOST_IWindow* window = m_windowManager->getActiveWindow();
695         UInt32 kind = ::GetEventKind(event);
696                         
697         switch (kind)
698     {
699                 case kEventMouseDown:
700                 case kEventMouseUp:
701                         // Handle Mac application responsibilities
702                         if ((kind == kEventMouseDown) && handleMouseDown(event)) {
703                                 err = noErr;
704                         }
705                         else {
706                                 GHOST_TEventType type = (kind == kEventMouseDown) ? GHOST_kEventButtonDown : GHOST_kEventButtonUp;
707                                 EventMouseButton button;
708                                 
709                                 /* Window still gets mouse up after command-H */
710                                 if (m_windowManager->getActiveWindow()) {
711                                         ::GetEventParameter(event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(button), NULL, &button);
712                                         pushEvent(new GHOST_EventButton(getMilliSeconds(), type, window, convertButton(button)));
713                                         err = noErr;
714                                 }
715                         }
716             break;
717                         
718                 case kEventMouseMoved:
719         case kEventMouseDragged:
720                         Point mousePos;
721                         if (window) {
722                                 ::GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(Point), NULL, &mousePos);
723                                 pushEvent(new GHOST_EventCursor(getMilliSeconds(), GHOST_kEventCursorMove, window, mousePos.h, mousePos.v));
724                                 err = noErr;
725                         }
726             break;
727
728                 case kEventMouseWheelMoved:
729                         {
730                                 OSStatus status;
731                                 //UInt32 modifiers;
732                                 EventMouseWheelAxis axis;
733                                 SInt32 delta;
734                                 //status = ::GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(modifiers), NULL, &modifiers);
735                                 //GHOST_ASSERT(status == noErr, "GHOST_SystemCarbon::handleMouseEvent(): GetEventParameter() failed");
736                                 status = ::GetEventParameter(event, kEventParamMouseWheelAxis, typeMouseWheelAxis, NULL, sizeof(axis), NULL, &axis);
737                                 GHOST_ASSERT(status == noErr, "GHOST_SystemCarbon::handleMouseEvent(): GetEventParameter() failed");
738                                 if (axis == kEventMouseWheelAxisY)
739                                 {
740                                         status = ::GetEventParameter(event, kEventParamMouseWheelDelta, typeLongInteger, NULL, sizeof(delta), NULL, &delta);
741                                         GHOST_ASSERT(status == noErr, "GHOST_SystemCarbon::handleMouseEvent(): GetEventParameter() failed");
742                                         /*
743                                          * Limit mouse wheel delta to plus and minus one.
744                                          */
745                                         delta = delta > 0 ? 1 : -1;
746                                         pushEvent(new GHOST_EventWheel(getMilliSeconds(), window, delta));
747                                         err = noErr;
748                                 }
749                         }
750                         break;
751                 }
752         
753         return err;
754 }
755
756
757 OSStatus GHOST_SystemCarbon::handleKeyEvent(EventRef event)
758 {
759     OSStatus err = eventNotHandledErr;
760         GHOST_IWindow* window = m_windowManager->getActiveWindow();
761         UInt32 kind = ::GetEventKind(event);
762         UInt32 modifiers;
763         UInt32 rawCode;
764         GHOST_TKey key;
765         unsigned char ascii;
766
767         /* Can happen, very rarely - seems to only be when command-H makes
768          * the window go away and we still get an HKey up. 
769          */
770         if (!window) {
771                 //::GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &rawCode);
772                 //key = convertKey(rawCode);
773                 return err;
774         }
775         
776         err = noErr;
777         switch (kind) {
778                 case kEventRawKeyDown: 
779                 case kEventRawKeyRepeat: 
780                 case kEventRawKeyUp: 
781                         ::GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &rawCode);
782                         ::GetEventParameter(event, kEventParamKeyMacCharCodes, typeChar, NULL, sizeof(char), NULL, &ascii);
783         
784                         key = convertKey(rawCode);
785                         ascii= convertRomanToLatin(ascii);
786                         
787         //              if (key!=GHOST_kKeyUnknown) {
788                                 GHOST_TEventType type;
789                                 if (kind == kEventRawKeyDown) {
790                                         type = GHOST_kEventKeyDown;
791                                 } else if (kind == kEventRawKeyRepeat) { 
792                                         type = GHOST_kEventKeyDown;  /* XXX, fixme */
793                                 } else {
794                                         type = GHOST_kEventKeyUp;
795                                 }
796                                 pushEvent( new GHOST_EventKey( getMilliSeconds(), type, window, key, ascii) );
797 //                      }
798                         break;
799         
800                 case kEventRawKeyModifiersChanged: 
801                                 /* ugh */
802                         ::GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers);
803                         if ((modifiers & shiftKey) != (m_modifierMask & shiftKey)) {
804                                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & shiftKey)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftShift) );
805                         }
806                         if ((modifiers & controlKey) != (m_modifierMask & controlKey)) {
807                                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & controlKey)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftControl) );
808                         }
809                         if ((modifiers & optionKey) != (m_modifierMask & optionKey)) {
810                                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & optionKey)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyLeftAlt) );
811                         }
812                         if ((modifiers & cmdKey) != (m_modifierMask & cmdKey)) {
813                                 pushEvent( new GHOST_EventKey(getMilliSeconds(), (modifiers & cmdKey)?GHOST_kEventKeyDown:GHOST_kEventKeyUp, window, GHOST_kKeyCommand) );
814                         }
815                         
816                         m_modifierMask = modifiers;
817                         break;
818                         
819                 default:
820                         err = eventNotHandledErr;
821                         break;
822         }
823         
824         return err;
825 }
826
827
828 bool GHOST_SystemCarbon::handleMouseDown(EventRef event)
829 {
830         WindowPtr                       window;
831         short                           part;
832         BitMap                          screenBits;
833     bool                                handled = true;
834     GHOST_WindowCarbon* ghostWindow;
835     Point                               mousePos = {0 , 0};
836         
837         ::GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(Point), NULL, &mousePos);
838         
839         part = ::FindWindow(mousePos, &window);
840         ghostWindow = (GHOST_WindowCarbon*) ::GetWRefCon(window);
841         
842         switch (part) {
843                 case inMenuBar:
844                         handleMenuCommand(::MenuSelect(mousePos));
845                         break;
846                         
847                 case inDrag:
848                         /*
849                          * The DragWindow() routine creates a lot of kEventWindowBoundsChanged
850                          * events. By setting m_ignoreWindowSizedMessages these are suppressed.
851                          * @see GHOST_SystemCarbon::handleWindowEvent(EventRef event)
852                          */
853                         GHOST_ASSERT(validWindow(ghostWindow), "GHOST_SystemCarbon::handleMouseDown: invalid window");
854                         m_ignoreWindowSizedMessages = true;
855                         ::DragWindow(window, mousePos, &GetQDGlobalsScreenBits(&screenBits)->bounds);
856                         m_ignoreWindowSizedMessages = false;
857                         break;
858                 
859                 case inContent:
860                         if (window != ::FrontWindow()) {
861                                 ::SelectWindow(window);
862                                 /*
863                                  * We add a mouse down event on the newly actived window
864                                  */             
865                                 //GHOST_PRINT("GHOST_SystemCarbon::handleMouseDown(): adding mouse down event, " << ghostWindow << "\n");
866                                 EventMouseButton button;
867                                 ::GetEventParameter(event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(button), NULL, &button);
868                                 pushEvent(new GHOST_EventButton(getMilliSeconds(), GHOST_kEventButtonDown, ghostWindow, convertButton(button)));
869                         } else {
870                                 handled = false;
871                         }
872                         break;
873                         
874                 case inGoAway:
875                         GHOST_ASSERT(ghostWindow, "GHOST_SystemCarbon::handleMouseEvent: ghostWindow==0");
876                         if (::TrackGoAway(window, mousePos))
877                         {
878                                 // todo: add option-close, because itØs in the HIG
879                                 // if (event.modifiers & optionKey) {
880                                         // Close the clean documents, others will be confirmed one by one.
881                                 //}
882                                 // else {
883                                 pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, ghostWindow));
884                                 //}
885                         }
886                         break;
887                         
888                 case inGrow:
889                         GHOST_ASSERT(ghostWindow, "GHOST_SystemCarbon::handleMouseEvent: ghostWindow==0");
890                         ::ResizeWindow(window, mousePos, NULL, NULL);
891                         break;
892                         
893                 case inZoomIn:
894                 case inZoomOut:
895                         GHOST_ASSERT(ghostWindow, "GHOST_SystemCarbon::handleMouseEvent: ghostWindow==0");
896                         if (::TrackBox(window, mousePos, part)) {
897                                 int macState;
898                                 
899                                 macState = ghostWindow->getMac_windowState();
900                                 if ( macState== 0)
901                                         ::ZoomWindow(window, part, true);
902                                 else 
903                                         if (macState == 2) { // always ok
904                                                         ::ZoomWindow(window, part, true);
905                                                         ghostWindow->setMac_windowState(1);
906                                         } else { // need to force size again
907                                         //      GHOST_TUns32 scr_x,scr_y; /*unused*/
908                                                 Rect outAvailableRect;
909                                                 
910                                                 ghostWindow->setMac_windowState(2);
911                                                 ::GetAvailableWindowPositioningBounds ( GetMainDevice(), &outAvailableRect);
912                                                 
913                                                 //this->getMainDisplayDimensions(scr_x,scr_y);
914                                                 ::SizeWindow (window, outAvailableRect.right-outAvailableRect.left,outAvailableRect.bottom-outAvailableRect.top-1,false);
915                                                 ::MoveWindow (window, outAvailableRect.left, outAvailableRect.top,true);
916                                         }
917                                 
918                         }
919                         break;
920
921                 default:
922                         handled = false;
923                         break;
924         }
925         
926         return handled;
927 }
928
929
930 bool GHOST_SystemCarbon::handleMenuCommand(GHOST_TInt32 menuResult)
931 {
932         short           menuID;
933         short           menuItem;
934         UInt32          command;
935         bool            handled;
936         OSErr           err;
937         
938         menuID = HiWord(menuResult);
939         menuItem = LoWord(menuResult);
940
941         err = ::GetMenuItemCommandID(::GetMenuHandle(menuID), menuItem, &command);
942
943         handled = false;
944         
945         if (err || command == 0) {
946         }
947         else {
948                 switch(command) {
949                 }
950         }
951
952         ::HiliteMenu(0);
953     return handled;
954 }
955
956 OSStatus GHOST_SystemCarbon::sEventHandlerProc(EventHandlerCallRef handler, EventRef event, void* userData)
957 {
958         GHOST_SystemCarbon* sys = (GHOST_SystemCarbon*) userData;
959     OSStatus err = eventNotHandledErr;
960
961     switch (::GetEventClass(event))
962     {
963                 case kEventClassAppleEvent:
964                         EventRecord eventrec;
965                         if (ConvertEventRefToEventRecord(event, &eventrec)) {
966                                 err = AEProcessAppleEvent(&eventrec);
967                         }
968                         break;
969         case kEventClassMouse:
970             err = sys->handleMouseEvent(event);
971             break;
972                 case kEventClassWindow:
973                         err = sys->handleWindowEvent(event);
974                         break;
975                 case kEventClassKeyboard:
976                         err = sys->handleKeyEvent(event);
977                         break;
978     }
979
980     return err;
981 }