split wxTextCtrl data members in wxSingle/MultiData

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/branches/wxUNIVERSAL@8853 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2000-11-27 20:26:24 +00:00
parent 10644bd0bd
commit be18e65726
3 changed files with 258 additions and 190 deletions

View File

@@ -359,11 +359,7 @@ protected:
wxCoord GetMaxWidth() const; wxCoord GetMaxWidth() const;
// force recalculation of the max line width // force recalculation of the max line width
void RecalcMaxWidth() void RecalcMaxWidth();
{
m_widthMax = -1;
(void)GetMaxWidth();
}
// update the max width after the given line was modified // update the max width after the given line was modified
void UpdateMaxWidth(wxTextCoord line); void UpdateMaxWidth(wxTextCoord line);
@@ -389,11 +385,29 @@ protected:
wxTextCoord *col, wxTextCoord *col,
wxTextCoord *row) const; wxTextCoord *row) const;
// get the height of one line (the same for all lines)
wxCoord GetLineHeight() const
{
// this one should be already precalculated
wxASSERT_MSG( m_heightLine != -1, _T("should have line height") );
return m_heightLine;
}
// recalc the line height (to call when the font changes)
void RecalcLineHeight();
// event handlers // event handlers
void OnIdle(wxIdleEvent& event); void OnIdle(wxIdleEvent& event);
void OnChar(wxKeyEvent& event); void OnChar(wxKeyEvent& event);
void OnSize(wxSizeEvent& event); void OnSize(wxSizeEvent& event);
// return the struct containing control-type dependent data
struct wxTextSingleLineData& SData() { return *m_data.sdata; }
struct wxTextMultiLineData& MData() { return *m_data.mdata; }
const wxTextSingleLineData& SData() const { return *m_data.sdata; }
const wxTextMultiLineData& MData() const { return *m_data.mdata; }
// accessors for derived classes (SL stands for single line) // accessors for derived classes (SL stands for single line)
const wxString& GetSLValue() const const wxString& GetSLValue() const
{ {
@@ -423,9 +437,6 @@ private:
// the control text (only used for single line controls) // the control text (only used for single line controls)
wxString m_value; wxString m_value;
// the lines of text (only used for multiline controls)
wxArrayString m_lines;
// current position // current position
wxTextPos m_curPos; wxTextPos m_curPos;
wxTextCoord m_curCol, wxTextCoord m_curCol,
@@ -447,32 +458,17 @@ private:
// the rectangle (in client coordinates) to draw text inside // the rectangle (in client coordinates) to draw text inside
wxRect m_rectText; wxRect m_rectText;
// this section is for the controls without horz scrollbar only // the height of one line (cached value of GetCharHeight)
wxCoord m_heightLine;
// the position of the first visible pixel and the first visible column
wxCoord m_ofsHorz; // we have some data which depends on the kind of control (single or multi
wxTextCoord m_colStart; // line)
union
// and the last ones (m_posLastVisible is the width but m_colLastVisible {
// is an absolute value) wxTextSingleLineData *sdata;
wxCoord m_posLastVisible; wxTextMultiLineData *mdata;
wxTextCoord m_colLastVisible; void *data;
} m_data;
// this section is for the controls with scrollbar(s)
// 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 object to which we delegate our undo/redo implementation // the object to which we delegate our undo/redo implementation
wxTextCtrlCommandProcessor *m_cmdProcessor; wxTextCtrlCommandProcessor *m_cmdProcessor;

View File

@@ -410,25 +410,14 @@ TextTestFrame::TextTestFrame(const wxString& title)
); );
wxSizer *sizerMiddle = new wxBoxSizer(wxVERTICAL); wxSizer *sizerMiddle = new wxBoxSizer(wxVERTICAL);
sizerMiddle->Add(sizerMiddleUp, 0, wxGROW); sizerMiddle->Add(sizerMiddleUp, 0, wxGROW);
sizerMiddle->Add(sizerMiddleDown, 0, wxGROW | wxTOP, 5); sizerMiddle->Add(sizerMiddleDown, 1, wxGROW | wxTOP, 5);
// I don't understand what's going on :-(
#ifdef __WXGTK__
sizerMiddle->SetMinSize(320, 0);
#endif
// right pane // right pane
wxStaticBox *box3 = new wxStaticBox(m_panel, -1, _T("&Text:")); wxStaticBox *box3 = new wxStaticBox(m_panel, -1, _T("&Text:"));
m_sizerText = new wxStaticBoxSizer(box3, wxHORIZONTAL); m_sizerText = new wxStaticBoxSizer(box3, wxHORIZONTAL);
Reset(); Reset();
CreateText(); CreateText();
m_sizerText->SetMinSize(250, m_sizerText->SetMinSize(250, 0);
#ifdef __WXGTK__
300
#else
0
#endif
);
// the 3 panes panes compose the upper part of the window // the 3 panes panes compose the upper part of the window
wxSizer *sizerUp = new wxBoxSizer(wxHORIZONTAL); wxSizer *sizerUp = new wxBoxSizer(wxHORIZONTAL);

View File

