+ wxString m_strText;
+ wxBitmap m_bitmap;
+};
+
+// ----------------------------------------------------------------------------
+// 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)
+ {
+ }
+
+ // this is for debugging - lets us see when exactly an object is freed
+ // (this may be later than you think if it's on the clipboard, for example)
+ virtual ~DnDShape() { }
+
+ // the functions used for drag-and-drop: they dump and restore a shape into
+ // some bitwise-copiable data (might use streams too...)
+ // ------------------------------------------------------------------------
+
+ // 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; }
+
+ void Move(const wxPoint& pos) { m_pos = pos; }
+
+ // to implement in derived classes
+ virtual Kind GetKind() const = 0;
+
+ virtual void Draw(wxDC& dc)
+ {
+ 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)
+ {
+ wxLogMessage("DnDTriangularShape is being created");
+ }
+
+ virtual ~DnDTriangularShape()
+ {
+ wxLogMessage("DnDTriangularShape is being deleted");
+ }
+
+ 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);
+
+#ifdef __WXMSW__
+ dc.FloodFill(GetCentre(), m_col, wxFLOOD_BORDER);
+#endif
+ }
+};
+
+class DnDRectangularShape : public DnDShape
+{
+public:
+ DnDRectangularShape(const wxPoint& pos,
+ const wxSize& size,
+ const wxColour& col)
+ : DnDShape(pos, size, col)
+ {
+ wxLogMessage("DnDRectangularShape is being created");
+ }
+
+ virtual ~DnDRectangularShape()
+ {
+ wxLogMessage("DnDRectangularShape is being deleted");
+ }
+
+ 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);
+
+#ifdef __WXMSW__
+ dc.FloodFill(GetCentre(), m_col, wxFLOOD_BORDER);
+#endif
+ }
+};
+
+class DnDEllipticShape : public DnDShape
+{
+public:
+ DnDEllipticShape(const wxPoint& pos,
+ const wxSize& size,
+ const wxColour& col)
+ : DnDShape(pos, size, col)
+ {
+ wxLogMessage("DnDEllipticShape is being created");
+ }
+
+ virtual ~DnDEllipticShape()
+ {
+ wxLogMessage("DnDEllipticShape is being deleted");
+ }
+
+ virtual Kind GetKind() const { return Ellipse; }
+ virtual void Draw(wxDC& dc)
+ {
+ DnDShape::Draw(dc);
+
+ dc.DrawEllipse(m_pos, m_size);
+
+#ifdef __WXMSW__
+ dc.FloodFill(GetCentre(), m_col, wxFLOOD_BORDER);
+#endif
+ }
+};
+
+// ----------------------------------------------------------------------------
+// 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 = (DnDShape *)NULL)
+ {
+ if ( shape )
+ {
+ // we need to copy the shape because the one we're handled may be
+ // deleted while it's still on the clipboard (for example) - and we
+ // reuse the serialisation methods here to copy it
+ void *buf = malloc(shape->DnDShape::GetDataSize());
+ shape->GetDataHere(buf);
+ m_shape = DnDShape::New(buf);
+
+ free(buf);
+ }
+ else
+ {
+ // nothing to copy
+ m_shape = NULL;
+ }
+
+ // 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;
+ }
+
+ virtual ~DnDShapeDataObject() { delete m_shape; }
+
+ // after a call to this function, the shape is owned by the caller and it
+ // is responsible for deleting it!
+ //
+ // NB: a better solution would be to make DnDShapes ref counted and this
+ // is what should probably be done in a real life program, otherwise
+ // the ownership problems become too complicated really fast
+ DnDShape *GetShape()
+ {
+ DnDShape *shape = m_shape;
+
+ m_shape = (DnDShape *)NULL;
+ m_hasBitmap = FALSE;
+
+ return shape;
+ }
+
+ // implement base class pure virtuals
+ // ----------------------------------
+
+ virtual wxDataFormat GetPreferredFormat(Direction WXUNUSED(dir)) const
+ {
+ return m_formatShape;
+ }
+
+ virtual size_t GetFormatCount(Direction dir) const
+ {
+ // our custom format is supported by both GetData() and SetData()
+ size_t nFormats = 1;
+ if ( dir == Get )
+ {
+ // but the bitmap format(s) are only supported for output
+ nFormats += m_dataobj.GetFormatCount(dir);
+ }
+
+ return nFormats;
+ }
+
+ virtual void GetAllFormats(wxDataFormat *formats, Direction dir) const
+ {
+ formats[0] = m_formatShape;
+ if ( dir == Get )
+ {
+ m_dataobj.GetAllFormats(&formats[1], dir);
+ }
+ }
+
+ virtual size_t GetDataSize(const wxDataFormat& format) const
+ {
+ if ( format == m_formatShape )
+ {
+ return m_shape->GetDataSize();
+ }
+ else
+ {
+ if ( !m_hasBitmap )
+ CreateBitmap();
+
+ return m_dataobj.GetDataSize();
+ }
+ }
+
+ virtual bool GetDataHere(const wxDataFormat& format, void *pBuf) const
+ {
+ if ( format == m_formatShape )
+ {
+ m_shape->GetDataHere(pBuf);
+
+ return TRUE;
+ }
+ else
+ {
+ if ( !m_hasBitmap )
+ CreateBitmap();
+
+ return m_dataobj.GetDataHere(pBuf);
+ }
+ }
+
+ virtual bool SetData(const wxDataFormat& format,
+ size_t len, const void *buf)
+ {
+ wxCHECK_MSG( format == m_formatShape, FALSE, "unsupported format" );
+
+ delete m_shape;
+ m_shape = DnDShape::New(buf);
+
+ // the shape has changed
+ m_hasBitmap = FALSE;
+
+ return TRUE;
+ }
+
+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 OnNewShape(wxCommandEvent& event);
+ void OnEditShape(wxCommandEvent& event);
+ void OnClearShape(wxCommandEvent& event);
+
+ void OnCopyShape(wxCommandEvent& event);
+ void OnPasteShape(wxCommandEvent& event);
+
+ void OnUpdateUICopy(wxUpdateUIEvent& event);
+ void OnUpdateUIPaste(wxUpdateUIEvent& event);
+
+ void OnDrag(wxMouseEvent& event);
+ void OnPaint(wxPaintEvent& event);
+ void OnDrop(wxCoord x, wxCoord y, DnDShape *shape);
+
+private:
+ DnDShape *m_shape;
+
+ static DnDShapeFrame *ms_lastDropTarget;
+
+ DECLARE_EVENT_TABLE()
+};
+
+// ----------------------------------------------------------------------------
+// wxDropTarget derivation for DnDShapes
+// ----------------------------------------------------------------------------
+
+class DnDShapeDropTarget : public wxDropTarget
+{
+public:
+ DnDShapeDropTarget(DnDShapeFrame *frame)
+ : wxDropTarget(new DnDShapeDataObject)
+ {
+ m_frame = frame;
+ }
+
+ // override base class (pure) virtuals
+ virtual wxDragResult OnEnter(wxCoord x, wxCoord y, wxDragResult def)
+ { m_frame->SetStatusText("Mouse entered the frame");
+ return OnDragOver(x, y, def); }
+ virtual void OnLeave()
+ { m_frame->SetStatusText("Mouse left the frame"); }
+ virtual wxDragResult OnData(wxCoord x, wxCoord y, wxDragResult def)
+ {
+ if ( !GetData() )
+ {
+ wxLogError("Failed to get drag and drop data");
+
+ return wxDragNone;
+ }
+
+ m_frame->OnDrop(x, y,
+ ((DnDShapeDataObject *)GetDataObject())->GetShape());
+
+ return def;
+ }
+
+private:
+ DnDShapeFrame *m_frame;