]> git.saurik.com Git - wxWidgets.git/blobdiff - src/richtext/richtextctrl.cpp
Include wx/arrstr.h according to precompiled headers of wx/wx.h (with other minor...
[wxWidgets.git] / src / richtext / richtextctrl.cpp
index e376c262f8856ba9527166cf1651e46392a4c4d6..b414fa7bb5f6cd270715fb77c9434d27ff3dfb7f 100644 (file)
@@ -1,10 +1,10 @@
 /////////////////////////////////////////////////////////////////////////////
-// Name:        richeditctrl.cpp
+// Name:        src/richtext/richeditctrl.cpp
 // Purpose:     A rich edit control
 // Author:      Julian Smart
-// Modified by: 
+// Modified by:
 // Created:     2005-09-30
-// RCS-ID:      
+// RCS-ID:      $Id$
 // Copyright:   (c) Julian Smart
 // Licence:     wxWindows licence
 /////////////////////////////////////////////////////////////////////////////
 #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"
 
 DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_ITEM_SELECTED)
@@ -56,6 +54,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)
@@ -128,7 +128,7 @@ bool wxRichTextCtrl::Create( wxWindow* parent, wxWindowID id, const wxPoint& pos
     {
         SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
     }
-    
+
     GetBuffer().SetRichTextCtrl(this);
 
     wxTextAttrEx attributes;
@@ -141,22 +141,17 @@ bool wxRichTextCtrl::Create( wxWindow* parent, wxWindowID id, const wxPoint& pos
     SetDefaultStyle(attributes);
     SetBasicStyle(attributes);
 
-    SetBackgroundColour(*wxWHITE);    
+    SetBackgroundColour(*wxWHITE);
     SetBackgroundStyle(wxBG_STYLE_CUSTOM);
 
-    // Tell the sizers to use the given or best size    
+    // Tell the sizers to use the given or best size
     SetBestFittingSize(size);
-    
+
     // Create a buffer
     RecreateBuffer(size);
 
-    wxCaret* caret = new wxCaret(this, wxRICHTEXT_DEFAULT_CARET_WIDTH, 16);
-    SetCaret(caret);
-    caret->Show();
-    PositionCaret();
-
     SetCursor(wxCursor(wxCURSOR_IBEAM));
-    
+
     return true;
 }
 
@@ -177,6 +172,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 +185,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);
     }
 }
 
@@ -209,7 +208,7 @@ void wxRichTextCtrl::Clear()
     if (m_freezeCount == 0)
     {
         SetupScrollbars();
-        Refresh();
+        Refresh(false);
     }
     SendUpdateEvent();
 }
@@ -217,30 +216,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());
-    // Paint the background
-    PaintBackground(dc);
+        if (m_freezeCount > 0)
+            return;
 
-    wxRegion dirtyRegion = GetUpdateRegion();
+        dc.SetFont(GetFont());
 
-    wxRect availableSpace(wxPoint(0, 0), GetClientSize());
-    if (GetBuffer().GetDirty())
-    {
-        GetBuffer().Layout(dc, availableSpace, wxRICHTEXT_FIXED_WIDTH|wxRICHTEXT_VARIABLE_HEIGHT);
-        GetBuffer().SetDirty(false);
-        SetupScrollbars();
-        PositionCaret();
+        // Paint the background
+        PaintBackground(dc);
+
+        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(), availableSpace, 0 /* descent */, 0 /* flags */);
+    if (GetCaret())
+        GetCaret()->Show();
+
+    PositionCaret();
 }
 
 // Empty implementation, to prevent flicker
@@ -250,14 +260,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
@@ -268,7 +285,7 @@ void wxRichTextCtrl::OnLeftClick(wxMouseEvent& event)
     wxClientDC dc(this);
     PrepareDC(dc);
     dc.SetFont(GetFont());
+
     long position = 0;
     int hit = GetBuffer().HitTest(dc, event.GetLogicalPosition(dc), position);
 
@@ -290,7 +307,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 --;
         }
@@ -338,7 +355,7 @@ void wxRichTextCtrl::OnMoveMouse(wxMouseEvent& event)
     wxClientDC dc(this);
     PrepareDC(dc);
     dc.SetFont(GetFont());
