1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/osx/textctrl_osx.cpp 
   4 // Author:      Stefan Csomor 
   5 // Modified by: Ryan Norton (MLTE GetLineLength and GetLineText) 
   7 // RCS-ID:      $Id: textctrl.cpp 54820 2008-07-29 20:04:11Z SC $ 
   8 // Copyright:   (c) Stefan Csomor 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 #include "wx/wxprec.h" 
  16 #include "wx/textctrl.h" 
  23     #include "wx/button.h" 
  25     #include "wx/settings.h" 
  26     #include "wx/msgdlg.h" 
  27     #include "wx/toplevel.h" 
  31     #include <sys/types.h> 
  37 #if wxUSE_STD_IOSTREAM 
  45 #include "wx/filefn.h" 
  46 #include "wx/sysopt.h" 
  47 #include "wx/thread.h" 
  49 #include "wx/osx/private.h" 
  51 IMPLEMENT_DYNAMIC_CLASS(wxTextCtrl
, wxTextCtrlBase
) 
  53 BEGIN_EVENT_TABLE(wxTextCtrl
, wxTextCtrlBase
) 
  54     EVT_DROP_FILES(wxTextCtrl::OnDropFiles
) 
  55     EVT_CHAR(wxTextCtrl::OnChar
) 
  56     EVT_MENU(wxID_CUT
, wxTextCtrl::OnCut
) 
  57     EVT_MENU(wxID_COPY
, wxTextCtrl::OnCopy
) 
  58     EVT_MENU(wxID_PASTE
, wxTextCtrl::OnPaste
) 
  59     EVT_MENU(wxID_UNDO
, wxTextCtrl::OnUndo
) 
  60     EVT_MENU(wxID_REDO
, wxTextCtrl::OnRedo
) 
  61     EVT_MENU(wxID_CLEAR
, wxTextCtrl::OnDelete
) 
  62     EVT_MENU(wxID_SELECTALL
, wxTextCtrl::OnSelectAll
) 
  64     EVT_CONTEXT_MENU(wxTextCtrl::OnContextMenu
) 
  66     EVT_UPDATE_UI(wxID_CUT
, wxTextCtrl::OnUpdateCut
) 
  67     EVT_UPDATE_UI(wxID_COPY
, wxTextCtrl::OnUpdateCopy
) 
  68     EVT_UPDATE_UI(wxID_PASTE
, wxTextCtrl::OnUpdatePaste
) 
  69     EVT_UPDATE_UI(wxID_UNDO
, wxTextCtrl::OnUpdateUndo
) 
  70     EVT_UPDATE_UI(wxID_REDO
, wxTextCtrl::OnUpdateRedo
) 
  71     EVT_UPDATE_UI(wxID_CLEAR
, wxTextCtrl::OnUpdateDelete
) 
  72     EVT_UPDATE_UI(wxID_SELECTALL
, wxTextCtrl::OnUpdateSelectAll
) 
  76 void wxTextCtrl::Init() 
  82     m_privateContextMenu 
= NULL
; 
  83     m_triggerUpdateEvents 
= true ; 
  86 wxTextCtrl::~wxTextCtrl() 
  89     delete m_privateContextMenu
; 
  93 bool wxTextCtrl::Create( wxWindow 
*parent
, 
  99     const wxValidator
& validator
, 
 100     const wxString
& name 
) 
 102     m_macIsUserPane 
= false ; 
 105     if ( ! (style 
& wxNO_BORDER
) ) 
 106         style 
= (style 
& ~wxBORDER_MASK
) | wxSUNKEN_BORDER 
; 
 108     if ( !wxTextCtrlBase::Create( parent
, id
, pos
, size
, style 
& ~(wxHSCROLL 
| wxVSCROLL
), validator
, name 
) ) 
 111     if ( m_windowStyle 
& wxTE_MULTILINE 
) 
 113         // always turn on this style for multi-line controls 
 114         m_windowStyle 
