diff --git a/src/storemodel.cpp b/src/storemodel.cpp index 1a7c334..340f596 100644 --- a/src/storemodel.cpp +++ b/src/storemodel.cpp @@ -1,276 +1,277 @@ /* SPDX-FileCopyrightText: 2014-2023 Anne Jan Brouwer SPDX-FileCopyrightText: 2018 Claudio Maradonna SPDX-FileCopyrightText: 2019 Maciej S. Szmigiero SPDX-FileCopyrightText: 2023 g10 Code GmbH SPDX-FileContributor: Sune Stolborg Vuorela SPDX-License-Identifier: GPL-3.0-or-later */ #include "storemodel.h" #include "pass.h" #include "util.h" #include #include #include #include #include #include #include static const QString mimeType = QStringLiteral("application/vnd+gnupgpass.dragAndDropInfoPasswordStore"); QDataStream &operator<<(QDataStream &out, const dragAndDropInfoPasswordStore &dragAndDropInfoPasswordStore) { out << dragAndDropInfoPasswordStore.isDir << dragAndDropInfoPasswordStore.isFile << dragAndDropInfoPasswordStore.path; return out; } QDataStream &operator>>(QDataStream &in, dragAndDropInfoPasswordStore &dragAndDropInfoPasswordStore) { in >> dragAndDropInfoPasswordStore.isDir >> dragAndDropInfoPasswordStore.isFile >> dragAndDropInfoPasswordStore.path; return in; } /** * @brief StoreModel::StoreModel * SubClass of QSortFilterProxyModel via * http://www.qtcentre.org/threads/46471-QTreeView-Filter */ StoreModel::StoreModel(Pass &pass) : m_pass(pass) { setRecursiveFilteringEnabled(true); + setAutoAcceptChildRows(true); } /** * @brief StoreModel::data don't show the .gpg at the end of a file. * @param index * @param role * @return */ QVariant StoreModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); auto initial_value = QSortFilterProxyModel::data(index, role); if (role == Qt::DisplayRole) { QString name = initial_value.toString(); name.replace(Util::endsWithGpg(), QString{}); return name; } return initial_value; } /** * @brief StoreModel::supportedDropActions enable drop. * @return */ Qt::DropActions StoreModel::supportedDropActions() const { return Qt::CopyAction | Qt::MoveAction; } /** * @brief StoreModel::supportedDragActions enable drag. * @return */ Qt::DropActions StoreModel::supportedDragActions() const { return Qt::CopyAction | Qt::MoveAction; } /** * @brief StoreModel::flags * @param index * @return */ Qt::ItemFlags StoreModel::flags(const QModelIndex &index) const { Qt::ItemFlags defaultFlags = QSortFilterProxyModel::flags(index); if (index.isValid()) { return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags; } return Qt::ItemIsDropEnabled | defaultFlags; } /** * @brief StoreModel::mimeTypes * @return */ QStringList StoreModel::mimeTypes() const { QStringList types; types << mimeType; return types; } /** * @brief StoreModel::mimeData * @param indexes * @return */ QMimeData *StoreModel::mimeData(const QModelIndexList &indexes) const { dragAndDropInfoPasswordStore info; QByteArray encodedData; // only use the first, otherwise we should enable multiselection QModelIndex index = indexes.at(0); if (index.isValid()) { QModelIndex useIndex = mapToSource(index); info.isDir = fs()->fileInfo(useIndex).isDir(); info.isFile = fs()->fileInfo(useIndex).isFile(); info.path = fs()->fileInfo(useIndex).absoluteFilePath(); QDataStream stream(&encodedData, QIODevice::WriteOnly); stream << info; } auto *mimeData = new QMimeData(); mimeData->setData(mimeType, encodedData); return mimeData; } /** * @brief StoreModel::canDropMimeData * @param data * @param action * @param row * @param column * @param parent * @return */ bool StoreModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const { #ifdef QT_DEBUG qDebug() << action << row; #else Q_UNUSED(action) Q_UNUSED(row) #endif if (!fs()) { return false; } QModelIndex useIndex = this->index(parent.row(), parent.column(), parent.parent()); QByteArray encodedData = data->data(mimeType); QDataStream stream(&encodedData, QIODevice::ReadOnly); dragAndDropInfoPasswordStore info; stream >> info; if (!data->hasFormat(mimeType)) return false; if (column > 0) { return false; } // you can drop a folder on a folder if (fs()->fileInfo(mapToSource(useIndex)).isDir() && info.isDir) { return true; } // you can drop a file on a folder if (fs()->fileInfo(mapToSource(useIndex)).isDir() && info.isFile) { return true; } // you can drop a file on a file if (fs()->fileInfo(mapToSource(useIndex)).isFile() && info.isFile) { return true; } return false; } /** * @brief StoreModel::dropMimeData * @param data * @param action * @param row * @param column * @param parent * @return */ bool StoreModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { if (!canDropMimeData(data, action, row, column, parent)) return false; if (action == Qt::IgnoreAction) { return true; } QByteArray encodedData = data->data(mimeType); QDataStream stream(&encodedData, QIODevice::ReadOnly); dragAndDropInfoPasswordStore info; stream >> info; QModelIndex destIndex = this->index(parent.row(), parent.column(), parent.parent()); QFileInfo destFileinfo = fs()->fileInfo(mapToSource(destIndex)); QFileInfo srcFileInfo = QFileInfo(info.path); QString cleanedSrc = QDir::cleanPath(srcFileInfo.absoluteFilePath()); QString cleanedDest = QDir::cleanPath(destFileinfo.absoluteFilePath()); if (info.isDir) { // dropped dir onto dir if (destFileinfo.isDir()) { QDir destDir = QDir(cleanedDest).filePath(srcFileInfo.fileName()); QString cleanedDestDir = QDir::cleanPath(destDir.absolutePath()); if (action == Qt::MoveAction) { m_pass.Move(cleanedSrc, cleanedDestDir); } else if (action == Qt::CopyAction) { m_pass.Copy(cleanedSrc, cleanedDestDir); } } } else if (info.isFile) { // dropped file onto a directory if (destFileinfo.isDir()) { if (action == Qt::MoveAction) { m_pass.Move(cleanedSrc, cleanedDest); } else if (action == Qt::CopyAction) { m_pass.Copy(cleanedSrc, cleanedDest); } } else if (destFileinfo.isFile()) { // dropped file onto a file int answer = QMessageBox::question(nullptr, i18n("force overwrite?"), i18nc("overwrite DestinationFile with SourceFile","overwrite %1 with %2?", cleanedDest, cleanedSrc), QMessageBox::Yes | QMessageBox::No); bool force = answer == QMessageBox::Yes; if (action == Qt::MoveAction) { m_pass.Move(cleanedSrc, cleanedDest, force); } else if (action == Qt::CopyAction) { m_pass.Copy(cleanedSrc, cleanedDest, force); } } } return true; } /** * @brief StoreModel::lessThan * @param source_left * @param source_right * @return */ bool StoreModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const { /* matches logic in QFileSystemModelSorter::compareNodes() */ #ifndef Q_OS_MAC if (fs() && (source_left.column() == 0 || source_left.column() == 1)) { bool leftD = fs()->isDir(source_left); bool rightD = fs()->isDir(source_right); if (leftD ^ rightD) return leftD; } #endif return QSortFilterProxyModel::lessThan(source_left, source_right); } QFileSystemModel* StoreModel::fs() const { return static_cast(sourceModel()); }