+
     long position = 0;
     wxPoint logicalPt = event.GetLogicalPosition(dc);
     int hit = GetBuffer().HitTest(dc, logicalPt, position);
@@ -357,7 +374,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 --;
         }
@@ -365,12 +382,12 @@ void wxRichTextCtrl::OnMoveMouse(wxMouseEvent& event)
         if (m_caretPosition != position)
         {
             bool extendSel = ExtendSelection(m_caretPosition, position, wxRICHTEXT_SHIFT_DOWN);
-            
+
             MoveCaret(position, caretAtLineStart);
             SetDefaultStyleToCursorStyle();
-            
+
             if (extendSel)
-                Refresh();
+                Refresh(false);
         }
     }
 }
@@ -412,13 +429,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;
     }
-    else if (event.GetKeyCode() == WXK_RETURN)
+
+    // all the other keys modify the controls contents which shouldn't be
+    // possible if we're read-only
+    if ( !IsEditable() )
+    {
+        event.Skip();
+        return;
+    }
+
+    if (event.GetKeyCode() == WXK_RETURN)
     {
         BeginBatchUndo(_("Insert Text"));
 
@@ -437,6 +462,8 @@ void wxRichTextCtrl::OnChar(wxKeyEvent& event)
 
         EndBatchUndo();
         SetDefaultStyleToCursorStyle();
+
+        ScrollIntoView(m_caretPosition, WXK_RIGHT);
     }
     else if (event.GetKeyCode() == WXK_BACK)
     {
@@ -466,6 +493,7 @@ void wxRichTextCtrl::OnChar(wxKeyEvent& event)
             SetDefaultStyleToCursorStyle();
         }
 
+        ScrollIntoView(m_caretPosition, WXK_LEFT);
     }
     else if (event.GetKeyCode() == WXK_DELETE)
     {
@@ -507,11 +535,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.
@@ -565,10 +590,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)
     {
@@ -598,11 +622,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);
     }
@@ -627,9 +651,6 @@ bool wxRichTextCtrl::Navigate(int keyCode, int flags)
         SetDefaultStyleToCursorStyle();
     }
 
-    // Only refresh if something changed
-    Thaw(success);
-
     return success;
 }
 
@@ -662,7 +683,7 @@ bool wxRichTextCtrl::ExtendSelection(long oldPos, long newPos, int flags)
         {
             wxLogDebug(wxT("Strange selection range"));
         }
-        
+
         return true;
     }
     else
@@ -686,7 +707,7 @@ bool wxRichTextCtrl::ScrollIntoView(long position, int keyCode)
     startX = 0;
     startY = startY * ppuY;
 
-    int sx, sy;
+    int sx = 0, sy = 0;
     GetVirtualSize(& sx, & sy);
     sx = 0;
     if (ppuY != 0)
@@ -699,42 +720,64 @@ 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));
+            y = (int) (0.5 + y/ppuY);
+
+            if (startY != y)
+            {
+                SetScrollbars(ppuX, ppuY, sx, sy, 0, y);
+                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));
+            y = (int) (0.5 + y/ppuY);
+
+            if (startY != y)
+            {
+                SetScrollbars(ppuX, ppuY, sx, sy, 0, y);
+                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));
+            y = (int) (0.5 + y/ppuY);
+
+            if (startY != y)
+            {
+                SetScrollbars(ppuX, ppuY, sx, sy, 0, y);
+                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));
+            y = (int) (0.5 + y/ppuY);
+
+            if (startY != y)
+            {
+                SetScrollbars(ppuX, ppuY, sx, sy, 0, y);
+                scrolled = true;
+            }
         }
-        scrolled = true;
     }
     PositionCaret();
 
@@ -757,7 +800,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)
@@ -791,10 +834,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)
                 {
@@ -808,7 +853,7 @@ void wxRichTextCtrl::MoveCaretForward(long oldPosition)
                     // the same position but indicate that we're to show
                     // at the start of the next line.
                     m_caretPosition = oldPosition;
-                    m_caretAtLineStart = true;                    
+                    m_caretAtLineStart = true;
                 }
                 SetDefaultStyleToCursorStyle();
                 return;
@@ -834,16 +879,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)
                 {
@@ -894,12 +941,12 @@ 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
-        return false;    
+        return false;
 }
 
 /// Move left
