Make wxEVT_CHAR_HOOK propagate upwards and send it to the window itself.
[wxWidgets.git] / src / osx / cocoa / window.mm
1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/osx/cocoa/window.mm
3 // Purpose:     widgets (non tlw) for cocoa
4 // Author:      Stefan Csomor
5 // Modified by:
6 // Created:     2008-06-20
7 // RCS-ID:      $Id$
8 // Copyright:   (c) Stefan Csomor
9 // Licence:     wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #include "wx/wxprec.h"
13
14 #ifndef WX_PRECOMP
15     #include "wx/dcclient.h"
16     #include "wx/frame.h"
17     #include "wx/log.h"
18     #include "wx/textctrl.h"
19     #include "wx/combobox.h"
20 #endif
21
22 #ifdef __WXMAC__
23     #include "wx/osx/private.h"
24 #endif
25
26 #include "wx/evtloop.h"
27
28 #if wxUSE_CARET
29     #include "wx/caret.h"
30 #endif
31
32 #if wxUSE_DRAG_AND_DROP
33     #include "wx/dnd.h"
34 #endif
35
36 #if wxUSE_TOOLTIPS
37     #include "wx/tooltip.h"
38 #endif
39
40 #include <objc/objc-runtime.h>
41
42 // Get the window with the focus
43
44 NSView* GetViewFromResponder( NSResponder* responder )
45 {
46     NSView* view = nil;
47     if ( [responder isKindOfClass:[NSTextView class]] )
48     {
49         NSView* delegate = (NSView*) [(NSTextView*)responder delegate];
50         if ( [delegate isKindOfClass:[NSTextField class] ] )
51             view = delegate;
52         else
53             view =  (NSView*) responder;
54     }
55     else
56     {
57         if ( [responder isKindOfClass:[NSView class]] )
58             view = (NSView*) responder;
59     }
60     return view;
61 }
62
63 NSView* GetFocusedViewInWindow( NSWindow* keyWindow )
64 {
65     NSView* focusedView = nil;
66     if ( keyWindow != nil )
67         focusedView = GetViewFromResponder([keyWindow firstResponder]);
68
69     return focusedView;
70 }
71
72 WXWidget wxWidgetImpl::FindFocus()
73 {
74     return GetFocusedViewInWindow( [NSApp keyWindow] );
75 }
76
77 NSRect wxOSXGetFrameForControl( wxWindowMac* window , const wxPoint& pos , const wxSize &size , bool adjustForOrigin )
78 {
79     int x, y, w, h ;
80
81     window->MacGetBoundsForControl( pos , size , x , y, w, h , adjustForOrigin ) ;
82     wxRect bounds(x,y,w,h);
83     NSView* sv = (window->GetParent()->GetHandle() );
84
85     return wxToNSRect( sv, bounds );
86 }
87
88 @interface wxNSView : NSView
89 {
90     NSTrackingRectTag rectTag;
91 #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
92     NSTrackingArea* _trackingArea;
93 #endif
94 }
95
96 // the tracking tag is needed to track mouse enter / exit events
97 - (void) setTrackingTag: (NSTrackingRectTag)tag;
98 - (NSTrackingRectTag) trackingTag;
99 #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
100 // under 10.5 we can also track mouse moved events on non-focused windows if
101 // we use the new NSTrackingArea APIs. 
102 - (void) updateTrackingArea;
103 - (NSTrackingArea*) trackingArea;
104 #endif
105 @end // wxNSView
106
107 @interface NSView(PossibleMethods)
108 - (void)setTitle:(NSString *)aString;
109 - (void)setStringValue:(NSString *)aString;
110 - (void)setIntValue:(int)anInt;
111 - (void)setFloatValue:(float)aFloat;
112 - (void)setDoubleValue:(double)aDouble;
113
114 - (double)minValue;
115 - (double)maxValue;
116 - (void)setMinValue:(double)aDouble;
117 - (void)setMaxValue:(double)aDouble;
118
119 - (void)sizeToFit;
120
121 - (BOOL)isEnabled;
122 - (void)setEnabled:(BOOL)flag;
123
124 - (void)setImage:(NSImage *)image;
125 - (void)setControlSize:(NSControlSize)size;
126
127 - (void)setFont:(NSFont *)fontObject;
128
129 - (id)contentView;
130
131 - (void)setTarget:(id)anObject;
132 - (void)setAction:(SEL)aSelector;
133 - (void)setDoubleAction:(SEL)aSelector;
134 - (void)setBackgroundColor:(NSColor*)aColor;
135 - (void)setOpaque:(BOOL)opaque;
136 - (void)setTextColor:(NSColor *)color;
137 - (void)setImagePosition:(NSCellImagePosition)aPosition;
138 @end
139
140 long wxOSXTranslateCocoaKey( NSEvent* event, int eventType )
141 {
142     long retval = 0;
143
144     if ([event type] != NSFlagsChanged)
145     {
146         NSString* s = [event charactersIgnoringModifiers];
147         // backspace char reports as delete w/modifiers for some reason
148         if ([s length] == 1)
149         {
150             if ( eventType == wxEVT_CHAR && ([event modifierFlags] & NSControlKeyMask) && ( [s characterAtIndex:0] >= 'a' && [s characterAtIndex:0] <= 'z' ) )
151             {
152                 retval = WXK_CONTROL_A + ([s characterAtIndex:0] - 'a');
153             }
154             else
155             {
156                 switch ( [s characterAtIndex:0] )
157                 {
158                     // backspace key
159                     case 0x7F :
160                     case 8 :
161                         retval = WXK_BACK;
162                         break;
163                     case NSUpArrowFunctionKey :
164                         retval = WXK_UP;
165                         break;
166                     case NSDownArrowFunctionKey :
167                         retval = WXK_DOWN;
168                         break;
169                     case NSLeftArrowFunctionKey :
170                         retval = WXK_LEFT;
171                         break;
172                     case NSRightArrowFunctionKey :
173                         retval = WXK_RIGHT;
174                         break;
175                     case NSInsertFunctionKey  :
176                         retval = WXK_INSERT;
177                         break;
178                     case NSDeleteFunctionKey  :
179                         retval = WXK_DELETE;
180                         break;
181                     case NSHomeFunctionKey  :
182                         retval = WXK_HOME;
183                         break;
184             //        case NSBeginFunctionKey  :
185             //            retval = WXK_BEGIN;
186             //            break;
187                     case NSEndFunctionKey  :
188                         retval = WXK_END;
189                         break;
190                     case NSPageUpFunctionKey  :
191                         retval = WXK_PAGEUP;
192                         break;
193                    case NSPageDownFunctionKey  :
194                         retval = WXK_PAGEDOWN;
195                         break;
196                    case NSHelpFunctionKey  :
197                         retval = WXK_HELP;
198                         break;
199                     default:
200                         int intchar = [s characterAtIndex: 0];
201                         if ( intchar >= NSF1FunctionKey && intchar <= NSF24FunctionKey )
202                             retval = WXK_F1 + (intchar - NSF1FunctionKey );
203                         else if ( intchar > 0 && intchar < 32 )
204                             retval = intchar;
205                         break;
206                 }
207             }
208         }
209     }
210
211     // Some keys don't seem to have constants. The code mimics the approach
212     // taken by WebKit. See:
213     // http://trac.webkit.org/browser/trunk/WebCore/platform/mac/KeyEventMac.mm
214     switch( [event keyCode] )
215     {
216         // command key
217         case 54:
218         case 55:
219             retval = WXK_CONTROL;
220             break;
221         // caps locks key
222         case 57: // Capslock
223             retval = WXK_CAPITAL;
224             break;
225         // shift key
226         case 56: // Left Shift
227         case 60: // Right Shift
228             retval = WXK_SHIFT;
229             break;
230         // alt key
231         case 58: // Left Alt
232         case 61: // Right Alt
233             retval = WXK_ALT;
234             break;
235         // ctrl key
236         case 59: // Left Ctrl
237         case 62: // Right Ctrl
238             retval = WXK_RAW_CONTROL;
239             break;
240         // clear key
241         case 71:
242             retval = WXK_CLEAR;
243             break;
244         // tab key
245         case 48:
246             retval = WXK_TAB;
247             break;
248
249         case 75: // /
250             retval = WXK_NUMPAD_DIVIDE;
251             break;
252         case 67: // *
253             retval = WXK_NUMPAD_MULTIPLY;
254             break;
255         case 78: // -
256             retval = WXK_NUMPAD_SUBTRACT;
257             break;
258         case 69: // +
259             retval = WXK_NUMPAD_ADD;
260             break;
261         case 76: // Enter
262             retval = WXK_NUMPAD_ENTER;
263             break;
264         case 65: // .
265             retval = WXK_NUMPAD_DECIMAL;
266             break;
267         case 82: // 0
268             retval = WXK_NUMPAD0;
269             break;
270         case 83: // 1
271             retval = WXK_NUMPAD1;
272             break;
273         case 84: // 2
274             retval = WXK_NUMPAD2;
275             break;
276         case 85: // 3
277             retval = WXK_NUMPAD3;
278             break;
279         case 86: // 4
280             retval = WXK_NUMPAD4;
281             break;
282         case 87: // 5
283             retval = WXK_NUMPAD5;
284             break;
285         case 88: // 6
286             retval = WXK_NUMPAD6;
287             break;
288         case 89: // 7
289             retval = WXK_NUMPAD7;
290             break;
291         case 91: // 8
292             retval = WXK_NUMPAD8;
293             break;
294         case 92: // 9
295             retval = WXK_NUMPAD9;
296             break;
297         default:
298             //retval = [event keyCode];
299             break;
300     }
301     return retval;
302 }
303
304 void wxWidgetCocoaImpl::SetupKeyEvent(wxKeyEvent &wxevent , NSEvent * nsEvent, NSString* charString)
305 {
306     UInt32 modifiers = [nsEvent modifierFlags] ;
307     int eventType = [nsEvent type];
308
309     wxevent.m_shiftDown = modifiers & NSShiftKeyMask;
310     wxevent.m_rawControlDown = modifiers & NSControlKeyMask;
311     wxevent.m_altDown = modifiers & NSAlternateKeyMask;
312     wxevent.m_controlDown = modifiers & NSCommandKeyMask;
313
314     wxevent.m_rawCode = [nsEvent keyCode];
315     wxevent.m_rawFlags = modifiers;
316
317     wxevent.SetTimestamp( (int)([nsEvent timestamp] * 1000) ) ;
318
319     wxString chars;
320     if ( eventType != NSFlagsChanged )
321     {
322         NSString* nschars = [[nsEvent charactersIgnoringModifiers] uppercaseString];
323         if ( charString )
324         {
325             // if charString is set, it did not come from key up / key down
326             wxevent.SetEventType( wxEVT_CHAR );
327             chars = wxCFStringRef::AsString(charString);
328         }
329         else if ( nschars )
330         {
331             chars = wxCFStringRef::AsString(nschars);
332         }
333     }
334
335     int aunichar = chars.Length() > 0 ? chars[0] : 0;
336     long keyval = 0;
337
338     if (wxevent.GetEventType() != wxEVT_CHAR)
339     {
340         keyval = wxOSXTranslateCocoaKey(nsEvent, wxevent.GetEventType()) ;
341         switch (eventType)
342         {
343             case NSKeyDown :
344                 wxevent.SetEventType( wxEVT_KEY_DOWN )  ;
345                 break;
346             case NSKeyUp :
347                 wxevent.SetEventType( wxEVT_KEY_UP )  ;
348                 break;
349             case NSFlagsChanged :
350                 switch (keyval)
351                 {
352                     case WXK_CONTROL:
353                         wxevent.SetEventType( wxevent.m_controlDown ? wxEVT_KEY_DOWN : wxEVT_KEY_UP);
354                         break;
355                     case WXK_SHIFT:
356                         wxevent.SetEventType( wxevent.m_shiftDown ? wxEVT_KEY_DOWN : wxEVT_KEY_UP);
357                         break;
358                     case WXK_ALT:
359                         wxevent.SetEventType( wxevent.m_altDown ? wxEVT_KEY_DOWN : wxEVT_KEY_UP);
360                         break;
361                     case WXK_RAW_CONTROL:
362                         wxevent.SetEventType( wxevent.m_rawControlDown ? wxEVT_KEY_DOWN : wxEVT_KEY_UP);
363                         break;
364                 }
365                 break;
366             default :
367                 break ;
368         }
369     }
370
371     if ( !keyval )
372     {
373         if ( wxevent.GetEventType() == wxEVT_KEY_UP || wxevent.GetEventType() == wxEVT_KEY_DOWN )
374             keyval = wxToupper( aunichar ) ;
375         else
376             keyval = aunichar;
377     }
378
379 #if wxUSE_UNICODE
380     // OS X generates events with key codes in Unicode private use area for
381     // unprintable symbols such as cursor arrows (WXK_UP is mapped to U+F700)
382     // and function keys (WXK_F2 is U+F705). We don't want to use them as the
383     // result of wxKeyEvent::GetUnicodeKey() however as it's supposed to return
384     // WXK_NONE for "non characters" so explicitly exclude them.
385     //
386     // We only exclude the private use area inside the Basic Multilingual Plane
387     // as key codes beyond it don't seem to be currently used.
388     if ( !(aunichar >= 0xe000 && aunichar < 0xf900) )
389         wxevent.m_uniChar = aunichar;
390 #endif
391     wxevent.m_keyCode = keyval;
392
393     wxWindowMac* peer = GetWXPeer();
394     if ( peer )
395     {
396         wxevent.SetEventObject(peer);
397         wxevent.SetId(peer->GetId()) ;
398     }
399 }
400
401 UInt32 g_lastButton = 0 ;
402 bool g_lastButtonWasFakeRight = false ;
403
404 // better scroll wheel support 
405 // see http://lists.apple.com/archives/cocoa-dev/2007/Feb/msg00050.html
406
407 @interface NSEvent (DeviceDelta)
408 - (CGFloat)deviceDeltaX;
409 - (CGFloat)deviceDeltaY;
410 @end
411
412 void wxWidgetCocoaImpl::SetupMouseEvent( wxMouseEvent &wxevent , NSEvent * nsEvent )
413 {
414     int eventType = [nsEvent type];
415     UInt32 modifiers = [nsEvent modifierFlags] ;
416
417     NSPoint locationInWindow = [nsEvent locationInWindow];
418     
419     // adjust coordinates for the window of the target view
420     if ( [nsEvent window] != [m_osxView window] )
421     {
422         if ( [nsEvent window] != nil )
423             locationInWindow = [[nsEvent window] convertBaseToScreen:locationInWindow];
424
425         if ( [m_osxView window] != nil )
426             locationInWindow = [[m_osxView window] convertScreenToBase:locationInWindow];
427     }
428
429     NSPoint locationInView = [m_osxView convertPoint:locationInWindow fromView:nil];
430     wxPoint locationInViewWX = wxFromNSPoint( m_osxView, locationInView );
431
432     // these parameters are not given for all events
433     UInt32 button = [nsEvent buttonNumber];
434     UInt32 clickCount = 0;
435
436     wxevent.m_x = locationInViewWX.x;
437     wxevent.m_y = locationInViewWX.y;
438     wxevent.m_shiftDown = modifiers & NSShiftKeyMask;
439     wxevent.m_rawControlDown = modifiers & NSControlKeyMask;
440     wxevent.m_altDown = modifiers & NSAlternateKeyMask;
441     wxevent.m_controlDown = modifiers & NSCommandKeyMask;
442     wxevent.SetTimestamp( (int)([nsEvent timestamp] * 1000) ) ;
443
444     UInt32 mouseChord = 0;
445
446     switch (eventType)
447     {
448         case NSLeftMouseDown :
449         case NSLeftMouseDragged :
450             mouseChord = 1U;
451             break;
452         case NSRightMouseDown :
453         case NSRightMouseDragged :
454             mouseChord = 2U;
455             break;
456         case NSOtherMouseDown :
457         case NSOtherMouseDragged :
458             mouseChord = 4U;
459             break;
460     }
461
462     // a control click is interpreted as a right click
463     bool thisButtonIsFakeRight = false ;
464     if ( button == 0 && (modifiers & NSControlKeyMask) )
465     {
466         button = 1 ;
467         thisButtonIsFakeRight = true ;
468     }
469
470     // otherwise we report double clicks by connecting a left click with a ctrl-left click
471     if ( clickCount > 1 && button != g_lastButton )
472         clickCount = 1 ;
473
474     // we must make sure that our synthetic 'right' button corresponds in
475     // mouse down, moved and mouse up, and does not deliver a right down and left up
476     switch (eventType)
477     {
478         case NSLeftMouseDown :
479         case NSRightMouseDown :
480         case NSOtherMouseDown :
481             g_lastButton = button ;
482             g_lastButtonWasFakeRight = thisButtonIsFakeRight ;
483             break;
484      }
485
486     if ( button == 0 )
487     {
488         g_lastButton = 0 ;
489         g_lastButtonWasFakeRight = false ;
490     }
491     else if ( g_lastButton == 1 && g_lastButtonWasFakeRight )
492         button = g_lastButton ;
493
494     // Adjust the chord mask to remove the primary button and add the
495     // secondary button.  It is possible that the secondary button is
496     // already pressed, e.g. on a mouse connected to a laptop, but this
497     // possibility is ignored here:
498     if( thisButtonIsFakeRight && ( mouseChord & 1U ) )
499         mouseChord = ((mouseChord & ~1U) | 2U);
500
501     if(mouseChord & 1U)
502                 wxevent.m_leftDown = true ;
503     if(mouseChord & 2U)
504                 wxevent.m_rightDown = true ;
505     if(mouseChord & 4U)
506                 wxevent.m_middleDown = true ;
507
508     // translate into wx types
509     switch (eventType)
510     {
511         case NSLeftMouseDown :
512         case NSRightMouseDown :
513         case NSOtherMouseDown :
514             clickCount = [nsEvent clickCount];
515             switch ( button )
516             {
517                 case 0 :
518                     wxevent.SetEventType( clickCount > 1 ? wxEVT_LEFT_DCLICK : wxEVT_LEFT_DOWN )  ;
519                     break ;
520
521                 case 1 :
522                     wxevent.SetEventType( clickCount > 1 ? wxEVT_RIGHT_DCLICK : wxEVT_RIGHT_DOWN ) ;
523                     break ;
524
525                 case 2 :
526                     wxevent.SetEventType( clickCount > 1 ? wxEVT_MIDDLE_DCLICK : wxEVT_MIDDLE_DOWN ) ;
527                     break ;
528
529                 default:
530                     break ;
531             }
532             break ;
533
534         case NSLeftMouseUp :
535         case NSRightMouseUp :
536         case NSOtherMouseUp :
537             clickCount = [nsEvent clickCount];
538             switch ( button )
539             {
540                 case 0 :
541                     wxevent.SetEventType( wxEVT_LEFT_UP )  ;
542                     break ;
543
544                 case 1 :
545                     wxevent.SetEventType( wxEVT_RIGHT_UP ) ;
546                     break ;
547
548                 case 2 :
549                     wxevent.SetEventType( wxEVT_MIDDLE_UP ) ;
550                     break ;
551
552                 default:
553                     break ;
554             }
555             break ;
556
557      case NSScrollWheel :
558         {
559             float deltaX = 0.0;
560             float deltaY = 0.0;
561
562             wxevent.SetEventType( wxEVT_MOUSEWHEEL ) ;
563
564             // see http://developer.apple.com/qa/qa2005/qa1453.html
565             // for more details on why we have to look for the exact type
566             
567             const EventRef cEvent = (EventRef) [nsEvent eventRef];
568             bool isMouseScrollEvent = false;
569             if ( cEvent )
570                 isMouseScrollEvent = ::GetEventKind(cEvent) == kEventMouseScroll;
571                 
572             if ( isMouseScrollEvent )
573             {
574                 deltaX = [nsEvent deviceDeltaX];
575                 deltaY = [nsEvent deviceDeltaY];
576             }
577             else
578             {
579                 deltaX = ([nsEvent deltaX] * 10);
580                 deltaY = ([nsEvent deltaY] * 10);
581             }
582             
583             wxevent.m_wheelDelta = 10;
584             wxevent.m_linesPerAction = 1;
585                 
586             if ( fabs(deltaX) > fabs(deltaY) )
587             {
588                 wxevent.m_wheelAxis = 1;
589                 wxevent.m_wheelRotation = (int)deltaX;
590             }
591             else
592             {
593                 wxevent.m_wheelRotation = (int)deltaY;
594             }
595
596         }
597         break ;
598
599         case NSMouseEntered :
600             wxevent.SetEventType( wxEVT_ENTER_WINDOW ) ;
601             break;
602         case NSMouseExited :
603             wxevent.SetEventType( wxEVT_LEAVE_WINDOW ) ;
604             break;
605         case NSLeftMouseDragged :
606         case NSRightMouseDragged :
607         case NSOtherMouseDragged :
608         case NSMouseMoved :
609             wxevent.SetEventType( wxEVT_MOTION ) ;
610             break;
611         default :
612             break ;
613     }
614
615     wxevent.m_clickCount = clickCount;
616     wxWindowMac* peer = GetWXPeer();
617     if ( peer )
618     {
619         wxevent.SetEventObject(peer);
620         wxevent.SetId(peer->GetId()) ;
621     }
622 }
623
624 @implementation wxNSView
625
626 + (void)initialize
627 {
628     static BOOL initialized = NO;
629     if (!initialized)
630     {
631         initialized = YES;
632         wxOSXCocoaClassAddWXMethods( self );
633     }
634 }
635
636 - (void) setTrackingTag: (NSTrackingRectTag)tag
637 {
638     rectTag = tag;
639 }
640
641 - (NSTrackingRectTag) trackingTag
642 {
643     return rectTag;
644 }
645
646 #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
647 - (void) updateTrackingArea
648 {
649     if (_trackingArea)
650     {
651         [self removeTrackingArea: _trackingArea];
652         [_trackingArea release];
653     }
654     
655     NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited|NSTrackingMouseMoved|NSTrackingActiveAlways;
656         
657     NSTrackingArea* area = [[NSTrackingArea alloc] initWithRect: [self bounds] options: options owner: self userInfo: nil];
658     [self addTrackingArea: area];
659
660     _trackingArea = area;
661 }
662
663 - (NSTrackingArea*) trackingArea
664 {
665     return _trackingArea;
666 }
667 #endif
668 @end // wxNSView
669
670 //
671 // event handlers
672 //
673
674 #if wxUSE_DRAG_AND_DROP
675
676 // see http://lists.apple.com/archives/Cocoa-dev/2005/Jul/msg01244.html
677 // for details on the NSPasteboard -> PasteboardRef conversion
678
679 NSDragOperation wxOSX_draggingEntered( id self, SEL _cmd, id <NSDraggingInfo>sender )
680 {
681     wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
682     if (impl == NULL)
683         return NSDragOperationNone;
684
685     return impl->draggingEntered(sender, self, _cmd);
686 }
687
688 void wxOSX_draggingExited( id self, SEL _cmd, id <NSDraggingInfo> sender )
689 {
690     wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
691     if (impl == NULL)
692         return ;
693
694     return impl->draggingExited(sender, self, _cmd);
695 }
696
697 NSDragOperation wxOSX_draggingUpdated( id self, SEL _cmd, id <NSDraggingInfo>sender )
698 {
699     wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
700     if (impl == NULL)
701         return NSDragOperationNone;
702
703     return impl->draggingUpdated(sender, self, _cmd);
704 }
705
706 BOOL wxOSX_performDragOperation( id self, SEL _cmd, id <NSDraggingInfo> sender )
707 {
708     wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
709     if (impl == NULL)
710         return NSDragOperationNone;
711
712     return impl->performDragOperation(sender, self, _cmd) ? YES:NO ;
713 }
714
715 #endif
716
717 void wxOSX_mouseEvent(NSView* self, SEL _cmd, NSEvent *event)
718 {
719     wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
720     if (impl == NULL)
721         return;
722
723     impl->mouseEvent(event, self, _cmd);
724 }
725
726 BOOL wxOSX_acceptsFirstMouse(NSView* WXUNUSED(self), SEL WXUNUSED(_cmd), NSEvent *WXUNUSED(event))
727 {
728     // This is needed to support click through, otherwise the first click on a window
729     // will not do anything unless it is the active window already.
730     return YES;
731 }
732
733 void wxOSX_keyEvent(NSView* self, SEL _cmd, NSEvent *event)
734 {
735     wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
736     if (impl == NULL)
737         return;
738
739     impl->keyEvent(event, self, _cmd);
740 }
741
742 void wxOSX_insertText(NSView* self, SEL _cmd, NSString* text)
743 {
744     wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
745     if (impl == NULL)
746         return;
747
748     impl->insertText(text, self, _cmd);
749 }
750
751 BOOL wxOSX_performKeyEquivalent(NSView* self, SEL _cmd, NSEvent *event)
752 {
753     wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
754     if (impl == NULL)
755         return NO;
756
757     return impl->performKeyEquivalent(event, self, _cmd);
758 }
759
760 BOOL wxOSX_acceptsFirstResponder(NSView* self, SEL _cmd)
761 {
762     wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
763     if (impl == NULL)
764         return NO;
765
766     return impl->acceptsFirstResponder(self, _cmd);
767 }
768
769 BOOL wxOSX_becomeFirstResponder(NSView* self, SEL _cmd)
770 {
771     wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
772     if (impl == NULL)
773         return NO;
774
775     return impl->becomeFirstResponder(self, _cmd);
776 }
777
778 BOOL wxOSX_resignFirstResponder(NSView* self, SEL _cmd)
779 {
780     wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
781     if (impl == NULL)
782         return NO;
783
784     return impl->resignFirstResponder(self, _cmd);
785 }
786
787 void wxOSX_resetCursorRects(NSView* self, SEL _cmd)
788 {
789     wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
790     if (impl == NULL)
791         return;
792
793     impl->resetCursorRects(self, _cmd);
794 }
795
796 BOOL wxOSX_isFlipped(NSView* self, SEL _cmd)
797 {
798     wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
799     if (impl == NULL)
800         return NO;
801
802     return impl->isFlipped(self, _cmd) ? YES:NO;
803 }
804
805 typedef void (*wxOSX_DrawRectHandlerPtr)(NSView* self, SEL _cmd, NSRect rect);
806
807 void wxOSX_drawRect(NSView* self, SEL _cmd, NSRect rect)
808 {
809     wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
810     if (impl == NULL)
811         return;
812
813 #ifdef wxUSE_THREADS
814     // OS X starts a NSUIHeartBeatThread for animating the default button in a
815     // dialog. This causes a drawRect of the active dialog from outside the
816     // main UI thread. This causes an occasional crash since the wx drawing
817     // objects (like wxPen) are not thread safe.
818     //
819     // Notice that NSUIHeartBeatThread seems to be undocumented and doing
820     // [NSWindow setAllowsConcurrentViewDrawing:NO] does not affect it.
821     if ( !wxThread::IsMain() )
822     {
823         if ( impl->IsUserPane() )
824         {
825             wxWindow* win = impl->GetWXPeer();
826             if ( win->UseBgCol() )
827             {
828                 
829                 CGContextRef context = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort];
830                 CGContextSaveGState( context );
831
832                 CGContextSetFillColorWithColor( context, win->GetBackgroundColour().GetCGColor());
833                 CGRect r = CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
834                 CGContextFillRect( context, r );
835
836                 CGContextRestoreGState( context );
837             }
838         }
839         else 
840         {
841             // just call the superclass handler, we don't need any custom wx drawing
842             // here and it seems to work fine:
843             wxOSX_DrawRectHandlerPtr
844             superimpl = (wxOSX_DrawRectHandlerPtr)
845             [[self superclass] instanceMethodForSelector:_cmd];
846             superimpl(self, _cmd, rect);
847         }
848
849       return;
850     }
851 #endif // wxUSE_THREADS
852
853     return impl->drawRect(&rect, self, _cmd);
854 }
855
856 void wxOSX_controlAction(NSView* self, SEL _cmd, id sender)
857 {
858     wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
859     if (impl == NULL)
860         return;
861
862     impl->controlAction(self, _cmd, sender);
863 }
864
865 void wxOSX_controlDoubleAction(NSView* self, SEL _cmd, id sender)
866 {
867     wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
868     if (impl == NULL)
869         return;
870
871     impl->controlDoubleAction(self, _cmd, sender);
872 }
873
874 unsigned int wxWidgetCocoaImpl::draggingEntered(void* s, WXWidget WXUNUSED(slf), void *WXUNUSED(_cmd))
875 {
876     id <NSDraggingInfo>sender = (id <NSDraggingInfo>) s;
877     NSPasteboard *pboard = [sender draggingPasteboard];
878     NSDragOperation sourceDragMask = [sender draggingSourceOperationMask];
879
880     wxWindow* wxpeer = GetWXPeer();
881     if ( wxpeer == NULL )
882         return NSDragOperationNone;
883
884     wxDropTarget* target = wxpeer->GetDropTarget();
885     if ( target == NULL )
886         return NSDragOperationNone;
887
888     wxDragResult result = wxDragNone;
889     NSPoint nspoint = [m_osxView convertPoint:[sender draggingLocation] fromView:nil];
890     wxPoint pt = wxFromNSPoint( m_osxView, nspoint );
891
892     if ( sourceDragMask & NSDragOperationLink )
893         result = wxDragLink;
894     else if ( sourceDragMask & NSDragOperationCopy )
895         result = wxDragCopy;
896     else if ( sourceDragMask & NSDragOperationMove )
897         result = wxDragMove;
898
899     PasteboardRef pboardRef;
900     PasteboardCreate((CFStringRef)[pboard name], &pboardRef);
901     target->SetCurrentDragPasteboard(pboardRef);
902     result = target->OnEnter(pt.x, pt.y, result);
903     CFRelease(pboardRef);
904
905     NSDragOperation nsresult = NSDragOperationNone;
906     switch (result )
907     {
908         case wxDragLink:
909             nsresult = NSDragOperationLink;
910         case wxDragMove:
911             nsresult = NSDragOperationMove;
912         case wxDragCopy:
913             nsresult = NSDragOperationCopy;
914         default :
915             break;
916     }
917     return nsresult;
918 }
919
920 void wxWidgetCocoaImpl::draggingExited(void* s, WXWidget WXUNUSED(slf), void *WXUNUSED(_cmd))
921 {
922     id <NSDraggingInfo>sender = (id <NSDraggingInfo>) s;
923     NSPasteboard *pboard = [sender draggingPasteboard];
924
925     wxWindow* wxpeer = GetWXPeer();
926     if ( wxpeer == NULL )
927         return;
928
929     wxDropTarget* target = wxpeer->GetDropTarget();
930     if ( target == NULL )
931         return;
932
933     PasteboardRef pboardRef;
934     PasteboardCreate((CFStringRef)[pboard name], &pboardRef);
935     target->SetCurrentDragPasteboard(pboardRef);
936     target->OnLeave();
937     CFRelease(pboardRef);
938  }
939
940 unsigned int wxWidgetCocoaImpl::draggingUpdated(void* s, WXWidget WXUNUSED(slf), void *WXUNUSED(_cmd))
941 {
942     id <NSDraggingInfo>sender = (id <NSDraggingInfo>) s;
943     NSPasteboard *pboard = [sender draggingPasteboard];
944     NSDragOperation sourceDragMask = [sender draggingSourceOperationMask];
945
946     wxWindow* wxpeer = GetWXPeer();
947     if ( wxpeer == NULL )
948         return NSDragOperationNone;
949
950     wxDropTarget* target = wxpeer->GetDropTarget();
951     if ( target == NULL )
952         return NSDragOperationNone;
953
954     wxDragResult result = wxDragNone;
955     NSPoint nspoint = [m_osxView convertPoint:[sender draggingLocation] fromView:nil];
956     wxPoint pt = wxFromNSPoint( m_osxView, nspoint );
957
958     if ( sourceDragMask & NSDragOperationLink )
959         result = wxDragLink;
960     else if ( sourceDragMask & NSDragOperationCopy )
961         result = wxDragCopy;
962     else if ( sourceDragMask & NSDragOperationMove )
963         result = wxDragMove;
964     
965     PasteboardRef pboardRef;
966     PasteboardCreate((CFStringRef)[pboard name], &pboardRef);
967     target->SetCurrentDragPasteboard(pboardRef);
968     result = target->OnDragOver(pt.x, pt.y, result);
969     CFRelease(pboardRef);
970
971     NSDragOperation nsresult = NSDragOperationNone;
972     switch (result )
973     {
974         case wxDragLink:
975             nsresult = NSDragOperationLink;
976         case wxDragMove:
977             nsresult = NSDragOperationMove;
978         case wxDragCopy:
979             nsresult = NSDragOperationCopy;
980         default :
981             break;
982     }
983     return nsresult;
984 }
985
986 bool wxWidgetCocoaImpl::performDragOperation(void* s, WXWidget WXUNUSED(slf), void *WXUNUSED(_cmd))
987 {
988     id <NSDraggingInfo>sender = (id <NSDraggingInfo>) s;
989
990     NSPasteboard *pboard = [sender draggingPasteboard];
991     NSDragOperation sourceDragMask = [sender draggingSourceOperationMask];
992
993     wxWindow* wxpeer = GetWXPeer();
994     wxDropTarget* target = wxpeer->GetDropTarget();
995     wxDragResult result = wxDragNone;
996     NSPoint nspoint = [m_osxView convertPoint:[sender draggingLocation] fromView:nil];
997     wxPoint pt = wxFromNSPoint( m_osxView, nspoint );
998
999     if ( sourceDragMask & NSDragOperationLink )
1000         result = wxDragLink;
1001     else if ( sourceDragMask & NSDragOperationCopy )
1002         result = wxDragCopy;
1003     else if ( sourceDragMask & NSDragOperationMove )
1004         result = wxDragMove;
1005
1006     PasteboardRef pboardRef;
1007     PasteboardCreate((CFStringRef)[pboard name], &pboardRef);
1008     target->SetCurrentDragPasteboard(pboardRef);
1009
1010     if (target->OnDrop(pt.x, pt.y))
1011         result = target->OnData(pt.x, pt.y, result);
1012
1013     CFRelease(pboardRef);
1014
1015     return result != wxDragNone;
1016 }
1017
1018 typedef void (*wxOSX_TextEventHandlerPtr)(NSView* self, SEL _cmd, NSString *event);
1019 typedef void (*wxOSX_EventHandlerPtr)(NSView* self, SEL _cmd, NSEvent *event);
1020 typedef BOOL (*wxOSX_PerformKeyEventHandlerPtr)(NSView* self, SEL _cmd, NSEvent *event);
1021 typedef BOOL (*wxOSX_FocusHandlerPtr)(NSView* self, SEL _cmd);
1022 typedef BOOL (*wxOSX_ResetCursorRectsHandlerPtr)(NSView* self, SEL _cmd);
1023
1024 void wxWidgetCocoaImpl::mouseEvent(WX_NSEvent event, WXWidget slf, void *_cmd)
1025 {
1026     if ( !DoHandleMouseEvent(event) )
1027     {
1028         // for plain NSView mouse events would propagate to parents otherwise
1029         if (!IsUserPane())
1030         {
1031             wxOSX_EventHandlerPtr superimpl = (wxOSX_EventHandlerPtr) [[slf superclass] instanceMethodForSelector:(SEL)_cmd];
1032             superimpl(slf, (SEL)_cmd, event);
1033             
1034             // super of built-ins keeps the mouse up, as wx expects this event, we have to synthesize it
1035             
1036             if ( [ event type]  == NSLeftMouseDown )
1037             {
1038                 wxMouseEvent wxevent(wxEVT_LEFT_DOWN);
1039                 SetupMouseEvent(wxevent , event) ;
1040                 wxevent.SetEventType(wxEVT_LEFT_UP);
1041                 
1042                 GetWXPeer()->HandleWindowEvent(wxevent);
1043             }
1044         }
1045     }
1046 }
1047
1048 void wxWidgetCocoaImpl::keyEvent(WX_NSEvent event, WXWidget slf, void *_cmd)
1049 {
1050     if ( [event type] == NSKeyDown )
1051     {
1052         // there are key equivalents that are not command-combos and therefore not handled by cocoa automatically, 
1053         // therefore we call the menubar directly here, exit if the menu is handling the shortcut
1054         if ( [[[NSApplication sharedApplication] mainMenu] performKeyEquivalent:event] )
1055             return;
1056     
1057         m_lastKeyDownEvent = event;
1058     }
1059     
1060     if ( GetFocusedViewInWindow([slf window]) != slf || m_hasEditor || !DoHandleKeyEvent(event) )
1061     {
1062         wxOSX_EventHandlerPtr superimpl = (wxOSX_EventHandlerPtr) [[slf superclass] instanceMethodForSelector:(SEL)_cmd];
1063         superimpl(slf, (SEL)_cmd, event);
1064     }
1065     m_lastKeyDownEvent = NULL;
1066 }
1067
1068 void wxWidgetCocoaImpl::insertText(NSString* text, WXWidget slf, void *_cmd)
1069 {
1070     if ( m_lastKeyDownEvent==NULL || m_hasEditor || !DoHandleCharEvent(m_lastKeyDownEvent, text) )
1071     {
1072         wxOSX_TextEventHandlerPtr superimpl = (wxOSX_TextEventHandlerPtr) [[slf superclass] instanceMethodForSelector:(SEL)_cmd];
1073         superimpl(slf, (SEL)_cmd, text);
1074     }
1075 }
1076
1077
1078 bool wxWidgetCocoaImpl::performKeyEquivalent(WX_NSEvent event, WXWidget slf, void *_cmd)
1079 {
1080     bool handled = false;
1081     
1082     wxKeyEvent wxevent(wxEVT_KEY_DOWN);
1083     SetupKeyEvent( wxevent, event );
1084    
1085     // because performKeyEquivalent is going up the entire view hierarchy, we don't have to
1086     // walk up the ancestors ourselves but let cocoa do it
1087     
1088     int command = m_wxPeer->GetAcceleratorTable()->GetCommand( wxevent );
1089     if (command != -1)
1090     {
1091         wxEvtHandler * const handler = m_wxPeer->GetEventHandler();
1092         
1093         wxCommandEvent command_event( wxEVT_COMMAND_MENU_SELECTED, command );
1094         handled = handler->ProcessEvent( command_event );
1095         
1096         if ( !handled )
1097         {
1098             // accelerators can also be used with buttons, try them too
1099             command_event.SetEventType(wxEVT_COMMAND_BUTTON_CLICKED);
1100             handled = handler->ProcessEvent( command_event );
1101         }
1102     }
1103     
1104     if ( !handled )
1105     {
1106         wxOSX_PerformKeyEventHandlerPtr superimpl = (wxOSX_PerformKeyEventHandlerPtr) [[slf superclass] instanceMethodForSelector:(SEL)_cmd];
1107         return superimpl(slf, (SEL)_cmd, event);
1108     }
1109     return YES;
1110 }
1111
1112 bool wxWidgetCocoaImpl::acceptsFirstResponder(WXWidget slf, void *_cmd)
1113 {
1114     if ( IsUserPane() )
1115         return m_wxPeer->AcceptsFocus();
1116     else
1117     {
1118         wxOSX_FocusHandlerPtr superimpl = (wxOSX_FocusHandlerPtr) [[slf superclass] instanceMethodForSelector:(SEL)_cmd];
1119         return superimpl(slf, (SEL)_cmd);
1120     }
1121 }
1122
1123 bool wxWidgetCocoaImpl::becomeFirstResponder(WXWidget slf, void *_cmd)
1124 {
1125     wxOSX_FocusHandlerPtr superimpl = (wxOSX_FocusHandlerPtr) [[slf superclass] instanceMethodForSelector:(SEL)_cmd];
1126     // get the current focus before running becomeFirstResponder
1127     NSView* otherView = FindFocus();
1128
1129     wxWidgetImpl* otherWindow = FindFromWXWidget(otherView);
1130     BOOL r = superimpl(slf, (SEL)_cmd);
1131     if ( r )
1132     {
1133         DoNotifyFocusEvent( true, otherWindow );
1134     }
1135
1136     return r;
1137 }
1138
1139 bool wxWidgetCocoaImpl::resignFirstResponder(WXWidget slf, void *_cmd)
1140 {
1141     wxOSX_FocusHandlerPtr superimpl = (wxOSX_FocusHandlerPtr) [[slf superclass] instanceMethodForSelector:(SEL)_cmd];
1142     BOOL r = superimpl(slf, (SEL)_cmd);
1143     // get the current focus after running resignFirstResponder
1144     // note that this value isn't reliable, it might return the same view that
1145     // is resigning
1146     NSView* otherView = FindFocus();
1147     wxWidgetImpl* otherWindow = FindFromWXWidget(otherView);
1148
1149     // It doesn't make sense to notify about the loss of focus if we're not
1150     // really losing it and the window which has just gained focus is the same
1151     // one as this window itself. Of course, this should never happen in the
1152     // first place but somehow it does in wxGrid code and without this check we
1153     // enter into an infinite recursion, see #12267.
1154     if ( otherWindow == this )
1155         return r;
1156
1157     // NSTextViews have an editor as true responder, therefore the might get the
1158     // resign notification if their editor takes over, don't trigger any event then
1159     if ( r && !m_hasEditor)
1160     {
1161         DoNotifyFocusEvent( false, otherWindow );
1162     }
1163     return r;
1164 }
1165
1166 void wxWidgetCocoaImpl::resetCursorRects(WXWidget slf, void *_cmd)
1167 {
1168     wxWindow* wxpeer = GetWXPeer();
1169     if ( wxpeer )
1170     {
1171         NSCursor *cursor = (NSCursor*)wxpeer->GetCursor().GetHCURSOR();
1172         if (cursor == NULL)
1173         {
1174             wxOSX_ResetCursorRectsHandlerPtr superimpl = (wxOSX_ResetCursorRectsHandlerPtr) [[slf superclass] instanceMethodForSelector:(SEL)_cmd];
1175             superimpl(slf, (SEL)_cmd);
1176         }
1177         else
1178         {
1179             [slf addCursorRect: [slf bounds]
1180                 cursor: cursor];
1181         }
1182     }
1183 }
1184
1185 bool wxWidgetCocoaImpl::isFlipped(WXWidget WXUNUSED(slf), void *WXUNUSED(_cmd))
1186 {
1187     return m_isFlipped;
1188 }
1189
1190
1191 #define OSX_DEBUG_DRAWING 0
1192
1193 void wxWidgetCocoaImpl::drawRect(void* rect, WXWidget slf, void *WXUNUSED(_cmd))
1194 {
1195     // preparing the update region
1196     
1197     wxRegion updateRgn;
1198     const NSRect *rects;
1199     NSInteger count;
1200
1201     [slf getRectsBeingDrawn:&rects count:&count];
1202     for ( int i = 0 ; i < count ; ++i )
1203     {
1204         updateRgn.Union(wxFromNSRect(slf, rects[i]));
1205     }
1206
1207     wxWindow* wxpeer = GetWXPeer();
1208
1209     if ( wxpeer->MacGetLeftBorderSize() != 0 || wxpeer->MacGetTopBorderSize() != 0 )
1210     {
1211         // as this update region is in native window locals we must adapt it to wx window local
1212         updateRgn.Offset( wxpeer->MacGetLeftBorderSize() , wxpeer->MacGetTopBorderSize() );
1213     }
1214     
1215     // Restrict the update region to the shape of the window, if any, and also
1216     // remember the region that we need to clear later.
1217     wxNonOwnedWindow* const tlwParent = wxpeer->MacGetTopLevelWindow();
1218     const bool isTopLevel = tlwParent == wxpeer;
1219     wxRegion clearRgn;
1220     if ( tlwParent->GetWindowStyle() & wxFRAME_SHAPED )
1221     {
1222         if ( isTopLevel )
1223             clearRgn = updateRgn;
1224
1225         int xoffset = 0, yoffset = 0;
1226         wxRegion rgn = tlwParent->GetShape();
1227         wxpeer->MacRootWindowToWindow( &xoffset, &yoffset );
1228         rgn.Offset( xoffset, yoffset );
1229         updateRgn.Intersect(rgn);
1230
1231         if ( isTopLevel )
1232         {
1233             // Exclude the window shape from the region to be cleared below.
1234             rgn.Xor(wxpeer->GetSize());
1235             clearRgn.Intersect(rgn);
1236         }
1237     }
1238     
1239     wxpeer->GetUpdateRegion() = updateRgn;
1240
1241     // setting up the drawing context
1242     
1243     CGContextRef context = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort];
1244     CGContextSaveGState( context );
1245     
1246 #if OSX_DEBUG_DRAWING
1247     CGContextBeginPath( context );
1248     CGContextMoveToPoint(context, 0, 0);
1249     NSRect bounds = [slf bounds];
1250     CGContextAddLineToPoint(context, 10, 0);
1251     CGContextMoveToPoint(context, 0, 0);
1252     CGContextAddLineToPoint(context, 0, 10);
1253     CGContextMoveToPoint(context, bounds.size.width, bounds.size.height);
1254     CGContextAddLineToPoint(context, bounds.size.width, bounds.size.height-10);
1255     CGContextMoveToPoint(context, bounds.size.width, bounds.size.height);
1256     CGContextAddLineToPoint(context, bounds.size.width-10, bounds.size.height);
1257     CGContextClosePath( context );
1258     CGContextStrokePath(context);
1259 #endif
1260     
1261     if ( !m_isFlipped )
1262     {
1263         CGContextTranslateCTM( context, 0,  [m_osxView bounds].size.height );
1264         CGContextScaleCTM( context, 1, -1 );
1265     }
1266     
1267     wxpeer->MacSetCGContextRef( context );
1268
1269     bool handled = wxpeer->MacDoRedraw( 0 );
1270     CGContextRestoreGState( context );
1271
1272     CGContextSaveGState( context );
1273     if ( !handled )
1274     {
1275         // call super
1276         SEL _cmd = @selector(drawRect:);
1277         wxOSX_DrawRectHandlerPtr superimpl = (wxOSX_DrawRectHandlerPtr) [[slf superclass] instanceMethodForSelector:_cmd];
1278         superimpl(slf, _cmd, *(NSRect*)rect);
1279         CGContextRestoreGState( context );
1280         CGContextSaveGState( context );
1281     }
1282     // as we called restore above, we have to flip again if necessary
1283     if ( !m_isFlipped )
1284     {
1285         CGContextTranslateCTM( context, 0,  [m_osxView bounds].size.height );
1286         CGContextScaleCTM( context, 1, -1 );
1287     }
1288
1289     if ( isTopLevel )
1290     {
1291         // We also need to explicitly draw the part of the top level window
1292         // outside of its region with transparent colour to ensure that it is
1293         // really transparent.
1294         if ( clearRgn.IsOk() )
1295         {
1296             wxMacCGContextStateSaver saveState(context);
1297             wxWindowDC dc(wxpeer);
1298             dc.SetBackground(wxBrush(wxTransparentColour));
1299             dc.SetDeviceClippingRegion(clearRgn);
1300             dc.Clear();
1301         }
1302
1303 #if wxUSE_GRAPHICS_CONTEXT
1304         // If the window shape is defined by a path, stroke the path to show
1305         // the window border.
1306         const wxGraphicsPath& path = tlwParent->GetShapePath();
1307         if ( !path.IsNull() )
1308         {
1309             CGContextSetLineWidth(context, 1);
1310             CGContextSetStrokeColorWithColor(context, wxLIGHT_GREY->GetCGColor());
1311             CGContextAddPath(context, (CGPathRef) path.GetNativePath());
1312             CGContextStrokePath(context);
1313         }
1314 #endif // wxUSE_GRAPHICS_CONTEXT
1315     }
1316
1317     wxpeer->MacPaintChildrenBorders();
1318     wxpeer->MacSetCGContextRef( NULL );
1319     CGContextRestoreGState( context );
1320 }
1321
1322 void wxWidgetCocoaImpl::controlAction( WXWidget WXUNUSED(slf), void *WXUNUSED(_cmd), void *WXUNUSED(sender))
1323 {
1324     wxWindow* wxpeer = (wxWindow*) GetWXPeer();
1325     if ( wxpeer )
1326         wxpeer->OSXHandleClicked(0);
1327 }
1328
1329 void wxWidgetCocoaImpl::controlDoubleAction( WXWidget WXUNUSED(slf), void *WXUNUSED(_cmd), void *WXUNUSED(sender))
1330 {
1331 }
1332
1333 void wxWidgetCocoaImpl::controlTextDidChange()
1334 {
1335     wxWindow* wxpeer = (wxWindow*)GetWXPeer();
1336     if ( wxpeer ) 
1337     {
1338         // since native rtti doesn't have to be enabled and wx' rtti is not aware of the mixin wxTextEntry, workaround is needed
1339         wxTextCtrl *tc = wxDynamicCast( wxpeer , wxTextCtrl );
1340         wxComboBox *cb = wxDynamicCast( wxpeer , wxComboBox );
1341         if ( tc )
1342             tc->SendTextUpdatedEventIfAllowed();
1343         else if ( cb )
1344             cb->SendTextUpdatedEventIfAllowed();
1345         else 
1346         {
1347             wxFAIL_MSG("Unexpected class for controlTextDidChange event");
1348         }
1349     }
1350 }
1351
1352 //
1353
1354 #if OBJC_API_VERSION >= 2
1355
1356 #define wxOSX_CLASS_ADD_METHOD( c, s, i, t ) \
1357     class_addMethod(c, s, i, t );
1358
1359 #else
1360
1361 #define wxOSX_CLASS_ADD_METHOD( c, s, i, t ) \
1362     { s, (char*) t, i },
1363
1364 #endif
1365
1366 void wxOSXCocoaClassAddWXMethods(Class c)
1367 {
1368
1369 #if OBJC_API_VERSION < 2
1370     static objc_method wxmethods[] =
1371     {
1372 #endif
1373
1374     wxOSX_CLASS_ADD_METHOD(c, @selector(mouseDown:), (IMP) wxOSX_mouseEvent, "v@:@" )
1375     wxOSX_CLASS_ADD_METHOD(c, @selector(rightMouseDown:), (IMP) wxOSX_mouseEvent, "v@:@" )
1376     wxOSX_CLASS_ADD_METHOD(c, @selector(otherMouseDown:), (IMP) wxOSX_mouseEvent, "v@:@" )
1377
1378     wxOSX_CLASS_ADD_METHOD(c, @selector(mouseUp:), (IMP) wxOSX_mouseEvent, "v@:@" )
1379     wxOSX_CLASS_ADD_METHOD(c, @selector(rightMouseUp:), (IMP) wxOSX_mouseEvent, "v@:@" )
1380     wxOSX_CLASS_ADD_METHOD(c, @selector(otherMouseUp:), (IMP) wxOSX_mouseEvent, "v@:@" )
1381
1382     wxOSX_CLASS_ADD_METHOD(c, @selector(mouseMoved:), (IMP) wxOSX_mouseEvent, "v@:@" )
1383
1384     wxOSX_CLASS_ADD_METHOD(c, @selector(mouseDragged:), (IMP) wxOSX_mouseEvent, "v@:@" )
1385     wxOSX_CLASS_ADD_METHOD(c, @selector(rightMouseDragged:), (IMP) wxOSX_mouseEvent, "v@:@" )
1386     wxOSX_CLASS_ADD_METHOD(c, @selector(otherMouseDragged:), (IMP) wxOSX_mouseEvent, "v@:@" )
1387     
1388     wxOSX_CLASS_ADD_METHOD(c, @selector(acceptsFirstMouse:), (IMP) wxOSX_acceptsFirstMouse, "v@:@" )
1389
1390     wxOSX_CLASS_ADD_METHOD(c, @selector(scrollWheel:), (IMP) wxOSX_mouseEvent, "v@:@" )
1391     wxOSX_CLASS_ADD_METHOD(c, @selector(mouseEntered:), (IMP) wxOSX_mouseEvent, "v@:@" )
1392     wxOSX_CLASS_ADD_METHOD(c, @selector(mouseExited:), (IMP) wxOSX_mouseEvent, "v@:@" )
1393
1394     wxOSX_CLASS_ADD_METHOD(c, @selector(keyDown:), (IMP) wxOSX_keyEvent, "v@:@" )
1395     wxOSX_CLASS_ADD_METHOD(c, @selector(keyUp:), (IMP) wxOSX_keyEvent, "v@:@" )
1396     wxOSX_CLASS_ADD_METHOD(c, @selector(flagsChanged:), (IMP) wxOSX_keyEvent, "v@:@" )
1397
1398     wxOSX_CLASS_ADD_METHOD(c, @selector(insertText:), (IMP) wxOSX_insertText, "v@:@" )
1399
1400     wxOSX_CLASS_ADD_METHOD(c, @selector(performKeyEquivalent:), (IMP) wxOSX_performKeyEquivalent, "c@:@" )
1401
1402     wxOSX_CLASS_ADD_METHOD(c, @selector(acceptsFirstResponder), (IMP) wxOSX_acceptsFirstResponder, "c@:" )
1403     wxOSX_CLASS_ADD_METHOD(c, @selector(becomeFirstResponder), (IMP) wxOSX_becomeFirstResponder, "c@:" )
1404     wxOSX_CLASS_ADD_METHOD(c, @selector(resignFirstResponder), (IMP) wxOSX_resignFirstResponder, "c@:" )
1405     wxOSX_CLASS_ADD_METHOD(c, @selector(resetCursorRects), (IMP) wxOSX_resetCursorRects, "v@:" )
1406
1407     wxOSX_CLASS_ADD_METHOD(c, @selector(isFlipped), (IMP) wxOSX_isFlipped, "c@:" )
1408     wxOSX_CLASS_ADD_METHOD(c, @selector(drawRect:), (IMP) wxOSX_drawRect, "v@:{_NSRect={_NSPoint=ff}{_NSSize=ff}}" )
1409
1410     wxOSX_CLASS_ADD_METHOD(c, @selector(controlAction:), (IMP) wxOSX_controlAction, "v@:@" )
1411     wxOSX_CLASS_ADD_METHOD(c, @selector(controlDoubleAction:), (IMP) wxOSX_controlDoubleAction, "v@:@" )
1412
1413 #if wxUSE_DRAG_AND_DROP
1414     wxOSX_CLASS_ADD_METHOD(c, @selector(draggingEntered:), (IMP) wxOSX_draggingEntered, "I@:@" )
1415     wxOSX_CLASS_ADD_METHOD(c, @selector(draggingUpdated:), (IMP) wxOSX_draggingUpdated, "I@:@" )
1416     wxOSX_CLASS_ADD_METHOD(c, @selector(draggingExited:), (IMP) wxOSX_draggingExited, "v@:@" )
1417     wxOSX_CLASS_ADD_METHOD(c, @selector(performDragOperation:), (IMP) wxOSX_performDragOperation, "c@:@" )
1418 #endif
1419
1420 #if OBJC_API_VERSION < 2
1421     } ;
1422     static int method_count = WXSIZEOF( wxmethods );
1423     static objc_method_list *wxmethodlist = NULL;
1424     if ( wxmethodlist == NULL )
1425     {
1426         wxmethodlist = (objc_method_list*) malloc(sizeof(objc_method_list) + sizeof(wxmethods) );
1427         memcpy( &wxmethodlist->method_list[0], &wxmethods[0], sizeof(wxmethods) );
1428         wxmethodlist->method_count = method_count;
1429         wxmethodlist->obsolete = 0;
1430     }
1431     class_addMethods( c, wxmethodlist );
1432 #endif
1433 }
1434
1435 //
1436 // C++ implementation class
1437 //
1438
1439 IMPLEMENT_DYNAMIC_CLASS( wxWidgetCocoaImpl , wxWidgetImpl )
1440
1441 wxWidgetCocoaImpl::wxWidgetCocoaImpl( wxWindowMac* peer , WXWidget w, bool isRootControl, bool isUserPane ) :
1442     wxWidgetImpl( peer, isRootControl, isUserPane )
1443 {
1444     Init();
1445     m_osxView = w;
1446
1447     // check if the user wants to create the control initially hidden
1448     if ( !peer->IsShown() )
1449         SetVisibility(false);
1450
1451     // gc aware handling
1452     if ( m_osxView )
1453         CFRetain(m_osxView);
1454     [m_osxView release];
1455 }
1456
1457 wxWidgetCocoaImpl::wxWidgetCocoaImpl()
1458 {
1459     Init();
1460 }
1461
1462 void wxWidgetCocoaImpl::Init()
1463 {
1464     m_osxView = NULL;
1465     m_isFlipped = true;
1466     m_lastKeyDownEvent = NULL;
1467     m_hasEditor = false;
1468 }
1469
1470 wxWidgetCocoaImpl::~wxWidgetCocoaImpl()
1471 {
1472     RemoveAssociations( this );
1473
1474     if ( !IsRootControl() )
1475     {
1476         NSView *sv = [m_osxView superview];
1477         if ( sv != nil )
1478             [m_osxView removeFromSuperview];
1479     }
1480     // gc aware handling
1481     if ( m_osxView )
1482         CFRelease(m_osxView);
1483 }
1484
1485 bool wxWidgetCocoaImpl::IsVisible() const
1486 {
1487     return [m_osxView isHiddenOrHasHiddenAncestor] == NO;
1488 }
1489
1490 void wxWidgetCocoaImpl::SetVisibility( bool visible )
1491 {
1492     [m_osxView setHidden:(visible ? NO:YES)];
1493 }
1494
1495 // ----------------------------------------------------------------------------
1496 // window animation stuff
1497 // ----------------------------------------------------------------------------
1498
1499 // define a delegate used to refresh the window during animation
1500 @interface wxNSAnimationDelegate : NSObject wxOSX_10_6_AND_LATER(<NSAnimationDelegate>)
1501 {
1502     wxWindow *m_win;
1503     bool m_isDone;
1504 }
1505
1506 - (id)init:(wxWindow *)win;
1507
1508 - (bool)isDone;
1509
1510 // NSAnimationDelegate methods
1511 - (void)animationDidEnd:(NSAnimation*)animation;
1512 - (void)animation:(NSAnimation*)animation
1513         didReachProgressMark:(NSAnimationProgress)progress;
1514 @end
1515
1516 @implementation wxNSAnimationDelegate
1517
1518 - (id)init:(wxWindow *)win
1519 {
1520     self = [super init];
1521
1522     m_win = win;
1523     m_isDone = false;
1524
1525     return self;
1526 }
1527
1528 - (bool)isDone
1529 {
1530     return m_isDone;
1531 }
1532
1533 - (void)animation:(NSAnimation*)animation
1534         didReachProgressMark:(NSAnimationProgress)progress
1535 {
1536     wxUnusedVar(animation);
1537     wxUnusedVar(progress);
1538
1539     m_win->SendSizeEvent();
1540 }
1541
1542 - (void)animationDidEnd:(NSAnimation*)animation
1543 {
1544     wxUnusedVar(animation);
1545     m_isDone = true;
1546 }
1547
1548 @end
1549
1550 /* static */
1551 bool
1552 wxWidgetCocoaImpl::ShowViewOrWindowWithEffect(wxWindow *win,
1553                                               bool show,
1554                                               wxShowEffect effect,
1555                                               unsigned timeout)
1556 {
1557     // create the dictionary describing the animation to perform on this view
1558     NSObject * const
1559         viewOrWin = static_cast<NSObject *>(win->OSXGetViewOrWindow());
1560     NSMutableDictionary * const
1561         dict = [NSMutableDictionary dictionaryWithCapacity:4];
1562     [dict setObject:viewOrWin forKey:NSViewAnimationTargetKey];
1563
1564     // determine the start and end rectangles assuming we're hiding the window
1565     const wxRect rectOrig = win->GetRect();
1566     wxRect rectStart,
1567            rectEnd;
1568     rectStart =
1569     rectEnd = rectOrig;
1570
1571     if ( show )
1572     {
1573         if ( effect == wxSHOW_EFFECT_ROLL_TO_LEFT ||
1574                 effect == wxSHOW_EFFECT_SLIDE_TO_LEFT )
1575             effect = wxSHOW_EFFECT_ROLL_TO_RIGHT;
1576         else if ( effect == wxSHOW_EFFECT_ROLL_TO_RIGHT ||
1577                     effect == wxSHOW_EFFECT_SLIDE_TO_RIGHT )
1578             effect = wxSHOW_EFFECT_ROLL_TO_LEFT;
1579         else if ( effect == wxSHOW_EFFECT_ROLL_TO_TOP ||
1580                     effect == wxSHOW_EFFECT_SLIDE_TO_TOP )
1581             effect = wxSHOW_EFFECT_ROLL_TO_BOTTOM;
1582         else if ( effect == wxSHOW_EFFECT_ROLL_TO_BOTTOM ||
1583                     effect == wxSHOW_EFFECT_SLIDE_TO_BOTTOM )
1584             effect = wxSHOW_EFFECT_ROLL_TO_TOP;
1585     }
1586
1587     switch ( effect )
1588     {
1589         case wxSHOW_EFFECT_ROLL_TO_LEFT:
1590         case wxSHOW_EFFECT_SLIDE_TO_LEFT:
1591             rectEnd.width = 0;
1592             break;
1593
1594         case wxSHOW_EFFECT_ROLL_TO_RIGHT:
1595         case wxSHOW_EFFECT_SLIDE_TO_RIGHT:
1596             rectEnd.x = rectStart.GetRight();
1597             rectEnd.width = 0;
1598             break;
1599
1600         case wxSHOW_EFFECT_ROLL_TO_TOP:
1601         case wxSHOW_EFFECT_SLIDE_TO_TOP:
1602             rectEnd.height = 0;
1603             break;
1604
1605         case wxSHOW_EFFECT_ROLL_TO_BOTTOM:
1606         case wxSHOW_EFFECT_SLIDE_TO_BOTTOM:
1607             rectEnd.y = rectStart.GetBottom();
1608             rectEnd.height = 0;
1609             break;
1610
1611         case wxSHOW_EFFECT_EXPAND:
1612             rectEnd.x = rectStart.x + rectStart.width / 2;
1613             rectEnd.y = rectStart.y + rectStart.height / 2;
1614             rectEnd.width =
1615             rectEnd.height = 0;
1616             break;
1617
1618         case wxSHOW_EFFECT_BLEND:
1619             [dict setObject:(show ? NSViewAnimationFadeInEffect
1620                                   : NSViewAnimationFadeOutEffect)
1621                   forKey:NSViewAnimationEffectKey];
1622             break;
1623
1624         case wxSHOW_EFFECT_NONE:
1625         case wxSHOW_EFFECT_MAX:
1626             wxFAIL_MSG( "unexpected animation effect" );
1627             return false;
1628
1629         default:
1630             wxFAIL_MSG( "unknown animation effect" );
1631             return false;
1632     };
1633
1634     if ( show )
1635     {
1636         // we need to restore it to the original rectangle instead of making it
1637         // disappear
1638         wxSwap(rectStart, rectEnd);
1639
1640         // and as the window is currently hidden, we need to show it for the
1641         // animation to be visible at all (but don't restore it at its full
1642         // rectangle as it shouldn't appear immediately)
1643         win->SetSize(rectStart);
1644         win->Show();
1645     }
1646
1647     NSView * const parentView = [viewOrWin isKindOfClass:[NSView class]]
1648                                     ? [(NSView *)viewOrWin superview]
1649                                     : nil;
1650     const NSRect rStart = wxToNSRect(parentView, rectStart);
1651     const NSRect rEnd = wxToNSRect(parentView, rectEnd);
1652
1653     [dict setObject:[NSValue valueWithRect:rStart]
1654           forKey:NSViewAnimationStartFrameKey];
1655     [dict setObject:[NSValue valueWithRect:rEnd]
1656           forKey:NSViewAnimationEndFrameKey];
1657
1658     // create an animation using the values in the above dictionary
1659     NSViewAnimation * const
1660         anim = [[NSViewAnimation alloc]
1661                 initWithViewAnimations:[NSArray arrayWithObject:dict]];
1662
1663     if ( !timeout )
1664     {
1665         // what is a good default duration? Windows uses 200ms, Web frameworks
1666         // use anything from 250ms to 1s... choose something in the middle
1667         timeout = 500;
1668     }
1669
1670     [anim setDuration:timeout/1000.];   // duration is in seconds here
1671
1672     // if the window being animated changes its layout depending on its size
1673     // (which is almost always the case) we need to redo it during animation
1674     //
1675     // the number of layouts here is arbitrary, but 10 seems like too few (e.g.
1676     // controls in wxInfoBar visibly jump around)
1677     const int NUM_LAYOUTS = 20;
1678     for ( float f = 1./NUM_LAYOUTS; f < 1.; f += 1./NUM_LAYOUTS )
1679         [anim addProgressMark:f];
1680
1681     wxNSAnimationDelegate * const
1682         animDelegate = [[wxNSAnimationDelegate alloc] init:win];
1683     [anim setDelegate:animDelegate];
1684     [anim startAnimation];
1685
1686     // Cocoa is capable of doing animation asynchronously or even from separate
1687     // thread but wx API doesn't provide any way to be notified about the
1688     // animation end and without this we really must ensure that the window has
1689     // the expected (i.e. the same as if a simple Show() had been used) size
1690     // when we return, so block here until the animation finishes
1691     //
1692     // notice that because the default animation mode is NSAnimationBlocking,
1693     // no user input events ought to be processed from here
1694     {
1695         wxEventLoopGuarantor ensureEventLoopExistence;
1696         wxEventLoopBase * const loop = wxEventLoopBase::GetActive();
1697         while ( ![animDelegate isDone] )
1698             loop->Dispatch();
1699     }
1700
1701     if ( !show )
1702     {
1703         // NSViewAnimation is smart enough to hide the NSView being animated at
1704         // the end but we also must ensure that it's hidden for wx too
1705         win->Hide();
1706
1707         // and we must also restore its size because it isn't expected to
1708         // change just because the window was hidden
1709         win->SetSize(rectOrig);
1710     }
1711     else
1712     {
1713         // refresh it once again after the end to ensure that everything is in
1714         // place
1715         win->SendSizeEvent();
1716     }
1717
1718     [anim setDelegate:nil];
1719     [animDelegate release];
1720     [anim release];
1721
1722     return true;
1723 }
1724
1725 bool wxWidgetCocoaImpl::ShowWithEffect(bool show,
1726                                        wxShowEffect effect,
1727                                        unsigned timeout)
1728 {
1729     return ShowViewOrWindowWithEffect(m_wxPeer, show, effect, timeout);
1730 }
1731
1732 /* note that the drawing order between siblings is not defined under 10.4 */
1733 /* only starting from 10.5 the subview order is respected */
1734
1735 /* NSComparisonResult is typedef'd as an enum pre-Leopard but typedef'd as
1736  * NSInteger post-Leopard.  Pre-Leopard the Cocoa toolkit expects a function
1737  * returning int and not NSComparisonResult.  Post-Leopard the Cocoa toolkit
1738  * expects a function returning the new non-enum NSComparsionResult.
1739  * Hence we create a typedef named CocoaWindowCompareFunctionResult.
1740  */
1741 #if defined(NSINTEGER_DEFINED)
1742 typedef NSComparisonResult CocoaWindowCompareFunctionResult;
1743 #else
1744 typedef int CocoaWindowCompareFunctionResult;
1745 #endif
1746
1747 class CocoaWindowCompareContext
1748 {
1749     wxDECLARE_NO_COPY_CLASS(CocoaWindowCompareContext);
1750 public:
1751     CocoaWindowCompareContext(); // Not implemented
1752     CocoaWindowCompareContext(NSView *target, NSArray *subviews)
1753     {
1754         m_target = target;
1755         // Cocoa sorts subviews in-place.. make a copy
1756         m_subviews = [subviews copy];
1757     }
1758     
1759     ~CocoaWindowCompareContext()
1760     {   // release the copy
1761         [m_subviews release];
1762     }
1763     NSView* target()
1764     {   return m_target; }
1765     
1766     NSArray* subviews()
1767     {   return m_subviews; }
1768     
1769     /* Helper function that returns the comparison based off of the original ordering */
1770     CocoaWindowCompareFunctionResult CompareUsingOriginalOrdering(id first, id second)
1771     {
1772         NSUInteger firstI = [m_subviews indexOfObjectIdenticalTo:first];
1773         NSUInteger secondI = [m_subviews indexOfObjectIdenticalTo:second];
1774         // NOTE: If either firstI or secondI is NSNotFound then it will be NSIntegerMax and thus will
1775         // likely compare higher than the other view which is reasonable considering the only way that
1776         // can happen is if the subview was added after our call to subviews but before the call to
1777         // sortSubviewsUsingFunction:context:.  Thus we don't bother checking.  Particularly because
1778         // that case should never occur anyway because that would imply a multi-threaded GUI call
1779         // which is a big no-no with Cocoa.
1780                 
1781         // Subviews are ordered from back to front meaning one that is already lower will have an lower index.
1782         NSComparisonResult result = (firstI < secondI)
1783                 ?   NSOrderedAscending /* -1 */
1784                 :   (firstI > secondI)
1785                 ?   NSOrderedDescending /* 1 */
1786                 :   NSOrderedSame /* 0 */;
1787                 
1788         return result;
1789     }
1790 private:
1791     /* The subview we are trying to Raise or Lower */
1792     NSView *m_target;
1793     /* A copy of the original array of subviews */
1794     NSArray *m_subviews;
1795 };
1796
1797 /* Causes Cocoa to raise the target view to the top of the Z-Order by telling the sort function that
1798  * the target view is always higher than every other view.  When comparing two views neither of
1799  * which is the target, it returns the correct response based on the original ordering
1800  */
1801 static CocoaWindowCompareFunctionResult CocoaRaiseWindowCompareFunction(id first, id second, void *ctx)
1802 {
1803     CocoaWindowCompareContext *compareContext = (CocoaWindowCompareContext*)ctx;
1804     // first should be ordered higher
1805     if(first==compareContext->target())
1806         return NSOrderedDescending;
1807     // second should be ordered higher
1808     if(second==compareContext->target())
1809         return NSOrderedAscending;
1810     return compareContext->CompareUsingOriginalOrdering(first,second);
1811 }
1812
1813 void wxWidgetCocoaImpl::Raise()
1814 {
1815         NSView* nsview = m_osxView;
1816         
1817     NSView *superview = [nsview superview];
1818     CocoaWindowCompareContext compareContext(nsview, [superview subviews]);
1819         
1820     [superview sortSubviewsUsingFunction:
1821          CocoaRaiseWindowCompareFunction
1822                                                                  context: &compareContext];
1823         
1824 }
1825
1826 /* Causes Cocoa to lower the target view to the bottom of the Z-Order by telling the sort function that
1827  * the target view is always lower than every other view.  When comparing two views neither of
1828  * which is the target, it returns the correct response based on the original ordering
1829  */
1830 static CocoaWindowCompareFunctionResult CocoaLowerWindowCompareFunction(id first, id second, void *ctx)
1831 {
1832     CocoaWindowCompareContext *compareContext = (CocoaWindowCompareContext*)ctx;
1833     // first should be ordered lower
1834     if(first==compareContext->target())
1835         return NSOrderedAscending;
1836     // second should be ordered lower
1837     if(second==compareContext->target())
1838         return NSOrderedDescending;
1839     return compareContext->CompareUsingOriginalOrdering(first,second);
1840 }
1841
1842 void wxWidgetCocoaImpl::Lower()
1843 {
1844         NSView* nsview = m_osxView;
1845         
1846     NSView *superview = [nsview superview];
1847     CocoaWindowCompareContext compareContext(nsview, [superview subviews]);
1848         
1849     [superview sortSubviewsUsingFunction:
1850          CocoaLowerWindowCompareFunction
1851                                                                  context: &compareContext];
1852 }
1853
1854 void wxWidgetCocoaImpl::ScrollRect( const wxRect *WXUNUSED(rect), int WXUNUSED(dx), int WXUNUSED(dy) )
1855 {
1856 #if 1
1857     SetNeedsDisplay() ;
1858 #else
1859     // We should do something like this, but it wasn't working in 10.4.
1860     if (GetNeedsDisplay() )
1861     {
1862         SetNeedsDisplay() ;
1863     }
1864     NSRect r = wxToNSRect( [m_osxView superview], *rect );
1865     NSSize offset = NSMakeSize((float)dx, (float)dy);
1866     [m_osxView scrollRect:r by:offset];
1867 #endif
1868 }
1869
1870 void wxWidgetCocoaImpl::Move(int x, int y, int width, int height)
1871 {
1872     wxWindowMac* parent = GetWXPeer()->GetParent();
1873     // under Cocoa we might have a contentView in the wxParent to which we have to
1874     // adjust the coordinates
1875     if (parent && [m_osxView superview] != parent->GetHandle() )
1876     {
1877         int cx = 0,cy = 0,cw = 0,ch = 0;
1878         if ( parent->GetPeer() )
1879         {
1880             parent->GetPeer()->GetContentArea(cx, cy, cw, ch);
1881             x -= cx;
1882             y -= cy;
1883         }
1884     }
1885     [[m_osxView superview] setNeedsDisplayInRect:[m_osxView frame]];
1886     NSRect r = wxToNSRect( [m_osxView superview], wxRect(x,y,width, height) );
1887     [m_osxView setFrame:r];
1888     [[m_osxView superview] setNeedsDisplayInRect:r];
1889
1890     wxNSView* wxview = (wxNSView*)m_osxView;
1891 #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
1892     if ([wxview respondsToSelector:@selector(updateTrackingArea)] )
1893         [wxview updateTrackingArea]; 
1894 #else
1895     if ([m_osxView respondsToSelector:@selector(trackingTag)] )
1896     {
1897         if ( [wxview trackingTag] )
1898             [wxview removeTrackingRect: [wxview trackingTag]];
1899
1900         [wxview setTrackingTag: [wxview addTrackingRect: [m_osxView bounds] owner: wxview userData: nil assumeInside: NO]];
1901     }
1902 #endif
1903 }
1904
1905 void wxWidgetCocoaImpl::GetPosition( int &x, int &y ) const
1906 {
1907     wxRect r = wxFromNSRect( [m_osxView superview], [m_osxView frame] );
1908     x = r.GetLeft();
1909     y = r.GetTop();
1910     
1911     // under Cocoa we might have a contentView in the wxParent to which we have to
1912     // adjust the coordinates
1913     wxWindowMac* parent = GetWXPeer()->GetParent();
1914     if (parent && [m_osxView superview] != parent->GetHandle() )
1915     {
1916         int cx = 0,cy = 0,cw = 0,ch = 0;
1917         if ( parent->GetPeer() )
1918         {
1919             parent->GetPeer()->GetContentArea(cx, cy, cw, ch);
1920             x += cx;
1921             y += cy;
1922         }
1923     }
1924 }
1925
1926 void wxWidgetCocoaImpl::GetSize( int &width, int &height ) const
1927 {
1928     NSRect rect = [m_osxView frame];
1929     width = (int)rect.size.width;
1930     height = (int)rect.size.height;
1931 }
1932
1933 void wxWidgetCocoaImpl::GetContentArea( int&left, int &top, int &width, int &height ) const
1934 {
1935     if ( [m_osxView respondsToSelector:@selector(contentView) ] )
1936     {
1937         NSView* cv = [m_osxView contentView];
1938
1939         NSRect bounds = [m_osxView bounds];
1940         NSRect rect = [cv frame];
1941
1942         int y = (int)rect.origin.y;
1943         int x = (int)rect.origin.x;
1944         if ( ![ m_osxView isFlipped ] )
1945             y = (int)(bounds.size.height - (rect.origin.y + rect.size.height));
1946         left = x;
1947         top = y;
1948         width = (int)rect.size.width;
1949         height = (int)rect.size.height;
1950     }
1951     else
1952     {
1953         left = top = 0;
1954         GetSize( width, height );
1955     }
1956 }
1957
1958 void wxWidgetCocoaImpl::SetNeedsDisplay( const wxRect* where )
1959 {
1960     if ( where )
1961         [m_osxView setNeedsDisplayInRect:wxToNSRect(m_osxView, *where )];
1962     else
1963         [m_osxView setNeedsDisplay:YES];
1964 }
1965
1966 bool wxWidgetCocoaImpl::GetNeedsDisplay() const
1967 {
1968     return [m_osxView needsDisplay];
1969 }
1970
1971 bool wxWidgetCocoaImpl::CanFocus() const
1972 {
1973     return [m_osxView canBecomeKeyView] == YES;
1974 }
1975
1976 bool wxWidgetCocoaImpl::HasFocus() const
1977 {
1978     return ( FindFocus() == m_osxView );
1979 }
1980
1981 bool wxWidgetCocoaImpl::SetFocus()
1982 {
1983     if ( !CanFocus() )
1984         return false;
1985
1986     [[m_osxView window] makeKeyAndOrderFront:nil] ;
1987     [[m_osxView window] makeFirstResponder: m_osxView] ;
1988     return true;
1989 }
1990
1991
1992 void wxWidgetCocoaImpl::RemoveFromParent()
1993 {
1994     [m_osxView removeFromSuperview];
1995 }
1996
1997 void wxWidgetCocoaImpl::Embed( wxWidgetImpl *parent )
1998 {
1999     NSView* container = parent->GetWXWidget() ;
2000     wxASSERT_MSG( container != NULL , wxT("No valid mac container control") ) ;
2001     [container addSubview:m_osxView];
2002 }
2003
2004 void wxWidgetCocoaImpl::SetBackgroundColour( const wxColour &col )
2005 {
2006     NSView* targetView = m_osxView;
2007     if ( [m_osxView isKindOfClass:[NSScrollView class] ] )
2008         targetView = [(NSScrollView*) m_osxView documentView];
2009
2010     if ( [targetView respondsToSelector:@selector(setBackgroundColor:) ] )
2011     {
2012         [targetView setBackgroundColor:[NSColor colorWithCalibratedRed:(CGFloat) (col.Red() / 255.0)
2013                                                                 green:(CGFloat) (col.Green() / 255.0)
2014                                                                  blue:(CGFloat) (col.Blue() / 255.0)
2015                                                                 alpha:(CGFloat) (col.Alpha() / 255.0)]];
2016     }
2017 }
2018
2019 bool wxWidgetCocoaImpl::SetBackgroundStyle( wxBackgroundStyle style )
2020 {
2021     BOOL opaque = ( style == wxBG_STYLE_PAINT );
2022     
2023     if ( [m_osxView respondsToSelector:@selector(setOpaque:) ] )
2024     {
2025         [m_osxView setOpaque: opaque];
2026     }
2027     
2028     return true ;
2029 }
2030
2031 void wxWidgetCocoaImpl::SetLabel( const wxString& title, wxFontEncoding encoding )
2032 {
2033     if ( [m_osxView respondsToSelector:@selector(setTitle:) ] )
2034     {
2035         wxCFStringRef cf( title , encoding );
2036         [m_osxView setTitle:cf.AsNSString()];
2037     }
2038     else if ( [m_osxView respondsToSelector:@selector(setStringValue:) ] )
2039     {
2040         wxCFStringRef cf( title , encoding );
2041         [m_osxView setStringValue:cf.AsNSString()];
2042     }
2043 }
2044
2045
2046 void  wxWidgetImpl::Convert( wxPoint *pt , wxWidgetImpl *from , wxWidgetImpl *to )
2047 {
2048     NSPoint p = wxToNSPoint( from->GetWXWidget(), *pt );
2049     p = [from->GetWXWidget() convertPoint:p toView:to->GetWXWidget() ];
2050     *pt = wxFromNSPoint( to->GetWXWidget(), p );
2051 }
2052
2053 wxInt32 wxWidgetCocoaImpl::GetValue() const
2054 {
2055     return [(NSControl*)m_osxView intValue];
2056 }
2057
2058 void wxWidgetCocoaImpl::SetValue( wxInt32 v )
2059 {
2060     if (  [m_osxView respondsToSelector:@selector(setIntValue:)] )
2061     {
2062         [m_osxView setIntValue:v];
2063     }
2064     else if (  [m_osxView respondsToSelector:@selector(setFloatValue:)] )
2065     {
2066         [m_osxView setFloatValue:(double)v];
2067     }
2068     else if (  [m_osxView respondsToSelector:@selector(setDoubleValue:)] )
2069     {
2070         [m_osxView setDoubleValue:(double)v];
2071     }
2072 }
2073
2074 void wxWidgetCocoaImpl::SetMinimum( wxInt32 v )
2075 {
2076     if (  [m_osxView respondsToSelector:@selector(setMinValue:)] )
2077     {
2078         [m_osxView setMinValue:(double)v];
2079     }
2080 }
2081
2082 void wxWidgetCocoaImpl::SetMaximum( wxInt32 v )
2083 {
2084     if (  [m_osxView respondsToSelector:@selector(setMaxValue:)] )
2085     {
2086         [m_osxView setMaxValue:(double)v];
2087     }
2088 }
2089
2090 wxInt32 wxWidgetCocoaImpl::GetMinimum() const
2091 {
2092     if (  [m_osxView respondsToSelector:@selector(minValue)] )
2093     {
2094         return (int)[m_osxView minValue];
2095     }
2096     return 0;
2097 }
2098
2099 wxInt32 wxWidgetCocoaImpl::GetMaximum() const
2100 {
2101     if (  [m_osxView respondsToSelector:@selector(maxValue)] )
2102     {
2103         return (int)[m_osxView maxValue];
2104     }
2105     return 0;
2106 }
2107
2108 wxBitmap wxWidgetCocoaImpl::GetBitmap() const
2109 {
2110     wxBitmap bmp;
2111
2112     // TODO: how to create a wxBitmap from NSImage?
2113 #if 0
2114     if ( [m_osxView respondsToSelector:@selector(image:)] )
2115         bmp = [m_osxView image];
2116 #endif
2117
2118     return bmp;
2119 }
2120
2121 void wxWidgetCocoaImpl::SetBitmap( const wxBitmap& bitmap )
2122 {
2123     if (  [m_osxView respondsToSelector:@selector(setImage:)] )
2124     {
2125         if (bitmap.IsOk())
2126             [m_osxView setImage:bitmap.GetNSImage()];
2127         else
2128             [m_osxView setImage:nil];
2129
2130         [m_osxView setNeedsDisplay:YES];
2131     }
2132 }
2133
2134 void wxWidgetCocoaImpl::SetBitmapPosition( wxDirection dir )
2135 {
2136     if ( [m_osxView respondsToSelector:@selector(setImagePosition:)] )
2137     {
2138         NSCellImagePosition pos;
2139         switch ( dir )
2140         {
2141             case wxLEFT:
2142                 pos = NSImageLeft;
2143                 break;
2144
2145             case wxRIGHT:
2146                 pos = NSImageRight;
2147                 break;
2148
2149             case wxTOP:
2150                 pos = NSImageAbove;
2151                 break;
2152
2153             case wxBOTTOM:
2154                 pos = NSImageBelow;
2155                 break;
2156
2157             default:
2158                 wxFAIL_MSG( "invalid image position" );
2159                 pos = NSNoImage;
2160         }
2161
2162         [m_osxView setImagePosition:pos];
2163     }
2164 }
2165
2166 void wxWidgetCocoaImpl::SetupTabs( const wxNotebook& WXUNUSED(notebook))
2167 {
2168     // implementation in subclass
2169 }
2170
2171 void wxWidgetCocoaImpl::GetBestRect( wxRect *r ) const
2172 {
2173     r->x = r->y = r->width = r->height = 0;
2174
2175     if (  [m_osxView respondsToSelector:@selector(sizeToFit)] )
2176     {
2177         NSRect former = [m_osxView frame];
2178         [m_osxView sizeToFit];
2179         NSRect best = [m_osxView frame];
2180         [m_osxView setFrame:former];
2181         r->width = (int)best.size.width;
2182         r->height = (int)best.size.height;
2183     }
2184 }
2185
2186 bool wxWidgetCocoaImpl::IsEnabled() const
2187 {
2188     NSView* targetView = m_osxView;
2189     if ( [m_osxView isKindOfClass:[NSScrollView class] ] )
2190         targetView = [(NSScrollView*) m_osxView documentView];
2191
2192     if ( [targetView respondsToSelector:@selector(isEnabled) ] )
2193         return [targetView isEnabled];
2194     return true;
2195 }
2196
2197 void wxWidgetCocoaImpl::Enable( bool enable )
2198 {
2199     NSView* targetView = m_osxView;
2200     if ( [m_osxView isKindOfClass:[NSScrollView class] ] )
2201         targetView = [(NSScrollView*) m_osxView documentView];
2202
2203     if ( [targetView respondsToSelector:@selector(setEnabled:) ] )
2204         [targetView setEnabled:enable];
2205 }
2206
2207 void wxWidgetCocoaImpl::PulseGauge()
2208 {
2209 }
2210
2211 void wxWidgetCocoaImpl::SetScrollThumb( wxInt32 WXUNUSED(val), wxInt32 WXUNUSED(view) )
2212 {
2213 }
2214
2215 void wxWidgetCocoaImpl::SetControlSize( wxWindowVariant variant )
2216 {
2217     NSControlSize size = NSRegularControlSize;
2218
2219     switch ( variant )
2220     {
2221         case wxWINDOW_VARIANT_NORMAL :
2222             size = NSRegularControlSize;
2223             break ;
2224
2225         case wxWINDOW_VARIANT_SMALL :
2226             size = NSSmallControlSize;
2227             break ;
2228
2229         case wxWINDOW_VARIANT_MINI :
2230             size = NSMiniControlSize;
2231             break ;
2232
2233         case wxWINDOW_VARIANT_LARGE :
2234             size = NSRegularControlSize;
2235             break ;
2236
2237         default:
2238             wxFAIL_MSG(wxT("unexpected window variant"));
2239             break ;
2240     }
2241     if ( [m_osxView respondsToSelector:@selector(setControlSize:)] )
2242         [m_osxView setControlSize:size];
2243     else if ([m_osxView respondsToSelector:@selector(cell)])
2244     {
2245         id cell = [(id)m_osxView cell];
2246         if ([cell respondsToSelector:@selector(setControlSize:)])
2247             [cell setControlSize:size];
2248     }
2249 }
2250
2251 void wxWidgetCocoaImpl::SetFont(wxFont const& font, wxColour const&col, long, bool)
2252 {
2253     if ([m_osxView respondsToSelector:@selector(setFont:)])
2254         [m_osxView setFont: font.OSXGetNSFont()];
2255     if ([m_osxView respondsToSelector:@selector(setTextColor:)])
2256         [m_osxView setTextColor:[NSColor colorWithCalibratedRed:(CGFloat) (col.Red() / 255.0)
2257                                                                  green:(CGFloat) (col.Green() / 255.0)
2258                                                                   blue:(CGFloat) (col.Blue() / 255.0)
2259                                                                  alpha:(CGFloat) (col.Alpha() / 255.0)]];
2260 }
2261
2262 void wxWidgetCocoaImpl::SetToolTip(wxToolTip* tooltip)
2263 {
2264     if (tooltip)
2265     {
2266         wxCFStringRef cf( tooltip->GetTip() , m_wxPeer->GetFont().GetEncoding() );
2267         [m_osxView setToolTip: cf.AsNSString()];
2268     }
2269     else 
2270         [m_osxView setToolTip: nil];
2271
2272 }
2273
2274 void wxWidgetCocoaImpl::InstallEventHandler( WXWidget control )
2275 {
2276     WXWidget c =  control ? control : (WXWidget) m_osxView;
2277     wxWidgetImpl::Associate( c, this ) ;
2278     if ([c respondsToSelector:@selector(setAction:)])
2279     {
2280         [c setTarget: c];
2281         [c setAction: @selector(controlAction:)];
2282         if ([c respondsToSelector:@selector(setDoubleAction:)])
2283         {
2284             [c setDoubleAction: @selector(controlDoubleAction:)];
2285         }
2286
2287     }
2288 }
2289
2290 bool wxWidgetCocoaImpl::DoHandleCharEvent(NSEvent *event, NSString *text)
2291 {
2292     wxKeyEvent wxevent(wxEVT_CHAR);
2293     SetupKeyEvent( wxevent, event, text );
2294
2295     return GetWXPeer()->OSXHandleKeyEvent(wxevent);
2296 }
2297
2298 bool wxWidgetCocoaImpl::DoHandleKeyEvent(NSEvent *event)
2299 {
2300     wxKeyEvent wxevent(wxEVT_KEY_DOWN);
2301     SetupKeyEvent( wxevent, event );
2302
2303     // Generate wxEVT_CHAR_HOOK before sending any other events but only when
2304     // the key is pressed, not when it's released (the type of wxevent is
2305     // changed by SetupKeyEvent() so it can be wxEVT_KEY_UP too by now).
2306     if ( wxevent.GetEventType() == wxEVT_KEY_DOWN )
2307     {
2308         wxKeyEvent eventHook(wxEVT_CHAR_HOOK, wxevent);
2309         if ( GetWXPeer()->OSXHandleKeyEvent(eventHook) )
2310             return true;
2311     }
2312
2313     bool result = GetWXPeer()->OSXHandleKeyEvent(wxevent);
2314
2315     // this will fire higher level events, like insertText, to help
2316     // us handle EVT_CHAR, etc.
2317
2318     if ( !result )
2319     {
2320         if ( IsUserPane() && [event type] == NSKeyDown)
2321         {
2322             long keycode = wxOSXTranslateCocoaKey( event, wxEVT_CHAR );
2323             
2324             if ( (keycode > 0 && keycode < WXK_SPACE) || keycode == WXK_DELETE || keycode >= WXK_START )
2325             {
2326                 // eventually we could setup a doCommandBySelector catcher and retransform this into the wx key chars
2327                 wxKeyEvent wxevent2(wxevent) ;
2328                 wxevent2.SetEventType(wxEVT_CHAR);
2329                 wxevent2.m_keyCode = keycode;
2330                 result = GetWXPeer()->OSXHandleKeyEvent(wxevent2);
2331             }
2332             else
2333             {
2334                 if ( !wxevent.CmdDown() )
2335                 {
2336                     if ( [m_osxView isKindOfClass:[NSScrollView class] ] )
2337                         [[(NSScrollView*)m_osxView documentView] interpretKeyEvents:[NSArray arrayWithObject:event]];
2338                     else
2339                         [m_osxView interpretKeyEvents:[NSArray arrayWithObject:event]];
2340                     result = true;
2341                 }
2342             }
2343         }
2344     }
2345
2346     return result;
2347 }
2348
2349 bool wxWidgetCocoaImpl::DoHandleMouseEvent(NSEvent *event)
2350 {
2351     wxMouseEvent wxevent(wxEVT_LEFT_DOWN);
2352     SetupMouseEvent(wxevent , event) ;
2353
2354     return GetWXPeer()->HandleWindowEvent(wxevent);
2355 }
2356
2357 void wxWidgetCocoaImpl::DoNotifyFocusEvent(bool receivedFocus, wxWidgetImpl* otherWindow)
2358 {
2359     wxWindow* thisWindow = GetWXPeer();
2360     if ( thisWindow->MacGetTopLevelWindow() && NeedsFocusRect() )
2361     {
2362         thisWindow->MacInvalidateBorders();
2363     }
2364
2365     if ( receivedFocus )
2366     {
2367         wxLogTrace(wxT("Focus"), wxT("focus set(%p)"), static_cast<void*>(thisWindow));
2368         wxChildFocusEvent eventFocus((wxWindow*)thisWindow);
2369         thisWindow->HandleWindowEvent(eventFocus);
2370
2371 #if wxUSE_CARET
2372         if ( thisWindow->GetCaret() )
2373             thisWindow->GetCaret()->OnSetFocus();
2374 #endif
2375
2376         wxFocusEvent event(wxEVT_SET_FOCUS, thisWindow->GetId());
2377         event.SetEventObject(thisWindow);
2378         if (otherWindow)
2379             event.SetWindow(otherWindow->GetWXPeer());
2380         thisWindow->HandleWindowEvent(event) ;
2381     }
2382     else // !receivedFocuss
2383     {
2384 #if wxUSE_CARET
2385         if ( thisWindow->GetCaret() )
2386             thisWindow->GetCaret()->OnKillFocus();
2387 #endif
2388
2389         wxLogTrace(wxT("Focus"), wxT("focus lost(%p)"), static_cast<void*>(thisWindow));
2390
2391         wxFocusEvent event( wxEVT_KILL_FOCUS, thisWindow->GetId());
2392         event.SetEventObject(thisWindow);
2393         if (otherWindow)
2394             event.SetWindow(otherWindow->GetWXPeer());
2395         thisWindow->HandleWindowEvent(event) ;
2396     }
2397 }
2398
2399 void wxWidgetCocoaImpl::SetCursor(const wxCursor& cursor)
2400 {
2401     if ( !wxIsBusy() )
2402     {
2403         NSPoint location = [NSEvent mouseLocation];
2404         location = [[m_osxView window] convertScreenToBase:location];
2405         NSPoint locationInView = [m_osxView convertPoint:location fromView:nil];
2406
2407         if( NSMouseInRect(locationInView, [m_osxView bounds], YES) )
2408         {
2409             [(NSCursor*)cursor.GetHCURSOR() set];
2410         }
2411     }
2412     [[m_osxView window] invalidateCursorRectsForView:m_osxView];
2413 }
2414
2415 void wxWidgetCocoaImpl::CaptureMouse()
2416 {
2417     [[m_osxView window] disableCursorRects];
2418 }
2419
2420 void wxWidgetCocoaImpl::ReleaseMouse()
2421 {
2422     [[m_osxView window] enableCursorRects];
2423 }
2424
2425 void wxWidgetCocoaImpl::SetFlipped(bool flipped)
2426 {
2427     m_isFlipped = flipped;
2428 }
2429
2430 //
2431 // Factory methods
2432 //
2433
2434 wxWidgetImpl* wxWidgetImpl::CreateUserPane( wxWindowMac* wxpeer, wxWindowMac* WXUNUSED(parent),
2435     wxWindowID WXUNUSED(id), const wxPoint& pos, const wxSize& size,
2436     long WXUNUSED(style), long WXUNUSED(extraStyle))
2437 {
2438     NSRect r = wxOSXGetFrameForControl( wxpeer, pos , size ) ;
2439     wxNSView* v = [[wxNSView alloc] initWithFrame:r];
2440
2441     // temporary hook for dnd
2442     [v registerForDraggedTypes:[NSArray arrayWithObjects:
2443         NSStringPboardType, NSFilenamesPboardType, NSTIFFPboardType, NSPICTPboardType, NSPDFPboardType, nil]];
2444
2445     wxWidgetCocoaImpl* c = new wxWidgetCocoaImpl( wxpeer, v, false, true );
2446     return c;
2447 }
2448
2449 wxWidgetImpl* wxWidgetImpl::CreateContentView( wxNonOwnedWindow* now )
2450 {
2451     NSWindow* tlw = now->GetWXWindow();
2452     
2453     wxWidgetCocoaImpl* c = NULL;
2454     if ( now->IsNativeWindowWrapper() )
2455     {
2456         NSView* cv = [tlw contentView];
2457         c = new wxWidgetCocoaImpl( now, cv, true );
2458         // increase ref count, because the impl destructor will decrement it again
2459         CFRetain(cv);
2460         if ( !now->IsShown() )
2461             [cv setHidden:NO];
2462         
2463     }
2464     else
2465     {
2466         wxNSView* v = [[wxNSView alloc] initWithFrame:[[tlw contentView] frame]];
2467         c = new wxWidgetCocoaImpl( now, v, true );
2468         c->InstallEventHandler();
2469         [tlw setContentView:v];
2470     }
2471     return c;
2472 }