|= wxTE_PROCESS_ENTER
; 
 115         style 
|= wxTE_PROCESS_ENTER 
; 
 119     m_peer 
= wxWidgetImpl::CreateTextControl( this, GetParent(), GetId(), str
, pos
, size
, style
, GetExtraStyle() ); 
 121     MacPostControlCreate(pos
, size
) ; 
 124     // under carbon everything can already be set before the MacPostControlCreate embedding takes place 
 125     // but under cocoa for single line textfields this only works after everything has been set up 
 126     GetTextPeer()->SetStringValue(str
); 
 129     // only now the embedding is correct and we can do a positioning update 
 131     MacSuperChangedPosition() ; 
 133     if ( m_windowStyle 
& wxTE_READONLY
) 
 134         SetEditable( false ) ; 
 136     SetCursor( wxCursor( wxCURSOR_IBEAM 
) ) ; 
 141 wxTextWidgetImpl
* wxTextCtrl::GetTextPeer() const 
 143     return dynamic_cast<wxTextWidgetImpl
*> (m_peer
); 
 146 void wxTextCtrl::MacSuperChangedPosition() 
 148     wxWindow::MacSuperChangedPosition() ; 
 150     GetPeer()->SuperChangedPosition() ; 
 154 void wxTextCtrl::MacVisibilityChanged() 
 157     GetPeer()->VisibilityChanged( GetPeer()->IsVisible() ); 
 161 void wxTextCtrl::MacCheckSpelling(bool check
) 
 163     GetTextPeer()->CheckSpelling(check
); 
 166 wxString 
wxTextCtrl::DoGetValue() const 
 168     return GetTextPeer()->GetStringValue() ; 
 171 void wxTextCtrl::GetSelection(long* from
, long* to
) const 
 173     GetTextPeer()->GetSelection( from 
, to 
) ; 
 176 void wxTextCtrl::SetMaxLength(unsigned long len
) 
 181 bool wxTextCtrl::SetFont( const wxFont
& font 
) 
 183     if ( !wxTextCtrlBase::SetFont( font 
) ) 
 186     GetPeer()->SetFont( font 
, GetForegroundColour() , GetWindowStyle(), false /* dont ignore black */ ) ; 
 191 bool wxTextCtrl::SetStyle(long start
, long end
, const wxTextAttr
& style
) 
 194         GetTextPeer()->SetStyle( start 
, end 
, style 
) ; 
 199 bool wxTextCtrl::SetDefaultStyle(const wxTextAttr
& style
) 
 201     wxTextCtrlBase::SetDefaultStyle( style 
) ; 
 202     SetStyle( -1  /*current selection*/  , -1 /*current selection*/ , GetDefaultStyle() ) ; 
 207 // Clipboard operations 
 209 void wxTextCtrl::Copy() 
 212         GetTextPeer()->Copy() ; 
 215 void wxTextCtrl::Cut() 
 219         GetTextPeer()->Cut() ; 
 221         SendTextUpdatedEvent(); 
 225 void wxTextCtrl::Paste() 
 229         GetTextPeer()->Paste() ; 
 231         // TODO: eventually we should add setting the default style again 
 232         SendTextUpdatedEvent(); 
 236 bool wxTextCtrl::CanCopy() const 
 238     // Can copy if there's a selection 
 240     GetSelection( &from
, &to 
); 
 245 bool wxTextCtrl::CanCut() const 
 250     // Can cut if there's a selection 
 252     GetSelection( &from
, &to 
); 
 257 bool wxTextCtrl::CanPaste() const 
 262     return GetTextPeer()->CanPaste() ; 
 265 void wxTextCtrl::SetEditable(bool editable
) 
 267     if ( editable 
!= m_editable 
) 
 269         m_editable 
= editable 
; 
 270         GetTextPeer()->SetEditable( editable 
) ; 
 274 void wxTextCtrl::SetInsertionPoint(long pos
) 
 276     SetSelection( pos 
, pos 
) ; 
 279 void wxTextCtrl::SetInsertionPointEnd() 
 281     long pos 
