diff --git a/TODO b/TODO index 1104a55590..d195b4cf4a 100644 --- a/TODO +++ b/TODO @@ -16,7 +16,6 @@ wxTextCtrl *! display corrupted when typing text in quickly (caret problem?) * caret leaves traces under wxGTK -* scrollbars don't disappear after deleting long line ? text ctrl display pb when text is truncated * too much is refreshed when double clicking (word select) @@ -52,6 +51,7 @@ All + DoDraw should iterate over update region instead of using bounding box + listbox: horz scrollbar doesn't appear + wxTextCtrl update rect overlaps with horz scrollbar ++ scrollbars don't disappear after deleting long line MSW diff --git a/include/wx/univ/textctrl.h b/include/wx/univ/textctrl.h index 947503dd0d..360eb26953 100644 --- a/include/wx/univ/textctrl.h +++ b/include/wx/univ/textctrl.h @@ -300,6 +300,9 @@ protected: // starts for wxTE_PASSWORD control wxString GetTextToShow(const wxString& text) const; + // find the number of characters of a line before it wraps at given width + size_t GetPartOfWrappedLine(const wxChar* text, wxCoord width) const; + // get the start and end of the selection for this line: if the line is // outside the selection, both will be -1 and FALSE will be returned bool GetSelectedPartOfLine(long line, int *start, int *end) const; @@ -331,6 +334,15 @@ protected: // update the max width after the given line was modified void UpdateMaxWidth(long line); + // HitTest2() is more efficient than 2 consecutive HitTest()s with the same + // line (i.e. y) and it also returns the offset of the starting position in + // pixels + wxTextCtrlHitTestResult HitTest2(wxCoord y, + wxCoord x1, wxCoord x2, + long *row, + long *colStart, long *colEnd, + wxCoord *ofsStart) const; + // event handlers void OnIdle(wxIdleEvent& event); void OnChar(wxKeyEvent& event); diff --git a/samples/textctrl/texttest.cpp b/samples/textctrl/texttest.cpp index 310018d48c..2f2e74465e 100644 --- a/samples/textctrl/texttest.cpp +++ b/samples/textctrl/texttest.cpp @@ -68,30 +68,29 @@ enum TextTest_Insert, TextTest_Clear, -#if 0 - TextTest_AddText, - TextTest_AddSeveral, - TextTest_AddMany, - TextTest_Change, - TextTest_ChangeText, - TextTest_Delete, - TextTest_DeleteText, - TextTest_DeleteSel, -#endif // 0 - TextTest_Password, - TextTest_HScroll, + TextTest_WrapLines, TextTest_Textctrl, TextTest_Quit }; // textctrl line number radiobox values -enum +enum TextLines { TextLines_Single, TextLines_Multi }; +// default values for the controls +static const struct ControlValues +{ + TextLines textLines; + bool password; + bool wraplines; + bool readonly; +} DEFAULTS = + { TextLines_Multi, FALSE, TRUE, FALSE }; + // ---------------------------------------------------------------------------- // our classes // ---------------------------------------------------------------------------- @@ -135,14 +134,6 @@ protected: void OnButtonAdd(wxCommandEvent& event); void OnButtonClear(wxCommandEvent& event); -#if 0 - void OnButtonChange(wxCommandEvent& event); - void OnButtonDelete(wxCommandEvent& event); - void OnButtonDeleteSel(wxCommandEvent& event); - void OnButtonAddSeveral(wxCommandEvent& event); - void OnButtonAddMany(wxCommandEvent& event); -#endif // 0 - void OnButtonQuit(wxCommandEvent& event); void OnText(wxCommandEvent& event); @@ -152,14 +143,8 @@ protected: void OnUpdateUIClearButton(wxUpdateUIEvent& event); -#if 0 - void OnUpdateUIAddSeveral(wxUpdateUIEvent& event); - void OnUpdateUIDeleteButton(wxUpdateUIEvent& event); - void OnUpdateUIDeleteSelButton(wxUpdateUIEvent& event); -#endif - void OnUpdateUIPasswordCheckbox(wxUpdateUIEvent& event); - void OnUpdateUIHScrollCheckbox(wxUpdateUIEvent& event); + void OnUpdateUIWrapLinesCheckbox(wxUpdateUIEvent& event); void OnUpdateUIResetButton(wxUpdateUIEvent& event); @@ -188,7 +173,7 @@ protected: // the checkboxes controlling text ctrl styles wxCheckBox *m_chkPassword, - *m_chkHScroll, + *m_chkWrapLines, *m_chkReadonly; // the textctrl itself and the sizer it is in @@ -200,6 +185,8 @@ protected: // the information text zones wxTextCtrl *m_textPosCur, + *m_textRowCur, + *m_textColCur, *m_textPosLast, *m_textSelFrom, *m_textSelTo; @@ -244,30 +231,10 @@ BEGIN_EVENT_TABLE(TextTestFrame, wxFrame) EVT_BUTTON(TextTest_Add, TextTestFrame::OnButtonAdd) EVT_BUTTON(TextTest_Insert, TextTestFrame::OnButtonInsert) -#if 0 - EVT_BUTTON(TextTest_Change, TextTestFrame::OnButtonChange) - EVT_BUTTON(TextTest_Delete, TextTestFrame::OnButtonDelete) - EVT_BUTTON(TextTest_DeleteSel, TextTestFrame::OnButtonDeleteSel) - EVT_BUTTON(TextTest_AddSeveral, TextTestFrame::OnButtonAddSeveral) - EVT_BUTTON(TextTest_AddMany, TextTestFrame::OnButtonAddMany) - - EVT_TEXT_ENTER(TextTest_AddText, TextTestFrame::OnButtonAdd) - EVT_TEXT_ENTER(TextTest_DeleteText, TextTestFrame::OnButtonDelete) -#endif // 0 - EVT_UPDATE_UI(TextTest_Clear, TextTestFrame::OnUpdateUIClearButton) -#if 0 - EVT_UPDATE_UI(TextTest_AddSeveral, TextTestFrame::OnUpdateUIAddSeveral) - EVT_UPDATE_UI(TextTest_DeleteText, TextTestFrame::OnUpdateUIClearButton) - EVT_UPDATE_UI(TextTest_Delete, TextTestFrame::OnUpdateUIDeleteButton) - EVT_UPDATE_UI(TextTest_Change, TextTestFrame::OnUpdateUIDeleteSelButton) - EVT_UPDATE_UI(TextTest_ChangeText, TextTestFrame::OnUpdateUIDeleteSelButton) - EVT_UPDATE_UI(TextTest_DeleteSel, TextTestFrame::OnUpdateUIDeleteSelButton) -#endif // 0 - EVT_UPDATE_UI(TextTest_Password, TextTestFrame::OnUpdateUIPasswordCheckbox) - EVT_UPDATE_UI(TextTest_HScroll, TextTestFrame::OnUpdateUIHScrollCheckbox) + EVT_UPDATE_UI(TextTest_WrapLines, TextTestFrame::OnUpdateUIWrapLinesCheckbox) EVT_UPDATE_UI(TextTest_Reset, TextTestFrame::OnUpdateUIResetButton) @@ -308,12 +275,14 @@ TextTestFrame::TextTestFrame(const wxString& title) m_radioTextLines = (wxRadioBox *)NULL; m_chkPassword = - m_chkHScroll = + m_chkWrapLines = m_chkReadonly = (wxCheckBox *)NULL; m_text = m_textLog = m_textPosCur = + m_textRowCur = + m_textColCur = m_textPosLast = m_textSelFrom = m_textSelTo = (wxTextCtrl *)NULL; @@ -352,7 +321,7 @@ TextTestFrame::TextTestFrame(const wxString& title) 1, wxRA_SPECIFY_COLS); m_chkPassword = new wxCheckBox(m_panel, TextTest_Password, _T("&Password control")); - m_chkHScroll = new wxCheckBox(m_panel, TextTest_HScroll, _T("&Horz scrollbar")); + m_chkWrapLines = new wxCheckBox(m_panel, TextTest_WrapLines, _T("&Horz scrollbar")); m_chkReadonly = new wxCheckBox(m_panel, -1, _T("&Read-only mode")); sizerLeft = new wxStaticBoxSizer(box, wxVERTICAL); @@ -360,7 +329,7 @@ TextTestFrame::TextTestFrame(const wxString& title) sizerLeft->Add(m_radioTextLines, 0, wxGROW | wxALL, 5); sizerLeft->Add(5, 5, 0, wxGROW | wxALL, 5); // spacer sizerLeft->Add(m_chkPassword, 0, wxLEFT | wxRIGHT, 5); - sizerLeft->Add(m_chkHScroll, 0, wxLEFT | wxRIGHT, 5); + sizerLeft->Add(m_chkWrapLines, 0, wxLEFT | wxRIGHT, 5); sizerLeft->Add(m_chkReadonly, 0, wxLEFT | wxRIGHT, 5); wxButton *btn = new wxButton(m_panel, TextTest_Reset, _T("&Reset")); @@ -379,44 +348,32 @@ TextTestFrame::TextTestFrame(const wxString& title) btn = new wxButton(m_panel, TextTest_Clear, _T("&Clear")); sizerMiddleUp->Add(btn, 0, wxALL | wxGROW, 5); -#if 0 - btn = new wxButton(m_panel, TextTest_AddSeveral, _T("&Insert a few strings")); - sizerMiddle->Add(btn, 0, wxALL | wxGROW, 5); - - btn = new wxButton(m_panel, TextTest_AddMany, _T("Add &many strings")); - sizerMiddle->Add(btn, 0, wxALL | wxGROW, 5); - - sizerRow = new wxBoxSizer(wxHORIZONTAL); - btn = new wxButton(m_panel, TextTest_Change, _T("C&hange current")); - m_textChange = new wxTextCtrl(m_panel, TextTest_ChangeText, _T("")); - sizerRow->Add(btn, 0, wxRIGHT, 5); - sizerRow->Add(m_textChange, 1, wxLEFT, 5); - sizerMiddle->Add(sizerRow, 0, wxALL | wxGROW, 5); - - sizerRow = new wxBoxSizer(wxHORIZONTAL); - btn = new wxButton(m_panel, TextTest_Delete, _T("&Delete this item")); - m_textDelete = new wxTextCtrl(m_panel, TextTest_DeleteText, _T("")); - sizerRow->Add(btn, 0, wxRIGHT, 5); - sizerRow->Add(m_textDelete, 1, wxLEFT, 5); - sizerMiddle->Add(sizerRow, 0, wxALL | wxGROW, 5); - - btn = new wxButton(m_panel, TextTest_DeleteSel, _T("Delete &selection")); - sizerMiddle->Add(btn, 0, wxALL | wxGROW, 5); -#endif // 0 - wxStaticBox *box4 = new wxStaticBox(m_panel, -1, _T("&Info:")); wxSizer *sizerMiddleDown = new wxStaticBoxSizer(box4, wxVERTICAL); m_textPosCur = CreateInfoText(); - sizerMiddleDown->Add - ( - CreateTextWithLabelSizer - ( - _T("Current position:"), - m_textPosCur - ), - 0, wxALL | wxGROW, 5 - ); + m_textRowCur = CreateInfoText(); + m_textColCur = CreateInfoText(); + wxSizer *sizerRow = new wxBoxSizer(wxHORIZONTAL); + sizerRow->Add(CreateTextWithLabelSizer + ( + _T("Current pos:"), + m_textPosCur + ), + 0, wxRIGHT | wxGROW, 5); + sizerRow->Add(CreateTextWithLabelSizer + ( + _T("Col:"), + m_textColCur + ), + 0, wxRIGHT | wxLEFT | wxGROW, 5); + sizerRow->Add(CreateTextWithLabelSizer + ( + _T("Row:"), + m_textRowCur + ), + 0, wxLEFT | wxGROW, 5); + sizerMiddleDown->Add(sizerRow, 0, wxALL | wxGROW, 5); m_textPosLast = CreateInfoText(); sizerMiddleDown->Add @@ -449,9 +406,15 @@ TextTestFrame::TextTestFrame(const wxString& title) // right pane wxStaticBox *box3 = new wxStaticBox(m_panel, -1, _T("&Text:")); m_sizerText = new wxStaticBoxSizer(box3, wxHORIZONTAL); - m_text = new wxTextCtrl(m_panel, TextTest_Textctrl, _T("Hello, world!")); - m_sizerText->Add(m_text, 1, wxALL | wxALIGN_CENTRE_VERTICAL, 5); - m_sizerText->SetMinSize(250, 300); + Reset(); + CreateText(); + m_sizerText->SetMinSize(250, +#ifdef __WXGTK__ + 300 +#else + 0 +#endif + ); // the 3 panes panes compose the upper part of the window sizerUp->Add(sizerLeft, 0, wxGROW | (wxALL & ~wxLEFT), 10); @@ -487,9 +450,6 @@ TextTestFrame::TextTestFrame(const wxString& title) sizerTop->Add(0, 5, 0, wxGROW); // spacer in between sizerTop->Add(sizerDown, 0, wxGROW | (wxALL & ~wxTOP), 10); - // final initialization - Reset(); - m_panel->SetAutoLayout(TRUE); m_panel->SetSizer(sizerTop); @@ -551,10 +511,10 @@ wxSizer *TextTestFrame::CreateTextWithLabelSizer(const wxString& label, void TextTestFrame::Reset() { - m_radioTextLines->SetSelection(TextLines_Single); - m_chkPassword->SetValue(FALSE); - m_chkHScroll->SetValue(TRUE); - m_chkReadonly->SetValue(FALSE); + m_radioTextLines->SetSelection(DEFAULTS.textLines); + m_chkPassword->SetValue(DEFAULTS.password); + m_chkWrapLines->SetValue(DEFAULTS.wraplines); + m_chkReadonly->SetValue(DEFAULTS.readonly); } void TextTestFrame::CreateText() @@ -578,7 +538,7 @@ void TextTestFrame::CreateText() flags |= wxTE_PASSWORD; if ( m_chkReadonly->GetValue() ) flags |= wxTE_READONLY; - if ( m_chkHScroll->GetValue() ) + if ( !m_chkWrapLines->GetValue() ) flags |= wxHSCROLL; wxString valueOld; @@ -589,6 +549,10 @@ void TextTestFrame::CreateText() m_sizerText->Remove(m_text); delete m_text; } + else + { + valueOld = _T("Hello, Universe!"); + } m_text = new wxTextCtrl(m_panel, TextTest_Textctrl, valueOld, @@ -614,7 +578,15 @@ void TextTestFrame::OnIdle(wxIdleEvent& WXUNUSED(event)) if ( posCur != m_posCur ) { m_textPosCur->Clear(); + m_textRowCur->Clear(); + m_textColCur->Clear(); + + long col, row; + m_text->PositionToXY(posCur, &col, &row); + *m_textPosCur << posCur; + *m_textRowCur << row; + *m_textColCur << col; m_posCur = posCur; } @@ -689,98 +661,12 @@ void TextTestFrame::OnButtonClear(wxCommandEvent& WXUNUSED(event)) m_text->SetFocus(); } -#if 0 -void TextTestFrame::OnButtonChange(wxCommandEvent& WXUNUSED(event)) -{ - wxArrayInt selections; - int count = m_text->GetSelections(selections); - wxString s = m_textChange->GetValue(); - for ( int n = 0; n < count; n++ ) - { - m_text->SetString(selections[n], s); - } -} - -void TextTestFrame::OnButtonDelete(wxCommandEvent& WXUNUSED(event)) -{ - unsigned long n; - if ( !m_textDelete->GetValue().ToULong(&n) || - (n >= (unsigned)m_text->GetCount()) ) - { - return; - } - - m_text->Delete(n); -} - -void TextTestFrame::OnButtonDeleteSel(wxCommandEvent& WXUNUSED(event)) -{ - wxArrayInt selections; - int n = m_text->GetSelections(selections); - while ( n > 0 ) - { - m_text->Delete(selections[--n]); - } -} - -void TextTestFrame::OnButtonAdd(wxCommandEvent& event) -{ - static size_t s_item = 0; - - wxString s = m_textAdd->GetValue(); - if ( !m_textAdd->IsModified() ) - { - // update the default string - m_textAdd->SetValue(wxString::Format(_T("test item %u"), ++s_item)); - } - - m_text->Append(s); -} - -void TextTestFrame::OnButtonAddMany(wxCommandEvent& WXUNUSED(event)) -{ - // "many" means 1000 here - for ( size_t n = 0; n < 1000; n++ ) - { - m_text->Append(wxString::Format(_T("item #%u"), n)); - } -} - -void TextTestFrame::OnButtonAddSeveral(wxCommandEvent& event) -{ - wxArrayString items; - items.Add(_T("First")); - items.Add(_T("another one")); - items.Add(_T("and the last (very very very very very very very very very very long) one")); - m_text->InsertItems(items, 0); -} - -void TextTestFrame::OnUpdateUIDeleteButton(wxUpdateUIEvent& event) -{ - unsigned long n; - event.Enable(m_textDelete->GetValue().ToULong(&n) && - (n < (unsigned)m_text->GetCount())); -} - -void TextTestFrame::OnUpdateUIDeleteSelButton(wxUpdateUIEvent& event) -{ - wxArrayInt selections; - event.Enable(m_text->GetSelections(selections) != 0); -} - -void TextTestFrame::OnUpdateUIAddSeveral(wxUpdateUIEvent& event) -{ - event.Enable(!(m_text->GetWindowStyle() & wxLB_SORT)); -} - -#endif // 0 - void TextTestFrame::OnUpdateUIClearButton(wxUpdateUIEvent& event) { event.Enable(!m_text->GetValue().empty()); } -void TextTestFrame::OnUpdateUIHScrollCheckbox(wxUpdateUIEvent& event) +void TextTestFrame::OnUpdateUIWrapLinesCheckbox(wxUpdateUIEvent& event) { event.Enable( !IsSingleLine() ); } @@ -793,14 +679,24 @@ void TextTestFrame::OnUpdateUIPasswordCheckbox(wxUpdateUIEvent& event) void TextTestFrame::OnUpdateUIResetButton(wxUpdateUIEvent& event) { - event.Enable( (m_radioTextLines->GetSelection() != TextLines_Single) || - m_chkReadonly->GetValue() || - m_chkPassword->GetValue() || - !m_chkHScroll->GetValue() ); + event.Enable( (m_radioTextLines->GetSelection() != DEFAULTS.textLines) || + (m_chkReadonly->GetValue() != DEFAULTS.readonly) || + (m_chkPassword->GetValue() != DEFAULTS.password) || + (m_chkWrapLines->GetValue() != DEFAULTS.wraplines) ); } void TextTestFrame::OnText(wxCommandEvent& event) { + // small hack to suppress the very first message: by then the logging is + // not yet redirected and so initial setting of the text value results in + // an annoying message box + static bool s_firstTime = TRUE; + if ( s_firstTime ) + { + s_firstTime = FALSE; + return; + } + wxLogMessage(_T("Text ctrl value changed")); } diff --git a/src/univ/textctrl.cpp b/src/univ/textctrl.cpp index 70560d30e8..2a7d28389d 100644 --- a/src/univ/textctrl.cpp +++ b/src/univ/textctrl.cpp @@ -60,20 +60,23 @@ The same example for a control with line wrap assuming "Universe" is too long to fit on the same line with "Hello,": - pos: 0 1 2 3 4 5 6 + pos: 0 1 2 3 4 5 H e l l o , line 0 (row 0) col: 0 1 2 3 4 5 - 1 1 1 1 1 1 1 - pos: 7 8 9 0 1 2 3 4 5 6 - U n i v e r s e ! line 0 (row 1) - col: 6 7 8 9 1 1 1 1 1 - 0 1 2 3 4 + 1 1 1 1 1 1 1 + pos: 6 7 8 9 0 1 2 3 4 5 6 + U n i v e r s e ! line 0 (row 1) + col: 6 7 8 9 1 1 1 1 1 1 + 0 1 2 3 4 5 - (line 1 == row 2 same as above) + (line 1 == row 2 same as above) - Note that now columns are offset relative to positions as the positions 6 - and 7 correspond to the same character. + Note that there is still the same number of columns and positions and that + there is no (logical) position at the end of the first ROW. This position + is identified with the preceding one (which is not how Windows does it: it + identifies it with the next one, i.e. the first position of the next line, + but much more logical IMHO). */ // ============================================================================ @@ -1862,6 +1865,25 @@ wxRect wxTextCtrl::GetRealTextArea() const return rectText; } +size_t wxTextCtrl::GetPartOfWrappedLine(const wxChar* text, + wxCoord width) const +{ + wxTextCtrl *self = wxConstCast(this, wxTextCtrl); + wxClientDC dc(self); + dc.SetFont(GetFont()); + self->DoPrepareDC(dc); + + // the text which we can keep in this ROW + wxString str; + for ( wxCoord w = 0; w < width; ) + { + str += *text++; + dc.GetTextExtent(str, &w, NULL); + } + + return str.length(); +} + wxTextCtrlHitTestResult wxTextCtrl::HitTestLine(const wxString& line, wxCoord x, long *colOut) const @@ -2018,63 +2040,80 @@ wxTextCtrlHitTestResult wxTextCtrl::HitTest(const wxPoint& pos, // calculate the row int row; - if ( IsSingleLine() ) - { - // there is only one row anyhow - row = 0; - } - else // multi line - { - int rowMax = GetNumberOfLines() - 1; - int hLine = GetCharHeight(); + size_t ofsLineStart = 0; // used only for WrapLines() case - if ( WrapLines() ) + int hLine = GetCharHeight(); + int rowMax = GetNumberOfLines() - 1; + if ( IsSingleLine() || !WrapLines() ) + { + // in this case row calculation is simple as all lines have the + // same height + row = y / hLine; + if ( row > rowMax ) { - // one line can take several rows in this case, so we need to - // rescan the text + // clicking below the text is the same as clicking on the last + // line + row = rowMax; - // OPT: as for horz hit testing we might approximate the result - // with y / hLine first and then scan from there, this is - // probably much more efficient when there is a lot of text - - int yCur = 0; - wxCoord wLine = m_rectText.w; - for ( row = 0; (yCur < y) && (row <= rowMax); row++ ) - { - wxCoord widthLineTotal = GetTextWidth(GetLineText(row)); - int nRowsPerLine = widthLineTotal == 0 - ? 1 - : (widthLineTotal + wLine - 1) / wLine; - yCur += nRowsPerLine*hLine; - } - - if ( row > rowMax ) - { - row = rowMax; - - res = wxTE_HT_AFTER; - } + res = wxTE_HT_AFTER; } - else // no line wrap + else if ( row < 0 ) { - // in this case row calculation is simple as we all lines have the - // same height - row = y / hLine; - if ( row > rowMax ) - { - // clicking below the text is the same as clicking on the last - // line - row = rowMax; + // and clicking before it is the same as clicking on the first + // one + row = 0; - res = wxTE_HT_AFTER; + res = wxTE_HT_BEFORE; + } + } + else // multline control with line wrap + { + // one line can take several rows in this case, so we need to + // rescan the text + + // OPT: as for horz hit testing we might approximate the result + // with y / hLine first and then scan from there, this is + // probably much more efficient when there is a lot of text + + wxString textLine; + row = 0; + int nRowInLine = 0, + nRowsPerLine = 0; + wxCoord wLine = m_rectText.width; + for ( int yCur = 0; yCur < y; yCur += hLine ) + { + if ( nRowInLine == nRowsPerLine ) + { + // pass to the next line + row++; + if ( row > rowMax ) + { + // no next line + row = rowMax; + + res = wxTE_HT_AFTER; + break; + } + + textLine = GetLineText(row); + wxCoord widthLineTotal = GetTextWidth(textLine); + nRowsPerLine = widthLineTotal == 0 + ? 1 + : (widthLineTotal + wLine - 1) / wLine; + + // the offset of the start of the ROW in which y is from the + // start of this LINE + ofsLineStart = 0; } - else if ( row < 0 ) + else { - // and clicking before it is the same as clicking on the first - // one - row = 0; - - res = wxTE_HT_BEFORE; + // pass to the next row in this line + ofsLineStart += GetPartOfWrappedLine + ( + textLine.c_str() + ofsLineStart, + wLine + ); + nRowInLine++; } } } @@ -2082,10 +2121,17 @@ wxTextCtrlHitTestResult wxTextCtrl::HitTest(const wxPoint& pos, if ( res == wxTE_HT_ON_TEXT ) { // now find the position in the line - wxString line = GetLineText(row); + wxString line; if ( ofsLineStart ) { - // + // look in this row only, not in whole line (well, we leave the + // tail unchanged, but it shouldn't hurt) + line = GetLineText(row).c_str() + ofsLineStart; + } + else + { + // just take the whole string + line = GetLineText(row); } res = HitTestLine(GetTextToShow(line), x, colOut); @@ -2116,6 +2162,44 @@ wxTextCtrlHitTestResult wxTextCtrl::HitTest(const wxPoint& pos, return res; } +// TODO: implement HitTest() via HitTest2(), not the other way round! + +wxTextCtrlHitTestResult wxTextCtrl::HitTest2(wxCoord y, + wxCoord x1, + wxCoord x2, + long *row, + long *colStart, + long *colEnd, + wxCoord *ofsStart) const +{ + wxTextCtrlHitTestResult htr = HitTest(wxPoint(x1, y), colStart, row); + if ( htr == wxTE_HT_AFTER ) + { + // if the start is after the text, the end is too + return wxTE_HT_AFTER; + } + + if ( colEnd ) + { + (void)HitTest(wxPoint(x2, y), colEnd, NULL); + } + + if ( ofsStart ) + { + long colStartRow; + (void)HitTest(wxPoint(GetRealTextArea().x + GetClientAreaOrigin().x, y), + &colStartRow, NULL); + wxASSERT_MSG( colStartRow <= *colStart, _T("are they on same line?") ); + + wxString s = GetLineText(*row); + + *ofsStart = GetTextWidth(s.Mid((size_t)colStartRow, + (size_t)(*colStart - colStartRow))); + } + + return htr; +} + // ---------------------------------------------------------------------------- // scrolling // ---------------------------------------------------------------------------- @@ -2371,7 +2455,7 @@ void wxTextCtrl::UpdateScrollbars() // is our width enough to show the longest line? wxCoord charWidth, maxWidth; bool showScrollbarX; - if ( GetWindowStyle() && wxHSCROLL ) + if ( !WrapLines() ) { charWidth = GetCharWidth(); maxWidth = GetMaxWidth(); @@ -2636,39 +2720,49 @@ void wxTextCtrl::DoDrawTextInRect(wxDC& dc, const wxRect& rectUpdate) } #endif // WXDEBUG_TEXT - // calculate the range of lines to refresh - wxPoint pt1 = rectUpdate.GetPosition(); - long colStart, lineStart; - (void)HitTest(pt1, &colStart, &lineStart); + // calculate the range lineStart..lineEnd of lines to redraw + wxPoint pt = rectUpdate.GetPosition(); + long lineStart; + (void)HitTest(pt, NULL, &lineStart); - wxPoint pt2 = pt1; - pt2.x += rectUpdate.width; - pt2.y += rectUpdate.height; - long colEnd, lineEnd; - (void)HitTest(pt2, &colEnd, &lineEnd); - - // pt1 and pt2 will run along the left and right update rect borders - // respectively from top to bottom (NB: they're in device coords) - pt2.y = pt1.y; + pt.y += rectUpdate.height; + long lineEnd; + (void)HitTest(pt, NULL, &lineEnd); // prepare for drawing + wxCoord hLine = GetCharHeight(); wxRect rectText; - rectText.height = GetCharHeight(); + rectText.height = hLine; rectText.y = m_rectText.y + lineStart*rectText.height; - // do draw the invalidated parts of each line + // these vars will be used for hit testing of the current row + wxCoord y = rectUpdate.y; + const wxCoord x1 = rectUpdate.x; + const wxCoord x2 = rectUpdate.x + rectUpdate.width; + + // do draw the invalidated parts of each line: note that we iterate here + // over ROWs, not over LINEs for ( long line = lineStart; line <= lineEnd; - line++, - rectText.y += rectText.height, - pt1.y += rectText.height, - pt2.y += rectText.height ) + rectText.y += hLine, + y += hLine ) { // calculate the update rect in text positions for this line - if ( HitTest(pt1, &colStart, NULL) == wxTE_HT_AFTER ) + long colStart, colEnd; + wxCoord ofsStart; + if ( HitTest2(y, x1, x2, + &line, &colStart, &colEnd, &ofsStart) == wxTE_HT_AFTER ) { + wxASSERT_MSG( line <= lineEnd, _T("how did we get that far?") ); + + if ( line == lineEnd ) + { + // we redrew everything + break; + } + // the update rect is beyond the end of line, no need to redraw - // anything + // anything on this line - but continue with the remaining ones continue; } @@ -2676,8 +2770,6 @@ void wxTextCtrl::DoDrawTextInRect(wxDC& dc, const wxRect& rectUpdate) if ( colStart < m_colStart ) colStart = m_colStart; - (void)HitTest(pt2, &colEnd, NULL); - // colEnd may be less than colStart if colStart was changed by the // assignment above if ( colEnd < colStart ) @@ -2735,7 +2827,7 @@ void wxTextCtrl::DoDrawTextInRect(wxDC& dc, const wxRect& rectUpdate) } // calculate the logical text coords - rectText.x = m_rectText.x + GetTextWidth(textLine.Left(colStart)); + rectText.x = m_rectText.x + ofsStart; rectText.width = GetTextWidth(text); // do draw the text diff --git a/src/univ/themes/win32.cpp b/src/univ/themes/win32.cpp index ac887692ab..97ade22eb0 100644 --- a/src/univ/themes/win32.cpp +++ b/src/univ/themes/win32.cpp @@ -701,7 +701,7 @@ wxColour wxWin32ColourScheme::GetBackground(wxWindow *win) const wxTextCtrl *text = wxDynamicCast(win, wxTextCtrl); if ( text ) { - if ( !text->IsEditable() ) + if ( !text->IsEnabled() ) // not IsEditable() col = Get(CONTROL); //else: execute code below } @@ -1736,7 +1736,7 @@ wxRect wxWin32Renderer::GetTextTotalArea(const wxTextCtrl *text, { // this is what Windows does wxRect rectTotal = rect; - rectTotal.Inflate(10); + rectTotal.Inflate(1); rectTotal.height++; return rectTotal; @@ -1748,7 +1748,7 @@ wxRect wxWin32Renderer::GetTextClientArea(const wxTextCtrl *text, // undo GetTextTotalArea() wxRect rectText = rect; rectText.height--; - rectText.Inflate(-10); + rectText.Inflate(-1); return rectText; }