1 /////////////////////////////////////////////////////////////////////////////
 
   2 // Name:        src/osx/iphone/textctrl.mm
 
   4 // Author:      Stefan Csomor
 
   5 // Modified by: Ryan Norton (MLTE GetLineLength and GetLineText)
 
   8 // Copyright:   (c) Stefan Csomor
 
   9 // Licence:     wxWindows licence
 
  10 /////////////////////////////////////////////////////////////////////////////
 
  12 #include "wx/wxprec.h"
 
  16 #include "wx/textctrl.h"
 
  23     #include "wx/button.h"
 
  25     #include "wx/settings.h"
 
  26     #include "wx/msgdlg.h"
 
  27     #include "wx/toplevel.h"
 
  31     #include <sys/types.h>
 
  37 #if wxUSE_STD_IOSTREAM
 
  45 #include "wx/filefn.h"
 
  46 #include "wx/sysopt.h"
 
  47 #include "wx/thread.h"
 
  49 #include "wx/osx/private.h"
 
  50 #include "wx/osx/iphone/private/textimpl.h"
 
  52 // currently for some reasong the UITextField leads to a recursion when the keyboard should be shown, so let's leave the code
 
  53 // in case this gets resolved...
 
  55 #define wxOSX_IPHONE_USE_TEXTFIELD 1
 
  60     wxMacEditHelper( UITextView* textView )
 
  62         m_textView = textView;
 
  66             m_formerState = [textView isEditable];
 
  67             [textView setEditable:YES];
 
  74             [m_textView setEditable:m_formerState];
 
  79     UITextView* m_textView;
 
  82 #if wxOSX_IPHONE_USE_TEXTFIELD
 
  84 @interface  wxUITextFieldDelegate : NSObject<UITextFieldDelegate>
 
  91 @interface wxUITextField : UITextField
 
  97 @interface wxNSSecureTextField : UITextField<UITextFieldDelegate>
 
 103 @implementation wxNSSecureTextField
 
 107     static BOOL initialized = NO;
 
 111         wxOSXIPhoneClassAddWXMethods( self );
 
 115 - (void)controlTextDidChange:(NSNotification *)aNotification
 
 117     wxUnusedVar(aNotification);
 
 118     wxWidgetIPhoneImpl* impl = (wxWidgetIPhoneImpl* ) wxWidgetImpl::FindFromWXWidget( self );
 
 120         impl->controlTextDidChange();
 
 123 - (void)controlTextDidEndEditing:(NSNotification *)aNotification
 
 125     wxUnusedVar(aNotification);
 
 126     wxWidgetIPhoneImpl* impl = (wxWidgetIPhoneImpl* ) wxWidgetImpl::FindFromWXWidget( self );
 
 129         impl->DoNotifyFocusEvent( false, NULL );
 
 136 @implementation wxUITextFieldEditor
 
 138 - (void) keyDown:(NSEvent*) event
 
 140     wxWidgetIPhoneImpl* impl = (wxWidgetIPhoneImpl* ) wxWidgetImpl::FindFromWXWidget( (WXWidget) [self delegate] );
 
 141     lastKeyDownEvent = event;
 
 142     if ( impl == NULL || !impl->DoHandleKeyEvent(event) )
 
 143         [super keyDown:event];
 
 144     lastKeyDownEvent = nil;
 
 147 - (void) keyUp:(NSEvent*) event
 
 149     wxWidgetIPhoneImpl* impl = (wxWidgetIPhoneImpl* ) wxWidgetImpl::FindFromWXWidget( (WXWidget) [self delegate] );
 
 150     if ( impl == NULL || !impl->DoHandleKeyEvent(event) )
 
 154 - (void) flagsChanged:(NSEvent*) event
 
 156     wxWidgetIPhoneImpl* impl = (wxWidgetIPhoneImpl* ) wxWidgetImpl::FindFromWXWidget( (WXWidget) [self delegate] );
 
 157     if ( impl == NULL || !impl->DoHandleKeyEvent(event) )
 
 158         [super flagsChanged:event];
 
 161 - (BOOL) performKeyEquivalent:(NSEvent*) event
 
 163     BOOL retval = [super performKeyEquivalent:event];
 
 167 - (void) insertText:(id) str
 
 169     wxWidgetIPhoneImpl* impl = (wxWidgetIPhoneImpl* ) wxWidgetImpl::FindFromWXWidget( (WXWidget) [self delegate] );
 
 170     if ( impl == NULL || lastKeyDownEvent==nil || !impl->DoHandleCharEvent(lastKeyDownEvent, str) )
 
 172         [super insertText:str];
 
 181 @implementation wxUITextFieldDelegate
 
 183 - (BOOL)textFieldShouldReturn:(UITextField *)textField
 
 185     // the user pressed the "Done" button, so dismiss the keyboard
 
 186     wxWidgetIPhoneImpl* impl = (wxWidgetIPhoneImpl* ) wxWidgetImpl::FindFromWXWidget( textField );
 
 189         wxWindow* wxpeer = (wxWindow*) impl->GetWXPeer();
 
 190         if ( wxpeer && wxpeer->GetWindowStyle() & wxTE_PROCESS_ENTER )
 
 192             wxCommandEvent event(wxEVT_TEXT_ENTER, wxpeer->GetId());
 
 193             event.SetEventObject( wxpeer );
 
 194             event.SetString( static_cast<wxTextCtrl*>(wxpeer)->GetValue() );
 
 195             wxpeer->HandleWindowEvent( event );
 
 199     [textField resignFirstResponder];
 
 204 - (BOOL)textFieldShouldBeginEditing:(UITextField *)textField;        // return NO to disallow editing.
 
 205 - (void)textFieldDidBeginEditing:(UITextField *)textField;           // became first responder
 
 206 - (BOOL)textFieldShouldEndEditing:(UITextField *)textField;          // return YES to allow editing to stop and to resign first responder status. NO to disallow the editing session to end
 
 207 - (void)textFieldDidEndEditing:(UITextField *)textField;             // may be called if forced even if shouldEndEditing returns NO (e.g. view removed from window) or endEditing:YES called
 
 209 - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string;   // return NO to not change text
 
 211 - (BOOL)textFieldShouldClear:(UITextField *)textField;               // called when clear button pressed. return NO to ignore (no notifications)
 
 217 @implementation wxUITextField
 
 221     static BOOL initialized = NO;
 
 225         wxOSXIPhoneClassAddWXMethods( self );
 
 230 - (void)controlTextDidChange:(NSNotification *)aNotification
 
 232     wxUnusedVar(aNotification);
 
 233     wxWidgetIPhoneImpl* impl = (wxWidgetIPhoneImpl* ) wxWidgetImpl::FindFromWXWidget( self );
 
 235         impl->controlTextDidChange();
 
 239 - (BOOL)textFieldShouldReturn:(UITextField *)textField
 
 241     wxUnusedVar(textField);
 
 247 - (void)controlTextDidEndEditing:(NSNotification *)aNotification
 
 249     wxUnusedVar(aNotification);
 
 250     wxWidgetIPhoneImpl* impl = (wxWidgetIPhoneImpl* ) wxWidgetImpl::FindFromWXWidget( self );
 
 253         impl->DoNotifyFocusEvent( false, NULL );
 
 260 @interface wxUITextViewDelegate : NSObject<UITextViewDelegate>
 
 264 - (void)textViewDidChange:(UITextView *)textView;
 
 265 - (void)textViewDidBeginEditing:(UITextView *)textView;
 
 266 - (void)textViewDidEndEditing:(UITextView *)textView;
 
 267 - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text;
 
 271 @implementation wxUITextViewDelegate
 
 273 - (void)textViewDidChange:(UITextView *)textView
 
 275     wxWidgetIPhoneImpl* impl = (wxWidgetIPhoneImpl* ) wxWidgetImpl::FindFromWXWidget( textView );
 
 277         impl->controlTextDidChange();
 
 280 - (void)textViewDidBeginEditing:(UITextView *)textView
 
 282     wxWidgetIPhoneImpl* impl = (wxWidgetIPhoneImpl* ) wxWidgetImpl::FindFromWXWidget( textView );
 
 284         impl->DoNotifyFocusEvent(true, NULL);
 
 287 - (void)textViewDidEndEditing:(UITextView *)textView
 
 289     wxWidgetIPhoneImpl* impl = (wxWidgetIPhoneImpl* ) wxWidgetImpl::FindFromWXWidget( textView );
 
 291         impl->DoNotifyFocusEvent(false, NULL);
 
 294 - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
 
 296     wxWidgetIPhoneImpl* impl = (wxWidgetIPhoneImpl* ) wxWidgetImpl::FindFromWXWidget( textView );
 
 299         if ( !impl->GetWXPeer()->HasFlag(wxTE_MULTILINE) && [text isEqualToString:@"\n"])
 
 301             [textView resignFirstResponder];
 
 312 // wxUITextViewControl
 
 315 wxUITextViewControl::wxUITextViewControl( wxTextCtrl *wxPeer, UITextView* v) : 
 
 316     wxWidgetIPhoneImpl(wxPeer, v),
 
 317     wxTextWidgetImpl(wxPeer)
 
 320     m_delegate= [[wxUITextViewDelegate alloc] init];
 
 322     [m_textView setDelegate:m_delegate];
 
 325 wxUITextViewControl::~wxUITextViewControl()
 
 329         [m_textView setDelegate: nil];
 
 331     [m_delegate release];
 
 334 bool wxUITextViewControl::CanFocus() const
 
 339 wxString wxUITextViewControl::GetStringValue() const
 
 343         wxString result = wxCFStringRef::AsString([m_textView text], m_wxPeer->GetFont().GetEncoding());
 
 344         wxMacConvertNewlines13To10( &result ) ;
 
 347     return wxEmptyString;
 
 350 void wxUITextViewControl::SetStringValue( const wxString &str)
 
 353     wxMacConvertNewlines10To13( &st );
 
 354     wxMacEditHelper helper(m_textView);
 
 357         [m_textView setText: wxCFStringRef( st , m_wxPeer->GetFont().GetEncoding() ).AsNSString()];
 
 360 void wxUITextViewControl::Copy()
 
 363         [m_textView copy:nil];
 
 367 void wxUITextViewControl::Cut()
 
 370         [m_textView cut:nil];
 
 373 void wxUITextViewControl::Paste()
 
 376         [m_textView paste:nil];
 
 379 bool wxUITextViewControl::CanPaste() const
 
 384 void wxUITextViewControl::SetEditable(bool editable)
 
 387         [m_textView setEditable: editable];
 
 390 void wxUITextViewControl::GetSelection( long* from, long* to) const
 
 394         NSRange range = [m_textView selectedRange];
 
 395         *from = range.location;
 
 396         *to = range.location + range.length;
 
 400 void wxUITextViewControl::SetSelection( long from , long to )
 
 402     long textLength = [[m_textView text] length];
 
 403     if ((from == -1) && (to == -1))
 
 410         from = wxMin(textLength,wxMax(from,0)) ;
 
 414             to = wxMax(0,wxMin(textLength,to)) ;
 
 417     NSRange selrange = NSMakeRange(from, to-from);
 
 418     [m_textView setSelectedRange:selrange];
 
 419     [m_textView scrollRangeToVisible:selrange];
 
 422 void wxUITextViewControl::WriteText(const wxString& str)
 
 425     wxMacConvertNewlines10To13( &st );
 
 426     wxMacEditHelper helper(m_textView);
 
 428     wxCFStringRef insert( st , m_wxPeer->GetFont().GetEncoding() );
 
 429     NSMutableString* subst = [NSMutableString stringWithString:[m_textView text]];
 
 430     [subst replaceCharactersInRange:[m_textView selectedRange] withString:insert.AsNSString()];
 
 432     [m_textView setText:subst];
 
 435 void wxUITextViewControl::SetFont( const wxFont & font , const wxColour& WXUNUSED(foreground) , long WXUNUSED(windowStyle), bool WXUNUSED(ignoreBlack) )
 
 437     if ([m_textView respondsToSelector:@selector(setFont:)])
 
 438         [m_textView setFont: font.OSXGetUIFont()];
 
 441 bool wxUITextViewControl::GetStyle(long position, wxTextAttr& style)
 
 443     if (m_textView && position >=0)
 
 445         // UIFont* font = NULL;
 
 446         // NSColor* bgcolor = NULL;
 
 447         // NSColor* fgcolor = NULL;
 
 448         // NOTE: It appears that other platforms accept GetStyle with the position == length
 
 449         // but that UITextStorage does not accept length as a valid position.
 
 450         // Therefore we return the default control style in that case.
 
 452         if (position < [[m_textView string] length]) 
 
 454             UITextStorage* storage = [m_textView textStorage];
 
 455             font = [[storage attribute:NSFontAttributeName atIndex:position effectiveRange:NULL] autorelease];
 
 456             bgcolor = [[storage attribute:NSBackgroundColorAttributeName atIndex:position effectiveRange:NULL] autorelease];
 
 457             fgcolor = [[storage attribute:NSForegroundColorAttributeName atIndex:position effectiveRange:NULL] autorelease];
 
 461             NSDictionary* attrs = [m_textView typingAttributes];
 
 462             font = [[attrs objectForKey:NSFontAttributeName] autorelease];
 
 463             bgcolor = [[attrs objectForKey:NSBackgroundColorAttributeName] autorelease];
 
 464             fgcolor = [[attrs objectForKey:NSForegroundColorAttributeName] autorelease];
 
 469             style.SetFont(wxFont(font));
 
 472             style.SetBackgroundColour(wxColour(bgcolor));
 
 475             style.SetTextColour(wxColour(fgcolor));
 
 483 void wxUITextViewControl::SetStyle(long start,
 
 485                                 const wxTextAttr& style)
 
 488         NSRange range = NSMakeRange(start, end-start);
 
 489         if (start == -1 && end == -1)
 
 490             range = [m_textView selectedRange];
 
 492         UITextStorage* storage = [m_textView textStorage];
 
 494         wxFont font = style.GetFont();
 
 495         if (style.HasFont() && font.IsOk())
 
 496             [storage addAttribute:NSFontAttributeName value:font.OSXGetNSFont() range:range];
 
 498         wxColour bgcolor = style.GetBackgroundColour();
 
 499         if (style.HasBackgroundColour() && bgcolor.IsOk())
 
 500             [storage addAttribute:NSBackgroundColorAttributeName value:bgcolor.OSXGetNSColor() range:range];
 
 502         wxColour fgcolor = style.GetTextColour();
 
 503         if (style.HasTextColour() && fgcolor.IsOk())
 
 504             [storage addAttribute:NSForegroundColorAttributeName value:fgcolor.OSXGetNSColor() range:range];
 
 509 void wxUITextViewControl::CheckSpelling(bool check)
 
 513 wxSize wxUITextViewControl::GetBestSize() const
 
 520     if (m_textView && [m_textView layoutManager])
 
 522         NSRect rect = [[m_textView layoutManager] usedRectForTextContainer: [m_textView textContainer]];
 
 523         wxSize size = wxSize(rect.size.width, rect.size.height);
 
 524         size.x += [m_textView textContainerInset].width;
 
 525         size.y += [m_textView textContainerInset].height;
 
 531     wxSize sz = r.GetSize();
 
 538 #if wxOSX_IPHONE_USE_TEXTFIELD
 
 541 // wxUITextFieldControl
 
 544 wxUITextFieldControl::wxUITextFieldControl( wxTextCtrl *wxPeer, UITextField* w ) : 
 
 545     wxWidgetIPhoneImpl(wxPeer, w),
 
 546     wxTextWidgetImpl(wxPeer)
 
 549     m_delegate = [[wxUITextFieldDelegate alloc] init];
 
 550     [m_textField setDelegate: m_delegate];
 
 551     m_selStart = m_selEnd = 0;
 
 554 wxUITextFieldControl::~wxUITextFieldControl()
 
 557         [m_textField setDelegate: nil];
 
 558     [m_delegate release];
 
 561 wxString wxUITextFieldControl::GetStringValue() const
 
 563     return wxCFStringRef::AsString([m_textField text], m_wxPeer->GetFont().GetEncoding());
 
 566 void wxUITextFieldControl::SetStringValue( const wxString &str)
 
 568 //    wxMacEditHelper helper(m_textField);
 
 569     [m_textField setText: wxCFStringRef( str , m_wxPeer->GetFont().GetEncoding() ).AsNSString()];
 
 572 wxSize wxUITextFieldControl::GetBestSize() const
 
 577     wxSize sz = r.GetSize();
 
 583 void wxUITextFieldControl::Copy()
 
 585     [m_textField copy:nil];
 
 588 void wxUITextFieldControl::Cut()
 
 590     [m_textField cut:nil];
 
 593 void wxUITextFieldControl::Paste()
 
 595     [m_textField paste:nil];
 
 598 bool wxUITextFieldControl::CanPaste() const
 
 603 void wxUITextFieldControl::SetEditable(bool editable)
 
 607 void wxUITextFieldControl::GetSelection( long* from, long* to) const
 
 613 void wxUITextFieldControl::SetSelection( long from , long to )
 
 615     long textLength = [[m_textField text] length];
 
 616     if ((from == -1) && (to == -1))
 
 623         from = wxMin(textLength,wxMax(from,0)) ;
 
 627             to = wxMax(0,wxMin(textLength,to)) ;
 
 634 void wxUITextFieldControl::WriteText(const wxString& str)
 
 637     NSEvent* formerEvent = m_lastKeyDownEvent;
 
 638         UIText* editor = [m_textField currentEditor];
 
 641         wxMacEditHelper helper(m_textField);
 
 642         [editor insertText:wxCFStringRef( str , m_wxPeer->GetFont().GetEncoding() ).AsNSString()];
 
 647         wxString val = GetStringValue() ;
 
 649         GetSelection( &start , &end ) ;
 
 650         val.Remove( start , end - start ) ;
 
 651         val.insert( start , str ) ;
 
 652         SetStringValue( val ) ;
 
 653         SetSelection( start + str.length() , start + str.length() ) ;
 
 656     m_lastKeyDownEvent = formerEvent;
 
 660 void wxUITextFieldControl::controlAction(WXWidget WXUNUSED(slf),
 
 661     void* WXUNUSED(_cmd), void *WXUNUSED(sender))
 
 663     wxWindow* wxpeer = (wxWindow*) GetWXPeer();
 
 664     if ( wxpeer && (wxpeer->GetWindowStyle() & wxTE_PROCESS_ENTER) )
 
 666         wxCommandEvent event(wxEVT_TEXT_ENTER, wxpeer->GetId());
 
 667         event.SetEventObject( wxpeer );
 
 668         event.SetString( static_cast<wxTextCtrl*>(wxpeer)->GetValue() );
 
 669         wxpeer->HandleWindowEvent( event );
 
 673 bool wxUITextFieldControl::SetHint(const wxString& hint)
 
 675     wxCFStringRef hintstring(hint);
 
 676     [m_textField setPlaceholder:hintstring.AsNSString()];
 
 686 wxWidgetImplType* wxWidgetImpl::CreateTextControl( wxTextCtrl* wxpeer,
 
 687                                     wxWindowMac* WXUNUSED(parent),
 
 688                                     wxWindowID WXUNUSED(id),
 
 693                                     long WXUNUSED(extraStyle))
 
 695     CGRect r = wxOSXGetFrameForControl( wxpeer, pos , size ) ;
 
 696     wxWidgetIPhoneImpl* c = NULL;
 
 697     wxTextWidgetImpl* t = NULL;
 
 698     id<UITextInputTraits> tv = nil;
 
 700 #if wxOSX_IPHONE_USE_TEXTFIELD
 
 701     if ( style & wxTE_MULTILINE || style & wxTE_RICH || style & wxTE_RICH2 )
 
 704         UITextView * v = nil;
 
 705         v = [[UITextView alloc] initWithFrame:r];
 
 708         wxUITextViewControl* tc = new wxUITextViewControl( wxpeer, v );
 
 712 #if wxOSX_IPHONE_USE_TEXTFIELD
 
 715         wxUITextField* v = [[wxUITextField alloc] initWithFrame:r];
 
 718                 v.textColor = [UIColor blackColor];
 
 719                 v.font = [UIFont systemFontOfSize:17.0];
 
 720                 v.backgroundColor = [UIColor whiteColor];
 
 722                 v.clearButtonMode = UITextFieldViewModeNever;
 
 724         [v setBorderStyle:UITextBorderStyleBezel];
 
 725         if ( style & wxNO_BORDER )
 
 726             v.borderStyle = UITextBorderStyleNone;
 
 728         wxUITextFieldControl* tc = new wxUITextFieldControl( wxpeer, v );
 
 734     if ( style & wxTE_PASSWORD )
 
 735         [tv setSecureTextEntry:YES];
 
 737     if ( !(style & wxTE_MULTILINE) )
 
 739         [tv setAutocorrectionType:UITextAutocorrectionTypeNo];
 
 740                 [tv setReturnKeyType:UIReturnKeyDone];
 
 742     [tv setKeyboardType:UIKeyboardTypeDefault];
 
 744     t->SetStringValue(str);
 
 750 #endif // wxUSE_TEXTCTRL