drag/drop problems, need some help plz

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
Post Reply
bjw0
Earned a small fee
Earned a small fee
Posts: 13
Joined: Mon Oct 25, 2004 5:10 pm

drag/drop problems, need some help plz

Post by bjw0 »

I'm using the wxDataObjectSimple class, which was supposed to make drag/drop implementation easy. I must be really stupid then cause I can't get what I want to work. =p

I'm a little confused about:
virtual size_t GetDataSize() const
virtual bool GetDataHere(void *buf) const
virtual bool SetData(size_t len, const void *buf)

Is the GetDataSize and GetDataHere called for the source data object and the SetData for the target data object?

I'm only using a single format, and to store the data i'm using wxArrayString. I haven't had a lot of experience with void pointers coming from Java, and I think I'm running into problems there. If someone is feeling really nice, could you please show me a generic implementation of those three methods, which would transfer the data from the source wxArrayString to the target wxArrayString?

Thank You
bjw0
Earned a small fee
Earned a small fee
Posts: 13
Joined: Mon Oct 25, 2004 5:10 pm

Post by bjw0 »

I just wanted to mention real quick that I know I could make the wxArrayString static. Then I wouldn't even have to use GetDataSize, GetDataHere, and SetData. Although, I would really like to see how I could use them though.
User avatar
tierra
Site Admin
Site Admin
Posts: 1355
Joined: Sun Aug 29, 2004 7:14 pm
Location: Salt Lake City, Utah, USA
Contact:

Post by tierra »

bjw0 wrote:I just wanted to mention real quick that I know I could make the wxArrayString static.
Well, then you still wouldn't be able to transfer between two different instances of your application ;)

The sample in samples/dnd gives a good example of wxDataObject (which is just wxDataObjectSimple except you have a more flexibility with wxDataFormats if I remember right... it's been a while since I've worked with it).

GetDataSize needs to return the size of the object you plan on storing in the void pointer (so the clipboard implementation knows how much to allocate), GetDataHere needs to save your object off in the given pointer, and SetData needs to read the data from the given pointer, and re-construct your object.
bjw0
Earned a small fee
Earned a small fee
Posts: 13
Joined: Mon Oct 25, 2004 5:10 pm

Post by bjw0 »

I didn't even think about having to handle two instances of an application. I'm glad you brought that up though cause now I definitely won't use the static wxArrayString.

I have looked at that dnd sample and that is where I've learned what I know so far. I tried following the code with my own project, but I'm getting access violation errors on the wxArrayString pointer, which I casted from the void pointer.

In GetDataSize, I get the size of the dropped wxArrayString. In GetDataHere is where I'm having the problems. I cast the void pointer to a wxArrayString pointer. At that point I try to copy the dropped wxArrayString into that wxArrayString pointer, but I get the access violation errors. :(
bjw0
Earned a small fee
Earned a small fee
Posts: 13
Joined: Mon Oct 25, 2004 5:10 pm

Post by bjw0 »

I'm still having problems getting this to work. If someone could look at the following code snippets and let me know what might be the problem, then that would be great!

Also, I'm no longer using the wxDataObjectSimple class in case I would have to support multiple formats in the future...

Code: Select all

size_t fxIDDataObject::GetDataSize(const wxDataFormat &Format) const
{
  //find requested format
  fxIDDataObjectFormat *format = pvFindFormat(Format);

  //make sure format was found
  if(format == (fxIDDataObjectFormat*)NULL)
  {
    return 0;
  }

  //return the size of the wxArrayString object
  return sizeof(format->GetObjectIDArray());
}

bool fxIDDataObject::GetDataHere(const wxDataFormat &Format, void *Buffer) const
{
  //find requested format
  fxIDDataObjectFormat *format = pvFindFormat(Format);

  //make sure format was found
  if(format == (fxIDDataObjectFormat*)NULL)
  {
    return 0;
  }

  //cast buffer to wxArrayString
  wxArrayString &objectIDArrayBuffer = *(wxArrayString*)Buffer;

  //get # of strings in source wxArrayString
  size_t idCt = format->GetObjectIDArray().GetCount();

  //copy all strings from source wxArrayString to the wxArrayString buffer
  for(size_t i = 0; i < idCt; ++i)
  {
	objectIDArrayBuffer.Add(format->GetObjectIDArray().Item(i));
  }

  return TRUE;
}

