• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IJHack / QtPass / 24617024444

19 Apr 2026 12:14AM UTC coverage: 22.688% (+0.08%) from 22.607%
24617024444

Pull #1055

github

web-flow
Merge c579ad946 into 68bbaccd0
Pull Request #1055: Refactor complex methods to reduce cyclomatic complexity

33 of 85 new or added lines in 2 files covered. (38.82%)

13 existing lines in 3 files now uncovered.

1315 of 5796 relevant lines covered (22.69%)

8.53 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

45.39
/src/storemodel.cpp
1
// SPDX-FileCopyrightText: 2014 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 <QtGlobal>
13
#include <utility>
14

15
auto operator<<(
1✔
16
    QDataStream &out,
17
    const dragAndDropInfoPasswordStore &dragAndDropInfoPasswordStore)
18
    -> QDataStream & {
19
  out << dragAndDropInfoPasswordStore.isDir
1✔
20
      << dragAndDropInfoPasswordStore.isFile
1✔
21
      << dragAndDropInfoPasswordStore.path;
1✔
22
  return out;
1✔
23
}
24

25
auto operator>>(QDataStream &in,
×
26
                dragAndDropInfoPasswordStore &dragAndDropInfoPasswordStore)
27
    -> QDataStream & {
28
  in >> dragAndDropInfoPasswordStore.isDir >>
×
29
      dragAndDropInfoPasswordStore.isFile >> dragAndDropInfoPasswordStore.path;
×
30
  return in;
×
31
}
32

33
/**
34
 * @brief StoreModel::StoreModel
35
 * SubClass of QSortFilterProxyModel via
36
 * http://www.qtcentre.org/threads/46471-QTreeView-Filter
37
 */
38
StoreModel::StoreModel() { fs = nullptr; }
19✔
39

40
/**
41
 * @brief StoreModel::filterAcceptsRow should row be shown, wrapper for
42
 * StoreModel::showThis method.
43
 * @param sourceRow
44
 * @param sourceParent
45
 * @return
46
 */
47
auto StoreModel::filterAcceptsRow(int sourceRow,
19✔
48
                                  const QModelIndex &sourceParent) const
49
    -> bool {
50
  QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
19✔
51
  return showThis(index);
19✔
52
}
53

54
/**
55
 * @brief StoreModel::showThis should a row be shown, based on our search
56
 * criteria.
57
 * @param index
58
 * @return
59
 */
60
auto StoreModel::showThis(const QModelIndex &index) const -> bool {
44✔
61
  bool retVal = false;
62
  if (fs == nullptr) {
44✔
63
    return retVal;
64
  }
65
  // Gives you the info for number of childs with a parent
66
  if (sourceModel()->rowCount(index) > 0) {
43✔
67
    for (int nChild = 0; nChild < sourceModel()->rowCount(index); ++nChild) {
24✔
68
      QModelIndex childIndex = sourceModel()->index(nChild, 0, index);
24✔
69
      if (!childIndex.isValid()) {
70
        break;
71
      }
72
      retVal = showThis(childIndex);
24✔
73
      if (retVal) {
24✔
74
        break;
75
      }
76
    }
77
  } else {
78
    QModelIndex useIndex = sourceModel()->index(index.row(), 0, index.parent());
19✔
79
    QString path = fs->filePath(useIndex);
19✔
80
    path = QDir(store).relativeFilePath(path);
38✔
81
    if (path.startsWith(".git")) {
38✔
82
      return false;
83
    }
84
    path.replace(Util::endsWithGpg(), "");
18✔
85
    retVal = path.contains(filterRegularExpression());
18✔
86
  }
87
  return retVal;
88
}
89

90
/**
91
 * @brief StoreModel::setModelAndStore update the source model and store.
92
 * @param sourceModel
93
 * @param passStore
94
 */
95
void StoreModel::setModelAndStore(QFileSystemModel *sourceModel,
14✔
96
                                  QString passStore) {
97
  setSourceModel(sourceModel);
14✔
98
  fs = sourceModel;
14✔
99
  store = std::move(passStore);
100
}
14✔
101

