textctrl.cpp
来自「A*算法 A*算法 A*算法 A*算法A*算法A*算法」· C++ 代码 · 共 2,131 行 · 第 1/5 页
CPP
2,131 行
/////////////////////////////////////////////////////////////////////////////
// Name: univ/textctrl.cpp
// Purpose: wxTextCtrl
// Author: Vadim Zeitlin
// Modified by:
// Created: 15.09.00
// RCS-ID: $Id: textctrl.cpp,v 1.35.2.1 2006/01/17 09:25:34 JS Exp $
// Copyright: (c) 2000 SciTech Software, Inc. (www.scitechsoft.com)
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
/*
TODO
+ 1. update vert scrollbar when any line length changes for WrapLines()
+ 2. cursor movement ("Hello,^" -> "^verse!" on Arrow Down)?
-> maybe save the x position and use it instead of current in handling
DOWN/UP actions (this would make up/down always return the cursor to
the same location)?
3. split file into chunks
+? 4. rewrite Replace() refresh logic to deal with wrapping lines
+? 5. cache info found by GetPartOfWrappedLine() - performance must be horrible
with lots of text
6. backspace refreshes too much (until end of line)
*/
/*
Optimisation hints from PureQuantify:
+1. wxStringTokenize is the slowest part of Replace
2. GetDC/ReleaseDC are very slow, avoid calling them several times
+3. GetCharHeight() should be cached too
4. wxClientDC construction/destruction in HitTestLine is horribly expensive
For line wrapping controls HitTest2 takes 50% of program time. The results
of GetRowsPerLine and GetPartOfWrappedLine *MUST* be cached.
Search for "OPT!" for things which must be optimized.
*/
/*
Some terminology:
Everywhere in this file LINE refers to a logical line of text, and ROW to a
physical line of text on the display. They are the same unless WrapLines()
is true in which case a single LINE may correspond to multiple ROWs.
A text position is an unsigned int (which for reasons of compatibility is
still a long as wxTextPos) from 0 to GetLastPosition() inclusive. The positions
correspond to the gaps between the letters so the position 0 is just
before the first character and the last position is the one beyond the last
character. For an empty text control GetLastPosition() returns 0.
Lines and columns returned/accepted by XYToPosition() and PositionToXY()
start from 0. The y coordinate is a LINE, not a ROW. Columns correspond to
the characters, the first column of a line is the first character in it,
the last one is length(line text). For compatibility, again, lines and
columns are also longs.
When translating lines/column coordinates to/from positions, the line and
column give the character after the given position. Thus, GetLastPosition()
doesn't have any corresponding column.
An example of positions and lines/columns for a control without wrapping
containing the text "Hello, Universe!\nGoodbye"
1 1 1 1 1 1 1
pos: 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6
H e l l o , U n i v e r s e ! line 0
col: 0 1 2 3 4 5 6 7 8 9 1 1 1 1 1 1
0 1 2 3 4 5
pos: 1 1 1 2 2 2 2 2
7 8 9 0 1 2 3 4
G o o d b y e line 1
col: 0 1 2 3 4 5 6
The same example for a control with line wrap assuming "Universe" is too
long to fit on the same line with "Hello,":
pos: 0 1 2 3 4 5
H e l l o , line 0 (row 0)
col: 0 1 2 3 4 5
1 1 1 1 1 1 1
pos: 6 7 8 9 0 1 2 3 4 5 6
U n i v e r s e ! line 0 (row 1)
col: 6 7 8 9 1 1 1 1 1 1
0 1 2 3 4 5
(line 1 == row 2 same as above)
Note that there is still the same number of columns and positions and that
there is no (logical) position at the end of the first ROW. This position
is identified with the preceding one (which is not how Windows does it: it
identifies it with the next one, i.e. the first position of the next line,
but much more logical IMHO).
*/
/*
Search for "OPT" for possible optimizations
A possible global optimization would be to always store the coords in the
text in triplets (pos, col, line) and update them simultaneously instead of
recalculating col and line from pos each time it is needed. Currently we
only do it for the current position but we might also do it for the
selection start and end.
*/
// ============================================================================
// declarations
// ============================================================================
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
#pragma implementation "univtextctrl.h"
#endif
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#if wxUSE_TEXTCTRL
#include <ctype.h>
#ifndef WX_PRECOMP
#include "wx/log.h"
#include "wx/dcclient.h"
#include "wx/validate.h"
#include "wx/textctrl.h"
#endif
#include "wx/clipbrd.h"
#include "wx/textfile.h"
#include "wx/caret.h"
#include "wx/univ/inphand.h"
#include "wx/univ/renderer.h"
#include "wx/univ/colschem.h"
#include "wx/univ/theme.h"
#include "wx/cmdproc.h"
#if wxUSE_CLIPBOARD
#include "wx/dataobj.h"
#endif
// turn extra wxTextCtrl-specific debugging on/off
#define WXDEBUG_TEXT
// turn wxTextCtrl::Replace() debugging on (slows down code a *lot*!)
#define WXDEBUG_TEXT_REPLACE
#ifndef __WXDEBUG__
#undef WXDEBUG_TEXT
#undef WXDEBUG_TEXT_REPLACE
#endif
// wxStringTokenize only needed for debug checks
#ifdef WXDEBUG_TEXT_REPLACE
#include "wx/tokenzr.h"
#endif // WXDEBUG_TEXT_REPLACE
// ----------------------------------------------------------------------------
// private functions
// ----------------------------------------------------------------------------
// exchange two positions so that from is always less than or equal to to
static inline void OrderPositions(wxTextPos& from, wxTextPos& to)
{
if ( from > to )
{
wxTextPos tmp = from;
from = to;
to = tmp;
}
}
// ----------------------------------------------------------------------------
// constants
// ----------------------------------------------------------------------------
// names of text ctrl commands
#define wxTEXT_COMMAND_INSERT _T("insert")
#define wxTEXT_COMMAND_REMOVE _T("remove")
// the value which is never used for text position, even not -1 which is
// sometimes used for some special meaning
static const wxTextPos INVALID_POS_VALUE = wxInvalidTextCoord;
// overlap between pages (when using PageUp/Dn) in lines
static const size_t PAGE_OVERLAP_IN_LINES = 1;
// ----------------------------------------------------------------------------
// private data of wxTextCtrl
// ----------------------------------------------------------------------------
// the data only used by single line text controls
struct WXDLLEXPORT wxTextSingleLineData
{
// the position of the first visible pixel and the first visible column
wxCoord m_ofsHorz;
wxTextCoord m_colStart;
// and the last ones (m_posLastVisible is the width but m_colLastVisible
// is an absolute value)
wxCoord m_posLastVisible;
wxTextCoord m_colLastVisible;
// def ctor
wxTextSingleLineData()
{
m_colStart = 0;
m_ofsHorz = 0;
m_colLastVisible = -1;
m_posLastVisible = -1;
}
};
// the data only used by multi line text controls
struct WXDLLEXPORT wxTextMultiLineData
{
// the lines of text
wxArrayString m_lines;
// the current ranges of the scrollbars
int m_scrollRangeX,
m_scrollRangeY;
// should we adjust the horz/vert scrollbar?
bool m_updateScrollbarX,
m_updateScrollbarY;
// the max line length in pixels
wxCoord m_widthMax;
// the index of the line which has the length of m_widthMax
wxTextCoord m_lineLongest;
// the rect in which text appears: it is even less than m_rectText because
// only the last _complete_ line is shown, hence there is an unoccupied
// horizontal band at the bottom of it
wxRect m_rectTextReal;
// the x-coordinate of the caret before we started moving it vertically:
// this is used to ensure that moving the caret up and then down will
// return it to the same position as if we always round it in one direction
// we would shift it in that direction
//
// when m_xCaret == -1, we don't have any remembered position
wxCoord m_xCaret;
// the def ctor
wxTextMultiLineData()
{
m_scrollRangeX =
m_scrollRangeY = 0;
m_updateScrollbarX =
m_updateScrollbarY = false;
m_widthMax = -1;
m_lineLongest = 0;
m_xCaret = -1;
}
};
// the data only used by multi line text controls in line wrap mode
class WXDLLEXPORT wxWrappedLineData
{
// these functions set all our values, so give them access to them
friend void wxTextCtrl::LayoutLine(wxTextCoord line,
wxWrappedLineData& lineData) const;
friend void wxTextCtrl::LayoutLines(wxTextCoord) const;
public:
// def ctor
wxWrappedLineData()
{
m_rowFirst = -1;
}
// get the start of any row (remember that accessing m_rowsStart doesn't work
// for the first one)
wxTextCoord GetRowStart(wxTextCoord row) const
{
wxASSERT_MSG( IsValid(), _T("this line hadn't been laid out") );
return row ? m_rowsStart[row - 1] : 0;
}
// get the length of the row (using the total line length which we don't
// have here but need to calculate the length of the last row, so it must
// be given to us)
wxTextCoord GetRowLength(wxTextCoord row, wxTextCoord lenLine) const
{
wxASSERT_MSG( IsValid(), _T("this line hadn't been laid out") );
// note that m_rowsStart[row] is the same as GetRowStart(row + 1) (but
// slightly more efficient) and lenLine is the same as the start of the
// first row of the next line
return ((size_t)row == m_rowsStart.GetCount() ? lenLine : m_rowsStart[row])
- GetRowStart(row);
}
// return the width of the row in pixels
wxCoord GetRowWidth(wxTextCoord row) const
{
wxASSERT_MSG( IsValid(), _T("this line hadn't been laid out") );
return m_rowsWidth[row];
}
// return the number of rows
size_t GetRowCount() const
{
wxASSERT_MSG( IsValid(), _T("this line hadn't been laid out") );
return m_rowsStart.GetCount() + 1;
}
// return the number of additional (i.e. after the first one) rows
size_t GetExtraRowCount() const
{
wxASSERT_MSG( IsValid(), _T("this line hadn't been laid out") );
return m_rowsStart.GetCount();
}
// return the first row of this line
wxTextCoord GetFirstRow() const
{
wxASSERT_MSG( IsValid(), _T("this line hadn't been laid out") );
return m_rowFirst;
}
// return the first row of the next line
wxTextCoord GetNextRow() const
{
wxASSERT_MSG( IsValid(), _T("this line hadn't been laid out") );
return m_rowFirst + m_rowsStart.GetCount() + 1;
}
// this just provides direct access to m_rowsStart aerray for efficiency
wxTextCoord GetExtraRowStart(wxTextCoord row) const
{
wxASSERT_MSG( IsValid(), _T("this line hadn't been laid out") );
return m_rowsStart[row];
}
// this code is unused any longer
#if 0
// return true if the column is in the start of the last row (hence the row
// it is in is not wrapped)
bool IsLastRow(wxTextCoord colRowStart) const
{
return colRowStart == GetRowStart(m_rowsStart.GetCount());
}
// return true if the column is the last column of the row starting in
// colRowStart
bool IsLastColInRow(wxTextCoord colRowStart,
wxTextCoord colRowEnd,
wxTextCoord lenLine) const
{
// find the row which starts with colRowStart
size_t nRows = GetRowCount();
for ( size_t n = 0; n < nRows; n++ )
{
if ( GetRowStart(n) == colRowStart )
{
wxTextCoord colNextRowStart = n == nRows - 1
? lenLine
: GetRowStart(n + 1);
wxASSERT_MSG( colRowEnd < colNextRowStart,
_T("this column is not in this row at all!") );
return colRowEnd == colNextRowStart - 1;
}
}
// caller got it wrong
wxFAIL_MSG( _T("this column is not in the start of the row!") );
return false;
}
#endif // 0
// is this row the last one in its line?
bool IsLastRow(wxTextCoord row) const
{
return (size_t)row == GetExtraRowCount();
}
// the line is valid if it had been laid out correctly: note that just
// shiwting the line (because one of previous lines changed) doesn't make
// it invalid
bool IsValid() const { return !m_rowsWidth.IsEmpty(); }
// invalidating line will relayout it
void Invalidate() { m_rowsWidth.Empty(); }
private:
// for each line we remember the starting columns of all its rows after the
// first one (which always starts at 0), i.e. if a line is wrapped twice
// (== takes 3 rows) its m_rowsStart[0] may be 10 and m_rowsStart[1] == 15
wxArrayLong m_rowsStart;
// and the width of each row in pixels (this array starts from 0, as usual)
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?