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