Drag and drop

This commit is contained in:
Jakub Melka 2021-07-11 17:17:13 +02:00
parent ee587d0809
commit 8442143efc
4 changed files with 190 additions and 3 deletions

View File

@ -271,7 +271,10 @@ bool MainWindow::canPerformOperation(Operation operation) const
return isSelected;
case Operation::Paste:
return !QApplication::clipboard()->text().isEmpty();
{
const QMimeData* mimeData = QApplication::clipboard()->mimeData();
return mimeData && mimeData->hasFormat(PageItemModel::getMimeDataType());
}
case Operation::RotateLeft:
case Operation::RotateRight:

View File

@ -21,7 +21,7 @@
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::InternalMove</enum>
<enum>QAbstractItemView::DragDrop</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::MoveAction</enum>
@ -167,7 +167,7 @@
<string>Remove Selection</string>
</property>
<property name="shortcut">
<string>Ctrl+R</string>
<string>Del</string>
</property>
</action>
<action name="actionRestoreRemovedItems">

View File

@ -375,5 +375,177 @@ std::set<int> PageGroupItem::getDocumentIndices() const
return result;
}
QStringList PageItemModel::mimeTypes() const
{
return { getMimeDataType() };
}
QMimeData* PageItemModel::mimeData(const QModelIndexList& indexes) const
{
if (indexes.isEmpty())
{
return nullptr;
}
QMimeData* mimeData = new QMimeData;
QByteArray serializedData;
{
QDataStream stream(&serializedData, QIODevice::WriteOnly);
for (const QModelIndex& index : indexes)
{
stream << index.row();
}
}
mimeData->setData(getMimeDataType(), serializedData);
return mimeData;
}
bool PageItemModel::canDropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) const
{
Q_UNUSED(row);
Q_UNUSED(column);
Q_UNUSED(parent);
switch (action)
{
case Qt::CopyAction:
case Qt::MoveAction:
case Qt::IgnoreAction:
break;
case Qt::LinkAction:
case Qt::TargetMoveAction:
return false;
default:
Q_ASSERT(false);
break;
}
return data && data->hasFormat(getMimeDataType());
}
bool PageItemModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent)
{
if (action == Qt::IgnoreAction)
{
return true;
}
if (!data->hasFormat(getMimeDataType()))
{
return false;
}
if (column > 0)
{
return false;
}
int insertRow = rowCount(QModelIndex());
if (row > -1)
{
insertRow = row;
}
else if (parent.isValid())
{
insertRow = parent.row();
}
std::vector<int> rows;
QByteArray serializedData = data->data(getMimeDataType());
QDataStream stream(&serializedData, QIODevice::ReadOnly);
while (!stream.atEnd())
{
int row = -1;
stream >> row;
rows.push_back(row);
}
qSort(rows);
// Sanity checks on rows
if (rows.empty())
{
return false;
}
if (rows.front() < 0 || rows.back() >= rowCount(QModelIndex()))
{
return false;
}
std::vector<PageGroupItem> newItems = m_pageGroupItems;
std::vector<PageGroupItem> workItems;
// When moving, update insert row
if (action == Qt::MoveAction)
{
const int originalInsertRow = insertRow;
if (rows.front() < originalInsertRow)
{
++insertRow;
}
for (int currentRow : rows)
{
if (currentRow < originalInsertRow)
{
--insertRow;
}
}
}
workItems.reserve(rows.size());
for (int currentRow : rows)
{
workItems.push_back(m_pageGroupItems[currentRow]);
}
// When move, delete old page items
if (action == Qt::MoveAction)
{
for (auto it = rows.rbegin(); it != rows.rend(); ++it)
{
newItems.erase(std::next(newItems.begin(), *it));
}
}
// Insert work items at a given position
newItems.insert(std::next(newItems.begin(), insertRow), workItems.begin(), workItems.end());
if (newItems != m_pageGroupItems)
{
beginResetModel();
m_pageGroupItems = std::move(newItems);
endResetModel();
}
return true;
}
Qt::DropActions PageItemModel::supportedDropActions() const
{
return Qt::CopyAction | Qt::MoveAction;
}
Qt::DropActions PageItemModel::supportedDragActions() const
{
return Qt::CopyAction | Qt::MoveAction;
}
Qt::ItemFlags PageItemModel::flags(const QModelIndex& index) const
{
Qt::ItemFlags flags = BaseClass::flags(index);
if (index.isValid())
{
flags |= Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
}
return flags;
}
} // namespace pdfdocpage

View File

@ -60,6 +60,9 @@ class PageItemModel : public QAbstractItemModel
{
Q_OBJECT
private:
using BaseClass = QAbstractItemModel;
public:
explicit PageItemModel(QObject* parent);
@ -69,6 +72,13 @@ public:
virtual int rowCount(const QModelIndex& parent) const override;
virtual int columnCount(const QModelIndex& parent) const override;
virtual QVariant data(const QModelIndex& index, int role) const override;
virtual QStringList mimeTypes() const override;
virtual QMimeData* mimeData(const QModelIndexList& indexes) const override;
virtual bool canDropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) const override;
virtual bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) override;
virtual Qt::DropActions supportedDropActions() const override;
virtual Qt::DropActions supportedDragActions() const override;
virtual Qt::ItemFlags flags(const QModelIndex& index) const override;
/// Adds document to the model, inserts one single page group containing
/// the whole document. Returns index of a newly added document. If document
@ -102,6 +112,8 @@ public:
void group(const QModelIndexList& list);
void ungroup(const QModelIndexList& list);
static QString getMimeDataType() { return QLatin1String("application/pagemodel.PDF4QtDocPageOrganizer"); }
private:
void createDocumentGroup(int index);
QString getGroupNameFromDocument(int index) const;