= GetLastPosition(); 
 282     SetInsertionPoint( pos 
); 
 285 long wxTextCtrl::GetInsertionPoint() const 
 288     GetSelection( &begin 
, &end 
) ; 
 293 wxTextPos 
wxTextCtrl::GetLastPosition() const 
 295     return GetTextPeer()->GetLastPosition() ; 
 298 void wxTextCtrl::Remove(long from
, long to
) 
 300     GetTextPeer()->Remove( from 
, to 
) ; 
 301     if ( m_triggerUpdateEvents 
) 
 302         SendTextUpdatedEvent(); 
 305 void wxTextCtrl::SetSelection(long from
, long to
) 
 307     GetTextPeer()->SetSelection( from 
, to 
) ; 
 310 void wxTextCtrl::WriteText(const wxString
& str
) 
 312     GetTextPeer()->WriteText( str 
) ; 
 313     if ( m_triggerUpdateEvents 
) 
 314         SendTextUpdatedEvent(); 
 317 void wxTextCtrl::Clear() 
 319     GetTextPeer()->Clear() ; 
 320     SendTextUpdatedEvent(); 
 323 bool wxTextCtrl::IsModified() const 
 328 bool wxTextCtrl::IsEditable() const 
 330     return IsEnabled() && m_editable 
; 
 333 bool wxTextCtrl::AcceptsFocus() const 
 335     // we don't want focus if we can't be edited 
 336     return /*IsEditable() && */ wxControl::AcceptsFocus(); 
 339 wxSize 
wxTextCtrl::DoGetBestSize() const 
 343         wxSize size 
= GetTextPeer()->GetBestSize(); 
 344         if (size
.x 
> 0 && size
.y 
> 0) 
 350     // these are the numbers from the HIG: 
 351     // we reduce them by the borders first 
 354     switch ( m_windowVariant 
) 
 356         case wxWINDOW_VARIANT_NORMAL 
: 
 360         case wxWINDOW_VARIANT_SMALL 
: 
 364         case wxWINDOW_VARIANT_MINI 
: 
 373     // as the above numbers have some free space around the text 
 374     // we get 5 lines like this anyway 
 375     if ( m_windowStyle 
& wxTE_MULTILINE 
) 
 378     if ( !HasFlag(wxNO_BORDER
) ) 
 381     return wxSize(wText
, hText
); 
 384 bool wxTextCtrl::GetStyle(long position
, wxTextAttr
& style
) 
 386     return GetTextPeer()->GetStyle(position
, style
); 
 389 // ---------------------------------------------------------------------------- 
 391 // ---------------------------------------------------------------------------- 
 393 void wxTextCtrl::Undo() 
 396         GetTextPeer()->Undo() ; 
 399 void wxTextCtrl::Redo() 
 402         GetTextPeer()->Redo() ; 
 405 bool wxTextCtrl::CanUndo() const 
 410     return GetTextPeer()->CanUndo() ; 
 413 bool wxTextCtrl::CanRedo() const 
 418     return GetTextPeer()->CanRedo() ; 
 421 void wxTextCtrl::MarkDirty() 
 426 void wxTextCtrl::DiscardEdits() 
 431 int wxTextCtrl::GetNumberOfLines() const 
 433     return GetTextPeer()->GetNumberOfLines() ; 
 436 long wxTextCtrl::XYToPosition(long x
, long y
) const 
 438     return GetTextPeer()->XYToPosition( x 
, y 
) ; 
 441 bool wxTextCtrl::PositionToXY(long pos
, long *x
, long *y
) const 
 443     return GetTextPeer()->PositionToXY( pos 
, x 
, y 
) ; 
 446 void wxTextCtrl::ShowPosition(long pos
) 
 448     return GetTextPeer()->ShowPosition(pos
) ; 
 451 int wxTextCtrl::GetLineLength(long lineNo
) const 
 453     return GetTextPeer()->GetLineLength(lineNo
) ; 
 456 wxString 
