1/////////////////////////////////////////////////////////////////////////////
2// Name:        src/osx/iphone/textctrl.mm
3// Purpose:     wxTextCtrl
4// Author:      Stefan Csomor
5// Modified by: Ryan Norton (MLTE GetLineLength and GetLineText)
6// Created:     1998-01-01
7// Copyright:   (c) Stefan Csomor
8// Licence:     wxWindows licence
9/////////////////////////////////////////////////////////////////////////////
10
11#include "wx/wxprec.h"
12
13#if wxUSE_TEXTCTRL
14
15#include "wx/textctrl.h"
16
17#ifndef WX_PRECOMP
18    #include "wx/intl.h"
19    #include "wx/app.h"
20    #include "wx/utils.h"
21    #include "wx/dc.h"
22    #include "wx/button.h"
23    #include "wx/menu.h"
24    #include "wx/settings.h"
25    #include "wx/msgdlg.h"
26    #include "wx/toplevel.h"
27#endif
28
29#ifdef __DARWIN__
30    #include <sys/types.h>
31    #include <sys/stat.h>
32#else
33    #include <stat.h>
34#endif
35
36#if wxUSE_STD_IOSTREAM
37    #if wxUSE_IOSTREAMH
38        #include <fstream.h>
39    #else
40        #include <fstream>
41    #endif
42#endif
43
44#include "wx/filefn.h"
45#include "wx/sysopt.h"
46#include "wx/thread.h"
47
48#include "wx/osx/private.h"
49#include "wx/osx/iphone/private/textimpl.h"
50
51// currently for some reasong the UITextField leads to a recursion when the keyboard should be shown, so let's leave the code
52// in case this gets resolved...
53
54#define wxOSX_IPHONE_USE_TEXTFIELD 1
55
56class wxMacEditHelper
57{
58public :
59    wxMacEditHelper( UITextView* textView )
60    {
61        m_textView = textView;
62        m_formerState = YES;
63        if ( textView )
64        {
65            m_formerState = [textView isEditable];
66            [textView setEditable:YES];
67        }
68    }
69
70    ~wxMacEditHelper()
71    {
72        if ( m_textView )
73            [m_textView setEditable:m_formerState];
74    }
75
76protected :
77    BOOL m_formerState ;
78    UITextView* m_textView;
79} ;
80
81#if wxOSX_IPHONE_USE_TEXTFIELD
82
83@interface  wxUITextFieldDelegate : NSObject<UITextFieldDelegate>
84{
85}
86
87@end
88
89
90@interface wxUITextField : UITextField
91{
92}
93
94@end
95
96@interface wxNSSecureTextField : UITextField<UITextFieldDelegate>
97{
98}
99
100@end
101
102@implementation wxNSSecureTextField
103
104+ (void)initialize
105{
106    static BOOL initialized = NO;
107    if (!initialized)
108    {
109        initialized = YES;
110        wxOSXIPhoneClassAddWXMethods( self );
111    }
112}
113
114- (void)controlTextDidChange:(NSNotification *)aNotification
115{
116    wxUnusedVar(aNotification);
117    wxWidgetIPhoneImpl* impl = (wxWidgetIPhoneImpl* ) wxWidgetImpl::FindFromWXWidget( self );
118    if ( impl )
119        impl->controlTextDidChange();
120}
121
122- (void)controlTextDidEndEditing:(NSNotification *)aNotification
123{
124    wxUnusedVar(aNotification);
125    wxWidgetIPhoneImpl* impl = (wxWidgetIPhoneImpl* ) wxWidgetImpl::FindFromWXWidget( self );
126    if ( impl )
127    {
128        impl->DoNotifyFocusEvent( false, NULL );
129    }
130}
131
132@end
133
134#if 0
135@implementation wxUITextFieldEditor
136
137- (void) keyDown:(NSEvent*) event
138{
139    wxWidgetIPhoneImpl* impl = (wxWidgetIPhoneImpl* ) wxWidgetImpl::FindFromWXWidget( (WXWidget) [self delegate] );
140    lastKeyDownEvent = event;
141    if ( impl == NULL || !impl->DoHandleKeyEvent(event) )
142        [super keyDown:event];
143    lastKeyDownEvent = nil;
144}
145
146- (void) keyUp:(NSEvent*) event
147{
148    wxWidgetIPhoneImpl* impl = (wxWidgetIPhoneImpl* ) wxWidgetImpl::FindFromWXWidget( (WXWidget) [self delegate] );
149    if ( impl == NULL || !impl->DoHandleKeyEvent(event) )
150        [super keyUp:event];
151}
152
153- (void) flagsChanged:(NSEvent*) event
154{
155    wxWidgetIPhoneImpl* impl = (wxWidgetIPhoneImpl* ) wxWidgetImpl::FindFromWXWidget( (WXWidget) [self delegate] );
156    if ( impl == NULL || !impl->DoHandleKeyEvent(event) )
157        [super flagsChanged:event];
158}
159
160- (BOOL) performKeyEquivalent:(NSEvent*) event
161{
162    BOOL retval = [super performKeyEquivalent:event];
163    return retval;
164}
165
166- (void) insertText:(id) str
167{
168    wxWidgetIPhoneImpl* impl = (wxWidgetIPhoneImpl* ) wxWidgetImpl::FindFromWXWidget( (WXWidget) [self delegate] );
169    if ( impl == NULL || lastKeyDownEvent==nil || !impl->DoHandleCharEvent(lastKeyDownEvent, str) )
170    {
171        [super insertText:str];
172    }
173}
174
175@end
176
177#endif
178
179
180@implementation wxUITextFieldDelegate
181
182- (BOOL)textFieldShouldReturn:(UITextField *)textField
183{
184    // the user pressed the "Done" button, so dismiss the keyboard
185    wxWidgetIPhoneImpl* impl = (wxWidgetIPhoneImpl* ) wxWidgetImpl::FindFromWXWidget( textField );
186    if ( impl  )
187    {
188        wxWindow* wxpeer = (wxWindow*) impl->GetWXPeer();
189        if ( wxpeer && wxpeer->GetWindowStyle() & wxTE_PROCESS_ENTER )
190        {
191            wxCommandEvent event(wxEVT_TEXT_ENTER, wxpeer->GetId());
192            event.SetEventObject( wxpeer );
193            event.SetString( static_cast<wxTextCtrl*>(wxpeer)->GetValue() );
194            wxpeer->HandleWindowEvent( event );
195        }
196    }
197
198    [textField resignFirstResponder];
199    return YES;
200}
201
202/*
203- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField;        // return NO to disallow editing.
204- (void)textFieldDidBeginEditing:(UITextField *)textField;           // became first responder
205- (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
206- (void)textFieldDidEndEditing:(UITextField *)textField;             // may be called if forced even if shouldEndEditing returns NO (e.g. view removed from window) or endEditing:YES called
207
208- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string;   // return NO to not change text
209
210- (BOOL)textFieldShouldClear:(UITextField *)textField;               // called when clear button pressed. return NO to ignore (no notifications)
211*/
212
213@end
214
215
216@implementation wxUITextField
217
218+ (void)initialize
219{
220    static BOOL initialized = NO;
221    if (!initialized)
222    {
223        initialized = YES;
224        wxOSXIPhoneClassAddWXMethods( self );
225    }
226}
227
228#if 0
229- (void)controlTextDidChange:(NSNotification *)aNotification
230{
231    wxUnusedVar(aNotification);
232    wxWidgetIPhoneImpl* impl = (wxWidgetIPhoneImpl* ) wxWidgetImpl::FindFromWXWidget( self );
233    if ( impl )
234        impl->controlTextDidChange();
235}
236#endif
237
238- (BOOL)textFieldShouldReturn:(UITextField *)textField
239{
240    wxUnusedVar(textField);
241
242
243    return NO;
244}
245
246- (void)controlTextDidEndEditing:(NSNotification *)aNotification
247{
248    wxUnusedVar(aNotification);
249    wxWidgetIPhoneImpl* impl = (wxWidgetIPhoneImpl* ) wxWidgetImpl::FindFromWXWidget( self );
250    if ( impl )
251    {
252        impl->DoNotifyFocusEvent( false, NULL );
253    }
254}
255@end
256
257#endif
258
259@interface wxUITextViewDelegate : NSObject<UITextViewDelegate>
260{
261}
262
263- (void)textViewDidChange:(UITextView *)textView;
264- (void)textViewDidBeginEditing:(UITextView *)textView;
265- (void)textViewDidEndEditing:(UITextView *)textView;
266- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text;
267
268@end
269
270@implementation wxUITextViewDelegate
271
272- (void)textViewDidChange:(UITextView *)textView
273{
274    wxWidgetIPhoneImpl* impl = (wxWidgetIPhoneImpl* ) wxWidgetImpl::FindFromWXWidget( textView );
275    if ( impl )
276        impl->controlTextDidChange();
277}
278
279- (void)textViewDidBeginEditing:(UITextView *)textView
280{
281    wxWidgetIPhoneImpl* impl = (wxWidgetIPhoneImpl* ) wxWidgetImpl::FindFromWXWidget( textView );
282    if ( impl )
283        impl->DoNotifyFocusEvent(true, NULL);
284}
285
286- (void)textViewDidEndEditing:(UITextView *)textView
287{
288    wxWidgetIPhoneImpl* impl = (wxWidgetIPhoneImpl* ) wxWidgetImpl::FindFromWXWidget( textView );
289    if ( impl )
290        impl->DoNotifyFocusEvent(false, NULL);
291}
292
293- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
294{
295    wxWidgetIPhoneImpl* impl = (wxWidgetIPhoneImpl* ) wxWidgetImpl::FindFromWXWidget( textView );
296    if ( impl )
297    {
298        if ( !impl->GetWXPeer()->HasFlag(wxTE_MULTILINE) && [text isEqualToString:@"\n"])
299        {
300            [textView resignFirstResponder];
301            return NO;
302        }
303    }
304    return YES;
305}
306
307
308@end
309
310//
311// wxUITextViewControl
312//
313
314wxUITextViewControl::wxUITextViewControl( wxTextCtrl *wxPeer, UITextView* v) :
315    wxWidgetIPhoneImpl(wxPeer, v),
316    wxTextWidgetImpl(wxPeer)
317{
318    m_textView = v;
319    m_delegate= [[wxUITextViewDelegate alloc] init];
320
321    [m_textView setDelegate:m_delegate];
322}
323
324wxUITextViewControl::~wxUITextViewControl()
325{
326    if (m_textView)
327    {
328        [m_textView setDelegate: nil];
329    }
330    [m_delegate release];
331}
332
333bool wxUITextViewControl::CanFocus() const
334{
335    return true;
336}
337
338wxString wxUITextViewControl::GetStringValue() const
339{
340    if (m_textView)
341    {
342        wxString result = wxCFStringRef::AsString([m_textView text], m_wxPeer->GetFont().GetEncoding());
343        wxMacConvertNewlines13To10( &result ) ;
344        return result;
345    }
346    return wxEmptyString;
347}
348
349void wxUITextViewControl::SetStringValue( const wxString &str)
350{
351    wxString st = str;
352    wxMacConvertNewlines10To13( &st );
353    wxMacEditHelper helper(m_textView);
354
355    if (m_textView)
356        [m_textView setText: wxCFStringRef( st , m_wxPeer->GetFont().GetEncoding() ).AsNSString()];
357}
358
359void wxUITextViewControl::Copy()
360{
361    if (m_textView)
362        [m_textView copy:nil];
363
364}
365
366void wxUITextViewControl::Cut()
367{
368    if (m_textView)
369        [m_textView cut:nil];
370}
371
372void wxUITextViewControl::Paste()
373{
374    if (m_textView)
375        [m_textView paste:nil];
376}
377
378bool wxUITextViewControl::CanPaste() const
379{
380    return true;
381}
382
383void wxUITextViewControl::SetEditable(bool editable)
384{
385    if (m_textView)
386        [m_textView setEditable: editable];
387}
388
389void wxUITextViewControl::GetSelection( long* from, long* to) const
390{
391    if (m_textView)
392    {
393        NSRange range = [m_textView selectedRange];
394        *from = range.location;
395        *to = range.location + range.length;
396    }
397}
398
399void wxUITextViewControl::SetSelection( long from , long to )
400{
401    long textLength = [[m_textView text] length];
402    if ((from == -1) && (to == -1))
403    {
404        from = 0 ;
405        to = textLength ;
406    }
407    else
408    {
409        from = wxMin(textLength,wxMax(from,0)) ;
410        if ( to == -1 )
411            to = textLength;
412        else
413            to = wxMax(0,wxMin(textLength,to)) ;
414    }
415
416    NSRange selrange = NSMakeRange(from, to-from);
417    [m_textView setSelectedRange:selrange];
418    [m_textView scrollRangeToVisible:selrange];
419}
420
421void wxUITextViewControl::WriteText(const wxString& str)
422{
423    wxString st = str;
424    wxMacConvertNewlines10To13( &st );
425    wxMacEditHelper helper(m_textView);
426
427    wxCFStringRef insert( st , m_wxPeer->GetFont().GetEncoding() );
428    NSMutableString* subst = [NSMutableString stringWithString:[m_textView text]];
429    [subst replaceCharactersInRange:[m_textView selectedRange] withString:insert.AsNSString()];
430
431    [m_textView setText:subst];
432}
433
434void wxUITextViewControl::SetFont( const wxFont & font , const wxColour& WXUNUSED(foreground) , long WXUNUSED(windowStyle), bool WXUNUSED(ignoreBlack) )
435{
436    if ([m_textView respondsToSelector:@selector(setFont:)])
437        [m_textView setFont: font.OSXGetUIFont()];
438}
439
440bool wxUITextViewControl::GetStyle(long position, wxTextAttr& style)
441{
442    if (m_textView && position >=0)
443    {
444        // UIFont* font = NULL;
445        // NSColor* bgcolor = NULL;
446        // NSColor* fgcolor = NULL;
447        // NOTE: It appears that other platforms accept GetStyle with the position == length
448        // but that UITextStorage does not accept length as a valid position.
449        // Therefore we return the default control style in that case.
450        /*
451        if (position < [[m_textView string] length])
452        {
453            UITextStorage* storage = [m_textView textStorage];
454            font = [[storage attribute:NSFontAttributeName atIndex:position effectiveRange:NULL] autorelease];
455            bgcolor = [[storage attribute:NSBackgroundColorAttributeName atIndex:position effectiveRange:NULL] autorelease];
456            fgcolor = [[storage attribute:NSForegroundColorAttributeName atIndex:position effectiveRange:NULL] autorelease];
457        }
458        else
459        {
460            NSDictionary* attrs = [m_textView typingAttributes];
461            font = [[attrs objectForKey:NSFontAttributeName] autorelease];
462            bgcolor = [[attrs objectForKey:NSBackgroundColorAttributeName] autorelease];
463            fgcolor = [[attrs objectForKey:NSForegroundColorAttributeName] autorelease];
464        }
465        */
466        /*
467        if (font)
468            style.SetFont(wxFont(font));
469
470        if (bgcolor)
471            style.SetBackgroundColour(wxColour(bgcolor));
472
473        if (fgcolor)
474            style.SetTextColour(wxColour(fgcolor));
475        */
476        return true;
477    }
478
479    return false;
480}
481
482void wxUITextViewControl::SetStyle(long start,
483                                long end,
484                                const wxTextAttr& style)
485{
486    if (m_textView) {
487        NSRange range = NSMakeRange(start, end-start);
488        if (start == -1 && end == -1)
489            range = [m_textView selectedRange];
490/*
491        UITextStorage* storage = [m_textView textStorage];
492
493        wxFont font = style.GetFont();
494        if (style.HasFont() && font.IsOk())
495            [storage addAttribute:NSFontAttributeName value:font.OSXGetNSFont() range:range];
496
497        wxColour bgcolor = style.GetBackgroundColour();
498        if (style.HasBackgroundColour() && bgcolor.IsOk())
499            [storage addAttribute:NSBackgroundColorAttributeName value:bgcolor.OSXGetNSColor() range:range];
500
501        wxColour fgcolor = style.GetTextColour();
502        if (style.HasTextColour() && fgcolor.IsOk())
503            [storage addAttribute:NSForegroundColorAttributeName value:fgcolor.OSXGetNSColor() range:range];
504*/
505    }
506}
507
508void wxUITextViewControl::CheckSpelling(bool check)
509{
510}
511
512wxSize wxUITextViewControl::GetBestSize() const
513{
514    wxRect r;
515
516    GetBestRect(&r);
517
518    /*
519    if (m_textView && [m_textView layoutManager])
520    {
521        NSRect rect = [[m_textView layoutManager] usedRectForTextContainer: [m_textView textContainer]];
522        wxSize size = wxSize(rect.size.width, rect.size.height);
523        size.x += [m_textView textContainerInset].width;
524        size.y += [m_textView textContainerInset].height;
525        return size;
526    }
527    return wxSize(0,0);
528    */
529
530    wxSize sz = r.GetSize();
531    if ( sz.y < 31 )
532        sz.y = 31;
533
534    return sz;
535}
536
537#if wxOSX_IPHONE_USE_TEXTFIELD
538
539//
540// wxUITextFieldControl
541//
542
543wxUITextFieldControl::wxUITextFieldControl( wxTextCtrl *wxPeer, UITextField* w ) :
544    wxWidgetIPhoneImpl(wxPeer, w),
545    wxTextWidgetImpl(wxPeer)
546{
547    m_textField = w;
548    m_delegate = [[wxUITextFieldDelegate alloc] init];
549    [m_textField setDelegate: m_delegate];
550    m_selStart = m_selEnd = 0;
551}
552
553wxUITextFieldControl::~wxUITextFieldControl()
554{
555    if (m_textField)
556        [m_textField setDelegate: nil];
557    [m_delegate release];
558}
559
560wxString wxUITextFieldControl::GetStringValue() const
561{
562    return wxCFStringRef::AsString([m_textField text], m_wxPeer->GetFont().GetEncoding());
563}
564
565void wxUITextFieldControl::SetStringValue( const wxString &str)
566{
567//    wxMacEditHelper helper(m_textField);
568    [m_textField setText: wxCFStringRef( str , m_wxPeer->GetFont().GetEncoding() ).AsNSString()];
569}
570
571wxSize wxUITextFieldControl::GetBestSize() const
572{
573    wxRect r;
574
575    GetBestRect(&r);
576    wxSize sz = r.GetSize();
577    if ( sz.y < 31 )
578        sz.y = 31;
579    return sz;
580}
581
582void wxUITextFieldControl::Copy()
583{
584    [m_textField copy:nil];
585}
586
587void wxUITextFieldControl::Cut()
588{
589    [m_textField cut:nil];
590}
591
592void wxUITextFieldControl::Paste()
593{
594    [m_textField paste:nil];
595}
596
597bool wxUITextFieldControl::CanPaste() const
598{
599    return true;
600}
601
602void wxUITextFieldControl::SetEditable(bool editable)
603{
604}
605
606void wxUITextFieldControl::GetSelection( long* from, long* to) const
607{
608    *from = m_selStart;
609    *to = m_selEnd;
610}
611
612void wxUITextFieldControl::SetSelection( long from , long to )
613{
614    long textLength = [[m_textField text] length];
615    if ((from == -1) && (to == -1))
616    {
617        from = 0 ;
618        to = textLength ;
619    }
620    else
621    {
622        from = wxMin(textLength,wxMax(from,0)) ;
623        if ( to == -1 )
624            to = textLength;
625        else
626            to = wxMax(0,wxMin(textLength,to)) ;
627    }
628
629    m_selStart = from;
630    m_selEnd = to;
631}
632
633void wxUITextFieldControl::WriteText(const wxString& str)
634{
635#if 0
636    NSEvent* formerEvent = m_lastKeyDownEvent;
637        UIText* editor = [m_textField currentEditor];
638    if ( editor )
639    {
640        wxMacEditHelper helper(m_textField);
641        [editor insertText:wxCFStringRef( str , m_wxPeer->GetFont().GetEncoding() ).AsNSString()];
642    }
643    else
644#endif
645    {
646        wxString val = GetStringValue() ;
647        long start , end ;
648        GetSelection( &start , &end ) ;
649        val.Remove( start , end - start ) ;
650        val.insert( start , str ) ;
651        SetStringValue( val ) ;
652        SetSelection( start + str.length() , start + str.length() ) ;
653    }
654#if 0
655    m_lastKeyDownEvent = formerEvent;
656#endif
657}
658
659void wxUITextFieldControl::controlAction(WXWidget WXUNUSED(slf),
660    void* WXUNUSED(_cmd), void *WXUNUSED(sender))
661{
662    wxWindow* wxpeer = (wxWindow*) GetWXPeer();
663    if ( wxpeer && (wxpeer->GetWindowStyle() & wxTE_PROCESS_ENTER) )
664    {
665        wxCommandEvent event(wxEVT_TEXT_ENTER, wxpeer->GetId());
666        event.SetEventObject( wxpeer );
667        event.SetString( static_cast<wxTextCtrl*>(wxpeer)->GetValue() );
668        wxpeer->HandleWindowEvent( event );
669    }
670}
671
672bool wxUITextFieldControl::SetHint(const wxString& hint)
673{
674    wxCFStringRef hintstring(hint);
675    [m_textField setPlaceholder:hintstring.AsNSString()];
676    return true;
677}
678
679#endif
680
681//
682//
683//
684
685wxWidgetImplType* wxWidgetImpl::CreateTextControl( wxTextCtrl* wxpeer,
686                                    wxWindowMac* WXUNUSED(parent),
687                                    wxWindowID WXUNUSED(id),
688                                    const wxString& str,
689                                    const wxPoint& pos,
690                                    const wxSize& size,
691                                    long style,
692                                    long WXUNUSED(extraStyle))
693{
694    CGRect r = wxOSXGetFrameForControl( wxpeer, pos , size ) ;
695    wxWidgetIPhoneImpl* c = NULL;
696    wxTextWidgetImpl* t = NULL;
697    id<UITextInputTraits> tv = nil;
698
699#if wxOSX_IPHONE_USE_TEXTFIELD
700    if ( style & wxTE_MULTILINE || style & wxTE_RICH || style & wxTE_RICH2 )
701#endif
702    {
703        UITextView * v = nil;
704        v = [[UITextView alloc] initWithFrame:r];
705        tv = v;
706
707        wxUITextViewControl* tc = new wxUITextViewControl( wxpeer, v );
708        c = tc;
709        t = tc;
710    }
711#if wxOSX_IPHONE_USE_TEXTFIELD
712    else
713    {
714        wxUITextField* v = [[wxUITextField alloc] initWithFrame:r];
715        tv = v;
716
717		v.textColor = [UIColor blackColor];
718		v.font = [UIFont systemFontOfSize:17.0];
719		v.backgroundColor = [UIColor whiteColor];
720
721		v.clearButtonMode = UITextFieldViewModeNever;
722
723        [v setBorderStyle:UITextBorderStyleBezel];
724        if ( style & wxNO_BORDER )
725            v.borderStyle = UITextBorderStyleNone;
726
727        wxUITextFieldControl* tc = new wxUITextFieldControl( wxpeer, v );
728        c = tc;
729        t = tc;
730    }
731#endif
732
733    if ( style & wxTE_PASSWORD )
734        [tv setSecureTextEntry:YES];
735
736    if ( !(style & wxTE_MULTILINE) )
737    {
738        [tv setAutocorrectionType:UITextAutocorrectionTypeNo];
739		[tv setReturnKeyType:UIReturnKeyDone];
740    }
741    [tv setKeyboardType:UIKeyboardTypeDefault];
742
743    t->SetStringValue(str);
744
745    return c;
746}
747
748
749#endif // wxUSE_TEXTCTRL
750