102
void StoreModel::setStore(const QString &passStore) {
×
103
#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)
104
  beginFilterChange();
105
  store = passStore;
106
  endFilterChange(QSortFilterProxyModel::Direction::Rows);
107
#else
108
  store = passStore;
×
109
#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
110
  QSortFilterProxyModel::invalidateFilter();
×
111
#else
112
  invalidateFilter();
113
#endif
114
#endif
115
}
×
116

117
/**
118
 * @brief StoreModel::data don't show the .gpg at the end of a file.
119
 * @param index
120
 * @param role
121
 * @return
122
 */
123
auto StoreModel::data(const QModelIndex &index, int role) const -> QVariant {
2✔
124
  if (!index.isValid()) {
125
    return {};
126
  }
127

128
  QVariant initial_value;
129
  initial_value = QSortFilterProxyModel::data(index, role);
1✔
130

131
  if (role == Qt::DisplayRole) {
1✔
132
    QString name = initial_value.toString();
1✔
133
    name.replace(Util::endsWithGpg(), "");
1✔
134
    initial_value.setValue(name);
1✔
135
  }
136

137
  return initial_value;
1✔
138
}
1✔
139

140
/**
141
 * @brief StoreModel::supportedDropActions enable drop.
142
 * @return
143
 */
144
auto StoreModel::supportedDropActions() const -> Qt::DropActions {
1✔
145
  return Qt::CopyAction | Qt::MoveAction;
1✔
146
}
147

148
/**
149
 * @brief StoreModel::supportedDragActions enable drag.
150
 * @return
151
 */
152
auto StoreModel::supportedDragActions() const -> Qt::DropActions {
1✔
153
  return Qt::CopyAction | Qt::MoveAction;
1✔
154
}
155

156
/**
157
 * @brief StoreModel::flags
158
 * @param index
159
 * @return
160
 */
161
auto StoreModel::flags(const QModelIndex &index) const -> Qt::ItemFlags {
2✔
162
  Qt::ItemFlags defaultFlags = QSortFilterProxyModel::flags(index);
2✔
163

164
  if (index.isValid()) {
165
    return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
166
  }
167
  return Qt::ItemIsDropEnabled | defaultFlags;
1✔
168
}
169

170
/**
171
 * @brief StoreModel::mimeTypes
172
 * @return
173
 */
174
auto StoreModel::mimeTypes() const -> QStringList {
1✔
175
  QStringList types;
1✔
176
  types << "application/vnd+qtpass.dragAndDropInfoPasswordStore";
1✔
177
  return types;
1✔
178
}
179

180
/**
181
 * @brief StoreModel::mimeData
182
 * @param indexes
183
 * @return
184
 */
185
auto StoreModel::mimeData(const QModelIndexList &indexes) const -> QMimeData * {
1✔
186
  dragAndDropInfoPasswordStore info;
187

188
  QByteArray encodedData;
1✔
189
  // only use the first, otherwise we should enable multiselection
190
  QModelIndex index = indexes.at(0);
1✔
191
  if (index.isValid()) {
192
    QModelIndex useIndex = mapToSource(index);
1✔
193

194
    info.isDir = fs->fileInfo(useIndex).isDir();
1✔
195
    info.isFile = fs->fileInfo(useIndex).isFile();
1✔
196
    info.path = fs->fileInfo(useIndex).absoluteFilePath();
2✔
197
    QDataStream stream(&encodedData, QIODevice::WriteOnly);
1✔
198
    stream << info;
1✔
199
  }
1✔
200

201
  auto *mimeData = new QMimeData();
1✔
202
  mimeData->setData("application/vnd+qtpass.dragAndDropInfoPasswordStore",
2✔
203
                    encodedData);
204
  return mimeData;
1✔
205
}
206

207
/**
208
 * @brief StoreModel::canDropMimeData
209
 * @param data
210
 * @param action
211
 * @param row
212
 * @param column
213
 * @param parent
214
 * @return
215
 */
