PDA

View Full Version : Drag Drop problem with model views



Jeffb
9th March 2012, 07:24
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

high_flyer
9th March 2012, 11:52
post the relevant code.
Specially your dropMimeData().

Jeffb
10th March 2012, 01:15
The model (JB_NodeModel) uses a helper class (dragDropHelper) that handles the various drag drop methods e.g. dropMimeData.



bool JB_NodeModel::dropMimeData(const QMimeData *data,
Qt::DropAction action,
int row,
int column,
const QModelIndex &parent)
{
if (dragDropHelper)
return dragDropHelper->dropMimeData(data,
action,
row,
column,
parent,
this);
else
return QAbstractItemModel::dropMimeData(data,
action,
row,
column,
parent);
}


bool JB_NMHP_DD_KwGrpsWidget::dropMimeData(const QMimeData *data,
Qt::DropAction action,
int row,
int column,
const QModelIndex &parent,
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;
QString aFirstNodeLineage;
QString aFirstNodeRowIDFieldName;
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(kwGrpParentIDFiel dName))
{
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;
}


Qt::DropActions JB_NMHP_DD_KwGrpsWidget::supportedDropActions() const
{
return Qt::MoveAction;
}

high_flyer
10th March 2012, 13:13
can you show your removeRows() as well?

Jeffb
11th March 2012, 05:07
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


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;
}


// 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(primaryTableNameE num);
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