textctrl.cpp
来自「A*算法 A*算法 A*算法 A*算法A*算法A*算法」· C++ 代码 · 共 2,131 行 · 第 1/5 页
CPP
2,131 行
wxArrayInt m_rowsWidth;
// and also its starting row (0 for the first line, first lines'
// m_rowsStart.GetCount() + 1 for the second &c): it is set to -1 initially
// and this means that the struct hadn't yet been initialized
wxTextCoord m_rowFirst;
// the last modification "time"-stamp used by LayoutLines()
size_t m_timestamp;
};
WX_DECLARE_OBJARRAY(wxWrappedLineData, wxArrayWrappedLinesData);
#include "wx/arrimpl.cpp"
WX_DEFINE_OBJARRAY(wxArrayWrappedLinesData);
struct WXDLLEXPORT wxTextWrappedData : public wxTextMultiLineData
{
// the width of the column to the right of the text rect used for the
// indicator mark display for the wrapped lines
wxCoord m_widthMark;
// the data for each line
wxArrayWrappedLinesData m_linesData;
// flag telling us to recalculate all starting rows starting from this line
// (if it is -1, we don't have to recalculate anything) - it is set when
// the number of the rows in the middle of the control changes
wxTextCoord m_rowFirstInvalid;
// the current timestamp used by LayoutLines()
size_t m_timestamp;
// invalidate starting rows of all lines (NOT rows!) after this one
void InvalidateLinesBelow(wxTextCoord line)
{
if ( m_rowFirstInvalid == -1 || m_rowFirstInvalid > line )
{
m_rowFirstInvalid = line;
}
}
// check if this line is valid: i.e. before the first invalid one
bool IsValidLine(wxTextCoord line) const
{
return ((m_rowFirstInvalid == -1) || (line < m_rowFirstInvalid)) &&
m_linesData[line].IsValid();
}
// def ctor
wxTextWrappedData()
{
m_widthMark = 0;
m_rowFirstInvalid = -1;
m_timestamp = 0;
}
};
// ----------------------------------------------------------------------------
// private classes for undo/redo management
// ----------------------------------------------------------------------------
/*
We use custom versions of wxWidgets command processor to implement undo/redo
as we want to avoid storing the backpointer to wxTextCtrl in wxCommand
itself: this is a waste of memory as all commands in the given command
processor always have the same associated wxTextCtrl and so it makes sense
to store the backpointer there.
As for the rest of the implementation, it's fairly standard: we have 2
command classes corresponding to adding and removing text.
*/
// a command corresponding to a wxTextCtrl action
class wxTextCtrlCommand : public wxCommand
{
public:
wxTextCtrlCommand(const wxString& name) : wxCommand(true, name) { }
// we don't use these methods as they don't make sense for us as we need a
// wxTextCtrl to be applied
virtual bool Do() { wxFAIL_MSG(_T("shouldn't be called")); return false; }
virtual bool Undo() { wxFAIL_MSG(_T("shouldn't be called")); return false; }
// instead, our command processor uses these methods
virtual bool Do(wxTextCtrl *text) = 0;
virtual bool Undo(wxTextCtrl *text) = 0;
};
// insert text command
class wxTextCtrlInsertCommand : public wxTextCtrlCommand
{
public:
wxTextCtrlInsertCommand(const wxString& textToInsert)
: wxTextCtrlCommand(wxTEXT_COMMAND_INSERT), m_text(textToInsert)
{
m_from = -1;
}
// combine the 2 commands together
void Append(wxTextCtrlInsertCommand *other);
virtual bool CanUndo() const;
virtual bool Do(wxTextCtrl *text);
virtual bool Do() { return wxTextCtrlCommand::Do(); }
virtual bool Undo() { return wxTextCtrlCommand::Undo(); }
virtual bool Undo(wxTextCtrl *text);
private:
// the text we insert
wxString m_text;
// the position where we inserted the text
wxTextPos m_from;
};
// remove text command
class wxTextCtrlRemoveCommand : public wxTextCtrlCommand
{
public:
wxTextCtrlRemoveCommand(wxTextPos from, wxTextPos to)
: wxTextCtrlCommand(wxTEXT_COMMAND_REMOVE)
{
m_from = from;
m_to = to;
}
virtual bool CanUndo() const;
virtual bool Do(wxTextCtrl *text);
virtual bool Do() { return wxTextCtrlCommand::Do(); }
virtual bool Undo() { return wxTextCtrlCommand::Undo(); }
virtual bool Undo(wxTextCtrl *text);
private:
// the range of text to delete
wxTextPos m_from,
m_to;
// the text which was deleted when this command was Do()ne
wxString m_textDeleted;
};
// a command processor for a wxTextCtrl
class wxTextCtrlCommandProcessor : public wxCommandProcessor
{
public:
wxTextCtrlCommandProcessor(wxTextCtrl *text)
{
m_compressInserts = false;
m_text = text;
}
// override Store() to compress multiple wxTextCtrlInsertCommand into one
virtual void Store(wxCommand *command);
// stop compressing insert commands when this is called
void StopCompressing() { m_compressInserts = false; }
// accessors
wxTextCtrl *GetTextCtrl() const { return m_text; }
bool IsCompressing() const { return m_compressInserts; }
protected:
virtual bool DoCommand(wxCommand& cmd)
{ return ((wxTextCtrlCommand &)cmd).Do(m_text); }
virtual bool UndoCommand(wxCommand& cmd)
{ return ((wxTextCtrlCommand &)cmd).Undo(m_text); }
// check if this command is a wxTextCtrlInsertCommand and return it casted
// to the right type if it is or NULL otherwise
wxTextCtrlInsertCommand *IsInsertCommand(wxCommand *cmd);
private:
// the control we're associated with
wxTextCtrl *m_text;
// if the flag is true we're compressing subsequent insert commands into
// one so that the entire typing could be undone in one call to Undo()
bool m_compressInserts;
};
// ============================================================================
// implementation
// ============================================================================
BEGIN_EVENT_TABLE(wxTextCtrl, wxControl)
EVT_CHAR(wxTextCtrl::OnChar)
EVT_SIZE(wxTextCtrl::OnSize)
END_EVENT_TABLE()
IMPLEMENT_DYNAMIC_CLASS(wxTextCtrl, wxControl)
// ----------------------------------------------------------------------------
// creation
// ----------------------------------------------------------------------------
void wxTextCtrl::Init()
{
m_selAnchor =
m_selStart =
m_selEnd = -1;
m_isModified = false;
m_isEditable = true;
m_posLast =
m_curPos =
m_curCol =
m_curRow = 0;
m_heightLine =
m_widthAvg = -1;
// init wxScrollHelper
SetWindow(this);
// init the undo manager
m_cmdProcessor = new wxTextCtrlCommandProcessor(this);
// no data yet
m_data.data = NULL;
}
bool wxTextCtrl::Create(wxWindow *parent,
wxWindowID id,
const wxString& value,
const wxPoint& pos,
const wxSize& size,
long style,
const wxValidator& validator,
const wxString &name)
{
if ( style & wxTE_MULTILINE )
{
// for compatibility with wxMSW we create the controls with vertical
// scrollbar always shown unless they have wxTE_RICH style (because
// Windows text controls always has vert scrollbar but richedit one
// doesn't)
if ( !(style & wxTE_RICH) )
{
style |= wxALWAYS_SHOW_SB;
}
// wrapping style: wxTE_DONTWRAP == wxHSCROLL so if it's _not_ given,
// we won't have horizontal scrollbar automatically, no need to do
// anything
// TODO: support wxTE_NO_VSCROLL (?)
// create data object for normal multiline or for controls with line
// wrap as needed
if ( style & wxHSCROLL )
m_data.mdata = new wxTextMultiLineData;
else
m_data.wdata = new wxTextWrappedData;
}
else
{
// this doesn't make sense for single line controls
style &= ~wxHSCROLL;
// create data object for single line controls
m_data.sdata = new wxTextSingleLineData;
}
#if wxUSE_TWO_WINDOWS
if ((style & wxBORDER_MASK) == 0)
style |= wxBORDER_SUNKEN;
#endif
if ( !wxControl::Create(parent, id, pos, size, style,
validator, name) )
{
return false;
}
SetCursor(wxCURSOR_IBEAM);
if ( style & wxTE_MULTILINE )
{
// we should always have at least one line in a multiline control
MData().m_lines.Add(wxEmptyString);
if ( !(style & wxHSCROLL) )
{
WData().m_linesData.Add(new wxWrappedLineData);
WData().InvalidateLinesBelow(0);
}
// we might support it but it's quite useless and other ports don't
// support it anyhow
wxASSERT_MSG( !(style & wxTE_PASSWORD),
_T("wxTE_PASSWORD can't be used with multiline ctrls") );
}
RecalcFontMetrics();
SetValue(value);
SetBestSize(size);
m_isEditable = !(style & wxTE_READONLY);
CreateCaret();
InitInsertionPoint();
// we can't show caret right now as we're not shown yet and so it would
// result in garbage on the screen - we'll do it after first OnPaint()
m_hasCaret = false;
CreateInputHandler(wxINP_HANDLER_TEXTCTRL);
wxSizeEvent sizeEvent(GetSize(), GetId());
GetEventHandler()->ProcessEvent(sizeEvent);
return true;
}
wxTextCtrl::~wxTextCtrl()
{
delete m_cmdProcessor;
if ( m_data.data )
{
if ( IsSingleLine() )
delete m_data.sdata;
else if ( WrapLines() )
delete m_data.wdata;
else
delete m_data.mdata;
}
}
// ----------------------------------------------------------------------------
// set/get the value
// ----------------------------------------------------------------------------
void wxTextCtrl::SetValue(const wxString& value)
{
if ( IsSingleLine() && (value == GetValue()) )
{
// nothing changed
return;
}
Replace(0, GetLastPosition(), value);
if ( IsSingleLine() )
{
SetInsertionPoint(0);
}
// TODO: should we generate the event or not, finally?
}
const wxArrayString& wxTextCtrl::GetLines() const
{
return MData().m_lines;
}
size_t wxTextCtrl::GetLineCount() const
{
return MData().m_lines.GetCount();
}
wxString wxTextCtrl::GetValue() const
{
// for multiline controls we don't always store the total value but only
// recompute it when asked - and to invalidate it we just empty it in
// Replace()
if ( !IsSingleLine() && m_value.empty() )
{
// recalculate: note that we always do it for empty multilien control,
// but then it's so quick that it's not important
// the first line is special as there is no \n before it, so it's
// outside the loop
const wxArrayString& lines = GetLines();
wxTextCtrl *self = wxConstCast(this, wxTextCtrl);
self->m_value << lines[0u];
size_t count = lines.GetCount();
for ( size_t n = 1; n < count; n++ )
{
self->m_value << _T('\n') << lines[n];
}
}
return m_value;
}
void wxTextCtrl::Clear()
{
SetValue(wxEmptyString);
}
bool wxTextCtrl::ReplaceLine(wxTextCoord line,
const wxString& text)
{
if ( WrapLines() )
{
// first, we have to relayout the line entirely
//
// OPT: we might try not to recalc the unchanged part of line
wxWrappedLineData& lineData = WData().m_linesData[line];
// if we had some number of rows before, use this number, otherwise
// just make sure that the test below (rowsNew != rowsOld) will be true
int rowsOld;
if ( lineData.IsValid() )
{
rowsOld = lineData.GetExtraRowCount();
}
else // line wasn't laid out yet
{
// assume it changed entirely as we can't do anything better
rowsOld = -1;
}
// now change the line
MData().m_lines[line] = text;
// OPT: we choose to lay it our immediately instead of delaying it
// until it is needed because it allows us to avoid invalidating
// lines further down if the number of rows didn't chnage, but
// maybe we can imporve this even further?
LayoutLine(line, lineData);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?