+// ----------------------------------------------------------------------------
+// A shape is an example of application-specific data which may be transported
+// via drag-and-drop or clipboard: in our case, we have different geometric
+// shapes, each one with its own colour and position
+// ----------------------------------------------------------------------------
+
+class DnDShape
+{
+public:
+ enum Kind
+ {
+ None,
+ Triangle,
+ Rectangle,
+ Ellipse
+ };
+
+ DnDShape(const wxPoint& pos,
+ const wxSize& size,
+ const wxColour& col)
+ : m_pos(pos), m_size(size), m_col(col)
+ {
+ }
+
+ // the functions used for drag-and-drop: they dump and restore a shape into
+ // some bitwise-copiable data
+ //
+ // NB: here we profit from the fact that wxPoint, wxSize and wxColour are
+ // POD (plain old data) and so can be copied directly - but it wouldn't
+ // work for other types!
+ // ------------------------------------------------------------------------
+
+ // restore from buffer
+ static DnDShape *New(const void *buf);
+
+ virtual size_t GetDataSize() const
+ {
+ return sizeof(ShapeDump);
+ }
+
+ virtual void GetDataHere(void *buf) const
+ {
+ ShapeDump& dump = *(ShapeDump *)buf;
+ dump.x = m_pos.x;
+ dump.y = m_pos.y;
+ dump.w = m_size.x;
+ dump.h = m_size.y;
+ dump.r = m_col.Red();
+ dump.g = m_col.Green();
+ dump.b = m_col.Blue();
+ dump.k = GetKind();
+ }
+
+ // accessors
+ const wxPoint& GetPosition() const { return m_pos; }
+ const wxColour& GetColour() const { return m_col; }
+ const wxSize& GetSize() const { return m_size; }
+
+ // to implement in derived classes
+ virtual Kind GetKind() const = 0;
+
+ virtual void Draw(wxDC& dc) = 0
+ {
+ dc.SetPen(wxPen(m_col, 1, wxSOLID));
+ }
+
+protected:
+ wxPoint GetCentre() const
+ { return wxPoint(m_pos.x + m_size.x / 2, m_pos.y + m_size.y / 2); }
+
+ struct ShapeDump
+ {
+ int x, y, // position
+ w, h, // size
+ r, g, b, // colour
+ k; // kind
+ };
+
+ wxPoint m_pos;
+ wxSize m_size;
+ wxColour m_col;
+};
+
+class DnDTriangularShape : public DnDShape
+{
+public:
+ DnDTriangularShape(const wxPoint& pos,
+ const wxSize& size,
+ const wxColour& col)
+ : DnDShape(pos, size, col)
+ {
+ }
+
+ virtual Kind GetKind() const { return Triangle; }
+ virtual void Draw(wxDC& dc)
+ {
+ DnDShape::Draw(dc);
+
+ // well, it's a bit difficult to describe a triangle by position and
+ // size, but we're not doing geometry here, do we? ;-)
+ wxPoint p1(m_pos);
+ wxPoint p2(m_pos.x + m_size.x, m_pos.y);
+ wxPoint p3(m_pos.x, m_pos.y + m_size.y);
+
+ dc.DrawLine(p1, p2);
+ dc.DrawLine(p2, p3);
+ dc.DrawLine(p3, p1);
+
+ dc.FloodFill(GetCentre(), m_col, wxFLOOD_BORDER);
+ }
+};
+
+class DnDRectangularShape : public DnDShape
+{
+public:
+ DnDRectangularShape(const wxPoint& pos,
+ const wxSize& size,
+ const wxColour& col)
+ : DnDShape(pos, size, col)
+ {
+ }
+
+ virtual Kind GetKind() const { return Rectangle; }
+ virtual void Draw(wxDC& dc)
+ {
+ DnDShape::Draw(dc);
+
+ wxPoint p1(m_pos);
+ wxPoint p2(p1.x + m_size.x, p1.y);
+ wxPoint p3(p2.x, p2.y + m_size.y);
+ wxPoint p4(p1.x, p3.y);
+
+ dc.DrawLine(p1, p2);
+ dc.DrawLine(p2, p3);
+ dc.DrawLine(p3, p4);
+ dc.DrawLine(p4, p1);
+
+ dc.FloodFill(GetCentre(), m_col, wxFLOOD_BORDER);
+ }
+};
+
+class DnDEllipticShape : public DnDShape
+{
+public:
+ DnDEllipticShape(const wxPoint& pos,
+ const wxSize& size,
+ const wxColour& col)
+ : DnDShape(pos, size, col)
+ {
+ }
+
+ virtual Kind GetKind() const { return Ellipse; }
+ virtual void Draw(wxDC& dc)
+ {
+ DnDShape::Draw(dc);
+
+ dc.DrawEllipse(m_pos, m_size);
+
+ dc.FloodFill(GetCentre(), m_col, wxFLOOD_BORDER);
+ }
+};
+
+// ----------------------------------------------------------------------------
+// A wxDataObject specialisation for the application-specific data
+// ----------------------------------------------------------------------------
+
+static const char *shapeFormatId = "wxShape";
+
+class DnDShapeDataObject : public wxDataObject
+{
+public:
+ // ctor doesn't copy the pointer, so it shouldn't go away while this object
+ // is alive
+ DnDShapeDataObject(DnDShape *shape)
+ {
+ m_shape = shape;
+
+ // this string should uniquely identify our format, but is otherwise
+ // arbitrary
+ m_formatShape.SetId(shapeFormatId);
+
+ // we don't draw the shape to a bitmap until it's really needed (i.e.
+ // we're asked to do so)
+ m_hasBitmap = FALSE;
+ }
+
+ // implement base class pure virtuals
+ // ----------------------------------
+
+ virtual wxDataFormat GetPreferredFormat() const
+ {
+ return m_formatShape;
+ }
+
+ virtual size_t GetFormatCount() const
+ {
+ // +1 for our custom format
+ return m_dataobj.GetFormatCount() + 1;
+ }
+
+ virtual void GetAllFormats(wxDataFormat *formats) const
+ {
+ formats[0] = m_formatShape;
+ m_dataobj.GetAllFormats(&formats[1]);
+ }
+
+ virtual size_t GetDataSize(const wxDataFormat& format) const
+ {
+ if ( format == m_formatShape )
+ {
+ return m_shape->GetDataSize();
+ }
+ else
+ {
+ wxASSERT_MSG( format == wxDF_BITMAP, "unsupported format" );
+
+ if ( !m_hasBitmap )
+ CreateBitmap();
+
+ return m_dataobj.GetDataSize(format);
+ }
+ }
+
+ virtual void GetDataHere(const wxDataFormat& format, void *pBuf) const
+ {
+ if ( format == m_formatShape )
+ {
+ m_shape->GetDataHere(pBuf);
+ }
+ else
+ {
+ wxASSERT_MSG( format == wxDF_BITMAP, "unsupported format" );
+
+ if ( !m_hasBitmap )
+ CreateBitmap();
+
+ m_dataobj.GetDataHere(format, pBuf);
+ }
+ }
+
+private:
+ // creates a bitmap and assigns it to m_dataobj (also sets m_hasBitmap)
+ void CreateBitmap() const;
+
+ wxDataFormat m_formatShape; // our custom format
+
+ wxBitmapDataObject m_dataobj; // it handles bitmaps
+ bool m_hasBitmap; // true if m_dataobj has valid bitmap
+
+ DnDShape *m_shape; // our data
+};
+
+// ----------------------------------------------------------------------------
+// A dialog to edit shape properties
+// ----------------------------------------------------------------------------
+
+class DnDShapeDialog : public wxDialog
+{
+public:
+ DnDShapeDialog(wxFrame *parent, DnDShape *shape);
+
+ DnDShape *GetShape() const;
+
+ virtual bool TransferDataToWindow();
+ virtual bool TransferDataFromWindow();
+
+ void OnColour(wxCommandEvent& event);
+
+private:
+ // input
+ DnDShape *m_shape;
+
+ // output
+ DnDShape::Kind m_shapeKind;
+ wxPoint m_pos;
+ wxSize m_size;
+ wxColour m_col;
+
+ // controls
+ wxRadioBox *m_radio;
+ wxTextCtrl *m_textX,
+ *m_textY,
+ *m_textW,
+ *m_textH;
+
+ DECLARE_EVENT_TABLE()
+};
+
+// ----------------------------------------------------------------------------
+// A frame for the shapes which can be drag-and-dropped between frames
+// ----------------------------------------------------------------------------
+
+class DnDShapeFrame : public wxFrame
+{
+public:
+ DnDShapeFrame(wxFrame *parent);
+ ~DnDShapeFrame();
+
+ void SetShape(DnDShape *shape);
+
+ // callbacks
+ void OnDrag(wxMouseEvent& event);
+ void OnEdit(wxMouseEvent& event);
+ void OnPaint(wxPaintEvent& event);
+ void OnDrop(long x, long y, DnDShape *shape);
+
+private:
+ DnDShape *m_shape;
+
+ DECLARE_EVENT_TABLE()
+};
+
+// ----------------------------------------------------------------------------
+// wxDropTarget derivation for DnDShapes
+// ----------------------------------------------------------------------------
+
+class DnDShapeDropTarget : public wxDropTarget
+{
+public:
+ DnDShapeDropTarget(DnDShapeFrame *frame)
+ {
+ m_frame = frame;
+
+ // the same as used by DnDShapeDataObject
+ m_formatShape.SetId(shapeFormatId);
+ }
+
+ // override base class (pure) virtuals
+ virtual void OnEnter()
+ { m_frame->SetStatusText("Mouse entered the frame"); }
+ virtual void OnLeave()
+ { m_frame->SetStatusText("Mouse left the frame"); }
+ virtual bool OnDrop(long x, long y, const void *pData)
+ {
+ m_frame->OnDrop(x, y, DnDShape::New(pData));
+
+ return TRUE;
+ }
+
+protected:
+ virtual size_t GetFormatCount() const { return 1; }
+ virtual wxDataFormat GetFormat(size_t WXUNUSED(n)) const
+ { return m_formatShape; }
+
+private:
+ DnDShapeFrame *m_frame;
+ wxDataFormat m_formatShape;
+};
+