bool fxIDDataObject::SetData(const wxDataFormat &Format,size_t Length,const void *Buffer)
{
  //find requested format
  fxIDDataObjectFormat *format = pvFindFormat(Format);

  //make sure format was found
  if(format == (fxIDDataObjectFormat*)NULL)
  {
    return 0;
  }

  //cast buffer to wxArrayString
  wxArrayString &objectIDArrayBuffer = *(wxArrayString*)Buffer;

  //get # of strings in wxArrayString buffer
  size_t idCt = objectIDArrayBuffer.GetCount();

  //copy all strings from wxArrayString buffer to target wxArrayString
  for(size_t i = 0; i < idCt; ++i)
  {
	format->GetObjectIDArray().Add(objectIDArrayBuffer.Item(i));
  }

  return TRUE;
}
Right now I'm getting access violation errors when I try to use the wxArrayString reference after casting the buffer from void*. In particular, the line of code in GetDataHere where a string is added to the wxArrayString buffer. I'm assuming it would happen in the line of code in SetData where a string is added to the target wxArrayString, but I haven't gotten that far due to the previous error.

I think I already mentioned this, but I never had to deal with void pointers let alone any pointers at all in Java. I feel confident enough in dealing with pointers, but as for the void pointers I just looked at the dnd sample and replicated the steps taken to cast it to what I want. Whethere that is or isn't my error, could someone please help me out?

Thanks, Bryan
User avatar
tierra
Site Admin
Site Admin
Posts: 1355
Joined: Sun Aug 29, 2004 7:14 pm
Location: Salt Lake City, Utah, USA
Contact:

Post by tierra »

Alright... I didn't want to mention my method before because the way I got it working is _not_ the way it should be done, and does have one drawback, however, you may be stuck in my same situation (I don't know for sure since I wasn't ever dealing with wxArrayString).

The issue comes up when your wxDataObject can be of variable size. The sample shows a single "shape" object being tossed on the clipboard, but never covers when you want to put more than one "shape" on the clipboard. You can only set one wxDataObject on the clipboard at any one time, thus if you want more than one shape on there, you need the wxDataObject container to carry multiple shapes. I think the problem arises with any objects allocated on the heap (which is the case with any variable length object, ie: vector, wxArrayString I think encloses vector in STL builds), either way, it stops you from being able to really copy anything into the void pointer other than the object containing all information about where your multiple objects are.

