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