@@ -923,12 +970,12 @@ bool wxRichTextCtrl::MoveLeft(int noPositions, int flags)
         PositionCaret();
         SetDefaultStyleToCursorStyle();
 
-        if (!IsFrozen())
-            Refresh();
+        if (extendSel)
+            Refresh(false);
         return true;
     }
     else
-        return false;    
+        return false;
 }
 
 /// Move up
@@ -940,6 +987,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;
@@ -962,8 +1012,8 @@ bool wxRichTextCtrl::MoveDown(int noLines, int flags)
 
     wxRichTextLine* lineObj = GetBuffer().GetLineForVisibleLineNumber(newLine);
     if (lineObj)
-    {                
-        pt.y = lineObj->GetAbsolutePosition().y + 2;                
+    {
+        pt.y = lineObj->GetAbsolutePosition().y + 2;
     }
     else
         return false;
@@ -972,9 +1022,9 @@ bool wxRichTextCtrl::MoveDown(int noLines, int flags)
     wxClientDC dc(this);
     PrepareDC(dc);
     dc.SetFont(GetFont());
-    
+
     int hitTest = GetBuffer().HitTest(dc, pt, newPos);
-    
+
     if (hitTest != wxRICHTEXT_HITTEST_NONE)
     {
         // If end of previous line, and hitTest is wxRICHTEXT_HITTEST_BEFORE,
@@ -984,13 +1034,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
             {
@@ -1002,19 +1053,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
@@ -1024,15 +1076,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;
     }
 
@@ -1046,15 +1099,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;
     }
 
@@ -1068,16 +1122,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;
     }
 
@@ -1090,19 +1146,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;
     }
 
@@ -1114,19 +1172,20 @@ 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
-        return false;    
+        return false;
 }
 
 /// Move to the end of the buffer
@@ -1136,19 +1195,20 @@ 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
-        return false;    
+        return false;
 }
 
 /// Move noPages pages up
@@ -1170,20 +1230,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;
             }
         }
@@ -1194,19 +1256,19 @@ bool wxRichTextCtrl::PageDown(int noPages, int flags)
 
 // Finds the caret position for the next word
 long wxRichTextCtrl::FindNextWordPosition(int direction) const
