Files
wxWidgets/src/osx/textctrl_osx.cpp
Vadim Zeitlin 005ac806ea Remove duplicated GetTextPeer() definitions from derived classes.
GetTextPeer() may be implemented directly in wxTextEntry under OS X, there is
no need to define it (identically) in wxTextCtrl and wxComboBox.

Also just call this method instead of duplicating its code in wxOSX
wxTextCtrl.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@63878 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2010-04-06 18:46:14 +00:00

802 lines
18 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: src/osx/textctrl_osx.cpp
// Purpose: wxTextCtrl
// Author: Stefan Csomor
// Modified by: Ryan Norton (MLTE GetLineLength and GetLineText)
// Created: 1998-01-01
// RCS-ID: $Id: textctrl.cpp 54820 2008-07-29 20:04:11Z SC $
// Copyright: (c) Stefan Csomor
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
#include "wx/wxprec.h"
#if wxUSE_TEXTCTRL
#include "wx/textctrl.h"
#ifndef WX_PRECOMP
#include "wx/intl.h"
#include "wx/app.h"
#include "wx/utils.h"
#include "wx/dc.h"
#include "wx/button.h"
#include "wx/menu.h"
#include "wx/settings.h"
#include "wx/msgdlg.h"
#include "wx/toplevel.h"
#endif
#ifdef __DARWIN__
#include <sys/types.h>
#include <sys/stat.h>
#else
#include <stat.h>
#endif
#if wxUSE_STD_IOSTREAM
#if wxUSE_IOSTREAMH
#include <fstream.h>
#else
#include <fstream>
#endif
#endif
#include "wx/filefn.h"
#include "wx/sysopt.h"
#include "wx/thread.h"
#include "wx/osx/private.h"
IMPLEMENT_DYNAMIC_CLASS(wxTextCtrl, wxTextCtrlBase)
BEGIN_EVENT_TABLE(wxTextCtrl, wxTextCtrlBase)
EVT_DROP_FILES(wxTextCtrl::OnDropFiles)
EVT_CHAR(wxTextCtrl::OnChar)
EVT_KEY_DOWN(wxTextCtrl::OnKeyDown)
EVT_MENU(wxID_CUT, wxTextCtrl::OnCut)
EVT_MENU(wxID_COPY, wxTextCtrl::OnCopy)
EVT_MENU(wxID_PASTE, wxTextCtrl::OnPaste)
EVT_MENU(wxID_UNDO, wxTextCtrl::OnUndo)
EVT_MENU(wxID_REDO, wxTextCtrl::OnRedo)
EVT_MENU(wxID_CLEAR, wxTextCtrl::OnDelete)
EVT_MENU(wxID_SELECTALL, wxTextCtrl::OnSelectAll)
EVT_CONTEXT_MENU(wxTextCtrl::OnContextMenu)
EVT_UPDATE_UI(wxID_CUT, wxTextCtrl::OnUpdateCut)
EVT_UPDATE_UI(wxID_COPY, wxTextCtrl::OnUpdateCopy)
EVT_UPDATE_UI(wxID_PASTE, wxTextCtrl::OnUpdatePaste)
EVT_UPDATE_UI(wxID_UNDO, wxTextCtrl::OnUpdateUndo)
EVT_UPDATE_UI(wxID_REDO, wxTextCtrl::OnUpdateRedo)
EVT_UPDATE_UI(wxID_CLEAR, wxTextCtrl::OnUpdateDelete)
EVT_UPDATE_UI(wxID_SELECTALL, wxTextCtrl::OnUpdateSelectAll)
END_EVENT_TABLE()
void wxTextCtrl::Init()
{
m_dirty = false;
m_privateContextMenu = NULL;
m_triggerUpdateEvents = true ;
}
wxTextCtrl::~wxTextCtrl()
{
#if wxUSE_MENUS
delete m_privateContextMenu;
#endif
}
bool wxTextCtrl::Create( wxWindow *parent,
wxWindowID id,
const wxString& str,
const wxPoint& pos,
const wxSize& size,
long style,
const wxValidator& validator,
const wxString& name )
{
m_macIsUserPane = false ;
m_editable = true ;
if ( ! (style & wxNO_BORDER) )
style = (style & ~wxBORDER_MASK) | wxSUNKEN_BORDER ;
if ( !wxTextCtrlBase::Create( parent, id, pos, size, style & ~(wxHSCROLL | wxVSCROLL), validator, name ) )
return false;
if ( m_windowStyle & wxTE_MULTILINE )
{
// always turn on this style for multi-line controls
m_windowStyle |= wxTE_PROCESS_ENTER;
style |= wxTE_PROCESS_ENTER ;
}
m_peer = wxWidgetImpl::CreateTextControl( this, GetParent(), GetId(), str, pos, size, style, GetExtraStyle() );
MacPostControlCreate(pos, size) ;
#if wxOSX_USE_COCOA
// under carbon everything can already be set before the MacPostControlCreate embedding takes place
// but under cocoa for single line textfields this only works after everything has been set up
GetTextPeer()->SetStringValue(str);
#endif
// only now the embedding is correct and we can do a positioning update
MacSuperChangedPosition() ;
if ( m_windowStyle & wxTE_READONLY)
SetEditable( false ) ;
SetCursor( wxCursor( wxCURSOR_IBEAM ) ) ;
return true;
}
void wxTextCtrl::MacSuperChangedPosition()
{
wxWindow::MacSuperChangedPosition() ;
#if wxOSX_USE_CARBON
GetPeer()->SuperChangedPosition() ;
#endif
}
void wxTextCtrl::MacVisibilityChanged()
{
#if wxOSX_USE_CARBON
GetPeer()->VisibilityChanged( GetPeer()->IsVisible() );
#endif
}
void wxTextCtrl::MacCheckSpelling(bool check)
{
GetTextPeer()->CheckSpelling(check);
}
void wxTextCtrl::SetMaxLength(unsigned long len)
{
m_maxLength = len ;
}
bool wxTextCtrl::SetFont( const wxFont& font )
{
if ( !wxTextCtrlBase::SetFont( font ) )
return false ;
GetPeer()->SetFont( font , GetForegroundColour() , GetWindowStyle(), false /* dont ignore black */ ) ;
return true ;
}
bool wxTextCtrl::SetStyle(long start, long end, const wxTextAttr& style)
{
if (GetTextPeer())
GetTextPeer()->SetStyle( start , end , style ) ;
return true ;
}
bool wxTextCtrl::SetDefaultStyle(const wxTextAttr& style)
{
wxTextCtrlBase::SetDefaultStyle( style ) ;
SetStyle( -1 /*current selection*/ , -1 /*current selection*/ , GetDefaultStyle() ) ;
return true ;
}
bool wxTextCtrl::IsModified() const
{
return m_dirty;
}
bool wxTextCtrl::AcceptsFocus() const
{
// we don't want focus if we can't be edited
return /*IsEditable() && */ wxControl::AcceptsFocus();
}
wxSize wxTextCtrl::DoGetBestSize() const
{
if (GetTextPeer())
{
wxSize size = GetTextPeer()->GetBestSize();
if (size.x > 0 && size.y > 0)
return size;
}
int wText, hText;
// these are the numbers from the HIG:
// we reduce them by the borders first
wText = 100 ;
switch ( m_windowVariant )
{
case wxWINDOW_VARIANT_NORMAL :
hText = 22 - 6 ;
break ;
case wxWINDOW_VARIANT_SMALL :
hText = 19 - 6 ;
break ;
case wxWINDOW_VARIANT_MINI :
hText = 15 - 6 ;
break ;
default :
hText = 22 - 6;
break ;
}
// as the above numbers have some free space around the text
// we get 5 lines like this anyway
if ( m_windowStyle & wxTE_MULTILINE )
hText *= 5 ;
if ( !HasFlag(wxNO_BORDER) )
hText += 6 ;
return wxSize(wText, hText);
}
bool wxTextCtrl::GetStyle(long position, wxTextAttr& style)
{
return GetTextPeer()->GetStyle(position, style);
}
void wxTextCtrl::MarkDirty()
{
m_dirty = true;
}
void wxTextCtrl::DiscardEdits()
{
m_dirty = false;
}
int wxTextCtrl::GetNumberOfLines() const
{
return GetTextPeer()->GetNumberOfLines() ;
}
long wxTextCtrl::XYToPosition(long x, long y) const
{
return GetTextPeer()->XYToPosition( x , y ) ;
}
bool wxTextCtrl::PositionToXY(long pos, long *x, long *y) const
{
return GetTextPeer()->PositionToXY( pos , x , y ) ;
}
void wxTextCtrl::ShowPosition(long pos)
{
return GetTextPeer()->ShowPosition(pos) ;
}
int wxTextCtrl::GetLineLength(long lineNo) const
{
return GetTextPeer()->GetLineLength(lineNo) ;
}
wxString wxTextCtrl::GetLineText(long lineNo) const
{
return GetTextPeer()->GetLineText(lineNo) ;
}
void wxTextCtrl::Remove(long from, long to)
{
wxTextEntry::Remove(from, to);
if ( m_triggerUpdateEvents )
SendTextUpdatedEvent();
}
void wxTextCtrl::WriteText(const wxString& str)
{
wxTextEntry::WriteText( str ) ;
if ( m_triggerUpdateEvents )
SendTextUpdatedEvent();
}
void wxTextCtrl::Clear()
{
wxTextEntry::Clear() ;
SendTextUpdatedEvent();
}
void wxTextCtrl::Copy()
{
if (CanCopy())
{
wxClipboardTextEvent evt(wxEVT_COMMAND_TEXT_COPY, GetId());
evt.SetEventObject(this);
if (!GetEventHandler()->ProcessEvent(evt))
{
wxTextEntry::Copy();
}
}
}
void wxTextCtrl::Cut()
{
if (CanCut())
{
wxClipboardTextEvent evt(wxEVT_COMMAND_TEXT_CUT, GetId());
evt.SetEventObject(this);
if (!GetEventHandler()->ProcessEvent(evt))
{
wxTextEntry::Cut();
SendTextUpdatedEvent();
}
}
}
void wxTextCtrl::Paste()
{
if (CanPaste())
{
wxClipboardTextEvent evt(wxEVT_COMMAND_TEXT_PASTE, GetId());
evt.SetEventObject(this);
if (!GetEventHandler()->ProcessEvent(evt))
{
wxTextEntry::Paste();
// TODO: eventually we should add setting the default style again
SendTextUpdatedEvent();
}
}
}
void wxTextCtrl::OnDropFiles(wxDropFilesEvent& event)
{
// By default, load the first file into the text window.
if (event.GetNumberOfFiles() > 0)
LoadFile( event.GetFiles()[0] );
}
void wxTextCtrl::OnKeyDown(wxKeyEvent& event)
{
if ( event.GetModifiers() == wxMOD_CMD )
{
switch( event.GetKeyCode() )
{
case 'A':
SelectAll();
return;
case 'C':
if ( CanCopy() )
Copy() ;
return;
case 'V':
if ( CanPaste() )
Paste() ;
return;
case 'X':
if ( CanCut() )
Cut() ;
return;
default:
break;
}
}
// no, we didn't process it
event.Skip();
}
void wxTextCtrl::OnChar(wxKeyEvent& event)
{
int key = event.GetKeyCode() ;
bool eat_key = false ;
long from, to;
if ( !IsEditable() && !event.IsKeyInCategory(WXK_CATEGORY_ARROW | WXK_CATEGORY_TAB) &&
!( key == WXK_RETURN && ( (m_windowStyle & wxTE_PROCESS_ENTER) || (m_windowStyle & wxTE_MULTILINE) ) )
// && key != WXK_PAGEUP && key != WXK_PAGEDOWN && key != WXK_HOME && key != WXK_END
)
{
// eat it
return ;
}
// Check if we have reached the max # of chars (if it is set), but still
// allow navigation and deletion
GetSelection( &from, &to );
if ( !IsMultiLine() && m_maxLength && GetValue().length() >= m_maxLength &&
!event.IsKeyInCategory(WXK_CATEGORY_ARROW | WXK_CATEGORY_TAB | WXK_CATEGORY_CUT) &&
!( key == WXK_RETURN && (m_windowStyle & wxTE_PROCESS_ENTER) ) &&
from == to )
{
// eat it, we don't want to add more than allowed # of characters
// TODO: generate EVT_TEXT_MAXLEN()
return;
}
// assume that any key not processed yet is going to modify the control
m_dirty = true;
switch ( key )
{
case WXK_RETURN:
if (m_windowStyle & wxTE_PROCESS_ENTER)
{
wxCommandEvent event(wxEVT_COMMAND_TEXT_ENTER, m_windowId);
event.SetEventObject( this );
event.SetString( GetValue() );
if ( HandleWindowEvent(event) )
return;
}
if ( !(m_windowStyle & wxTE_MULTILINE) )
{
wxTopLevelWindow *tlw = wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow);
if ( tlw && tlw->GetDefaultItem() )
{
wxButton *def = wxDynamicCast(tlw->GetDefaultItem(), wxButton);
if ( def && def->IsEnabled() )
{
wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, def->GetId() );
event.SetEventObject(def);
def->Command(event);
return ;
}
}
// this will make wxWidgets eat the ENTER key so that
// we actually prevent line wrapping in a single line text control
eat_key = true;
}
break;
case WXK_TAB:
if ( !(m_windowStyle & wxTE_PROCESS_TAB))
{
int flags = 0;
if (!event.ShiftDown())
flags |= wxNavigationKeyEvent::IsForward ;
if (event.ControlDown())
flags |= wxNavigationKeyEvent::WinChange ;
Navigate(flags);
return;
}
else
{
// This is necessary (don't know why);
// otherwise the tab will not be inserted.
WriteText(wxT("\t"));
eat_key = true;
}
break;
default:
break;
}
if (!eat_key)
{
// perform keystroke handling
event.Skip(true) ;
}
// osx_cocoa sends its event upon insertText
#if wxOSX_USE_CARBON
if ( ( key >= 0x20 && key < WXK_START ) ||
( key >= WXK_NUMPAD0 && key <= WXK_DIVIDE ) ||
key == WXK_RETURN ||
key == WXK_DELETE ||
key == WXK_BACK)
{
wxCommandEvent event1(wxEVT_COMMAND_TEXT_UPDATED, m_windowId);
event1.SetEventObject( this );
wxPostEvent( GetEventHandler(), event1 );
}
#endif
}
void wxTextCtrl::Command(wxCommandEvent & event)
{
SetValue(event.GetString());
ProcessCommand(event);
}
// ----------------------------------------------------------------------------
// standard handlers for standard edit menu events
// ----------------------------------------------------------------------------
// CS: Context Menus only work with MLTE implementations or non-multiline HIViews at the moment
void wxTextCtrl::OnCut(wxCommandEvent& WXUNUSED(event))
{
Cut();
}
void wxTextCtrl::OnCopy(wxCommandEvent& WXUNUSED(event))
{
Copy();
}
void wxTextCtrl::OnPaste(wxCommandEvent& WXUNUSED(event))
{
Paste();
}
void wxTextCtrl::OnUndo(wxCommandEvent& WXUNUSED(event))
{
Undo();
}
void wxTextCtrl::OnRedo(wxCommandEvent& WXUNUSED(event))
{
Redo();
}
void wxTextCtrl::OnDelete(wxCommandEvent& WXUNUSED(event))
{
long from, to;
GetSelection( &from, &to );
if (from != -1 && to != -1)
Remove( from, to );
}
void wxTextCtrl::OnSelectAll(wxCommandEvent& WXUNUSED(event))
{
SetSelection(-1, -1);
}
void wxTextCtrl::OnUpdateCut(wxUpdateUIEvent& event)
{
event.Enable( CanCut() );
}
void wxTextCtrl::OnUpdateCopy(wxUpdateUIEvent& event)
{
event.Enable( CanCopy() );
}
void wxTextCtrl::OnUpdatePaste(wxUpdateUIEvent& event)
{
event.Enable( CanPaste() );
}
void wxTextCtrl::OnUpdateUndo(wxUpdateUIEvent& event)
{
event.Enable( CanUndo() );
}
void wxTextCtrl::OnUpdateRedo(wxUpdateUIEvent& event)
{
event.Enable( CanRedo() );
}
void wxTextCtrl::OnUpdateDelete(wxUpdateUIEvent& event)
{
long from, to;
GetSelection( &from, &to );
event.Enable( from != -1 && to != -1 && from != to && IsEditable() ) ;
}
void wxTextCtrl::OnUpdateSelectAll(wxUpdateUIEvent& event)
{
event.Enable(GetLastPosition() > 0);
}
void wxTextCtrl::OnContextMenu(wxContextMenuEvent& event)
{
if ( GetTextPeer()->HasOwnContextMenu() )
{
event.Skip() ;
return ;
}
#if wxUSE_MENUS
if (m_privateContextMenu == NULL)
{
m_privateContextMenu = new wxMenu;
m_privateContextMenu->Append(wxID_UNDO, _("&Undo"));
m_privateContextMenu->Append(wxID_REDO, _("&Redo"));
m_privateContextMenu->AppendSeparator();
m_privateContextMenu->Append(wxID_CUT, _("Cu&t"));
m_privateContextMenu->Append(wxID_COPY, _("&Copy"));
m_privateContextMenu->Append(wxID_PASTE, _("&Paste"));
m_privateContextMenu->Append(wxID_CLEAR, _("&Delete"));
m_privateContextMenu->AppendSeparator();
m_privateContextMenu->Append(wxID_SELECTALL, _("Select &All"));
}
PopupMenu(m_privateContextMenu);
#endif
}
bool wxTextCtrl::MacSetupCursor( const wxPoint& pt )
{
if ( !GetTextPeer()->SetupCursor( pt ) )
return wxWindow::MacSetupCursor( pt ) ;
else
return true ;
}
// ----------------------------------------------------------------------------
// implementation base class
// ----------------------------------------------------------------------------
bool wxTextWidgetImpl::GetStyle(long WXUNUSED(position),
wxTextAttr& WXUNUSED(style))
{
return false;
}
void wxTextWidgetImpl::SetStyle(long WXUNUSED(start),
long WXUNUSED(end),
const wxTextAttr& WXUNUSED(style))
{
}
void wxTextWidgetImpl::Copy()
{
}
void wxTextWidgetImpl::Cut()
{
}
void wxTextWidgetImpl::Paste()
{
}
bool wxTextWidgetImpl::CanPaste() const
{
return false ;
}
void wxTextWidgetImpl::SetEditable(bool WXUNUSED(editable))
{
}
long wxTextWidgetImpl::GetLastPosition() const
{
return GetStringValue().length() ;
}
void wxTextWidgetImpl::Replace( long from , long to , const wxString &val )
{
SetSelection( from , to ) ;
WriteText( val ) ;
}
void wxTextWidgetImpl::Remove( long from , long to )
{
SetSelection( from , to ) ;
WriteText( wxEmptyString) ;
}
void wxTextWidgetImpl::Clear()
{
SetStringValue( wxEmptyString ) ;
}
bool wxTextWidgetImpl::CanUndo() const
{
return false ;
}
void wxTextWidgetImpl::Undo()
{
}
bool wxTextWidgetImpl::CanRedo() const
{
return false ;
}
void wxTextWidgetImpl::Redo()
{
}
long wxTextWidgetImpl::XYToPosition(long WXUNUSED(x), long WXUNUSED(y)) const
{
return 0 ;
}
bool wxTextWidgetImpl::PositionToXY(long WXUNUSED(pos),
long *WXUNUSED(x),
long *WXUNUSED(y)) const
{
return false ;
}
void wxTextWidgetImpl::ShowPosition( long WXUNUSED(pos) )
{
}
int wxTextWidgetImpl::GetNumberOfLines() const
{
wxString content = GetStringValue() ;
ItemCount lines = 1;
for (size_t i = 0; i < content.length() ; i++)
{
#if wxOSX_USE_COCOA
if (content[i] == '\n')
#else
if (content[i] == '\r')
#endif
lines++;
}
return lines ;
}
wxString wxTextWidgetImpl::GetLineText(long lineNo) const
{
// TODO: change this if possible to reflect real lines
wxString content = GetStringValue() ;
// Find line first
int count = 0;
for (size_t i = 0; i < content.length() ; i++)
{
if (count == lineNo)
{
// Add chars in line then
wxString tmp;
for (size_t j = i; j < content.length(); j++)
{
if (content[j] == '\n')
return tmp;
tmp += content[j];
}
return tmp;
}
if (content[i] == '\n')
count++;
}
return wxEmptyString ;
}
int wxTextWidgetImpl::GetLineLength(long lineNo) const
{
// TODO: change this if possible to reflect real lines
wxString content = GetStringValue() ;
// Find line first
int count = 0;
for (size_t i = 0; i < content.length() ; i++)
{
if (count == lineNo)
{
// Count chars in line then
count = 0;
for (size_t j = i; j < content.length(); j++)
{
count++;
if (content[j] == '\n')
return count;
}
return count;
}
if (content[i] == '\n')
count++;
}
return 0 ;
}
#endif // wxUSE_TEXTCTRL