%% options copyright owner = Dirk Krause copyright year = 2013-xxxx license = bsd %% header #include #include #ifdef __BORLANDC__ #pragma hdrstop #endif #ifndef WX_PRECOMP #include #endif /** This is a base class for triple-buffered views. Like double-buffering we write the windows contents to an image and finally transfer the entire image to the device context. Additionally we keep the image between the paint events and rebuild the image only if necessary (data to show has changed). When the frame containing your view becomes invisible (frame iconized or hidden) you should use the releaseBuffer() method to decrease the memory footprint of the program. In your derived class, call the DkWxBufferedControl constructors from the constructors. From Create() call DkWxBufferedControl::Create(). You should also use DECLARE_DYNAMIC_CLASS() and IMPLEMENT_DYNAMIC_CLASS(). */ class DkWxBufferedControl : public wxControl { private: /** Avoid simultaneous access from multiple threads. */ wxMutex mxProtectData; /** Bitmap for buffered drawing operations. */ wxBitmap *pBitmap; /** Flag: Must redraw. */ bool bMustDraw; /** Bitmap width, window width. */ int iWidth; /** Bitmap height, window height. */ int iHeight; /** For RTTI. */ DECLARE_DYNAMIC_CLASS(DkWxBufferedControl) /** Event table. */ DECLARE_EVENT_TABLE() protected: static wxChar const DkWxBufferedControlName[]; /** Internal method to paint. This method saves some DC attributes and restores them before returning. @param pdc Device context to draw to. @param event Paint event to process. @param buffered Flag: Operating on a memory DC. @param clWidth Client area width. @param clHeight Client area height. */ void InternalPaint( wxDC & pdc, wxPaintEvent & event, bool buffered, int clWidth, int clHeight ); public: /** Default constructor. */ DkWxBufferedControl(); /** Constructor. @param pParent Parent window. @param wid Window ID @param pos Window position. @param size Window size. @param style Window style. @param validator Validator for control. @param name Window type name. */ DkWxBufferedControl( wxWindow *pParent, wxWindowID wid = wxID_ANY, const wxPoint & pos = wxDefaultPosition, const wxSize & size = wxDefaultSize, long style = 0L, const wxValidator & validator = wxDefaultValidator, wxChar const *name = DkWxBufferedControlName ); /** Create after using the default constructor. @param pParent Parent window. @param wid Window ID @param pos Window position. @param size Window size. @param style Window style. @param validator Validator for control. @param name Window type name. */ bool Create( wxWindow *pParent, wxWindowID wid = wxID_ANY, const wxPoint & pos = wxDefaultPosition, const wxSize & size = wxDefaultSize, long style = 0L, const wxValidator & validator = wxDefaultValidator, const wxChar *name = DkWxBufferedControlName ); /** Destructor. */ ~DkWxBufferedControl(); /** Indicate that data has changed so on next paint we must rebuild the bitmap. @param flag: Flag to turn bMustDraw on or off. */ void SetMustUpdate(bool flag = true); /** Release bitmap buffer. This function should be called when the frame or dialog containing this view is iconized or hidden. */ void releaseBuffer(void); /** Handler for erase background event. @param event Event to process. */ void OnErase(wxEraseEvent & event); /** Handler for paint event. @param event Event to process. */ void OnPaint(wxPaintEvent & event); /** Overwrite this method to do the real drawing. @param pdc Device context to draw to. Normally a memory dc, but may be a direct dc in special situations (failed to allocate bitmap). @param event Paint event to process. @param buffered Flag: Operating on a memory DC. @param clWidth Client area width. @param clHeight Client area height. */ virtual void PaintOperation( wxDC & pdc, wxPaintEvent & event, bool buffered, int clWidth, int clHeight ); }; %% module #include "DkWxBufferedControl.h" $!trace-include wxChar const DkWxBufferedControl::DkWxBufferedControlName[] = { wxT("DkWxBufferedControl") }; IMPLEMENT_DYNAMIC_CLASS(DkWxBufferedControl, wxControl) BEGIN_EVENT_TABLE(DkWxBufferedControl,wxControl) EVT_ERASE_BACKGROUND(DkWxBufferedControl::OnErase) EVT_PAINT(DkWxBufferedControl::OnPaint) END_EVENT_TABLE() DkWxBufferedControl::DkWxBufferedControl() { $? "+ DkWxBufferedControl::DkWxBufferedControl" pBitmap = NULL; iWidth = -1; iHeight = -1; bMustDraw = true; SetBackgroundStyle(wxBG_STYLE_CUSTOM); $? "- DkWxBufferedControl::DkWxBufferedControl" } bool DkWxBufferedControl::Create( wxWindow *pParent, wxWindowID wid, const wxPoint & pos, const wxSize & size, long style, const wxValidator & validator, wxChar const *name ) { bool back; back = wxControl::Create( pParent, wid, pos, size, style, validator, wxString(name) ); pBitmap = NULL; iWidth = -1; iHeight = -1; bMustDraw = true; SetBackgroundStyle(wxBG_STYLE_CUSTOM); return back; } DkWxBufferedControl::DkWxBufferedControl( wxWindow *pParent, wxWindowID wid, const wxPoint & pos, const wxSize & size, long style, const wxValidator & validator, wxChar const *name ) : wxControl( pParent, wid, pos, size, style, validator, wxString(name) ) { $? "+ DkWxBufferedControl::DkWxBufferedControl" pBitmap = NULL; iWidth = -1; iHeight = -1; bMustDraw = true; SetBackgroundStyle(wxBG_STYLE_CUSTOM); $? "- DkWxBufferedControl::DkWxBufferedControl" } DkWxBufferedControl::~DkWxBufferedControl() { $? "+ DkWxBufferedControl::~DkWxBufferedControl" { wxMutexLocker lock(mxProtectData); if(NULL != pBitmap) { $? ". delete bitmap buffer (destructor)" delete(pBitmap); pBitmap = NULL; } iWidth = -1; iHeight = -1; bMustDraw = false; } $? "- DkWxBufferedControl::~DkWxBufferedControl" } void DkWxBufferedControl::OnErase(wxEraseEvent & event) { $? "+ DkWxBufferedControl::OnErase" /* Do nothing here. */ $? "- DkWxBufferedControl::OnErase" } void DkWxBufferedControl::OnPaint(wxPaintEvent & event) { bool mustdraw = false; bool done = false; int w = 0; int h = 0; $? "+ DkWxBufferedControl::OnPaint" wxPaintDC paintDC(this); { wxMutexLocker lock(mxProtectData); #if VERSION_BEFORE_20150821 w = iWidth; h = iHeight; #endif wxSize sz = GetClientSize(); if(NULL != pBitmap) { if((iWidth != sz.x) || (iHeight != sz.y)) { delete(pBitmap); $? ". delete bitmap buffer (size)" pBitmap = NULL; mustdraw = true; $? ". bitmap size mismatch" } } w = iWidth = sz.x; h = iHeight = sz.y; if(NULL == pBitmap) { $? ". allocate bitmap buffer" pBitmap = new wxBitmap(iWidth, iHeight); mustdraw = true; $? ". new bitmap PTR=%d", TR_IPTR(pBitmap) } if(NULL != pBitmap) { $? ". have bitmap" if(false == mustdraw) { if(bMustDraw) { $? ". must redraw, requested" mustdraw = true; } } if(mustdraw) { $? ". must redraw, use bitmap" wxMemoryDC memDC; memDC.SelectObject(*pBitmap); InternalPaint(memDC, event, true, w, h); memDC.SelectObject(wxNullBitmap); bMustDraw = false; $? ". result saved in bitmap" } else { $? ". simply use existing bitmap" } paintDC.DrawBitmap(*pBitmap, 0, 0, false); done = true; } } if(!(done)) { $? ". paint unbuffered" InternalPaint(paintDC, event, false, w, h); } $? "- DkWxBufferedControl::OnPaint" } void DkWxBufferedControl::PaintOperation( wxDC & pdc, wxPaintEvent & event, bool buffered, int clWidth, int clHeight ) { /* The default implementation intentionally does nothing. */ } void DkWxBufferedControl::InternalPaint( wxDC & pdc, wxPaintEvent & event, bool buffered, int clWidth, int clHeight ) { wxBrush const br = pdc.GetBrush(); wxBrush const bg = pdc.GetBackground(); wxFont const fn = pdc.GetFont(); wxPen const pn = pdc.GetPen(); wxColour const tbg = pdc.GetTextBackground(); wxColour const tfg = pdc.GetTextForeground(); int bgm = pdc.GetBackgroundMode(); $? "+ DkWxBufferedControl::InternalPaint" PaintOperation(pdc, event, buffered, clWidth, clHeight); pdc.SetBackgroundMode(bgm); pdc.SetTextForeground(tfg); pdc.SetTextBackground(tbg); pdc.SetPen(pn); pdc.SetFont(fn); pdc.SetBackground(bg); pdc.SetBrush(br); $? "- DkWxBufferedControl::InternalPaint" } void DkWxBufferedControl::SetMustUpdate(bool flag) { $? "+ DkWxBufferedControl::SetMustUpdate" { wxMutexLocker lock(mxProtectData); bMustDraw = flag; } $? "- DkWxBufferedControl::SetMustUpdate" } void DkWxBufferedControl::releaseBuffer(void) { $? "+ DkWxBufferedControl::releaseBuffer" { wxMutexLocker lock(mxProtectData); if(pBitmap) { $? ". delete bitmap buffer (explicit release)" delete(pBitmap); pBitmap = NULL; } } $? "- DkWxBufferedControl::releaseBuffer" }