%% options copyright owner = Dirk Krause copyright year = 2015-xxxx license = bsd %% header /** @file Dk4WxControl.h Triple-buffered control. */ #ifndef DK4CONF_H_INCLUDED #include "dk4conf.h" #endif #ifndef DK4TYPES_H_INCLUDED #include "dk4types.h" #endif #ifndef WX_WXPREC_H_INCLUDED #include #define WX_WXPREC_H_INCLUDED 1 #endif #ifdef __BORLANDC__ #pragma hdrstop #endif #ifndef WX_PRECOMP #ifndef WX_WX_H_INCLUDED #include #define WX_WX_H_INCLUDED 1 #endif #endif /** Base class for user-defined controls. 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 Dk4WxControl constructors from the constructors. From Create() call Dk4WxControl::Create(). You should also use DECLARE_DYNAMIC_CLASS() and IMPLEMENT_DYNAMIC_CLASS(). */ class Dk4WxControl : public wxControl { private: /** For RTTI. */ DECLARE_DYNAMIC_CLASS(Dk4WxControl) /** Event table. */ DECLARE_EVENT_TABLE() protected: /** Class name. */ static const wxChar Dk4WxControlName[]; /** Protection against simultaneous access. */ wxCriticalSection csProtect; /** Bitmap to cache contents. */ wxBitmap *pBitmap; /** Bitmap width. */ int iBmWidth; /** Bitmap height. */ int iBmHeight; /** Flag: Must update contents. */ bool bUpdate; protected: /** Overwrite this method to do the real drawing. @param pdc Device context to draw to. @param event Paint event to draw for. @param buffered Flag: Operating on 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 ); /** Internal paint operation. @param pdc Device context to draw to. @param event Paint event to draw for. @param buffered Flag: Operating on 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. */ Dk4WxControl(); /** Useful constructor. @param parent Parent window. @param wid Window ID. @param pos Position. @param size Size. @param style Style. @param validator Validator. @param name Type name. */ Dk4WxControl( wxWindow *parent, wxWindowID wid = wxID_ANY, const wxPoint & pos = wxDefaultPosition, const wxSize & size = wxDefaultSize, long style = 0L, const wxValidator & validator = wxDefaultValidator, const wxChar *name = Dk4WxControlName ); /** Create window after using default constructor. @param parent Parent window. @param wid Window ID. @param pos Position. @param size Size. @param style Style. @param validator Validator. @param name Type name. */ bool Create( wxWindow *parent, wxWindowID wid = wxID_ANY, const wxPoint & pos = wxDefaultPosition, const wxSize & size = wxDefaultSize, long style = 0L, const wxValidator & validator = wxDefaultValidator, const wxChar *name = Dk4WxControlName ); /** Destructor. */ ~Dk4WxControl(); /** Mark update necessarity. */ void SetUpdate(bool flag = true); /** 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); /** Release buffer when frame is iconized or hidden. */ void ReleaseBuffer(void); }; %% module #include "Dk4WxControl.h" $!trace-include const wxChar Dk4WxControl::Dk4WxControlName[] = { wxT("Dk4WxControl") }; IMPLEMENT_DYNAMIC_CLASS(Dk4WxControl, wxControl) BEGIN_EVENT_TABLE(Dk4WxControl, wxControl) EVT_ERASE_BACKGROUND(Dk4WxControl::OnErase) EVT_PAINT(Dk4WxControl::OnPaint) END_EVENT_TABLE() Dk4WxControl::Dk4WxControl() : wxControl() { $? "+ Dk4WxControl()" pBitmap = NULL; iBmWidth = -1; iBmHeight = -1; SetBackgroundStyle(wxBG_STYLE_CUSTOM); $? "- Dk4WxControl()" } bool Dk4WxControl::Create( wxWindow *parent, wxWindowID wid, const wxPoint & pos, const wxSize & size, long style, const wxValidator & validator, const wxChar *name ) { bool back; $? "+ Create" back = wxControl::Create( parent, wid, pos, size, style, validator, name ); pBitmap = NULL; iBmWidth = -1; iBmHeight = -1; SetBackgroundStyle(wxBG_STYLE_CUSTOM); $? "- Create" return back; } Dk4WxControl::Dk4WxControl( wxWindow *parent, wxWindowID wid, const wxPoint & pos, const wxSize & size, long style, const wxValidator & validator, const wxChar *name ) : wxControl( parent, wid, pos, size, style, validator, wxString(name)) { $? "+ Dk4WxControl(...)" pBitmap = NULL; iBmWidth = -1; iBmHeight = -1; SetBackgroundStyle(wxBG_STYLE_CUSTOM); $? "- Dk4WxControl(...)" } Dk4WxControl::~Dk4WxControl() { $? "+ ~Dk4WxControl" { wxCriticalSectionLocker lock(csProtect); if (NULL != pBitmap) { delete(pBitmap); } pBitmap = NULL; iBmWidth = -1; iBmHeight = -1; } $? "- ~Dk4WxControl" } void Dk4WxControl::OnErase(wxEraseEvent & event) { $? "+ OnErase" $? "- OnErase" } void Dk4WxControl::PaintOperation( wxDC & pdc, wxPaintEvent & event, bool buffered, int clWidth, int clHeight ) { pdc.SetBrush(*wxWHITE_BRUSH); pdc.DrawRectangle(0, 0, clWidth, clHeight); } void Dk4WxControl::InternalPaint( wxDC & pdc, wxPaintEvent & event, bool buffered, int clWidth, int clHeight ) { /* Save current DC settings. */ 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(); $? "+ InternalPaint" /* Do the real pain operation. */ PaintOperation(pdc, event, buffered, clWidth, clHeight); /* Restore DC settings from start. */ pdc.SetBackgroundMode(bgm); pdc.SetTextForeground(tfg); pdc.SetTextBackground(tbg); pdc.SetPen(pn); pdc.SetFont(fn); pdc.SetBackground(bg); pdc.SetBrush(br); $? "- InternalPaint" } void Dk4WxControl::OnPaint(wxPaintEvent & event) { wxPaintDC paintDC(this); int w = 0; int h = 0; bool mustDraw = false; bool done = false; $? "+ OnPaint" { wxCriticalSectionLocker lock(csProtect); wxSize sz = GetClientSize(); mustDraw = bUpdate; #if VERSION_BEFORE_20150821 w = iBmWidth; h = iBmHeight; #endif if (NULL != pBitmap) { if ((iBmWidth != sz.x) || (iBmHeight != sz.y)) { delete(pBitmap); pBitmap = NULL; } } w = iBmWidth = sz.x; h = iBmHeight = sz.y; if (NULL == pBitmap) { pBitmap = new wxBitmap(iBmWidth, iBmHeight); mustDraw = true; } if (NULL != pBitmap) { if (mustDraw) { wxMemoryDC memDC; memDC.SelectObject(*pBitmap); InternalPaint(memDC, event, true, w, h); memDC.SelectObject(wxNullBitmap); bUpdate = false; } paintDC.DrawBitmap(*pBitmap, 0, 0, false); done = true; } } if (!(done)) { InternalPaint(paintDC, event, false, w, h); } $? "- OnPaint" } void Dk4WxControl::SetUpdate(bool flag) { $? "+ SetUpdate" if (flag) { wxCriticalSectionLocker lock(csProtect); bUpdate = true; } $? "- SetUpdate" } void Dk4WxControl::ReleaseBuffer(void) { { wxCriticalSectionLocker lock(csProtect); if (NULL != pBitmap) { delete(pBitmap); pBitmap = NULL; } } }