When items of data are exported from a model in a drag and drop operation, they are encoded into an appropriate format corresponding to one or more MIME types. Models declare the MIME types that they can use to supply items by reimplementing the QAbstractItemModel::mimeTypes() function, returning a list of standard MIME types.
For example, a model that only provides plain text would provide the following implementation:
The model must also provide code to encode data in the advertised format. This is achieved by reimplementing the QAbstractItemModel::mimeData() function to provide a QMimeData object, just as in any other drag and drop operation.
The following code shows how each item of data, corresponding to a given list of indexes, is encoded as plain text and stored in a QMimeData object.
Since a list of model indexes is supplied to the function, this approach is general enough to be used in both hierarchical and non-heirarchical models.
Inserting Dropped Data into a Model
The way that any given model handles dropped data depends on both its type (list, table, or tree) and the way its contents is likely to be presented to the user. Generally, the approach taken to accommodate dropped data should be the one that most suits the model's underlying data store.
Different types of model tend to handle dropped data in different ways. List and table models only provide a flat structure in which items of data are stored. As a result, they may insert new rows (and columns) when data is dropped on an existing item in a view, or they may overwrite the item's contents in the model using some of the data supplied. Tree models are often able to add child items containing new data to their underlying data stores, and will therefore behave more predictably as far as the user is concerned.
Dropped data is handled by a model's reimplementation of QAbstractItemModel::dropMimeData(). For example, a model that handles a simple list of strings can provide an implementation that handles data dropped onto existing items separately to data dropped into the top level of the model (i.e., onto an invalid item).
The model first has to make sure that the operation should be acted on, the data supplied is in a format that can be used, and that its destination within the model is valid:
bool DragDropListModel::dropMimeData(const QMimeData *data,
Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
if (action == Qt::IgnoreAction)
return true;
if (!data->hasFormat("application/vnd.text.list"))
return false;
if (column > 0)
return false;
A simple one column string list model can indicate failure if the data supplied is not plain text, or if the column number given for the drop is invalid.
The data to be inserted into the model is treated differently depending on whether it is dropped onto an existing item or not. In this simple example, we want to allow drops between existing items, before the first item in the list, and after the last item.
When a drop occurs, the model index corresponding to the parent item will either be valid, indicating that the drop occurred on an item, or it will be invalid, indicating that the drop occurred somewhere in the view that corresponds to top level of the model.
int beginRow;
if (row != -1)
beginRow = row;
We initially examine the row number supplied to see if we can use it to insert items into the model, regardless of whether the parent index is valid or not.
else if (parent.isValid())
beginRow = parent.row();
If the parent model index is valid, the drop occurred on an item. In this simple list model, we find out the row number of the item and use that value to insert dropped items into the top level of the model.
else
beginRow = rowCount(QModelIndex());
When a drop occurs elsewhere in the view, and the row number is unusable, we append items to the top level of the model.
In hierarchical models, when a drop occurs on an item, it would be better to insert new items into the model as children of that item. In the simple example shown here, the model only has one level, so this approach is not appropriate.
Decoding Imported Data
Each implementation of dropMimeData() must also decode the data and insert it into the model's underlying data structure.
For a simple string list model, the encoded items can be decoded and streamed into a QStringList:
QByteArray encodedData = data->data("application/vnd.text.list");
QDataStream stream(&encodedData, QIODevice::ReadOnly);
QStringList newItems;
int rows = 0;
while (!stream.atEnd()) {
QString text;
stream >> text;
newItems << text;
++rows;
}
The strings can then be inserted into the underlying data store. For consistency, this can be done through the model's own interface:
insertRows(beginRow, rows, QModelIndex());
foreach (QString text, newItems) {
QModelIndex idx = index(beginRow, 0, QModelIndex());
setData(idx, text);
beginRow++;
}
return true;
}
Note that the model will typically need to provide implementations of the QAbstractItemModel::insertRows() and QAbstractItemModel::setData() functions.
See also Item Views Puzzle Example.
[Previous: Item View Convenience Classes]
[Contents]
[Next: Proxy Models]