1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/osx/textctrl_osx.cpp 
   4 // Author:      Stefan Csomor 
   5 // Modified by: Ryan Norton (MLTE GetLineLength and GetLineText) 
   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_KEY_DOWN(wxTextCtrl::OnKeyDown
) 
  57     EVT_MENU(wxID_CUT
, wxTextCtrl::OnCut
) 
  58     EVT_MENU(wxID_COPY
, wxTextCtrl::OnCopy
) 
  59     EVT_MENU(wxID_PASTE
, wxTextCtrl::OnPaste
) 
  60     EVT_MENU(wxID_UNDO
, wxTextCtrl::OnUndo
) 
  61     EVT_MENU(wxID_REDO
, wxTextCtrl::OnRedo
) 
  62     EVT_MENU(wxID_CLEAR
, wxTextCtrl::OnDelete
) 
  63     EVT_MENU(wxID_SELECTALL
, wxTextCtrl::OnSelectAll
) 
  65     EVT_CONTEXT_MENU(wxTextCtrl::OnContextMenu
) 
  67     EVT_UPDATE_UI(wxID_CUT
, wxTextCtrl::OnUpdateCut
) 
  68     EVT_UPDATE_UI(wxID_COPY
, wxTextCtrl::OnUpdateCopy
) 
  69     EVT_UPDATE_UI(wxID_PASTE
, wxTextCtrl::OnUpdatePaste
) 
  70     EVT_UPDATE_UI(wxID_UNDO
, wxTextCtrl::OnUpdateUndo
) 
  71     EVT_UPDATE_UI(wxID_REDO
, wxTextCtrl::OnUpdateRedo
) 
  72     EVT_UPDATE_UI(wxID_CLEAR
, wxTextCtrl::OnUpdateDelete
) 
  73     EVT_UPDATE_UI(wxID_SELECTALL
, wxTextCtrl::OnUpdateSelectAll
) 
  77 void wxTextCtrl::Init() 
  81     m_privateContextMenu 
= NULL
; 
  84 wxTextCtrl::~wxTextCtrl() 
  87     delete m_privateContextMenu
; 
  91 bool wxTextCtrl::Create( wxWindow 
*parent
, 
  97     const wxValidator
& validator
, 
  98     const wxString
& name 
) 
 100     m_macIsUserPane 
= false ; 
 103     if ( ! (style 
& wxNO_BORDER
) ) 
 104         style 
= (style 
& ~wxBORDER_MASK
) | wxSUNKEN_BORDER 
; 
 106     if ( !wxTextCtrlBase::Create( parent
, id
, pos
, size
, style 
& ~(wxHSCROLL 
| wxVSCROLL
), validator
, name 
) ) 
 109     if ( m_windowStyle 
& wxTE_MULTILINE 
) 
 111         // always turn on this style for multi-line controls 
 112         m_windowStyle 
|= wxTE_PROCESS_ENTER
; 
 113         style 
|= wxTE_PROCESS_ENTER 
; 
 117     m_peer 