With that in mind, my implementation never makes a full copy. I enclose my wxDataObject around STL vector. GetDataSize() returns sizeof(vector<MyObject>). GetDataHere() copies the vector into the void pointer, and SetData() re-constitutes the vector (with it's containing pointer still pointing at the copied vector I never cleaned out on copy), and from there I make a copy of the vector and send it off to where ever it needs to go from there. I can still copy from one instance of the program to another, but, if the originating instance is closed, the data on the clipboard (the vector pointing to the objects created on the heap by the originating instance) becomes invalidated and I have memory leaks.

Now you see why I don't advocate that method. I'm guessing there's still yet another official way to do it, but I'm not any C++ memory management guru myself, and I don't know all the in and outs to it.

In your case, you could still get away with making all your strings into one wxString (with a special delimiter for splitting the string once you grab it back off the clipboard), and simply using wxStringDataObject which probably implements what I'm looking for, but I'm not going to look at the code. Then again, in using wxStringDataObject, users would be able to paste your info you put in there in any control accepting text type data (any text control, or any other program like notepad, etc...).
bjw0
Earned a small fee
Earned a small fee
Posts: 13
Joined: Mon Oct 25, 2004 5:10 pm

Post by bjw0 »

Thanks again for more help Tierra, but I'm still getting the same errors after trying the wxString suggestion. I removed wxArrayString and just threw all the strings into one wxString with a delimiter. I get the size of that string in GetDataSize and then cast the buffer in GetDataHere to wxString using the same technique as was done with wxArrayString or the dnd sample. Immediately following that, I try to start appending my strings to the buffer, but I'm still getting the same access violation error.

Can wxString not be used either? Maybe for the same reasons as wxArrayString?

You mentioned wxStringDataObject a couple times, is that a wxWidget class? I can't find anything about it.
User avatar
tierra
Site Admin
Site Admin
Posts: 1355
Joined: Sun Aug 29, 2004 7:14 pm
Location: Salt Lake City, Utah, USA
Contact:

Post by tierra »

Oh, sorry, I was in fact referring to a already programmed class for wxString. I had the name wrong though. It's wxTextDataObject.

http://www.wxwidgets.org/manuals/2.5.3/ ... dataobject
bjw0
Earned a small fee
Earned a small fee
Posts: 13
Joined: Mon Oct 25, 2004 5:10 pm

Post by bjw0 »

wxTextDataObject looks like what I need. I actually just got it working without it though. I had to go character by character though...

Code: Select all

size_t fxIDDataObject::GetDataSize(const wxDataFormat &Format) const
{
  // see if we have that format
  fxIDDataObjectFormat *format = pvFindFormat(Format);

  // check it
  if(format == (fxIDDataObjectFormat*)NULL)
  {
    // couldn't find the format
    return 0;
  }

  wxString objectIDs = wxEmptyString;
  size_t numObjects = format->GetObjectIDArray().GetCount();

  //build delimited string of ids
  for(size_t i = 0; i < numObjects; ++i)
  {
	objectIDs += format->GetObjectIDArray().Item(i) + "|";
  }

  //return size of delimited string of ids
  return (objectIDs.Length() * sizeof(char));
}

bool fxIDDataObject::GetDataHere(const wxDataFormat &Format,void *Buffer) const
{
  // see if we have that format
  fxIDDataObjectFormat *format = pvFindFormat(Format);

  // check it
  if(format == (fxIDDataObjectFormat*)NULL)
  {
    // couldn't find the format
    return FALSE;
  }

  char *chArray = (char*)Buffer;

  size_t numObjects = format->GetObjectIDArray().GetCount();
  size_t idLength = format->GetObjectIDArray().Item(0).Length();  //all IDs within a format are the same length
  size_t ct = 0;
  wxString id;

  //add each of the ids to the buffer
  for(size_t i = 0; i < numObjects; ++i)
  {
	id = format->GetObjectIDArray().Item(i);

	//add each character of the id to the buffer
	for(size_t j = 0; j < idLength; ++j)
	{
	  *(chArray + ct) = id.GetChar(j);
	  ++ct;
	}

	//add delimiter after the id
	*(chArray + ct) = '|';
	++ct;
  }

  return TRUE;
}

bool fxIDDataObject::SetData(const wxDataFormat &Format,size_t Length,const void *Buffer)
{
  // find the format
  fxIDDataObjectFormat *format = pvFindFormat(Format);

  // check it
  if(format == (fxIDDataObjectFormat*)NULL)
  {
    // not in our list
    return FALSE;
  }

  char *chArray = (char*)Buffer;
  wxString id = wxEmptyString;
  char ch;

  //add all ids in the buffer to the format's id array
  for(size_t i = 0; i < Length; ++i)
  {
	ch = *(chArray + i);

	//if not delimiter, then add to id string
	//else we have full id and add it to the id array
	if(ch != '|')
	{
	  id += *(chArray + i);
	}
	else
	{
	  format->GetObjectIDArray().Add(id);
	  id = wxEmptyString;
	}
  }

  return TRUE;
}
That code does work, but I think using wxTextDataObject would make things easier. So I'm going to try that instead.

Thanks again
User avatar
tierra
Site Admin
Site Admin
Posts: 1355
Joined: Sun Aug 29, 2004 7:14 pm
Location: Salt Lake City, Utah, USA
Contact:

Post by tierra »

wxTextDataObject probably has a lot more flexibility with Unicode, or other character encodings if changes like that are made in the future as well.
Post Reply