Provide shorter synonyms for wxEVT_XXX constants.
[wxWidgets.git] / src / osx / cocoa / textctrl.mm
1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/osx/cocoa/textctrl.mm
3 // Purpose:     wxTextCtrl
4 // Author:      Stefan Csomor
5 // Modified by: Ryan Norton (MLTE GetLineLength and GetLineText)
6 // Created:     1998-01-01
7 // RCS-ID:      $Id$
8 // Copyright:   (c) Stefan Csomor
9 // Licence:     wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #include "wx/wxprec.h"
13
14 #if wxUSE_TEXTCTRL
15
16 #include "wx/textctrl.h"
17
18 #ifndef WX_PRECOMP
19     #include "wx/intl.h"
20     #include "wx/app.h"
21     #include "wx/utils.h"
22     #include "wx/dc.h"
23     #include "wx/button.h"
24     #include "wx/menu.h"
25     #include "wx/settings.h"
26     #include "wx/msgdlg.h"
27     #include "wx/toplevel.h"
28 #endif
29
30 #ifdef __DARWIN__
31     #include <sys/types.h>
32     #include <sys/stat.h>
33 #else
34     #include <stat.h>
35 #endif
36
37 #if wxUSE_STD_IOSTREAM
38     #if wxUSE_IOSTREAMH
39         #include <fstream.h>
40     #else
41         #include <fstream>
42     #endif
43 #endif
44
45 #include "wx/filefn.h"
46 #include "wx/sysopt.h"
47 #include "wx/thread.h"
48 #include "wx/textcompleter.h"
49
50 #include "wx/osx/private.h"
51 #include "wx/osx/cocoa/private/textimpl.h"
52
53 @interface NSView(EditableView)
54 - (BOOL)isEditable;
55 - (void)setEditable:(BOOL)flag;
56 - (BOOL)isSelectable;
57 - (void)setSelectable:(BOOL)flag;
58 @end
59
60 // An object of this class is created before the text is modified
61 // programmatically and destroyed as soon as this is done. It does several
62 // things, like ensuring that the control is editable to allow setting its text
63 // at all and eating any unwanted focus loss events from textDidEndEditing:
64 // which don't really correspond to focus change.
65 class wxMacEditHelper
66 {
67 public :
68     wxMacEditHelper( NSView* textView )
69     {
70         m_viewPreviouslyEdited = ms_viewCurrentlyEdited;
71         ms_viewCurrentlyEdited =
72         m_textView = textView;
73         m_formerEditable = YES;
74         if ( textView )
75         {
76             m_formerEditable = [textView isEditable];
77             m_formerSelectable = [textView isSelectable];
78             [textView setEditable:YES];
79         }
80     }
81
82     ~wxMacEditHelper()
83     {
84         if ( m_textView )
85         {
86             [m_textView setEditable:m_formerEditable];
87             [m_textView setSelectable:m_formerSelectable];
88         }
89
90         ms_viewCurrentlyEdited = m_viewPreviouslyEdited;
91     }
92
93     // Returns the last view we were instantiated for or NULL.
94     static NSView *GetCurrentlyEditedView() { return ms_viewCurrentlyEdited; }
95
96 protected :
97     BOOL m_formerEditable ;
98     BOOL m_formerSelectable;
99     NSView* m_textView;
100
101     // The original value of ms_viewCurrentlyEdited when this object was
102     // created.
103     NSView* m_viewPreviouslyEdited;
104
105     static NSView* ms_viewCurrentlyEdited;
106 } ;
107
108 NSView* wxMacEditHelper::ms_viewCurrentlyEdited = nil;
109
110 // a minimal NSFormatter that just avoids getting too long entries
111 @interface wxMaximumLengthFormatter : NSFormatter
112 {
113     int maxLength;
114 }
115
116 @end
117
118 @implementation wxMaximumLengthFormatter
119
120 - (id)init 
121 {
122     self = [super init];
123     maxLength = 0;
124     return self;
125 }
126
127 - (void) setMaxLength:(int) maxlen 
128 {
129     maxLength = maxlen;
130 }
131
132 - (NSString *)stringForObjectValue:(id)anObject 
133 {
134     if(![anObject isKindOfClass:[NSString class]])
135         return nil;
136     return [NSString stringWithString:anObject];
137 }
138
139 - (BOOL)getObjectValue:(id *)obj forString:(NSString *)string errorDescription:(NSString  **)error 
140 {
141     *obj = [NSString stringWithString:string];
142     return YES;
143 }
144
145 - (BOOL)isPartialStringValid:(NSString **)partialStringPtr proposedSelectedRange:(NSRangePointer)proposedSelRangePtr 
146               originalString:(NSString *)origString originalSelectedRange:(NSRange)origSelRange errorDescription:(NSString **)error
147 {
148     int len = [*partialStringPtr length];
149     if ( maxLength > 0 && len > maxLength )
150     {
151         // TODO wxEVT_TEXT_MAXLEN
152         return NO;
153     }
154     return YES;
155 }
156
157 @end
158
159 @implementation wxNSSecureTextField
160
161 + (void)initialize
162 {
163     static BOOL initialized = NO;
164     if (!initialized)
165     {
166         initialized = YES;
167         wxOSXCocoaClassAddWXMethods( self );
168     }
169 }
170
171 - (void)controlTextDidChange:(NSNotification *)aNotification
172 {
173     wxUnusedVar(aNotification);
174     wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
175     if ( impl )
176         impl->controlTextDidChange();
177 }
178
179 - (void)controlTextDidEndEditing:(NSNotification *)aNotification
180 {
181     wxUnusedVar(aNotification);
182     wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
183     if ( impl )
184     {
185         impl->DoNotifyFocusEvent( false, NULL );
186     }
187 }
188
189 - (BOOL)control:(NSControl*)control textView:(NSTextView*)textView doCommandBySelector:(SEL)commandSelector
190 {
191     wxUnusedVar(textView);
192     
193     BOOL handled = NO;
194     
195     wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( control );
196     if ( impl  )
197     {
198         wxWindow* wxpeer = (wxWindow*) impl->GetWXPeer();
199         if ( wxpeer )
200         {
201             if (commandSelector == @selector(insertNewline:))
202             {
203                 wxTopLevelWindow *tlw = wxDynamicCast(wxGetTopLevelParent(wxpeer), wxTopLevelWindow);
204                 if ( tlw && tlw->GetDefaultItem() )
205                 {
206                     wxButton *def = wxDynamicCast(tlw->GetDefaultItem(), wxButton);
207                     if ( def && def->IsEnabled() )
208                     {
209                         wxCommandEvent event(wxEVT_BUTTON, def->GetId() );
210                         event.SetEventObject(def);
211                         def->Command(event);
212                         handled = YES;
213                     }
214                 }
215             }
216         }
217     }
218     
219     return handled;
220 }
221
222 @end
223
224 @interface wxNSTextScrollView : NSScrollView
225 {
226 }
227 @end
228
229 @implementation wxNSTextScrollView
230
231 + (void)initialize
232 {
233     static BOOL initialized = NO;
234     if (!initialized)
235     {
236         initialized = YES;
237         wxOSXCocoaClassAddWXMethods( self );
238     }
239 }
240
241 @end
242
243 @implementation wxNSTextFieldEditor
244
245 - (void) keyDown:(NSEvent*) event
246 {
247     wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( (WXWidget) [self delegate] );
248     lastKeyDownEvent = event;
249     if ( impl == NULL || !impl->DoHandleKeyEvent(event) )
250         [super keyDown:event];
251     lastKeyDownEvent = nil;
252 }
253
254 - (void) keyUp:(NSEvent*) event
255 {
256     wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( (WXWidget) [self delegate] );
257     if ( impl == NULL || !impl->DoHandleKeyEvent(event) )
258         [super keyUp:event];
259 }
260
261 - (void) flagsChanged:(NSEvent*) event
262 {
263     wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( (WXWidget) [self delegate] );
264     if ( impl == NULL || !impl->DoHandleKeyEvent(event) )
265         [super flagsChanged:event];
266 }
267
268 - (BOOL) performKeyEquivalent:(NSEvent*) event
269 {
270     BOOL retval = [super performKeyEquivalent:event];
271     return retval;
272 }
273
274 - (void) insertText:(id) str
275 {
276     wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( (WXWidget) [self delegate] );
277     if ( impl == NULL || lastKeyDownEvent==nil || !impl->DoHandleCharEvent(lastKeyDownEvent, str) )
278     {
279         [super insertText:str];
280     }
281 }
282
283 @end
284
285 @implementation wxNSTextView
286
287 + (void)initialize
288 {
289     static BOOL initialized = NO;
290     if (!initialized)
291     {
292         initialized = YES;
293         wxOSXCocoaClassAddWXMethods( self );
294     }
295 }
296
297 - (void)textDidChange:(NSNotification *)aNotification
298 {
299     wxUnusedVar(aNotification);
300     wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
301     if ( impl )
302         impl->controlTextDidChange();
303 }
304
305 - (void) setEnabled:(BOOL) flag
306 {
307     // from Technical Q&A QA1461
308     if (flag) {
309         [self setTextColor: [NSColor controlTextColor]];
310
311     } else {
312         [self setTextColor: [NSColor disabledControlTextColor]];
313     }
314
315     [self setSelectable: flag];
316     [self setEditable: flag];
317 }
318
319 - (BOOL) isEnabled
320 {
321     return [self isEditable];
322 }
323
324 - (void)textDidEndEditing:(NSNotification *)aNotification
325 {
326     wxUnusedVar(aNotification);
327
328     if ( self == wxMacEditHelper::GetCurrentlyEditedView() )
329     {
330         // This notification is generated as the result of calling our own
331         // wxTextCtrl method (e.g. WriteText()) and doesn't correspond to any
332         // real focus loss event so skip generating it.
333         return;
334     }
335
336     wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
337     if ( impl )
338     {
339         impl->DoNotifyFocusEvent( false, NULL );
340     }
341 }
342
343 @end
344
345 @implementation wxNSTextField
346
347 + (void)initialize
348 {
349     static BOOL initialized = NO;
350     if (!initialized)
351     {
352         initialized = YES;
353         wxOSXCocoaClassAddWXMethods( self );
354     }
355 }
356
357 - (id) initWithFrame:(NSRect) frame
358 {
359     self = [super initWithFrame:frame];
360     fieldEditor = nil;
361     return self;
362 }
363
364 - (void) dealloc
365 {
366     [fieldEditor release];
367     [super dealloc];
368 }
369
370 - (void) setFieldEditor:(wxNSTextFieldEditor*) editor
371 {
372     if ( editor != fieldEditor )
373     {
374         [editor retain];
375         [fieldEditor release];
376         fieldEditor = editor;
377     }
378 }
379
380 - (wxNSTextFieldEditor*) fieldEditor
381 {
382     return fieldEditor;
383 }
384
385 - (void) setEnabled:(BOOL) flag
386 {
387     [super setEnabled: flag];
388
389     if (![self drawsBackground]) {
390         // Static text is drawn incorrectly when disabled.
391         // For an explanation, see
392         // http://www.cocoabuilder.com/archive/message/cocoa/2006/7/21/168028
393         if (flag) {
394             [self setTextColor: [NSColor controlTextColor]];
395         } else {
396             [self setTextColor: [NSColor secondarySelectedControlColor]];
397         }
398     }
399 }
400
401 - (void)controlTextDidChange:(NSNotification *)aNotification
402 {
403     wxUnusedVar(aNotification);
404     wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
405     if ( impl )
406         impl->controlTextDidChange();
407 }
408
409 - (NSArray *)control:(NSControl *)control textView:(NSTextView *)textView completions:(NSArray *)words
410  forPartialWordRange:(NSRange)charRange indexOfSelectedItem:(NSInteger*)index
411 {
412     NSMutableArray* matches = NULL;
413
414     wxTextWidgetImpl* impl = (wxNSTextFieldControl * ) wxWidgetImpl::FindFromWXWidget( self );
415     wxTextEntry * const entry = impl->GetTextEntry();
416     wxTextCompleter * const completer = entry->OSXGetCompleter();
417     if ( completer )
418     {
419         const wxString prefix = entry->GetValue();
420         if ( completer->Start(prefix) )
421         {
422             const wxString
423                 wordStart = wxCFStringRef::AsString(
424                               [[textView string] substringWithRange:charRange]
425                             );
426
427             matches = [NSMutableArray array];
428             for ( ;; )
429             {
430                 const wxString s = completer->GetNext();
431                 if ( s.empty() )
432                     break;
433
434                 // Normally the completer should return only the strings
435                 // starting with the prefix, but there could be exceptions
436                 // and, for compatibility with MSW which simply ignores all
437                 // entries that don't match the current text control contents,
438                 // we ignore them as well. Besides, our own wxTextCompleterFixed
439                 // doesn't respect this rule and, moreover, we need to extract
440                 // just the rest of the string anyhow.
441                 wxString completion;
442                 if ( s.StartsWith(prefix, &completion) )
443                 {
444                     // We discarded the entire prefix above but actually we
445                     // should include the part of it that consists of the
446                     // beginning of the current word, otherwise it would be
447                     // lost when completion is accepted as OS X supposes that
448                     // our matches do start with the "partial word range"
449                     // passed to us.
450                     const wxCFStringRef fullWord(wordStart + completion);
451                     [matches addObject: fullWord.AsNSString()];
452                 }
453             }
454         }
455     }
456
457     return matches;
458 }
459
460 - (BOOL)control:(NSControl*)control textView:(NSTextView*)textView doCommandBySelector:(SEL)commandSelector
461 {
462     wxUnusedVar(textView);
463     wxUnusedVar(control);
464     
465     BOOL handled = NO;
466
467     // send back key events wx' common code knows how to handle
468     
469     wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
470     if ( impl  )
471     {
472         wxWindow* wxpeer = (wxWindow*) impl->GetWXPeer();
473         if ( wxpeer )
474         {
475             if (commandSelector == @selector(insertNewline:))
476             {
477                 [textView insertNewlineIgnoringFieldEditor:self];
478                 handled = YES;
479             }
480             else if ( commandSelector == @selector(insertTab:))
481             {
482                 [textView insertTabIgnoringFieldEditor:self];
483                 handled = YES;
484             }
485             else if ( commandSelector == @selector(insertBacktab:))
486             {
487                 [textView insertTabIgnoringFieldEditor:self];
488                 handled = YES;
489             }
490         }
491     }
492     
493     return handled;
494 }
495
496 - (void)controlTextDidEndEditing:(NSNotification *)aNotification
497 {
498     wxUnusedVar(aNotification);
499     wxWidgetCocoaImpl* impl = (wxWidgetCocoaImpl* ) wxWidgetImpl::FindFromWXWidget( self );
500     if ( impl )
501     {
502         wxNSTextFieldControl* timpl = dynamic_cast<wxNSTextFieldControl*>(impl);
503         if ( fieldEditor )
504         {
505             NSRange range = [fieldEditor selectedRange];
506             timpl->SetInternalSelection(range.location, range.location + range.length);
507         }
508
509         impl->DoNotifyFocusEvent( false, NULL );
510     }
511 }
512 @end
513
514 // wxNSTextViewControl
515
516 wxNSTextViewControl::wxNSTextViewControl( wxTextCtrl *wxPeer, WXWidget w )
517     : wxWidgetCocoaImpl(wxPeer, w),
518       wxTextWidgetImpl(wxPeer)
519 {
520     wxNSTextScrollView* sv = (wxNSTextScrollView*) w;
521     m_scrollView = sv;
522
523     [m_scrollView setHasVerticalScroller:YES];
524     [m_scrollView setHasHorizontalScroller:NO];
525     // TODO Remove if no regression, this was causing automatic resizes of multi-line textfields when the tlw changed
526     // [m_scrollView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
527     NSSize contentSize = [m_scrollView contentSize];
528
529     wxNSTextView* tv = [[wxNSTextView alloc] initWithFrame: NSMakeRect(0, 0,
530             contentSize.width, contentSize.height)];
531     m_textView = tv;
532     [tv setVerticallyResizable:YES];
533     [tv setHorizontallyResizable:NO];
534     [tv setAutoresizingMask:NSViewWidthSizable];
535
536     [m_scrollView setDocumentView: tv];
537
538     [tv setDelegate: tv];
539
540     InstallEventHandler(tv);
541 }
542
543 wxNSTextViewControl::~wxNSTextViewControl()
544 {
545     if (m_textView)
546         [m_textView setDelegate: nil];
547 }
548
549 bool wxNSTextViewControl::CanFocus() const
550 {
551     // since this doesn't work (return false), we hardcode
552     // if (m_textView)
553     //    return [m_textView canBecomeKeyView];
554     return true;
555 }
556
557 wxString wxNSTextViewControl::GetStringValue() const
558 {
559     if (m_textView)
560     {
561         wxString result = wxCFStringRef::AsString([m_textView string], m_wxPeer->GetFont().GetEncoding());
562         wxMacConvertNewlines13To10( &result ) ;
563         return result;
564     }
565     return wxEmptyString;
566 }
567 void wxNSTextViewControl::SetStringValue( const wxString &str)
568 {
569     wxString st = str;
570     wxMacConvertNewlines10To13( &st );
571     wxMacEditHelper helper(m_textView);
572
573     if (m_textView)
574         [m_textView setString: wxCFStringRef( st , m_wxPeer->GetFont().GetEncoding() ).AsNSString()];
575 }
576
577 void wxNSTextViewControl::Copy()
578 {
579     if (m_textView)
580         [m_textView copy:nil];
581
582 }
583
584 void wxNSTextViewControl::Cut()
585 {
586     if (m_textView)
587         [m_textView cut:nil];
588 }
589
590 void wxNSTextViewControl::Paste()
591 {
592     if (m_textView)
593         [m_textView paste:nil];
594 }
595
596 bool wxNSTextViewControl::CanPaste() const
597 {
598     return true;
599 }
600
601 void wxNSTextViewControl::SetEditable(bool editable)
602 {
603     if (m_textView)
604         [m_textView setEditable: editable];
605 }
606
607 void wxNSTextViewControl::GetSelection( long* from, long* to) const
608 {
609     if (m_textView)
610     {
611         NSRange range = [m_textView selectedRange];
612         *from = range.location;
613         *to = range.location + range.length;
614     }
615 }
616
617 void wxNSTextViewControl::SetSelection( long from , long to )
618 {
619     long textLength = [[m_textView string] length];
620     if ((from == -1) && (to == -1))
621     {
622         from = 0 ;
623         to = textLength ;
624     }
625     else
626     {
627         from = wxMin(textLength,wxMax(from,0)) ;
628         if ( to == -1 )
629             to = textLength;
630         else
631             to = wxMax(0,wxMin(textLength,to)) ;
632     }
633
634     NSRange selrange = NSMakeRange(from, to-from);
635     [m_textView setSelectedRange:selrange];
636     [m_textView scrollRangeToVisible:selrange];
637 }
638
639 void wxNSTextViewControl::WriteText(const wxString& str)
640 {
641     wxString st = str;
642     wxMacConvertNewlines10To13( &st );
643     wxMacEditHelper helper(m_textView);
644     NSEvent* formerEvent = m_lastKeyDownEvent;
645     m_lastKeyDownEvent = nil;
646     [m_textView insertText:wxCFStringRef( st , m_wxPeer->GetFont().GetEncoding() ).AsNSString()];
647     m_lastKeyDownEvent = formerEvent;
648 }
649
650 void wxNSTextViewControl::SetFont( const wxFont & font , const wxColour& WXUNUSED(foreground) , long WXUNUSED(windowStyle), bool WXUNUSED(ignoreBlack) )
651 {
652     if ([m_textView respondsToSelector:@selector(setFont:)])
653         [m_textView setFont: font.OSXGetNSFont()];
654 }
655
656 bool wxNSTextViewControl::GetStyle(long position, wxTextAttr& style)
657 {
658     if (m_textView && position >=0)
659     {   
660         NSFont* font = NULL;
661         NSColor* bgcolor = NULL;
662         NSColor* fgcolor = NULL;
663         // NOTE: It appears that other platforms accept GetStyle with the position == length
664         // but that NSTextStorage does not accept length as a valid position.
665         // Therefore we return the default control style in that case.
666         if (position < (long) [[m_textView string] length]) 
667         {
668             NSTextStorage* storage = [m_textView textStorage];
669             font = [[storage attribute:NSFontAttributeName atIndex:position effectiveRange:NULL] autorelease];
670             bgcolor = [[storage attribute:NSBackgroundColorAttributeName atIndex:position effectiveRange:NULL] autorelease];
671             fgcolor = [[storage attribute:NSForegroundColorAttributeName atIndex:position effectiveRange:NULL] autorelease];
672         }
673         else
674         {
675             NSDictionary* attrs = [m_textView typingAttributes];
676             font = [[attrs objectForKey:NSFontAttributeName] autorelease];
677             bgcolor = [[attrs objectForKey:NSBackgroundColorAttributeName] autorelease];
678             fgcolor = [[attrs objectForKey:NSForegroundColorAttributeName] autorelease];
679         }
680         
681         if (font)
682             style.SetFont(wxFont(font));
683         
684         if (bgcolor)
685             style.SetBackgroundColour(wxColour(bgcolor));
686             
687         if (fgcolor)
688             style.SetTextColour(wxColour(fgcolor));
689         return true;
690     }
691
692     return false;
693 }
694
695 void wxNSTextViewControl::SetStyle(long start,
696                                 long end,
697                                 const wxTextAttr& style)
698 {
699     if ( !m_textView )
700         return;
701
702     if ( start == -1 && end == -1 )
703     {
704         NSMutableDictionary* const
705             attrs = [NSMutableDictionary dictionaryWithCapacity:3];
706         if ( style.HasFont() )
707             [attrs setValue:style.GetFont().OSXGetNSFont() forKey:NSFontAttributeName];
708         if ( style.HasBackgroundColour() )
709             [attrs setValue:style.GetBackgroundColour().OSXGetNSColor() forKey:NSBackgroundColorAttributeName];
710         if ( style.HasTextColour() )
711             [attrs setValue:style.GetTextColour().OSXGetNSColor() forKey:NSForegroundColorAttributeName];
712
713         [m_textView setTypingAttributes:attrs];
714     }
715     else // Set the attributes just for this range.
716     {
717         NSRange range = NSMakeRange(start, end-start);
718
719         NSTextStorage* storage = [m_textView textStorage];
720         if ( style.HasFont() )
721             [storage addAttribute:NSFontAttributeName value:style.GetFont().OSXGetNSFont() range:range];
722
723         if ( style.HasBackgroundColour() )
724             [storage addAttribute:NSBackgroundColorAttributeName value:style.GetBackgroundColour().OSXGetNSColor() range:range];
725
726         if ( style.HasTextColour() )
727             [storage addAttribute:NSForegroundColorAttributeName value:style.GetTextColour().OSXGetNSColor() range:range];
728     }
729 }
730
731 void wxNSTextViewControl::CheckSpelling(bool check)
732 {
733     if (m_textView)
734         [m_textView setContinuousSpellCheckingEnabled: check];
735 }
736
737 wxSize wxNSTextViewControl::GetBestSize() const
738 {
739     if (m_textView && [m_textView layoutManager])
740     {
741         NSRect rect = [[m_textView layoutManager] usedRectForTextContainer: [m_textView textContainer]];
742         return wxSize((int)(rect.size.width + [m_textView textContainerInset].width),
743                       (int)(rect.size.height + [m_textView textContainerInset].height));
744     }
745     return wxSize(0,0);
746 }
747
748 // wxNSTextFieldControl
749
750 wxNSTextFieldControl::wxNSTextFieldControl( wxTextCtrl *text, WXWidget w )
751     : wxWidgetCocoaImpl(text, w),
752       wxTextWidgetImpl(text)
753 {
754     Init(w);
755 }
756
757 wxNSTextFieldControl::wxNSTextFieldControl(wxWindow *wxPeer,
758                                            wxTextEntry *entry,
759                                            WXWidget w)
760     : wxWidgetCocoaImpl(wxPeer, w),
761       wxTextWidgetImpl(entry)
762 {
763     Init(w);
764 }
765
766 void wxNSTextFieldControl::Init(WXWidget w)
767 {
768     NSTextField wxOSX_10_6_AND_LATER(<NSTextFieldDelegate>) *tf = (NSTextField wxOSX_10_6_AND_LATER(<NSTextFieldDelegate>)*) w;
769     m_textField = tf;
770     [m_textField setDelegate: tf];
771     m_selStart = m_selEnd = 0;
772     m_hasEditor = [w isKindOfClass:[NSTextField class]];
773 }
774
775 wxNSTextFieldControl::~wxNSTextFieldControl()
776 {
777     if (m_textField)
778         [m_textField setDelegate: nil];
779 }
780
781 wxString wxNSTextFieldControl::GetStringValue() const
782 {
783     return wxCFStringRef::AsString([m_textField stringValue], m_wxPeer->GetFont().GetEncoding());
784 }
785
786 void wxNSTextFieldControl::SetStringValue( const wxString &str)
787 {
788     wxMacEditHelper helper(m_textField);
789     [m_textField setStringValue: wxCFStringRef( str , m_wxPeer->GetFont().GetEncoding() ).AsNSString()];
790 }
791
792 void wxNSTextFieldControl::SetMaxLength(unsigned long len)
793 {
794     wxMaximumLengthFormatter* formatter = [[[wxMaximumLengthFormatter alloc] init] autorelease];
795     [formatter setMaxLength:len];
796     [m_textField setFormatter:formatter];
797 }
798
799 void wxNSTextFieldControl::Copy()
800 {
801     NSText* editor = [m_textField currentEditor];
802     if ( editor )
803     {
804         [editor copy:nil];
805     }
806 }
807
808 void wxNSTextFieldControl::Cut()
809 {
810     NSText* editor = [m_textField currentEditor];
811     if ( editor )
812     {
813         [editor cut:nil];
814     }
815 }
816
817 void wxNSTextFieldControl::Paste()
818 {
819     NSText* editor = [m_textField currentEditor];
820     if ( editor )
821     {
822         [editor paste:nil];
823     }
824 }
825
826 bool wxNSTextFieldControl::CanPaste() const
827 {
828     return true;
829 }
830
831 void wxNSTextFieldControl::SetEditable(bool editable)
832 {
833     [m_textField setEditable:editable];
834 }
835
836 void wxNSTextFieldControl::GetSelection( long* from, long* to) const
837 {
838     NSText* editor = [m_textField currentEditor];
839     if ( editor )
840     {
841         NSRange range = [editor selectedRange];
842         *from = range.location;
843         *to = range.location + range.length;
844     }
845     else
846     {
847         *from = m_selStart;
848         *to = m_selEnd;
849     }
850 }
851
852 void wxNSTextFieldControl::SetSelection( long from , long to )
853 {
854     long textLength = [[m_textField stringValue] length];
855     if ((from == -1) && (to == -1))
856     {
857         from = 0 ;
858         to = textLength ;
859     }
860     else
861     {
862         from = wxMin(textLength,wxMax(from,0)) ;
863         if ( to == -1 )
864             to = textLength;
865         else
866             to = wxMax(0,wxMin(textLength,to)) ;
867     }
868
869     NSText* editor = [m_textField currentEditor];
870     if ( editor )
871     {
872         [editor setSelectedRange:NSMakeRange(from, to-from)];
873     }
874
875     // the editor might still be in existence, but we might be already passed our 'focus lost' storage
876     // of the selection, so make sure we copy this
877     m_selStart = from;
878     m_selEnd = to;
879 }
880
881 void wxNSTextFieldControl::WriteText(const wxString& str)
882 {
883     NSEvent* formerEvent = m_lastKeyDownEvent;
884     m_lastKeyDownEvent = nil;
885     NSText* editor = [m_textField currentEditor];
886     if ( editor )
887     {
888         wxMacEditHelper helper(m_textField);
889         BOOL hasUndo = [editor respondsToSelector:@selector(setAllowsUndo:)];
890         if ( hasUndo )
891             [(NSTextView*)editor setAllowsUndo:NO];
892         [editor insertText:wxCFStringRef( str , m_wxPeer->GetFont().GetEncoding() ).AsNSString()];
893         if ( hasUndo )
894             [(NSTextView*)editor setAllowsUndo:YES];
895     }
896     else
897     {
898         wxString val = GetStringValue() ;
899         long start , end ;
900         GetSelection( &start , &end ) ;
901         val.Remove( start , end - start ) ;
902         val.insert( start , str ) ;
903         SetStringValue( val ) ;
904         SetSelection( start + str.length() , start + str.length() ) ;
905     }
906     m_lastKeyDownEvent = formerEvent;
907 }
908
909 void wxNSTextFieldControl::controlAction(WXWidget WXUNUSED(slf),
910     void* WXUNUSED(_cmd), void *WXUNUSED(sender))
911 {
912     wxWindow* wxpeer = (wxWindow*) GetWXPeer();
913     if ( wxpeer && (wxpeer->GetWindowStyle() & wxTE_PROCESS_ENTER) )
914     {
915         wxCommandEvent event(wxEVT_TEXT_ENTER, wxpeer->GetId());
916         event.SetEventObject( wxpeer );
917         event.SetString( GetTextEntry()->GetValue() );
918         wxpeer->HandleWindowEvent( event );
919     }
920 }
921
922 void wxNSTextFieldControl::SetInternalSelection( long from , long to )
923 {
924     m_selStart = from;
925     m_selEnd = to;
926 }
927
928 // as becoming first responder on a window - triggers a resign on the same control, we have to avoid
929 // the resign notification writing back native selection values before we can set our own
930
931 static WXWidget s_widgetBecomingFirstResponder = nil;
932
933 bool wxNSTextFieldControl::becomeFirstResponder(WXWidget slf, void *_cmd)
934 {
935     s_widgetBecomingFirstResponder = slf;
936     bool retval = wxWidgetCocoaImpl::becomeFirstResponder(slf, _cmd);
937     s_widgetBecomingFirstResponder = nil;
938     if ( retval )
939     {
940         NSText* editor = [m_textField currentEditor];
941         if ( editor )
942         {
943             long textLength = [[m_textField stringValue] length];
944             m_selStart = wxMin(textLength,wxMax(m_selStart,0)) ;
945             m_selEnd = wxMax(0,wxMin(textLength,m_selEnd)) ;
946             
947             [editor setSelectedRange:NSMakeRange(m_selStart, m_selEnd-m_selStart)];
948         }
949     }
950     return retval;
951 }
952
953 bool wxNSTextFieldControl::resignFirstResponder(WXWidget slf, void *_cmd)
954 {
955     if ( slf != s_widgetBecomingFirstResponder )
956     {
957         NSText* editor = [m_textField currentEditor];
958         if ( editor )
959         {
960             NSRange range = [editor selectedRange];
961             m_selStart = range.location;
962             m_selEnd = range.location + range.length;
963         }
964     }
965     return wxWidgetCocoaImpl::resignFirstResponder(slf, _cmd);
966 }
967
968 bool wxNSTextFieldControl::SetHint(const wxString& hint)
969 {
970     wxCFStringRef hintstring(hint);
971     [[m_textField cell] setPlaceholderString:hintstring.AsNSString()];
972     return true;
973 }
974
975 //
976 //
977 //
978
979 wxWidgetImplType* wxWidgetImpl::CreateTextControl( wxTextCtrl* wxpeer,
980                                     wxWindowMac* WXUNUSED(parent),
981                                     wxWindowID WXUNUSED(id),
982                                     const wxString& WXUNUSED(str),
983                                     const wxPoint& pos,
984                                     const wxSize& size,
985                                     long style,
986                                     long WXUNUSED(extraStyle))
987 {
988     NSRect r = wxOSXGetFrameForControl( wxpeer, pos , size ) ;
989     wxWidgetCocoaImpl* c = NULL;
990
991     if ( style & wxTE_MULTILINE )
992     {
993         wxNSTextScrollView* v = nil;
994         v = [[wxNSTextScrollView alloc] initWithFrame:r];
995         c = new wxNSTextViewControl( wxpeer, v );
996         c->SetNeedsFocusRect( true );
997     }
998     else
999     {
1000         NSTextField* v = nil;
1001         if ( style & wxTE_PASSWORD )
1002             v = [[wxNSSecureTextField alloc] initWithFrame:r];
1003         else
1004             v = [[wxNSTextField alloc] initWithFrame:r];
1005
1006         if ( style & wxTE_RIGHT)
1007         {
1008             [v setAlignment:NSRightTextAlignment];
1009         }
1010         else if ( style & wxTE_CENTRE)
1011         {
1012             [v setAlignment:NSCenterTextAlignment];
1013         }
1014                 
1015         NSTextFieldCell* cell = [v cell];
1016         [cell setScrollable:YES];
1017         // TODO: Remove if we definitely are sure, it's not needed
1018         // as setting scrolling to yes, should turn off any wrapping
1019         // [cell setLineBreakMode:NSLineBreakByClipping]; 
1020
1021         c = new wxNSTextFieldControl( wxpeer, wxpeer, v );
1022         
1023         if ( (style & wxNO_BORDER) || (style & wxSIMPLE_BORDER) )
1024         {
1025             // under 10.7 the textcontrol can draw its own focus
1026             // even if no border is shown, on previous systems
1027             // we have to emulate this
1028             [v setBezeled:NO];
1029             [v setBordered:NO];
1030             if ( UMAGetSystemVersion() < 0x1070 )
1031                 c->SetNeedsFocusRect( true );
1032         }
1033         else
1034         {
1035             // use native border
1036             c->SetNeedsFrame(false);
1037         }
1038     }
1039
1040     return c;
1041 }
1042
1043
1044 #endif // wxUSE_TEXTCTRL