PDA

View Full Version : QStandardItem::clone() not being called for Drag and Drop



AaronMK
17th August 2011, 18:15
I have a Qt application where I am using a QStandardItemModel derived class, and a QTreeView to interact with it. I would like to enable drag and drop to copy and move items around the model. To enable this, I have done the following:


In QStandardItem subclasses that represent leaf nodes: setDragEnabled(true) and override clone() to return a real copy of the item.
In QStandardItem subclasses that represent Folder nodes: setDropEnabled(true)
In QTreeView: setDragEnabled(true); setAcceptDrops(true); setDropIndicatorShown(true);


Drag and Drop works to the extent that it honors which items can be dragged and which can accept drops. However, whether moving or copying it does not create new items using my clone() functions. It only copies the settings and data available to the QStandardItem base class, losing subclass overrides and such.

How do I get the model and views to make use of my clone() functions, or work around this?

Thank you for any assistance.

high_flyer
18th August 2011, 14:02
It only copies the settings and data available to the QStandardItem base class, losing subclass overrides and such.
Show this part of your code.

AaronMK
19th August 2011, 17:29
Show this part of your code.

Ok, here is how I am initializing my TreeView in the contructor:



setModel(SceneModel::instance());
setContextMenuPolicy(Qt::CustomContextMenu);
setSelectionBehavior(QAbstractItemView::SelectRows );
setDragEnabled(true);
setAcceptDrops(true);
setDropIndicatorShown(true);
setHeaderHidden(true);

connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), SLOT(onContextRequested(const QPoint&)));


And the main functionality that I am losing. This code calls a context handler for my custom item classes. It gets run, but since the new items are not of the right type, it does nothing with the actual items.



void SceneTree::onContextRequested(const QPoint& pos)
{
QModelIndex ItemIndex(indexAt(pos));

if (false == ItemIndex.isValid())
return;

QStandardItem *Item = SceneModel::instance()->itemFromIndex(ItemIndex);

if ( SceneModel::SCN_OBJ_REF == Item->type() )
{
SceneObjectRef* scnItem = dynamic_cast<SceneObjectRef*>(Item);
scnItem->getSceneObject()->onContextRequested( mapToGlobal(pos) );
}
}

Here is code from my subclass of QStandardItem which populates a lot of the model, including overrides and copy constructor. (SceneItem is an empty QStandardItem subclass, a placeholder for other common item functionality to come later. SceneObj is an object not directly part of the data model, but only referenced by the model.)



SceneObjectRef::SceneObjectRef(const SceneObjectRef &Other)
: QObject(), SceneItem(), scnObject(Other.scnObject)
{
init();
}


int SceneObjectRef::type() const
{
return SceneModel::SCN_OBJ_REF;
}

void SceneObjectRef::setData(const QVariant &value, int role)
{
QStandardItem::setData(value, role);

if ( Qt::EditRole == role && value.toString() != scnObject->objectName())
scnObject->setName(value.toString());
}

// This is never getting called.
QStandardItem* SceneObjectRef::clone() const
{
SceneObjectRef* clonedItem = new SceneObjectRef(*this);

for ( int i = 0; i < rowCount(); i++)
clonedItem->appendRow( child(i)->clone() );

return clonedItem;
}


void SceneObjectRef::init()
{
setDragEnabled(true);
setDropEnabled(false);
setText(scnObject->objectName());

connect(scnObject.data(), SIGNAL(NameChanged(const QString&)), SLOT(onNameChanged(const QString&)));
}

void SceneObjectRef::onNameChanged(const QString &Name)
{
if ( text() != Name)
setText(Name);
}

So it is really the name synching and context handling that I am losing.

high_flyer
20th August 2011, 00:42
Your post is a bit too much for me at this hour...
However in your original post you write:

It only copies the settings and data available to the QStandardItem base class, losing subclass overrides and such.
And in your code you use QStandardItem so no wonder that its all you get.
Also you say this should happen during drag and drop, but I see nothing that has to do with drag and drop in your code...

It would help if you could "distill" the problem in to one line of code - which line would be it be from all you have posted, and why?


And the main functionality that I am losing. This code calls a context handler for my custom item classes. It gets run, but since the new items are not of the right type, it does nothing with the actual items.

But in your code:

int SceneObjectRef::type() const
{
return SceneModel::SCN_OBJ_REF;
}

does returns the correct type... so why do you think it should not be the correct type?

Oh, one more thing:

It only copies the settings and data available to the QStandardItem base class, losing subclass overrides and such.
You didn't forget 'virtual' did you?

AaronMK
24th August 2011, 23:33
Thank you for the feedback. (Sorry for the delay in response)

You are correct, type() is returning the correct type, and if I manually insert the SceneObjectRef objects into the model, they function as expected, but copies the model automatically makes it never enters my override, which is marked virtual. What I really suspect is happening is that it is using a default QStandardItem prototype for the drag copy/move operations, not clone(). (If I understand the documentation correctly, this is controlled by QStandardItemModel::setItemPrototype(), but I don't know what single protoype I would use to get polymorphic functionality.)

I was able to get around this by deriving from the class defined in the attached files, but it is a bit round-about. I ended up encoding pointers to the model items in mimedata, decoding on drop, and calling clone. While the code does not specifically handle move, it seems like I am getting that for "free".

If there is a more proper way of achieving this, I really am curious (especially if this will have side effects), but this is working for now.