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