= wxWidgetImpl::CreateTextControl( this, GetParent(), GetId(), str
, pos
, size
, style
, GetExtraStyle() ); 
 119     MacPostControlCreate(pos
, size
) ; 
 122     // under carbon everything can already be set before the MacPostControlCreate embedding takes place 
 123     // but under cocoa for single line textfields this only works after everything has been set up 
 124     GetTextPeer()->SetStringValue(str
); 
 127     // only now the embedding is correct and we can do a positioning update 
 129     MacSuperChangedPosition() ; 
 131     if ( m_windowStyle 
& wxTE_READONLY
) 
 132         SetEditable( false ) ; 
 134     SetCursor( wxCursor( wxCURSOR_IBEAM 
) ) ; 
 139 void wxTextCtrl::MacSuperChangedPosition() 
 141     wxWindow::MacSuperChangedPosition() ; 
 143     GetPeer()->SuperChangedPosition() ; 
 147 void wxTextCtrl::MacVisibilityChanged() 
 150     GetPeer()->VisibilityChanged( GetPeer()->IsVisible() ); 
 154 void wxTextCtrl::MacCheckSpelling(bool check
) 
 156     GetTextPeer()->CheckSpelling(check
); 
 159 void wxTextCtrl::SetMaxLength(unsigned long len
) 
 164 bool wxTextCtrl::SetFont( const wxFont
& font 
) 
 166     if ( !wxTextCtrlBase::SetFont( font 
) ) 
 169     GetPeer()->SetFont( font 
, GetForegroundColour() , GetWindowStyle(), false /* dont ignore black */ ) ; 
 174 bool wxTextCtrl::SetStyle(long start
, long end
, const wxTextAttr
& style
) 
 177         GetTextPeer()->SetStyle( start 
, end 
, style 
) ; 
 182 bool wxTextCtrl::SetDefaultStyle(const wxTextAttr
& style
) 
 184     wxTextCtrlBase::SetDefaultStyle( style 
) ; 
 185     SetStyle( -1  /*current selection*/  , -1 /*current selection*/ , GetDefaultStyle() ) ; 
 190 bool wxTextCtrl::IsModified() const 
 195 bool wxTextCtrl::AcceptsFocus() const 
 197     // we don't want focus if we can't be edited 
 198     return /*IsEditable() && */ wxControl::AcceptsFocus(); 
 201 wxSize 
wxTextCtrl::DoGetBestSize() const 
 205         wxSize size 
= GetTextPeer()->GetBestSize(); 
 206         if (size
.x 
> 0 && size
.y 
> 0) 
 212     // these are the numbers from the HIG: 
 213     // we reduce them by the borders first 
 216     switch ( m_windowVariant 
) 
 218         case wxWINDOW_VARIANT_NORMAL 
: 
 222         case wxWINDOW_VARIANT_SMALL 
: 
 226         case wxWINDOW_VARIANT_MINI 
: 
 235     // as the above numbers have some free space around the text 
 236     // we get 5 lines like this anyway 
 237     if ( m_windowStyle 
& wxTE_MULTILINE 
) 
 240     if ( !HasFlag(wxNO_BORDER
) ) 
 243     return wxSize(wText
, hText
); 
 246 bool wxTextCtrl::GetStyle(long position
, wxTextAttr
& style
) 
 248     return GetTextPeer()->GetStyle(position
, style
); 
 251 void wxTextCtrl::MarkDirty() 
 256 void wxTextCtrl::DiscardEdits() 
 261 int wxTextCtrl::GetNumberOfLines() const 
 263     return GetTextPeer()->GetNumberOfLines() ; 
 266 long wxTextCtrl::XYToPosition(long x
, long y
) const 
 268     return GetTextPeer()->XYToPosition( x 
, y 
) ; 
 271 bool wxTextCtrl::PositionToXY(long pos
, long *x
, long *y
) const 
 273     return GetTextPeer()->PositionToXY( pos 
, x 
, y 
) ; 
 276 void wxTextCtrl::ShowPosition(long pos
) 
 278     return GetTextPeer()->ShowPosition(pos
) ; 
 281 int wxTextCtrl::GetLineLength(long lineNo
) const 
 283     return GetTextPeer()->GetLineLength(lineNo
) ; 
 286 wxString 
wxTextCtrl::GetLineText(long lineNo
) const 
 288     return GetTextPeer()->GetLineText(lineNo
) ; 
 291 void wxTextCtrl::Copy() 
 295         wxClipboardTextEvent 
evt(wxEVT_COMMAND_TEXT_COPY
, GetId());         
 296         evt
.SetEventObject(this); 
 297         if (!GetEventHandler()->ProcessEvent(evt
)) 
 304 void wxTextCtrl::Cut() 
 308         wxClipboardTextEvent 
evt(wxEVT_COMMAND_TEXT_CUT
, GetId());         
 309         evt