-{    
+{
     long endPos = GetBuffer().GetRange().GetEnd();
 
     if (direction > 0)
     {
         long i = m_caretPosition+1+direction; // +1 for conversion to character pos
-        
+
         // First skip current text to space
         while (i < endPos && i > -1)
         {
             // i is in character, not caret positions
             wxString text = GetBuffer().GetTextForRange(wxRichTextRange(i, i));
-            if (text != wxT(" ") && !text.IsEmpty())
+            if (text != wxT(" ") && !text.empty())
                 i += direction;
             else
             {
@@ -1217,9 +1279,9 @@ long wxRichTextCtrl::FindNextWordPosition(int direction) const
         {
             // i is in character, not caret positions
             wxString text = GetBuffer().GetTextForRange(wxRichTextRange(i, i));
-            if (text.IsEmpty()) // End of paragraph, or maybe an image
+            if (text.empty()) // End of paragraph, or maybe an image
                 return wxMax(-1, i - 1);
-            else if (text == wxT(" ") || text.IsEmpty())
+            else if (text == wxT(" ") || text.empty())
                 i += direction;
             else
             {
@@ -1240,9 +1302,9 @@ long wxRichTextCtrl::FindNextWordPosition(int direction) const
         {
             // i is in character, not caret positions
             wxString text = GetBuffer().GetTextForRange(wxRichTextRange(i, i));
-            if (text.IsEmpty()) // End of paragraph, or maybe an image
+            if (text.empty()) // End of paragraph, or maybe an image
                 break;
-            else if (text == wxT(" ") || text.IsEmpty())
+            else if (text == wxT(" ") || text.empty())
                 i += direction;
             else
                 break;
@@ -1252,12 +1314,11 @@ long wxRichTextCtrl::FindNextWordPosition(int direction) const
         {
             // i is in character, not caret positions
             wxString text = GetBuffer().GetTextForRange(wxRichTextRange(i, i));
-            if (text != wxT(" ") /* && !text.IsEmpty() */)
+            if (text != wxT(" ") /* && !text.empty() */)
                 i += direction;
             else
             {
                 return i;
-                break;
             }
         }
         if (i < -1)
@@ -1273,19 +1334,20 @@ bool wxRichTextCtrl::WordLeft(int WXUNUSED(n), int flags)
     if (pos != m_caretPosition)
     {
         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;
     }
-    
+
     return false;
 }
 
@@ -1296,34 +1358,68 @@ bool wxRichTextCtrl::WordRight(int WXUNUSED(n), int flags)
     if (pos != m_caretPosition)
     {
         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;
     }
-    
+
     return false;
 }
 
 /// 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;
@@ -1343,12 +1439,13 @@ void wxRichTextCtrl::SetupScrollbars()
 
     int unitsY = maxHeight/pixelsPerUnit;
 
-    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;
-    
+
     // Move to previous scroll position if
     // possible
     SetScrollbars(0, pixelsPerUnit,
@@ -1366,10 +1463,10 @@ void wxRichTextCtrl::PaintBackground(wxDC& dc)
     // Clear the background
     dc.SetBrush(wxBrush(backgroundColour));
     dc.SetPen(*wxTRANSPARENT_PEN);
-    wxRect windowRect(wxPoint(0, 0), GetClientSize());    
+    wxRect windowRect(GetClientSize());
     windowRect.x -= 2; windowRect.y -= 2;
     windowRect.width += 4; windowRect.height += 4;
-    
+
     // We need to shift the rectangle to take into account
     // scrolling. Converting device to logical coordinates.
     CalcUnscrolledPosition(windowRect.x, windowRect.y, & windowRect.x, & windowRect.y);
@@ -1382,10 +1479,10 @@ bool wxRichTextCtrl::RecreateBuffer(const wxSize& size)
     wxSize sz = size;
     if (sz == wxDefaultSize)
         sz = GetClientSize();
-        
+
     if (sz.x < 1 || sz.y < 1)
         return false;
-        
+
     if (!m_bufferBitmap.Ok() || m_bufferBitmap.GetWidth() < sz.x || m_bufferBitmap.GetHeight() < sz.y)
         m_bufferBitmap = wxBitmap(sz.x, sz.y);
     return m_bufferBitmap.Ok();
@@ -1403,9 +1500,10 @@ bool wxRichTextCtrl::LoadFile(const wxString& filename, int type)
 
     DiscardEdits();
     SetInsertionPoint(0);
-    Layout();
+    LayoutContent();
     PositionCaret();
-    Refresh();
+    SetupScrollbars(true);
+    Refresh(false);
     SendUpdateEvent();
 
     if (success)
@@ -1413,7 +1511,7 @@ bool wxRichTextCtrl::LoadFile(const wxString& filename, int type)
     else
     {
         wxLogError(_("File couldn't be loaded."));
-        
+
         return false;
     }
 }
@@ -1473,7 +1571,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 +1585,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
@@ -1527,14 +1629,20 @@ wxRichTextCtrl::HitTest(const wxPoint& pt,
     ((wxRichTextCtrl*)this)->PrepareDC(dc);
 
     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;
+
+    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 +1712,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 +1723,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)
@@ -1629,13 +1737,12 @@ bool wxRichTextCtrl::WriteImage(const wxBitmap& bitmap, int bitmapType)
     if (bitmap.Ok())
     {
         wxRichTextImageBlock imageBlock;
-        
+
         wxImage image = bitmap.ConvertToImage();
         if (image.Ok() && imageBlock.MakeImageBlock(image, bitmapType))
             return WriteImage(imageBlock);
-        else
-            return false;
     }
+
     return false;
 }
 
@@ -1667,8 +1774,8 @@ void wxRichTextCtrl::Cut()
         GetBuffer().CopyToClipboard(range);
 
         DeleteSelectedContent();
-        Layout();
-        Refresh();
+        LayoutContent();
+        Refresh(false);
     }
 }
 
@@ -1677,10 +1784,10 @@ void wxRichTextCtrl::Paste()
     if (CanPaste())
     {
         BeginBatchUndo(_("Paste"));
-        
+
         long newPos = m_caretPosition;
         DeleteSelectedContent(& newPos);
-        
+
         GetBuffer().PasteFromClipboard(newPos);
 
         EndBatchUndo();
@@ -1791,8 +1898,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 +1926,9 @@ void wxRichTextCtrl::Remove(long from, long to)
         from,                           // New caret position
         this);
 
-    Layout();
+    LayoutContent();
     if (!IsFrozen())
-        Refresh();
+        Refresh(false);
 }
 
 bool wxRichTextCtrl::IsModified() const
@@ -2109,8 +2215,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 +2225,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 +2236,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());
+        }
     }
 }
 
