many more methods implemented (though untested) for multline text ctrls

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/branches/wxUNIVERSAL@8539 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2000-10-12 19:39:12 +00:00
parent b7cdbb0e4c
commit b16e3fb5e8
2 changed files with 254 additions and 110 deletions

View File

@@ -170,7 +170,7 @@ public:
virtual void ShowCaret(bool show = TRUE); virtual void ShowCaret(bool show = TRUE);
void HideCaret() { ShowCaret(FALSE); } void HideCaret() { ShowCaret(FALSE); }
void CreateCaret(); // for the current font size void CreateCaret(); // for the current font size
wxCoord GetCaretPosition() const; // in pixels wxCoord GetCaretPosition(long pos = -1) const; // in pixels, def => current
// helpers for cursor movement // helpers for cursor movement
long GetWordStart() const; long GetWordStart() const;
@@ -256,12 +256,19 @@ protected:
// get the extent (width) of the text // get the extent (width) of the text
wxCoord GetTextWidth(const wxString& text) const; wxCoord GetTextWidth(const wxString& text) const;
// refresh the text in the given (in logical coords) rect
void RefreshTextRect(wxRect& rect);
// refresh the text in the given range (in logical coords) of this line, if // refresh the text in the given range (in logical coords) of this line, if
// width is 0, refresh to the end of line // width is 0, refresh to the end of line
void RefreshPixelRange(long line, wxCoord start, wxCoord width); void RefreshPixelRange(long line, wxCoord start, wxCoord width);
// refresh the text in the given range (in text coords) in this line // refresh the text in the given range (in text coords) in this line
void RefreshLineRange(long line, long start, long count); void RefreshColRange(long line, long start, long count);
// refresh the text from in the given line range (inclusive), if lineLast
// is -1, refresh all [visible] text after the lineFirst
void RefreshLineRange(long lineFirst, long lineLast = 0);
// refresh the text in the given range which can span multiple lines // refresh the text in the given range which can span multiple lines
// (this method accepts arguments in any order) // (this method accepts arguments in any order)
@@ -293,11 +300,25 @@ protected:
void OnChar(wxKeyEvent& event); void OnChar(wxKeyEvent& event);
void OnSize(wxSizeEvent& event); void OnSize(wxSizeEvent& event);
// accessors for derived classes (SL stands for single line)
const wxString& GetSLValue() const
{
wxASSERT_MSG( IsSingleLine(), _T("only for single line controls") );
return m_value;
}
void SetSLValue(const wxString& value)
{
wxASSERT_MSG( IsSingleLine(), _T("only for single line controls") );
m_value = value;
}
private: private:
// the initially specified control size // the initially specified control size
wxSize m_sizeInitial; wxSize m_sizeInitial;
// the entire control (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) // the lines of text (only used for multiline controls)

View File

