Line data Source code
1 : // SPDX-FileCopyrightText: 2016 Anne Jan Brouwer
2 : // SPDX-License-Identifier: GPL-3.0-or-later
3 : #include "storemodel.h"
4 : #include "qtpasssettings.h"
5 :
6 : #include "util.h"
7 : #include <QDebug>
8 : #include <QFileSystemModel>
9 : #include <QMessageBox>
10 : #include <QMimeData>
11 : #include <QRegularExpression>
12 : #include <utility>
13 :
14 0 : auto operator<<(
15 : QDataStream &out,
16 : const dragAndDropInfoPasswordStore &dragAndDropInfoPasswordStore)
17 : -> QDataStream & {
18 0 : out << dragAndDropInfoPasswordStore.isDir
19 0 : << dragAndDropInfoPasswordStore.isFile
20 0 : << dragAndDropInfoPasswordStore.path;
21 0 : return out;
22 : }
23 :
24 0 : auto operator>>(QDataStream &in,
25 : dragAndDropInfoPasswordStore &dragAndDropInfoPasswordStore)
26 : -> QDataStream & {
27 0 : in >> dragAndDropInfoPasswordStore.isDir >>
28 0 : dragAndDropInfoPasswordStore.isFile >> dragAndDropInfoPasswordStore.path;
29 0 : return in;
30 : }
31 :
32 : /**
33 : * @brief StoreModel::StoreModel
34 : * SubClass of QSortFilterProxyModel via
35 : * http://www.qtcentre.org/threads/46471-QTreeView-Filter
36 : */
37 265 : StoreModel::StoreModel() { fs = nullptr; }
38 :
39 : /**
40 : * @brief StoreModel::filterAcceptsRow should row be shown, wrapper for
41 : * StoreModel::ShowThis method.
42 : * @param sourceRow
43 : * @param sourceParent
44 : * @return
45 : */
46 48 : auto StoreModel::filterAcceptsRow(int sourceRow,
47 : const QModelIndex &sourceParent) const
48 : -> bool {
49 48 : QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
50 48 : return ShowThis(index);
51 : }
52 :
53 : /**
54 : * @brief StoreModel::ShowThis should a row be shown, based on our search
55 : * criteria.
56 : * @param index
57 : * @return
58 : */
59 54 : auto StoreModel::ShowThis(const QModelIndex index) const -> bool {
60 : bool retVal = false;
61 54 : if (fs == nullptr) {
62 : return retVal;
63 : }
64 : // Gives you the info for number of childs with a parent
65 54 : if (sourceModel()->rowCount(index) > 0) {
66 12 : for (int nChild = 0; nChild < sourceModel()->rowCount(index); ++nChild) {
67 6 : QModelIndex childIndex = sourceModel()->index(nChild, 0, index);
68 : if (!childIndex.isValid()) {
69 : break;
70 : }
71 6 : retVal = ShowThis(childIndex);
72 6 : if (retVal) {
73 : break;
74 : }
75 : }
76 : } else {
77 48 : QModelIndex useIndex = sourceModel()->index(index.row(), 0, index.parent());
78 48 : QString path = fs->filePath(useIndex);
79 96 : path = QDir(store).relativeFilePath(path);
80 48 : path.replace(Util::endsWithGpg(), "");
81 48 : retVal = path.contains(filterRegularExpression());
82 : }
83 : return retVal;
84 : }
85 :
86 : /**
87 : * @brief StoreModel::setModelAndStore update the source model and store.
88 : * @param sourceModel
89 : * @param passStore
90 : */
91 188 : void StoreModel::setModelAndStore(QFileSystemModel *sourceModel,
92 : QString passStore) {
93 188 : setSourceModel(sourceModel);
94 188 : fs = sourceModel;
95 : store = std::move(passStore);
96 188 : }
97 :
98 : /**
99 : * @brief StoreModel::data don't show the .gpg at the end of a file.
100 : * @param index
101 : * @param role
102 : * @return
103 : */
104 35 : auto StoreModel::data(const QModelIndex &index, int role) const -> QVariant {
105 : if (!index.isValid()) {
106 : return {};
107 : }
108 :
109 : QVariant initial_value;
110 35 : initial_value = QSortFilterProxyModel::data(index, role);
111 :
112 35 : if (role == Qt::DisplayRole) {
113 35 : QString name = initial_value.toString();
114 35 : name.replace(Util::endsWithGpg(), "");
115 35 : initial_value.setValue(name);
116 : }
117 :
118 : return initial_value;
119 35 : }
120 :
121 : /**
122 : * @brief StoreModel::supportedDropActions enable drop.
123 : * @return
124 : */
125 21 : auto StoreModel::supportedDropActions() const -> Qt::DropActions {
126 21 : return Qt::CopyAction | Qt::MoveAction;
127 : }
128 :
129 : /**
130 : * @brief StoreModel::supportedDragActions enable drag.
131 : * @return
132 : */
133 21 : auto StoreModel::supportedDragActions() const -> Qt::DropActions {
134 21 : return Qt::CopyAction | Qt::MoveAction;
135 : }
136 :
137 : /**
138 : * @brief StoreModel::flags
139 : * @param index
140 : * @return
141 : */
142 70 : auto StoreModel::flags(const QModelIndex &index) const -> Qt::ItemFlags {
143 70 : Qt::ItemFlags defaultFlags = QSortFilterProxyModel::flags(index);
144 :
145 : if (index.isValid()) {
146 : return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
147 : }
148 35 : return Qt::ItemIsDropEnabled | defaultFlags;
149 : }
150 :
151 : /**
152 : * @brief StoreModel::mimeTypes
153 : * @return
154 : */
155 35 : auto StoreModel::mimeTypes() const -> QStringList {
156 35 : QStringList types;
157 35 : types << "application/vnd+qtpass.dragAndDropInfoPasswordStore";
158 35 : return types;
159 : }
160 :
161 : /**
162 : * @brief StoreModel::mimeData
163 : * @param indexes
164 : * @return
165 : */
166 0 : auto StoreModel::mimeData(const QModelIndexList &indexes) const -> QMimeData * {
167 : dragAndDropInfoPasswordStore info;
168 :
169 0 : QByteArray encodedData;
170 : // only use the first, otherwise we should enable multiselection
171 0 : QModelIndex index = indexes.at(0);
172 : if (index.isValid()) {
173 0 : QModelIndex useIndex = mapToSource(index);
174 :
175 0 : info.isDir = fs->fileInfo(useIndex).isDir();
176 0 : info.isFile = fs->fileInfo(useIndex).isFile();
177 0 : info.path = fs->fileInfo(useIndex).absoluteFilePath();
178 0 : QDataStream stream(&encodedData, QIODevice::WriteOnly);
179 0 : stream << info;
180 0 : }
181 :
182 0 : auto *mimeData = new QMimeData();
183 0 : mimeData->setData("application/vnd+qtpass.dragAndDropInfoPasswordStore",
184 : encodedData);
185 0 : return mimeData;
186 : }
187 :
188 : /**
189 : * @brief StoreModel::canDropMimeData
190 : * @param data
191 : * @param action
192 : * @param row
193 : * @param column
194 : * @param parent
195 : * @return
196 : */
197 0 : auto StoreModel::canDropMimeData(const QMimeData *data, Qt::DropAction action,
198 : int row, int column,
199 : const QModelIndex &parent) const -> bool {
200 : #ifdef QT_DEBUG
201 : qDebug() << action << row;
202 : #else
203 : Q_UNUSED(action)
204 : Q_UNUSED(row)
205 : #endif
206 :
207 : QModelIndex useIndex =
208 0 : this->index(parent.row(), parent.column(), parent.parent());
209 : QByteArray encodedData =
210 0 : data->data("application/vnd+qtpass.dragAndDropInfoPasswordStore");
211 0 : QDataStream stream(&encodedData, QIODevice::ReadOnly);
212 : dragAndDropInfoPasswordStore info;
213 0 : stream >> info;
214 0 : if (!data->hasFormat("application/vnd+qtpass.dragAndDropInfoPasswordStore")) {
215 : return false;
216 : }
217 :
218 0 : if (column > 0) {
219 : return false;
220 : }
221 :
222 : // you can drop a folder on a folder
223 0 : if (fs->fileInfo(mapToSource(useIndex)).isDir() && info.isDir) {
224 : return true;
225 : }
226 : // you can drop a file on a folder
227 0 : if (fs->fileInfo(mapToSource(useIndex)).isDir() && info.isFile) {
228 : return true;
229 : }
230 : // you can drop a file on a file
231 0 : if (fs->fileInfo(mapToSource(useIndex)).isFile() && info.isFile) {
232 : return true;
233 : }
234 :
235 : return false;
236 0 : }
237 :
238 : /**
239 : * @brief StoreModel::dropMimeData
240 : * @param data
241 : * @param action
242 : * @param row
243 : * @param column
244 : * @param parent
245 : * @return
246 : */
247 0 : auto StoreModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
248 : int row, int column, const QModelIndex &parent)
249 : -> bool {
250 0 : if (!canDropMimeData(data, action, row, column, parent)) {
251 : return false;
252 : }
253 :
254 0 : if (action == Qt::IgnoreAction) {
255 : return true;
256 : }
257 : QByteArray encodedData =
258 0 : data->data("application/vnd+qtpass.dragAndDropInfoPasswordStore");
259 :
260 0 : QDataStream stream(&encodedData, QIODevice::ReadOnly);
261 : dragAndDropInfoPasswordStore info;
262 0 : stream >> info;
263 : QModelIndex destIndex =
264 0 : this->index(parent.row(), parent.column(), parent.parent());
265 0 : QFileInfo destFileinfo = fs->fileInfo(mapToSource(destIndex));
266 0 : QFileInfo srcFileInfo = QFileInfo(info.path);
267 0 : QString cleanedSrc = QDir::cleanPath(srcFileInfo.absoluteFilePath());
268 0 : QString cleanedDest = QDir::cleanPath(destFileinfo.absoluteFilePath());
269 0 : if (info.isDir) {
270 : // dropped dir onto dir
271 0 : if (destFileinfo.isDir()) {
272 0 : QDir destDir = QDir(cleanedDest).filePath(srcFileInfo.fileName());
273 0 : QString cleanedDestDir = QDir::cleanPath(destDir.absolutePath());
274 0 : if (action == Qt::MoveAction) {
275 0 : QtPassSettings::getPass()->Move(cleanedSrc, cleanedDestDir);
276 0 : } else if (action == Qt::CopyAction) {
277 0 : QtPassSettings::getPass()->Copy(cleanedSrc, cleanedDestDir);
278 : }
279 0 : }
280 0 : } else if (info.isFile) {
281 : // dropped file onto a directory
282 0 : if (destFileinfo.isDir()) {
283 0 : if (action == Qt::MoveAction) {
284 0 : QtPassSettings::getPass()->Move(cleanedSrc, cleanedDest);
285 0 : } else if (action == Qt::CopyAction) {
286 0 : QtPassSettings::getPass()->Copy(cleanedSrc, cleanedDest);
287 : }
288 0 : } else if (destFileinfo.isFile()) {
289 : // dropped file onto a file
290 0 : int answer = QMessageBox::question(
291 0 : nullptr, tr("force overwrite?"),
292 0 : tr("overwrite %1 with %2?").arg(cleanedDest, cleanedSrc),
293 : QMessageBox::Yes | QMessageBox::No);
294 0 : bool force = answer == QMessageBox::Yes;
295 0 : if (action == Qt::MoveAction) {
296 0 : QtPassSettings::getPass()->Move(cleanedSrc, cleanedDest, force);
297 0 : } else if (action == Qt::CopyAction) {
298 0 : QtPassSettings::getPass()->Copy(cleanedSrc, cleanedDest, force);
299 : }
300 : }
301 : }
302 : return true;
303 0 : }
304 :
305 : /**
306 : * @brief StoreModel::lessThan
307 : * @param source_left
308 : * @param source_right
309 : * @return
310 : */
311 35 : auto StoreModel::lessThan(const QModelIndex &source_left,
312 : const QModelIndex &source_right) const -> bool {
313 : /* matches logic in QFileSystemModelSorter::compareNodes() */
314 : #ifndef Q_OS_MAC
315 35 : if (fs && (source_left.column() == 0 || source_left.column() == 1)) {
316 35 : bool leftD = fs->isDir(source_left);
317 35 : bool rightD = fs->isDir(source_right);
318 :
319 35 : if (leftD ^ rightD) {
320 : return leftD;
321 : }
322 : }
323 : #endif
324 :
325 35 : return QSortFilterProxyModel::lessThan(source_left, source_right);
326 : }
|