216
auto StoreModel::canDropMimeData(const QMimeData *data, Qt::DropAction action,
×
217
                                 int row, int column,
218
                                 const QModelIndex &parent) const -> bool {
219
#ifdef QT_DEBUG
220
  qDebug() << action << row;
221
#else
222
  Q_UNUSED(action)
223
  Q_UNUSED(row)
224
#endif
225

226
  if (data == nullptr ||
×
227
      !data->hasFormat("application/vnd+qtpass.dragAndDropInfoPasswordStore")) {
×
228
    return false;
229
  }
230

231
  QByteArray encodedData =
232
      data->data("application/vnd+qtpass.dragAndDropInfoPasswordStore");
×
233
  if (encodedData.isEmpty()) {
×
234
    return false;
235
  }
236
  QDataStream stream(&encodedData, QIODevice::ReadOnly);
×
237
  dragAndDropInfoPasswordStore info;
238
  stream >> info;
×
239
  if (stream.status() != QDataStream::Ok) {
×
240
    return false;
241
  }
242

243
  QModelIndex useIndex =
244
      this->index(parent.row(), parent.column(), parent.parent());
×
245

246
  if (column > 0) {
×
247
    return false;
248
  }
249

250
  // you can drop a folder on a folder
251
  if (fs->fileInfo(mapToSource(useIndex)).isDir() && info.isDir) {
×
252
    return true;
253
  }
254
  // you can drop a file on a folder
255
  if (fs->fileInfo(mapToSource(useIndex)).isDir() && info.isFile) {
×
256
    return true;
257
  }
258
  // you can drop a file on a file
259
  if (fs->fileInfo(mapToSource(useIndex)).isFile() && info.isFile) {
×
260
    return true;
261
  }
262

263
  return false;
264
}
×
265

266
/**
267
 * @brief StoreModel::dropMimeData
268
 * @param data
269
 * @param action
270
 * @param row
271
 * @param column
272
 * @param parent
273
 * @return
274
 */
275
auto StoreModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
×
276
                              int row, int column, const QModelIndex &parent)
277
    -> bool {
278
  if (!canDropMimeData(data, action, row, column, parent)) {
×
279
    return false;
280
  }
281

282
  if (action == Qt::IgnoreAction) {
×
283
    return true;
284
  }
285

NEW
286
  if (action != Qt::MoveAction && action != Qt::CopyAction) {
×
287
    return false;
288
  }
289

290
  dragAndDropInfoPasswordStore info;
NEW
291
  if (!parseDropData(data, &info)) {
×
292
    return false;
293
  }
294

NEW
295
  return executeDropAction(info, action, parent);
×
296
}
297

NEW
298
bool StoreModel::parseDropData(const QMimeData *data,
×
299
                               dragAndDropInfoPasswordStore *outInfo) {
300
  QByteArray encodedData =
301
      data->data("application/vnd+qtpass.dragAndDropInfoPasswordStore");
×
302
  if (encodedData.isEmpty()) {
×
303
    return false;
304
  }
305

UNCOV
306
  QDataStream stream(&encodedData, QIODevice::ReadOnly);
×
307
  dragAndDropInfoPasswordStore info;
308
  stream >> info;
×
309
  if (stream.status() != QDataStream::Ok) {
×
310
    return false;
311
  }
312

313
  *outInfo = info;
NEW
314
  return true;
×
NEW
315
}
×
316

NEW
317
auto StoreModel::executeDropAction(const dragAndDropInfoPasswordStore &info,
×
318
                                   Qt::DropAction action,
319
                                   const QModelIndex &parent) -> bool {
320
  QModelIndex destIndex =
321
      this->index(parent.row(), parent.column(), parent.parent());
×
322
  QFileInfo destFileinfo = fs->fileInfo(mapToSource(destIndex));
×
323
  QFileInfo srcFileInfo = QFileInfo(info.path);
×
324

325
  QString cleanedSrc = QDir::cleanPath(srcFileInfo.absoluteFilePath());
×
326
  QString cleanedDest = QDir::cleanPath(destFileinfo.absoluteFilePath());
×
327

UNCOV
328
  if (info.isDir) {
×
NEW
329
    return handleDirDrop(cleanedSrc, destFileinfo, srcFileInfo, action);
×
330
  }
NEW
331
  return handleFileDrop(cleanedSrc, cleanedDest, destFileinfo, action,
×
NEW
332
                        info.isFile);
×
NEW
333
}
×
334

