wxTextCtrl work:
1. horz scrolling (not perfect yet) 2. more actions (clipboard, double click) 3. wxCaret fixes 4. clipboard support 5. wxTE_PASSWORD support wxScrolledWindow/wxlistBox: better hit test calculation: take into account GetClientAreaOrigin() fix for wxGTK bug in wxDC::Blit() in wxWindow::ScrollWindow draw transparent radio bitmaps under GTK wxWindow::ScrollWindow() can now scroll a part of window only git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/branches/wxUNIVERSAL@8404 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
@@ -30,6 +30,10 @@
|
||||
#if wxUSE_TEXTCTRL
|
||||
|
||||
#ifndef WX_PRECOMP
|
||||
#include "wx/log.h"
|
||||
|
||||
#include "wx/clipbrd.h"
|
||||
|
||||
#include "wx/dcclient.h"
|
||||
#include "wx/validate.h"
|
||||
#include "wx/textctrl.h"
|
||||
@@ -39,6 +43,7 @@
|
||||
|
||||
#include "wx/univ/inphand.h"
|
||||
#include "wx/univ/renderer.h"
|
||||
#include "wx/univ/colschem.h"
|
||||
#include "wx/univ/theme.h"
|
||||
|
||||
// ============================================================================
|
||||
@@ -47,6 +52,7 @@
|
||||
|
||||
BEGIN_EVENT_TABLE(wxTextCtrl, wxControl)
|
||||
EVT_CHAR(OnChar)
|
||||
EVT_SIZE(OnSize)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
IMPLEMENT_DYNAMIC_CLASS(wxTextCtrl, wxControl)
|
||||
@@ -64,7 +70,7 @@ void wxTextCtrl::Init()
|
||||
m_isModified = FALSE;
|
||||
m_isEditable = TRUE;
|
||||
|
||||
m_caret = (wxCaret *)NULL;
|
||||
m_ofsHorz = 0;
|
||||
}
|
||||
|
||||
bool wxTextCtrl::Create(wxWindow *parent,
|
||||
@@ -83,15 +89,17 @@ bool wxTextCtrl::Create(wxWindow *parent,
|
||||
}
|
||||
|
||||
// FIXME use renderer
|
||||
wxCaret *caret = new wxCaret(this, 2, GetCharHeight());
|
||||
wxCaret *caret = new wxCaret(this, 1, GetCharHeight());
|
||||
caret->SetBlinkTime(0);
|
||||
SetCaret(caret);
|
||||
caret->Show();
|
||||
|
||||
SetCursor(wxCURSOR_IBEAM);
|
||||
|
||||
SetValue(value);
|
||||
SetBestSize(size);
|
||||
|
||||
caret->Show();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -122,6 +130,10 @@ void wxTextCtrl::Replace(long from, long to, const wxString& text)
|
||||
wxCHECK_RET( from >= 0 && to >= 0 && from <= to && to <= GetLastPosition(),
|
||||
_T("invalid range in wxTextCtrl::Replace") );
|
||||
|
||||
// remember the line width as we may need to refresh beyond the end of the
|
||||
// new text if the text shrinks
|
||||
wxCoord widthOld = GetTextWidth(m_value);
|
||||
|
||||
// replace the part of the text with the new value
|
||||
wxString valueNew(m_value, (size_t)from);
|
||||
valueNew += text;
|
||||
@@ -138,8 +150,12 @@ void wxTextCtrl::Replace(long from, long to, const wxString& text)
|
||||
// for selection anchor)
|
||||
ClearSelection();
|
||||
|
||||
// FIXME shouldn't refresh everything of course
|
||||
Refresh();
|
||||
// refresh to the end of the line
|
||||
wxCoord start = GetTextWidth(m_value.Left((size_t)from));
|
||||
wxCoord end = GetTextWidth(m_value);
|
||||
RefreshLine(0, start, wxMax(end, widthOld));
|
||||
|
||||
// and the affected parts of the next line (TODO)
|
||||
}
|
||||
|
||||
void wxTextCtrl::Remove(long from, long to)
|
||||
@@ -158,6 +174,9 @@ void wxTextCtrl::Remove(long from, long to)
|
||||
|
||||
void wxTextCtrl::WriteText(const wxString& text)
|
||||
{
|
||||
// replace the selection with the new text
|
||||
RemoveSelection();
|
||||
|
||||
Replace(m_curPos, m_curPos, text);
|
||||
}
|
||||
|
||||
@@ -174,7 +193,7 @@ void wxTextCtrl::AppendText(const wxString& text)
|
||||
void wxTextCtrl::SetInsertionPoint(long pos)
|
||||
{
|
||||
wxCHECK_RET( pos >= 0 && pos <= GetLastPosition(),
|
||||
_T("insertion poitn position out of range") );
|
||||
_T("insertion point position out of range") );
|
||||
|
||||
if ( pos != m_curPos )
|
||||
{
|
||||
@@ -186,6 +205,34 @@ void wxTextCtrl::SetInsertionPoint(long pos)
|
||||
{
|
||||
m_curLine = 0;
|
||||
m_curRow = m_curPos;
|
||||
|
||||
// the current position should always be shown, scroll the window
|
||||
// for this if necessary
|
||||
wxCoord x = GetCaretPosition();
|
||||
|
||||
static const wxCoord MARGIN = 3;
|
||||
if ( x < wxMax(0, m_ofsHorz + MARGIN) )
|
||||
{
|
||||
// scroll backwards
|
||||
ScrollText(x - MARGIN);
|
||||
}
|
||||
else
|
||||
{
|
||||
wxCoord width = m_rectText.width;
|
||||
if ( !width )
|
||||
{
|
||||
// if we are called from the ctor, m_rectText is not
|
||||
// initialized yet, so do it now
|
||||
UpdateTextRect();
|
||||
width = m_rectText.width;
|
||||
}
|
||||
|
||||
if ( x > m_ofsHorz + width - MARGIN )
|
||||
{
|
||||
// scroll forward
|
||||
ScrollText(x - width + MARGIN);
|
||||
}
|
||||
}
|
||||
}
|
||||
else // multi line
|
||||
{
|
||||
@@ -232,6 +279,26 @@ void wxTextCtrl::GetSelection(long* from, long* to) const
|
||||
*to = m_selEnd;
|
||||
}
|
||||
|
||||
wxString wxTextCtrl::GetSelectionText() const
|
||||
{
|
||||
wxString sel;
|
||||
|
||||
if ( HasSelection() )
|
||||
{
|
||||
if ( IsSingleLine() )
|
||||
{
|
||||
sel = wxString(m_value.c_str() + m_selStart,
|
||||
m_value.c_str() + m_selEnd);
|
||||
}
|
||||
else
|
||||
{
|
||||
wxFAIL_MSG(_T("unimplemented for multi line"));
|
||||
}
|
||||
}
|
||||
|
||||
return sel;
|
||||
}
|
||||
|
||||
void wxTextCtrl::SetSelection(long from, long to)
|
||||
{
|
||||
if ( from == -1 || to == -1 )
|
||||
@@ -255,6 +322,10 @@ void wxTextCtrl::SetSelection(long from, long to)
|
||||
m_selStart = from;
|
||||
m_selEnd = to;
|
||||
|
||||
wxLogTrace(_T("text"), _T("Selection range is %ld-%ld"),
|
||||
m_selStart, m_selEnd);
|
||||
|
||||
// FIXME: shouldn't refresh everything
|
||||
Refresh();
|
||||
}
|
||||
//else: nothing to do
|
||||
@@ -263,12 +334,17 @@ void wxTextCtrl::SetSelection(long from, long to)
|
||||
|
||||
void wxTextCtrl::ClearSelection()
|
||||
{
|
||||
m_selStart =
|
||||
m_selEnd = -1;
|
||||
if ( HasSelection() )
|
||||
{
|
||||
m_selStart =
|
||||
m_selEnd = -1;
|
||||
|
||||
// FIXME: shouldn't refresh everything
|
||||
Refresh();
|
||||
}
|
||||
|
||||
// the anchor should be moved even if there was no selection previously
|
||||
m_selAnchor = m_curPos;
|
||||
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void wxTextCtrl::RemoveSelection()
|
||||
@@ -279,6 +355,60 @@ void wxTextCtrl::RemoveSelection()
|
||||
Remove(m_selStart, m_selEnd);
|
||||
}
|
||||
|
||||
bool wxTextCtrl::GetSelectedPartOfLine(long line, int *start, int *end) const
|
||||
{
|
||||
if ( start )
|
||||
*start = -1;
|
||||
if ( end )
|
||||
*end = -1;
|
||||
|
||||
if ( !HasSelection() )
|
||||
{
|
||||
// no selection at all, hence no selection in this line
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
long lineStart, colStart;
|
||||
PositionToXY(m_selStart, &colStart, &lineStart);
|
||||
if ( lineStart > line )
|
||||
{
|
||||
// this line is entirely above the selection
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
long lineEnd, colEnd;
|
||||
PositionToXY(m_selEnd, &colEnd, &lineEnd);
|
||||
if ( lineEnd < line )
|
||||
{
|
||||
// this line is entirely below the selection
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ( line == lineStart )
|
||||
{
|
||||
if ( start )
|
||||
*start = colStart;
|
||||
if ( end )
|
||||
*end = lineEnd == lineStart ? colEnd : GetLineLength(line);
|
||||
}
|
||||
else if ( line == lineEnd )
|
||||
{
|
||||
if ( start )
|
||||
*start = lineEnd == lineStart ? colStart : 0;
|
||||
if ( end )
|
||||
*end = colEnd;
|
||||
}
|
||||
else // the line is entirely inside the selection
|
||||
{
|
||||
if ( start )
|
||||
*start = 0;
|
||||
if ( end )
|
||||
*end = GetLineLength(line);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// flags
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -304,6 +434,7 @@ void wxTextCtrl::SetEditable(bool editable)
|
||||
{
|
||||
m_isEditable = editable;
|
||||
|
||||
// the appearance of the control might have changed
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
@@ -378,8 +509,11 @@ bool wxTextCtrl::PositionToXY(long pos, long *x, long *y) const
|
||||
{
|
||||
if ( IsSingleLine() )
|
||||
{
|
||||
if ( pos > GetLastPosition() )
|
||||
return FALSE;
|
||||
|
||||
if ( x )
|
||||
*x = m_curPos;
|
||||
*x = pos;
|
||||
if ( y )
|
||||
*y = 0;
|
||||
|
||||
@@ -431,6 +565,11 @@ long wxTextCtrl::GetWordStart() const
|
||||
if ( m_curPos == -1 || m_curPos == 0 )
|
||||
return 0;
|
||||
|
||||
// it shouldn't be possible to learn where the word starts in the password
|
||||
// text entry zone
|
||||
if ( IsPassword() )
|
||||
return 0;
|
||||
|
||||
// start at the previous position
|
||||
const wxChar *p0 = m_value.c_str();
|
||||
const wxChar *p = p0 + m_curPos - 1;
|
||||
@@ -455,6 +594,11 @@ long wxTextCtrl::GetWordEnd() const
|
||||
if ( m_curPos == -1 )
|
||||
return 0;
|
||||
|
||||
// it shouldn't be possible to learn where the word ends in the password
|
||||
// text entry zone
|
||||
if ( IsPassword() )
|
||||
return GetLastPosition();
|
||||
|
||||
// start at the current position
|
||||
const wxChar *p0 = m_value.c_str();
|
||||
const wxChar *p = p0 + m_curPos;
|
||||
@@ -480,19 +624,41 @@ long wxTextCtrl::GetWordEnd() const
|
||||
|
||||
void wxTextCtrl::Copy()
|
||||
{
|
||||
wxFAIL_MSG(_T("not implemented"));
|
||||
#if wxUSE_CLIPBOARD
|
||||
if ( HasSelection() )
|
||||
{
|
||||
wxClipboardLocker clipLock;
|
||||
|
||||
wxString text = GetTextToShow(GetSelectionText());
|
||||
wxTextDataObject *data = new wxTextDataObject(text);
|
||||
wxTheClipboard->SetData(data);
|
||||
}
|
||||
#endif // wxUSE_CLIPBOARD
|
||||
}
|
||||
|
||||
void wxTextCtrl::Cut()
|
||||
{
|
||||
wxFAIL_MSG(_T("not implemented"));
|
||||
if ( HasSelection() )
|
||||
{
|
||||
Copy();
|
||||
|
||||
RemoveSelection();
|
||||
}
|
||||
}
|
||||
|
||||
void wxTextCtrl::Paste()
|
||||
{
|
||||
wxFAIL_MSG(_T("not implemented"));
|
||||
}
|
||||
#if wxUSE_CLIPBOARD
|
||||
wxClipboardLocker clipLock;
|
||||
|
||||
wxTextDataObject data;
|
||||
if ( wxTheClipboard->IsSupported(data.GetFormat())
|
||||
&& wxTheClipboard->GetData(data) )
|
||||
{
|
||||
WriteText(data.GetText());
|
||||
}
|
||||
#endif // wxUSE_CLIPBOARD
|
||||
}
|
||||
|
||||
void wxTextCtrl::Undo()
|
||||
{
|
||||
@@ -522,7 +688,7 @@ bool wxTextCtrl::CanRedo() const
|
||||
wxSize wxTextCtrl::DoGetBestClientSize() const
|
||||
{
|
||||
wxCoord w, h;
|
||||
GetTextExtent(GetLineText(0), &w, &h);
|
||||
GetTextExtent(GetTextToShow(GetLineText(0)), &w, &h);
|
||||
|
||||
int wChar = GetCharWidth(),
|
||||
hChar = GetCharHeight();
|
||||
@@ -532,27 +698,383 @@ wxSize wxTextCtrl::DoGetBestClientSize() const
|
||||
if ( h < hChar )
|
||||
h = hChar;
|
||||
|
||||
w += 2*wChar;
|
||||
h += hChar / 2;
|
||||
wxRect rectText;
|
||||
rectText.width = w;
|
||||
rectText.height = h;
|
||||
wxRect rectTotal = GetRenderer()->GetTextTotalArea(this, rectText);
|
||||
return wxSize(rectTotal.width, rectTotal.height);
|
||||
}
|
||||
|
||||
return wxSize(w, h);
|
||||
void wxTextCtrl::UpdateTextRect()
|
||||
{
|
||||
m_rectText = GetRenderer()->
|
||||
GetTextClientArea(this,
|
||||
wxRect(wxPoint(0, 0), GetClientSize()));
|
||||
}
|
||||
|
||||
void wxTextCtrl::OnSize(wxSizeEvent& event)
|
||||
{
|
||||
UpdateTextRect();
|
||||
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
wxCoord wxTextCtrl::GetTextWidth(const wxString& text) const
|
||||
{
|
||||
wxCoord w;
|
||||
GetTextExtent(GetTextToShow(text), &w, NULL);
|
||||
return w;
|
||||
}
|
||||
|
||||
bool wxTextCtrl::HitTest(const wxPoint& pos, long *colOut, long *rowOut) const
|
||||
{
|
||||
// is the point in the text area or to the right or below it?
|
||||
bool inside = TRUE;
|
||||
|
||||
int x, y;
|
||||
wxPoint pt = GetClientAreaOrigin();
|
||||
pt += m_rectText.GetPosition();
|
||||
CalcUnscrolledPosition(pos.x - pt.x, pos.y - pt.y, &x, &y);
|
||||
|
||||
// row calculation is simple as we assume that all lines have the same
|
||||
// height
|
||||
int row = y / GetCharHeight();
|
||||
int rowMax = GetNumberOfLines() - 1;
|
||||
if ( row > rowMax )
|
||||
{
|
||||
// clicking below the text is the same as clicking on the last line
|
||||
row = rowMax;
|
||||
|
||||
inside = FALSE;
|
||||
}
|
||||
else if ( row < 0 )
|
||||
{
|
||||
row = 0;
|
||||
|
||||
inside = FALSE;
|
||||
}
|
||||
|
||||
// if the line is empty, the column can only be the first one
|
||||
wxString line = GetTextToShow(GetLineText(row));
|
||||
int col;
|
||||
wxTextCtrl *self = wxConstCast(this, wxTextCtrl);
|
||||
wxClientDC dc(self);
|
||||
dc.SetFont(GetFont());
|
||||
self->DoPrepareDC(dc);
|
||||
|
||||
wxCoord width;
|
||||
dc.GetTextExtent(line, &width, NULL);
|
||||
if ( x >= width )
|
||||
{
|
||||
// clicking beyond the end of line is equivalent to clicking at
|
||||
// the end of it
|
||||
col = line.length();
|
||||
|
||||
inside = FALSE;
|
||||
}
|
||||
else if ( x < 0 )
|
||||
{
|
||||
col = 0;
|
||||
|
||||
inside = FALSE;
|
||||
}
|
||||
else // we're inside the line
|
||||
{
|
||||
// now calculate the column: first, approximate it with fixed-width
|
||||
// value and then calculate the correct value iteratively: note that
|
||||
// we use the first character of the line instead of (average)
|
||||
// GetCharWidth(): it is common to have lines of dashes, for example,
|
||||
// and this should give us much better approximation in such case
|
||||
dc.GetTextExtent(line[0], &width, NULL);
|
||||
col = x / width;
|
||||
|
||||
// matchDir is -1 if we must move left, +1 to move right and 0 when
|
||||
// we're exactly on the character we need
|
||||
int matchDir = 0;
|
||||
for ( ;; )
|
||||
{
|
||||
// check that we didn't go beyond the line boundary
|
||||
if ( col < 0 )
|
||||
{
|
||||
col = 0;
|
||||
break;
|
||||
}
|
||||
if ( (size_t)col > line.length() )
|
||||
{
|
||||
col = line.length();
|
||||
break;
|
||||
}
|
||||
|
||||
wxString strBefore(line, (size_t)col);
|
||||
dc.GetTextExtent(strBefore, &width, NULL);
|
||||
if ( width >= x )
|
||||
{
|
||||
if ( matchDir == 1 )
|
||||
{
|
||||
// we were going to the right and, finally, moved beyond
|
||||
// the original position: so the current is the next after
|
||||
// the correct one
|
||||
col--;
|
||||
|
||||
break;
|
||||
}
|
||||
else if ( matchDir == 0 )
|
||||
{
|
||||
// we just started iterating, now we know that we should
|
||||
// move to the left
|
||||
matchDir = -1;
|
||||
}
|
||||
//else: we are still to the right of the target, continue
|
||||
}
|
||||
else // width < x
|
||||
{
|
||||
// same logic as above ...
|
||||
if ( matchDir == -1 )
|
||||
{
|
||||
// ... except that we don't need to back track
|
||||
break;
|
||||
}
|
||||
else if ( matchDir == 0 )
|
||||
{
|
||||
// go to the right
|
||||
matchDir = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// this is not supposed to happen
|
||||
wxASSERT_MSG( matchDir, _T("logic error in wxTextCtrl::HitTest") );
|
||||
|
||||
if ( matchDir == 1 )
|
||||
col++;
|
||||
else
|
||||
col--;
|
||||
}
|
||||
}
|
||||
|
||||
if ( colOut )
|
||||
*colOut = col;
|
||||
if ( rowOut )
|
||||
*rowOut = row;
|
||||
|
||||
return inside;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// scrolling
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void wxTextCtrl::ScrollText(wxCoord x)
|
||||
{
|
||||
// never scroll beyond the left border
|
||||
wxCoord ofsHorz = x > 0 ? x : 0;
|
||||
if ( ofsHorz != m_ofsHorz )
|
||||
{
|
||||
// NB1: to scroll to the right, offset must be negative, hence the
|
||||
// order of operands
|
||||
int dx = m_ofsHorz - ofsHorz;
|
||||
|
||||
// NB2: ScrollWindow() calls Refresh() which results in a call to
|
||||
// DoDraw(), so we must update m_ofsHorz before calling it
|
||||
m_ofsHorz = ofsHorz;
|
||||
|
||||
ScrollWindow(dx, 0, &m_rectText);
|
||||
}
|
||||
}
|
||||
|
||||
void wxTextCtrl::CalcUnscrolledPosition(int x, int y, int *xx, int *yy) const
|
||||
{
|
||||
wxScrollHelper::CalcUnscrolledPosition(x + m_ofsHorz, y, xx, yy);
|
||||
}
|
||||
|
||||
void wxTextCtrl::DoPrepareDC(wxDC& dc)
|
||||
{
|
||||
// adjust the DC origin if the text is shifted
|
||||
if ( m_ofsHorz )
|
||||
{
|
||||
wxPoint pt = dc.GetDeviceOrigin();
|
||||
dc.SetDeviceOrigin(pt.x - m_ofsHorz, pt.y);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// refresh
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void wxTextCtrl::RefreshLine(long line, wxCoord from, wxCoord to)
|
||||
{
|
||||
wxCoord h = GetCharHeight();
|
||||
wxRect rect;
|
||||
wxPoint pt = GetClientAreaOrigin() + m_rectText.GetPosition();
|
||||
rect.x = pt.x + from;
|
||||
rect.y = pt.y + line*h;
|
||||
rect.width = to - from + 1;
|
||||
rect.height = h;
|
||||
|
||||
wxLogTrace(_T("text"), _T("Refreshing (%d, %d)-(%d, %d)"),
|
||||
rect.x, rect.y, rect.x + rect.width, rect.y + rect.height);
|
||||
|
||||
Refresh(TRUE, &rect);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// drawing
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
wxString wxTextCtrl::GetTextToShow(const wxString& text) const
|
||||
{
|
||||
wxString textShown;
|
||||
if ( IsPassword() )
|
||||
textShown = wxString(_T('*'), text.length());
|
||||
else
|
||||
textShown = text;
|
||||
|
||||
return textShown;
|
||||
}
|
||||
|
||||
void wxTextCtrl::DrawTextLine(wxDC& dc, const wxRect& rect,
|
||||
const wxString& text,
|
||||
long selStart, long selEnd)
|
||||
{
|
||||
if ( selStart == -1 )
|
||||
{
|
||||
// just draw it as is
|
||||
dc.DrawText(text, rect.x, rect.y);
|
||||
}
|
||||
else // we have selection
|
||||
{
|
||||
wxCoord width,
|
||||
x = rect.x;
|
||||
|
||||
// draw the part before selection
|
||||
wxString s(text, (size_t)selStart);
|
||||
if ( !s.empty() )
|
||||
{
|
||||
dc.DrawText(s, x, rect.y);
|
||||
|
||||
dc.GetTextExtent(s, &width, NULL);
|
||||
x += width;
|
||||
}
|
||||
|
||||
// draw the selection itself
|
||||
s = wxString(text.c_str() + selStart, text.c_str() + selEnd);
|
||||
if ( !s.empty() )
|
||||
{
|
||||
wxColour colFg = dc.GetTextForeground();
|
||||
dc.SetTextForeground(wxTHEME_COLOUR(HIGHLIGHT_TEXT));
|
||||
dc.SetTextBackground(wxTHEME_COLOUR(HIGHLIGHT));
|
||||
dc.SetBackgroundMode(wxSOLID);
|
||||
|
||||
dc.DrawText(s, x, rect.y);
|
||||
dc.GetTextExtent(s, &width, NULL);
|
||||
x += width;
|
||||
|
||||
dc.SetBackgroundMode(wxTRANSPARENT);
|
||||
dc.SetTextForeground(colFg);
|
||||
}
|
||||
|
||||
// draw the final part
|
||||
s = text.c_str() + selEnd;
|
||||
if ( !s.empty() )
|
||||
{
|
||||
dc.DrawText(s, x, rect.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: don't redraw the parts of line which are not visible because they're
|
||||
// outside the window (because of horz scrolling)
|
||||
void wxTextCtrl::DoDraw(wxControlRenderer *renderer)
|
||||
{
|
||||
if ( IsSingleLine() )
|
||||
wxCaretSuspend cs(this);
|
||||
|
||||
wxDC& dc = renderer->GetDC();
|
||||
dc.SetFont(GetFont());
|
||||
dc.SetTextForeground(GetForegroundColour());
|
||||
DoPrepareDC(dc);
|
||||
|
||||
// calculate the lines which must be redrawn
|
||||
wxRegion rgnUpdate = GetUpdateRegion();
|
||||
wxRect rectTextArea = m_rectText;
|
||||
wxPoint pt = GetClientAreaOrigin();
|
||||
rectTextArea.x += pt.x;
|
||||
rectTextArea.y += pt.y;
|
||||
rgnUpdate.Intersect(rectTextArea);
|
||||
wxRect rectUpdate = rgnUpdate.GetBox();
|
||||
|
||||
pt = rectUpdate.GetPosition();
|
||||
long colStart, lineStart;
|
||||
(void)HitTest(pt, &colStart, &lineStart);
|
||||
|
||||
long colEnd, lineEnd;
|
||||
pt.x += rectUpdate.width;
|
||||
pt.y += rectUpdate.height;
|
||||
(void)HitTest(pt, &colEnd, &lineEnd);
|
||||
|
||||
wxLogTrace(_T("text"), _T("Redrawing positions (%ld, %ld)-(%ld, %ld)"),
|
||||
colStart, lineStart, colEnd, lineEnd);
|
||||
|
||||
// prepare for drawing
|
||||
wxRect rectText;
|
||||
rectText.x = m_rectText.x;
|
||||
rectText.width = m_rectText.width;
|
||||
rectText.height = GetCharHeight();
|
||||
rectText.y = m_rectText.y + lineStart*rectText.height;
|
||||
|
||||
wxString text;
|
||||
int selStart, selEnd;
|
||||
|
||||
// draw the part of the first line
|
||||
GetSelectedPartOfLine(lineStart, &selStart, &selEnd);
|
||||
wxString textLine = GetLineText(lineStart);
|
||||
text = textLine.c_str() + colStart;
|
||||
if ( lineEnd == lineStart )
|
||||
{
|
||||
// just redraw everything
|
||||
renderer->DrawTextLine(m_value, (int)m_selStart, (int)m_selEnd);
|
||||
text.Truncate(colEnd - colStart + 1);
|
||||
}
|
||||
else
|
||||
|
||||
rectText.x += GetTextWidth(textLine.Left(colStart));
|
||||
rectText.width = GetTextWidth(text);
|
||||
DrawTextLine(dc, rectText, GetTextToShow(text), selStart, selEnd);
|
||||
rectText.y += rectText.height;
|
||||
|
||||
wxLogTrace(_T("text"), _T("Start line: positions %ld-%ld redrawn."),
|
||||
colStart,
|
||||
lineEnd == lineStart ? colEnd : GetLineLength(lineStart));
|
||||
|
||||
// all intermediate lines must be redrawn completely
|
||||
rectText.x = m_rectText.x;
|
||||
rectText.width = m_rectText.width;
|
||||
for ( long line = lineStart + 1; line < lineEnd; line++ )
|
||||
{
|
||||
// TODO
|
||||
GetSelectedPartOfLine(line, &selStart, &selEnd);
|
||||
|
||||
DrawTextLine(dc, rectText, GetTextToShow(GetLineText(line)),
|
||||
selStart, selEnd);
|
||||
rectText.y += rectText.height;
|
||||
|
||||
wxLogTrace(_T("text"), _T("Middle line %ld entirely redrawn."), line);
|
||||
}
|
||||
|
||||
// and draw the part of the last line if it hadn't been drawn yet
|
||||
if ( lineEnd != lineStart )
|
||||
{
|
||||
GetSelectedPartOfLine(lineEnd, &selStart, &selEnd);
|
||||
text = GetLineText(lineEnd).Left(colEnd);
|
||||
|
||||
rectText.y += rectText.height;
|
||||
DrawTextLine(dc, rectText, GetTextToShow(text),
|
||||
selStart, selEnd);
|
||||
|
||||
wxLogTrace(_T("text"), _T("End line: positions 0-%ld redrawn."),
|
||||
colEnd);
|
||||
}
|
||||
}
|
||||
|
||||
wxCoord wxTextCtrl::GetCaretPosition() const
|
||||
{
|
||||
wxString textBeforeCaret(GetLineText(m_curLine), (size_t)m_curRow);
|
||||
|
||||
return GetTextWidth(textBeforeCaret);
|
||||
}
|
||||
|
||||
void wxTextCtrl::ShowCaret(bool show)
|
||||
@@ -562,16 +1084,9 @@ void wxTextCtrl::ShowCaret(bool show)
|
||||
{
|
||||
caret->Show(show);
|
||||
|
||||
if ( caret->IsVisible() )
|
||||
{
|
||||
// position it correctly
|
||||
wxString textBeforeCaret(m_value, (size_t)m_curPos);
|
||||
wxCoord x;
|
||||
GetTextExtent(textBeforeCaret, &x, NULL);
|
||||
|
||||
// FIXME: use renderer
|
||||
caret->Move(x + 3, 3);
|
||||
}
|
||||
// position it correctly
|
||||
caret->Move(m_rectText.x + GetCaretPosition() - m_ofsHorz,
|
||||
m_rectText.y + m_curLine*GetCharHeight());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -635,14 +1150,15 @@ bool wxTextCtrl::PerformAction(const wxControlAction& actionOrig,
|
||||
{
|
||||
if ( !strArg.empty() )
|
||||
{
|
||||
// replace the selection with the new text
|
||||
RemoveSelection();
|
||||
|
||||
WriteText(strArg);
|
||||
|
||||
textChanged = TRUE;
|
||||
}
|
||||
}
|
||||
else if ( action == wxACTION_TEXT_SEL_WORD )
|
||||
{
|
||||
SetSelection(GetWordStart(), GetWordEnd());
|
||||
}
|
||||
else if ( action == wxACTION_TEXT_ANCHOR_SEL )
|
||||
{
|
||||
newPos = numArg;
|
||||
@@ -651,6 +1167,18 @@ bool wxTextCtrl::PerformAction(const wxControlAction& actionOrig,
|
||||
{
|
||||
SetSelection(m_selAnchor, numArg);
|
||||
}
|
||||
else if ( action == wxACTION_TEXT_COPY )
|
||||
{
|
||||
Copy();
|
||||
}
|
||||
else if ( action == wxACTION_TEXT_CUT )
|
||||
{
|
||||
Cut();
|
||||
}
|
||||
else if ( action == wxACTION_TEXT_PASTE )
|
||||
{
|
||||
Paste();
|
||||
}
|
||||
else
|
||||
{
|
||||
return wxControl::PerformAction(action, numArg, strArg);
|
||||
@@ -719,7 +1247,7 @@ void wxTextCtrl::OnChar(wxKeyEvent& event)
|
||||
if ( !event.HasModifiers() )
|
||||
{
|
||||
int keycode = event.GetKeyCode();
|
||||
if ( keycode != WXK_DELETE && keycode != WXK_BACK )
|
||||
if ( keycode < 255 && keycode != WXK_DELETE && keycode != WXK_BACK )
|
||||
{
|
||||
PerformAction(wxACTION_TEXT_INSERT, -1, (wxChar)keycode);
|
||||
|
||||
@@ -745,105 +1273,8 @@ wxStdTextCtrlInputHandler::wxStdTextCtrlInputHandler(wxInputHandler *inphand)
|
||||
long wxStdTextCtrlInputHandler::HitTest(const wxTextCtrl *text,
|
||||
const wxPoint& pos)
|
||||
{
|
||||
int x, y;
|
||||
text->CalcUnscrolledPosition(pos.x, pos.y, &x, &y);
|
||||
|
||||
// row calculation is simple as we assume that all lines have the same
|
||||
// height
|
||||
int row = y / text->GetCharHeight();
|
||||
int rowMax = text->GetNumberOfLines() - 1;
|
||||
if ( row > rowMax )
|
||||
{
|
||||
// clicking below the text is the same as clicking on the last line
|
||||
row = rowMax;
|
||||
}
|
||||
|
||||
// if the line is empty, the column can only be the first one
|
||||
wxString line = text->GetLineText(row);
|
||||
int col;
|
||||
wxClientDC dc(wxConstCast(text, wxTextCtrl));
|
||||
dc.SetFont(text->GetFont());
|
||||
|
||||
wxCoord width;
|
||||
dc.GetTextExtent(line, &width, NULL);
|
||||
if ( x >= width )
|
||||
{
|
||||
// clicking beyond the end of line is equivalent to clicking at
|
||||
// the end of it
|
||||
col = line.length();
|
||||
}
|
||||
else // we're inside the line
|
||||
{
|
||||
// now calculate the column: first, approximate it with fixed-width
|
||||
// value and then calculate the correct value iteratively: note that
|
||||
// we use the first character of the line instead of (average)
|
||||
// GetCharWidth(): it is common to have lines of dashes, for example,
|
||||
// and this should give us much better approximation in such case
|
||||
dc.GetTextExtent(line[0], &width, NULL);
|
||||
col = x / width;
|
||||
|
||||
// matchDir is -1 if we must move left, +1 to move right and 0 when
|
||||
// we're exactly on the character we need
|
||||
int matchDir = 0;
|
||||
for ( ;; )
|
||||
{
|
||||
// check that we didn't go beyond the line boundary
|
||||
if ( col < 0 )
|
||||
{
|
||||
col = 0;
|
||||
break;
|
||||
}
|
||||
if ( col > line.length() )
|
||||
{
|
||||
col = line.length();
|
||||
break;
|
||||
}
|
||||
|
||||
wxString strBefore(line, (size_t)col);
|
||||
dc.GetTextExtent(strBefore, &width, NULL);
|
||||
if ( width >= x )
|
||||
{
|
||||
if ( matchDir == 1 )
|
||||
{
|
||||
// we were going to the right and, finally, moved beyond
|
||||
// the original position: so the current is the next after
|
||||
// the correct one
|
||||
col--;
|
||||
|
||||
break;
|
||||
}
|
||||
else if ( matchDir == 0 )
|
||||
{
|
||||
// we just started iterating, now we know that we should
|
||||
// move to the left
|
||||
matchDir = -1;
|
||||
}
|
||||
//else: we are still to the right of the target, continue
|
||||
}
|
||||
else // width < x
|
||||
{
|
||||
// same logic as above ...
|
||||
if ( matchDir == -1 )
|
||||
{
|
||||
// ... except that we don't need to back track
|
||||
break;
|
||||
}
|
||||
else if ( matchDir == 0 )
|
||||
{
|
||||
// go to the right
|
||||
matchDir = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// this is not supposed to happen
|
||||
wxASSERT_MSG( matchDir, _T("logic error in wxTextCtrl::HitTest") );
|
||||
|
||||
if ( matchDir == 1 )
|
||||
col++;
|
||||
else
|
||||
col--;
|
||||
}
|
||||
}
|
||||
long col, row;
|
||||
(void)text->HitTest(pos, &col, &row);
|
||||
|
||||
return text->XYToPosition(col, row);
|
||||
}
|
||||
@@ -899,6 +1330,24 @@ bool wxStdTextCtrlInputHandler::HandleKey(wxControl *control,
|
||||
// reset the action as it could be already set to one of the
|
||||
// prefixes
|
||||
action = wxACTION_NONE;
|
||||
|
||||
if ( ctrlDown )
|
||||
{
|
||||
switch ( keycode )
|
||||
{
|
||||
case 'C':
|
||||
action = wxACTION_TEXT_COPY;
|
||||
break;
|
||||
|
||||
case 'V':
|
||||
action = wxACTION_TEXT_PASTE;
|
||||
break;
|
||||
|
||||
case 'X':
|
||||
action = wxACTION_TEXT_CUT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( action != wxACTION_NONE )
|
||||
@@ -933,7 +1382,8 @@ bool wxStdTextCtrlInputHandler::HandleMouse(wxControl *control,
|
||||
}
|
||||
else if ( event.LeftDClick() )
|
||||
{
|
||||
// TODO: select the word the cursor is on
|
||||
// select the word the cursor is on
|
||||
control->PerformAction(wxACTION_TEXT_SEL_WORD);
|
||||
}
|
||||
else if ( event.LeftUp() )
|
||||
{
|
||||
|
Reference in New Issue
Block a user