///////////////////////////////////////////////////////////////////////////// // Name: src/osx/cocoa/textctrl.mm // Purpose: wxTextCtrl // Author: Stefan Csomor // Modified by: Ryan Norton (MLTE GetLineLength and GetLineText) // Created: 1998-01-01 // RCS-ID: $Id: textctrl.cpp 54820 2008-07-29 20:04:11Z SC $ // Copyright: (c) Stefan Csomor // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// #include "wx/wxprec.h" #if wxUSE_TEXTCTRL #include "wx/textctrl.h" #ifndef WX_PRECOMP #include "wx/intl.h" #include "wx/app.h" #include "wx/utils.h" #include "wx/dc.h" #include "wx/button.h" #include "wx/menu.h" #include "wx/settings.h" #include "wx/msgdlg.h" #include "wx/toplevel.h" #endif #ifdef __DARWIN__ #include #include #else #include #endif #if wxUSE_STD_IOSTREAM #if wxUSE_IOSTREAMH #include #else #include #endif #endif #include "wx/filefn.h" #include "wx/sysopt.h" #include "wx/thread.h" #include "wx/osx/private.h" #include "wx/osx/cocoa/private/textimpl.h" @interface NSView(EditableView) - (BOOL)isEditable; - (void)setEditable:(BOOL)flag; @end class wxMacEditHelper { public : wxMacEditHelper( NSView* textView ) { m_textView = textView ; if ( textView ) { m_formerState = [textView isEditable]; [textView setEditable:YES]; } } ~wxMacEditHelper() { if ( m_textView ) [m_textView setEditable:m_formerState]; } protected : BOOL m_formerState ; NSView* m_textView; } ; @interface wxNSSecureTextField : NSSecureTextField { } @end @implementation wxNSSecureTextField + (void)initialize { static BOOL initialized = NO; if (!initialized) { initialized = YES; wxOSXCocoaClassAddWXMethods( self ); } } - (void)controlTextDidChange:(NSNotification *)aNotification { wxUnusedVar(aNotification); wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self ); if ( impl ) { wxWindow* wxpeer = (wxWindow*) impl->GetWXPeer(); if ( wxpeer ) { wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, wxpeer->GetId()); event.SetEventObject( wxpeer ); event.SetString( static_cast(wxpeer)->GetValue() ); wxpeer->HandleWindowEvent( event ); } } } - (void)controlTextDidEndEditing:(NSNotification *)aNotification { wxUnusedVar(aNotification); wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self ); if ( impl ) { impl->DoNotifyFocusEvent( false, NULL ); } } @end @interface wxNSTextScrollView : NSScrollView { } @end @interface wxNSTextView : NSTextView { wxNSTextScrollView* scrollView; } - (void)setScrollView: (wxNSTextScrollView *) sv; - (wxNSTextScrollView*) scrollView; @end @implementation wxNSTextScrollView + (void)initialize { static BOOL initialized = NO; if (!initialized) { initialized = YES; wxOSXCocoaClassAddWXMethods( self ); } } - (void)textDidChange:(NSNotification *)aNotification { wxUnusedVar(aNotification); wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self ); if ( impl ) { wxWindow* wxpeer = (wxWindow*) impl->GetWXPeer(); if ( wxpeer ) { wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, wxpeer->GetId()); event.SetEventObject( wxpeer ); event.SetString( static_cast(wxpeer)->GetValue() ); wxpeer->HandleWindowEvent( event ); } } } - (BOOL)textView:(NSTextView *)aTextView doCommandBySelector:(SEL)commandSelector { wxUnusedVar(aTextView); wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self ); if ( impl ) { wxWindow* wxpeer = (wxWindow*) impl->GetWXPeer(); if (commandSelector == @selector(insertNewline:)) { if ( wxpeer && wxpeer->GetWindowStyle() & wxTE_PROCESS_ENTER ) { wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER, wxpeer->GetId()); event.SetEventObject( wxpeer ); event.SetString( static_cast(wxpeer)->GetValue() ); wxpeer->HandleWindowEvent( event ); } } } return NO; } - (void)textDidEndEditing:(NSNotification *)aNotification { wxUnusedVar(aNotification); wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self ); if ( impl ) { impl->DoNotifyFocusEvent( false, NULL ); } } @end @implementation wxNSTextView - (BOOL) becomeFirstResponder { BOOL val = [super becomeFirstResponder]; if ( val ) { wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( scrollView ); if (impl ) impl->DoNotifyFocusEvent( true, NULL ); } return val; } - (void)setScrollView: (wxNSTextScrollView *) sv { scrollView = sv; } - (wxNSTextScrollView*) scrollView { return scrollView; } @end @implementation wxNSTextField + (void)initialize { static BOOL initialized = NO; if (!initialized) { initialized = YES; wxOSXCocoaClassAddWXMethods( self ); } } - (void) setEnabled:(BOOL) flag { [super setEnabled: flag]; if (![self drawsBackground]) { // Static text is drawn incorrectly when disabled. // For an explanation, see // http://www.cocoabuilder.com/archive/message/cocoa/2006/7/21/168028 if (flag) { [self setTextColor: [NSColor controlTextColor]]; } else { [self setTextColor: [NSColor secondarySelectedControlColor]]; } } } - (void)controlTextDidChange:(NSNotification *)aNotification { wxUnusedVar(aNotification); wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self ); if ( impl ) { wxWindow* wxpeer = (wxWindow*) impl->GetWXPeer(); if ( wxpeer ) { wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, wxpeer->GetId()); event.SetEventObject( wxpeer ); event.SetString( static_cast(wxpeer)->GetValue() ); wxpeer->HandleWindowEvent( event ); } } } typedef BOOL (*wxOSX_insertNewlineHandlerPtr)(NSView* self, SEL _cmd, NSControl *control, NSTextView* textView, SEL commandSelector); - (BOOL)control:(NSControl*)control textView:(NSTextView*)textView doCommandBySelector:(SEL)commandSelector { wxUnusedVar(textView); wxUnusedVar(control); if (commandSelector == @selector(insertNewline:)) { wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self ); if ( impl ) { wxWindow* wxpeer = (wxWindow*) impl->GetWXPeer(); if ( wxpeer && wxpeer->GetWindowStyle() & wxTE_PROCESS_ENTER ) { wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER, wxpeer->GetId()); event.SetEventObject( wxpeer ); event.SetString( static_cast(wxpeer)->GetValue() ); wxpeer->HandleWindowEvent( event ); } } } return NO; } - (void)controlTextDidEndEditing:(NSNotification *)aNotification { wxUnusedVar(aNotification); wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self ); if ( impl ) { impl->DoNotifyFocusEvent( false, NULL ); } } @end // wxNSTextViewControl wxNSTextViewControl::wxNSTextViewControl( wxTextCtrl *wxPeer, WXWidget w ) : wxWidgetCocoaImpl(wxPeer, w) { wxNSTextScrollView* sv = (wxNSTextScrollView*) w; m_scrollView = sv; [m_scrollView setHasVerticalScroller:YES]; [m_scrollView setHasHorizontalScroller:NO]; [m_scrollView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; NSSize contentSize = [m_scrollView contentSize]; wxNSTextView* tv = [[wxNSTextView alloc] initWithFrame: NSMakeRect(0, 0, contentSize.width, contentSize.height)]; m_textView = tv; [tv setVerticallyResizable:YES]; [tv setHorizontallyResizable:NO]; [tv setAutoresizingMask:NSViewWidthSizable]; [m_scrollView setDocumentView: tv]; [tv setDelegate: w]; [tv setScrollView:sv]; } wxNSTextViewControl::~wxNSTextViewControl() { if (m_textView) [m_textView setDelegate: nil]; } wxString wxNSTextViewControl::GetStringValue() const { if (m_textView) { wxCFStringRef cf( (CFStringRef) [[m_textView string] retain] ); wxString result = cf.AsString(m_wxPeer->GetFont().GetEncoding()); wxMacConvertNewlines13To10( &result ) ; return result; } return wxEmptyString; } void wxNSTextViewControl::SetStringValue( const wxString &str) { wxString st = str; wxMacConvertNewlines10To13( &st ); wxMacEditHelper helper(m_textView); if (m_textView) [m_textView setString: wxCFStringRef( st , m_wxPeer->GetFont().GetEncoding() ).AsNSString()]; } void wxNSTextViewControl::Copy() { if (m_textView) [m_textView copy:nil]; } void wxNSTextViewControl::Cut() { if (m_textView) [m_textView cut:nil]; } void wxNSTextViewControl::Paste() { if (m_textView) [m_textView paste:nil]; } bool wxNSTextViewControl::CanPaste() const { return true; } void wxNSTextViewControl::SetEditable(bool editable) { if (m_textView) [m_textView setEditable: editable]; } void wxNSTextViewControl::GetSelection( long* from, long* to) const { if (m_textView) { NSRange range = [m_textView selectedRange]; *from = range.location; *to = range.location + range.length; } } void wxNSTextViewControl::SetSelection( long from , long to ) { long textLength = [[m_textView string] length]; if ((from == -1) && (to == -1)) { from = 0 ; to = textLength ; } else { from = wxMin(textLength,wxMax(from,0)) ; if ( to == -1 ) to = textLength; else to = wxMax(0,wxMin(textLength,to)) ; } NSRange selrange = NSMakeRange(from, to-from); [m_textView setSelectedRange:selrange]; [m_textView scrollRangeToVisible:selrange]; } void wxNSTextViewControl::WriteText(const wxString& str) { wxString st = str; wxMacConvertNewlines10To13( &st ); wxMacEditHelper helper(m_textView); [m_textView insertText:wxCFStringRef( st , m_wxPeer->GetFont().GetEncoding() ).AsNSString()]; } void wxNSTextViewControl::SetFont( const wxFont & font , const wxColour& foreground , long windowStyle, bool ignoreBlack ) { if ([m_textView respondsToSelector:@selector(setFont:)]) [m_textView setFont: font.OSXGetNSFont()]; } // wxNSTextFieldControl wxNSTextFieldControl::wxNSTextFieldControl( wxTextCtrl *wxPeer, WXWidget w ) : wxWidgetCocoaImpl(wxPeer, w) { m_textField = (NSTextField*) w; [m_textField setDelegate: w]; m_selStart = m_selEnd = 0; } wxNSTextFieldControl::~wxNSTextFieldControl() { if (m_textField) [m_textField setDelegate: nil]; } wxString wxNSTextFieldControl::GetStringValue() const { wxCFStringRef cf( (CFStringRef) [[m_textField stringValue] retain] ); return cf.AsString(m_wxPeer->GetFont().GetEncoding()); } void wxNSTextFieldControl::SetStringValue( const wxString &str) { wxMacEditHelper helper(m_textField); [m_textField setStringValue: wxCFStringRef( str , m_wxPeer->GetFont().GetEncoding() ).AsNSString()]; } void wxNSTextFieldControl::Copy() { NSText* editor = [m_textField currentEditor]; if ( editor ) { [editor copy:nil]; } } void wxNSTextFieldControl::Cut() { NSText* editor = [m_textField currentEditor]; if ( editor ) { [editor cut:nil]; } } void wxNSTextFieldControl::Paste() { NSText* editor = [m_textField currentEditor]; if ( editor ) { [editor paste:nil]; } } bool wxNSTextFieldControl::CanPaste() const { return true; } void wxNSTextFieldControl::SetEditable(bool editable) { [m_textField setEditable:editable]; } void wxNSTextFieldControl::GetSelection( long* from, long* to) const { NSText* editor = [m_textField currentEditor]; if ( editor ) { NSRange range = [editor selectedRange]; *from = range.location; *to = range.location + range.length; } else { *from = m_selStart; *to = m_selEnd; } } void wxNSTextFieldControl::SetSelection( long from , long to ) { long textLength = [[m_textField stringValue] length]; if ((from == -1) && (to == -1)) { from = 0 ; to = textLength ; } else { from = wxMin(textLength,wxMax(from,0)) ; if ( to == -1 ) to = textLength; else to = wxMax(0,wxMin(textLength,to)) ; } NSText* editor = [m_textField currentEditor]; if ( editor ) { [editor setSelectedRange:NSMakeRange(from, to-from)]; } else { m_selStart = from; m_selEnd = to; } } void wxNSTextFieldControl::WriteText(const wxString& str) { NSText* editor = [m_textField currentEditor]; if ( editor ) { wxMacEditHelper helper(m_textField); [editor insertText:wxCFStringRef( str , m_wxPeer->GetFont().GetEncoding() ).AsNSString()]; } else { wxString val = GetStringValue() ; long start , end ; GetSelection( &start , &end ) ; val.Remove( start , end - start ) ; val.insert( start , str ) ; SetStringValue( val ) ; SetSelection( start + str.length() , start + str.length() ) ; } } void wxNSTextFieldControl::controlAction(WXWidget WXUNUSED(slf), void* WXUNUSED(_cmd), void *WXUNUSED(sender)) { wxWindow* wxpeer = (wxWindow*) GetWXPeer(); if ( wxpeer && (wxpeer->GetWindowStyle() & wxTE_PROCESS_ENTER) ) { wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER, wxpeer->GetId()); event.SetEventObject( wxpeer ); event.SetString( static_cast(wxpeer)->GetValue() ); wxpeer->HandleWindowEvent( event ); } } // // // wxWidgetImplType* wxWidgetImpl::CreateTextControl( wxTextCtrl* wxpeer, wxWindowMac* WXUNUSED(parent), wxWindowID WXUNUSED(id), const wxString& str, const wxPoint& pos, const wxSize& size, long style, long WXUNUSED(extraStyle)) { NSRect r = wxOSXGetFrameForControl( wxpeer, pos , size ) ; wxWidgetCocoaImpl* c = NULL; if ( style & wxTE_MULTILINE || style & wxTE_RICH || style & wxTE_RICH2 ) { wxNSTextScrollView* v = nil; v = [[wxNSTextScrollView alloc] initWithFrame:r]; c = new wxNSTextViewControl( wxpeer, v ); static_cast(c)->SetStringValue(str); } else { NSTextField* v = nil; if ( style & wxTE_PASSWORD ) v = [[wxNSSecureTextField alloc] initWithFrame:r]; else v = [[wxNSTextField alloc] initWithFrame:r]; if ( style & wxNO_BORDER ) { // FIXME: How can we remove the native control's border? // setBordered is separate from the text ctrl's border. } [v setBezeled:NO]; [v setBordered:NO]; c = new wxNSTextFieldControl( wxpeer, v ); static_cast(c)->SetStringValue(str); } return c; } #endif // wxUSE_TEXTCTRL