]> git.saurik.com Git - wxWidgets.git/blobdiff - src/osx/cocoa/window.mm
Fix horizontal mouse wheel scrolling in wxGTK.
[wxWidgets.git] / src / osx / cocoa / window.mm
index faaff0b3504024f6b719f999ae2d72ed3c7d07ca..78cd6f99aa9716875b025ff422d719f15105f812 100644 (file)
@@ -4,7 +4,6 @@
 // Author:      Stefan Csomor
 // Modified by:
 // Created:     2008-06-20
-// RCS-ID:      $Id$
 // Copyright:   (c) Stefan Csomor
 // Licence:     wxWindows licence
 /////////////////////////////////////////////////////////////////////////////
@@ -17,6 +16,7 @@
     #include "wx/log.h"
     #include "wx/textctrl.h"
     #include "wx/combobox.h"
+    #include "wx/radiobut.h"
 #endif
 
 #ifdef __WXMAC__
@@ -41,7 +41,7 @@
 
 // Get the window with the focus
 
-NSView* GetViewFromResponder( NSResponder* responder )
+NSView* wxOSXGetViewFromResponder( NSResponder* responder )
 {
     NSView* view = nil;
     if ( [responder isKindOfClass:[NSTextView class]] )
@@ -64,16 +64,29 @@ NSView* GetFocusedViewInWindow( NSWindow* keyWindow )
 {
     NSView* focusedView = nil;
     if ( keyWindow != nil )
-        focusedView = GetViewFromResponder([keyWindow firstResponder]);
+        focusedView = wxOSXGetViewFromResponder([keyWindow firstResponder]);
 
     return focusedView;
 }
 
 WXWidget wxWidgetImpl::FindFocus()
 {
-    return GetFocusedViewInWindow( [NSApp keyWindow] );
+    return GetFocusedViewInWindow( [NSApp keyWindow] );;
 }
 
+wxWidgetImpl* wxWidgetImpl::FindBestFromWXWidget(WXWidget control)
+{
+    wxWidgetImpl* impl = FindFromWXWidget(control);
+    
+    // NSScrollViews can have their subviews like NSClipView
+    // therefore check and use the NSScrollView peer in that case
+    if ( impl == NULL && [[control superview] isKindOfClass:[NSScrollView class]])
+        impl = FindFromWXWidget([control superview]);
+    
+    return impl;
+}
+
+
 NSRect wxOSXGetFrameForControl( wxWindowMac* window , const wxPoint& pos , const wxSize &size , bool adjustForOrigin )
 {
     int x, y, w, h ;
@@ -96,6 +109,22 @@ NSRect wxOSXGetFrameForControl( wxWindowMac* window , const wxPoint& pos , const
 
 @end // wxNSView
 
+@interface wxNSView(TextInput) <NSTextInputClient>
+
+- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange;
+- (void)doCommandBySelector:(SEL)aSelector;
+- (void)setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange;
+- (void)unmarkText;
+- (NSRange)selectedRange;
+- (NSRange)markedRange;
+- (BOOL)hasMarkedText;
+- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange;
+- (NSArray*)validAttributesForMarkedText;
+- (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange;
+- (NSUInteger)characterIndexForPoint:(NSPoint)aPoint;
+
+@end
+
 @interface NSView(PossibleMethods)
 - (void)setTitle:(NSString *)aString;
 - (void)setStringValue:(NSString *)aString;
@@ -669,11 +698,15 @@ void wxWidgetCocoaImpl::SetupMouseEvent( wxMouseEvent &wxevent , NSEvent * nsEve
             
             wxevent.m_wheelDelta = 10;
             wxevent.m_linesPerAction = 1;
+            wxevent.m_columnsPerAction = 1;
                 
             if ( fabs(deltaX) > fabs(deltaY) )
             {
+                // wx conventions for horizontal are inverted from vertical (originating from native msw behavior)
+                // right and up are positive values, left and down are negative values, while on OSX right and down
+                // are negative and left and up are positive.
                 wxevent.m_wheelAxis = wxMOUSE_WHEEL_HORIZONTAL;
-                wxevent.m_wheelRotation = (int)deltaX;
+                wxevent.m_wheelRotation = -(int)deltaX;
             }
             else
             {
@@ -819,6 +852,71 @@ void wxWidgetCocoaImpl::SetupMouseEvent( wxMouseEvent &wxevent , NSEvent * nsEve
 
 @end // wxNSView
 
+// We need to adopt NSTextInputClient protocol in order to interpretKeyEvents: to work.
+// Currently, only insertText:(replacementRange:) is
+// implemented here, and the rest of the methods are stubs.
+// It is hoped that someday IME-related functionality is implemented in
+// wxWidgets and the methods of this protocol are fully working.
+
+@implementation wxNSView(TextInput)
+
+void wxOSX_insertText(NSView* self, SEL _cmd, NSString* text);
+
+- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange
+{
+    wxOSX_insertText(self, @selector(insertText:), aString);
+}
+
+- (void)doCommandBySelector:(SEL)aSelector
+{
+    // these are already caught in the keyEvent handler
+}
+
+- (void)setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
+{
+}
+
+- (void)unmarkText
+{
+}
+
+- (NSRange)selectedRange
+{    
+    return NSMakeRange(NSNotFound, 0);
+}
+
+- (NSRange)markedRange
+{
+    return NSMakeRange(NSNotFound, 0);
+}
+
+- (BOOL)hasMarkedText
+{
+    return NO;
+}
+
+- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
+{
+    return nil;
+}
+
+- (NSArray*)validAttributesForMarkedText
+{
+    return nil;
+}
+
+- (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
+{
+    return NSMakeRect(0, 0, 0, 0);
+}
+- (NSUInteger)characterIndexForPoint:(NSPoint)aPoint
+{
+    return NSNotFound;
+}
+
+@end // wxNSView(TextInput)
+
+
 //
 // event handlers
 //
@@ -1171,16 +1269,11 @@ bool wxWidgetCocoaImpl::performDragOperation(void* s, WXWidget WXUNUSED(slf), vo
     return result != wxDragNone;
 }
 
-typedef void (*wxOSX_TextEventHandlerPtr)(NSView* self, SEL _cmd, NSString *event);
-typedef void (*wxOSX_EventHandlerPtr)(NSView* self, SEL _cmd, NSEvent *event);
-typedef BOOL (*wxOSX_PerformKeyEventHandlerPtr)(NSView* self, SEL _cmd, NSEvent *event);
-typedef BOOL (*wxOSX_FocusHandlerPtr)(NSView* self, SEL _cmd);
-
 void wxWidgetCocoaImpl::mouseEvent(WX_NSEvent event, WXWidget slf, void *_cmd)
 {
     // we are getting moved events for all windows in the hierarchy, not something wx expects
     // therefore we only handle it for the deepest child in the hierarchy
-    if ( 1 /* [event type] == NSMouseMoved */ )
+    if ( [event type] == NSMouseMoved )
     {
         NSView* hitview = [[[slf window] contentView] hitTest:[event locationInWindow]];
         if ( hitview == NULL || hitview != slf)
@@ -1190,7 +1283,8 @@ void wxWidgetCocoaImpl::mouseEvent(WX_NSEvent event, WXWidget slf, void *_cmd)
     if ( !DoHandleMouseEvent(event) )
     {
         // for plain NSView mouse events would propagate to parents otherwise
-        if (!IsUserPane())
+        // scrollwheel events have to be propagated if not handled in all cases
+        if (!IsUserPane() || [event type] == NSScrollWheel )
         {
             wxOSX_EventHandlerPtr superimpl = (wxOSX_EventHandlerPtr) [[slf superclass] instanceMethodForSelector:(SEL)_cmd];
             superimpl(slf, (SEL)_cmd, event);
@@ -1295,14 +1389,14 @@ bool wxWidgetCocoaImpl::performKeyEquivalent(WX_NSEvent event, WXWidget slf, voi
     {
         wxEvtHandler * const handler = m_wxPeer->GetEventHandler();
         
-        wxCommandEvent command_event( wxEVT_COMMAND_MENU_SELECTED, command );
+        wxCommandEvent command_event( wxEVT_MENU, command );
         command_event.SetEventObject( wxevent.GetEventObject() );
         handled = handler->ProcessEvent( command_event );
         
         if ( !handled )
         {
             // accelerators can also be used with buttons, try them too
-            command_event.SetEventType(wxEVT_COMMAND_BUTTON_CLICKED);
+            command_event.SetEventType(wxEVT_BUTTON);
             handled = handler->ProcessEvent( command_event );
         }
     }
@@ -1346,20 +1440,17 @@ bool wxWidgetCocoaImpl::resignFirstResponder(WXWidget slf, void *_cmd)
 {
     wxOSX_FocusHandlerPtr superimpl = (wxOSX_FocusHandlerPtr) [[slf superclass] instanceMethodForSelector:(SEL)_cmd];
     BOOL r = superimpl(slf, (SEL)_cmd);
-    // get the current focus after running resignFirstResponder
-    // note that this value isn't reliable, it might return the same view that
-    // is resigning
-    NSView* otherView = FindFocus();
-    wxWidgetImpl* otherWindow = FindFromWXWidget(otherView);
+    NSResponder * responder = wxNonOwnedWindowCocoaImpl::GetNextFirstResponder();
+    NSView* otherView = wxOSXGetViewFromResponder(responder);
 
-    // It doesn't make sense to notify about the loss of focus if we're not
-    // really losing it and the window which has just gained focus is the same
-    // one as this window itself. Of course, this should never happen in the
-    // first place but somehow it does in wxGrid code and without this check we
-    // enter into an infinite recursion, see #12267.
+    wxWidgetImpl* otherWindow = FindBestFromWXWidget(otherView);
+    
+    // It doesn't make sense to notify about the loss of focus if it's the same
+    // control in the end, and just a different subview
     if ( otherWindow == this )
         return r;
-
+    
     // NSTextViews have an editor as true responder, therefore the might get the
     // resign notification if their editor takes over, don't trigger any event then
     if ( r && !m_hasEditor)
@@ -1695,6 +1786,17 @@ void wxWidgetCocoaImpl::SetVisibility( bool visible )
     [m_osxView setHidden:(visible ? NO:YES)];
 }
 
+double wxWidgetCocoaImpl::GetContentScaleFactor() const
+{
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
+    NSWindow* tlw = [m_osxView window];
+    if ( [ tlw respondsToSelector:@selector(backingScaleFactor) ] )
+        return [tlw backingScaleFactor];
+    else
+#endif
+        return 1.0;
+}
+
 // ----------------------------------------------------------------------------
 // window animation stuff
 // ----------------------------------------------------------------------------
@@ -1740,7 +1842,6 @@ void wxWidgetCocoaImpl::SetVisibility( bool visible )
     wxUnusedVar(progress);
 
     m_win->SendSizeEvent();
-    m_win->MacOnInternalSize();
 }
 
 - (void)animationDidEnd:(NSAnimation*)animation
@@ -1917,7 +2018,6 @@ wxWidgetCocoaImpl::ShowViewOrWindowWithEffect(wxWindow *win,
         // refresh it once again after the end to ensure that everything is in
         // place
         win->SendSizeEvent();
-        win->MacOnInternalSize();
     }
 
     [anim setDelegate:nil];
@@ -2514,7 +2614,13 @@ void wxWidgetCocoaImpl::InstallEventHandler( WXWidget control )
     if ([c respondsToSelector:@selector(setAction:)])
     {
         [c setTarget: c];
-        [c setAction: @selector(controlAction:)];
+        if ( dynamic_cast<wxRadioButton*>(GetWXPeer()) )
+        {
+            // everything already set up
+        }
+        else
+            [c setAction: @selector(controlAction:)];
+        
         if ([c respondsToSelector:@selector(setDoubleAction:)])
         {
             [c setDoubleAction: @selector(controlDoubleAction:)];
@@ -2522,17 +2628,25 @@ void wxWidgetCocoaImpl::InstallEventHandler( WXWidget control )
 
     }
     NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited|NSTrackingCursorUpdate|NSTrackingMouseMoved|NSTrackingActiveAlways|NSTrackingInVisibleRect;
-        NSTrackingArea* area = [[NSTrackingArea alloc] initWithRect: NSZeroRect options: options owner: m_osxView userInfo: nil];
+    NSTrackingArea* area = [[NSTrackingArea alloc] initWithRect: NSZeroRect options: options owner: m_osxView userInfo: nil];
     [m_osxView addTrackingArea: area];
     [area release];
  }
 
 bool wxWidgetCocoaImpl::DoHandleCharEvent(NSEvent *event, NSString *text)
 {
-    wxKeyEvent wxevent(wxEVT_CHAR);
-    SetupKeyEvent( wxevent, event, text );
+    bool result = false;
+    
+    for (NSUInteger i = 0; i < [text length]; ++i)
+    {
+        wxKeyEvent wxevent(wxEVT_CHAR);
+        unichar c = [text characterAtIndex:i];
+        SetupKeyEvent( wxevent, event, [NSString stringWithCharacters:&c length:1]);
 
-    return GetWXPeer()->OSXHandleKeyEvent(wxevent);
+        result = GetWXPeer()->OSXHandleKeyEvent(wxevent) || result;
+    }
+    
+    return result;
 }
 
 bool wxWidgetCocoaImpl::DoHandleKeyEvent(NSEvent *event)