diff --git a/docs/changes.txt b/docs/changes.txt index 04c318080a..3fecbee9ed 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -34,6 +34,7 @@ All (GUI): - Send events when toggling wxPropertyGrid nodes from keyboard (Armel Asselin). - Fix wxRearrangeList::Check() which asserted and misbehaved before. - Optimized wxRTC insertion and deletion when floating objects are present. +- Added on-demand image loading option to wxRTC. wxGTK: diff --git a/include/wx/richtext/richtextbuffer.h b/include/wx/richtext/richtextbuffer.h index e12dd055ee..32c2fa67c9 100644 --- a/include/wx/richtext/richtextbuffer.h +++ b/include/wx/richtext/richtextbuffer.h @@ -2211,7 +2211,8 @@ public: */ wxRichTextDrawingContext(wxRichTextBuffer* buffer); - void Init() { m_buffer = NULL; m_enableVirtualAttributes = true; m_enableImages = true; m_layingOut = false; } + void Init() + { m_buffer = NULL; m_enableVirtualAttributes = true; m_enableImages = true; m_layingOut = false; m_enableDelayedImageLoading = false; } /** Does this object have virtual attributes? @@ -2293,9 +2294,22 @@ public: bool GetLayingOut() const { return m_layingOut; } + /** + Enable or disable delayed image loading + */ + + void EnableDelayedImageLoading(bool b) { m_enableDelayedImageLoading = b; } + + /** + Returns @true if delayed image loading is enabled. + */ + + bool GetDelayedImageLoading() const { return m_enableDelayedImageLoading; } + wxRichTextBuffer* m_buffer; bool m_enableVirtualAttributes; bool m_enableImages; + bool m_enableDelayedImageLoading; bool m_layingOut; }; @@ -2428,7 +2442,6 @@ public: virtual bool Merge(wxRichTextObject* WXUNUSED(object), wxRichTextDrawingContext& WXUNUSED(context)) { return false; } /** - JACS Returns @true if this object can potentially be split, by virtue of having different virtual attributes for individual sub-objects. */ @@ -4742,6 +4755,8 @@ class WXDLLIMPEXP_RICHTEXT wxRichTextImage: public wxRichTextObject { DECLARE_DYNAMIC_CLASS(wxRichTextImage) public: + enum { ImageState_Unloaded, ImageState_Loaded, ImageState_Bad }; + // Constructors /** @@ -4824,12 +4839,12 @@ public: /** Sets the image cache. */ - void SetImageCache(const wxBitmap& bitmap) { m_imageCache = bitmap; m_originalImageSize = wxSize(bitmap.GetWidth(), bitmap.GetHeight()); } + void SetImageCache(const wxBitmap& bitmap) { m_imageCache = bitmap; m_originalImageSize = wxSize(bitmap.GetWidth(), bitmap.GetHeight()); m_imageState = ImageState_Loaded; } /** Resets the image cache. */ - void ResetImageCache() { m_imageCache = wxNullBitmap; m_originalImageSize = wxSize(-1, -1); } + void ResetImageCache() { m_imageCache = wxNullBitmap; m_originalImageSize = wxSize(-1, -1); m_imageState = ImageState_Unloaded; } /** Returns the image block containing the raw data. @@ -4851,7 +4866,12 @@ public: /** Creates a cached image at the required size. */ - virtual bool LoadImageCache(wxDC& dc, wxRichTextDrawingContext& context, bool resetCache = false, const wxSize& parentSize = wxDefaultSize); + virtual bool LoadImageCache(wxDC& dc, wxRichTextDrawingContext& context, wxSize& retImageSize, bool resetCache = false, const wxSize& parentSize = wxDefaultSize); + + /** + Do the loading and scaling + */ + virtual bool LoadAndScaleImageCache(wxImage& image, const wxSize& sz, bool delayLoading, bool& changed); /** Gets the original image size. @@ -4863,10 +4883,21 @@ public: */ void SetOriginalImageSize(const wxSize& sz) { m_originalImageSize = sz; } + /** + Gets the image state. + */ + int GetImageState() const { return m_imageState; } + + /** + Sets the image state. + */ + void SetImageState(int state) { m_imageState = state; } + protected: wxRichTextImageBlock m_imageBlock; wxBitmap m_imageCache; wxSize m_originalImageSize; + int m_imageState; }; class WXDLLIMPEXP_FWD_RICHTEXT wxRichTextCommand; diff --git a/include/wx/richtext/richtextctrl.h b/include/wx/richtext/richtextctrl.h index 3f3ef8bda2..52fe8f17b4 100644 --- a/include/wx/richtext/richtextctrl.h +++ b/include/wx/richtext/richtextctrl.h @@ -17,7 +17,7 @@ #include "wx/scrolwin.h" #include "wx/caret.h" - +#include "wx/timer.h" #include "wx/textctrl.h" #if wxUSE_DRAG_AND_DROP @@ -79,6 +79,8 @@ class WXDLLIMPEXP_FWD_RICHTEXT wxRichTextStyleDefinition; #define wxRICHTEXT_DEFAULT_DELAYED_LAYOUT_THRESHOLD 20000 // Milliseconds before layout occurs after resize #define wxRICHTEXT_DEFAULT_LAYOUT_INTERVAL 50 +// Milliseconds before delayed image processing occurs +#define wxRICHTEXT_DEFAULT_DELAYED_IMAGE_PROCESSING_INTERVAL 200 /* Identifiers */ @@ -1980,6 +1982,12 @@ public: */ bool RefreshForSelectionChange(const wxRichTextSelection& oldSelection, const wxRichTextSelection& newSelection); + /** + Overrides standard refresh in order to provoke delayed image loading. + */ + virtual void Refresh( bool eraseBackground = true, + const wxRect *rect = (const wxRect *) NULL ); + /** Sets the caret position. @@ -2134,6 +2142,38 @@ public: bool GetImagesEnabled() const { return m_enableImages; } + /** + Enable or disable delayed image loading + */ + + void EnableDelayedImageLoading(bool b) { m_enableDelayedImageLoading = b; } + + /** + Returns @true if delayed image loading is enabled. + */ + + bool GetDelayedImageLoading() const { return m_enableDelayedImageLoading; } + + /** + Gets the flag indicating that delayed image processing is required. + */ + bool GetDelayedImageProcessingRequired() const { return m_delayedImageProcessingRequired; } + + /** + Sets the flag indicating that delayed image processing is required. + */ + void SetDelayedImageProcessingRequired(bool b) { m_delayedImageProcessingRequired = b; } + + /** + Returns the last time delayed image processing was performed. + */ + wxLongLong GetDelayedImageProcessingTime() const { return m_delayedImageProcessingTime; } + + /** + Sets the last time delayed image processing was performed. + */ + void SetDelayedImageProcessingTime(wxLongLong t) { m_delayedImageProcessingTime = t; } + #ifdef DOXYGEN /** Returns the content of the entire control as a string. @@ -2209,6 +2249,22 @@ public: // implement wxTextEntry methods virtual wxString DoGetValue() const; + /** + Do delayed image loading and garbage-collect other images + */ + bool ProcessDelayedImageLoading(bool refresh); + bool ProcessDelayedImageLoading(const wxRect& screenRect, wxRichTextParagraphLayoutBox* box, int& loadCount); + + /** + Request delayed image processing. + */ + void RequestDelayedImageProcessing(); + + /** + Respond to timer events. + */ + void OnTimer(wxTimerEvent& event); + protected: // implement the wxTextEntry pure virtual method virtual wxWindow *GetEditableWindow() { return this; } @@ -2336,6 +2392,12 @@ protected: /// Whether images are enabled for this control bool m_enableImages; + + /// Whether delayed image loading is enabled for this control + bool m_enableDelayedImageLoading; + bool m_delayedImageProcessingRequired; + wxLongLong m_delayedImageProcessingTime; + wxTimer m_delayedImageProcessingTimer; }; #if wxUSE_DRAG_AND_DROP diff --git a/interface/wx/richtext/richtextbuffer.h b/interface/wx/richtext/richtextbuffer.h index 95a1076cf7..1d2365b528 100644 --- a/interface/wx/richtext/richtextbuffer.h +++ b/interface/wx/richtext/richtextbuffer.h @@ -2123,9 +2123,35 @@ public: bool GetImagesEnabled() const { return m_enableImages; } + /** + Set laying out flag + */ + + void SetLayingOut(bool b) { m_layingOut = b; } + + /** + Returns @true if laying out. + */ + + bool GetLayingOut() const { return m_layingOut; } + + /** + Enable or disable delayed image loading + */ + + void EnableDelayedImageLoading(bool b) { m_enableDelayedImageLoading = b; } + + /** + Returns @true if delayed image loading is enabled. + */ + + bool GetDelayedImageLoading() const { return m_enableDelayedImageLoading; } + wxRichTextBuffer* m_buffer; bool m_enableVirtualAttributes; bool m_enableImages; + bool m_enableDelayedImageLoading; + bool m_layingOut; }; /** @@ -4671,11 +4697,38 @@ public: /** Creates a cached image at the required size. */ - virtual bool LoadImageCache(wxDC& dc, wxRichTextDrawingContext& context, bool resetCache = false, const wxSize& parentSize = wxDefaultSize); + virtual bool LoadImageCache(wxDC& dc, wxRichTextDrawingContext& context, wxSize& retImageSize, bool resetCache = false, const wxSize& parentSize = wxDefaultSize); + + /** + Do the loading and scaling + */ + virtual bool LoadAndScaleImageCache(wxImage& image, const wxSize& sz, bool delayLoading, bool& changed); + + /** + Gets the original image size. + */ + wxSize GetOriginalImageSize() const { return m_originalImageSize; } + + /** + Sets the original image size. + */ + void SetOriginalImageSize(const wxSize& sz) { m_originalImageSize = sz; } + + /** + Gets the image state. + */ + int GetImageState() const { return m_imageState; } + + /** + Sets the image state. + */ + void SetImageState(int state) { m_imageState = state; } protected: wxRichTextImageBlock m_imageBlock; wxBitmap m_imageCache; + wxSize m_originalImageSize; + int m_imageState; }; class wxRichTextCommand; diff --git a/interface/wx/richtext/richtextctrl.h b/interface/wx/richtext/richtextctrl.h index 4c6bca1e5f..2ada97c629 100644 --- a/interface/wx/richtext/richtextctrl.h +++ b/interface/wx/richtext/richtextctrl.h @@ -55,6 +55,8 @@ #define wxRICHTEXT_DEFAULT_DELAYED_LAYOUT_THRESHOLD 20000 // Milliseconds before layout occurs after resize #define wxRICHTEXT_DEFAULT_LAYOUT_INTERVAL 50 +// Milliseconds before delayed image processing occurs +#define wxRICHTEXT_DEFAULT_DELAYED_IMAGE_PROCESSING_INTERVAL 200 /* Identifiers */ @@ -2044,6 +2046,38 @@ public: bool GetImagesEnabled() const; + /** + Enable or disable delayed image loading + */ + + void EnableDelayedImageLoading(bool b) { m_enableDelayedImageLoading = b; } + + /** + Returns @true if delayed image loading is enabled. + */ + + bool GetDelayedImageLoading() const { return m_enableDelayedImageLoading; } + + /** + Gets the flag indicating that delayed image processing is required. + */ + bool GetDelayedImageProcessingRequired() const { return m_delayedImageProcessingRequired; } + + /** + Sets the flag indicating that delayed image processing is required. + */ + void SetDelayedImageProcessingRequired(bool b) { m_delayedImageProcessingRequired = b; } + + /** + Returns the last time delayed image processing was performed. + */ + wxLongLong GetDelayedImageProcessingTime() const { return m_delayedImageProcessingTime; } + + /** + Sets the last time delayed image processing was performed. + */ + void SetDelayedImageProcessingTime(wxLongLong t) { m_delayedImageProcessingTime = t; } + /** Returns the caret position since the default formatting was changed. As soon as this position changes, we no longer reflect the default style @@ -2137,6 +2171,22 @@ public: // implement wxTextEntry methods virtual wxString DoGetValue() const; + /** + Do delayed image loading and garbage-collect other images + */ + bool ProcessDelayedImageLoading(bool refresh); + bool ProcessDelayedImageLoading(const wxRect& screenRect, wxRichTextParagraphLayoutBox* box, int& loadCount); + + /** + Request delayed image processing. + */ + void RequestDelayedImageProcessing(); + + /** + Respond to timer events. + */ + void OnTimer(wxTimerEvent& event); + protected: // implement the wxTextEntry pure virtual method virtual wxWindow *GetEditableWindow(); @@ -2220,6 +2270,23 @@ protected: /// The object that currently has the editing focus wxRichTextParagraphLayoutBox* m_focusObject; + + /// An overall scale factor + double m_scale; + + /// Variables for scrollbar hysteresis detection + wxSize m_lastWindowSize; + int m_setupScrollbarsCount; + int m_setupScrollbarsCountInOnSize; + + /// Whether images are enabled for this control + bool m_enableImages; + + /// Whether delayed image loading is enabled for this control + bool m_enableDelayedImageLoading; + bool m_delayedImageProcessingRequired; + wxLongLong m_delayedImageProcessingTime; + wxTimer m_delayedImageProcessingTimer; }; /** diff --git a/src/richtext/richtextbuffer.cpp b/src/richtext/richtextbuffer.cpp index 5c5de592db..ec47ad7b94 100644 --- a/src/richtext/richtextbuffer.cpp +++ b/src/richtext/richtextbuffer.cpp @@ -5150,7 +5150,8 @@ bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, co } while (doLoop); - if (child->IsTopLevel()) + // 2014-03-08: also need to set object positions + //if (child->IsTopLevel()) { // We can move it to the correct position at this point // TODO: probably need to add margin @@ -12213,17 +12214,24 @@ wxRichTextImage::~wxRichTextImage() void wxRichTextImage::Init() { m_originalImageSize = wxSize(-1, -1); + m_imageState = ImageState_Unloaded; } /// Create a cached image at the required size -bool wxRichTextImage::LoadImageCache(wxDC& dc, wxRichTextDrawingContext& context, bool resetCache, const wxSize& parentSize) +bool wxRichTextImage::LoadImageCache(wxDC& dc, wxRichTextDrawingContext& context, wxSize& retImageSize, bool resetCache, const wxSize& parentSize) { if (!m_imageBlock.IsOk()) + { + m_imageState = ImageState_Bad; return false; + } // Don't repeat unless absolutely necessary if (m_imageCache.IsOk() && !resetCache && !context.GetLayingOut()) + { + retImageSize = wxSize(m_imageCache.GetWidth(), m_imageCache.GetHeight()); return true; + } if (!context.GetImagesEnabled()) { @@ -12231,7 +12239,9 @@ bool wxRichTextImage::LoadImageCache(wxDC& dc, wxRichTextDrawingContext& context { wxBitmap bitmap(image_placeholder24x24_xpm); m_imageCache = bitmap; + m_imageState = ImageState_Loaded; } + retImageSize = wxSize(m_imageCache.GetWidth(), m_imageCache.GetHeight()); return true; } @@ -12243,13 +12253,15 @@ bool wxRichTextImage::LoadImageCache(wxDC& dc, wxRichTextDrawingContext& context if (resetCache || m_originalImageSize.GetWidth() <= 0 || m_originalImageSize.GetHeight() <= 0) { m_imageCache = wxNullBitmap; + m_imageState = ImageState_Unloaded; - m_imageBlock.Load(image); - if (!image.IsOk()) + if (!m_imageBlock.Load(image) || !image.IsOk()) { wxBitmap bitmap(image_placeholder24x24_xpm); m_imageCache = bitmap; m_originalImageSize = wxSize(bitmap.GetWidth(), bitmap.GetHeight()); + m_imageState = ImageState_Bad; + retImageSize = wxSize(m_imageCache.GetWidth(), m_imageCache.GetHeight()); return false; } @@ -12359,24 +12371,49 @@ bool wxRichTextImage::LoadImageCache(wxDC& dc, wxRichTextDrawingContext& context width = wxMax(1, width); height = wxMax(1, height); + retImageSize = wxSize(width, height); + + bool changed = false; + return LoadAndScaleImageCache(image, retImageSize, context.GetDelayedImageLoading(), changed); +} + +// Do the loading and scaling +bool wxRichTextImage::LoadAndScaleImageCache(wxImage& image, const wxSize& sz, bool delayLoading, bool& changed) +{ + int width = sz.x; + int height = sz.y; + if (m_imageCache.IsOk() && m_imageCache.GetWidth() == width && m_imageCache.GetHeight() == height) { // Do nothing, we didn't need to change the image cache + changed = false; } else { + changed = true; + + if (delayLoading) + { + if (m_imageCache.IsOk()) + m_imageCache = wxNullBitmap; + m_imageState = ImageState_Unloaded; + return true; + } + if (!image.IsOk()) { - m_imageBlock.Load(image); - if (!image.IsOk()) + if (!m_imageBlock.Load(image) || !image.IsOk()) { wxBitmap bitmap(image_placeholder24x24_xpm); m_imageCache = bitmap; m_originalImageSize = wxSize(bitmap.GetWidth(), bitmap.GetHeight()); + m_imageState = ImageState_Bad; return false; } } + m_originalImageSize = wxSize(image.GetWidth(), image.GetHeight()); + if (image.GetWidth() == width && image.GetHeight() == height) m_imageCache = wxBitmap(image); else @@ -12397,6 +12434,11 @@ bool wxRichTextImage::LoadImageCache(wxDC& dc, wxRichTextDrawingContext& context } } + if (m_imageCache.IsOk()) + m_imageState = ImageState_Loaded; + else + m_imageState = ImageState_Bad; + return m_imageCache.IsOk(); } @@ -12406,9 +12448,6 @@ bool wxRichTextImage::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wx if (!IsShown()) return true; - if (!m_imageCache.IsOk()) - return false; - wxRichTextAttr attr(GetAttributes()); AdjustAttributes(attr, context); @@ -12419,7 +12458,14 @@ bool wxRichTextImage::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wx marginRect = rect; // outer rectangle, will calculate contentRect GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect); - dc.DrawBitmap(m_imageCache, contentRect.x, contentRect.y, true); + if (m_imageCache.IsOk()) + dc.DrawBitmap(m_imageCache, contentRect.x, contentRect.y, true); + else + { + dc.SetPen(*wxLIGHT_GREY_PEN); + dc.SetBrush(*wxTRANSPARENT_BRUSH); + dc.DrawRectangle(contentRect); + } if (selection.WithinSelection(GetRange().GetStart(), this)) { @@ -12436,10 +12482,10 @@ bool wxRichTextImage::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wx /// Lay the item out bool wxRichTextImage::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int WXUNUSED(style)) { - if (!LoadImageCache(dc, context, false, parentRect.GetSize())) + wxSize imageSize; + if (!LoadImageCache(dc, context, imageSize, false, parentRect.GetSize())) return false; - wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight()); wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect; contentRect = wxRect(wxPoint(0,0), imageSize); @@ -12465,7 +12511,8 @@ bool wxRichTextImage::GetRangeSize(const wxRichTextRange& range, wxSize& size, i if (!range.IsWithin(GetRange())) return false; - if (!((wxRichTextImage*)this)->LoadImageCache(dc, context, false, parentSize)) + wxSize imageSize; + if (!((wxRichTextImage*)this)->LoadImageCache(dc, context, imageSize, false, parentSize)) { size.x = 0; size.y = 0; if (partialExtents) @@ -12476,7 +12523,6 @@ bool wxRichTextImage::GetRangeSize(const wxRichTextRange& range, wxSize& size, i wxRichTextAttr attr(GetAttributes()); ((wxRichTextObject*)this)->AdjustAttributes(attr, context); - wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight()); wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect; contentRect = wxRect(wxPoint(0,0), imageSize); GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect); @@ -15040,6 +15086,7 @@ wxRichTextDrawingContext::wxRichTextDrawingContext(wxRichTextBuffer* buffer) { EnableVirtualAttributes(m_buffer->GetRichTextCtrl()->GetVirtualAttributesEnabled()); m_enableImages = m_buffer->GetRichTextCtrl()->GetImagesEnabled(); + m_enableDelayedImageLoading = m_buffer->GetRichTextCtrl()->GetDelayedImageLoading(); } } diff --git a/src/richtext/richtextctrl.cpp b/src/richtext/richtextctrl.cpp index 2cb5e7b505..3057450c3c 100644 --- a/src/richtext/richtextctrl.cpp +++ b/src/richtext/richtextctrl.cpp @@ -177,6 +177,7 @@ BEGIN_EVENT_TABLE( wxRichTextCtrl, wxControl ) EVT_MOUSE_CAPTURE_LOST(wxRichTextCtrl::OnCaptureLost) EVT_CONTEXT_MENU(wxRichTextCtrl::OnContextMenu) EVT_SYS_COLOUR_CHANGED(wxRichTextCtrl::OnSysColourChanged) + EVT_TIMER(wxID_ANY, wxRichTextCtrl::OnTimer) EVT_MENU(wxID_UNDO, wxRichTextCtrl::OnUndo) EVT_UPDATE_UI(wxID_UNDO, wxRichTextCtrl::OnUpdateUndo) @@ -350,6 +351,8 @@ wxRichTextCtrl::~wxRichTextCtrl() GetBuffer().RemoveEventHandler(this); delete m_contextMenu; + + m_delayedImageProcessingTimer.Stop(); } /// Member initialisation @@ -382,6 +385,10 @@ void wxRichTextCtrl::Init() m_setupScrollbarsCountInOnSize = 0; m_enableImages = true; + + m_enableDelayedImageLoading = false; + m_delayedImageProcessingRequired = false; + m_delayedImageProcessingTime = 0; } void wxRichTextCtrl::DoThaw() @@ -2602,6 +2609,9 @@ void wxRichTextCtrl::OnSize(wxSizeEvent& event) // OnSize was the source of a scrollbar change. m_setupScrollbarsCountInOnSize = m_setupScrollbarsCount; + if (GetDelayedImageLoading()) + RequestDelayedImageProcessing(); + event.Skip(); } @@ -2642,6 +2652,16 @@ void wxRichTextCtrl::OnIdle(wxIdleEvent& event) Refresh(false); } + const int imageProcessingInterval = wxRICHTEXT_DEFAULT_DELAYED_IMAGE_PROCESSING_INTERVAL; + + if (m_enableDelayedImageLoading && m_delayedImageProcessingRequired && (wxGetLocalTimeMillis() > (m_delayedImageProcessingTime + imageProcessingInterval))) + { + m_delayedImageProcessingTimer.Stop(); + m_delayedImageProcessingRequired = false; + m_delayedImageProcessingTime = 0; + ProcessDelayedImageLoading(true); + } + if (m_caretPositionForDefaultStyle != -2) { // If the caret position has changed, no longer reflect the default style @@ -4045,6 +4065,9 @@ bool wxRichTextCtrl::LayoutContent(bool onlyVisibleRect) if (!IsFrozen() && !onlyVisibleRect) SetupScrollbars(); + + if (GetDelayedImageLoading()) + RequestDelayedImageProcessing(); } return true; @@ -4668,6 +4691,15 @@ bool wxRichTextCtrl::RefreshForSelectionChange(const wxRichTextSelection& oldSel return true; } +// Overrides standard refresh in order to provoke delayed image loading. +void wxRichTextCtrl::Refresh( bool eraseBackground, const wxRect *rect) +{ + if (GetDelayedImageLoading()) + RequestDelayedImageProcessing(); + + wxWindow::Refresh(eraseBackground, rect); +} + // margins functions bool wxRichTextCtrl::DoSetMargins(const wxPoint& pt) { @@ -4906,6 +4938,112 @@ wxRect wxRichTextCtrl::GetScaledRect(const wxRect& rect) const (int) (0.5 + double(rect.width) * GetScale()), (int) (0.5 + double(rect.height) * GetScale())); } +// Do delayed image loading and garbage-collect other images +bool wxRichTextCtrl::ProcessDelayedImageLoading(bool refresh) +{ + int loadCount = 0; + + wxSize clientSize = GetUnscaledSize(GetClientSize()); + wxPoint firstVisiblePt = GetUnscaledPoint(GetFirstVisiblePoint()); + wxRect screenRect(firstVisiblePt, clientSize); + + // Expand screen rect so that we actually process images in the vicinity, + // for smoother paging and scrolling. + screenRect.y -= (clientSize.y*3); + screenRect.height += (clientSize.y*6); + ProcessDelayedImageLoading(screenRect, & GetBuffer(), loadCount); + + if (loadCount > 0 && refresh) + { + wxWindow::Refresh(false); + } + + return loadCount > 0; +} + +bool wxRichTextCtrl::ProcessDelayedImageLoading(const wxRect& screenRect, wxRichTextParagraphLayoutBox* box, int& loadCount) +{ + if (!box || !box->IsShown()) + return true; + + wxRichTextObjectList::compatibility_iterator node = box->GetChildren().GetFirst(); + while (node) + { + // Could be a cell or a paragraph + wxRichTextCompositeObject* composite = wxDynamicCast(node->GetData(), wxRichTextCompositeObject); + if (composite->IsTopLevel()) + ProcessDelayedImageLoading(screenRect, wxDynamicCast(composite, wxRichTextParagraphLayoutBox), loadCount); + else // assume a paragraph + { + wxRichTextObjectList::compatibility_iterator node2 = composite->GetChildren().GetFirst(); + while (node2) + { + wxRichTextObject* obj = node2->GetData(); + if (obj->IsTopLevel()) + ProcessDelayedImageLoading(screenRect, wxDynamicCast(obj, wxRichTextParagraphLayoutBox), loadCount); + else + { + wxRichTextImage* imageObj = wxDynamicCast(obj, wxRichTextImage); + if (imageObj && imageObj->IsShown()) + { + const wxRect& rect(imageObj->GetRect()); + if ((rect.GetBottom() < screenRect.GetTop()) || (rect.GetTop() > screenRect.GetBottom())) + { + // Off-screen + imageObj->ResetImageCache(); + } + else + { + // On-screen + wxRichTextDrawingContext context(& GetBuffer()); + context.SetLayingOut(true); + context.EnableDelayedImageLoading(false); + + wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect; + marginRect = imageObj->GetRect(); // outer rectangle, will calculate contentRect + if (marginRect.GetSize() != wxDefaultSize) + { + wxClientDC dc(this); + wxRichTextAttr attr(imageObj->GetAttributes()); + imageObj->AdjustAttributes(attr, context); + imageObj->GetBoxRects(dc, & GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect); + + wxImage image; + bool changed = false; + if (imageObj->LoadAndScaleImageCache(image, contentRect.GetSize(), false, changed) && changed) + { + loadCount ++; + } + } + } + } + } + node2 = node2->GetNext(); + } + } + + node = node->GetNext(); + } + + return true; +} + +void wxRichTextCtrl::RequestDelayedImageProcessing() +{ + SetDelayedImageProcessingRequired(true); + SetDelayedImageProcessingTime(wxGetLocalTimeMillis()); + m_delayedImageProcessingTimer.SetOwner(this, GetId()); + m_delayedImageProcessingTimer.Start(wxRICHTEXT_DEFAULT_DELAYED_IMAGE_PROCESSING_INTERVAL); +} + +void wxRichTextCtrl::OnTimer(wxTimerEvent& event) +{ + if (event.GetId() == GetId()) + wxWakeUpIdle(); + else + event.Skip(); +} + #if wxRICHTEXT_USE_OWN_CARET // ----------------------------------------------------------------------------