.SetEventObject(this); 
 310         if (!GetEventHandler()->ProcessEvent(evt
)) 
 314             SendTextUpdatedEvent(); 
 319 void wxTextCtrl::Paste() 
 323         wxClipboardTextEvent 
evt(wxEVT_COMMAND_TEXT_PASTE
, GetId());         
 324         evt
.SetEventObject(this); 
 325         if (!GetEventHandler()->ProcessEvent(evt
)) 
 327             wxTextEntry::Paste(); 
 329             // TODO: eventually we should add setting the default style again 
 330             SendTextUpdatedEvent(); 
 335 void wxTextCtrl::OnDropFiles(wxDropFilesEvent
& event
) 
 337     // By default, load the first file into the text window. 
 338     if (event
.GetNumberOfFiles() > 0) 
 339         LoadFile( event
.GetFiles()[0] ); 
 342 void wxTextCtrl::OnKeyDown(wxKeyEvent
& event
) 
 344     if ( event
.GetModifiers() == wxMOD_CMD 
) 
 346         switch( event
.GetKeyCode() ) 
 367     // no, we didn't process it 
 371 void wxTextCtrl::OnChar(wxKeyEvent
& event
) 
 373     int key 
= event
.GetKeyCode() ; 
 374     bool eat_key 
= false ; 
 377     if ( !IsEditable() && !event
.IsKeyInCategory(WXK_CATEGORY_ARROW 
| WXK_CATEGORY_TAB
) && 
 378         !( key 
== WXK_RETURN 
&& ( (m_windowStyle 
& wxTE_PROCESS_ENTER
) || (m_windowStyle 
& wxTE_MULTILINE
) ) ) 
 379 //        && key != WXK_PAGEUP && key != WXK_PAGEDOWN && key != WXK_HOME && key != WXK_END 
 386     // Check if we have reached the max # of chars (if it is set), but still 
 387     // allow navigation and deletion 
 388     GetSelection( &from
, &to 
); 
 389     if ( !IsMultiLine() && m_maxLength 
&& GetValue().length() >= m_maxLength 
&& 
 390         !event
.IsKeyInCategory(WXK_CATEGORY_ARROW 
| WXK_CATEGORY_TAB 
| WXK_CATEGORY_CUT
) && 
 391         !( key 
== WXK_RETURN 
&& (m_windowStyle 
& wxTE_PROCESS_ENTER
) ) && 
 394         // eat it, we don't want to add more than allowed # of characters 
 396         // TODO: generate EVT_TEXT_MAXLEN() 
 400     // assume that any key not processed yet is going to modify the control 
 406             if (m_windowStyle 
& wxTE_PROCESS_ENTER
) 
 408                 wxCommandEvent 
event(wxEVT_COMMAND_TEXT_ENTER
, m_windowId
); 
 409                 event
.SetEventObject( this ); 
 410                 event
.SetString( GetValue() ); 
 411                 if ( HandleWindowEvent(event
) ) 
 415             if ( !(m_windowStyle 
& wxTE_MULTILINE
) ) 
 417                 wxTopLevelWindow 
*tlw 
= wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow
); 
 418                 if ( tlw 
&& tlw
->GetDefaultItem() ) 
 420                     wxButton 
*def 
= wxDynamicCast(tlw
->GetDefaultItem(), wxButton
); 
 421                     if ( def 
&& def
->IsEnabled() ) 
 423                         wxCommandEvent 
event(wxEVT_COMMAND_BUTTON_CLICKED
, def
->GetId() ); 
 424                         event
.SetEventObject(def
); 
 431                 // this will make wxWidgets eat the ENTER key so that 
 432                 // we actually prevent line wrapping in a single line text control 
 438             if ( !(m_windowStyle 
& wxTE_PROCESS_TAB
)) 
 441                 if (!event
.ShiftDown()) 
 442                     flags 
|= wxNavigationKeyEvent::IsForward 
; 
 443                 if (event
.ControlDown()) 
 444                     flags 
|= wxNavigationKeyEvent::WinChange 
; 
 451                 // This is necessary (don't know why); 
 452                 // otherwise the tab will not be inserted. 
 453                 WriteText(wxT("\t")); 
 464         // perform keystroke handling 
 468     // osx_cocoa sends its event upon insertText 
 470     if ( ( key 
>= 0x20 && key 
< WXK_START 
) || 
 471          ( key 
>= WXK_NUMPAD0 
&& key 
<= WXK_DIVIDE 
) || 
 476         wxCommandEvent 
event1(wxEVT_COMMAND_TEXT_UPDATED
, m_windowId
); 
 477         event1
.SetEventObject( this ); 
 478         wxPostEvent( GetEventHandler(), event1 
); 
 483 void wxTextCtrl::Command(wxCommandEvent 
& event
) 
 485     SetValue(event
.GetString()); 
 486     ProcessCommand(event
); 
 489 // ---------------------------------------------------------------------------- 
 490 // standard handlers for standard edit menu events 
 491 // ---------------------------------------------------------------------------- 
 493 // CS: Context Menus only work with MLTE implementations or non-multiline HIViews at the moment 
 495 void wxTextCtrl::OnCut(wxCommandEvent
& WXUNUSED(event
)) 
 500 void wxTextCtrl::OnCopy(wxCommandEvent
& WXUNUSED(event
)) 
 505 void wxTextCtrl::OnPaste(wxCommandEvent
& WXUNUSED(event
)) 
 510 void wxTextCtrl::OnUndo(wxCommandEvent
& WXUNUSED(event
)) 
 515 void wxTextCtrl::OnRedo(wxCommandEvent
& WXUNUSED(event
)) 
 520 void wxTextCtrl::OnDelete(wxCommandEvent
& WXUNUSED(event
)) 
 524     GetSelection( &from
, &to 
); 
 525     if (from 
!= -1 && to 
!= -1) 
 529 void wxTextCtrl::OnSelectAll(wxCommandEvent
& WXUNUSED(event
)) 
 531     SetSelection(-1, -1); 
 534 void wxTextCtrl::OnUpdateCut(wxUpdateUIEvent
& event
) 
 536     event
.Enable( CanCut() ); 
 539 void wxTextCtrl::OnUpdateCopy(wxUpdateUIEvent
& event
) 
 541     event
.Enable( CanCopy() ); 
 544 void wxTextCtrl::OnUpdatePaste(wxUpdateUIEvent
& event
) 
 546     event
.Enable( CanPaste() ); 
 549 void wxTextCtrl::OnUpdateUndo(wxUpdateUIEvent
& event
) 
 551     event
.Enable( CanUndo() ); 
 554 void wxTextCtrl::OnUpdateRedo(wxUpdateUIEvent
& event
) 
 556     event
.Enable( CanRedo() ); 
 559 void wxTextCtrl::OnUpdateDelete(wxUpdateUIEvent
& event
) 
 563     GetSelection( &from
, &to 
); 
 564     event
.Enable( from 
!= -1 && to 
!= -1 && from 
!= to 
&& IsEditable() ) ; 
 567 void wxTextCtrl::OnUpdateSelectAll(wxUpdateUIEvent
& event
) 
 569     event
.Enable(GetLastPosition() > 0); 
 572 void wxTextCtrl::OnContextMenu(wxContextMenuEvent
& event
) 
 574     if ( GetTextPeer()->HasOwnContextMenu() ) 
 581     if (m_privateContextMenu 
== NULL
) 
 583         m_privateContextMenu 
= new wxMenu
; 
 584         m_privateContextMenu
->Append(wxID_UNDO
, _("&Undo")); 
 585         m_privateContextMenu
->Append(wxID_REDO
, _("&Redo")); 
 586         m_privateContextMenu
->AppendSeparator(); 
 587         m_privateContextMenu
->Append(wxID_CUT
, _("Cu&t")); 
 588         m_privateContextMenu
->Append(wxID_COPY
, _("&Copy")); 
 589         m_privateContextMenu
->Append(wxID_PASTE
, _("&Paste")); 
 590         m_privateContextMenu
->Append(wxID_CLEAR
, _("&Delete")); 
 591         m_privateContextMenu
->AppendSeparator(); 
 592         m_privateContextMenu
->Append(wxID_SELECTALL
, _("Select &All")); 
 595     PopupMenu(m_privateContextMenu
); 
 599 bool wxTextCtrl::MacSetupCursor( const wxPoint
& pt 
) 
 601     if ( !GetTextPeer()->SetupCursor( pt 
) ) 
 602         return wxWindow::MacSetupCursor( pt 
) ; 
 607 // ---------------------------------------------------------------------------- 
 608 // implementation base class 
 609 // ---------------------------------------------------------------------------- 
 611 bool wxTextWidgetImpl::GetStyle(long WXUNUSED(position
), 
 612                                 wxTextAttr
& WXUNUSED(style
)) 
 617 void wxTextWidgetImpl::SetStyle(long WXUNUSED(start
), 
 619                                 const wxTextAttr
& WXUNUSED(style
)) 
 623 void wxTextWidgetImpl::Copy() 
 627 void wxTextWidgetImpl::Cut() 
 631 void wxTextWidgetImpl::Paste() 
 635 bool wxTextWidgetImpl::CanPaste() const 
 640 void wxTextWidgetImpl::SetEditable(bool WXUNUSED(editable
)) 
 644 long wxTextWidgetImpl::GetLastPosition() const 
 646     return GetStringValue().length() ; 
 649 void wxTextWidgetImpl::Replace( long from 
, long to 
, const wxString 
&val 
) 
 651     SetSelection( from 
, to 
) ; 
 655 void wxTextWidgetImpl::Remove( long from 
, long to 
) 
 657     SetSelection( from 
, to 
) ; 
 658     WriteText( wxEmptyString
) ; 
 661 void wxTextWidgetImpl::Clear() 
 663     SetStringValue( wxEmptyString 
) ; 
 666 bool wxTextWidgetImpl::CanUndo() const 
 671 void wxTextWidgetImpl::Undo() 
 675 bool wxTextWidgetImpl::CanRedo()  const 
 680 void wxTextWidgetImpl::Redo() 
 684 long wxTextWidgetImpl::XYToPosition(long WXUNUSED(x
), long WXUNUSED(y
)) const 
 689 bool wxTextWidgetImpl::PositionToXY(long WXUNUSED(pos
), 
 691                                     long *WXUNUSED(y
)) const 
 696 void wxTextWidgetImpl::ShowPosition( long WXUNUSED(pos
) ) 
 700 int wxTextWidgetImpl::GetNumberOfLines() const 
 702     wxString content 
= GetStringValue() ; 
 705     for (size_t i 
= 0; i 
< content
.length() ; i
++) 
 708         if (content
[i
] == '\n') 
 710         if (content
[i
] == '\r') 
 718 wxString 
wxTextWidgetImpl::GetLineText(long lineNo
) const 
 720     // TODO: change this if possible to reflect real lines 
 721     wxString content 
= GetStringValue() ; 
 725     for (size_t i 
= 0; i 
< content
.length() ; i
++) 
 729             // Add chars in line then 
 732             for (size_t j 
= i
; j 
< content
.length(); j
++) 
 734                 if (content
[j
] == '\n') 
 743         if (content
[i
] == '\n') 
 747     return wxEmptyString 
; 
 750 int wxTextWidgetImpl::GetLineLength(long lineNo
) const 
 752     // TODO: change this if possible to reflect real lines 
 753     wxString content 
= GetStringValue() ; 
 757     for (size_t i 
= 0; i 
< content
.length() ; i
++) 
 761             // Count chars in line then 
 763             for (size_t j 
= i
; j 
< content
.length(); j
++) 
 766                 if (content
[j
] == '\n') 
 773         if (content
[i
] == '\n') 
 780 #endif // wxUSE_TEXTCTRL