@@ -39,6 +39,8 @@
#include "wx/dcclient.h" #include "wx/dcclient.h"
#include "wx/validate.h" #include "wx/validate.h"
#include "wx/textctrl.h" #include "wx/textctrl.h"
#include "wx/tokenzr.h"
#endif #endif
#include "wx/clipbrd.h" #include "wx/clipbrd.h"
@@ -154,7 +156,7 @@ bool wxTextCtrl::SetFont(const wxFont& font)
void wxTextCtrl::SetValue(const wxString& value) void wxTextCtrl::SetValue(const wxString& value)
{ {
if ( IsSingleLine() && (m_value == value) ) if ( IsSingleLine() && (value == GetSLValue()) )
{ {
// nothing changed // nothing changed
return; return;
@@ -168,7 +170,7 @@ wxString wxTextCtrl::GetValue() const
wxString value; wxString value;
if ( IsSingleLine() ) if ( IsSingleLine() )
{ {
value = m_value; value = GetSLValue();
} }
else // multiline else // multiline
{ {
@@ -219,6 +221,9 @@ void wxTextCtrl::Replace(long from, long to, const wxString& text)
wxString lineFirstOrig = GetLineText(lineStart); wxString lineFirstOrig = GetLineText(lineStart);
wxString lineFirst(lineFirstOrig, colStart); wxString lineFirst(lineFirstOrig, colStart);
// remember the start of the update region for later use
wxCoord startNewText = GetTextWidth(lineFirst);
// (2) add the text which replaces this part // (2) add the text which replaces this part
if ( !lines.IsEmpty() ) if ( !lines.IsEmpty() )
{ {
@@ -233,24 +238,64 @@ void wxTextCtrl::Replace(long from, long to, const wxString& text)
lineFirst += lineFirstOrig.c_str() + (size_t)colEnd; lineFirst += lineFirstOrig.c_str() + (size_t)colEnd;
} }
// (4) modify the line and refresh the part of the window which changed // (4) refresh the part of the line which changed
// we usually refresh till the end of line except of the most common case
// when some text is appended to the end of it in which case we refresh
// just the newly appended text
wxCoord widthNewText;
if ( (lineEnd == lineStart) &&
((size_t)colStart == lineFirstOrig.length()) )
{
// text appended, not replaced, so refresh only the new text
widthNewText = GetTextWidth(lines[0u]);
}
else
{
// refresh till the end of line as all its tail changed
// OPT: should only refresh the part occupied by the text, but OTOH
// the line won't be really repainted beyond it anyhow due to the
// checks in DoDrawTextInRect(), so is it really worth it?
widthNewText = 0;
}
RefreshPixelRange(lineStart, startNewText, widthNewText);
// (5) modify the line
if ( IsSingleLine() ) if ( IsSingleLine() )
{ {
m_value = lineFirst; SetSLValue(lineFirst);
// force m_colLastVisible update
m_colLastVisible = -1;
// consistency check: when replacing text in a single line control, we // consistency check: when replacing text in a single line control, we
// shouldn't have more than one line // shouldn't have more than one line
wxASSERT_MSG(lines.GetCount() <= 1, wxASSERT_MSG(lines.GetCount() <= 1,
_T("can't have more than one line in this wxTextCtrl")); _T("can't have more than one line in this wxTextCtrl"));
// nothing more can happen to us // update the current position
SetInsertionPoint(from + text.length());
// and the selection (do it after setting the cursor to have correct value
// for selection anchor)
ClearSelection();
// nothing more can happen to us, so bail out
return;
} }
else // multiline else // multiline
{ {
m_lines[lineStart] = lineFirst; m_lines[lineStart] = lineFirst;
} }
// now replace all intermediate lines entirely and refresh // now replace all intermediate lines entirely
bool refreshAllBelow = FALSE;
// (1) modify all lines which are really repaced
size_t nReplaceLine = 1, size_t nReplaceLine = 1,
nReplaceCount = lines.GetCount(); nReplaceCount = lines.GetCount();
for ( long line = lineStart + 1; line < lineEnd; line++ ) for ( long line = lineStart + 1; line < lineEnd; line++ )
@@ -260,10 +305,11 @@ void wxTextCtrl::Replace(long from, long to, const wxString& text)
// replace line // replace line
m_lines[line] = lines[nReplaceLine++]; m_lines[line] = lines[nReplaceLine++];
} }
else // no more replacement text - remove line else // no more replacement text - remove line(s)
{ {
// optimization: remove not only this line but all the next ones as // (2) remove all lines for which there is no more replacement
// well // text (this is slightly more efficient than continuing to
// run the loop)
// adjust the index by the number of lines removed // adjust the index by the number of lines removed
lineEnd -= lineEnd - line; lineEnd -= lineEnd - line;
@@ -273,17 +319,32 @@ void wxTextCtrl::Replace(long from, long to, const wxString& text)
{ {
m_lines.RemoveAt(line++); m_lines.RemoveAt(line++);
} }
// the lines below will scroll up
refreshAllBelow = TRUE;
} }
} }
// if there are still lines left in the replacement text, insert them // (3) if there are still lines left in the replacement text, insert them
// before modifying the last line // before modifying the last line
while ( nReplaceLine < nReplaceCount - 1 ) // OPT: insert all at once? if ( nReplaceLine < nReplaceCount - 1 )
{ {
// insert line and adjust for index change by incrementing lineEnd // the lines below will scroll down
m_lines.Insert(lines[nReplaceLine++], lineEnd++); refreshAllBelow = TRUE;
while ( nReplaceLine < nReplaceCount - 1 ) // OPT: insert all at once?
{
// insert line and adjust for index change by incrementing lineEnd
m_lines.Insert(lines[nReplaceLine++], lineEnd++);
}
} }
// (4) refresh the lines: if we had replaced exactly the same number of
// lines that we had before, we can just refresh these lines,
// otherwise the lines below will change as well, so we have to
// refresh them too (by passing -1 as RefreshLineRange() argument)
RefreshLineRange(lineStart + 1, refreshAllBelow ? -1 : lineEnd - 1);
// now deal with the last line: (1) replace its beginning with the end of // now deal with the last line: (1) replace its beginning with the end of
// the replacement text // the replacement text
wxString lineLast; wxString lineLast;
@@ -301,56 +362,21 @@ void wxTextCtrl::Replace(long from, long to, const wxString& text)
lineLast += lineLastOrig.c_str() + (size_t)colEnd; lineLast += lineLastOrig.c_str() + (size_t)colEnd;
} }
// (3) always refresh the last line entirely if it hadn't been already
// refreshed above
if ( !refreshAllBelow )
{
RefreshPixelRange(lineEnd, 0, 0); // entire line
}
m_lines[lineEnd] = lineLast; m_lines[lineEnd] = lineLast;
#if 0 // single line only
// replace the part of the text with the new value
wxString valueNew(m_value, (size_t)from);
// remember it for later use
wxCoord startNewText = GetTextWidth(valueNew);
valueNew += text;
if ( (size_t)to < m_value.length() )
{
valueNew += m_value.c_str() + (size_t)to;
}
// OPT: is the following really ok? not sure any more now at 2 am...
// we usually refresh till the end of line except of the most common case
// when some text is appended to the end of the string in which case we
// refresh just it
wxCoord widthNewText;
if ( (size_t)from < m_value.length() )
{
// refresh till the end of line
widthNewText = 0;
}
else // text appended, not replaced
{
// refresh only the new text
widthNewText = GetTextWidth(text);
}
m_value = valueNew;
// force m_colLastVisible update
m_colLastVisible = -1;
// update the current position // update the current position
SetInsertionPoint(from + text.length()); SetInsertionPoint(to);
// and the selection (do it after setting the cursor to have correct value // and the selection (do it after setting the cursor to have correct value
// for selection anchor) // for selection anchor)
ClearSelection(); ClearSelection();
// repaint
RefreshPixelRange(0, startNewText, widthNewText);
// TODO: and the affected parts of the next line(s)
#endif // 0
} }
void wxTextCtrl::Remove(long from, long to) void wxTextCtrl::Remove(long from, long to)
@@ -403,20 +429,8 @@ void wxTextCtrl::DoSetInsertionPoint(long pos)
HideCaret(); HideCaret();
m_curPos = pos; m_curPos = pos;
PositionToXY(m_curPos, &m_curRow, &m_curLine);
if ( IsSingleLine() ) ShowPosition(m_curPos);
{
m_curLine = 0;
m_curRow = m_curPos;
// the current position should always be shown, scroll the window
// for this if necessary
ShowHorzPosition(GetCaretPosition());
}
else // multi line
{
wxFAIL_MSG(_T("unimplemented for multi line"));
}
ShowCaret(); ShowCaret();
} }
@@ -433,16 +447,31 @@ long wxTextCtrl::GetInsertionPoint() const
long wxTextCtrl::GetLastPosition() const long wxTextCtrl::GetLastPosition() const
{ {
long pos;
if ( IsSingleLine() ) if ( IsSingleLine() )
{ {
return m_value.length(); pos = GetSLValue().length();
} }
else else // multiline
{ {
wxFAIL_MSG(_T("unimplemented for multi line")); pos = 0;
size_t nLineCount = m_lines.GetCount();
for ( size_t nLine = 0; nLine < nLineCount; nLine++ )
{
// +1 is because the positions at the end of this line and of the
// start of the next one are different
pos += m_lines[nLine].length() + 1;
}
return -1; if ( pos > 0 )
{
// the last position is at the end of the last line, not in the
// beginning of the next line after it
pos--;
}
} }
return pos;
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@@ -465,12 +494,36 @@ wxString wxTextCtrl::GetSelectionText() const
{ {
if ( IsSingleLine() ) if ( IsSingleLine() )
{ {
sel = wxString(m_value.c_str() + m_selStart, sel = GetSLValue().Mid(m_selStart, m_selEnd - m_selStart);
m_value.c_str() + m_selEnd);
} }
else else // multiline
{ {
wxFAIL_MSG(_T("unimplemented for multi line")); long colStart, lineStart,
colEnd, lineEnd;
PositionToXY(m_selStart, &colStart, &lineStart);
PositionToXY(m_selEnd, &colEnd, &lineEnd);
// as always, we need to check for the special case when the start
// and end line are the same
if ( lineEnd == lineStart )
{
sel = m_lines[lineStart].Mid(colStart, colEnd - colStart);
}
else // sel on multiple lines
{
// take the end of the first line
sel = m_lines[lineStart].c_str() + colStart;
sel += _T('\n');
// all intermediate ones
for ( long line = lineStart + 1; line < lineEnd; line++ )
{
sel << m_lines[line] << _T('\n');
}
// and the start of the last one
sel += m_lines[lineEnd].Left(colEnd);
}
} }
} }
@@ -655,13 +708,14 @@ int wxTextCtrl::GetLineLength(long line) const
{ {
wxASSERT_MSG( line == 0, _T("invalid GetLineLength() parameter") ); wxASSERT_MSG( line == 0, _T("invalid GetLineLength() parameter") );
return m_value.length(); return GetSLValue().length();
} }
else // multiline else // multiline
{ {
wxFAIL_MSG(_T("unimplemented for multi line")); wxCHECK_MSG( (size_t)line < m_lines.GetCount(), -1,
_T("line index out of range") );
return 0; return m_lines[line].length();
} }
} }
@@ -675,24 +729,18 @@ wxString wxTextCtrl::GetLineText(long line) const
} }
else // multiline else // multiline
{ {
wxFAIL_MSG(_T("unimplemented for multi line")); wxCHECK_MSG( (size_t)line < m_lines.GetCount(), _T(""),
_T("line index out of range") );
return _T(""); return m_lines[line];
} }
} }
int wxTextCtrl::GetNumberOfLines() const int wxTextCtrl::GetNumberOfLines() const
{ {
if ( IsSingleLine() ) // for single line controls there is always 1 line, even if the text is
{ // empty
return 1; return IsSingleLine() ? 1 : m_lines.GetCount();
}
else // multiline
{
wxFAIL_MSG(_T("unimplemented for multi line"));
return 0;
}
} }
long wxTextCtrl::XYToPosition(long x, long y) const long wxTextCtrl::XYToPosition(long x, long y) const
@@ -705,9 +753,23 @@ long wxTextCtrl::XYToPosition(long x, long y) const
} }
else // multiline else // multiline
{ {
wxFAIL_MSG(_T("unimplemented for multi line")); long pos = 0;
for ( size_t nLine = 0; nLine < (size_t)y; nLine++ )
{
// +1 is because the positions at the end of this line and of the
// start of the next one are different
pos += m_lines[nLine].length() + 1;
}
return -1; if ( pos > 0 )
{
// the last position is at the end of the last line, not in the
// beginning of the next line after it
pos--;
}
// take into account also the position in line
return pos + x;
} }
} }
@@ -715,7 +777,7 @@ bool wxTextCtrl::PositionToXY(long pos, long *x, long *y) const
{ {
if ( IsSingleLine() ) if ( IsSingleLine() )
{ {
if ( pos > GetLastPosition() ) if ( (size_t)pos > GetSLValue().length() )
return FALSE; return FALSE;
if ( x ) if ( x )
@@ -727,15 +789,42 @@ bool wxTextCtrl::PositionToXY(long pos, long *x, long *y) const
} }
else // multiline else // multiline
{ {
wxFAIL_MSG(_T("unimplemented for multi line")); size_t nLineCount = m_lines.GetCount();
for ( size_t nLine = 0; nLine < nLineCount; nLine++ )
{
long posNew = pos - (m_lines[nLine].length() + 1);
if ( posNew < 0 )
{
// we've found the line, now just calc the column
if ( x )
*x = nLine;
if ( y )
*y = pos;
return TRUE;
}
else // go further down
{
pos = posNew;
}
}
// beyond the last line
return FALSE; return FALSE;
} }
} }
void wxTextCtrl::ShowPosition(long pos) void wxTextCtrl::ShowPosition(long pos)
{ {
wxFAIL_MSG(_T("not implemented")); if ( IsSingleLine() )
{
ShowHorzPosition(GetCaretPosition(pos));
}
else
{
wxFAIL_MSG(_T("not implemented for multiline"));
}
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@@ -768,6 +857,9 @@ static inline bool IsWordChar(wxChar ch)
long wxTextCtrl::GetWordStart() const long wxTextCtrl::GetWordStart() const
{ {
// TODO
wxCHECK_MSG( IsSingleLine(), -1, _T("not implemented for multline") );
if ( m_curPos == -1 || m_curPos == 0 ) if ( m_curPos == -1 || m_curPos == 0 )
return 0; return 0;
@@ -797,6 +889,9 @@ long wxTextCtrl::GetWordStart() const
long wxTextCtrl::GetWordEnd() const long wxTextCtrl::GetWordEnd() const
{ {
// TODO
wxCHECK_MSG( IsSingleLine(), -1, _T("not implemented for multline") );
if ( m_curPos == -1 ) if ( m_curPos == -1 )
return 0; return 0;
@@ -922,6 +1017,10 @@ void wxTextCtrl::UpdateTextRect()
void wxTextCtrl::UpdateLastVisible() void wxTextCtrl::UpdateLastVisible()
{ {
// this method is only used for horizontal "scrollbarless" scrolling which
// is used only with single line controls
wxASSERT_MSG( IsSingleLine(), _T("shouldn't be called for multiline") );
// OPT: estimate the correct value first, just adjust it later // OPT: estimate the correct value first, just adjust it later
wxString text; wxString text;
@@ -1184,7 +1283,7 @@ void wxTextCtrl::ShowHorzPosition(wxCoord pos)
{ {
// scroll backwards // scroll backwards
long col; long col;
HitTestLine(GetValue(), pos, &col); HitTestLine(GetSLValue(), pos, &col);
ScrollText(col); ScrollText(col);
} }
else else
@@ -1203,7 +1302,7 @@ void wxTextCtrl::ShowHorzPosition(wxCoord pos)
{ {
// scroll forward // scroll forward
long col; long col;
HitTestLine(GetValue(), pos - width, &col); HitTestLine(GetSLValue(), pos - width, &col);
ScrollText(col + 1); ScrollText(col + 1);
} }
} }
@@ -1326,6 +1425,18 @@ void wxTextCtrl::DoPrepareDC(wxDC& dc)
// refresh // refresh
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void wxTextCtrl::RefreshLineRange(long lineFirst, long lineLast)
{
wxRect rect;
// rect.x is already 0
rect.width = m_rectText.width;
wxCoord h = GetCharHeight();
rect.y = lineFirst*h;
rect.SetBottom(lineLast == -1 ? m_rectText.height : (lineLast + 1)*h);
RefreshTextRect(rect);
}
void wxTextCtrl::RefreshTextRange(long start, long end) void wxTextCtrl::RefreshTextRange(long start, long end)
{ {
wxCHECK_RET( start != -1 && end != -1, wxCHECK_RET( start != -1 && end != -1,
@@ -1369,11 +1480,11 @@ void wxTextCtrl::RefreshTextRange(long start, long end)
posCount = colEnd - posStart; posCount = colEnd - posStart;
} }
RefreshLineRange(line, posStart, posCount); RefreshColRange(line, posStart, posCount);
} }
} }
void wxTextCtrl::RefreshLineRange(long line, long start, long count) void wxTextCtrl::RefreshColRange(long line, long start, long count)
{ {
wxString text = GetLineText(line); wxString text = GetLineText(line);
@@ -1386,7 +1497,7 @@ void wxTextCtrl::RefreshPixelRange(long line, wxCoord start, wxCoord width)
{ {
wxCoord h = GetCharHeight(); wxCoord h = GetCharHeight();
wxRect rect; wxRect rect;
rect.x = start - m_ofsHorz; rect.x = start;
rect.y = line*h; rect.y = line*h;
if ( width == 0 ) if ( width == 0 )
@@ -1402,6 +1513,17 @@ void wxTextCtrl::RefreshPixelRange(long line, wxCoord start, wxCoord width)
rect.height = h; rect.height = h;
RefreshTextRect(rect);
}
void wxTextCtrl::RefreshTextRect(wxRect& rect)
{
if ( IsSingleLine() )
{
// account for horz scrolling
rect.x -= m_ofsHorz;
}
// account for the text area offset // account for the text area offset
rect.Offset(m_rectText.GetPosition()); rect.Offset(m_rectText.GetPosition());
@@ -1568,7 +1690,7 @@ void wxTextCtrl::DoDrawTextInRect(wxDC& dc, const wxRect& rectUpdate)
// extract the part of line we need to redraw // extract the part of line we need to redraw
wxString textLine = GetLineText(line); wxString textLine = GetLineText(line);
wxString text = textLine.Mid(colStart, colEnd - colStart + 1); wxString text = textLine.Mid(colStart, colEnd - colStart);
// now deal with the selection // now deal with the selection
int selStart, selEnd; int selStart, selEnd;
@@ -1673,9 +1795,10 @@ void wxTextCtrl::CreateCaret()
SetCaret(caret); SetCaret(caret);
} }
wxCoord wxTextCtrl::GetCaretPosition() const wxCoord wxTextCtrl::GetCaretPosition(long pos = -1) const
{ {
wxString textBeforeCaret(GetLineText(m_curLine), (size_t)m_curRow); wxString textBeforeCaret(GetLineText(m_curLine),
(size_t)(pos == -1 ? m_curRow : pos));
return GetTextWidth(textBeforeCaret); return GetTextWidth(textBeforeCaret);
} }