@@ -2146,7 +2259,7 @@ bool wxRichTextCtrl::GetCaretPositionForIndex(long position, wxRect& rect)
 {
     wxClientDC dc(this);
     dc.SetFont(GetFont());
+
     PrepareDC(dc);
 
     wxPoint pt;
@@ -2157,8 +2270,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 +2284,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 +2300,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 +2316,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(wxPoint(0, 0), 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());
-    PrepareDC(dc);
+        int flags = wxRICHTEXT_FIXED_WIDTH|wxRICHTEXT_VARIABLE_HEIGHT;
+        if (onlyVisibleRect)
+        {
+            flags |= wxRICHTEXT_LAYOUT_SPECIFIED_RECT;
+            availableSpace.SetPosition(GetLogicalPoint(wxPoint(0, 0)));
+        }
 
-    GetBuffer().Defragment();
-    GetBuffer().UpdateRanges();     // If items were deleted, ranges need recalculation
-    GetBuffer().Layout(dc, availableSpace, wxRICHTEXT_FIXED_WIDTH|wxRICHTEXT_VARIABLE_HEIGHT);
-    GetBuffer().SetDirty(false);
-    
-    if (!IsFrozen())
-        SetupScrollbars();
+        wxClientDC dc(this);
+        dc.SetFont(GetFont());
+
+        PrepareDC(dc);
+
+        GetBuffer().Defragment();
+        GetBuffer().UpdateRanges();     // If items were deleted, ranges need recalculation
+        GetBuffer().Layout(dc, availableSpace, flags);
+        GetBuffer().SetDirty(false);
+
+        if (!IsFrozen())
+            SetupScrollbars();
+    }
 
     return true;
 }
@@ -2235,7 +2359,7 @@ bool wxRichTextCtrl::IsSelectionBold() const
         wxRichTextRange range = GetSelectionRange();
         attr.SetFlags(wxTEXT_ATTR_FONT_WEIGHT);
         attr.SetFontWeight(wxBOLD);
-        
+
         return HasCharacterAttributes(range, attr);
     }
     else
@@ -2262,7 +2386,7 @@ bool wxRichTextCtrl::IsSelectionItalics() const
         wxRichTextAttr attr;
         attr.SetFlags(wxTEXT_ATTR_FONT_ITALIC);
         attr.SetFontStyle(wxITALIC);
-        
+
         return HasCharacterAttributes(range, attr);
     }
     else
@@ -2289,7 +2413,7 @@ bool wxRichTextCtrl::IsSelectionUnderlined() const
         wxRichTextAttr attr;
         attr.SetFlags(wxTEXT_ATTR_FONT_UNDERLINE);
         attr.SetFontUnderlined(true);
-        
+
         return HasCharacterAttributes(range, attr);
     }
     else
@@ -2340,7 +2464,7 @@ bool wxRichTextCtrl::ApplyUnderlineToSelection()
 {
     wxRichTextAttr attr;
     attr.SetFlags(wxTEXT_ATTR_FONT_UNDERLINE);
-    attr.SetFontWeight(!IsSelectionUnderlined());
+    attr.SetFontUnderlined(!IsSelectionUnderlined());
 
     if (HasSelection())
         return SetStyle(GetSelectionRange(), attr);
@@ -2357,7 +2481,7 @@ bool wxRichTextCtrl::IsSelectionAligned(wxTextAttrAlignment alignment) const
         wxRichTextRange range = GetSelectionRange();
         wxRichTextAttr attr;
         attr.SetAlignment(alignment);
-        
+
         return HasParagraphAttributes(range, attr);
     }
     else
@@ -2397,10 +2521,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
-