Added wxNonOwnedWindow::SetShape(wxGraphicsPath).
[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     }
1035 }
1036
1037 void wxWidgetCocoaImpl::keyEvent(WX_NSEvent event, WXWidget slf, void *_cmd)
1038 {
1039     if ( [event type] == NSKeyDown )
1040     {
1041         // there are key equivalents that are not command-combos and therefore not handled by cocoa automatically, 
1042         // therefore we call the menubar directly here, exit if the menu is handling the shortcut
1043         if ( [[[NSApplication sharedApplication] mainMenu] performKeyEquivalent:event] )
1044             return;
1045     
1046         m_lastKeyDownEvent = event;
1047     }
1048     
1049     if ( GetFocusedViewInWindow([slf window]) != slf || m_hasEditor || !DoHandleKeyEvent(event) )
1050     {
1051         wxOSX_EventHandlerPtr superimpl = (wxOSX_EventHandlerPtr) [[slf superclass] instanceMethodForSelector:(SEL)_cmd];
1052         superimpl(slf, (SEL)_cmd, event);
1053     }
1054     m_lastKeyDownEvent = NULL;
1055 }
1056
1057 void wxWidgetCocoaImpl::insertText(NSString* text, WXWidget slf, void *_cmd)
1058 {
1059     if ( m_lastKeyDownEvent==NULL || m_hasEditor || !DoHandleCharEvent(m_lastKeyDownEvent, text) )
1060     {
1061         wxOSX_TextEventHandlerPtr superimpl = (wxOSX_TextEventHandlerPtr) [[slf superclass] instanceMethodForSelector:(SEL)_cmd];
1062         superimpl(slf, (SEL)_cmd, text);
1063     }
1064 }
1065
1066
1067 bool wxWidgetCocoaImpl::performKeyEquivalent(WX_NSEvent event, WXWidget slf, void *_cmd)
1068 {
1069     bool handled = false;
1070     
1071     wxKeyEvent wxevent(wxEVT_KEY_DOWN);
1072     SetupKeyEvent( wxevent, event );
1073    
1074     // because performKeyEquivalent is going up the entire view hierarchy, we don't have to
1075     // walk up the ancestors ourselves but let cocoa do it
1076     
1077     int command = m_wxPeer->GetAcceleratorTable()->GetCommand( wxevent );
1078     if (command != -1)
1079     {
1080         wxEvtHandler * const handler = m_wxPeer->GetEventHandler();
1081         
1082         wxCommandEvent command_event( wxEVT_COMMAND_MENU_SELECTED, command );
1083         handled = handler->ProcessEvent( command_event );
1084         
1085         if ( !handled )
1086         {
1087             // accelerators can also be used with buttons, try them too
1088             command_event.SetEventType(wxEVT_COMMAND_BUTTON_CLICKED);
1089             handled = handler->ProcessEvent( command_event );
1090         }
1091     }
1092     
1093     if ( !handled )
1094     {
1095         wxOSX_PerformKeyEventHandlerPtr superimpl = (wxOSX_PerformKeyEventHandlerPtr) [[slf superclass] instanceMethodForSelector:(SEL)_cmd];
1096         return superimpl(slf, (SEL)_cmd, event);
1097     }
1098     return YES;
1099 }
1100
1101 bool wxWidgetCocoaImpl::acceptsFirstResponder(WXWidget slf, void *_cmd)
1102 {
1103     if ( IsUserPane() )
1104         return m_wxPeer->AcceptsFocus();
1105     else
1106     {
1107         wxOSX_FocusHandlerPtr superimpl = (wxOSX_FocusHandlerPtr) [[slf superclass] instanceMethodForSelector:(SEL)_cmd];
1108         return superimpl(slf, (SEL)_cmd);
1109     }
1110 }
1111
1112 bool wxWidgetCocoaImpl::becomeFirstResponder(WXWidget slf, void *_cmd)
1113 {
1114     wxOSX_FocusHandlerPtr superimpl = (wxOSX_FocusHandlerPtr) [[slf superclass] instanceMethodForSelector:(SEL)_cmd];
1115     // get the current focus before running becomeFirstResponder
1116     NSView* otherView = FindFocus();
1117
1118     wxWidgetImpl* otherWindow = FindFromWXWidget(otherView);
1119     BOOL r = superimpl(slf, (SEL)_cmd);
1120     if ( r )
1121     {
1122         DoNotifyFocusEvent( true, otherWindow );
1123     }
1124
1125     return r;
1126 }
1127
1128 bool wxWidgetCocoaImpl::resignFirstResponder(WXWidget slf, void *_cmd)
1129 {
1130     wxOSX_FocusHandlerPtr superimpl = (wxOSX_FocusHandlerPtr) [[slf superclass] instanceMethodForSelector:(SEL)_cmd];
1131     BOOL r = superimpl(slf, (SEL)_cmd);
1132     // get the current focus after running resignFirstResponder
1133     // note that this value isn't reliable, it might return the same view that
1134     // is resigning
1135     NSView* otherView = FindFocus();
1136     wxWidgetImpl* otherWindow = FindFromWXWidget(otherView);
1137
1138     // It doesn't make sense to notify about the loss of focus if we're not
1139     // really losing it and the window which has just gained focus is the same
1140     // one as this window itself. Of course, this should never happen in the
1141     // first place but somehow it does in wxGrid code and without this check we
1142     // enter into an infinite recursion, see #12267.
1143     if ( otherWindow == this )
1144         return r;
1145
1146     // NSTextViews have an editor as true responder, therefore the might get the
1147     // resign notification if their editor takes over, don't trigger any event then
1148     if ( r && !m_hasEditor)
1149     {
1150         DoNotifyFocusEvent( false, otherWindow );
1151     }
1152     return r;
1153 }
1154
1155 void wxWidgetCocoaImpl::resetCursorRects(WXWidget slf, void *_cmd)
1156 {
1157     wxWindow* wxpeer = GetWXPeer();
1158     if ( wxpeer )
1159     {
1160         NSCursor *cursor = (NSCursor*)wxpeer->GetCursor().GetHCURSOR();
1161         if (cursor == NULL)
1162         {
1163             wxOSX_ResetCursorRectsHandlerPtr superimpl = (wxOSX_ResetCursorRectsHandlerPtr) [[slf superclass] instanceMethodForSelector:(SEL)_cmd];
1164             superimpl(slf, (SEL)_cmd);
1165         }
1166         else
1167         {
1168             [slf addCursorRect: [slf bounds]
1169                 cursor: cursor];
1170         }
1171     }
1172 }
1173
1174 bool wxWidgetCocoaImpl::isFlipped(WXWidget WXUNUSED(slf), void *WXUNUSED(_cmd))
1175 {
1176     return m_isFlipped;
1177 }
1178
1179
1180 #define OSX_DEBUG_DRAWING 0
1181
1182 void wxWidgetCocoaImpl::drawRect(void* rect, WXWidget slf, void *WXUNUSED(_cmd))
1183 {
1184     // preparing the update region
1185     
1186     wxRegion updateRgn;
1187     const NSRect *rects;
1188     NSInteger count;
1189
1190     [slf getRectsBeingDrawn:&rects count:&count];
1191     for ( int i = 0 ; i < count ; ++i )
1192     {
1193         updateRgn.Union(wxFromNSRect(slf, rects[i]));
1194     }
1195
1196     wxWindow* wxpeer = GetWXPeer();
1197
1198     if ( wxpeer->MacGetLeftBorderSize() != 0 || wxpeer->MacGetTopBorderSize() != 0 )
1199     {
1200         // as this update region is in native window locals we must adapt it to wx window local
1201         updateRgn.Offset( wxpeer->MacGetLeftBorderSize() , wxpeer->MacGetTopBorderSize() );
1202     }
1203     
1204     // Restrict the update region to the shape of the window, if any, and also
1205     // remember the region that we need to clear later.
1206     wxNonOwnedWindow* const tlwParent = wxpeer->MacGetTopLevelWindow();
1207     const bool isTopLevel = tlwParent == wxpeer;
1208     wxRegion clearRgn;
1209     if ( tlwParent->GetWindowStyle() & wxFRAME_SHAPED )
1210     {
1211         if ( isTopLevel )
1212             clearRgn = updateRgn;
1213
1214         int xoffset = 0, yoffset = 0;
1215         wxRegion rgn = tlwParent->GetShape();
1216         wxpeer->MacRootWindowToWindow( &xoffset, &yoffset );
1217         rgn.Offset( xoffset, yoffset );
1218         updateRgn.Intersect(rgn);
1219
1220         if ( isTopLevel )
1221         {
1222             // Exclude the window shape from the region to be cleared below.
1223             rgn.Xor(wxpeer->GetSize());
1224             clearRgn.Intersect(rgn);
1225         }
1226     }
1227     
1228     wxpeer->GetUpdateRegion() = updateRgn;
1229
1230     // setting up the drawing context
1231     
1232     CGContextRef context = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort];
1233     CGContextSaveGState( context );
1234     
1235 #if OSX_DEBUG_DRAWING
1236     CGContextBeginPath( context );
1237     CGContextMoveToPoint(context, 0, 0);
1238     NSRect bounds = [slf bounds];
1239     CGContextAddLineToPoint(context, 10, 0);
1240     CGContextMoveToPoint(context, 0, 0);
1241     CGContextAddLineToPoint(context, 0, 10);
1242     CGContextMoveToPoint(context, bounds.size.width, bounds.size.height);
1243     CGContextAddLineToPoint(context, bounds.size.width, bounds.size.height-10);
1244     CGContextMoveToPoint(context, bounds.size.width, bounds.size.height);
1245     CGContextAddLineToPoint(context, bounds.size.width-10, bounds.size.height);
1246     CGContextClosePath( context );
1247     CGContextStrokePath(context);
1248 #endif
1249     
1250     if ( !m_isFlipped )
1251     {
1252         CGContextTranslateCTM( context, 0,  [m_osxView bounds].size.height );
1253         CGContextScaleCTM( context, 1, -1 );
1254     }
1255     
1256     wxpeer->MacSetCGContextRef( context );
1257
1258     bool handled = wxpeer->MacDoRedraw( 0 );
1259     CGContextRestoreGState( context );
1260
1261     CGContextSaveGState( context );
1262     if ( !handled )
1263     {
1264         // call super
1265         SEL _cmd = @selector(drawRect:);
1266         wxOSX_DrawRectHandlerPtr superimpl = (wxOSX_DrawRectHandlerPtr) [[slf superclass] instanceMethodForSelector:_cmd];
1267         superimpl(slf, _cmd, *(NSRect*)rect);
1268         CGContextRestoreGState( context );
1269         CGContextSaveGState( context );
1270     }
1271     // as we called restore above, we have to flip again if necessary
1272     if ( !m_isFlipped )
1273     {
1274         CGContextTranslateCTM( context, 0,  [m_osxView bounds].size.height );
1275         CGContextScaleCTM( context, 1, -1 );
1276     }
1277
1278     if ( isTopLevel )
1279     {
1280         // We also need to explicitly draw the part of the top level window
1281         // outside of its region with transparent colour to ensure that it is
1282         // really transparent.
1283         if ( clearRgn.IsOk() )
1284         {
1285             wxMacCGContextStateSaver saveState(context);
1286             wxWindowDC dc(wxpeer);
1287             dc.SetBackground(wxBrush(wxTransparentColour));
1288             dc.SetDeviceClippingRegion(clearRgn);
1289             dc.Clear();
1290         }
1291
1292 #if wxUSE_GRAPHICS_CONTEXT
1293         // If the window shape is defined by a path, stroke the path to show
1294         // the window border.
1295         const wxGraphicsPath& path = tlwParent->GetShapePath();
1296         if ( !path.IsNull() )
1297         {
1298             CGContextSetLineWidth(context, 1);
1299             CGContextSetStrokeColorWithColor(context, wxLIGHT_GREY->GetCGColor());
1300             CGContextAddPath(context, (CGPathRef) path.GetNativePath());
1301             CGContextStrokePath(context);
1302         }
1303 #endif // wxUSE_GRAPHICS_CONTEXT
1304     }
1305
1306     wxpeer->MacPaintChildrenBorders();
1307     wxpeer->MacSetCGContextRef( NULL );
1308     CGContextRestoreGState( context );
1309 }
1310
1311 void wxWidgetCocoaImpl::controlAction( WXWidget WXUNUSED(slf), void *WXUNUSED(_cmd), void *WXUNUSED(sender))
1312 {
1313     wxWindow* wxpeer = (wxWindow*) GetWXPeer();
1314     if ( wxpeer )
1315         wxpeer->OSXHandleClicked(0);
1316 }
1317
1318 void wxWidgetCocoaImpl::controlDoubleAction( WXWidget WXUNUSED(slf), void *WXUNUSED(_cmd), void *WXUNUSED(sender))
1319 {
1320 }
1321
1322 void wxWidgetCocoaImpl::controlTextDidChange()
1323 {
1324     wxWindow* wxpeer = (wxWindow*)GetWXPeer();
1325     if ( wxpeer ) 
1326     {
1327         // since native rtti doesn't have to be enabled and wx' rtti is not aware of the mixin wxTextEntry, workaround is needed
1328         wxTextCtrl *tc = wxDynamicCast( wxpeer , wxTextCtrl );
1329         wxComboBox *cb = wxDynamicCast( wxpeer , wxComboBox );
1330         if ( tc )
1331             tc->SendTextUpdatedEventIfAllowed();
1332         else if ( cb )
1333             cb->SendTextUpdatedEventIfAllowed();
1334         else 
1335         {
1336             wxFAIL_MSG("Unexpected class for controlTextDidChange event");
1337         }
1338     }
1339 }
1340
1341 //
1342
1343 #if OBJC_API_VERSION >= 2
1344
1345 #define wxOSX_CLASS_ADD_METHOD( c, s, i, t ) \
1346     class_addMethod(c, s, i, t );
1347
1348 #else
1349
1350 #define wxOSX_CLASS_ADD_METHOD( c, s, i, t ) \
1351     { s, (char*) t, i },
1352
1353 #endif
1354
1355 void wxOSXCocoaClassAddWXMethods(Class c)
1356 {
1357
1358 #if OBJC_API_VERSION < 2
1359     static objc_method wxmethods[] =
1360     {
1361 #endif
1362
1363     wxOSX_CLASS_ADD_METHOD(c, @selector(mouseDown:), (IMP) wxOSX_mouseEvent, "v@:@" )
1364     wxOSX_CLASS_ADD_METHOD(c, @selector(rightMouseDown:), (IMP) wxOSX_mouseEvent, "v@:@" )
1365     wxOSX_CLASS_ADD_METHOD(c, @selector(otherMouseDown:), (IMP) wxOSX_mouseEvent, "v@:@" )
1366
1367     wxOSX_CLASS_ADD_METHOD(c, @selector(mouseUp:), (IMP) wxOSX_mouseEvent, "v@:@" )
1368     wxOSX_CLASS_ADD_METHOD(c, @selector(rightMouseUp:), (IMP) wxOSX_mouseEvent, "v@:@" )
1369     wxOSX_CLASS_ADD_METHOD(c, @selector(otherMouseUp:), (IMP) wxOSX_mouseEvent, "v@:@" )
1370
1371     wxOSX_CLASS_ADD_METHOD(c, @selector(mouseMoved:), (IMP) wxOSX_mouseEvent, "v@:@" )
1372
1373     wxOSX_CLASS_ADD_METHOD(c, @selector(mouseDragged:), (IMP) wxOSX_mouseEvent, "v@:@" )
1374     wxOSX_CLASS_ADD_METHOD(c, @selector(rightMouseDragged:), (IMP) wxOSX_mouseEvent, "v@:@" )
1375     wxOSX_CLASS_ADD_METHOD(c, @selector(otherMouseDragged:), (IMP) wxOSX_mouseEvent, "v@:@" )
1376     
1377     wxOSX_CLASS_ADD_METHOD(c, @selector(acceptsFirstMouse:), (IMP) wxOSX_acceptsFirstMouse, "v@:@" )
1378
1379     wxOSX_CLASS_ADD_METHOD(c, @selector(scrollWheel:), (IMP) wxOSX_mouseEvent, "v@:@" )
1380     wxOSX_CLASS_ADD_METHOD(c, @selector(mouseEntered:), (IMP) wxOSX_mouseEvent, "v@:@" )
1381     wxOSX_CLASS_ADD_METHOD(c, @selector(mouseExited:), (IMP) wxOSX_mouseEvent, "v@:@" )
1382
1383     wxOSX_CLASS_ADD_METHOD(c, @selector(keyDown:), (IMP) wxOSX_keyEvent, "v@:@" )
1384     wxOSX_CLASS_ADD_METHOD(c, @selector(keyUp:), (IMP) wxOSX_keyEvent, "v@:@" )
1385     wxOSX_CLASS_ADD_METHOD(c, @selector(flagsChanged:), (IMP) wxOSX_keyEvent, "v@:@" )
1386
1387     wxOSX_CLASS_ADD_METHOD(c, @selector(insertText:), (IMP) wxOSX_insertText, "v@:@" )
1388
1389     wxOSX_CLASS_ADD_METHOD(c, @selector(performKeyEquivalent:), (IMP) wxOSX_performKeyEquivalent, "c@:@" )
1390
1391     wxOSX_CLASS_ADD_METHOD(c, @selector(acceptsFirstResponder), (IMP) wxOSX_acceptsFirstResponder, "c@:" )
1392     wxOSX_CLASS_ADD_METHOD(c, @selector(becomeFirstResponder), (IMP) wxOSX_becomeFirstResponder, "c@:" )
1393     wxOSX_CLASS_ADD_METHOD(c, @selector(resignFirstResponder), (IMP) wxOSX_resignFirstResponder, "c@:" )
1394     wxOSX_CLASS_ADD_METHOD(c, @selector(resetCursorRects), (IMP) wxOSX_resetCursorRects, "v@:" )
1395
1396     wxOSX_CLASS_ADD_METHOD(c, @selector(isFlipped), (IMP) wxOSX_isFlipped, "c@:" )
1397     wxOSX_CLASS_ADD_METHOD(c, @selector(drawRect:), (IMP) wxOSX_drawRect, "v@:{_NSRect={_NSPoint=ff}{_NSSize=ff}}" )
1398
1399     wxOSX_CLASS_ADD_METHOD(c, @selector(controlAction:), (IMP) wxOSX_controlAction, "v@:@" )
1400     wxOSX_CLASS_ADD_METHOD(c, @selector(controlDoubleAction:), (IMP) wxOSX_controlDoubleAction, "v@:@" )
1401
1402 #if wxUSE_DRAG_AND_DROP
1403     wxOSX_CLASS_ADD_METHOD(c, @selector(draggingEntered:), (IMP) wxOSX_draggingEntered, "I@:@" )
1404     wxOSX_CLASS_ADD_METHOD(c, @selector(draggingUpdated:), (IMP) wxOSX_draggingUpdated, "I@:@" )
1405     wxOSX_CLASS_ADD_METHOD(c, @selector(draggingExited:), (IMP) wxOSX_draggingExited, "v@:@" )
1406     wxOSX_CLASS_ADD_METHOD(c, @selector(performDragOperation:), (IMP) wxOSX_performDragOperation, "c@:@" )
1407 #endif
1408
1409 #if OBJC_API_VERSION < 2
1410     } ;
1411     static int method_count = WXSIZEOF( wxmethods );
1412     static objc_method_list *wxmethodlist = NULL;
1413     if ( wxmethodlist == NULL )
1414     {
1415         wxmethodlist = (objc_method_list*) malloc(sizeof(objc_method_list) + sizeof(wxmethods) );
1416         memcpy( &wxmethodlist->method_list[0], &wxmethods[0], sizeof(wxmethods) );
1417         wxmethodlist->method_count = method_count;
1418         wxmethodlist->obsolete = 0;
1419     }
1420     class_addMethods( c, wxmethodlist );
1421 #endif
1422 }
1423
1424 //
1425 // C++ implementation class
1426 //
1427
1428 IMPLEMENT_DYNAMIC_CLASS( wxWidgetCocoaImpl , wxWidgetImpl )
1429
1430 wxWidgetCocoaImpl::wxWidgetCocoaImpl( wxWindowMac* peer , WXWidget w, bool isRootControl, bool isUserPane ) :
1431     wxWidgetImpl( peer, isRootControl, isUserPane )
1432 {
1433     Init();
1434     m_osxView = w;
1435
1436     // check if the user wants to create the control initially hidden
1437     if ( !peer->IsShown() )
1438         SetVisibility(false);
1439
1440     // gc aware handling
1441     if ( m_osxView )
1442         CFRetain(m_osxView);
1443     [m_osxView release];
1444 }
1445
1446 wxWidgetCocoaImpl::wxWidgetCocoaImpl()
1447 {
1448     Init();
1449 }
1450
1451 void wxWidgetCocoaImpl::Init()
1452 {
1453     m_osxView = NULL;
1454     m_isFlipped = true;
1455     m_lastKeyDownEvent = NULL;
1456     m_hasEditor = false;
1457 }
1458
1459 wxWidgetCocoaImpl::~wxWidgetCocoaImpl()
1460 {
1461     RemoveAssociations( this );
1462
1463     if ( !IsRootControl() )
1464     {
1465         NSView *sv = [m_osxView superview];
1466         if ( sv != nil )
1467             [m_osxView removeFromSuperview];
1468     }
1469     // gc aware handling
1470     if ( m_osxView )
1471         CFRelease(m_osxView);
1472 }
1473
1474 bool wxWidgetCocoaImpl::IsVisible() const
1475 {
1476     return [m_osxView isHiddenOrHasHiddenAncestor] == NO;
1477 }
1478
1479 void wxWidgetCocoaImpl::SetVisibility( bool visible )
1480 {
1481     [m_osxView setHidden:(visible ? NO:YES)];
1482 }
1483
1484 // ----------------------------------------------------------------------------
1485 // window animation stuff
1486 // ----------------------------------------------------------------------------
1487
1488 // define a delegate used to refresh the window during animation
1489 @interface wxNSAnimationDelegate : NSObject wxOSX_10_6_AND_LATER(<NSAnimationDelegate>)
1490 {
1491     wxWindow *m_win;
1492     bool m_isDone;
1493 }
1494
1495 - (id)init:(wxWindow *)win;
1496
1497 - (bool)isDone;
1498
1499 // NSAnimationDelegate methods
1500 - (void)animationDidEnd:(NSAnimation*)animation;
1501 - (void)animation:(NSAnimation*)animation
1502         didReachProgressMark:(NSAnimationProgress)progress;
1503 @end
1504
1505 @implementation wxNSAnimationDelegate
1506
1507 - (id)init:(wxWindow *)win
1508 {
1509     self = [super init];
1510
1511     m_win = win;
1512     m_isDone = false;
1513
1514     return self;
1515 }
1516
1517 - (bool)isDone
1518 {
1519     return m_isDone;
1520 }
1521
1522 - (void)animation:(NSAnimation*)animation
1523         didReachProgressMark:(NSAnimationProgress)progress
1524 {
1525     wxUnusedVar(animation);
1526     wxUnusedVar(progress);
1527
1528     m_win->SendSizeEvent();
1529 }
1530
1531 - (void)animationDidEnd:(NSAnimation*)animation
1532 {
1533     wxUnusedVar(animation);
1534     m_isDone = true;
1535 }
1536
1537 @end
1538
1539 /* static */
1540 bool
1541 wxWidgetCocoaImpl::ShowViewOrWindowWithEffect(wxWindow *win,
1542                                               bool show,
1543                                               wxShowEffect effect,
1544                                               unsigned timeout)
1545 {
1546     // create the dictionary describing the animation to perform on this view
1547     NSObject * const
1548         viewOrWin = static_cast<NSObject *>(win->OSXGetViewOrWindow());
1549     NSMutableDictionary * const
1550         dict = [NSMutableDictionary dictionaryWithCapacity:4];
1551     [dict setObject:viewOrWin forKey:NSViewAnimationTargetKey];
1552
1553     // determine the start and end rectangles assuming we're hiding the window
1554     const wxRect rectOrig = win->GetRect();
1555     wxRect rectStart,
1556            rectEnd;
1557     rectStart =
1558     rectEnd = rectOrig;
1559
1560     if ( show )
1561     {
1562         if ( effect == wxSHOW_EFFECT_ROLL_TO_LEFT ||
1563                 effect == wxSHOW_EFFECT_SLIDE_TO_LEFT )
1564             effect = wxSHOW_EFFECT_ROLL_TO_RIGHT;
1565         else if ( effect == wxSHOW_EFFECT_ROLL_TO_RIGHT ||
1566                     effect == wxSHOW_EFFECT_SLIDE_TO_RIGHT )
1567             effect = wxSHOW_EFFECT_ROLL_TO_LEFT;
1568         else if ( effect == wxSHOW_EFFECT_ROLL_TO_TOP ||
1569                     effect == wxSHOW_EFFECT_SLIDE_TO_TOP )
1570             effect = wxSHOW_EFFECT_ROLL_TO_BOTTOM;
1571         else if ( effect == wxSHOW_EFFECT_ROLL_TO_BOTTOM ||
1572                     effect == wxSHOW_EFFECT_SLIDE_TO_BOTTOM )
1573             effect = wxSHOW_EFFECT_ROLL_TO_TOP;
1574     }
1575
1576     switch ( effect )
1577     {
1578         case wxSHOW_EFFECT_ROLL_TO_LEFT:
1579         case wxSHOW_EFFECT_SLIDE_TO_LEFT:
1580             rectEnd.width = 0;
1581             break;
1582
1583         case wxSHOW_EFFECT_ROLL_TO_RIGHT:
1584         case wxSHOW_EFFECT_SLIDE_TO_RIGHT:
1585             rectEnd.x = rectStart.GetRight();
1586             rectEnd.width = 0;
1587             break;
1588
1589         case wxSHOW_EFFECT_ROLL_TO_TOP:
1590         case wxSHOW_EFFECT_SLIDE_TO_TOP:
1591             rectEnd.height = 0;
1592             break;
1593
1594         case wxSHOW_EFFECT_ROLL_TO_BOTTOM:
1595         case wxSHOW_EFFECT_SLIDE_TO_BOTTOM:
1596             rectEnd.y = rectStart.GetBottom();
1597             rectEnd.height = 0;
1598             break;
1599
1600         case wxSHOW_EFFECT_EXPAND:
1601             rectEnd.x = rectStart.x + rectStart.width / 2;
1602             rectEnd.y = rectStart.y + rectStart.height / 2;
1603             rectEnd.width =
1604             rectEnd.height = 0;
1605             break;
1606
1607         case wxSHOW_EFFECT_BLEND:
1608             [dict setObject:(show ? NSViewAnimationFadeInEffect
1609                                   : NSViewAnimationFadeOutEffect)
1610                   forKey:NSViewAnimationEffectKey];
1611             break;
1612
1613         case wxSHOW_EFFECT_NONE:
1614         case wxSHOW_EFFECT_MAX:
1615             wxFAIL_MSG( "unexpected animation effect" );
1616             return false;
1617
1618         default:
1619             wxFAIL_MSG( "unknown animation effect" );
1620             return false;
1621     };
1622
1623     if ( show )
1624     {
1625         // we need to restore it to the original rectangle instead of making it
1626         // disappear
1627         wxSwap(rectStart, rectEnd);
1628
1629         // and as the window is currently hidden, we need to show it for the
1630         // animation to be visible at all (but don't restore it at its full
1631         // rectangle as it shouldn't appear immediately)
1632         win->SetSize(rectStart);
1633         win->Show();
1634     }
1635
1636     NSView * const parentView = [viewOrWin isKindOfClass:[NSView class]]
1637                                     ? [(NSView *)viewOrWin superview]
1638                                     : nil;
1639     const NSRect rStart = wxToNSRect(parentView, rectStart);
1640     const NSRect rEnd = wxToNSRect(parentView, rectEnd);
1641
1642     [dict setObject:[NSValue valueWithRect:rStart]
1643           forKey:NSViewAnimationStartFrameKey];
1644     [dict setObject:[NSValue valueWithRect:rEnd]
1645           forKey:NSViewAnimationEndFrameKey];
1646
1647     // create an animation using the values in the above dictionary
1648     NSViewAnimation * const
1649         anim = [[NSViewAnimation alloc]
1650                 initWithViewAnimations:[NSArray arrayWithObject:dict]];
1651
1652     if ( !timeout )
1653     {
1654         // what is a good default duration? Windows uses 200ms, Web frameworks
1655         // use anything from 250ms to 1s... choose something in the middle
1656         timeout = 500;
1657     }
1658
1659     [anim setDuration:timeout/1000.];   // duration is in seconds here
1660
1661     // if the window being animated changes its layout depending on its size
1662     // (which is almost always the case) we need to redo it during animation
1663     //
1664     // the number of layouts here is arbitrary, but 10 seems like too few (e.g.
1665     // controls in wxInfoBar visibly jump around)
1666     const int NUM_LAYOUTS = 20;
1667     for ( float f = 1./NUM_LAYOUTS; f < 1.; f += 1./NUM_LAYOUTS )
1668         [anim addProgressMark:f];
1669
1670     wxNSAnimationDelegate * const
1671         animDelegate = [[wxNSAnimationDelegate alloc] init:win];
1672     [anim setDelegate:animDelegate];
1673     [anim startAnimation];
1674
1675     // Cocoa is capable of doing animation asynchronously or even from separate
1676     // thread but wx API doesn't provide any way to be notified about the
1677     // animation end and without this we really must ensure that the window has
1678     // the expected (i.e. the same as if a simple Show() had been used) size
1679     // when we return, so block here until the animation finishes
1680     //
1681     // notice that because the default animation mode is NSAnimationBlocking,
1682     // no user input events ought to be processed from here
1683     {
1684         wxEventLoopGuarantor ensureEventLoopExistence;
1685         wxEventLoopBase * const loop = wxEventLoopBase::GetActive();
1686         while ( ![animDelegate isDone] )
1687             loop->Dispatch();
1688     }
1689
1690     if ( !show )
1691     {
1692         // NSViewAnimation is smart enough to hide the NSView being animated at
1693         // the end but we also must ensure that it's hidden for wx too
1694         win->Hide();
1695
1696         // and we must also restore its size because it isn't expected to
1697         // change just because the window was hidden
1698         win->SetSize(rectOrig);
1699     }
1700     else
1701     {
1702         // refresh it once again after the end to ensure that everything is in
1703         // place
1704         win->SendSizeEvent();
1705     }
1706
1707     [anim setDelegate:nil];
1708     [animDelegate release];
1709     [anim release];
1710
1711     return true;
1712 }
1713
1714 bool wxWidgetCocoaImpl::ShowWithEffect(bool show,
1715                                        wxShowEffect effect,
1716                                        unsigned timeout)
1717 {
1718     return ShowViewOrWindowWithEffect(m_wxPeer, show, effect, timeout);
1719 }
1720
1721 void wxWidgetCocoaImpl::Raise()
1722 {
1723     // Not implemented
1724 }
1725
1726 void wxWidgetCocoaImpl::Lower()
1727 {
1728     // Not implemented
1729 }
1730
1731 void wxWidgetCocoaImpl::ScrollRect( const wxRect *WXUNUSED(rect), int WXUNUSED(dx), int WXUNUSED(dy) )
1732 {
1733 #if 1
1734     SetNeedsDisplay() ;
1735 #else
1736     // We should do something like this, but it wasn't working in 10.4.
1737     if (GetNeedsDisplay() )
1738     {
1739         SetNeedsDisplay() ;
1740     }
1741     NSRect r = wxToNSRect( [m_osxView superview], *rect );
1742     NSSize offset = NSMakeSize((float)dx, (float)dy);
1743     [m_osxView scrollRect:r by:offset];
1744 #endif
1745 }
1746
1747 void wxWidgetCocoaImpl::Move(int x, int y, int width, int height)
1748 {
1749     wxWindowMac* parent = GetWXPeer()->GetParent();
1750     // under Cocoa we might have a contentView in the wxParent to which we have to
1751     // adjust the coordinates
1752     if (parent && [m_osxView superview] != parent->GetHandle() )
1753     {
1754         int cx = 0,cy = 0,cw = 0,ch = 0;
1755         if ( parent->GetPeer() )
1756         {
1757             parent->GetPeer()->GetContentArea(cx, cy, cw, ch);
1758             x -= cx;
1759             y -= cy;
1760         }
1761     }
1762     [[m_osxView superview] setNeedsDisplayInRect:[m_osxView frame]];
1763     NSRect r = wxToNSRect( [m_osxView superview], wxRect(x,y,width, height) );
1764     [m_osxView setFrame:r];
1765     [[m_osxView superview] setNeedsDisplayInRect:r];
1766
1767     wxNSView* wxview = (wxNSView*)m_osxView;
1768 #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
1769     if ([wxview respondsToSelector:@selector(updateTrackingArea)] )
1770         [wxview updateTrackingArea]; 
1771 #else
1772     if ([m_osxView respondsToSelector:@selector(trackingTag)] )
1773     {
1774         if ( [wxview trackingTag] )
1775             [wxview removeTrackingRect: [wxview trackingTag]];
1776
1777         [wxview setTrackingTag: [wxview addTrackingRect: [m_osxView bounds] owner: wxview userData: nil assumeInside: NO]];
1778     }
1779 #endif
1780 }
1781
1782 void wxWidgetCocoaImpl::GetPosition( int &x, int &y ) const
1783 {
1784     wxRect r = wxFromNSRect( [m_osxView superview], [m_osxView frame] );
1785     x = r.GetLeft();
1786     y = r.GetTop();
1787     
1788     // under Cocoa we might have a contentView in the wxParent to which we have to
1789     // adjust the coordinates
1790     wxWindowMac* parent = GetWXPeer()->GetParent();
1791     if (parent && [m_osxView superview] != parent->GetHandle() )
1792     {
1793         int cx = 0,cy = 0,cw = 0,ch = 0;
1794         if ( parent->GetPeer() )
1795         {
1796             parent->GetPeer()->GetContentArea(cx, cy, cw, ch);
1797             x += cx;
1798             y += cy;
1799         }
1800     }
1801 }
1802
1803 void wxWidgetCocoaImpl::GetSize( int &width, int &height ) const
1804 {
1805     NSRect rect = [m_osxView frame];
1806     width = (int)rect.size.width;
1807     height = (int)rect.size.height;
1808 }
1809
1810 void wxWidgetCocoaImpl::GetContentArea( int&left, int &top, int &width, int &height ) const
1811 {
1812     if ( [m_osxView respondsToSelector:@selector(contentView) ] )
1813     {
1814         NSView* cv = [m_osxView contentView];
1815
1816         NSRect bounds = [m_osxView bounds];
1817         NSRect rect = [cv frame];
1818
1819         int y = (int)rect.origin.y;
1820         int x = (int)rect.origin.x;
1821         if ( ![ m_osxView isFlipped ] )
1822             y = (int)(bounds.size.height - (rect.origin.y + rect.size.height));
1823         left = x;
1824         top = y;
1825         width = (int)rect.size.width;
1826         height = (int)rect.size.height;
1827     }
1828     else
1829     {
1830         left = top = 0;
1831         GetSize( width, height );
1832     }
1833 }
1834
1835 void wxWidgetCocoaImpl::SetNeedsDisplay( const wxRect* where )
1836 {
1837     if ( where )
1838         [m_osxView setNeedsDisplayInRect:wxToNSRect(m_osxView, *where )];
1839     else
1840         [m_osxView setNeedsDisplay:YES];
1841 }
1842
1843 bool wxWidgetCocoaImpl::GetNeedsDisplay() const
1844 {
1845     return [m_osxView needsDisplay];
1846 }
1847
1848 bool wxWidgetCocoaImpl::CanFocus() const
1849 {
1850     return [m_osxView canBecomeKeyView] == YES;
1851 }
1852
1853 bool wxWidgetCocoaImpl::HasFocus() const
1854 {
1855     return ( FindFocus() == m_osxView );
1856 }
1857
1858 bool wxWidgetCocoaImpl::SetFocus()
1859 {
1860     if ( !CanFocus() )
1861         return false;
1862
1863     [[m_osxView window] makeKeyAndOrderFront:nil] ;
1864     [[m_osxView window] makeFirstResponder: m_osxView] ;
1865     return true;
1866 }
1867
1868
1869 void wxWidgetCocoaImpl::RemoveFromParent()
1870 {
1871     [m_osxView removeFromSuperview];
1872 }
1873
1874 void wxWidgetCocoaImpl::Embed( wxWidgetImpl *parent )
1875 {
1876     NSView* container = parent->GetWXWidget() ;
1877     wxASSERT_MSG( container != NULL , wxT("No valid mac container control") ) ;
1878     [container addSubview:m_osxView];
1879 }
1880
1881 void wxWidgetCocoaImpl::SetBackgroundColour( const wxColour &col )
1882 {
1883     NSView* targetView = m_osxView;
1884     if ( [m_osxView isKindOfClass:[NSScrollView class] ] )
1885         targetView = [(NSScrollView*) m_osxView documentView];
1886
1887     if ( [targetView respondsToSelector:@selector(setBackgroundColor:) ] )
1888     {
1889         [targetView setBackgroundColor:[NSColor colorWithCalibratedRed:(CGFloat) (col.Red() / 255.0)
1890                                                                 green:(CGFloat) (col.Green() / 255.0)
1891                                                                  blue:(CGFloat) (col.Blue() / 255.0)
1892                                                                 alpha:(CGFloat) (col.Alpha() / 255.0)]];
1893     }
1894 }
1895
1896 bool wxWidgetCocoaImpl::SetBackgroundStyle( wxBackgroundStyle style )
1897 {
1898     BOOL opaque = ( style == wxBG_STYLE_PAINT );
1899     
1900     if ( [m_osxView respondsToSelector:@selector(setOpaque:) ] )
1901     {
1902         [m_osxView setOpaque: opaque];
1903     }
1904     
1905     return true ;
1906 }
1907
1908 void wxWidgetCocoaImpl::SetLabel( const wxString& title, wxFontEncoding encoding )
1909 {
1910     if ( [m_osxView respondsToSelector:@selector(setTitle:) ] )
1911     {
1912         wxCFStringRef cf( title , encoding );
1913         [m_osxView setTitle:cf.AsNSString()];
1914     }
1915     else if ( [m_osxView respondsToSelector:@selector(setStringValue:) ] )
1916     {
1917         wxCFStringRef cf( title , encoding );
1918         [m_osxView setStringValue:cf.AsNSString()];
1919     }
1920 }
1921
1922
1923 void  wxWidgetImpl::Convert( wxPoint *pt , wxWidgetImpl *from , wxWidgetImpl *to )
1924 {
1925     NSPoint p = wxToNSPoint( from->GetWXWidget(), *pt );
1926     p = [from->GetWXWidget() convertPoint:p toView:to->GetWXWidget() ];
1927     *pt = wxFromNSPoint( to->GetWXWidget(), p );
1928 }
1929
1930 wxInt32 wxWidgetCocoaImpl::GetValue() const
1931 {
1932     return [(NSControl*)m_osxView intValue];
1933 }
1934
1935 void wxWidgetCocoaImpl::SetValue( wxInt32 v )
1936 {
1937     if (  [m_osxView respondsToSelector:@selector(setIntValue:)] )
1938     {
1939         [m_osxView setIntValue:v];
1940     }
1941     else if (  [m_osxView respondsToSelector:@selector(setFloatValue:)] )
1942     {
1943         [m_osxView setFloatValue:(double)v];
1944     }
1945     else if (  [m_osxView respondsToSelector:@selector(setDoubleValue:)] )
1946     {
1947         [m_osxView setDoubleValue:(double)v];
1948     }
1949 }
1950
1951 void wxWidgetCocoaImpl::SetMinimum( wxInt32 v )
1952 {
1953     if (  [m_osxView respondsToSelector:@selector(setMinValue:)] )
1954     {
1955         [m_osxView setMinValue:(double)v];
1956     }
1957 }
1958
1959 void wxWidgetCocoaImpl::SetMaximum( wxInt32 v )
1960 {
1961     if (  [m_osxView respondsToSelector:@selector(setMaxValue:)] )
1962     {
1963         [m_osxView setMaxValue:(double)v];
1964     }
1965 }
1966
1967 wxInt32 wxWidgetCocoaImpl::GetMinimum() const
1968 {
1969     if (  [m_osxView respondsToSelector:@selector(minValue)] )
1970     {
1971         return (int)[m_osxView minValue];
1972     }
1973     return 0;
1974 }
1975
1976 wxInt32 wxWidgetCocoaImpl::GetMaximum() const
1977 {
1978     if (  [m_osxView respondsToSelector:@selector(maxValue)] )
1979     {
1980         return (int)[m_osxView maxValue];
1981     }
1982     return 0;
1983 }
1984
1985 wxBitmap wxWidgetCocoaImpl::GetBitmap() const
1986 {
1987     wxBitmap bmp;
1988
1989     // TODO: how to create a wxBitmap from NSImage?
1990 #if 0
1991     if ( [m_osxView respondsToSelector:@selector(image:)] )
1992         bmp = [m_osxView image];
1993 #endif
1994
1995     return bmp;
1996 }
1997
1998 void wxWidgetCocoaImpl::SetBitmap( const wxBitmap& bitmap )
1999 {
2000     if (  [m_osxView respondsToSelector:@selector(setImage:)] )
2001     {
2002         if (bitmap.IsOk())
2003             [m_osxView setImage:bitmap.GetNSImage()];
2004         else
2005             [m_osxView setImage:nil];
2006
2007         [m_osxView setNeedsDisplay:YES];
2008     }
2009 }
2010
2011 void wxWidgetCocoaImpl::SetBitmapPosition( wxDirection dir )
2012 {
2013     if ( [m_osxView respondsToSelector:@selector(setImagePosition:)] )
2014     {
2015         NSCellImagePosition pos;
2016         switch ( dir )
2017         {
2018             case wxLEFT:
2019                 pos = NSImageLeft;
2020                 break;
2021
2022             case wxRIGHT:
2023                 pos = NSImageRight;
2024                 break;
2025
2026             case wxTOP:
2027                 pos = NSImageAbove;
2028                 break;
2029
2030             case wxBOTTOM:
2031                 pos = NSImageBelow;
2032                 break;
2033
2034             default:
2035                 wxFAIL_MSG( "invalid image position" );
2036                 pos = NSNoImage;
2037         }
2038
2039         [m_osxView setImagePosition:pos];
2040     }
2041 }
2042
2043 void wxWidgetCocoaImpl::SetupTabs( const wxNotebook& WXUNUSED(notebook))
2044 {
2045     // implementation in subclass
2046 }
2047
2048 void wxWidgetCocoaImpl::GetBestRect( wxRect *r ) const
2049 {
2050     r->x = r->y = r->width = r->height = 0;
2051
2052     if (  [m_osxView respondsToSelector:@selector(sizeToFit)] )
2053     {
2054         NSRect former = [m_osxView frame];
2055         [m_osxView sizeToFit];
2056         NSRect best = [m_osxView frame];
2057         [m_osxView setFrame:former];
2058         r->width = (int)best.size.width;
2059         r->height = (int)best.size.height;
2060     }
2061 }
2062
2063 bool wxWidgetCocoaImpl::IsEnabled() const
2064 {
2065     NSView* targetView = m_osxView;
2066     if ( [m_osxView isKindOfClass:[NSScrollView class] ] )
2067         targetView = [(NSScrollView*) m_osxView documentView];
2068
2069     if ( [targetView respondsToSelector:@selector(isEnabled) ] )
2070         return [targetView isEnabled];
2071     return true;
2072 }
2073
2074 void wxWidgetCocoaImpl::Enable( bool enable )
2075 {
2076     NSView* targetView = m_osxView;
2077     if ( [m_osxView isKindOfClass:[NSScrollView class] ] )
2078         targetView = [(NSScrollView*) m_osxView documentView];
2079
2080     if ( [targetView respondsToSelector:@selector(setEnabled:) ] )
2081         [targetView setEnabled:enable];
2082 }
2083
2084 void wxWidgetCocoaImpl::PulseGauge()
2085 {
2086 }
2087
2088 void wxWidgetCocoaImpl::SetScrollThumb( wxInt32 WXUNUSED(val), wxInt32 WXUNUSED(view) )
2089 {
2090 }
2091
2092 void wxWidgetCocoaImpl::SetControlSize( wxWindowVariant variant )
2093 {
2094     NSControlSize size = NSRegularControlSize;
2095
2096     switch ( variant )
2097     {
2098         case wxWINDOW_VARIANT_NORMAL :
2099             size = NSRegularControlSize;
2100             break ;
2101
2102         case wxWINDOW_VARIANT_SMALL :
2103             size = NSSmallControlSize;
2104             break ;
2105
2106         case wxWINDOW_VARIANT_MINI :
2107             size = NSMiniControlSize;
2108             break ;
2109
2110         case wxWINDOW_VARIANT_LARGE :
2111             size = NSRegularControlSize;
2112             break ;
2113
2114         default:
2115             wxFAIL_MSG(wxT("unexpected window variant"));
2116             break ;
2117     }
2118     if ( [m_osxView respondsToSelector:@selector(setControlSize:)] )
2119         [m_osxView setControlSize:size];
2120     else if ([m_osxView respondsToSelector:@selector(cell)])
2121     {
2122         id cell = [(id)m_osxView cell];
2123         if ([cell respondsToSelector:@selector(setControlSize:)])
2124             [cell setControlSize:size];
2125     }
2126 }
2127
2128 void wxWidgetCocoaImpl::SetFont(wxFont const& font, wxColour const&col, long, bool)
2129 {
2130     if ([m_osxView respondsToSelector:@selector(setFont:)])
2131         [m_osxView setFont: font.OSXGetNSFont()];
2132     if ([m_osxView respondsToSelector:@selector(setTextColor:)])
2133         [m_osxView setTextColor:[NSColor colorWithCalibratedRed:(CGFloat) (col.Red() / 255.0)
2134                                                                  green:(CGFloat) (col.Green() / 255.0)
2135                                                                   blue:(CGFloat) (col.Blue() / 255.0)
2136                                                                  alpha:(CGFloat) (col.Alpha() / 255.0)]];
2137 }
2138
2139 void wxWidgetCocoaImpl::SetToolTip(wxToolTip* tooltip)
2140 {
2141     if (tooltip)
2142     {
2143         wxCFStringRef cf( tooltip->GetTip() , m_wxPeer->GetFont().GetEncoding() );
2144         [m_osxView setToolTip: cf.AsNSString()];
2145     }
2146     else 
2147         [m_osxView setToolTip: nil];
2148
2149 }
2150
2151 void wxWidgetCocoaImpl::InstallEventHandler( WXWidget control )
2152 {
2153     WXWidget c =  control ? control : (WXWidget) m_osxView;
2154     wxWidgetImpl::Associate( c, this ) ;
2155     if ([c respondsToSelector:@selector(setAction:)])
2156     {
2157         [c setTarget: c];
2158         [c setAction: @selector(controlAction:)];
2159         if ([c respondsToSelector:@selector(setDoubleAction:)])
2160         {
2161             [c setDoubleAction: @selector(controlDoubleAction:)];
2162         }
2163
2164     }
2165 }
2166
2167 bool wxWidgetCocoaImpl::DoHandleCharEvent(NSEvent *event, NSString *text)
2168 {
2169     wxKeyEvent wxevent(wxEVT_CHAR);
2170     SetupKeyEvent( wxevent, event, text );
2171
2172     return GetWXPeer()->OSXHandleKeyEvent(wxevent);
2173 }
2174
2175 bool wxWidgetCocoaImpl::DoHandleKeyEvent(NSEvent *event)
2176 {
2177     wxKeyEvent wxevent(wxEVT_KEY_DOWN);
2178     SetupKeyEvent( wxevent, event );
2179     bool result = GetWXPeer()->OSXHandleKeyEvent(wxevent);
2180
2181     // this will fire higher level events, like insertText, to help
2182     // us handle EVT_CHAR, etc.
2183
2184     if ( !result )
2185     {
2186         if ( IsUserPane() && [event type] == NSKeyDown)
2187         {
2188             long keycode = wxOSXTranslateCocoaKey( event, wxEVT_CHAR );
2189             
2190             if ( (keycode > 0 && keycode < WXK_SPACE) || keycode == WXK_DELETE || keycode >= WXK_START )
2191             {
2192                 // eventually we could setup a doCommandBySelector catcher and retransform this into the wx key chars
2193                 wxKeyEvent wxevent2(wxevent) ;
2194                 wxevent2.SetEventType(wxEVT_CHAR);
2195                 wxevent2.m_keyCode = keycode;
2196                 result = GetWXPeer()->OSXHandleKeyEvent(wxevent2);
2197             }
2198             else
2199             {
2200                 if ( !wxevent.CmdDown() )
2201                 {
2202                     if ( [m_osxView isKindOfClass:[NSScrollView class] ] )
2203                         [[(NSScrollView*)m_osxView documentView] interpretKeyEvents:[NSArray arrayWithObject:event]];
2204                     else
2205                         [m_osxView interpretKeyEvents:[NSArray arrayWithObject:event]];
2206                     result = true;
2207                 }
2208             }
2209         }
2210     }
2211
2212     return result;
2213 }
2214
2215 bool wxWidgetCocoaImpl::DoHandleMouseEvent(NSEvent *event)
2216 {
2217     wxMouseEvent wxevent(wxEVT_LEFT_DOWN);
2218     SetupMouseEvent(wxevent , event) ;
2219
2220     return GetWXPeer()->HandleWindowEvent(wxevent);
2221 }
2222
2223 void wxWidgetCocoaImpl::DoNotifyFocusEvent(bool receivedFocus, wxWidgetImpl* otherWindow)
2224 {
2225     wxWindow* thisWindow = GetWXPeer();
2226     if ( thisWindow->MacGetTopLevelWindow() && NeedsFocusRect() )
2227     {
2228         thisWindow->MacInvalidateBorders();
2229     }
2230
2231     if ( receivedFocus )
2232     {
2233         wxLogTrace(wxT("Focus"), wxT("focus set(%p)"), static_cast<void*>(thisWindow));
2234         wxChildFocusEvent eventFocus((wxWindow*)thisWindow);
2235         thisWindow->HandleWindowEvent(eventFocus);
2236
2237 #if wxUSE_CARET
2238         if ( thisWindow->GetCaret() )
2239             thisWindow->GetCaret()->OnSetFocus();
2240 #endif
2241
2242         wxFocusEvent event(wxEVT_SET_FOCUS, thisWindow->GetId());
2243         event.SetEventObject(thisWindow);
2244         if (otherWindow)
2245             event.SetWindow(otherWindow->GetWXPeer());
2246         thisWindow->HandleWindowEvent(event) ;
2247     }
2248     else // !receivedFocuss
2249     {
2250 #if wxUSE_CARET
2251         if ( thisWindow->GetCaret() )
2252             thisWindow->GetCaret()->OnKillFocus();
2253 #endif
2254
2255         wxLogTrace(wxT("Focus"), wxT("focus lost(%p)"), static_cast<void*>(thisWindow));
2256
2257         wxFocusEvent event( wxEVT_KILL_FOCUS, thisWindow->GetId());
2258         event.SetEventObject(thisWindow);
2259         if (otherWindow)
2260             event.SetWindow(otherWindow->GetWXPeer());
2261         thisWindow->HandleWindowEvent(event) ;
2262     }
2263 }
2264
2265 void wxWidgetCocoaImpl::SetCursor(const wxCursor& cursor)
2266 {
2267     if ( !wxIsBusy() )
2268     {
2269         NSPoint location = [NSEvent mouseLocation];
2270         location = [[m_osxView window] convertScreenToBase:location];
2271         NSPoint locationInView = [m_osxView convertPoint:location fromView:nil];
2272
2273         if( NSMouseInRect(locationInView, [m_osxView bounds], YES) )
2274         {
2275             [(NSCursor*)cursor.GetHCURSOR() set];
2276         }
2277     }
2278     [[m_osxView window] invalidateCursorRectsForView:m_osxView];
2279 }
2280
2281 void wxWidgetCocoaImpl::CaptureMouse()
2282 {
2283     [[m_osxView window] disableCursorRects];
2284 }
2285
2286 void wxWidgetCocoaImpl::ReleaseMouse()
2287 {
2288     [[m_osxView window] enableCursorRects];
2289 }
2290
2291 void wxWidgetCocoaImpl::SetFlipped(bool flipped)
2292 {
2293     m_isFlipped = flipped;
2294 }
2295
2296 //
2297 // Factory methods
2298 //
2299
2300 wxWidgetImpl* wxWidgetImpl::CreateUserPane( wxWindowMac* wxpeer, wxWindowMac* WXUNUSED(parent),
2301     wxWindowID WXUNUSED(id), const wxPoint& pos, const wxSize& size,
2302     long WXUNUSED(style), long WXUNUSED(extraStyle))
2303 {
2304     NSRect r = wxOSXGetFrameForControl( wxpeer, pos , size ) ;
2305     wxNSView* v = [[wxNSView alloc] initWithFrame:r];
2306
2307     // temporary hook for dnd
2308     [v registerForDraggedTypes:[NSArray arrayWithObjects:
2309         NSStringPboardType, NSFilenamesPboardType, NSTIFFPboardType, NSPICTPboardType, NSPDFPboardType, nil]];
2310
2311     wxWidgetCocoaImpl* c = new wxWidgetCocoaImpl( wxpeer, v, false, true );
2312     return c;
2313 }
2314
2315 wxWidgetImpl* wxWidgetImpl::CreateContentView( wxNonOwnedWindow* now )
2316 {
2317     NSWindow* tlw = now->GetWXWindow();
2318     
2319     wxWidgetCocoaImpl* c = NULL;
2320     if ( now->IsNativeWindowWrapper() )
2321     {
2322         NSView* cv = [tlw contentView];
2323         c = new wxWidgetCocoaImpl( now, cv, true );
2324         // increase ref count, because the impl destructor will decrement it again
2325         CFRetain(cv);
2326         if ( !now->IsShown() )
2327             [cv setHidden:NO];
2328         
2329     }
2330     else
2331     {
2332         wxNSView* v = [[wxNSView alloc] initWithFrame:[[tlw contentView] frame]];
2333         c = new wxWidgetCocoaImpl( now, v, true );
2334         c->InstallEventHandler();
2335         [tlw setContentView:v];
2336     }
2337     return c;
2338 }