@@ -28,7 +28,7 @@
1. wxStringTokenize is the slowest part of Replace 1. wxStringTokenize is the slowest part of Replace
2. GetDC/ReleaseDC are very slow, avoid calling them several times 2. GetDC/ReleaseDC are very slow, avoid calling them several times
3. GetCharHeight() should be cached too +3. GetCharHeight() should be cached too
4. wxClientDC construction/destruction in HitTestLine is horribly expensive 4. wxClientDC construction/destruction in HitTestLine is horribly expensive
For line wrapping controls HitTest2 takes 50% of program time. The results For line wrapping controls HitTest2 takes 50% of program time. The results
@@ -180,6 +180,66 @@ static inline void OrderPositions(wxTextPos& from, wxTextPos& to)
#define wxTEXT_COMMAND_INSERT _T("insert") #define wxTEXT_COMMAND_INSERT _T("insert")
#define wxTEXT_COMMAND_REMOVE _T("remove") #define wxTEXT_COMMAND_REMOVE _T("remove")
// ----------------------------------------------------------------------------
// private data of wxTextCtrl
// ----------------------------------------------------------------------------
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;
}
};
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 def ctor
wxTextMultiLineData()
{
m_scrollRangeX =
m_scrollRangeY = 0;
m_updateScrollbarX =
m_updateScrollbarY = FALSE;
m_widthMax = -1;
m_lineLongest = 0;
}
};
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// private classes for undo/redo management // private classes for undo/redo management
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@@ -327,31 +387,21 @@ void wxTextCtrl::Init()
m_isModified = FALSE; m_isModified = FALSE;
m_isEditable = TRUE; m_isEditable = TRUE;
m_colStart = 0;
m_ofsHorz = 0;
m_colLastVisible = -1;
m_posLastVisible = -1;
m_posLast = m_posLast =
m_curPos = m_curPos =
m_curCol = m_curCol =
m_curRow = 0; m_curRow = 0;
m_scrollRangeX = m_heightLine = -1;
m_scrollRangeY = 0;
m_updateScrollbarX =
m_updateScrollbarY = FALSE;
m_widthMax = -1;
m_lineLongest = 0;
// init wxScrollHelper // init wxScrollHelper
SetWindow(this); SetWindow(this);
// init the undo manager // init the undo manager
m_cmdProcessor = new wxTextCtrlCommandProcessor(this); m_cmdProcessor = new wxTextCtrlCommandProcessor(this);
// no data yet
m_data.data = NULL;
} }
bool wxTextCtrl::Create(wxWindow *parent, bool wxTextCtrl::Create(wxWindow *parent,
@@ -381,11 +431,17 @@ bool wxTextCtrl::Create(wxWindow *parent,
} }
// TODO: support wxTE_NO_VSCROLL (?) // TODO: support wxTE_NO_VSCROLL (?)
// create data object for multiline controls
m_data.mdata = new wxTextMultiLineData;
} }
else else
{ {
// this doesn't make sense for single line controls // this doesn't make sense for single line controls
style &= ~wxHSCROLL; style &= ~wxHSCROLL;
// create data object for single line controls
m_data.sdata = new wxTextSingleLineData;
} }
if ( !wxControl::Create(parent, id, pos, size, style, if ( !wxControl::Create(parent, id, pos, size, style,
@@ -399,7 +455,7 @@ bool wxTextCtrl::Create(wxWindow *parent,
if ( style & wxTE_MULTILINE ) if ( style & wxTE_MULTILINE )
{ {
// we should always have at least one line in a multiline control // we should always have at least one line in a multiline control
m_lines.Add(wxEmptyString); MData().m_lines.Add(wxEmptyString);
// we might support it but it's quite useless and other ports don't // we might support it but it's quite useless and other ports don't
// support it anyhow // support it anyhow
@@ -407,6 +463,7 @@ bool wxTextCtrl::Create(wxWindow *parent,
_T("wxTE_PASSWORD can't be used with multiline ctrls") ); _T("wxTE_PASSWORD can't be used with multiline ctrls") );
} }
RecalcLineHeight();
SetValue(value); SetValue(value);
SetBestSize(size); SetBestSize(size);
@@ -425,6 +482,14 @@ bool wxTextCtrl::Create(wxWindow *parent,
wxTextCtrl::~wxTextCtrl() wxTextCtrl::~wxTextCtrl()
{ {
delete m_cmdProcessor; delete m_cmdProcessor;
if ( m_data.data )
{
if ( IsSingleLine() )
delete m_data.sdata;
else
delete m_data.mdata;
}
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@@ -453,13 +518,13 @@ wxString wxTextCtrl::GetValue() const
} }
else // multiline else // multiline
{ {
size_t count = m_lines.GetCount(); size_t count = MData().m_lines.GetCount();
for ( size_t n = 0; n < count; n++ ) for ( size_t n = 0; n < count; n++ )
{ {
if ( n ) if ( n )
value += _T('\n'); // from preceding line value += _T('\n'); // from preceding line
value += m_lines[n]; value += MData().m_lines[n];
} }
} }
@@ -529,8 +594,8 @@ void wxTextCtrl::Replace(wxTextPos from, wxTextPos to, const wxString& text)
m_value = valueNew; m_value = valueNew;
// force m_colLastVisible update // force SData().m_colLastVisible update
m_colLastVisible = -1; SData().m_colLastVisible = -1;
// repaint // repaint
RefreshPixelRange(0, startNewText, widthNewText); RefreshPixelRange(0, startNewText, widthNewText);
@@ -557,12 +622,12 @@ void wxTextCtrl::Replace(wxTextPos from, wxTextPos to, const wxString& text)
textOrig += _T('\n'); textOrig += _T('\n');
} }
textOrig += m_lines[line]; textOrig += MData().m_lines[line];
} }
// we need to append the '\n' for the last line unless there is no // we need to append the '\n' for the last line unless there is no
// following line // following line
size_t countOld = m_lines.GetCount(); size_t countOld = MData().m_lines.GetCount();
// (2) replace text in the combined string // (2) replace text in the combined string
@@ -572,7 +637,7 @@ void wxTextCtrl::Replace(wxTextPos from, wxTextPos to, const wxString& text)
// these values will be used to refresh the changed area below // these values will be used to refresh the changed area below
wxCoord widthNewText, wxCoord widthNewText,
startNewText = GetTextWidth(textNew); startNewText = GetTextWidth(textNew);
if ( (size_t)colStart == m_lines[lineStart].length() ) if ( (size_t)colStart == MData().m_lines[lineStart].length() )
{ {
// text appended, refresh just enough to show the new text // text appended, refresh just enough to show the new text
widthNewText = GetTextWidth(text.BeforeFirst(_T('\n'))); widthNewText = GetTextWidth(text.BeforeFirst(_T('\n')));
@@ -608,7 +673,7 @@ void wxTextCtrl::Replace(wxTextPos from, wxTextPos to, const wxString& text)
// (3b) special case: if we replace everything till the end we need to // (3b) special case: if we replace everything till the end we need to
// keep an empty line or the lines would disappear completely // keep an empty line or the lines would disappear completely
// (this also takes care of never leaving m_lines empty) // (this also takes care of never leaving MData().m_lines empty)
if ( ((size_t)lineEnd == countOld - 1) && lines.IsEmpty() ) if ( ((size_t)lineEnd == countOld - 1) && lines.IsEmpty() )
{ {
lines.Add(wxEmptyString); lines.Add(wxEmptyString);
@@ -625,7 +690,7 @@ void wxTextCtrl::Replace(wxTextPos from, wxTextPos to, const wxString& text)
if ( nReplaceLine < nReplaceCount ) if ( nReplaceLine < nReplaceCount )
{ {
// we have the replacement line for this one // we have the replacement line for this one
m_lines[line] = lines[nReplaceLine]; MData().m_lines[line] = lines[nReplaceLine];
UpdateMaxWidth(line); UpdateMaxWidth(line);
} }
@@ -636,13 +701,13 @@ void wxTextCtrl::Replace(wxTextPos from, wxTextPos to, const wxString& text)
bool deletedLongestLine = FALSE; bool deletedLongestLine = FALSE;
for ( wxTextCoord lineDel = lineEnd; lineDel >= line; lineDel-- ) for ( wxTextCoord lineDel = lineEnd; lineDel >= line; lineDel-- )
{ {
if ( lineDel == m_lineLongest ) if ( lineDel == MData().m_lineLongest )
{ {
// we will need to recalc the max line width // we will need to recalc the max line width
deletedLongestLine = TRUE; deletedLongestLine = TRUE;
} }
m_lines.RemoveAt(lineDel); MData().m_lines.RemoveAt(lineDel);
} }
if ( deletedLongestLine ) if ( deletedLongestLine )
@@ -658,7 +723,7 @@ void wxTextCtrl::Replace(wxTextPos from, wxTextPos to, const wxString& text)
// (4c) insert the new lines // (4c) insert the new lines
while ( nReplaceLine < nReplaceCount ) while ( nReplaceLine < nReplaceCount )
{ {
m_lines.Insert(lines[nReplaceLine++], ++lineEnd); MData().m_lines.Insert(lines[nReplaceLine++], ++lineEnd);
UpdateMaxWidth(lineEnd); UpdateMaxWidth(lineEnd);
} }
@@ -694,14 +759,14 @@ void wxTextCtrl::Replace(wxTextPos from, wxTextPos to, const wxString& text)
} }
//else: refresh all lines we changed (OPT?) //else: refresh all lines we changed (OPT?)
wxTextCoord lineEnd = m_lines.GetCount() - 1; wxTextCoord lineEnd = MData().m_lines.GetCount() - 1;
if ( lineStart <= lineEnd ) if ( lineStart <= lineEnd )
RefreshLineRange(lineStart, lineEnd); RefreshLineRange(lineStart, lineEnd);
// refresh text rect left below // refresh text rect left below
if ( rowsNew < rowsOld ) if ( rowsNew < rowsOld )
{ {
wxCoord h = GetCharHeight(); wxCoord h = GetLineHeight();
wxRect rect; wxRect rect;
rect.x = 0; rect.x = 0;
rect.width = m_rectText.width; rect.width = m_rectText.width;
@@ -711,7 +776,7 @@ void wxTextCtrl::Replace(wxTextPos from, wxTextPos to, const wxString& text)
} }
// the vert scrollbar might [dis]appear // the vert scrollbar might [dis]appear
m_updateScrollbarY = TRUE; MData().m_updateScrollbarY = TRUE;
} }
} }
@@ -828,12 +893,12 @@ wxTextPos wxTextCtrl::GetLastPosition() const
{ {
#ifdef WXDEBUG_TEXT #ifdef WXDEBUG_TEXT
pos = 0; pos = 0;
size_t nLineCount = m_lines.GetCount(); size_t nLineCount = MData().m_lines.GetCount();
for ( size_t nLine = 0; nLine < nLineCount; nLine++ ) for ( size_t nLine = 0; nLine < nLineCount; nLine++ )
{ {
// +1 is because the positions at the end of this line and of the // +1 is because the positions at the end of this line and of the
// start of the next one are different // start of the next one are different
pos += m_lines[nLine].length() + 1; pos += MData().m_lines[nLine].length() + 1;
} }
if ( pos > 0 ) if ( pos > 0 )
@@ -886,22 +951,22 @@ wxString wxTextCtrl::GetSelectionText() const
// and end line are the same // and end line are the same
if ( lineEnd == lineStart ) if ( lineEnd == lineStart )
{ {
sel = m_lines[lineStart].Mid(colStart, colEnd - colStart); sel = MData().m_lines[lineStart].Mid(colStart, colEnd - colStart);
} }
else // sel on multiple lines else // sel on multiple lines
{ {
// take the end of the first line // take the end of the first line
sel = m_lines[lineStart].c_str() + colStart; sel = MData().m_lines[lineStart].c_str() + colStart;
sel += _T('\n'); sel += _T('\n');
// all intermediate ones // all intermediate ones
for ( wxTextCoord line = lineStart + 1; line < lineEnd; line++ ) for ( wxTextCoord line = lineStart + 1; line < lineEnd; line++ )
{ {
sel << m_lines[line] << _T('\n'); sel << MData().m_lines[line] << _T('\n');
} }
// and the start of the last one // and the start of the last one
sel += m_lines[lineEnd].Left(colEnd); sel += MData().m_lines[lineEnd].Left(colEnd);
} }
} }
} }
@@ -1104,10 +1169,10 @@ int wxTextCtrl::GetLineLength(wxTextCoord line) const
} }
else // multiline else // multiline
{ {
wxCHECK_MSG( (size_t)line < m_lines.GetCount(), -1, wxCHECK_MSG( (size_t)line < MData().m_lines.GetCount(), -1,
_T("line index out of range") ); _T("line index out of range") );
return m_lines[line].length(); return MData().m_lines[line].length();
} }
} }
@@ -1121,17 +1186,17 @@ wxString wxTextCtrl::GetLineText(wxTextCoord line) const
} }
else // multiline else // multiline
{ {
wxCHECK_MSG( (size_t)line < m_lines.GetCount(), _T(""), wxCHECK_MSG( (size_t)line < MData().m_lines.GetCount(), _T(""),
_T("line index out of range") ); _T("line index out of range") );
return m_lines[line]; return MData().m_lines[line];
} }
} }
int wxTextCtrl::GetNumberOfLines() const int wxTextCtrl::GetNumberOfLines() const
{ {
// there is always 1 line, even if the text is empty // there is always 1 line, even if the text is empty
return IsSingleLine() ? 1 : m_lines.GetCount(); return IsSingleLine() ? 1 : MData().m_lines.GetCount();
} }
wxTextPos wxTextCtrl::XYToPosition(wxTextCoord x, wxTextCoord y) const wxTextPos wxTextCtrl::XYToPosition(wxTextCoord x, wxTextCoord y) const
@@ -1144,7 +1209,7 @@ wxTextPos wxTextCtrl::XYToPosition(wxTextCoord x, wxTextCoord y) const
} }
else // multiline else // multiline
{ {
if ( (size_t)y >= m_lines.GetCount() ) if ( (size_t)y >= MData().m_lines.GetCount() )
{ {
// this position is below the text // this position is below the text
return GetLastPosition(); return GetLastPosition();
@@ -1155,14 +1220,14 @@ wxTextPos wxTextCtrl::XYToPosition(wxTextCoord x, wxTextCoord y) const
{ {
// +1 is because the positions at the end of this line and of the // +1 is because the positions at the end of this line and of the
// start of the next one are different // start of the next one are different
pos += m_lines[nLine].length() + 1; pos += MData().m_lines[nLine].length() + 1;
} }
// take into account also the position in line // take into account also the position in line
if ( (size_t)x > m_lines[y].length() ) if ( (size_t)x > MData().m_lines[y].length() )
{ {
// don't return position in the next line // don't return position in the next line
x = m_lines[y].length(); x = MData().m_lines[y].length();
} }
return pos + x; return pos + x;
@@ -1187,12 +1252,12 @@ bool wxTextCtrl::PositionToXY(wxTextPos pos,
else // multiline else // multiline
{ {
wxTextPos posCur = 0; wxTextPos posCur = 0;
size_t nLineCount = m_lines.GetCount(); size_t nLineCount = MData().m_lines.GetCount();
for ( size_t nLine = 0; nLine < nLineCount; nLine++ ) for ( size_t nLine = 0; nLine < nLineCount; nLine++ )
{ {
// +1 is because the start the start of the next line is one // +1 is because the start the start of the next line is one
// position after the end of this one // position after the end of this one
wxTextPos posNew = posCur + m_lines[nLine].length() + 1; wxTextPos posNew = posCur + MData().m_lines[nLine].length() + 1;
if ( posNew > pos ) if ( posNew > pos )
{ {
// we've found the line, now just calc the column // we've found the line, now just calc the column
@@ -1277,7 +1342,7 @@ bool wxTextCtrl::PositionToLogicalXY(wxTextPos pos,
return FALSE; return FALSE;
} }
int hLine = GetCharHeight(); int hLine = GetLineHeight();
wxCoord x, y; wxCoord x, y;
wxString textLine = GetLineText(line); wxString textLine = GetLineText(line);
if ( IsSingleLine() || !WrapLines() ) if ( IsSingleLine() || !WrapLines() )
@@ -1347,10 +1412,10 @@ bool wxTextCtrl::PositionToDeviceXY(wxTextPos pos,
// finally translate the logical text rect coords into physical client // finally translate the logical text rect coords into physical client
// coords // coords
CalcScrolledPosition(m_rectText.x + x - m_ofsHorz, if ( IsSingleLine() )
m_rectText.y + y, x -= SData().m_ofsHorz;
xOut,
yOut); CalcScrolledPosition(m_rectText.x + x, m_rectText.y + y, xOut, yOut);
return TRUE; return TRUE;
} }
@@ -1375,7 +1440,7 @@ void wxTextCtrl::ShowPosition(wxTextPos pos)
{ {
ShowHorzPosition(GetTextWidth(GetSLValue().Left(pos))); ShowHorzPosition(GetTextWidth(GetSLValue().Left(pos)));
} }
else if ( m_scrollRangeX || m_scrollRangeY ) // multiline with scrollbars else if ( MData().m_scrollRangeX || MData().m_scrollRangeY ) // multiline with scrollbars
{ {
int xStart, yStart; int xStart, yStart;
GetViewStart(&xStart, &yStart); GetViewStart(&xStart, &yStart);
@@ -1390,9 +1455,9 @@ void wxTextCtrl::ShowPosition(wxTextPos pos)
// scroll the position vertically into view: if it is currently above // scroll the position vertically into view: if it is currently above
// it, make it the first one, otherwise the last one // it, make it the first one, otherwise the last one
if ( m_scrollRangeY ) if ( MData().m_scrollRangeY )
{ {
y /= GetCharHeight(); y /= GetLineHeight();
if ( y < yStart ) if ( y < yStart )
{ {
@@ -1432,7 +1497,7 @@ void wxTextCtrl::ShowPosition(wxTextPos pos)
{ {
// finding the last line is easy if each line has exactly // finding the last line is easy if each line has exactly
// one row // one row
yEnd = yStart + rectText.height / GetCharHeight() - 1; yEnd = yStart + rectText.height / GetLineHeight() - 1;
} }
if ( yEnd < y ) if ( yEnd < y )
@@ -1445,7 +1510,7 @@ void wxTextCtrl::ShowPosition(wxTextPos pos)
} }
// scroll the position horizontally into view // scroll the position horizontally into view
if ( m_scrollRangeX ) if ( MData().m_scrollRangeX )
{ {
// unlike for the rows, xStart doesn't correspond to the starting // unlike for the rows, xStart doesn't correspond to the starting
// column as they all have different widths, so we need to // column as they all have different widths, so we need to
@@ -1795,7 +1860,7 @@ wxSize wxTextCtrl::DoGetBestClientSize() const
GetTextExtent(GetTextToShow(GetLineText(0)), &w, &h); GetTextExtent(GetTextToShow(GetLineText(0)), &w, &h);
int wChar = GetCharWidth(), int wChar = GetCharWidth(),
hChar = GetCharHeight(); hChar = GetLineHeight();
int widthMin = wxMax(10*wChar, 100); int widthMin = wxMax(10*wChar, 100);
if ( w < widthMin ) if ( w < widthMin )
@@ -1841,16 +1906,16 @@ void wxTextCtrl::UpdateLastVisible()
return; return;
// use (efficient) HitTestLine to find the last visible character // use (efficient) HitTestLine to find the last visible character
wxString text = m_value.Mid((size_t)m_colStart /* to the end */); wxString text = m_value.Mid((size_t)SData().m_colStart /* to the end */);
wxTextCoord col; wxTextCoord col;
switch ( HitTestLine(text, m_rectText.width, &col) ) switch ( HitTestLine(text, m_rectText.width, &col) )
{ {
case wxTE_HT_BEYOND: case wxTE_HT_BEYOND:
// everything is visible // everything is visible
m_colLastVisible = text.length(); SData().m_colLastVisible = text.length();
// calc it below // calc it below
m_posLastVisible = -1; SData().m_posLastVisible = -1;
break; break;
/* /*
@@ -1867,43 +1932,46 @@ void wxTextCtrl::UpdateLastVisible()
// the last entirely seen character is the previous one because // the last entirely seen character is the previous one because
// this one is only partly visible - unless the width of the // this one is only partly visible - unless the width of the
// string is exactly the max width // string is exactly the max width
m_posLastVisible = GetTextWidth(text.Truncate(col + 1)); SData().m_posLastVisible = GetTextWidth(text.Truncate(col + 1));
if ( m_posLastVisible > m_rectText.width ) if ( SData().m_posLastVisible > m_rectText.width )
{ {
// this character is not entirely visible, take the // this character is not entirely visible, take the
// previous one // previous one
col--; col--;
// recalc it // recalc it
m_posLastVisible = -1; SData().m_posLastVisible = -1;
} }
//else: we can just see it //else: we can just see it
m_colLastVisible = col; SData().m_colLastVisible = col;
} }
break; break;
} }
// calculate the width of the text really shown // calculate the width of the text really shown
if ( m_posLastVisible == -1 ) if ( SData().m_posLastVisible == -1 )
{ {
m_posLastVisible = GetTextWidth(text.Truncate(m_colLastVisible + 1)); SData().m_posLastVisible = GetTextWidth(text.Truncate(SData().m_colLastVisible + 1));
} }
// current value is relative the start of the string text which starts at // current value is relative the start of the string text which starts at
// m_colStart, we need an absolute offset into string // SData().m_colStart, we need an absolute offset into string
m_colLastVisible += m_colStart; SData().m_colLastVisible += SData().m_colStart;
wxLogTrace(_T("text"), _T("Last visible column/position is %d/%ld"), wxLogTrace(_T("text"), _T("Last visible column/position is %d/%ld"),
m_colLastVisible, m_posLastVisible); SData().m_colLastVisible, SData().m_posLastVisible);
} }
void wxTextCtrl::OnSize(wxSizeEvent& event) void wxTextCtrl::OnSize(wxSizeEvent& event)
{ {
UpdateTextRect(); UpdateTextRect();
m_updateScrollbarX = if ( !IsSingleLine() )
m_updateScrollbarY = TRUE; {
MData().m_updateScrollbarX =
MData().m_updateScrollbarY = TRUE;
}
event.Skip(); event.Skip();
} }
@@ -1927,7 +1995,7 @@ wxRect wxTextCtrl::GetRealTextArea() const
// the real text area always holds an entire number of lines, so the only // the real text area always holds an entire number of lines, so the only
// difference with the text area is a narrow strip along the bottom border // difference with the text area is a narrow strip along the bottom border
wxRect rectText = m_rectText; wxRect rectText = m_rectText;
int hLine = GetCharHeight(); int hLine = GetLineHeight();
rectText.height = (m_rectText.height / hLine) * hLine; rectText.height = (m_rectText.height / hLine) * hLine;
return rectText; return rectText;
} }
@@ -2273,7 +2341,7 @@ wxTextCtrlHitTestResult wxTextCtrl::HitTest2(wxCoord y0,
if ( colRowStartOut ) if ( colRowStartOut )
*colRowStartOut = 0; *colRowStartOut = 0;
int hLine = GetCharHeight(); int hLine = GetLineHeight();
wxTextCoord rowLast = GetNumberOfLines() - 1; wxTextCoord rowLast = GetNumberOfLines() - 1;
if ( IsSingleLine() || !WrapLines() ) if ( IsSingleLine() || !WrapLines() )
{ {
@@ -2439,18 +2507,18 @@ wxTextCtrlHitTestResult wxTextCtrl::HitTest2(wxCoord y0,
wxTextCtrl has not one but two scrolling mechanisms: one is semi-automatic wxTextCtrl has not one but two scrolling mechanisms: one is semi-automatic
scrolling in both horizontal and vertical direction implemented using scrolling in both horizontal and vertical direction implemented using
wxScrollHelper and the second one is manual scrolling implemented using wxScrollHelper and the second one is manual scrolling implemented using
m_ofsHorz and used by the single line controls without scroll bar. SData().m_ofsHorz and used by the single line controls without scroll bar.
The first version (the standard one) always scrolls by fixed amount which is The first version (the standard one) always scrolls by fixed amount which is
fine for vertical scrolling as all lines have the same height but is rather fine for vertical scrolling as all lines have the same height but is rather
ugly for horizontal scrolling if proportional font is used. This is why we ugly for horizontal scrolling if proportional font is used. This is why we
manually update and use m_ofsHorz which contains the length of the string manually update and use SData().m_ofsHorz which contains the length of the string
which is hidden beyond the left borde. An important property of text which is hidden beyond the left borde. An important property of text
controls using this kind of scrolling is that an entire number of characters controls using this kind of scrolling is that an entire number of characters
is always shown and that parts of characters never appear on display - is always shown and that parts of characters never appear on display -
neither in the leftmost nor rightmost positions. neither in the leftmost nor rightmost positions.
Once again, for multi line controls m_ofsHorz is always 0 and scrolling is Once again, for multi line controls SData().m_ofsHorz is always 0 and scrolling is
done as usual for wxScrollWindow. done as usual for wxScrollWindow.
*/ */
@@ -2458,8 +2526,8 @@ void wxTextCtrl::ShowHorzPosition(wxCoord pos)
{ {
// pos is the logical position to show // pos is the logical position to show
// m_ofsHorz is the fisrt logical position shown // SData().m_ofsHorz is the fisrt logical position shown
if ( pos < m_ofsHorz ) if ( pos < SData().m_ofsHorz )
{ {
// scroll backwards // scroll backwards
wxTextCoord col; wxTextCoord col;
@@ -2477,8 +2545,8 @@ void wxTextCtrl::ShowHorzPosition(wxCoord pos)
width = m_rectText.width; width = m_rectText.width;
} }
// m_ofsHorz + width is the last logical position shown // SData().m_ofsHorz + width is the last logical position shown
if ( pos > m_ofsHorz + width) if ( pos > SData().m_ofsHorz + width)
{ {
// scroll forward // scroll forward
wxTextCoord col; wxTextCoord col;
@@ -2500,25 +2568,25 @@ void wxTextCtrl::ScrollText(wxTextCoord col)
col = 0; col = 0;
// OPT: could only get the extent of the part of the string between col // OPT: could only get the extent of the part of the string between col
// and m_colStart // and SData().m_colStart
wxCoord ofsHorz = GetTextWidth(GetLineText(0).Left(col)); wxCoord ofsHorz = GetTextWidth(GetLineText(0).Left(col));
if ( ofsHorz != m_ofsHorz ) if ( ofsHorz != SData().m_ofsHorz )
{ {
// what is currently shown? // what is currently shown?
if ( m_colLastVisible == -1 ) if ( SData().m_colLastVisible == -1 )
{ {
UpdateLastVisible(); UpdateLastVisible();
} }
// NB1: to scroll to the right, offset must be negative, hence the // NB1: to scroll to the right, offset must be negative, hence the
// order of operands // order of operands
int dx = m_ofsHorz - ofsHorz; int dx = SData().m_ofsHorz - ofsHorz;
// NB2: we call Refresh() below which results in a call to // NB2: we call Refresh() below which results in a call to
// DoDraw(), so we must update m_ofsHorz before calling it // DoDraw(), so we must update SData().m_ofsHorz before calling it
m_ofsHorz = ofsHorz; SData().m_ofsHorz = ofsHorz;
m_colStart = col; SData().m_colStart = col;
// scroll only the rectangle inside which there is the text // scroll only the rectangle inside which there is the text
if ( dx > 0 ) if ( dx > 0 )
@@ -2535,11 +2603,11 @@ void wxTextCtrl::ScrollText(wxTextCoord col)
// before will be refreshed and redrawn // before will be refreshed and redrawn
// but we still need to force updating after scrolling // but we still need to force updating after scrolling
m_colLastVisible = -1; SData().m_colLastVisible = -1;
} }
wxRect rect = m_rectText; wxRect rect = m_rectText;
rect.width = m_posLastVisible; rect.width = SData().m_posLastVisible;
rect = ScrollNoRefresh(dx, 0, &rect); rect = ScrollNoRefresh(dx, 0, &rect);
@@ -2572,14 +2640,14 @@ void wxTextCtrl::ScrollText(wxTextCoord col)
Refresh(TRUE, &rect); Refresh(TRUE, &rect);
// and now the area on the right // and now the area on the right
rect.x = m_rectText.x + m_posLastVisible; rect.x = m_rectText.x + SData().m_posLastVisible;
rect.width = m_rectText.width - m_posLastVisible; rect.width = m_rectText.width - SData().m_posLastVisible;
} }
else else
{ {
// just extend the rect covering the uncovered area to the edge of // just extend the rect covering the uncovered area to the edge of
// the text rect // the text rect
rect.width += m_rectText.width - m_posLastVisible; rect.width += m_rectText.width - SData().m_posLastVisible;
} }
Refresh(TRUE, &rect); Refresh(TRUE, &rect);
@@ -2588,18 +2656,21 @@ void wxTextCtrl::ScrollText(wxTextCoord col)
void wxTextCtrl::CalcUnscrolledPosition(int x, int y, int *xx, int *yy) const void wxTextCtrl::CalcUnscrolledPosition(int x, int y, int *xx, int *yy) const
{ {
wxScrollHelper::CalcUnscrolledPosition(x + m_ofsHorz, y, xx, yy); if ( IsSingleLine() )
x += SData().m_ofsHorz;
wxScrollHelper::CalcUnscrolledPosition(x, y, xx, yy);
} }
void wxTextCtrl::DoPrepareDC(wxDC& dc) void wxTextCtrl::DoPrepareDC(wxDC& dc)
{ {
// for single line controls we only have to deal with m_ofsHorz and it's // for single line controls we only have to deal with SData().m_ofsHorz and it's
// useless to call base class version as they don't use normal scrolling // useless to call base class version as they don't use normal scrolling
if ( m_ofsHorz ) if ( IsSingleLine() && SData().m_ofsHorz )
{ {
// adjust the DC origin if the text is shifted // adjust the DC origin if the text is shifted
wxPoint pt = dc.GetDeviceOrigin(); wxPoint pt = dc.GetDeviceOrigin();
dc.SetDeviceOrigin(pt.x - m_ofsHorz, pt.y); dc.SetDeviceOrigin(pt.x - SData().m_ofsHorz, pt.y);
} }
else else
{ {
@@ -2610,18 +2681,18 @@ void wxTextCtrl::DoPrepareDC(wxDC& dc)
void wxTextCtrl::UpdateMaxWidth(wxTextCoord line) void wxTextCtrl::UpdateMaxWidth(wxTextCoord line)
{ {
// check if the max width changes after this line was modified // check if the max width changes after this line was modified
wxCoord widthMaxOld = m_widthMax, wxCoord widthMaxOld = MData().m_widthMax,
width; width;
GetTextExtent(GetLineText(line), &width, NULL); GetTextExtent(GetLineText(line), &width, NULL);
if ( line == m_lineLongest ) if ( line == MData().m_lineLongest )
{ {
// this line was the longest one, is it still? // this line was the longest one, is it still?
if ( width > m_widthMax ) if ( width > MData().m_widthMax )
{ {
m_widthMax = width; MData().m_widthMax = width;
} }
else if ( width < m_widthMax ) else if ( width < MData().m_widthMax )
{ {
// we need to find the new longest line // we need to find the new longest line
RecalcMaxWidth(); RecalcMaxWidth();
@@ -2630,20 +2701,31 @@ void wxTextCtrl::UpdateMaxWidth(wxTextCoord line)
} }
else // it wasn't the longest line, but maybe it became it? else // it wasn't the longest line, but maybe it became it?
{ {
// GetMaxWidth() and not m_widthMax as it might be not calculated yet // GetMaxWidth() and not MData().m_widthMax as it might be not calculated yet
if ( width > GetMaxWidth() ) if ( width > GetMaxWidth() )
{ {
m_widthMax = width; MData().m_widthMax = width;
m_lineLongest = line; MData().m_lineLongest = line;
} }
} }
m_updateScrollbarX = m_widthMax != widthMaxOld; MData().m_updateScrollbarX = MData().m_widthMax != widthMaxOld;
}
void wxTextCtrl::RecalcLineHeight()
{
m_heightLine = GetCharHeight();
}
void wxTextCtrl::RecalcMaxWidth()
{
MData().m_widthMax = -1;
(void)GetMaxWidth();
} }
wxCoord wxTextCtrl::GetMaxWidth() const wxCoord wxTextCtrl::GetMaxWidth() const
{ {
if ( m_widthMax == -1 ) if ( MData().m_widthMax == -1 )
{ {
// recalculate it // recalculate it
@@ -2653,25 +2735,25 @@ wxCoord wxTextCtrl::GetMaxWidth() const
wxClientDC dc(self); wxClientDC dc(self);
dc.SetFont(GetFont()); dc.SetFont(GetFont());
self->m_widthMax = 0; self->MData().m_widthMax = 0;
size_t count = m_lines.GetCount(); size_t count = MData().m_lines.GetCount();
for ( size_t n = 0; n < count; n++ ) for ( size_t n = 0; n < count; n++ )
{ {
wxCoord width; wxCoord width;
dc.GetTextExtent(m_lines[n], &width, NULL); dc.GetTextExtent(MData().m_lines[n], &width, NULL);
if ( width > m_widthMax ) if ( width > MData().m_widthMax )
{ {
// remember the width and the line which has it // remember the width and the line which has it
self->m_widthMax = width; self->MData().m_widthMax = width;
self->m_lineLongest = n; self->MData().m_lineLongest = n;
} }
} }
} }
wxASSERT_MSG( m_widthMax != -1, _T("should have at least 1 line") ); wxASSERT_MSG( MData().m_widthMax != -1, _T("should have at least 1 line") );
return m_widthMax; return MData().m_widthMax;
} }
void wxTextCtrl::UpdateScrollbars() void wxTextCtrl::UpdateScrollbars()
@@ -2680,7 +2762,7 @@ void wxTextCtrl::UpdateScrollbars()
// is our height enough to show all items? // is our height enough to show all items?
wxTextCoord nRows = GetNumberOfRowsBefore(GetNumberOfLines()); wxTextCoord nRows = GetNumberOfRowsBefore(GetNumberOfLines());
wxCoord lineHeight = GetCharHeight(); wxCoord lineHeight = GetLineHeight();
bool showScrollbarY = !IsSingleLine() && nRows*lineHeight > size.y; bool showScrollbarY = !IsSingleLine() && nRows*lineHeight > size.y;
// is our width enough to show the longest line? // is our width enough to show the longest line?
@@ -2706,7 +2788,7 @@ void wxTextCtrl::UpdateScrollbars()
: 0; : 0;
int scrollRangeY = showScrollbarY ? nRows : 0; int scrollRangeY = showScrollbarY ? nRows : 0;
if ( (scrollRangeY != m_scrollRangeY) || (scrollRangeX != m_scrollRangeX) ) if ( (scrollRangeY != MData().m_scrollRangeY) || (scrollRangeX != MData().m_scrollRangeX) )
{ {
int x, y; int x, y;
GetViewStart(&x, &y); GetViewStart(&x, &y);
@@ -2714,8 +2796,8 @@ void wxTextCtrl::UpdateScrollbars()
scrollRangeX, scrollRangeY, scrollRangeX, scrollRangeY,
x, y); x, y);
m_scrollRangeX = scrollRangeX; MData().m_scrollRangeX = scrollRangeX;
m_scrollRangeY = scrollRangeY; MData().m_scrollRangeY = scrollRangeY;
// bring the current position in view // bring the current position in view
ShowPosition(-1); ShowPosition(-1);
@@ -2725,12 +2807,12 @@ void wxTextCtrl::UpdateScrollbars()
void wxTextCtrl::OnIdle(wxIdleEvent& event) void wxTextCtrl::OnIdle(wxIdleEvent& event)
{ {
// notice that single line text control never has scrollbars // notice that single line text control never has scrollbars
if ( !IsSingleLine() && (m_updateScrollbarX || m_updateScrollbarY) ) if ( !IsSingleLine() && (MData().m_updateScrollbarX || MData().m_updateScrollbarY) )
{ {
UpdateScrollbars(); UpdateScrollbars();
m_updateScrollbarX = MData().m_updateScrollbarX =
m_updateScrollbarY = FALSE; MData().m_updateScrollbarY = FALSE;
} }
event.Skip(); event.Skip();
@@ -2747,7 +2829,7 @@ void wxTextCtrl::RefreshLineRange(wxTextCoord lineFirst, wxTextCoord lineLast)
wxRect rect; wxRect rect;
// rect.x is already 0 // rect.x is already 0
rect.width = m_rectText.width; rect.width = m_rectText.width;
wxCoord h = GetCharHeight(); wxCoord h = GetLineHeight();
rect.y = GetNumberOfRowsBefore(lineFirst)*h; rect.y = GetNumberOfRowsBefore(lineFirst)*h;
wxCoord bottom = GetNumberOfRowsBefore(lineLast + 1)*h; wxCoord bottom = GetNumberOfRowsBefore(lineLast + 1)*h;
@@ -2856,7 +2938,7 @@ void wxTextCtrl::RefreshPixelRange(wxTextCoord line,
} }
//else: just refresh the specified part //else: just refresh the specified part
wxCoord h = GetCharHeight(); wxCoord h = GetLineHeight();
wxRect rect; wxRect rect;
rect.x = start; rect.x = start;
rect.y = GetNumberOfRowsBefore(line)*h; rect.y = GetNumberOfRowsBefore(line)*h;
@@ -2904,7 +2986,7 @@ void wxTextCtrl::RefreshTextRect(const wxRect& rectClient)
if ( IsSingleLine() ) if ( IsSingleLine() )
{ {
// account for horz scrolling // account for horz scrolling
rect.x -= m_ofsHorz; rect.x -= SData().m_ofsHorz;
} }
else // multiline else // multiline
{ {
@@ -3037,7 +3119,7 @@ void wxTextCtrl::DoDrawTextInRect(wxDC& dc, const wxRect& rectUpdate)
} }
// prepare for drawing // prepare for drawing
wxCoord hLine = GetCharHeight(); wxCoord hLine = GetLineHeight();
// these vars will be used for hit testing of the current row // these vars will be used for hit testing of the current row
wxCoord y = rectUpdate.y; wxCoord y = rectUpdate.y;
@@ -3084,36 +3166,36 @@ void wxTextCtrl::DoDrawTextInRect(wxDC& dc, const wxRect& rectUpdate)
continue; continue;
} }
// don't show the columns which are scrolled out to the left
if ( colStart < m_colStart )
colStart = m_colStart;
// colEnd may be less than colStart if colStart was changed by the
// assignment above
if ( colEnd < colStart )
colEnd = colStart;
// for single line controls we may additionally cut off everything // for single line controls we may additionally cut off everything
// which is to the right of the last visible position // which is to the right of the last visible position
if ( IsSingleLine() ) if ( IsSingleLine() )
{ {
// don't show the columns which are scrolled out to the left
if ( colStart < SData().m_colStart )
colStart = SData().m_colStart;
// colEnd may be less than colStart if colStart was changed by the
// assignment above
if ( colEnd < colStart )
colEnd = colStart;
// don't draw the chars beyond the rightmost one // don't draw the chars beyond the rightmost one
if ( m_colLastVisible == -1 ) if ( SData().m_colLastVisible == -1 )
{ {
// recalculate this rightmost column // recalculate this rightmost column
UpdateLastVisible(); UpdateLastVisible();
} }
if ( colStart > m_colLastVisible ) if ( colStart > SData().m_colLastVisible )
{ {
// don't bother redrawing something that is beyond the last // don't bother redrawing something that is beyond the last
// visible position // visible position
continue; continue;
} }
if ( colEnd > m_colLastVisible ) if ( colEnd > SData().m_colLastVisible )
{ {
colEnd = m_colLastVisible; colEnd = SData().m_colLastVisible;
} }
} }
@@ -3244,6 +3326,7 @@ bool wxTextCtrl::SetFont(const wxFont& font)
// update geometry parameters // update geometry parameters
UpdateTextRect(); UpdateTextRect();
UpdateScrollbars(); UpdateScrollbars();
RecalcLineHeight();
RecalcMaxWidth(); RecalcMaxWidth();
Refresh(); Refresh();
@@ -3268,7 +3351,7 @@ void wxTextCtrl::CreateCaret()
if ( IsEditable() ) if ( IsEditable() )
{ {
// FIXME use renderer // FIXME use renderer
caret = new wxCaret(this, 1, GetCharHeight()); caret = new wxCaret(this, 1, GetLineHeight());
#ifndef __WXMSW__ #ifndef __WXMSW__
caret->SetBlinkTime(0); caret->SetBlinkTime(0);
#endif // __WXMSW__ #endif // __WXMSW__
@@ -3366,7 +3449,7 @@ bool wxTextCtrl::PerformAction(const wxControlAction& actionOrig,
// the previous line // the previous line
wxPoint pt = GetCaretPosition() - m_rectText.GetPosition(); wxPoint pt = GetCaretPosition() - m_rectText.GetPosition();
CalcUnscrolledPosition(pt.x, pt.y, &pt.x, &pt.y); CalcUnscrolledPosition(pt.x, pt.y, &pt.x, &pt.y);
pt.y -= GetCharHeight(); pt.y -= GetLineHeight();
wxTextCoord col, row; wxTextCoord col, row;
if ( HitTestLogical(pt, &col, &row) != wxTE_HT_BEFORE ) if ( HitTestLogical(pt, &col, &row) != wxTE_HT_BEFORE )
@@ -3379,7 +3462,7 @@ bool wxTextCtrl::PerformAction(const wxControlAction& actionOrig,
// see comments for wxACTION_TEXT_UP // see comments for wxACTION_TEXT_UP
wxPoint pt = GetCaretPosition() - m_rectText.GetPosition(); wxPoint pt = GetCaretPosition() - m_rectText.GetPosition();
CalcUnscrolledPosition(pt.x, pt.y, &pt.x, &pt.y); CalcUnscrolledPosition(pt.x, pt.y, &pt.x, &pt.y);
pt.y += GetCharHeight(); pt.y += GetLineHeight();
wxTextCoord col, row; wxTextCoord col, row;
if ( HitTestLogical(pt, &col, &row) != wxTE_HT_BELOW ) if ( HitTestLogical(pt, &col, &row) != wxTE_HT_BELOW )