NEW
335
auto StoreModel::handleDirDrop(const QString &cleanedSrc,
×
336
                               const QFileInfo &destFileinfo,
337
                               const QFileInfo &srcFileInfo,
338
                               Qt::DropAction action) -> bool {
NEW
339
  if (!destFileinfo.isDir()) {
×
340
    return false;
341
  }
342

NEW
343
  QDir destDir = QDir(QDir::cleanPath(destFileinfo.absoluteFilePath()))
×
NEW
344
                     .filePath(srcFileInfo.fileName());
×
NEW
345
  QString cleanedDestDir = QDir::cleanPath(destDir.absolutePath());
×
346

NEW
347
  if (action == Qt::MoveAction) {
×
NEW
348
    QtPassSettings::getPass()->Move(cleanedSrc, cleanedDestDir);
×
NEW
349
  } else if (action == Qt::CopyAction) {
×
NEW
350
    QtPassSettings::getPass()->Copy(cleanedSrc, cleanedDestDir);
×
351
  }
352
  return true;
NEW
353
}
×
354

NEW
355
auto StoreModel::handleFileDrop(const QString &cleanedSrc,
×
356
                                const QString &cleanedDest,
357
                                const QFileInfo &destFileinfo,
358
                                Qt::DropAction action, bool isFile) -> bool {
NEW
359
  if (destFileinfo.isDir()) {
×
NEW
360
    return handleFileToDirDrop(cleanedSrc, cleanedDest, action);
×
361
  }
NEW
362
  return handleFileToFileDrop(cleanedSrc, cleanedDest, action);
×
363
}
364

NEW
365
auto StoreModel::handleFileToDirDrop(const QString &cleanedSrc,
×
366
                                     const QString &cleanedDest,
367
                                     Qt::DropAction action) -> bool {
NEW
368
  if (action == Qt::MoveAction) {
×
NEW
369
    QtPassSettings::getPass()->Move(cleanedSrc, cleanedDest);
×
NEW
370
  } else if (action == Qt::CopyAction) {
×
NEW
371
    QtPassSettings::getPass()->Copy(cleanedSrc, cleanedDest);
×
372
  }
NEW
373
  return true;
×
374
}
375

NEW
376
auto StoreModel::handleFileToFileDrop(const QString &cleanedSrc,
×
377
                                      const QString &cleanedDest,
378
                                      Qt::DropAction action) -> bool {
NEW
379
  int answer = QMessageBox::question(
×
NEW
380
      nullptr, tr("force overwrite?"),
×
NEW
381
      tr("overwrite %1 with %2?").arg(cleanedDest, cleanedSrc),
×
382
      QMessageBox::Yes | QMessageBox::No);
NEW
383
  bool force = answer == QMessageBox::Yes;
×
384

NEW
385
  if (action == Qt::MoveAction) {
×
NEW
386
    QtPassSettings::getPass()->Move(cleanedSrc, cleanedDest, force);
×
NEW
387
  } else if (action == Qt::CopyAction) {
×
NEW
388
    QtPassSettings::getPass()->Copy(cleanedSrc, cleanedDest, force);
×
389
  }
UNCOV
390
  return true;
×
391
}
392

393
/**
394
 * @brief StoreModel::lessThan
395
 * @param source_left
396
 * @param source_right
397
 * @return
398
 */
399
auto StoreModel::lessThan(const QModelIndex &source_left,
2✔
400
                          const QModelIndex &source_right) const -> bool {
401
/* matches logic in QFileSystemModelSorter::compareNodes() */
402
#ifndef Q_OS_MAC
403
  if (fs && (source_left.column() == 0 || source_left.column() == 1)) {
2✔
404
    bool leftD = fs->isDir(source_left);
2✔
405
    bool rightD = fs->isDir(source_right);
2✔
406

407
    if (leftD ^ rightD) {
2✔
408
      return leftD;
409
    }
410
  }
411
#endif
412

413
  return QSortFilterProxyModel::lessThan(source_left, source_right);
1✔
414
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc