PDA

View Full Version : QAbstractItemModel::dropMimeData always passes -1,-1 for row and column for DnD



golgobot
2nd April 2014, 17:00
I am trying to build a TreeView where you can reparent and move items around by dragging and dropping rows within the same TreeView. My model is included below. Data is just a struct with parent, children, and a string.

I've set the properties of the TreeView correctly in mainwindow.cpp.

But no matter what, when I drag an item to reparent it, dropMimeData is always passes (-1, -1) for it's row and column arguments. If I don't reimplement dropMimeData, the insertRows and removeRows are called in that order, but insertRows is always passed the number of rows that that parent has children. No matter which item I drag and drop, that number always stays the same.

What I would like is to just know what index is being moved and to where it's being moved (parent/row) when I drag and drop an item internally. It looks like moveRows would be the function that would allow me to do this, but it never seems to be called by anyone.

My code is supplied below. Thanks in advance.


mainwindow.cpp


ui->treeView->setSelectionMode(QAbstractItemView::SingleSelectio n);
ui->treeView->setDragEnabled(true);
ui->treeView->setAcceptDrops(true);
ui->treeView->setDropIndicatorShown(true);
ui->treeView->setDragDropMode(QAbstractItemView::InternalMove);
ui->treeView->setModel(new model());


model.h


class model : public QAbstractItemModel
{
public:
model(QObject *parent = 0);
QVariant data(const QModelIndex &index, int role) const;
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole);
Qt::ItemFlags flags(const QModelIndex &index) const;
QVariant headerData(int selection, Qt::Orientation orientation, int role) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
QModelIndex parent(const QModelIndex &index) const;
bool hasChildren(const QModelIndex &parent = QModelIndex()) const;
Qt::DropActions supportedDropActions() const;
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex());
bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild);
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
private:
Data* item;
};


model.cpp


#include "model.h"

model::model(QObject *parent) :
QAbstractItemModel (parent)
{
item = new Data("parent");
for(int i = 0; i < 100; i++) {
QString s = QString("Row %1").arg(i);
Data* child = new Data(s);
child->parent = item;
item->children.append(child);
}
}

bool model::setData(const QModelIndex &index, const QVariant &value, int role) {
qDebug() << "setData" << value;
return false;
}

bool model::hasChildren(const QModelIndex &parent) const {
Data* d = static_cast<Data*>(parent.internalPointer());
if(!parent.isValid()) {
return true;
}
return d->children.size() > 0;
}

QVariant model::data(const QModelIndex &index, int role) const {
if (!index.isValid()) {
return QVariant();
}
if (role != Qt::DisplayRole) {
return QVariant();
}
if(index.column() == 0) {
return QVariant("Hello");
}
if(index.column() == 1) {
Data* d = static_cast<Data*>(index.internalPointer());
return d->data;
}
return QVariant();
}

QVariant model::headerData(int selection, Qt::Orientation orientation, int role) const {
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
if(selection == 0) {
return "";
}
else {
return "Decorator Type";
}
}
return QVariant();
}

int model::rowCount(const QModelIndex &parent) const {
if (!parent.isValid()) {
return 1;
}
Data* d = static_cast<Data*>(parent.internalPointer());
return d->children.size();
}

int model::columnCount(const QModelIndex& parent) const {
return 2;
}

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

Qt::ItemFlags model::flags(const QModelIndex &index) const {
Qt::ItemFlags defaultFlags = QAbstractItemModel::flags(index);

if (index.isValid()) {
return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
}
else {
return Qt::ItemIsDropEnabled | Qt::ItemIsDragEnabled | defaultFlags;
}
}

QModelIndex model::index(int row, int column, const QModelIndex &parent) const {
if (!hasIndex(row, column, parent)) {
return QModelIndex();
}
if (!parent.isValid()) {
return createIndex(row, column, item);
}
Data* d = static_cast<Data*>(parent.internalPointer());
return createIndex(row, column, d->children[row]);
}

QModelIndex model::parent(const QModelIndex &index) const {
if (!index.isValid()) {
return QModelIndex();
}

Data* childItem = static_cast<Data*>(index.internalPointer());

if (childItem->parent == NULL) {
return QModelIndex();
}
int row = childItem->parent->children.indexOf(childItem);

return createIndex(row, 0, childItem->parent);
}

bool model::dropMimeData(const QMimeData *d, Qt::DropAction action, int row, int column, const QModelIndex &parent) {
qDebug() << "dropMimeData: " << row << " " << column;
}

bool model::removeRows(int row, int count, const QModelIndex &parent) {
qDebug() << "Remove Rows " << row;
return false;
}

bool model::insertRows(int row, int count, const QModelIndex &parent) {
qDebug() << "Insert Rows " << row;
return true;
}

bool model::moveRows(const QModelIndex& sourceParent, int sourceRow, int count, const QModelIndex& destinationParent, int destinationChild) {
qDebug() << "sourceRow: " << sourceRow << ", count: " << count << " destinationChild: " << destinationChild;
return true;
}



Added after 58 minutes:

Ok I figured out what is going on.

When I drop I have to be careful that I drop between items, and not on an item.

Now i have a new problem. insertRows gets called first, so I don't know what rows are being moved, so I have to insert some kind of placeholder Data*. Then removeRows gets called and I'm trying to swap the row that gets removed with the fake one that was inserted and then remove the actual row.

This seems completely convoluted.

The second issue is that, for some reason, insertRows gets called twice for every one drag and drop:

Insert Rows 15
Insert Rows 16
Remove Rows 10

I'm not sure what to do with this.

I just want to be able to reparent items by dragging them around, but this framework is making that very difficult. Thanks.