wxTextCtrl::GetLineText(long lineNo
) const 
 458     return GetTextPeer()->GetLineText(lineNo
) ; 
 461 void wxTextCtrl::Command(wxCommandEvent 
& event
) 
 463     SetValue(event
.GetString()); 
 464     ProcessCommand(event
); 
 467 void wxTextCtrl::OnDropFiles(wxDropFilesEvent
& event
) 
 469     // By default, load the first file into the text window. 
 470     if (event
.GetNumberOfFiles() > 0) 
 471         LoadFile( event
.GetFiles()[0] ); 
 474 void wxTextCtrl::OnChar(wxKeyEvent
& event
) 
 476     int key 
= event
.GetKeyCode() ; 
 477     bool eat_key 
= false ; 
 480     if ( key 
== 'a' && event
.MetaDown() ) 
 487     if ( key 
== 'c' && event
.MetaDown() ) 
 495     if ( !IsEditable() && !event
.IsKeyInCategory(WXK_CATEGORY_ARROW 
| WXK_CATEGORY_TAB
) && 
 496         !( key 
== WXK_RETURN 
&& ( (m_windowStyle 
& wxTE_PROCESS_ENTER
) || (m_windowStyle 
& wxTE_MULTILINE
) ) ) 
 497 //        && key != WXK_PAGEUP && key != WXK_PAGEDOWN && key != WXK_HOME && key != WXK_END 
 504     // Check if we have reached the max # of chars (if it is set), but still 
 505     // allow navigation and deletion 
 506     GetSelection( &from
, &to 
); 
 507     if ( !IsMultiLine() && m_maxLength 
&& GetValue().length() >= m_maxLength 
&& 
 508         !event
.IsKeyInCategory(WXK_CATEGORY_ARROW 
| WXK_CATEGORY_TAB 
| WXK_CATEGORY_CUT
) && 
 509         !( key 
== WXK_RETURN 
&& (m_windowStyle 
& wxTE_PROCESS_ENTER
) ) && 
 512         // eat it, we don't want to add more than allowed # of characters 
 514         // TODO: generate EVT_TEXT_MAXLEN() 
 518     // assume that any key not processed yet is going to modify the control 
 521     if ( key 
== 'v' && event
.MetaDown() ) 
 529     if ( key 
== 'x' && event
.MetaDown() ) 
 540             if (m_windowStyle 
& wxTE_PROCESS_ENTER
) 
 542                 wxCommandEvent 
event(wxEVT_COMMAND_TEXT_ENTER
, m_windowId
); 
 543                 event
.SetEventObject( this ); 
 544                 event
.SetString( GetValue() ); 
 545                 if ( HandleWindowEvent(event
) ) 
 549             if ( !(m_windowStyle 
& wxTE_MULTILINE
) ) 
 551                 wxTopLevelWindow 
*tlw 
= wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow
); 
 552                 if ( tlw 
&& tlw
->GetDefaultItem() ) 
 554                     wxButton 
*def 
= wxDynamicCast(tlw
->GetDefaultItem(), wxButton
); 
 555                     if ( def 
&& def
->IsEnabled() ) 
 557                         wxCommandEvent 
event(wxEVT_COMMAND_BUTTON_CLICKED
, def
->GetId() ); 
 558                         event
.SetEventObject(def
); 
 565                 // this will make wxWidgets eat the ENTER key so that 
 566                 // we actually prevent line wrapping in a single line text control 
 572             if ( !(m_windowStyle 
& wxTE_PROCESS_TAB
)) 
 575                 if (!event
.ShiftDown()) 
 576                     flags 
|= wxNavigationKeyEvent::IsForward 
; 
 577                 if (event
.ControlDown()) 
 578                     flags 
|= wxNavigationKeyEvent::WinChange 
; 
 585                 // This is necessary (don't know why); 
 586                 // otherwise the tab will not be inserted. 
 587                 WriteText(wxT("\t")); 
 598         // perform keystroke handling 
 602     // osx_cocoa sends its event upon insertText 
 604     if ( ( key 
>= 0x20 && key 
< WXK_START 
) || 
 605          ( key 
>= WXK_NUMPAD0 
&& key 
<= WXK_DIVIDE 
) || 
 610         wxCommandEvent 
event1(wxEVT_COMMAND_TEXT_UPDATED
, m_windowId
); 
 611         event1
.SetEventObject( this ); 
 612         wxPostEvent( GetEventHandler(), event1 
); 
 617 // ---------------------------------------------------------------------------- 
 618 // standard handlers for standard edit menu events 
 619 // ---------------------------------------------------------------------------- 
 621 void wxTextCtrl::OnCut(wxCommandEvent
& WXUNUSED(event
)) 
 626 void wxTextCtrl::OnCopy(wxCommandEvent
& WXUNUSED(event
)) 
 631 void wxTextCtrl::OnPaste(wxCommandEvent
& WXUNUSED(event
)) 
 636 void wxTextCtrl::OnUndo(wxCommandEvent
& WXUNUSED(event
)) 
 641 void wxTextCtrl::OnRedo(wxCommandEvent
& WXUNUSED(event
)) 
 646 void wxTextCtrl::OnDelete(wxCommandEvent
& WXUNUSED(event
)) 
 650     GetSelection( &from
, &to 
); 
 651     if (from 
!= -1 && to 
!= -1) 
 655 void wxTextCtrl::OnSelectAll(wxCommandEvent
& WXUNUSED(event
)) 
 657     SetSelection(-1, -1); 
 660 void wxTextCtrl::OnUpdateCut(wxUpdateUIEvent
& event
) 
 662     event
.Enable( CanCut() ); 
 665 void wxTextCtrl::OnUpdateCopy(wxUpdateUIEvent
& event
) 
 667     event
.Enable( CanCopy() ); 
 670 void wxTextCtrl::OnUpdatePaste(wxUpdateUIEvent
& event
) 
 672     event
.Enable( CanPaste() ); 
 675 void wxTextCtrl::OnUpdateUndo(wxUpdateUIEvent
& event
) 
 677     event
.Enable( CanUndo() ); 
 680 void wxTextCtrl::OnUpdateRedo(wxUpdateUIEvent
& event
) 
 682     event
.Enable( CanRedo() ); 
 685 void wxTextCtrl::OnUpdateDelete(wxUpdateUIEvent
& event
) 
 689     GetSelection( &from
, &to 
); 
 690     event
.Enable( from 
!= -1 && to 
!= -1 && from 
!= to 
&& IsEditable() ) ; 
 693 void wxTextCtrl::OnUpdateSelectAll(wxUpdateUIEvent
& event
) 
 695     event
.Enable(GetLastPosition() > 0); 
 698 // CS: Context Menus only work with MLTE implementations or non-multiline HIViews at the moment 
 700 void wxTextCtrl::OnContextMenu(wxContextMenuEvent
& event
) 
 702     if ( GetTextPeer()->HasOwnContextMenu() ) 
 709     if (m_privateContextMenu 
== NULL
) 
 711         m_privateContextMenu 
= new wxMenu
; 
 712         m_privateContextMenu
->Append(wxID_UNDO
, _("&Undo")); 
 713         m_privateContextMenu
->Append(wxID_REDO
, _("&Redo")); 
 714         m_privateContextMenu
->AppendSeparator(); 
 715         m_privateContextMenu
->Append(wxID_CUT
, _("Cu&t")); 
 716         m_privateContextMenu
->Append(wxID_COPY
, _("&Copy")); 
 717         m_privateContextMenu
->Append(wxID_PASTE
, _("&Paste")); 
 718         m_privateContextMenu
->Append(wxID_CLEAR
, _("&Delete")); 
 719         m_privateContextMenu
->AppendSeparator(); 
 720         m_privateContextMenu
->Append(wxID_SELECTALL
, _("Select &All")); 
 723     PopupMenu(m_privateContextMenu
); 
 727 bool wxTextCtrl::MacSetupCursor( const wxPoint
& pt 
) 
 729     if ( !GetTextPeer()->SetupCursor( pt 
) ) 
 730         return wxWindow::MacSetupCursor( pt 
) ; 
 735 // ---------------------------------------------------------------------------- 
 736 // implementation base class 
 737 // ---------------------------------------------------------------------------- 
 739 bool wxTextWidgetImpl::GetStyle(long WXUNUSED(position
), 
 740                                 wxTextAttr
& WXUNUSED(style
)) 
 745 void wxTextWidgetImpl::SetStyle(long WXUNUSED(start
), 
 747                                 const wxTextAttr
& WXUNUSED(style
)) 
 751 void wxTextWidgetImpl::Copy() 
 755 void wxTextWidgetImpl::Cut() 
 759 void wxTextWidgetImpl::Paste() 
 763 bool wxTextWidgetImpl::CanPaste() const 
 768 void wxTextWidgetImpl::SetEditable(bool WXUNUSED(editable
)) 
 772 long wxTextWidgetImpl::GetLastPosition() const 
 774     return GetStringValue().length() ; 
 777 void wxTextWidgetImpl::Replace( long from 
, long to 
, const wxString 
&val 
) 
 779     SetSelection( from 
, to 
) ; 
 783 void wxTextWidgetImpl::Remove( long from 
, long to 
) 
 785     SetSelection( from 
, to 
) ; 
 786     WriteText( wxEmptyString
) ; 
 789 void wxTextWidgetImpl::Clear() 
 791     SetStringValue( wxEmptyString 
) ; 
 794 bool wxTextWidgetImpl::CanUndo() const 
 799 void wxTextWidgetImpl::Undo() 
 803 bool wxTextWidgetImpl::CanRedo()  const 
 808 void wxTextWidgetImpl::Redo() 
 812 long wxTextWidgetImpl::XYToPosition(long WXUNUSED(x
), long WXUNUSED(y
)) const 
 817 bool wxTextWidgetImpl::PositionToXY(long WXUNUSED(pos
), 
 819                                     long *WXUNUSED(y
)) const 
 824 void wxTextWidgetImpl::ShowPosition( long WXUNUSED(pos
) ) 
 828 int wxTextWidgetImpl::GetNumberOfLines() const 
 830     wxString content 
= GetStringValue() ; 
 833     for (size_t i 
= 0; i 
< content
.length() ; i
++) 
 836         if (content
[i
] == '\n') 
 838         if (content
[i
] == '\r') 
 846 wxString 
wxTextWidgetImpl::GetLineText(long lineNo
) const 
 848     // TODO: change this if possible to reflect real lines 
 849     wxString content 
= GetStringValue() ; 
 853     for (size_t i 
= 0; i 
< content
.length() ; i
++) 
 857             // Add chars in line then 
 860             for (size_t j 
= i
; j 
< content
.length(); j
++) 
 862                 if (content
[j
] == '\n') 
 871         if (content
[i
] == '\n') 
 875     return wxEmptyString 
; 
 878 int wxTextWidgetImpl::GetLineLength(long lineNo
) const 
 880     // TODO: change this if possible to reflect real lines 
 881     wxString content 
= GetStringValue() ; 
 885     for (size_t i 
= 0; i 
< content
.length() ; i
++) 
 889             // Count chars in line then 
 891             for (size_t j 
= i
; j 
< content
.length(); j
++) 
 894                 if (content
[j
] == '\n') 
 901         if (content
[i
] == '\n') 
 908 #endif // wxUSE_TEXTCTRL