Drag Drop problem with model views
Hi Guys
I've got a QTreeView that I've setup so that I can drag and drop a node within the tree.
That included reimplementing supportedDropActions(), mimeTypes(), mimeData(const QModelIndexList &indexes), bool dropMimeData(...) and removeRows().
Most of it works fine:
When I start the drag, mimeData() is called.
When I drop the node, dropMimeData(...) gets called - It creates a new node from the mimeData and insert the node into the data structure (between beginInsertRows() and endInsertRows() ).
It then automatically calls removeRows() which removes the original node.
But there are some occasions where I need to reject the drop. If I simply return false in dropMimeData,
the dragged node returns to its original position but then removeRows() is still called and the node gets removed.
If I set up a flag so that the node isn't removed within removeRows(), it leaves the row there but makes it invisible.
Does anyone know how to fix this?
Thanks
Jeff
Re: Drag Drop problem with model views
post the relevant code.
Specially your dropMimeData().
Re: Drag Drop problem with model views
The model (JB_NodeModel) uses a helper class (dragDropHelper) that handles the various drag drop methods e.g. dropMimeData.
Code:
bool JB_NodeModel
::dropMimeData(const QMimeData *data,
Qt::DropAction action,
int row,
int column,
{
if (dragDropHelper)
return dragDropHelper->dropMimeData(data,
action,
row,
column,
parent,
this);
else
action,
row,
column,
parent);
}
Code:
bool JB_NMHP_DD_KwGrpsWidget
::dropMimeData(const QMimeData *data,
Qt::DropAction action,
int row,
int column,
JB_NodeModel *aNodeModel)
{
Q_CHECK_PTR(aNodeModel);
bool handleDrop = true;
JB_Node* movedNode = 0;
if (action == Qt::IgnoreAction)
handleDrop = true;
if (!data->hasFormat("text/plain"))
handleDrop = false;
else if (column > 0)
handleDrop = false;
else
{
JB_Node* aParentNode = 0;
if (modelWrapper->getJBNodeModel() == aNodeModel)
{
aParentNode = aNodeModel->nodeFromIndex(parent);
Q_CHECK_PTR(aParentNode);
}
else
JB_Utilities::getJBUtilities()->debugMB("ERROR: The Drag&Drop modelWrapper and nodeModel do not match", 0);
int beginRow;
if (row >= 0)
beginRow = row;
else
beginRow = 0;
//beginRow = aParentNode->children.count();
QString aFirstNodePrimaryTableName;
QHash<QString, QVariant> aFirstNodeFieldValues;
QStringList nodesMimeDataTextList
= JB_Utilities
::getJBUtilities()->getTextListFromMimeData
(data
);
QString firstNodeText
= nodesMimeDataTextList.
value(0);
JB_Utilities::getJBUtilities()->getInfoFromMIMEDataText(firstNodeText,
aFirstNodePrimaryTableName,
aFirstNodeLineage,
aFirstNodeRowIDFieldName,
aFirstNodeFieldValues);
if (aFirstNodePrimaryTableName == "KeywordGroup")
{
if (nodesMimeDataTextList.size() > 1)
JB_Utilities::getJBUtilities()->debugMB("ERROR: The Drag&Drop is trying to move more than one Keyword Group", 0);
else
{
movedNode = aNodeModel->getNodeForNodeLineage(aFirstNodeLineage);
Q_CHECK_PTR(movedNode);
if (movedNode->parent == aParentNode)
handleDrop = false;
else
{
handleDrop = true;
JB_Node* newFromMovedNode = new JB_Node(*movedNode);
newFromMovedNode->children = movedNode->children;
for (int i = 0; i < newFromMovedNode->children.count(); i++)
newFromMovedNode->children.value(i)->parent = newFromMovedNode;
int parentRowID = aParentNode->getRowID();
int unhashedParentRowID = JB_Utilities::getJBUtilities()->getUnHashedTreeLevelID(parentRowID);
QString kwGrpParentIDFieldName
= "kwGrp_KwGrpParentID";
if (newFromMovedNode->getMappedDBFieldNames().contains(kwGrpParentIDFieldName))
{
newFromMovedNode
->setData
("kwGrp_KwGrpParentID",
QVariant(unhashedParentRowID
));
newFromMovedNode->setDragDropStatus(JB_DRAGDROP);
aNodeModel->insertNode(aParentNode, beginRow, newFromMovedNode, false, true);
}
else
JB_Utilities::getJBUtilities()->debugMB("ERROR: newFromMovedNode should have a kwGrp_KwGrpParentID field", 0);
}
}
}
}
if (handleDrop == false && movedNode)
movedNode->setDragDropStatus(JB_CANCEL_DRAGDROP);
return handleDrop;
}
Code:
Qt::DropActions JB_NMHP_DD_KwGrpsWidget::supportedDropActions() const
{
return Qt::MoveAction;
}
Re: Drag Drop problem with model views
can you show your removeRows() as well?
Re: Drag Drop problem with model views
Sure.
This has actually changed a little. I wanted to stop a child node being dropped on it's parent - this was problematic with the underlying data structure. In the mean time I have changed the underlying structure so that it allows a child to be dropped on the parent.
BUT...
I still need to know why it didn't allow me to stop the row being removed when I cancelled the drop i.e. returned false in dropMimeData() as I will need to be able to do that shortly in another situation.
Anyway, here's the implementation for removeRows().
Thanks
Jeff
Code:
bool JB_NodeModel
::removeRows(int row,
int count,
const QModelIndex &parent
) {
bool returnVal = false;
for (int i = 0; i < count; i++)
{
int rowNum = row + i;
returnVal = removeRow(rowNum, parent);
}
return returnVal;
}
Code:
// NOTE: converting hashed tree IDs to normal DB IDs is handled in JB_DBTGeneric table
bool JB_NodeModel
::removeRow(int row,
const QModelIndex &parent
) {
// The first table in the tablesUsedList is the one that rows can be inserted into or deleted from
bool success = false;
JB_Node* parentNode = rootNode;
if (parent.isValid())
parentNode = nodeFromIndex(parent);
Q_CHECK_PTR(parentNode);
int numChildren = parentNode->children.count();
JB_Node* removedNode = parentNode->children.takeAt(row);
QString oldLineage
= removedNode
->getNodeRowIDLineage
();
int firstRow = row;
int lastRow = row;
if (nodesHashTable->contains(oldLineage))
{
beginRemoveRows(parent, firstRow, lastRow);
if (removedNode->getDragDropStatus() == JB_DRAGDROP)
{
int numberRemoved = nodesHashTable->removeFromLineageToNodesHash(oldLineage);
if (numberRemoved == 1)
success = true;
}
else // if it == JB_NO_DRAGDROP
{
// Don't actually delete anything - just set it to not Active. By setting IsDeleted to true, the model updates the row in the DB (but doesn't delete it at this point ?)
removedNode->parent = 0;
removedNode->setIsDeleted(true);
int primaryTableNameEnum = tablesUsedList.value(0);
QString isActiveFieldName
= JB_DBTableFields
::getTC()->getIsActiveFieldNameForTableEnum
(primaryTableNameEnum
);
removedNode
->setData
(isActiveFieldName,
QVariant(false));
success = true;
}
endRemoveRows();
emit removingRow();
}
bool equalHashes = nodesHashTable->isHashSizesEqual();
Q_ASSERT(equalHashes == true);
removedNode->setDragDropStatus(JB_NO_DRAGDROP);
return success;
}
Added after 4 minutes:
Just found this link - looks like others have had problems with it too.
https://bugreports.qt-project.org/browse/QTBUG-6679
I'm using Mac, Lion OS 10.7.3
Added after 45 minutes:
I just changed the ex-treemodel example to return false from dropMimeData() and after running it and dragging and dropping a node, it still called deleteRows() on the dragged node and deleted it.
So there is clearly a bug here.
I commented out the code within removeRows and ran it again and it didn't delete the row or make it invisible (which is what happened and I put a flag into removeRows() to stop it from deleting the row on a cancelled drop) so I'm not sure why that happened but at least with a bit of fiddling there appears to be a workaround.
Still it's a pretty decent bug that has been around for a couple of years according to the link above.
Any other ideas are welcome.
Jeff