From b269afd58c3495d4934894369c4068ae46eaca26 Mon Sep 17 00:00:00 2001 From: Robin Dunn Date: Wed, 27 Jul 2016 16:13:13 -0700 Subject: [PATCH] Begin adding PseudoDC --- etg/pseudodc.py | 55 +++ src/pseudodc.cpp | 626 ++++++++++++++++++++++++++++ src/pseudodc.h | 829 +++++++++++++++++++++++++++++++++++++ unittests/test_pseudodc.py | 16 + 4 files changed, 1526 insertions(+) create mode 100644 etg/pseudodc.py create mode 100644 src/pseudodc.cpp create mode 100644 src/pseudodc.h create mode 100644 unittests/test_pseudodc.py diff --git a/etg/pseudodc.py b/etg/pseudodc.py new file mode 100644 index 00000000..31c3a0c3 --- /dev/null +++ b/etg/pseudodc.py @@ -0,0 +1,55 @@ +#--------------------------------------------------------------------------- +# Name: etg/pseudodc.py +# Author: Robin Dunn +# +# Created: 26-Jul-2016 +# Copyright: (c) 2016 by Total Control Software +# License: wxWindows License +#--------------------------------------------------------------------------- + +import etgtools +import etgtools.tweaker_tools as tools + +PACKAGE = "wx" +MODULE = "_adv" +NAME = "pseudodc" # Base name of the file to generate to for this script +DOCSTRING = "" + +# The classes and/or the basename of the Doxygen XML files to be processed by +# this script. +ITEMS = [ ] + +OTHERDEPS = [ 'src/pseudodc.h', + 'src/pseudodc.cpp', + ] + +#--------------------------------------------------------------------------- + +def run(): + # Parse the XML file(s) building a collection of Extractor objects + module = etgtools.ModuleDef(PACKAGE, MODULE, NAME, DOCSTRING) + etgtools.parseDoxyXML(module, ITEMS) + + #----------------------------------------------------------------- + # Tweak the parsed meta objects in the module object as needed for + # customizing the generated code and docstrings. + + # The PseudoDC classes are not in wxWidgets, so there is no Doxygen XML + # for them. That means we'll have to construct each of the extractor + # objects from scratch. + + module.addHeaderCode('#include "pseudodc.h"') + module.includeCppCode('src/pseudodc.cpp') + + + + + #----------------------------------------------------------------- + tools.doCommonTweaks(module) + tools.runGenerators(module) + + +#--------------------------------------------------------------------------- +if __name__ == '__main__': + run() + diff --git a/src/pseudodc.cpp b/src/pseudodc.cpp new file mode 100644 index 00000000..5c5e0dd3 --- /dev/null +++ b/src/pseudodc.cpp @@ -0,0 +1,626 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/pseudodc.cpp +// Purpose: Implementation of the wxPseudoDC classes +// Author: Paul Lanier +// Modified by: Robin Dunn +// +// Created: 05/25/06 +// Copyright: (c) 2006-2016 Total Control Software +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +// For compilers that support precompilation, includes "wx.h". +//include "wx/wxprec.h" + +#undef DEBUG +#include +#include "wx/wxPython/wxPython.h" +#include "wx/wxPython/pseudodc.h" + +// wxList based class definitions +#include +WX_DEFINE_LIST(pdcOpList); +WX_DEFINE_LIST(pdcObjectList); + +//---------------------------------------------------------------------------- +// Helper functions used for drawing greyed out versions of objects +//---------------------------------------------------------------------------- +wxColour &MakeColourGrey(const wxColour &c) +{ + static wxColour rval; + rval.Set(byte((230-c.Red())*0.7+c.Red()), + byte((230-c.Green())*0.7+c.Green()), + byte((230-c.Blue())*0.7+c.Blue())); + return rval; +} +wxBrush &GetGreyBrush(wxBrush &brush) +{ + static wxBrush b; + wxColour c; + b = brush; + c = MakeColourGrey(brush.GetColour()); + b.SetColour(c); + return b; +} + +wxPen &GetGreyPen(wxPen &pen) +{ + static wxPen p; + wxColour c; + p = pen; + c = MakeColourGrey(pen.GetColour()); + p.SetColour(c); + return p; +} + +void GreyOutImage(wxImage &img) +{ + unsigned char *data = img.GetData(); + unsigned char r,g,b; + unsigned char mr,mg,mb; + int i, tst; + int len = img.GetHeight()*img.GetWidth()*3; + if (img.HasMask()) + { + mr = img.GetMaskRed(); + mg = img.GetMaskGreen(); + mb = img.GetMaskBlue(); + } + tst=0; + for (i=0;iGetData()->DrawToDC(dc, m_greyedout); + node = node->GetNext(); + } +} + +// ---------------------------------------------------------------------------- +// Translate - translate all the operations by some dx,dy +// ---------------------------------------------------------------------------- +void pdcObject::Translate(wxCoord dx, wxCoord dy) +{ + pdcOpList::compatibility_iterator node = m_oplist.GetFirst(); + while(node) + { + node->GetData()->Translate(dx,dy); + node = node->GetNext(); + } + if (m_bounded) + { + m_bounds.x += dx; + m_bounds.y += dy; + } +} + +// ---------------------------------------------------------------------------- +// SetGreyedOut - set the greyout member and cache grey versions of everything +// if greyout is true +// ---------------------------------------------------------------------------- +void pdcObject::SetGreyedOut(bool greyout) +{ + m_greyedout=greyout; + if (greyout) + { + pdcOpList::compatibility_iterator node = m_oplist.GetFirst(); + pdcOp *obj; + while(node) + { + obj = node->GetData(); + obj->CacheGrey(); + node = node->GetNext(); + } + } +} + +// ============================================================================ +// wxPseudoDC implementation +// ============================================================================ + +// ---------------------------------------------------------------------------- +// Destructor +// ---------------------------------------------------------------------------- +wxPseudoDC::~wxPseudoDC() +{ + // delete all the nodes in the list + RemoveAll(); + +} + +// ---------------------------------------------------------------------------- +// ClearAll - remove all nodes from list +// ---------------------------------------------------------------------------- +void wxPseudoDC::RemoveAll(void) +{ + m_objectlist.Clear(); + m_objectIndex.clear(); + m_currId = -1; + m_lastObject = NULL; + +} + +// ---------------------------------------------------------------------------- +// GetLen - return the number of operations in the current op list +// ---------------------------------------------------------------------------- +int wxPseudoDC::GetLen(void) +{ + pdcObjectList::compatibility_iterator pt = m_objectlist.GetFirst(); + int len=0; + while (pt) + { + len += pt->GetData()->GetLen(); + pt = pt->GetNext(); + } + return len; +} + +// ---------------------------------------------------------------------------- +// FindObject - find and return an object node by id. If node doesn't exist +// and create is true then create one and return it. Otherwise +// return NULL. +// ---------------------------------------------------------------------------- +pdcObject *wxPseudoDC::FindObject(int id, bool create) +{ + // see if last operation was for same id + //~ if (m_lastObject && m_lastObject->GetId() == id) + //~ return m_lastObject; + // if not then search for it + pdcObjectHash::iterator lookup = m_objectIndex.find(id); + if (lookup == m_objectIndex.end()) {//not found + if (create) { + m_lastObject = new pdcObject(id); + m_objectlist.Append(m_lastObject); + pdcObjectHash::value_type insert(id, m_lastObject); + m_objectIndex.insert(insert); + return m_lastObject; + } else { + return NULL; + } + } else { //found + return lookup->second; + } +} + +// ---------------------------------------------------------------------------- +// AddToList - Add a node to the list at the end (preserve draw order) +// ---------------------------------------------------------------------------- +void wxPseudoDC::AddToList(pdcOp *newOp) +{ + pdcObject *obj = FindObject(m_currId, true); + obj->AddOp(newOp); +} + +// ---------------------------------------------------------------------------- +// ClearID - remove all the operations associated with a single ID +// ---------------------------------------------------------------------------- +void wxPseudoDC::ClearId(int id) +{ + pdcObject *obj = FindObject(id); + if (obj) obj->Clear(); +} + +// ---------------------------------------------------------------------------- +// RemoveID - Remove the object node (and all operations) associated with an id +// ---------------------------------------------------------------------------- +void wxPseudoDC::RemoveId(int id) +{ + pdcObject *obj = FindObject(id); + if (obj) + { + if (m_lastObject == obj) + m_lastObject = obj; + m_objectlist.DeleteObject(obj); + } + m_objectIndex.erase(id); +} + +// ---------------------------------------------------------------------------- +// SetIdBounds - Set the bounding rect for a given id +// ---------------------------------------------------------------------------- +void wxPseudoDC::SetIdBounds(int id, wxRect& rect) +{ + pdcObject *obj = FindObject(id, true); + obj->SetBounds(rect); +} + +// ---------------------------------------------------------------------------- +// GetIdBounds - Get the bounding rect for a given id +// ---------------------------------------------------------------------------- +void wxPseudoDC::GetIdBounds(int id, wxRect& rect) +{ + pdcObject *obj = FindObject(id); + if (obj && obj->IsBounded()) + rect = obj->GetBounds(); + else + rect.x = rect.y = rect.width = rect.height = 0; +} + +// ---------------------------------------------------------------------------- +// TranslateId - Translate all the operations of a single id +// ---------------------------------------------------------------------------- +void wxPseudoDC::TranslateId(int id, wxCoord dx, wxCoord dy) +{ + pdcObject *obj = FindObject(id); + if (obj) obj->Translate(dx,dy); +} + +// ---------------------------------------------------------------------------- +// DrawIdToDC - Draw a specific id to the dc passed in +// ---------------------------------------------------------------------------- +void wxPseudoDC::DrawIdToDC(int id, wxDC *dc) +{ + pdcObject *obj = FindObject(id); + if (obj) obj->DrawToDC(dc); +} + +// ---------------------------------------------------------------------------- +// SetIdGreyedOut - Set the greyedout member of id +// ---------------------------------------------------------------------------- +void wxPseudoDC::SetIdGreyedOut(int id, bool greyout) +{ + pdcObject *obj = FindObject(id); + if (obj) obj->SetGreyedOut(greyout); +} + +// ---------------------------------------------------------------------------- +// GetIdGreyedOut - Get the greyedout member of id +// ---------------------------------------------------------------------------- +bool wxPseudoDC::GetIdGreyedOut(int id) +{ + pdcObject *obj = FindObject(id); + if (obj) return obj->GetGreyedOut(); + else return false; +} + +// ---------------------------------------------------------------------------- +// FindObjectsByBBox - Return a list of all the ids whose bounding boxes +// contain (x,y) +// ---------------------------------------------------------------------------- +PyObject *wxPseudoDC::FindObjectsByBBox(wxCoord x, wxCoord y) +{ + //wxPyBlock_t blocked = wxPyBeginBlockThreads(); + pdcObjectList::compatibility_iterator pt = m_objectlist.GetFirst(); + pdcObject *obj; + PyObject* pyList = NULL; + pyList = PyList_New(0); + wxRect r; + while (pt) + { + obj = pt->GetData(); + r = obj->GetBounds(); + if (obj->IsBounded() && r.Contains(x,y)) + { + PyObject* pyObj = PyInt_FromLong((long)obj->GetId()); + PyList_Insert(pyList, 0, pyObj); + Py_DECREF(pyObj); + } + pt = pt->GetNext(); + } + //wxPyEndBlockThreads(blocked); + return pyList; +} + +// ---------------------------------------------------------------------------- +// FindObjects - Return a list of all the ids that draw to (x,y) +// ---------------------------------------------------------------------------- +PyObject *wxPseudoDC::FindObjects(wxCoord x, wxCoord y, + wxCoord radius, const wxColor& bg) +{ + //wxPyBlock_t blocked = wxPyBeginBlockThreads(); + pdcObjectList::compatibility_iterator pt = m_objectlist.GetFirst(); + pdcObject *obj; + PyObject* pyList = NULL; + pyList = PyList_New(0); + wxBrush bgbrush(bg); + wxPen bgpen(bg); + // special case radius = 0 + if (radius == 0) + { + wxBitmap bmp(4,4,24); + wxMemoryDC memdc; + wxColor pix; + wxRect viewrect(x-2,y-2,4,4); + // setup the memdc for rendering + memdc.SelectObject(bmp); + memdc.SetBackground(bgbrush); + memdc.Clear(); + memdc.SetDeviceOrigin(2-x,2-y); + while (pt) + { + obj = pt->GetData(); + if (obj->IsBounded() && obj->GetBounds().Contains(x,y)) + { + // start clean + memdc.SetBrush(bgbrush); + memdc.SetPen(bgpen); + memdc.DrawRectangle(viewrect); + // draw the object + obj->DrawToDC(&memdc); + memdc.GetPixel(x,y,&pix); + // clear and update rgn2 + if (pix != bg) + { + PyObject* pyObj = PyInt_FromLong((long)obj->GetId()); + PyList_Insert(pyList, 0, pyObj); + Py_DECREF(pyObj); + } + } + pt = pt->GetNext(); + } + memdc.SelectObject(wxNullBitmap); + } + else + { + wxRect viewrect(x-radius,y-radius,2*radius,2*radius); + wxBitmap maskbmp(2*radius,2*radius,24); + wxMemoryDC maskdc; + // create bitmap with circle for masking + maskdc.SelectObject(maskbmp); + maskdc.SetBackground(*wxBLACK_BRUSH); + maskdc.Clear(); + maskdc.SetBrush(*wxWHITE_BRUSH); + maskdc.SetPen(*wxWHITE_PEN); + maskdc.DrawCircle(radius,radius,radius); + // now setup a memdc for rendering our object + wxBitmap bmp(2*radius,2*radius,24); + wxMemoryDC memdc; + memdc.SelectObject(bmp); + // set the origin so (x,y) is in the bmp center + memdc.SetDeviceOrigin(radius-x,radius-y); + // a region will be used to see if the result is empty + wxRegion rgn2; + while (pt) + { + obj = pt->GetData(); + if (obj->IsBounded() && viewrect.Intersects(obj->GetBounds())) + { + // start clean + //memdc.Clear(); + memdc.SetBrush(bgbrush); + memdc.SetPen(bgpen); + memdc.DrawRectangle(viewrect); + // draw the object + obj->DrawToDC(&memdc); + // remove background color + memdc.SetLogicalFunction(wxXOR); + memdc.SetBrush(bgbrush); + memdc.SetPen(bgpen); + memdc.DrawRectangle(viewrect); + memdc.SetLogicalFunction(wxCOPY); + memdc.Blit(x-radius,y-radius,2*radius,2*radius,&maskdc,0,0,wxCOPY); + // clear and update rgn2 + memdc.SelectObject(wxNullBitmap); + rgn2.Clear(); + rgn2.Union(bmp, *wxBLACK); + //rgn2.Intersect(rgn); + memdc.SelectObject(bmp); + if (!rgn2.IsEmpty()) + { + PyObject* pyObj = PyInt_FromLong((long)obj->GetId()); + PyList_Insert(pyList, 0, pyObj); + Py_DECREF(pyObj); + } + } + pt = pt->GetNext(); + } + maskdc.SelectObject(wxNullBitmap); + memdc.SelectObject(wxNullBitmap); + } + //wxPyEndBlockThreads(blocked); + return pyList; +} + +// ---------------------------------------------------------------------------- +// DrawToDCClipped - play back the op list to the DC but clip any objects +// known to be not in rect. This is a coarse level of +// clipping to speed things up when lots of objects are off +// screen and doesn't affect the dc level clipping +// ---------------------------------------------------------------------------- +void wxPseudoDC::DrawToDCClipped(wxDC *dc, const wxRect& rect) +{ + pdcObjectList::compatibility_iterator pt = m_objectlist.GetFirst(); + pdcObject *obj; + while (pt) + { + obj = pt->GetData(); + if (!obj->IsBounded() || rect.Intersects(obj->GetBounds())) + obj->DrawToDC(dc); + pt = pt->GetNext(); + } +} +void wxPseudoDC::DrawToDCClippedRgn(wxDC *dc, const wxRegion& region) +{ + pdcObjectList::compatibility_iterator pt = m_objectlist.GetFirst(); + pdcObject *obj; + while (pt) + { + obj = pt->GetData(); + if (!obj->IsBounded() || + (region.Contains(obj->GetBounds()) != wxOutRegion)) + obj->DrawToDC(dc); + pt = pt->GetNext(); + } +} + +// ---------------------------------------------------------------------------- +// DrawToDC - play back the op list to the DC +// ---------------------------------------------------------------------------- +void wxPseudoDC::DrawToDC(wxDC *dc) +{ + pdcObjectList::compatibility_iterator pt = m_objectlist.GetFirst(); + while (pt) + { + pt->GetData()->DrawToDC(dc); + pt = pt->GetNext(); + } +} + diff --git a/src/pseudodc.h b/src/pseudodc.h new file mode 100644 index 00000000..96e66a4a --- /dev/null +++ b/src/pseudodc.h @@ -0,0 +1,829 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: pseudodc.h +// Purpose: wxPseudoDC classes +// Author: Paul Lanier +// Modified by: Robin Dunn +// +// Created: 05/25/06 +// Copyright: (c) 2006-2016 Total Control Software +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#ifndef _WX_PSUEDO_DC_H_BASE_ +#define _WX_PSUEDO_DC_H_BASE_ + +//---------------------------------------------------------------------------- +// Base class for all pdcOp classes +//---------------------------------------------------------------------------- +class pdcOp +{ + public: + // Constructor and Destructor + pdcOp() {} + virtual ~pdcOp() {} + + // Virtual Drawing Methods + virtual void DrawToDC(wxDC *dc, bool grey=false)=0; + virtual void Translate(wxCoord WXUNUSED(dx), wxCoord WXUNUSED(dy)) {} + virtual void CacheGrey() {} +}; + +//---------------------------------------------------------------------------- +// declare a list class for list of pdcOps +//---------------------------------------------------------------------------- +WX_DECLARE_LIST(pdcOp, pdcOpList); + + +//---------------------------------------------------------------------------- +// Helper functions used for drawing greyed out versions of objects +//---------------------------------------------------------------------------- +wxColour &MakeColourGrey(const wxColour &c); +wxBrush &GetGreyBrush(wxBrush &brush); +wxPen &GetGreyPen(wxPen &pen); +wxIcon &GetGreyIcon(wxIcon &icon); +wxBitmap &GetGreyBitmap(wxBitmap &bmp); + +//---------------------------------------------------------------------------- +// Classes derived from pdcOp +// There is one class for each method mirrored from wxDC to wxPseudoDC +//---------------------------------------------------------------------------- +class pdcSetFontOp : public pdcOp +{ + public: + pdcSetFontOp(const wxFont& font) + {m_font=font;} + virtual void DrawToDC(wxDC *dc, bool WXUNUSED(grey)=false) {dc->SetFont(m_font);} + protected: + wxFont m_font; +}; + +class pdcSetBrushOp : public pdcOp +{ + public: + pdcSetBrushOp(const wxBrush& brush) + {m_greybrush=m_brush=brush;} + virtual void DrawToDC(wxDC *dc, bool grey=false) + { + if (!grey) dc->SetBrush(m_brush); + else dc->SetBrush(m_greybrush); + } + virtual void CacheGrey() {m_greybrush=GetGreyBrush(m_brush);} + protected: + wxBrush m_brush; + wxBrush m_greybrush; +}; + +class pdcSetBackgroundOp : public pdcOp +{ + public: + pdcSetBackgroundOp(const wxBrush& brush) + {m_greybrush=m_brush=brush;} + virtual void DrawToDC(wxDC *dc, bool grey=false) + { + if (!grey) dc->SetBackground(m_brush); + else dc->SetBackground(m_greybrush); + } + virtual void CacheGrey() {m_greybrush=GetGreyBrush(m_brush);} + protected: + wxBrush m_brush; + wxBrush m_greybrush; +}; + +class pdcSetPenOp : public pdcOp +{ + public: + pdcSetPenOp(const wxPen& pen) + {m_greypen=m_pen=pen;} + virtual void DrawToDC(wxDC *dc, bool grey=false) + { + if (!grey) dc->SetPen(m_pen); + else dc->SetPen(m_greypen); + } + virtual void CacheGrey() {m_greypen=GetGreyPen(m_pen);} + protected: + wxPen m_pen; + wxPen m_greypen; +}; + +class pdcSetTextBackgroundOp : public pdcOp +{ + public: + pdcSetTextBackgroundOp(const wxColour& colour) + {m_colour=colour;} + virtual void DrawToDC(wxDC *dc, bool grey=false) + { + if (!grey) dc->SetTextBackground(m_colour); + else dc->SetTextBackground(MakeColourGrey(m_colour)); + } + protected: + wxColour m_colour; +}; + +class pdcSetTextForegroundOp : public pdcOp +{ + public: + pdcSetTextForegroundOp(const wxColour& colour) + {m_colour=colour;} + virtual void DrawToDC(wxDC *dc, bool grey=false) + { + if (!grey) dc->SetTextForeground(m_colour); + else dc->SetTextForeground(MakeColourGrey(m_colour)); + } + protected: + wxColour m_colour; +}; + +class pdcDrawRectangleOp : public pdcOp +{ + public: + pdcDrawRectangleOp(wxCoord x, wxCoord y, wxCoord w, wxCoord h) + {m_x=x; m_y=y; m_w=w; m_h=h;} + virtual void DrawToDC(wxDC *dc, bool WXUNUSED(grey)=false) {dc->DrawRectangle(m_x,m_y,m_w,m_h);} + virtual void Translate(wxCoord dx, wxCoord dy) + {m_x+=dx;m_y+=dy;} + protected: + wxCoord m_x,m_y,m_w,m_h; +}; + +class pdcDrawLineOp : public pdcOp +{ + public: + pdcDrawLineOp(wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2) + {m_x1=x1; m_y1=y1; m_x2=x2; m_y2=y2;} + virtual void DrawToDC(wxDC *dc, bool WXUNUSED(grey)=false) {dc->DrawLine(m_x1,m_y1,m_x2,m_y2);} + virtual void Translate(wxCoord dx, wxCoord dy) + {m_x1+=dx; m_y1+=dy; m_x2+=dx; m_y2+=dy;} + protected: + wxCoord m_x1,m_y1,m_x2,m_y2; +}; + +class pdcSetBackgroundModeOp : public pdcOp +{ + public: + pdcSetBackgroundModeOp(int mode) {m_mode=mode;} + virtual void DrawToDC(wxDC *dc, bool WXUNUSED(grey)=false) {dc->SetBackgroundMode(m_mode);} + protected: + int m_mode; +}; + +class pdcDrawTextOp : public pdcOp +{ + public: + pdcDrawTextOp(const wxString& text, wxCoord x, wxCoord y) + {m_text=text; m_x=x; m_y=y;} + virtual void DrawToDC(wxDC *dc, bool WXUNUSED(grey)=false) {dc->DrawText(m_text, m_x, m_y);} + virtual void Translate(wxCoord dx, wxCoord dy) + {m_x+=dx; m_y+=dy;} + protected: + wxString m_text; + wxCoord m_x, m_y; +}; + +class pdcClearOp : public pdcOp +{ + public: + pdcClearOp() {} + virtual void DrawToDC(wxDC *dc, bool WXUNUSED(grey)=false) {dc->Clear();} +}; + +class pdcBeginDrawingOp : public pdcOp +{ + public: + pdcBeginDrawingOp() {} + virtual void DrawToDC(wxDC *WXUNUSED(dc), bool WXUNUSED(grey)=false) {} +}; + +class pdcEndDrawingOp : public pdcOp +{ + public: + pdcEndDrawingOp() {} + virtual void DrawToDC(wxDC *WXUNUSED(dc), bool WXUNUSED(grey)=false) {} +}; + +class pdcFloodFillOp : public pdcOp +{ + public: + pdcFloodFillOp(wxCoord x, wxCoord y, const wxColour& col, + wxFloodFillStyle style) {m_x=x; m_y=y; m_col=col; m_style=style;} + virtual void DrawToDC(wxDC *dc, bool grey=false) + { + if (!grey) dc->FloodFill(m_x,m_y,m_col,m_style); + else dc->FloodFill(m_x,m_y,MakeColourGrey(m_col),m_style); + } + virtual void Translate(wxCoord dx, wxCoord dy) + {m_x+=dx; m_y+=dy;} + protected: + wxCoord m_x,m_y; + wxColour m_col; + wxFloodFillStyle m_style; +}; + +class pdcCrossHairOp : public pdcOp +{ + public: + pdcCrossHairOp(wxCoord x, wxCoord y) {m_x=x; m_y=y;} + virtual void DrawToDC(wxDC *dc, bool WXUNUSED(grey)=false) {dc->CrossHair(m_x,m_y);} + virtual void Translate(wxCoord dx, wxCoord dy) + {m_x+=dx; m_y+=dy;} + protected: + wxCoord m_x,m_y; +}; + +class pdcDrawArcOp : public pdcOp +{ + public: + pdcDrawArcOp(wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2, + wxCoord xc, wxCoord yc) + {m_x1=x1; m_y1=y1; m_x2=x2; m_y2=y2; m_xc=xc; m_yc=yc;} + virtual void DrawToDC(wxDC *dc, bool WXUNUSED(grey)=false) + {dc->DrawArc(m_x1,m_y1,m_x2,m_y2,m_xc,m_yc);} + virtual void Translate(wxCoord dx, wxCoord dy) + {m_x1+=dx; m_x2+=dx; m_y1+=dy; m_y2+=dy;} + protected: + wxCoord m_x1,m_x2,m_xc; + wxCoord m_y1,m_y2,m_yc; +}; + +class pdcDrawCheckMarkOp : public pdcOp +{ + public: + pdcDrawCheckMarkOp(wxCoord x, wxCoord y, + wxCoord width, wxCoord height) + {m_x=x; m_y=y; m_w=width; m_h=height;} + virtual void DrawToDC(wxDC *dc, bool WXUNUSED(grey)=false) + {dc->DrawCheckMark(m_x,m_y,m_w,m_h);} + virtual void Translate(wxCoord dx, wxCoord dy) + {m_x+=dx; m_y+=dy;} + protected: + wxCoord m_x,m_y,m_w,m_h; +}; + +class pdcDrawEllipticArcOp : public pdcOp +{ + public: + pdcDrawEllipticArcOp(wxCoord x, wxCoord y, wxCoord w, wxCoord h, + double sa, double ea) + {m_x=x; m_y=y; m_w=w; m_h=h; m_sa=sa; m_ea=ea;} + virtual void DrawToDC(wxDC *dc, bool WXUNUSED(grey)=false) + {dc->DrawEllipticArc(m_x,m_y,m_w,m_h,m_sa,m_ea);} + virtual void Translate(wxCoord dx, wxCoord dy) + {m_x+=dx; m_y+=dy;} + protected: + wxCoord m_x,m_y,m_w,m_h; + double m_sa,m_ea; +}; + +class pdcDrawPointOp : public pdcOp +{ + public: + pdcDrawPointOp(wxCoord x, wxCoord y) + {m_x=x; m_y=y;} + virtual void DrawToDC(wxDC *dc, bool WXUNUSED(grey)=false) {dc->DrawPoint(m_x,m_y);} + virtual void Translate(wxCoord dx, wxCoord dy) + {m_x+=dx; m_y+=dy;} + protected: + wxCoord m_x,m_y; +}; + +class pdcDrawRoundedRectangleOp : public pdcOp +{ + public: + pdcDrawRoundedRectangleOp(wxCoord x, wxCoord y, wxCoord width, + wxCoord height, double radius) + {m_x=x; m_y=y; m_w=width; m_h=height; m_r=radius;} + virtual void DrawToDC(wxDC *dc, bool WXUNUSED(grey)=false) + {dc->DrawRoundedRectangle(m_x,m_y,m_w,m_h,m_r);} + virtual void Translate(wxCoord dx, wxCoord dy) + {m_x+=dx; m_y+=dy;} + protected: + wxCoord m_x,m_y,m_w,m_h; + double m_r; +}; + +class pdcDrawEllipseOp : public pdcOp +{ + public: + pdcDrawEllipseOp(wxCoord x, wxCoord y, wxCoord width, wxCoord height) + {m_x=x; m_y=y; m_w=width; m_h=height;} + virtual void DrawToDC(wxDC *dc, bool WXUNUSED(grey)=false) {dc->DrawEllipse(m_x,m_y,m_w,m_h);} + virtual void Translate(wxCoord dx, wxCoord dy) + {m_x+=dx; m_y+=dy;} + protected: + wxCoord m_x,m_y,m_w,m_h; +}; + +class pdcDrawIconOp : public pdcOp +{ + public: + pdcDrawIconOp(const wxIcon& icon, wxCoord x, wxCoord y) + {m_icon=icon; m_x=x; m_y=y;} + virtual void DrawToDC(wxDC *dc, bool grey=false) + { + if (grey) dc->DrawIcon(m_greyicon,m_x,m_y); + else dc->DrawIcon(m_icon,m_x,m_y); + } + virtual void CacheGrey() {m_greyicon=GetGreyIcon(m_icon);} + virtual void Translate(wxCoord dx, wxCoord dy) + {m_x+=dx; m_y+=dy;} + protected: + wxIcon m_icon; + wxIcon m_greyicon; + wxCoord m_x,m_y; +}; + +class pdcDrawLinesOp : public pdcOp +{ + public: + pdcDrawLinesOp(int n, wxPoint points[], + wxCoord xoffset = 0, wxCoord yoffset = 0); + virtual ~pdcDrawLinesOp(); + virtual void DrawToDC(wxDC *dc, bool WXUNUSED(grey)=false) + {dc->DrawLines(m_n,m_points,m_xoffset,m_yoffset);} + virtual void Translate(wxCoord dx, wxCoord dy) + { + for(int i=0; iDrawPolygon(m_n,m_points,m_xoffset,m_yoffset,m_fillStyle);} + + virtual void Translate(wxCoord dx, wxCoord dy) + { + for(int i=0; iDrawPolyPolygon(m_n,m_count,m_points, + m_xoffset,m_yoffset,m_fillStyle);} + virtual void Translate(wxCoord dx, wxCoord dy) + { + for(int i=0; iDrawRotatedText(m_text,m_x,m_y,m_angle);} + virtual void Translate(wxCoord dx, wxCoord dy) + {m_x+=dx; m_y+=dy;} + protected: + wxString m_text; + wxCoord m_x,m_y; + double m_angle; +}; + +class pdcDrawBitmapOp : public pdcOp +{ + public: + pdcDrawBitmapOp(const wxBitmap &bmp, wxCoord x, wxCoord y, + bool useMask = false) + {m_bmp=bmp; m_x=x; m_y=y; m_useMask=useMask;} + virtual void DrawToDC(wxDC *dc, bool grey=false) + { + if (grey) dc->DrawBitmap(m_greybmp,m_x,m_y,m_useMask); + else dc->DrawBitmap(m_bmp,m_x,m_y,m_useMask); + } + virtual void CacheGrey() {m_greybmp=GetGreyBitmap(m_bmp);} + virtual void Translate(wxCoord dx, wxCoord dy) + {m_x+=dx; m_y+=dy;} + protected: + wxBitmap m_bmp; + wxBitmap m_greybmp; + wxCoord m_x,m_y; + bool m_useMask; +}; + +class pdcDrawLabelOp : public pdcOp +{ + public: + pdcDrawLabelOp(const wxString& text, + const wxBitmap& image, + const wxRect& rect, + int alignment = wxALIGN_LEFT | wxALIGN_TOP, + int indexAccel = -1) + {m_text=text; m_image=image; m_rect=rect; + m_align=alignment; m_iAccel=indexAccel;} + virtual void DrawToDC(wxDC *dc, bool WXUNUSED(grey)=false) + {dc->DrawLabel(m_text,m_image,m_rect,m_align,m_iAccel);} + virtual void Translate(wxCoord dx, wxCoord dy) + {m_rect.x+=dx; m_rect.y+=dy;} + protected: + wxString m_text; + wxBitmap m_image; + wxRect m_rect; + int m_align; + int m_iAccel; +}; + +#if wxUSE_SPLINES +class pdcDrawSplineOp : public pdcOp +{ + public: + pdcDrawSplineOp(int n, wxPoint points[]); + virtual ~pdcDrawSplineOp(); + virtual void DrawToDC(wxDC *dc, bool WXUNUSED(grey)=false) {dc->DrawSpline(m_n,m_points);} + virtual void Translate(wxCoord dx, wxCoord dy) + { + int i; + for(i=0; iSetPalette(m_palette);} + protected: + wxPalette m_palette; +}; +#endif // wxUSE_PALETTE + +class pdcSetLogicalFunctionOp : public pdcOp +{ + public: + pdcSetLogicalFunctionOp(wxRasterOperationMode function) {m_function=function;} + virtual void DrawToDC(wxDC *dc, bool WXUNUSED(grey)=false) {dc->SetLogicalFunction(m_function);} + protected: + wxRasterOperationMode m_function; +}; + +//---------------------------------------------------------------------------- +// pdcObject type to contain list of operations for each real (Python) object +//---------------------------------------------------------------------------- +class pdcObject +{ + public: + pdcObject(int id) + {m_id=id; m_bounded=false; m_oplist.DeleteContents(true); + m_greyedout=false;} + + virtual ~pdcObject() {m_oplist.Clear();} + + // Protected Member Access + void SetId(int id) {m_id=id;} + int GetId() {return m_id;} + void SetBounds(wxRect& rect) {m_bounds=rect; m_bounded=true;} + wxRect GetBounds() {return m_bounds;} + void SetBounded(bool bounded) {m_bounded=bounded;} + bool IsBounded() {return m_bounded;} + void SetGreyedOut(bool greyout=true); + bool GetGreyedOut() {return m_greyedout;} + + // Op List Management Methods + void Clear() {m_oplist.Clear();} + void AddOp(pdcOp *op) + { + m_oplist.Append(op); + if (m_greyedout) op->CacheGrey(); + } + int GetLen() {return m_oplist.GetCount();} + virtual void Translate(wxCoord dx, wxCoord dy); + + // Drawing Method + virtual void DrawToDC(wxDC *dc); + protected: + int m_id; // id of object (associates this pdcObject + // with a Python object with same id) + wxRect m_bounds; // bounding rect of this object + bool m_bounded; // true if bounds is valid, false by default + pdcOpList m_oplist; // list of operations for this object + bool m_greyedout; // if true then draw this object in greys only +}; + + +//---------------------------------------------------------------------------- +// Declare a wxList to hold all the objects. List order reflects drawing +// order (Z order) and is the same order as objects are added to the list +//---------------------------------------------------------------------------- +class pdcObjectList; +WX_DECLARE_LIST(pdcObject, pdcObjectList); + +//Declare a hashmap that maps from ids to nodes in the object list. +WX_DECLARE_HASH_MAP( + int, + pdcObject *, + wxIntegerHash, + wxIntegerEqual, + pdcObjectHash +); + + +// ---------------------------------------------------------------------------- +// wxPseudoDC class +// ---------------------------------------------------------------------------- +// This is the actual PseudoDC class +// This class stores a list of recorded dc operations in m_list +// and plays them back to a real dc using DrawToDC or DrawToDCClipped. +// Drawing methods are mirrored from wxDC but add nodes to m_list +// instead of doing any real drawing. +// ---------------------------------------------------------------------------- +class wxPseudoDC : public wxObject +{ +public: + wxPseudoDC() + {m_currId=-1; m_lastObject=NULL; m_objectlist.DeleteContents(true);m_objectIndex.clear();} + ~wxPseudoDC(); + // ------------------------------------------------------------------------ + // List managment methods + // + void RemoveAll(); + int GetLen(); + + // ------------------------------------------------------------------------ + // methods for managing operations by ID + // + // Set the Id for all subsequent operations (until SetId is called again) + void SetId(int id) {m_currId = id;} + // Remove all the operations associated with an id so it can be redrawn + void ClearId(int id); + // Remove the object node (and all operations) associated with an id + void RemoveId(int id); + // Set the bounding rect of a given object + // This will create an object node if one doesn't exist + void SetIdBounds(int id, wxRect& rect); + void GetIdBounds(int id, wxRect& rect); + // Translate all the operations for this id + void TranslateId(int id, wxCoord dx, wxCoord dy); + // Grey-out an object + void SetIdGreyedOut(int id, bool greyout=true); + bool GetIdGreyedOut(int id); + // Find Objects at a point. Returns Python list of id's + // sorted in reverse drawing order (result[0] is top object) + // This version looks at drawn pixels + PyObject *FindObjects(wxCoord x, wxCoord y, + wxCoord radius=1, const wxColor& bg=*wxWHITE); + // This version only looks at bounding boxes + PyObject *FindObjectsByBBox(wxCoord x, wxCoord y); + + // ------------------------------------------------------------------------ + // Playback Methods + // + // draw to dc but skip objects known to be outside of rect + // This is a coarse level of clipping to speed things up + // when lots of objects are off screen and doesn't affect the dc level + // clipping + void DrawToDCClipped(wxDC *dc, const wxRect& rect); + void DrawToDCClippedRgn(wxDC *dc, const wxRegion& region); + // draw to dc with no clipping (well the dc will still clip) + void DrawToDC(wxDC *dc); + // draw a single object to the dc + void DrawIdToDC(int id, wxDC *dc); + + // ------------------------------------------------------------------------ + // Hit Detection Methods + // + // returns list of object with a drawn pixel within radius pixels of (x,y) + // the list is in reverse draw order so last drawn is first in list + // PyObject *HitTest(wxCoord x, wxCoord y, double radius) + // returns list of objects whose bounding boxes include (x,y) + // PyObject *HitTestBB(wxCoord x, wxCoord y) + + + // ------------------------------------------------------------------------ + // Methods mirrored from wxDC + // + void FloodFill(wxCoord x, wxCoord y, const wxColour& col, + wxFloodFillStyle style = wxFLOOD_SURFACE) + {AddToList(new pdcFloodFillOp(x,y,col,style));} + void FloodFill(const wxPoint& pt, const wxColour& col, + wxFloodFillStyle style = wxFLOOD_SURFACE) + { FloodFill(pt.x, pt.y, col, style); } + + void DrawLine(wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2) + {AddToList(new pdcDrawLineOp(x1, y1, x2, y2));} + void DrawLine(const wxPoint& pt1, const wxPoint& pt2) + { DrawLine(pt1.x, pt1.y, pt2.x, pt2.y); } + + void CrossHair(wxCoord x, wxCoord y) + {AddToList(new pdcCrossHairOp(x,y));} + void CrossHair(const wxPoint& pt) + { CrossHair(pt.x, pt.y); } + + void DrawArc(wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2, + wxCoord xc, wxCoord yc) + {AddToList(new pdcDrawArcOp(x1,y1,x2,y2,xc,yc));} + void DrawArc(const wxPoint& pt1, const wxPoint& pt2, const wxPoint& centre) + { DrawArc(pt1.x, pt1.y, pt2.x, pt2.y, centre.x, centre.y); } + + void DrawCheckMark(wxCoord x, wxCoord y, + wxCoord width, wxCoord height) + {AddToList(new pdcDrawCheckMarkOp(x,y,width,height));} + void DrawCheckMark(const wxRect& rect) + { DrawCheckMark(rect.x, rect.y, rect.width, rect.height); } + + void DrawEllipticArc(wxCoord x, wxCoord y, wxCoord w, wxCoord h, + double sa, double ea) + {AddToList(new pdcDrawEllipticArcOp(x,y,w,h,sa,ea));} + void DrawEllipticArc(const wxPoint& pt, const wxSize& sz, + double sa, double ea) + { DrawEllipticArc(pt.x, pt.y, sz.x, sz.y, sa, ea); } + + void DrawPoint(wxCoord x, wxCoord y) + {AddToList(new pdcDrawPointOp(x,y));} + void DrawPoint(const wxPoint& pt) + { DrawPoint(pt.x, pt.y); } + + void DrawPolygon(int n, wxPoint points[], + wxCoord xoffset = 0, wxCoord yoffset = 0, + wxPolygonFillMode fillStyle = wxODDEVEN_RULE) + {AddToList(new pdcDrawPolygonOp(n,points,xoffset,yoffset,fillStyle));} + + void DrawPolyPolygon(int n, int count[], wxPoint points[], + wxCoord xoffset = 0, wxCoord yoffset = 0, + wxPolygonFillMode fillStyle = wxODDEVEN_RULE) + {AddToList(new pdcDrawPolyPolygonOp(n,count,points,xoffset,yoffset,fillStyle));} + + void DrawRectangle(wxCoord x, wxCoord y, wxCoord width, wxCoord height) + {AddToList(new pdcDrawRectangleOp(x, y, width, height));} + void DrawRectangle(const wxPoint& pt, const wxSize& sz) + { DrawRectangle(pt.x, pt.y, sz.x, sz.y); } + void DrawRectangle(const wxRect& rect) + { DrawRectangle(rect.x, rect.y, rect.width, rect.height); } + + void DrawRoundedRectangle(wxCoord x, wxCoord y, wxCoord width, wxCoord height, + double radius) + {AddToList(new pdcDrawRoundedRectangleOp(x,y,width,height,radius));} + void DrawRoundedRectangle(const wxPoint& pt, const wxSize& sz, + double radius) + { DrawRoundedRectangle(pt.x, pt.y, sz.x, sz.y, radius); } + void DrawRoundedRectangle(const wxRect& r, double radius) + { DrawRoundedRectangle(r.x, r.y, r.width, r.height, radius); } + + void DrawCircle(wxCoord x, wxCoord y, wxCoord radius) + { DrawEllipse(x - radius, y - radius, 2*radius, 2*radius); } + void DrawCircle(const wxPoint& pt, wxCoord radius) + { DrawCircle(pt.x, pt.y, radius); } + + void DrawEllipse(wxCoord x, wxCoord y, wxCoord width, wxCoord height) + {AddToList(new pdcDrawEllipseOp(x,y,width,height));} + void DrawEllipse(const wxPoint& pt, const wxSize& sz) + { DrawEllipse(pt.x, pt.y, sz.x, sz.y); } + void DrawEllipse(const wxRect& rect) + { DrawEllipse(rect.x, rect.y, rect.width, rect.height); } + + void DrawIcon(const wxIcon& icon, wxCoord x, wxCoord y) + {AddToList(new pdcDrawIconOp(icon,x,y));} + void DrawIcon(const wxIcon& icon, const wxPoint& pt) + { DrawIcon(icon, pt.x, pt.y); } + + void DrawLines(int n, wxPoint points[], + wxCoord xoffset = 0, wxCoord yoffset = 0) + {AddToList(new pdcDrawLinesOp(n,points,xoffset,yoffset));} + + void DrawBitmap(const wxBitmap &bmp, wxCoord x, wxCoord y, + bool useMask = false) + {AddToList(new pdcDrawBitmapOp(bmp,x,y,useMask));} + void DrawBitmap(const wxBitmap &bmp, const wxPoint& pt, + bool useMask = false) + { DrawBitmap(bmp, pt.x, pt.y, useMask); } + + void DrawText(const wxString& text, wxCoord x, wxCoord y) + {AddToList(new pdcDrawTextOp(text, x, y));} + void DrawText(const wxString& text, const wxPoint& pt) + { DrawText(text, pt.x, pt.y); } + + void DrawRotatedText(const wxString& text, wxCoord x, wxCoord y, double angle) + {AddToList(new pdcDrawRotatedTextOp(text,x,y,angle));} + void DrawRotatedText(const wxString& text, const wxPoint& pt, double angle) + { DrawRotatedText(text, pt.x, pt.y, angle); } + + // this version puts both optional bitmap and the text into the given + // rectangle and aligns is as specified by alignment parameter; it also + // will emphasize the character with the given index if it is != -1 + void DrawLabel(const wxString& text, + const wxBitmap& image, + const wxRect& rect, + int alignment = wxALIGN_LEFT | wxALIGN_TOP, + int indexAccel = -1) + {AddToList(new pdcDrawLabelOp(text,image,rect,alignment,indexAccel));} + + void DrawLabel(const wxString& text, const wxRect& rect, + int alignment = wxALIGN_LEFT | wxALIGN_TOP, + int indexAccel = -1) + { DrawLabel(text, wxNullBitmap, rect, alignment, indexAccel); } + +/*?????? I don't think that the source dc would stick around + void Blit(wxCoord xdest, wxCoord ydest, wxCoord width, wxCoord height, + wxDC *source, wxCoord xsrc, wxCoord ysrc, + int rop = wxCOPY, bool useMask = false, wxCoord xsrcMask = wxDefaultCoord, wxCoord ysrcMask = wxDefaultCoord) + {AddToList(new pdcBlitOp(xdest,ydest,width,height,source,xsrc, + ysrc,rop,useMask,xsrcMask,ysrcMask));} + void Blit(const wxPoint& destPt, const wxSize& sz, + wxDC *source, const wxPoint& srcPt, + int rop = wxCOPY, bool useMask = false, const wxPoint& srcPtMask = wxDefaultPosition) + { + Blit(destPt.x, destPt.y, sz.x, sz.y, source, srcPt.x, srcPt.y, + rop, useMask, srcPtMask.x, srcPtMask.y); + } +??????*/ + +#if wxUSE_SPLINES + void DrawSpline(int n, wxPoint points[]) + {AddToList(new pdcDrawSplineOp(n,points));} +#endif // wxUSE_SPLINES + +#if wxUSE_PALETTE + void SetPalette(const wxPalette& palette) + {AddToList(new pdcSetPaletteOp(palette));} +#endif // wxUSE_PALETTE + + void SetLogicalFunction(wxRasterOperationMode function) + {AddToList(new pdcSetLogicalFunctionOp(function));} + void SetFont(const wxFont& font) + {AddToList(new pdcSetFontOp(font));} + void SetPen(const wxPen& pen) + {AddToList(new pdcSetPenOp(pen));} + void SetBrush(const wxBrush& brush) + {AddToList(new pdcSetBrushOp(brush));} + void SetBackground(const wxBrush& brush) + {AddToList(new pdcSetBackgroundOp(brush));} + void SetBackgroundMode(int mode) + {AddToList(new pdcSetBackgroundModeOp(mode));} + void SetTextBackground(const wxColour& colour) + {AddToList(new pdcSetTextBackgroundOp(colour));} + void SetTextForeground(const wxColour& colour) + {AddToList(new pdcSetTextForegroundOp(colour));} + + void Clear() + {AddToList(new pdcClearOp());} + void BeginDrawing() + {AddToList(new pdcBeginDrawingOp());} + void EndDrawing() + {AddToList(new pdcEndDrawingOp());} + +protected: + // ------------------------------------------------------------------------ + // protected helper methods + void AddToList(pdcOp *newOp); + pdcObject *FindObject(int id, bool create=false); + + // ------------------------------------------------------------------------ + // Data members + // + int m_currId; // id to use for operations done on the PseudoDC + pdcObject *m_lastObject; // used to find last used object quickly + pdcObjectList m_objectlist; // list of objects + pdcObjectHash m_objectIndex; //id->object lookup index + +}; + +#endif + diff --git a/unittests/test_pseudodc.py b/unittests/test_pseudodc.py new file mode 100644 index 00000000..86e35080 --- /dev/null +++ b/unittests/test_pseudodc.py @@ -0,0 +1,16 @@ +import unittest +import wtc +import wx + +#--------------------------------------------------------------------------- + +class pseudodc_Tests(wtc.WidgetTestCase): + + # TODO: Remove this test and add real ones. + def test_pseudodc1(self): + self.fail("Unit tests for pseudodc not implemented yet.") + +#--------------------------------------------------------------------------- + +if __name__ == '__main__': + unittest.main()