None of this appears to be documented, so I thought it would be helpful to put this simplified sample code here.
The custom data format is straight forward. Just delcare it and use it.
The custom data object is a bit trickier: you need to get the right levels of indirection on the buffer copying. Since wxDataObjectSimple casts everything to void*, you don't have any help from the compiler. In my example, I show how to properly cast these to your app's object.
wxDropTarget is the messiest part. This is mostly because the implementation doesn't support all of the functionality implied by the class documentation. First, I don't believe there is a way to use wxDragLink. If you look at the source code for wxDropSource, you will see that there is no usage of wxDragLink, and if you try to use SetCursor to make use of it, that will actually set the cursor for wxDragNone instead.
So, for wxDropTarget, all you really have available is wxDragNone, wxDragCopy, and wxDragMove. I've created custom cursors for these and put some simple logic into onDragOver to switch between the latter two cursors.
One other thing worth noting: in wxDropTarget, there's no way to get the "owner" object. So, I subclassed it and passed the in the "owner".
This code was built and tested on Windows XP.
If you have any suggestions to improve this example, or if you find it useful, please let me know
Here's the source:
Code: Select all
#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include "wx/dnd.h"
// ----------------------------------------------------------------------------
// cursors
// ----------------------------------------------------------------------------
static char * top_half_xpm[] = {
"16 16 2 1 8 8",
" c None",
". c #000000",
" ",
" .............. ",
" . . ",
" . . ",
" . . ",
" .............. ",
" ",
"................",
"................",
" ",
" ",
" ",
" ",
" ",
" ",
" "};
static char * bottom_half_xpm[] = {
"16 16 2 1 8 8",
" c None",
". c #000000",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
"................",
"................",
" ",
" .............. ",
" . . ",
" . . ",
" . . ",
" .............. ",
" "};
static char * none_xpm[] = {
"16 16 2 1 8 8",
" c None",
". c #000000",
". .",
" . . ",
" . . ",
" . . ",
" . . ",
" . . ",
" . . ",
"................",
"................",
" . . ",
" . . ",
" . . ",
" . . ",
" . . ",
" . . ",
". ."};
// ----------------------------------------------------------------------------
// MyDataFormat
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
class MyDataFormat : public wxDataFormat
{
public:
MyDataFormat () : wxDataFormat (wxT("MyDataFormat")) {}
};
MyDataFormat myDataFormat;
// ----------------------------------------------------------------------------
// MyDataObject
// ----------------------------------------------------------------------------
class Thing;
// ----------------------------------------------------------------------------
class MyDataObject : public wxDataObjectSimple
{
private:
Thing* theThing;
public:
MyDataObject () : wxDataObjectSimple () { SetFormat (myDataFormat); }
size_t GetDataSize() const { return sizeof (void*); }
bool GetDataHere (void* buf) const { *(Thing**)buf = theThing; return true; }
bool SetData (size_t len, const void* buf) { theThing = *(Thing**)buf; return true; }
const Thing* getTheThing() { return theThing; }
};
// ----------------------------------------------------------------------------
// MyDropTarget interface
// ----------------------------------------------------------------------------
class MyDropTarget : public wxDropTarget
{
private:
Thing* destThing;
public:
MyDropTarget (Thing* destThing);
wxDragResult OnData (wxCoord x, wxCoord y, wxDragResult def);
wxDragResult OnDragOver (wxCoord x, wxCoord y, wxDragResult def);
};
// ----------------------------------------------------------------------------
// Thing
// ----------------------------------------------------------------------------
class Thing : public wxWindow
{
private:
int id;
wxCursor* cursorTop;
wxCursor* cursorBottom;
wxCursor* cursorNone;
DECLARE_EVENT_TABLE();
public:
// ----------------------------------------------------------------------------
// constructor
Thing (wxWindow* parent, int id, wxCursor* cursorTop, wxCursor* cursorBottom, wxCursor* cursorNone) :
wxWindow (parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE),
id (id),
cursorTop (cursorTop),
cursorBottom (cursorBottom),
cursorNone (cursorNone)
{
SetInitialSize (wxSize(50,20));
SetDropTarget (new MyDropTarget(this));
}
// ----------------------------------------------------------------------------
void Thing::onLeftDown (wxMouseEvent& event)
{
Thing* myData = this; // we need one more level of indirection for SetData.
MyDataObject data;
data.SetData (sizeof (Thing*), &myData);
wxDropSource source (data, this, *cursorTop, *cursorBottom, *cursorNone);
wxDragResult result = source.DoDragDrop (wxDrag_AllowMove);
}
// ----------------------------------------------------------------------------
void Thing::OnPaint (wxPaintEvent& event)
{
int xOffset = 20;
int yOffset = 5;
wxString s;
s.Printf (wxT("%d"), id);
wxPaintDC dc (this);
dc.DrawText (s, xOffset, yOffset);
}
// ----------------------------------------------------------------------------
int getId () const { return id; }
};
BEGIN_EVENT_TABLE(Thing, wxWindow)
EVT_PAINT(Thing::OnPaint)
EVT_LEFT_DOWN (Thing::onLeftDown)
END_EVENT_TABLE()
// ----------------------------------------------------------------------------
// MyDropTarget
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
MyDropTarget::MyDropTarget (Thing* destThing) :
destThing (destThing)
{
SetDataObject (new MyDataObject());
}
// ----------------------------------------------------------------------------
wxDragResult MyDropTarget::OnData (wxCoord x, wxCoord y, wxDragResult def)
{
if (!GetData())
{
wxLogDebug ("GetData failed");
return wxDragError;
}
MyDataObject* data = dynamic_cast<MyDataObject*>(m_dataObject);
wxLogDebug ("onData: from %d to %d", data->getTheThing()->getId(), destThing->getId());
return def;
}
// ----------------------------------------------------------------------------
wxDragResult MyDropTarget::OnDragOver (wxCoord x, wxCoord y, wxDragResult def)
{
int height = destThing->GetSize().y;
if (y < height/2)
return wxDragCopy;
else
return wxDragMove;
}
// ----------------------------------------------------------------------------
// Pane
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
class Pane : public wxScrolledWindow
{
private:
wxCursor* cursorTop;
wxCursor* cursorBottom;
wxCursor* cursorNone;
DECLARE_EVENT_TABLE();
public:
// ----------------------------------------------------------------------------
Pane (wxWindow* frame) :
wxScrolledWindow (frame, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL)
{
SetScrollRate (0, 20);
SetBackgroundColour (wxT("YELLOW"));
createCursors ();
wxSizer* sizer = new wxBoxSizer (wxVERTICAL);
SetSizer (sizer);
SetAutoLayout (true);
for (size_t i = 1; i < 6; i++)
{
Thing* node = new Thing (this, i, cursorTop, cursorBottom, cursorNone);
sizer->Add (node);
}
wxSize size = GetBestVirtualSize();
SetVirtualSize( size );
}
// ----------------------------------------------------------------------------
// destructor
~Pane ()
{
delete cursorTop;
delete cursorBottom;
delete cursorNone;
}
// ----------------------------------------------------------------------------
void createCursors ()
{
wxBitmap bmTop (top_half_xpm, wxBITMAP_TYPE_XPM);
wxImage imgTop = bmTop.ConvertToImage();
imgTop.SetOption (wxIMAGE_OPTION_CUR_HOTSPOT_X, 8);
imgTop.SetOption (wxIMAGE_OPTION_CUR_HOTSPOT_Y, 8);
cursorTop = new wxCursor (imgTop);
wxBitmap bmBottom (bottom_half_xpm, wxBITMAP_TYPE_XPM);
wxImage imgBottom = bmBottom.ConvertToImage();
imgBottom.SetOption (wxIMAGE_OPTION_CUR_HOTSPOT_X, 8);
imgBottom.SetOption (wxIMAGE_OPTION_CUR_HOTSPOT_Y, 8);
cursorBottom = new wxCursor (imgBottom);
wxBitmap bmNone (none_xpm, wxBITMAP_TYPE_XPM);
wxImage imgNone = bmNone.ConvertToImage();
imgNone.SetOption (wxIMAGE_OPTION_CUR_HOTSPOT_X, 8);
imgNone.SetOption (wxIMAGE_OPTION_CUR_HOTSPOT_Y, 8);
cursorNone = new wxCursor (imgNone);
}
}; // Pane
BEGIN_EVENT_TABLE(Pane, wxScrolledWindow)
END_EVENT_TABLE()
// ----------------------------------------------------------------------------
// MainFrame
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
class MainFrame : public wxFrame
{
private:
DECLARE_EVENT_TABLE();
Pane* pane;
public:
// ----------------------------------------------------------------------------
MainFrame(const wxString& title) :
wxFrame(NULL, wxID_ANY, title, wxPoint (300,100), wxSize (500,500))
{
// build contents
SetBackgroundColour (wxT("GREEN"));
pane = new Pane (this);
}
// ----------------------------------------------------------------------------
void onClose(wxCloseEvent& event)
{
Destroy();
}
};
BEGIN_EVENT_TABLE(MainFrame, wxFrame)
EVT_CLOSE(MainFrame::onClose)
END_EVENT_TABLE();
// ----------------------------------------------------------------------------
// Application
// ----------------------------------------------------------------------------
class Test : public wxApp
{
public:
virtual bool OnInit()
{
// debug settings - for detecting memory leaks
_CrtSetDbgFlag (_CrtSetDbgFlag (_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
// call the base class initialization method, currently it only parses a
// few common command-line options but it could be do more in the future
if ( !wxApp::OnInit() )
return false;
// create the main application window
MainFrame *frame = new MainFrame(_T("Test"));
frame->Show(true);
return true;
}
virtual ~Test()
{
}
};
IMPLEMENT_APP(Test)