Added on-demand image loading option to wxRTC.
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@76110 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
@@ -34,6 +34,7 @@ All (GUI):
|
|||||||
- Send events when toggling wxPropertyGrid nodes from keyboard (Armel Asselin).
|
- Send events when toggling wxPropertyGrid nodes from keyboard (Armel Asselin).
|
||||||
- Fix wxRearrangeList::Check() which asserted and misbehaved before.
|
- Fix wxRearrangeList::Check() which asserted and misbehaved before.
|
||||||
- Optimized wxRTC insertion and deletion when floating objects are present.
|
- Optimized wxRTC insertion and deletion when floating objects are present.
|
||||||
|
- Added on-demand image loading option to wxRTC.
|
||||||
|
|
||||||
wxGTK:
|
wxGTK:
|
||||||
|
|
||||||
|
@@ -2211,7 +2211,8 @@ public:
|
|||||||
*/
|
*/
|
||||||
wxRichTextDrawingContext(wxRichTextBuffer* buffer);
|
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?
|
Does this object have virtual attributes?
|
||||||
@@ -2293,9 +2294,22 @@ public:
|
|||||||
|
|
||||||
bool GetLayingOut() const { return m_layingOut; }
|
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;
|
wxRichTextBuffer* m_buffer;
|
||||||
bool m_enableVirtualAttributes;
|
bool m_enableVirtualAttributes;
|
||||||
bool m_enableImages;
|
bool m_enableImages;
|
||||||
|
bool m_enableDelayedImageLoading;
|
||||||
bool m_layingOut;
|
bool m_layingOut;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2428,7 +2442,6 @@ public:
|
|||||||
virtual bool Merge(wxRichTextObject* WXUNUSED(object), wxRichTextDrawingContext& WXUNUSED(context)) { return false; }
|
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
|
Returns @true if this object can potentially be split, by virtue of having
|
||||||
different virtual attributes for individual sub-objects.
|
different virtual attributes for individual sub-objects.
|
||||||
*/
|
*/
|
||||||
@@ -4742,6 +4755,8 @@ class WXDLLIMPEXP_RICHTEXT wxRichTextImage: public wxRichTextObject
|
|||||||
{
|
{
|
||||||
DECLARE_DYNAMIC_CLASS(wxRichTextImage)
|
DECLARE_DYNAMIC_CLASS(wxRichTextImage)
|
||||||
public:
|
public:
|
||||||
|
enum { ImageState_Unloaded, ImageState_Loaded, ImageState_Bad };
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -4824,12 +4839,12 @@ public:
|
|||||||
/**
|
/**
|
||||||
Sets the image cache.
|
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.
|
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.
|
Returns the image block containing the raw data.
|
||||||
@@ -4851,7 +4866,12 @@ public:
|
|||||||
/**
|
/**
|
||||||
Creates a cached image at the required size.
|
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.
|
Gets the original image size.
|
||||||
@@ -4863,10 +4883,21 @@ public:
|
|||||||
*/
|
*/
|
||||||
void SetOriginalImageSize(const wxSize& sz) { m_originalImageSize = sz; }
|
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:
|
protected:
|
||||||
wxRichTextImageBlock m_imageBlock;
|
wxRichTextImageBlock m_imageBlock;
|
||||||
wxBitmap m_imageCache;
|
wxBitmap m_imageCache;
|
||||||
wxSize m_originalImageSize;
|
wxSize m_originalImageSize;
|
||||||
|
int m_imageState;
|
||||||
};
|
};
|
||||||
|
|
||||||
class WXDLLIMPEXP_FWD_RICHTEXT wxRichTextCommand;
|
class WXDLLIMPEXP_FWD_RICHTEXT wxRichTextCommand;
|
||||||
|
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
#include "wx/scrolwin.h"
|
#include "wx/scrolwin.h"
|
||||||
#include "wx/caret.h"
|
#include "wx/caret.h"
|
||||||
|
#include "wx/timer.h"
|
||||||
#include "wx/textctrl.h"
|
#include "wx/textctrl.h"
|
||||||
|
|
||||||
#if wxUSE_DRAG_AND_DROP
|
#if wxUSE_DRAG_AND_DROP
|
||||||
@@ -79,6 +79,8 @@ class WXDLLIMPEXP_FWD_RICHTEXT wxRichTextStyleDefinition;
|
|||||||
#define wxRICHTEXT_DEFAULT_DELAYED_LAYOUT_THRESHOLD 20000
|
#define wxRICHTEXT_DEFAULT_DELAYED_LAYOUT_THRESHOLD 20000
|
||||||
// Milliseconds before layout occurs after resize
|
// Milliseconds before layout occurs after resize
|
||||||
#define wxRICHTEXT_DEFAULT_LAYOUT_INTERVAL 50
|
#define wxRICHTEXT_DEFAULT_LAYOUT_INTERVAL 50
|
||||||
|
// Milliseconds before delayed image processing occurs
|
||||||
|
#define wxRICHTEXT_DEFAULT_DELAYED_IMAGE_PROCESSING_INTERVAL 200
|
||||||
|
|
||||||
/* Identifiers
|
/* Identifiers
|
||||||
*/
|
*/
|
||||||
@@ -1980,6 +1982,12 @@ public:
|
|||||||
*/
|
*/
|
||||||
bool RefreshForSelectionChange(const wxRichTextSelection& oldSelection, const wxRichTextSelection& newSelection);
|
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.
|
Sets the caret position.
|
||||||
|
|
||||||
@@ -2134,6 +2142,38 @@ public:
|
|||||||
|
|
||||||
bool GetImagesEnabled() const { return m_enableImages; }
|
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
|
#ifdef DOXYGEN
|
||||||
/**
|
/**
|
||||||
Returns the content of the entire control as a string.
|
Returns the content of the entire control as a string.
|
||||||
@@ -2209,6 +2249,22 @@ public:
|
|||||||
// implement wxTextEntry methods
|
// implement wxTextEntry methods
|
||||||
virtual wxString DoGetValue() const;
|
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:
|
protected:
|
||||||
// implement the wxTextEntry pure virtual method
|
// implement the wxTextEntry pure virtual method
|
||||||
virtual wxWindow *GetEditableWindow() { return this; }
|
virtual wxWindow *GetEditableWindow() { return this; }
|
||||||
@@ -2336,6 +2392,12 @@ protected:
|
|||||||
|
|
||||||
/// Whether images are enabled for this control
|
/// Whether images are enabled for this control
|
||||||
bool m_enableImages;
|
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
|
#if wxUSE_DRAG_AND_DROP
|
||||||
|
@@ -2123,9 +2123,35 @@ public:
|
|||||||
|
|
||||||
bool GetImagesEnabled() const { return m_enableImages; }
|
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;
|
wxRichTextBuffer* m_buffer;
|
||||||
bool m_enableVirtualAttributes;
|
bool m_enableVirtualAttributes;
|
||||||
bool m_enableImages;
|
bool m_enableImages;
|
||||||
|
bool m_enableDelayedImageLoading;
|
||||||
|
bool m_layingOut;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -4671,11 +4697,38 @@ public:
|
|||||||
/**
|
/**
|
||||||
Creates a cached image at the required size.
|
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:
|
protected:
|
||||||
wxRichTextImageBlock m_imageBlock;
|
wxRichTextImageBlock m_imageBlock;
|
||||||
wxBitmap m_imageCache;
|
wxBitmap m_imageCache;
|
||||||
|
wxSize m_originalImageSize;
|
||||||
|
int m_imageState;
|
||||||
};
|
};
|
||||||
|
|
||||||
class wxRichTextCommand;
|
class wxRichTextCommand;
|
||||||
|
@@ -55,6 +55,8 @@
|
|||||||
#define wxRICHTEXT_DEFAULT_DELAYED_LAYOUT_THRESHOLD 20000
|
#define wxRICHTEXT_DEFAULT_DELAYED_LAYOUT_THRESHOLD 20000
|
||||||
// Milliseconds before layout occurs after resize
|
// Milliseconds before layout occurs after resize
|
||||||
#define wxRICHTEXT_DEFAULT_LAYOUT_INTERVAL 50
|
#define wxRICHTEXT_DEFAULT_LAYOUT_INTERVAL 50
|
||||||
|
// Milliseconds before delayed image processing occurs
|
||||||
|
#define wxRICHTEXT_DEFAULT_DELAYED_IMAGE_PROCESSING_INTERVAL 200
|
||||||
|
|
||||||
/* Identifiers
|
/* Identifiers
|
||||||
*/
|
*/
|
||||||
@@ -2044,6 +2046,38 @@ public:
|
|||||||
|
|
||||||
bool GetImagesEnabled() const;
|
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
|
Returns the caret position since the default formatting was changed. As
|
||||||
soon as this position changes, we no longer reflect the default style
|
soon as this position changes, we no longer reflect the default style
|
||||||
@@ -2137,6 +2171,22 @@ public:
|
|||||||
// implement wxTextEntry methods
|
// implement wxTextEntry methods
|
||||||
virtual wxString DoGetValue() const;
|
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:
|
protected:
|
||||||
// implement the wxTextEntry pure virtual method
|
// implement the wxTextEntry pure virtual method
|
||||||
virtual wxWindow *GetEditableWindow();
|
virtual wxWindow *GetEditableWindow();
|
||||||
@@ -2220,6 +2270,23 @@ protected:
|
|||||||
|
|
||||||
/// The object that currently has the editing focus
|
/// The object that currently has the editing focus
|
||||||
wxRichTextParagraphLayoutBox* m_focusObject;
|
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -5150,7 +5150,8 @@ bool wxRichTextParagraph::Layout(wxDC& dc, wxRichTextDrawingContext& context, co
|
|||||||
}
|
}
|
||||||
while (doLoop);
|
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
|
// We can move it to the correct position at this point
|
||||||
// TODO: probably need to add margin
|
// TODO: probably need to add margin
|
||||||
@@ -12213,17 +12214,24 @@ wxRichTextImage::~wxRichTextImage()
|
|||||||
void wxRichTextImage::Init()
|
void wxRichTextImage::Init()
|
||||||
{
|
{
|
||||||
m_originalImageSize = wxSize(-1, -1);
|
m_originalImageSize = wxSize(-1, -1);
|
||||||
|
m_imageState = ImageState_Unloaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a cached image at the required size
|
/// 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())
|
if (!m_imageBlock.IsOk())
|
||||||
|
{
|
||||||
|
m_imageState = ImageState_Bad;
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Don't repeat unless absolutely necessary
|
// Don't repeat unless absolutely necessary
|
||||||
if (m_imageCache.IsOk() && !resetCache && !context.GetLayingOut())
|
if (m_imageCache.IsOk() && !resetCache && !context.GetLayingOut())
|
||||||
|
{
|
||||||
|
retImageSize = wxSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (!context.GetImagesEnabled())
|
if (!context.GetImagesEnabled())
|
||||||
{
|
{
|
||||||
@@ -12231,7 +12239,9 @@ bool wxRichTextImage::LoadImageCache(wxDC& dc, wxRichTextDrawingContext& context
|
|||||||
{
|
{
|
||||||
wxBitmap bitmap(image_placeholder24x24_xpm);
|
wxBitmap bitmap(image_placeholder24x24_xpm);
|
||||||
m_imageCache = bitmap;
|
m_imageCache = bitmap;
|
||||||
|
m_imageState = ImageState_Loaded;
|
||||||
}
|
}
|
||||||
|
retImageSize = wxSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -12243,13 +12253,15 @@ bool wxRichTextImage::LoadImageCache(wxDC& dc, wxRichTextDrawingContext& context
|
|||||||
if (resetCache || m_originalImageSize.GetWidth() <= 0 || m_originalImageSize.GetHeight() <= 0)
|
if (resetCache || m_originalImageSize.GetWidth() <= 0 || m_originalImageSize.GetHeight() <= 0)
|
||||||
{
|
{
|
||||||
m_imageCache = wxNullBitmap;
|
m_imageCache = wxNullBitmap;
|
||||||
|
m_imageState = ImageState_Unloaded;
|
||||||
|
|
||||||
m_imageBlock.Load(image);
|
if (!m_imageBlock.Load(image) || !image.IsOk())
|
||||||
if (!image.IsOk())
|
|
||||||
{
|
{
|
||||||
wxBitmap bitmap(image_placeholder24x24_xpm);
|
wxBitmap bitmap(image_placeholder24x24_xpm);
|
||||||
m_imageCache = bitmap;
|
m_imageCache = bitmap;
|
||||||
m_originalImageSize = wxSize(bitmap.GetWidth(), bitmap.GetHeight());
|
m_originalImageSize = wxSize(bitmap.GetWidth(), bitmap.GetHeight());
|
||||||
|
m_imageState = ImageState_Bad;
|
||||||
|
retImageSize = wxSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -12359,24 +12371,49 @@ bool wxRichTextImage::LoadImageCache(wxDC& dc, wxRichTextDrawingContext& context
|
|||||||
width = wxMax(1, width);
|
width = wxMax(1, width);
|
||||||
height = wxMax(1, height);
|
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)
|
if (m_imageCache.IsOk() && m_imageCache.GetWidth() == width && m_imageCache.GetHeight() == height)
|
||||||
{
|
{
|
||||||
// Do nothing, we didn't need to change the image cache
|
// Do nothing, we didn't need to change the image cache
|
||||||
|
changed = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
changed = true;
|
||||||
|
|
||||||
|
if (delayLoading)
|
||||||
|
{
|
||||||
|
if (m_imageCache.IsOk())
|
||||||
|
m_imageCache = wxNullBitmap;
|
||||||
|
m_imageState = ImageState_Unloaded;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (!image.IsOk())
|
if (!image.IsOk())
|
||||||
{
|
{
|
||||||
m_imageBlock.Load(image);
|
if (!m_imageBlock.Load(image) || !image.IsOk())
|
||||||
if (!image.IsOk())
|
|
||||||
{
|
{
|
||||||
wxBitmap bitmap(image_placeholder24x24_xpm);
|
wxBitmap bitmap(image_placeholder24x24_xpm);
|
||||||
m_imageCache = bitmap;
|
m_imageCache = bitmap;
|
||||||
m_originalImageSize = wxSize(bitmap.GetWidth(), bitmap.GetHeight());
|
m_originalImageSize = wxSize(bitmap.GetWidth(), bitmap.GetHeight());
|
||||||
|
m_imageState = ImageState_Bad;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_originalImageSize = wxSize(image.GetWidth(), image.GetHeight());
|
||||||
|
|
||||||
if (image.GetWidth() == width && image.GetHeight() == height)
|
if (image.GetWidth() == width && image.GetHeight() == height)
|
||||||
m_imageCache = wxBitmap(image);
|
m_imageCache = wxBitmap(image);
|
||||||
else
|
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();
|
return m_imageCache.IsOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -12406,9 +12448,6 @@ bool wxRichTextImage::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wx
|
|||||||
if (!IsShown())
|
if (!IsShown())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (!m_imageCache.IsOk())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
wxRichTextAttr attr(GetAttributes());
|
wxRichTextAttr attr(GetAttributes());
|
||||||
AdjustAttributes(attr, context);
|
AdjustAttributes(attr, context);
|
||||||
|
|
||||||
@@ -12419,7 +12458,14 @@ bool wxRichTextImage::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wx
|
|||||||
marginRect = rect; // outer rectangle, will calculate contentRect
|
marginRect = rect; // outer rectangle, will calculate contentRect
|
||||||
GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
|
GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
|
||||||
|
|
||||||
|
if (m_imageCache.IsOk())
|
||||||
dc.DrawBitmap(m_imageCache, contentRect.x, contentRect.y, true);
|
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))
|
if (selection.WithinSelection(GetRange().GetStart(), this))
|
||||||
{
|
{
|
||||||
@@ -12436,10 +12482,10 @@ bool wxRichTextImage::Draw(wxDC& dc, wxRichTextDrawingContext& context, const wx
|
|||||||
/// Lay the item out
|
/// Lay the item out
|
||||||
bool wxRichTextImage::Layout(wxDC& dc, wxRichTextDrawingContext& context, const wxRect& rect, const wxRect& parentRect, int WXUNUSED(style))
|
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;
|
return false;
|
||||||
|
|
||||||
wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
|
|
||||||
wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
|
wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
|
||||||
contentRect = wxRect(wxPoint(0,0), imageSize);
|
contentRect = wxRect(wxPoint(0,0), imageSize);
|
||||||
|
|
||||||
@@ -12465,7 +12511,8 @@ bool wxRichTextImage::GetRangeSize(const wxRichTextRange& range, wxSize& size, i
|
|||||||
if (!range.IsWithin(GetRange()))
|
if (!range.IsWithin(GetRange()))
|
||||||
return false;
|
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;
|
size.x = 0; size.y = 0;
|
||||||
if (partialExtents)
|
if (partialExtents)
|
||||||
@@ -12476,7 +12523,6 @@ bool wxRichTextImage::GetRangeSize(const wxRichTextRange& range, wxSize& size, i
|
|||||||
wxRichTextAttr attr(GetAttributes());
|
wxRichTextAttr attr(GetAttributes());
|
||||||
((wxRichTextObject*)this)->AdjustAttributes(attr, context);
|
((wxRichTextObject*)this)->AdjustAttributes(attr, context);
|
||||||
|
|
||||||
wxSize imageSize(m_imageCache.GetWidth(), m_imageCache.GetHeight());
|
|
||||||
wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
|
wxRect marginRect, borderRect, contentRect, paddingRect, outlineRect;
|
||||||
contentRect = wxRect(wxPoint(0,0), imageSize);
|
contentRect = wxRect(wxPoint(0,0), imageSize);
|
||||||
GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
|
GetBoxRects(dc, GetBuffer(), attr, marginRect, borderRect, contentRect, paddingRect, outlineRect);
|
||||||
@@ -15040,6 +15086,7 @@ wxRichTextDrawingContext::wxRichTextDrawingContext(wxRichTextBuffer* buffer)
|
|||||||
{
|
{
|
||||||
EnableVirtualAttributes(m_buffer->GetRichTextCtrl()->GetVirtualAttributesEnabled());
|
EnableVirtualAttributes(m_buffer->GetRichTextCtrl()->GetVirtualAttributesEnabled());
|
||||||
m_enableImages = m_buffer->GetRichTextCtrl()->GetImagesEnabled();
|
m_enableImages = m_buffer->GetRichTextCtrl()->GetImagesEnabled();
|
||||||
|
m_enableDelayedImageLoading = m_buffer->GetRichTextCtrl()->GetDelayedImageLoading();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -177,6 +177,7 @@ BEGIN_EVENT_TABLE( wxRichTextCtrl, wxControl )
|
|||||||
EVT_MOUSE_CAPTURE_LOST(wxRichTextCtrl::OnCaptureLost)
|
EVT_MOUSE_CAPTURE_LOST(wxRichTextCtrl::OnCaptureLost)
|
||||||
EVT_CONTEXT_MENU(wxRichTextCtrl::OnContextMenu)
|
EVT_CONTEXT_MENU(wxRichTextCtrl::OnContextMenu)
|
||||||
EVT_SYS_COLOUR_CHANGED(wxRichTextCtrl::OnSysColourChanged)
|
EVT_SYS_COLOUR_CHANGED(wxRichTextCtrl::OnSysColourChanged)
|
||||||
|
EVT_TIMER(wxID_ANY, wxRichTextCtrl::OnTimer)
|
||||||
|
|
||||||
EVT_MENU(wxID_UNDO, wxRichTextCtrl::OnUndo)
|
EVT_MENU(wxID_UNDO, wxRichTextCtrl::OnUndo)
|
||||||
EVT_UPDATE_UI(wxID_UNDO, wxRichTextCtrl::OnUpdateUndo)
|
EVT_UPDATE_UI(wxID_UNDO, wxRichTextCtrl::OnUpdateUndo)
|
||||||
@@ -350,6 +351,8 @@ wxRichTextCtrl::~wxRichTextCtrl()
|
|||||||
GetBuffer().RemoveEventHandler(this);
|
GetBuffer().RemoveEventHandler(this);
|
||||||
|
|
||||||
delete m_contextMenu;
|
delete m_contextMenu;
|
||||||
|
|
||||||
|
m_delayedImageProcessingTimer.Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Member initialisation
|
/// Member initialisation
|
||||||
@@ -382,6 +385,10 @@ void wxRichTextCtrl::Init()
|
|||||||
m_setupScrollbarsCountInOnSize = 0;
|
m_setupScrollbarsCountInOnSize = 0;
|
||||||
|
|
||||||
m_enableImages = true;
|
m_enableImages = true;
|
||||||
|
|
||||||
|
m_enableDelayedImageLoading = false;
|
||||||
|
m_delayedImageProcessingRequired = false;
|
||||||
|
m_delayedImageProcessingTime = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void wxRichTextCtrl::DoThaw()
|
void wxRichTextCtrl::DoThaw()
|
||||||
@@ -2602,6 +2609,9 @@ void wxRichTextCtrl::OnSize(wxSizeEvent& event)
|
|||||||
// OnSize was the source of a scrollbar change.
|
// OnSize was the source of a scrollbar change.
|
||||||
m_setupScrollbarsCountInOnSize = m_setupScrollbarsCount;
|
m_setupScrollbarsCountInOnSize = m_setupScrollbarsCount;
|
||||||
|
|
||||||
|
if (GetDelayedImageLoading())
|
||||||
|
RequestDelayedImageProcessing();
|
||||||
|
|
||||||
event.Skip();
|
event.Skip();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2642,6 +2652,16 @@ void wxRichTextCtrl::OnIdle(wxIdleEvent& event)
|
|||||||
Refresh(false);
|
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 (m_caretPositionForDefaultStyle != -2)
|
||||||
{
|
{
|
||||||
// If the caret position has changed, no longer reflect the default style
|
// If the caret position has changed, no longer reflect the default style
|
||||||
@@ -4045,6 +4065,9 @@ bool wxRichTextCtrl::LayoutContent(bool onlyVisibleRect)
|
|||||||
|
|
||||||
if (!IsFrozen() && !onlyVisibleRect)
|
if (!IsFrozen() && !onlyVisibleRect)
|
||||||
SetupScrollbars();
|
SetupScrollbars();
|
||||||
|
|
||||||
|
if (GetDelayedImageLoading())
|
||||||
|
RequestDelayedImageProcessing();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -4668,6 +4691,15 @@ bool wxRichTextCtrl::RefreshForSelectionChange(const wxRichTextSelection& oldSel
|
|||||||
return true;
|
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
|
// margins functions
|
||||||
bool wxRichTextCtrl::DoSetMargins(const wxPoint& pt)
|
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()));
|
(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
|
#if wxRICHTEXT_USE_OWN_CARET
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
Reference in New Issue
Block a user