X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/7fe8059f5825e58af0864e5933e43e5f0526bc76..c08ee0349dd4ed6d62f734fe576e47c7027e2b9e:/src/richtext/richtextctrl.cpp diff --git a/src/richtext/richtextctrl.cpp b/src/richtext/richtextctrl.cpp index 0dfa365185..b3cef4837e 100644 --- a/src/richtext/richtextctrl.cpp +++ b/src/richtext/richtextctrl.cpp @@ -1,5 +1,5 @@ ///////////////////////////////////////////////////////////////////////////// -// Name: richtext/richeditctrl.cpp +// Name: src/richtext/richeditctrl.cpp // Purpose: A rich edit control // Author: Julian Smart // Modified by: @@ -13,26 +13,28 @@ #include "wx/wxprec.h" #ifdef __BORLANDC__ - #pragma hdrstop + #pragma hdrstop #endif -#ifndef WX_PRECOMP - #include "wx/wx.h" -#endif +#if wxUSE_RICHTEXT -#include "wx/image.h" +#include "wx/richtext/richtextctrl.h" -#if wxUSE_RICHTEXT +#ifndef WX_PRECOMP + #include "wx/wx.h" + #include "wx/settings.h" +#endif #include "wx/textfile.h" #include "wx/ffile.h" -#include "wx/settings.h" #include "wx/filename.h" #include "wx/dcbuffer.h" - -#include "wx/richtext/richtextctrl.h" #include "wx/arrimpl.cpp" +// DLL options compatibility check: +#include "wx/app.h" +WX_CHECK_BUILD_OPTIONS("wxRichTextCtrl") + DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_ITEM_SELECTED) DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_ITEM_DESELECTED) DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_LEFT_CLICK) @@ -56,6 +58,8 @@ BEGIN_EVENT_TABLE( wxRichTextCtrl, wxScrolledWindow ) #endif EVT_PAINT(wxRichTextCtrl::OnPaint) EVT_ERASE_BACKGROUND(wxRichTextCtrl::OnEraseBackground) + EVT_IDLE(wxRichTextCtrl::OnIdle) + EVT_SCROLLWIN(wxRichTextCtrl::OnScroll) EVT_LEFT_DOWN(wxRichTextCtrl::OnLeftClick) EVT_MOTION(wxRichTextCtrl::OnMoveMouse) EVT_LEFT_UP(wxRichTextCtrl::OnLeftUp) @@ -102,17 +106,17 @@ wxRichTextCtrl::wxRichTextCtrl() Init(); } -wxRichTextCtrl::wxRichTextCtrl( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style) +wxRichTextCtrl::wxRichTextCtrl( wxWindow* parent, wxWindowID id, const wxString& value, const wxPoint& pos, const wxSize& size, long style) #if wxRICHTEXT_DERIVES_FROM_TEXTCTRLBASE : wxScrollHelper(this) #endif { Init(); - Create(parent, id, pos, size, style); + Create(parent, id, value, pos, size, style); } /// Creation -bool wxRichTextCtrl::Create( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style) +bool wxRichTextCtrl::Create( wxWindow* parent, wxWindowID id, const wxString& value, const wxPoint& pos, const wxSize& size, long style) { #if wxRICHTEXT_DERIVES_FROM_TEXTCTRLBASE if (!wxTextCtrlBase::Create(parent, id, pos, size, style|wxFULL_REPAINT_ON_RESIZE @@ -150,12 +154,10 @@ bool wxRichTextCtrl::Create( wxWindow* parent, wxWindowID id, const wxPoint& pos // Create a buffer RecreateBuffer(size); - wxCaret* caret = new wxCaret(this, wxRICHTEXT_DEFAULT_CARET_WIDTH, 16); - SetCaret(caret); - caret->Show(); - PositionCaret(); - SetCursor(wxCursor(wxCURSOR_IBEAM)); + + if (!value.IsEmpty()) + SetValue(value); return true; } @@ -177,6 +179,10 @@ void wxRichTextCtrl::Init() m_editable = true; m_caretAtLineStart = false; m_dragging = false; + m_fullLayoutRequired = false; + m_fullLayoutTime = 0; + m_fullLayoutSavedPosition = 0; + m_delayedLayoutThreshold = wxRICHTEXT_DEFAULT_DELAYED_LAYOUT_THRESHOLD; } /// Call Freeze to prevent refresh @@ -186,14 +192,14 @@ void wxRichTextCtrl::Freeze() } /// Call Thaw to refresh -void wxRichTextCtrl::Thaw(bool refresh) +void wxRichTextCtrl::Thaw() { m_freezeCount --; - if (m_freezeCount == 0 && refresh) + if (m_freezeCount == 0) { SetupScrollbars(); - Refresh(); + Refresh(false); } } @@ -206,10 +212,12 @@ void wxRichTextCtrl::Clear() m_caretAtLineStart = false; m_selectionRange.SetRange(-2, -2); + SetScrollbars(0, 0, 0, 0, 0, 0); + if (m_freezeCount == 0) { SetupScrollbars(); - Refresh(); + Refresh(false); } SendUpdateEvent(); } @@ -217,31 +225,41 @@ void wxRichTextCtrl::Clear() /// Painting void wxRichTextCtrl::OnPaint(wxPaintEvent& WXUNUSED(event)) { - wxBufferedPaintDC dc(this, m_bufferBitmap); + if (GetCaret()) + GetCaret()->Hide(); - PrepareDC(dc); + { + wxBufferedPaintDC dc(this, m_bufferBitmap); + //wxLogDebug(wxT("OnPaint")); - if (m_freezeCount > 0) - return; + PrepareDC(dc); - dc.SetFont(GetFont()); + if (m_freezeCount > 0) + return; - // Paint the background - PaintBackground(dc); + dc.SetFont(GetFont()); - wxRegion dirtyRegion = GetUpdateRegion(); + // Paint the background + PaintBackground(dc); - wxRect drawingArea(GetLogicalPoint(wxPoint(0, 0)), GetClientSize()); - wxRect availableSpace(GetClientSize()); - if (GetBuffer().GetDirty()) - { - GetBuffer().Layout(dc, availableSpace, GetBuffer().GetRange(), wxRICHTEXT_FIXED_WIDTH|wxRICHTEXT_VARIABLE_HEIGHT); - GetBuffer().SetDirty(false); - SetupScrollbars(); - PositionCaret(); + wxRegion dirtyRegion = GetUpdateRegion(); + + wxRect drawingArea(GetLogicalPoint(wxPoint(0, 0)), GetClientSize()); + wxRect availableSpace(GetClientSize()); + if (GetBuffer().GetDirty()) + { + GetBuffer().Layout(dc, availableSpace, wxRICHTEXT_FIXED_WIDTH|wxRICHTEXT_VARIABLE_HEIGHT); + GetBuffer().SetDirty(false); + SetupScrollbars(); + } + + GetBuffer().Draw(dc, GetBuffer().GetRange(), GetSelectionRange(), drawingArea, 0 /* descent */, 0 /* flags */); } - GetBuffer().Draw(dc, GetBuffer().GetRange(), GetSelectionRange(), drawingArea, 0 /* descent */, 0 /* flags */); + if (GetCaret()) + GetCaret()->Show(); + + PositionCaret(); } // Empty implementation, to prevent flicker @@ -251,14 +269,21 @@ void wxRichTextCtrl::OnEraseBackground(wxEraseEvent& WXUNUSED(event)) void wxRichTextCtrl::OnSetFocus(wxFocusEvent& WXUNUSED(event)) { + wxCaret* caret = new wxCaret(this, wxRICHTEXT_DEFAULT_CARET_WIDTH, 16); + SetCaret(caret); + caret->Show(); + PositionCaret(); + if (!IsFrozen()) - Refresh(); + Refresh(false); } void wxRichTextCtrl::OnKillFocus(wxFocusEvent& WXUNUSED(event)) { + SetCaret(NULL); + if (!IsFrozen()) - Refresh(); + Refresh(false); } /// Left-click @@ -291,7 +316,7 @@ void wxRichTextCtrl::OnLeftClick(wxMouseEvent& event) wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(position); wxRichTextLine* line = GetBuffer().GetLineAtPosition(position); - if (line && para && line->GetRange().GetStart() == position && para->GetRange().GetStart() != position) + if (line && para && line->GetAbsoluteRange().GetStart() == position && para->GetRange().GetStart() != position) caretAtLineStart = true; position --; } @@ -358,7 +383,7 @@ void wxRichTextCtrl::OnMoveMouse(wxMouseEvent& event) wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(position); wxRichTextLine* line = GetBuffer().GetLineAtPosition(position); - if (line && para && line->GetRange().GetStart() == position && para->GetRange().GetStart() != position) + if (line && para && line->GetAbsoluteRange().GetStart() == position && para->GetRange().GetStart() != position) caretAtLineStart = true; position --; } @@ -371,7 +396,7 @@ void wxRichTextCtrl::OnMoveMouse(wxMouseEvent& event) SetDefaultStyleToCursorStyle(); if (extendSel) - Refresh(); + Refresh(false); } } } @@ -413,13 +438,21 @@ void wxRichTextCtrl::OnChar(wxKeyEvent& event) event.GetKeyCode() == WXK_HOME || event.GetKeyCode() == WXK_PAGEUP || event.GetKeyCode() == WXK_PAGEDOWN || - event.GetKeyCode() == WXK_PRIOR || - event.GetKeyCode() == WXK_NEXT || event.GetKeyCode() == WXK_END) { - Navigate(event.GetKeyCode(), flags); + KeyboardNavigate(event.GetKeyCode(), flags); + return; + } + + // all the other keys modify the controls contents which shouldn't be + // possible if we're read-only + if ( !IsEditable() ) + { + event.Skip(); + return; } - else if (event.GetKeyCode() == WXK_RETURN) + + if (event.GetKeyCode() == WXK_RETURN) { BeginBatchUndo(_("Insert Text")); @@ -438,6 +471,8 @@ void wxRichTextCtrl::OnChar(wxKeyEvent& event) EndBatchUndo(); SetDefaultStyleToCursorStyle(); + + ScrollIntoView(m_caretPosition, WXK_RIGHT); } else if (event.GetKeyCode() == WXK_BACK) { @@ -467,6 +502,7 @@ void wxRichTextCtrl::OnChar(wxKeyEvent& event) SetDefaultStyleToCursorStyle(); } + ScrollIntoView(m_caretPosition, WXK_LEFT); } else if (event.GetKeyCode() == WXK_DELETE) { @@ -508,11 +544,8 @@ void wxRichTextCtrl::OnChar(wxKeyEvent& event) EndBatchUndo(); SetDefaultStyleToCursorStyle(); + ScrollIntoView(m_caretPosition, WXK_RIGHT); } -#if 0 - else - event.Skip(); -#endif } /// Delete content if there is a selection, e.g. when pressing a key. @@ -566,10 +599,9 @@ Adding Shift does the above but starts/extends selection. */ -bool wxRichTextCtrl::Navigate(int keyCode, int flags) +bool wxRichTextCtrl::KeyboardNavigate(int keyCode, int flags) { bool success = false; - Freeze(); if (keyCode == WXK_RIGHT) { @@ -599,11 +631,11 @@ bool wxRichTextCtrl::Navigate(int keyCode, int flags) else success = MoveDown(1, flags); } - else if (keyCode == WXK_PAGEUP || keyCode == WXK_PRIOR) + else if (keyCode == WXK_PAGEUP) { success = PageUp(1, flags); } - else if (keyCode == WXK_PAGEDOWN || keyCode == WXK_NEXT) + else if (keyCode == WXK_PAGEDOWN) { success = PageDown(1, flags); } @@ -628,9 +660,6 @@ bool wxRichTextCtrl::Navigate(int keyCode, int flags) SetDefaultStyleToCursorStyle(); } - // Only refresh if something changed - Thaw(success); - return success; } @@ -682,16 +711,16 @@ bool wxRichTextCtrl::ScrollIntoView(long position, int keyCode) int ppuX, ppuY; GetScrollPixelsPerUnit(& ppuX, & ppuY); - int startX, startY; - GetViewStart(& startX, & startY); - startX = 0; - startY = startY * ppuY; + int startXUnits, startYUnits; + GetViewStart(& startXUnits, & startYUnits); + int startY = startYUnits * ppuY; - int sx, sy; + int sx = 0, sy = 0; GetVirtualSize(& sx, & sy); - sx = 0; + int sxUnits = 0; + int syUnits = 0; if (ppuY != 0) - sy = sy/ppuY; + syUnits = sy/ppuY; wxRect rect = line->GetRect(); @@ -700,42 +729,72 @@ bool wxRichTextCtrl::ScrollIntoView(long position, int keyCode) wxSize clientSize = GetClientSize(); // Going down - if (keyCode == WXK_DOWN || keyCode == WXK_RIGHT || keyCode == WXK_END || keyCode == WXK_NEXT || keyCode == WXK_PAGEDOWN) + if (keyCode == WXK_DOWN || keyCode == WXK_RIGHT || keyCode == WXK_END || keyCode == WXK_PAGEDOWN) { if ((rect.y + rect.height) > (clientSize.y + startY)) { // Make it scroll so this item is at the bottom // of the window int y = rect.y - (clientSize.y - rect.height); - SetScrollbars(ppuX, ppuY, sx, sy, 0, (int) (0.5 + y/ppuY)); + int yUnits = (int) (0.5 + ((float) y)/(float) ppuY); + + // If we're still off the screen, scroll another line down + if ((rect.y + rect.height) > (clientSize.y + (yUnits*ppuY))) + yUnits ++; + + if (startYUnits != yUnits) + { + SetScrollbars(ppuX, ppuY, sxUnits, syUnits, 0, yUnits); + scrolled = true; + } } else if (rect.y < startY) { // Make it scroll so this item is at the top // of the window int y = rect.y ; - SetScrollbars(ppuX, ppuY, sx, sy, 0, (int) (0.5 + y/ppuY)); + int yUnits = (int) (0.5 + ((float) y)/(float) ppuY); + + if (startYUnits != yUnits) + { + SetScrollbars(ppuX, ppuY, sxUnits, syUnits, 0, yUnits); + scrolled = true; + } } - scrolled = true; } // Going up - else if (keyCode == WXK_UP || keyCode == WXK_LEFT || keyCode == WXK_HOME || keyCode == WXK_PRIOR || keyCode == WXK_PAGEUP) + else if (keyCode == WXK_UP || keyCode == WXK_LEFT || keyCode == WXK_HOME || keyCode == WXK_PAGEUP ) { if (rect.y < startY) { // Make it scroll so this item is at the top // of the window int y = rect.y ; - SetScrollbars(ppuX, ppuY, sx, sy, 0, (int) (0.5 + y/ppuY)); + int yUnits = (int) (0.5 + ((float) y)/(float) ppuY); + + if (startYUnits != yUnits) + { + SetScrollbars(ppuX, ppuY, sxUnits, syUnits, 0, yUnits); + scrolled = true; + } } else if ((rect.y + rect.height) > (clientSize.y + startY)) { // Make it scroll so this item is at the bottom // of the window int y = rect.y - (clientSize.y - rect.height); - SetScrollbars(ppuX, ppuY, sx, sy, 0, (int) (0.5 + y/ppuY)); + int yUnits = (int) (0.5 + ((float) y)/(float) ppuY); + + // If we're still off the screen, scroll another line down + if ((rect.y + rect.height) > (clientSize.y + (yUnits*ppuY))) + yUnits ++; + + if (startYUnits != yUnits) + { + SetScrollbars(ppuX, ppuY, sxUnits, syUnits, 0, yUnits); + scrolled = true; + } } - scrolled = true; } PositionCaret(); @@ -758,7 +817,7 @@ bool wxRichTextCtrl::IsPositionVisible(long pos) const startX = 0; startY = startY * ppuY; - int sx, sy; + int sx = 0, sy = 0; GetVirtualSize(& sx, & sy); sx = 0; if (ppuY != 0) @@ -792,10 +851,12 @@ void wxRichTextCtrl::MoveCaretForward(long oldPosition) if (line) { + wxRichTextRange lineRange = line->GetAbsoluteRange(); + // We're at the end of a line. See whether we need to // stay at the same actual caret position but change visual // position, or not. - if (oldPosition == line->GetRange().GetEnd()) + if (oldPosition == lineRange.GetEnd()) { if (m_caretAtLineStart) { @@ -835,16 +896,18 @@ void wxRichTextCtrl::MoveCaretBack(long oldPosition) if (line) { + wxRichTextRange lineRange = line->GetAbsoluteRange(); + // We're at the start of a line. See whether we need to // stay at the same actual caret position but change visual // position, or not. - if (oldPosition == line->GetRange().GetStart()) + if (oldPosition == lineRange.GetStart()) { m_caretPosition = oldPosition-1; m_caretAtLineStart = true; return; } - else if (oldPosition == line->GetRange().GetEnd()) + else if (oldPosition == lineRange.GetEnd()) { if (m_caretAtLineStart) { @@ -895,8 +958,8 @@ bool wxRichTextCtrl::MoveRight(int noPositions, int flags) PositionCaret(); SetDefaultStyleToCursorStyle(); - if (!IsFrozen()) - Refresh(); // TODO: optimize so that if we didn't change the selection, we don't refresh + if (extendSel) + Refresh(false); return true; } else @@ -924,8 +987,8 @@ bool wxRichTextCtrl::MoveLeft(int noPositions, int flags) PositionCaret(); SetDefaultStyleToCursorStyle(); - if (!IsFrozen()) - Refresh(); + if (extendSel) + Refresh(false); return true; } else @@ -941,6 +1004,9 @@ bool wxRichTextCtrl::MoveUp(int noLines, int flags) /// Move up bool wxRichTextCtrl::MoveDown(int noLines, int flags) { + if (!GetCaret()) + return false; + long lineNumber = GetBuffer().GetVisibleLineNumber(m_caretPosition, true, m_caretAtLineStart); wxPoint pt = GetCaret()->GetPosition(); long newLine = lineNumber + noLines; @@ -985,13 +1051,14 @@ bool wxRichTextCtrl::MoveDown(int noLines, int flags) if (hitTest == wxRICHTEXT_HITTEST_BEFORE) { wxRichTextLine* thisLine = GetBuffer().GetLineAtPosition(newPos-1); - if (thisLine && (newPos-1) == thisLine->GetRange().GetEnd()) + wxRichTextRange lineRange; + if (thisLine) + lineRange = thisLine->GetAbsoluteRange(); + + if (thisLine && (newPos-1) == lineRange.GetEnd()) { - // if (para->GetRange().GetStart() != thisLine->GetRange().GetStart()) - { - newPos --; - caretLineStart = true; - } + newPos --; + caretLineStart = true; } else { @@ -1003,19 +1070,20 @@ bool wxRichTextCtrl::MoveDown(int noLines, int flags) long newSelEnd = newPos; - if (!ExtendSelection(m_caretPosition, newSelEnd, flags)) + bool extendSel = ExtendSelection(m_caretPosition, newSelEnd, flags); + if (!extendSel) SelectNone(); SetCaretPosition(newPos, caretLineStart); PositionCaret(); SetDefaultStyleToCursorStyle(); - if (!IsFrozen()) - Refresh(); + if (extendSel) + Refresh(false); return true; } - else - return false; + + return false; } /// Move to the end of the paragraph @@ -1025,15 +1093,16 @@ bool wxRichTextCtrl::MoveToParagraphEnd(int flags) if (para) { long newPos = para->GetRange().GetEnd() - 1; - if (!ExtendSelection(m_caretPosition, newPos, flags)) + bool extendSel = ExtendSelection(m_caretPosition, newPos, flags); + if (!extendSel) SelectNone(); SetCaretPosition(newPos); PositionCaret(); SetDefaultStyleToCursorStyle(); - if (!IsFrozen()) - Refresh(); + if (extendSel) + Refresh(false); return true; } @@ -1047,15 +1116,16 @@ bool wxRichTextCtrl::MoveToParagraphStart(int flags) if (para) { long newPos = para->GetRange().GetStart() - 1; - if (!ExtendSelection(m_caretPosition, newPos, flags)) + bool extendSel = ExtendSelection(m_caretPosition, newPos, flags); + if (!extendSel) SelectNone(); SetCaretPosition(newPos); PositionCaret(); SetDefaultStyleToCursorStyle(); - if (!IsFrozen()) - Refresh(); + if (extendSel) + Refresh(false); return true; } @@ -1069,16 +1139,18 @@ bool wxRichTextCtrl::MoveToLineEnd(int flags) if (line) { - long newPos = line->GetRange().GetEnd(); - if (!ExtendSelection(m_caretPosition, newPos, flags)) + wxRichTextRange lineRange = line->GetAbsoluteRange(); + long newPos = lineRange.GetEnd(); + bool extendSel = ExtendSelection(m_caretPosition, newPos, flags); + if (!extendSel) SelectNone(); SetCaretPosition(newPos); PositionCaret(); SetDefaultStyleToCursorStyle(); - if (!IsFrozen()) - Refresh(); + if (extendSel) + Refresh(false); return true; } @@ -1091,19 +1163,21 @@ bool wxRichTextCtrl::MoveToLineStart(int flags) wxRichTextLine* line = GetVisibleLineForCaretPosition(m_caretPosition); if (line) { - long newPos = line->GetRange().GetStart()-1; + wxRichTextRange lineRange = line->GetAbsoluteRange(); + long newPos = lineRange.GetStart()-1; - if (!ExtendSelection(m_caretPosition, newPos, flags)) + bool extendSel = ExtendSelection(m_caretPosition, newPos, flags); + if (!extendSel) SelectNone(); wxRichTextParagraph* para = GetBuffer().GetParagraphForLine(line); - SetCaretPosition(newPos, para->GetRange().GetStart() != line->GetRange().GetStart()); + SetCaretPosition(newPos, para->GetRange().GetStart() != lineRange.GetStart()); PositionCaret(); SetDefaultStyleToCursorStyle(); - if (!IsFrozen()) - Refresh(); + if (extendSel) + Refresh(false); return true; } @@ -1115,15 +1189,16 @@ bool wxRichTextCtrl::MoveHome(int flags) { if (m_caretPosition != -1) { - if (!ExtendSelection(m_caretPosition, -1, flags)) + bool extendSel = ExtendSelection(m_caretPosition, -1, flags); + if (!extendSel) SelectNone(); SetCaretPosition(-1); PositionCaret(); SetDefaultStyleToCursorStyle(); - if (!IsFrozen()) - Refresh(); + if (extendSel) + Refresh(false); return true; } else @@ -1137,15 +1212,16 @@ bool wxRichTextCtrl::MoveEnd(int flags) if (m_caretPosition != endPos) { - if (!ExtendSelection(m_caretPosition, endPos, flags)) + bool extendSel = ExtendSelection(m_caretPosition, endPos, flags); + if (!extendSel) SelectNone(); SetCaretPosition(endPos); PositionCaret(); SetDefaultStyleToCursorStyle(); - if (!IsFrozen()) - Refresh(); + if (extendSel) + Refresh(false); return true; } else @@ -1171,20 +1247,22 @@ bool wxRichTextCtrl::PageDown(int noPages, int flags) wxRichTextLine* newLine = GetBuffer().GetLineAtYPosition(newY); if (newLine) { - long pos = newLine->GetRange().GetStart()-1; + wxRichTextRange lineRange = newLine->GetAbsoluteRange(); + long pos = lineRange.GetStart()-1; if (pos != m_caretPosition) { wxRichTextParagraph* para = GetBuffer().GetParagraphForLine(newLine); - if (!ExtendSelection(m_caretPosition, pos, flags)) + bool extendSel = ExtendSelection(m_caretPosition, pos, flags); + if (!extendSel) SelectNone(); - SetCaretPosition(pos, para->GetRange().GetStart() != newLine->GetRange().GetStart()); + SetCaretPosition(pos, para->GetRange().GetStart() != lineRange.GetStart()); PositionCaret(); SetDefaultStyleToCursorStyle(); - if (!IsFrozen()) - Refresh(); + if (extendSel) + Refresh(false); return true; } } @@ -1274,15 +1352,16 @@ bool wxRichTextCtrl::WordLeft(int WXUNUSED(n), int flags) { wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(pos, true); - if (!ExtendSelection(m_caretPosition, pos, flags)) + bool extendSel = ExtendSelection(m_caretPosition, pos, flags); + if (!extendSel) SelectNone(); SetCaretPosition(pos, para->GetRange().GetStart() != pos); PositionCaret(); SetDefaultStyleToCursorStyle(); - if (!IsFrozen()) - Refresh(); + if (extendSel) + Refresh(false); return true; } @@ -1297,15 +1376,16 @@ bool wxRichTextCtrl::WordRight(int WXUNUSED(n), int flags) { wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(pos, true); - if (!ExtendSelection(m_caretPosition, pos, flags)) + bool extendSel = ExtendSelection(m_caretPosition, pos, flags); + if (!extendSel) SelectNone(); SetCaretPosition(pos, para->GetRange().GetStart() != pos); PositionCaret(); SetDefaultStyleToCursorStyle(); - if (!IsFrozen()) - Refresh(); + if (extendSel) + Refresh(false); return true; } @@ -1315,15 +1395,48 @@ bool wxRichTextCtrl::WordRight(int WXUNUSED(n), int flags) /// Sizing void wxRichTextCtrl::OnSize(wxSizeEvent& event) { - GetBuffer().SetDirty(true); + // Only do sizing optimization for large buffers + if (GetBuffer().GetRange().GetEnd() > m_delayedLayoutThreshold) + { + m_fullLayoutRequired = true; + m_fullLayoutTime = wxGetLocalTimeMillis(); + m_fullLayoutSavedPosition = GetFirstVisiblePosition(); + LayoutContent(true /* onlyVisibleRect */); + } + else + GetBuffer().Invalidate(wxRICHTEXT_ALL); RecreateBuffer(); event.Skip(); } + +/// Idle-time processing +void wxRichTextCtrl::OnIdle(wxIdleEvent& event) +{ + const int layoutInterval = wxRICHTEXT_DEFAULT_LAYOUT_INTERVAL; + + if (m_fullLayoutRequired && (wxGetLocalTimeMillis() > (m_fullLayoutTime + layoutInterval))) + { + m_fullLayoutRequired = false; + m_fullLayoutTime = 0; + GetBuffer().Invalidate(wxRICHTEXT_ALL); + ShowPosition(m_fullLayoutSavedPosition); + Refresh(false); + } + event.Skip(); +} + +/// Scrolling +void wxRichTextCtrl::OnScroll(wxScrollWinEvent& event) +{ + // Not used + event.Skip(); +} + /// Set up scrollbars, e.g. after a resize -void wxRichTextCtrl::SetupScrollbars() +void wxRichTextCtrl::SetupScrollbars(bool atTop) { if (m_freezeCount) return; @@ -1336,18 +1449,20 @@ void wxRichTextCtrl::SetupScrollbars() // TODO: reimplement scrolling so we scroll by line, not by fixed number // of pixels. See e.g. wxVScrolledWindow for ideas. - int pixelsPerUnit = 5; // 10; + int pixelsPerUnit = 5; wxSize clientSize = GetClientSize(); int maxHeight = GetBuffer().GetCachedSize().y; - int unitsY = maxHeight/pixelsPerUnit; + // Round up so we have at least maxHeight pixels + int unitsY = (int) (((float)maxHeight/(float)pixelsPerUnit) + 0.5); - int startX, startY; - GetViewStart(& startX, & startY); + int startX = 0, startY = 0; + if (!atTop) + GetViewStart(& startX, & startY); int maxPositionX = 0; // wxMax(sz.x - clientSize.x, 0); - int maxPositionY = (wxMax(maxHeight - clientSize.y, 0))/pixelsPerUnit; + int maxPositionY = (int) ((((float)(wxMax((unitsY*pixelsPerUnit) - clientSize.y, 0)))/((float)pixelsPerUnit)) + 0.5); // Move to previous scroll position if // possible @@ -1403,9 +1518,10 @@ bool wxRichTextCtrl::LoadFile(const wxString& filename, int type) DiscardEdits(); SetInsertionPoint(0); - Layout(); + LayoutContent(); PositionCaret(); - Refresh(); + SetupScrollbars(true); + Refresh(false); SendUpdateEvent(); if (success) @@ -1473,7 +1589,8 @@ void wxRichTextCtrl::SelectAll() /// Select none void wxRichTextCtrl::SelectNone() { - SetSelection(-2, -2); + if (!(GetSelectionRange() == wxRichTextRange(-2, -2))) + SetSelection(-2, -2); m_selectionAnchor = -2; } @@ -1486,17 +1603,20 @@ wxString wxRichTextCtrl::GetStringSelection() const } // do the window-specific processing after processing the update event +#if !wxRICHTEXT_DERIVES_FROM_TEXTCTRLBASE void wxRichTextCtrl::DoUpdateWindowUI(wxUpdateUIEvent& event) { - if ( event.GetSetEnabled() ) - Enable(event.GetEnabled()); + // call inherited + wxWindowBase::DoUpdateWindowUI(event); + // update text if ( event.GetSetText() ) { if ( event.GetText() != GetValue() ) SetValue(event.GetText()); } } +#endif // !wxRICHTEXT_DERIVES_FROM_TEXTCTRLBASE // ---------------------------------------------------------------------------- // hit testing @@ -1525,16 +1645,26 @@ wxRichTextCtrl::HitTest(const wxPoint& pt, { wxClientDC dc((wxRichTextCtrl*) this); ((wxRichTextCtrl*)this)->PrepareDC(dc); + + // Buffer uses logical position (relative to start of buffer) + // so convert + wxPoint pt2 = GetLogicalPoint(pt); - int hit = ((wxRichTextCtrl*)this)->GetBuffer().HitTest(dc, pt, *pos); - if (hit == wxRICHTEXT_HITTEST_BEFORE) - return wxTE_HT_BEFORE; - else if (hit == wxRICHTEXT_HITTEST_AFTER) - return wxTE_HT_BEYOND; - else if (hit == wxRICHTEXT_HITTEST_ON) - return wxTE_HT_ON_TEXT; - else - return wxTE_HT_UNKNOWN; + int hit = ((wxRichTextCtrl*)this)->GetBuffer().HitTest(dc, pt2, *pos); + + switch ( hit ) + { + case wxRICHTEXT_HITTEST_BEFORE: + return wxTE_HT_BEFORE; + + case wxRICHTEXT_HITTEST_AFTER: + return wxTE_HT_BEYOND; + + case wxRICHTEXT_HITTEST_ON: + return wxTE_HT_ON_TEXT; + } + + return wxTE_HT_UNKNOWN; } // ---------------------------------------------------------------------------- @@ -1604,8 +1734,8 @@ bool wxRichTextCtrl::WriteImage(const wxImage& image, int bitmapType) wxImage image2 = image; if (imageBlock.MakeImageBlock(image2, bitmapType)) return WriteImage(imageBlock); - else - return false; + + return false; } bool wxRichTextCtrl::WriteImage(const wxString& filename, int bitmapType) @@ -1615,8 +1745,8 @@ bool wxRichTextCtrl::WriteImage(const wxString& filename, int bitmapType) wxImage image; if (imageBlock.MakeImageBlock(filename, bitmapType, image, false)) return WriteImage(imageBlock); - else - return false; + + return false; } bool wxRichTextCtrl::WriteImage(const wxRichTextImageBlock& imageBlock) @@ -1633,9 +1763,8 @@ bool wxRichTextCtrl::WriteImage(const wxBitmap& bitmap, int bitmapType) wxImage image = bitmap.ConvertToImage(); if (image.Ok() && imageBlock.MakeImageBlock(image, bitmapType)) return WriteImage(imageBlock); - else - return false; } + return false; } @@ -1667,8 +1796,8 @@ void wxRichTextCtrl::Cut() GetBuffer().CopyToClipboard(range); DeleteSelectedContent(); - Layout(); - Refresh(); + LayoutContent(); + Refresh(false); } } @@ -1791,8 +1920,7 @@ void wxRichTextCtrl::DoSetSelection(long from, long to, bool WXUNUSED(scrollCare { m_selectionAnchor = from; m_selectionRange.SetRange(from, to); - if (!IsFrozen()) - Refresh(); + Refresh(false); PositionCaret(); } @@ -1820,9 +1948,9 @@ void wxRichTextCtrl::Remove(long from, long to) from, // New caret position this); - Layout(); + LayoutContent(); if (!IsFrozen()) - Refresh(); + Refresh(false); } bool wxRichTextCtrl::IsModified() const @@ -1911,10 +2039,10 @@ bool wxRichTextCtrl::CanRedo() const } // ---------------------------------------------------------------------------- -// implemenation details +// implementation details // ---------------------------------------------------------------------------- -void wxRichTextCtrl::Command(wxCommandEvent & event) +void wxRichTextCtrl::Command(wxCommandEvent& event) { SetValue(event.GetString()); GetEventHandler()->ProcessEvent(event); @@ -2067,6 +2195,11 @@ bool wxRichTextCtrl::SetStyle(long start, long end, const wxTextAttrEx& style) return GetBuffer().SetStyle(wxRichTextRange(start, end), style); } +bool wxRichTextCtrl::SetStyle(long start, long end, const wxTextAttr& style) +{ + return GetBuffer().SetStyle(wxRichTextRange(start, end), wxTextAttrEx(style)); +} + bool wxRichTextCtrl::SetStyle(const wxRichTextRange& range, const wxRichTextAttr& style) { return GetBuffer().SetStyle(range, style); @@ -2077,11 +2210,33 @@ bool wxRichTextCtrl::SetDefaultStyle(const wxTextAttrEx& style) return GetBuffer().SetDefaultStyle(style); } +bool wxRichTextCtrl::SetDefaultStyle(const wxTextAttr& style) +{ + return GetBuffer().SetDefaultStyle(wxTextAttrEx(style)); +} + const wxTextAttrEx& wxRichTextCtrl::GetDefaultStyleEx() const { return GetBuffer().GetDefaultStyle(); } +const wxTextAttr& wxRichTextCtrl::GetDefaultStyle() const +{ + return GetBuffer().GetDefaultStyle(); +} + +bool wxRichTextCtrl::GetStyle(long position, wxTextAttr& style) const +{ + wxTextAttrEx attr; + if (GetBuffer().GetStyle(position, attr)) + { + style = attr; + return true; + } + else + return false; +} + bool wxRichTextCtrl::GetStyle(long position, wxTextAttrEx& style) const { return GetBuffer().GetStyle(position, style); @@ -2109,8 +2264,8 @@ bool wxRichTextCtrl::SetFont(const wxFont& font) return true; } -/// Transform logical to physical (unscrolling) -wxPoint wxRichTextCtrl::GetPhysicalPoint(const wxPoint& ptLogical) +/// Transform logical to physical +wxPoint wxRichTextCtrl::GetPhysicalPoint(const wxPoint& ptLogical) const { wxPoint pt; CalcScrolledPosition(ptLogical.x, ptLogical.y, & pt.x, & pt.y); @@ -2119,7 +2274,7 @@ wxPoint wxRichTextCtrl::GetPhysicalPoint(const wxPoint& ptLogical) } /// Transform physical to logical -wxPoint wxRichTextCtrl::GetLogicalPoint(const wxPoint& ptPhysical) +wxPoint wxRichTextCtrl::GetLogicalPoint(const wxPoint& ptPhysical) const { wxPoint pt; CalcUnscrolledPosition(ptPhysical.x, ptPhysical.y, & pt.x, & pt.y); @@ -2130,14 +2285,21 @@ wxPoint wxRichTextCtrl::GetLogicalPoint(const wxPoint& ptPhysical) /// Position the caret void wxRichTextCtrl::PositionCaret() { + if (!GetCaret()) + return; + + //wxLogDebug(wxT("PositionCaret")); + wxRect caretRect; if (GetCaretPositionForIndex(GetCaretPosition(), caretRect)) { wxPoint originalPt = caretRect.GetPosition(); wxPoint pt = GetPhysicalPoint(originalPt); - - GetCaret()->Move(pt); - GetCaret()->SetSize(caretRect.GetSize()); + if (GetCaret()->GetPosition() != pt) + { + GetCaret()->Move(pt); + GetCaret()->SetSize(caretRect.GetSize()); + } } } @@ -2157,8 +2319,8 @@ bool wxRichTextCtrl::GetCaretPositionForIndex(long position, wxRect& rect) rect = wxRect(pt, wxSize(wxRICHTEXT_DEFAULT_CARET_WIDTH, height)); return true; } - else - return false; + + return false; } /// Gets the line for the visible caret position. If the caret is @@ -2171,8 +2333,9 @@ wxRichTextLine* wxRichTextCtrl::GetVisibleLineForCaretPosition(long caretPositio wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(caretPosition, true); if (line) { - if (caretPosition == line->GetRange().GetStart()-1 && - (para->GetRange().GetStart() != line->GetRange().GetStart())) + wxRichTextRange lineRange = line->GetAbsoluteRange(); + if (caretPosition == lineRange.GetStart()-1 && + (para->GetRange().GetStart() != lineRange.GetStart())) { if (!m_caretAtLineStart) line = GetBuffer().GetLineAtPosition(caretPosition-1, true); @@ -2186,7 +2349,7 @@ wxRichTextLine* wxRichTextCtrl::GetVisibleLineForCaretPosition(long caretPositio bool wxRichTextCtrl::MoveCaret(long pos, bool showAtLineStart) { if (GetBuffer().GetDirty()) - Layout(); + LayoutContent(); if (pos <= GetBuffer().GetRange().GetEnd()) { @@ -2202,26 +2365,36 @@ bool wxRichTextCtrl::MoveCaret(long pos, bool showAtLineStart) /// Layout the buffer: which we must do before certain operations, such as /// setting the caret position. -bool wxRichTextCtrl::Layout() +bool wxRichTextCtrl::LayoutContent(bool onlyVisibleRect) { - wxRect availableSpace(GetClientSize()); - if (availableSpace.width == 0) - availableSpace.width = 10; - if (availableSpace.height == 0) - availableSpace.height = 10; + if (GetBuffer().GetDirty() || onlyVisibleRect) + { + wxRect availableSpace(GetClientSize()); + if (availableSpace.width == 0) + availableSpace.width = 10; + if (availableSpace.height == 0) + availableSpace.height = 10; - wxClientDC dc(this); - dc.SetFont(GetFont()); + int flags = wxRICHTEXT_FIXED_WIDTH|wxRICHTEXT_VARIABLE_HEIGHT; + if (onlyVisibleRect) + { + flags |= wxRICHTEXT_LAYOUT_SPECIFIED_RECT; + availableSpace.SetPosition(GetLogicalPoint(wxPoint(0, 0))); + } - PrepareDC(dc); + wxClientDC dc(this); + dc.SetFont(GetFont()); - GetBuffer().Defragment(); - GetBuffer().UpdateRanges(); // If items were deleted, ranges need recalculation - GetBuffer().Layout(dc, availableSpace, GetBuffer().GetRange(), wxRICHTEXT_FIXED_WIDTH|wxRICHTEXT_VARIABLE_HEIGHT); - GetBuffer().SetDirty(false); + PrepareDC(dc); - if (!IsFrozen()) - SetupScrollbars(); + GetBuffer().Defragment(); + GetBuffer().UpdateRanges(); // If items were deleted, ranges need recalculation + GetBuffer().Layout(dc, availableSpace, flags); + GetBuffer().SetDirty(false); + + if (!IsFrozen()) + SetupScrollbars(); + } return true; } @@ -2340,7 +2513,7 @@ bool wxRichTextCtrl::ApplyUnderlineToSelection() { wxRichTextAttr attr; attr.SetFlags(wxTEXT_ATTR_FONT_UNDERLINE); - attr.SetFontWeight(!IsSelectionUnderlined()); + attr.SetFontUnderlined(!IsSelectionUnderlined()); if (HasSelection()) return SetStyle(GetSelectionRange(), attr); @@ -2397,10 +2570,19 @@ bool wxRichTextCtrl::SetDefaultStyleToCursorStyle() SetDefaultStyle(attr); return true; } + + return false; +} + +/// Returns the first visible position in the current view +long wxRichTextCtrl::GetFirstVisiblePosition() const +{ + wxRichTextLine* line = GetBuffer().GetLineAtYPosition(GetLogicalPoint(wxPoint(0, 0)).y); + if (line) + return line->GetAbsoluteRange().GetStart(); else - return false; + return 0; } #endif // wxUSE_RICHTEXT -