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

IJHack / QtPass / 24161188444

08 Apr 2026 10:06PM UTC coverage: 20.992% (-0.05%) from 21.04%
24161188444

push

github

web-flow
fix: let window manager handle dialog positioning (#947)

* fix: let window manager handle dialog positioning

- Config dialog: Only restore position/size if previously saved (not first launch)
- Close event: Only save position/size when not maximized

* fix: use getDialogGeometry() as reliable discriminator for saved state

Replaces savedPos.isNull() check with getDialogGeometry() non-empty check
to properly handle cases where position QPoint(0,0) was legitimately saved.

* fix: avoid saving maximized dialog position/size in all dialogs

Apply same guard pattern as ConfigDialog to KeygenDialog and UsersDialog:
only save pos()/size() when not maximized.

* fix: guard dialog position/size restore with geometry check in all dialogs

Apply same pattern as ConfigDialog to KeygenDialog and UsersDialog:
only restore pos()/size() if saved geometry exists.

0 of 21 new or added lines in 3 files covered. (0.0%)

4 existing lines in 3 files now uncovered.

1109 of 5283 relevant lines covered (20.99%)

7.78 hits per line

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

0.0
/src/configdialog.cpp
1
// SPDX-FileCopyrightText: 2014 Anne Jan Brouwer
2
// SPDX-License-Identifier: GPL-3.0-or-later
3
#include "configdialog.h"
4
#include "keygendialog.h"
5
#include "mainwindow.h"
6
#include "qtpasssettings.h"
7
#include "ui_configdialog.h"
8
#include "util.h"
9
#include <QClipboard>
10
#include <QDir>
11
#include <QFileDialog>
12
#include <QMessageBox>
13
#include <QPushButton>
14
#include <QSystemTrayIcon>
15
#include <QTableWidgetItem>
16
#include <utility>
17
#ifdef Q_OS_WIN
18
#include <windows.h>
19
#endif
20

21
#ifdef QT_DEBUG
22
#include "debughelper.h"
23
#endif
24

25
/**
26
 * @brief ConfigDialog::ConfigDialog this sets up the configuration screen.
27
 * @param parent
28
 */
29
ConfigDialog::ConfigDialog(MainWindow *parent)
×
30
    : QDialog(parent), ui(new Ui::ConfigDialog) {
×
31
  mainWindow = parent;
×
32
  ui->setupUi(this);
×
33

34
  // Restore dialog state
NEW
35
  QByteArray savedGeometry = QtPassSettings::getDialogGeometry("configDialog");
×
36
  bool hasSavedGeometry = !savedGeometry.isEmpty();
NEW
37
  if (hasSavedGeometry) {
×
NEW
38
    restoreGeometry(savedGeometry);
×
39
  }
40
  if (QtPassSettings::isDialogMaximized("configDialog")) {
×
41
    showMaximized();
×
NEW
42
  } else if (!hasSavedGeometry) {
×
43
    // Let window manager handle positioning for first launch
44
  } else {
45
    move(QtPassSettings::getDialogPos("configDialog"));
×
46
    resize(QtPassSettings::getDialogSize("configDialog"));
×
47
  }
48

49
  ui->passPath->setText(QtPassSettings::getPassExecutable());
×
50
  setGitPath(QtPassSettings::getGitExecutable());
×
51
  ui->gpgPath->setText(QtPassSettings::getGpgExecutable());
×
52
  ui->storePath->setText(QtPassSettings::getPassStore());
×
53

54
  ui->spinBoxAutoclearSeconds->setValue(QtPassSettings::getAutoclearSeconds());
×
55
  ui->spinBoxAutoclearPanelSeconds->setValue(
×
56
      QtPassSettings::getAutoclearPanelSeconds());
×
57
  ui->checkBoxHidePassword->setChecked(QtPassSettings::isHidePassword());
×
58
  ui->checkBoxHideContent->setChecked(QtPassSettings::isHideContent());
×
59
  ui->checkBoxUseMonospace->setChecked(QtPassSettings::isUseMonospace());
×
60
  ui->checkBoxDisplayAsIs->setChecked(QtPassSettings::isDisplayAsIs());
×
61
  ui->checkBoxNoLineWrapping->setChecked(QtPassSettings::isNoLineWrapping());
×
62
  ui->checkBoxAddGPGId->setChecked(QtPassSettings::isAddGPGId(true));
×
63

64
  if (QSystemTrayIcon::isSystemTrayAvailable()) {
×
65
    ui->checkBoxHideOnClose->setChecked(QtPassSettings::isHideOnClose());
×
66
    ui->checkBoxStartMinimized->setChecked(QtPassSettings::isStartMinimized());
×
67
  } else {
68
    ui->checkBoxUseTrayIcon->setEnabled(false);
×
69
    ui->checkBoxUseTrayIcon->setToolTip(tr("System tray is not available"));
×
70
    ui->checkBoxHideOnClose->setEnabled(false);
×
71
    ui->checkBoxStartMinimized->setEnabled(false);
×
72
  }
73

74
  ui->checkBoxAvoidCapitals->setChecked(QtPassSettings::isAvoidCapitals());
×
75
  ui->checkBoxAvoidNumbers->setChecked(QtPassSettings::isAvoidNumbers());
×
76
  ui->checkBoxLessRandom->setChecked(QtPassSettings::isLessRandom());
×
77
  ui->checkBoxUseSymbols->setChecked(QtPassSettings::isUseSymbols());
×
78
  ui->plainTextEditTemplate->setPlainText(QtPassSettings::getPassTemplate());
×
79
  ui->checkBoxTemplateAllFields->setChecked(
×
80
      QtPassSettings::isTemplateAllFields());
×
81
  ui->checkBoxAutoPull->setChecked(QtPassSettings::isAutoPull());
×
82
  ui->checkBoxAutoPush->setChecked(QtPassSettings::isAutoPush());
×
83
  ui->checkBoxAlwaysOnTop->setChecked(QtPassSettings::isAlwaysOnTop());
×
84

85
#if defined(Q_OS_WIN)
86
  ui->checkBoxUseOtp->hide();
87
  ui->checkBoxUseQrencode->hide();
88
  ui->label_10->hide();
89
#endif
90

91
  if (!isPassOtpAvailable()) {
×
92
    ui->checkBoxUseOtp->setEnabled(false);
93
    ui->checkBoxUseOtp->setToolTip(
×
94
        tr("Pass OTP extension needs to be installed"));
×
95
  }
96

97
  if (!isQrencodeAvailable()) {
×
UNCOV
98
    ui->checkBoxUseQrencode->setEnabled(false);
×
99
    ui->checkBoxUseQrencode->setToolTip(tr("qrencode needs to be installed"));
×
100
  }
101

102
  setProfiles(QtPassSettings::getProfiles(), QtPassSettings::getProfile());
×
103
  setPwgenPath(QtPassSettings::getPwgenExecutable());
×
104
  setPasswordConfiguration(QtPassSettings::getPasswordConfiguration());
×
105

106
  usePass(QtPassSettings::isUsePass());
×
107
  useAutoclear(QtPassSettings::isUseAutoclear());
×
108
  useAutoclearPanel(QtPassSettings::isUseAutoclearPanel());
×
109
  useTrayIcon(QtPassSettings::isUseTrayIcon());
×
110
  useGit(QtPassSettings::isUseGit());
×
111

112
  useOtp(QtPassSettings::isUseOtp());
×
113
  useQrencode(QtPassSettings::isUseQrencode());
×
114

115
  usePwgen(QtPassSettings::isUsePwgen());
×
116
  useTemplate(QtPassSettings::isUseTemplate());
×
117

118
  ui->profileTable->verticalHeader()->hide();
×
119
  ui->profileTable->horizontalHeader()->setSectionResizeMode(
×
120
      1, QHeaderView::Stretch);
121
  ui->label->setText(ui->label->text() + VERSION);
×
122
  ui->comboBoxClipboard->clear();
×
123

124
  ui->comboBoxClipboard->addItem(tr("No Clipboard"));
×
125
  ui->comboBoxClipboard->addItem(tr("Always copy to clipboard"));
×
126
  ui->comboBoxClipboard->addItem(tr("On-demand copy to clipboard"));
×
127

128
  int currentIndex = QtPassSettings::getClipBoardTypeRaw();
×
129
  ui->comboBoxClipboard->setCurrentIndex(currentIndex);
×
130
  on_comboBoxClipboard_activated(currentIndex);
×
131

132
  QClipboard *clip = QApplication::clipboard();
×
133
  if (!clip->supportsSelection()) {
×
134
    useSelection(false);
×
135
    ui->checkBoxSelection->setVisible(false);
×
136
  } else {
137
    useSelection(QtPassSettings::isUseSelection());
×
138
  }
139

140
  if (!Util::configIsValid()) {
×
141
    // Show Programs tab, which is likely
142
    // what the user needs to fix now.
143
    ui->tabWidget->setCurrentIndex(1);
×
144
  }
145

146
  connect(ui->profileTable, &QTableWidget::itemChanged, this,
×
147
          &ConfigDialog::onProfileTableItemChanged);
×
148
  connect(this, &ConfigDialog::accepted, this, &ConfigDialog::on_accepted);
×
149
}
×
150

151
/**
152
 * @brief ConfigDialog::~ConfigDialog config destructor, makes sure the
153
 * mainWindow knows about git, gpg and pass executables.
154
 */
155
ConfigDialog::~ConfigDialog() {
×
156
  QtPassSettings::setGitExecutable(ui->gitPath->text());
×
157
  QtPassSettings::setGpgExecutable(ui->gpgPath->text());
×
158
  QtPassSettings::setPassExecutable(ui->passPath->text());
×
159
}
×
160

161
/**
162
 * @brief ConfigDialog::setGitPath set the git executable path.
163
 * Make sure the checkBoxUseGit is updated.
164
 * @param path
165
 */
166
void ConfigDialog::setGitPath(const QString &path) {
×
167
  ui->gitPath->setText(path);
×
168
  ui->checkBoxUseGit->setEnabled(!path.isEmpty());
×
169
  if (path.isEmpty()) {
×
170
    useGit(false);
×
171
  }
172
}
×
173

174
/**
175
 * @brief ConfigDialog::usePass set wether or not we want to use pass.
176
 * Update radio buttons accordingly.
177
 * @param usePass
178
 */
179
void ConfigDialog::usePass(bool usePass) {
×
180
  ui->radioButtonNative->setChecked(!usePass);
×
181
  ui->radioButtonPass->setChecked(usePass);
×
182
  setGroupBoxState();
×
183
}
×
184

185
/**
186
 * @brief Validates the configuration table fields and enables or disables the
187
 * OK button accordingly.
188
 * @example
189
 * ConfigDialog dialog;
190
 * QTableWidgetItem *item = dialog.findChild<QTableWidgetItem*>();
191
 * dialog.validate(item);
192
 *
193
 * @param QTableWidgetItem *item - The table item to validate; if null,
194
 * validates all relevant items in the profile table.
195
 * @return void - This function does not return a value.
196
 */
197
void ConfigDialog::validate(QTableWidgetItem *item) {
×
198
  bool status = true;
199

200
  if (item == nullptr) {
×
201
    for (int i = 0; i < ui->profileTable->rowCount(); i++) {
×
202
      for (int j = 0; j < ui->profileTable->columnCount(); j++) {
×
203
        QTableWidgetItem *_item = ui->profileTable->item(i, j);
×
204

205
        if (_item->text().isEmpty() && j != 2) {
×
206
          _item->setBackground(Qt::red);
×
207
          _item->setToolTip(tr("This field is required"));
×
208
          status = false;
209
          break;
210
        } else {
211
          _item->setToolTip(QString());
×
212
        }
213
      }
214

215
      if (!status) {
216
        break;
217
      }
218
    }
219
  } else {
220
    if (item->text().isEmpty() && item->column() != 2) {
×
221
      item->setBackground(Qt::red);
×
222
      item->setToolTip(tr("This field is required"));
×
223
      status = false;
224
    } else {
225
      item->setToolTip(QString());
×
226
    }
227
  }
228

229
  ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(status);
×
230
}
×
231

232
/**
233
 * @brief Saves the configuration dialog settings to persistent application
234
 * settings.
235
 * @example
236
 * ConfigDialog dialog;
237
 * dialog.on_accepted();
238
 * // Expected output: All UI-selected configuration values are stored via
239
 * QtPassSettings.
240
 *
241
 * @return void - This method does not return a value.
242
 */
243
void ConfigDialog::on_accepted() {
×
244
  QtPassSettings::setPassExecutable(ui->passPath->text());
×
245
  QtPassSettings::setGitExecutable(ui->gitPath->text());
×
246
  QtPassSettings::setGpgExecutable(ui->gpgPath->text());
×
247
  QtPassSettings::setPassStore(
×
248
      Util::normalizeFolderPath(ui->storePath->text()));
×
249
  QtPassSettings::setUsePass(ui->radioButtonPass->isChecked());
×
250
  QtPassSettings::setClipBoardType(ui->comboBoxClipboard->currentIndex());
×
251
  QtPassSettings::setUseSelection(ui->checkBoxSelection->isChecked());
×
252
  QtPassSettings::setUseAutoclear(ui->checkBoxAutoclear->isChecked());
×
253
  QtPassSettings::setAutoclearSeconds(ui->spinBoxAutoclearSeconds->value());
×
254
  QtPassSettings::setUseAutoclearPanel(ui->checkBoxAutoclearPanel->isChecked());
×
255
  QtPassSettings::setAutoclearPanelSeconds(
×
256
      ui->spinBoxAutoclearPanelSeconds->value());
×
257
  QtPassSettings::setHidePassword(ui->checkBoxHidePassword->isChecked());
×
258
  QtPassSettings::setHideContent(ui->checkBoxHideContent->isChecked());
×
259
  QtPassSettings::setUseMonospace(ui->checkBoxUseMonospace->isChecked());
×
260
  QtPassSettings::setDisplayAsIs(ui->checkBoxDisplayAsIs->isChecked());
×
261
  QtPassSettings::setNoLineWrapping(ui->checkBoxNoLineWrapping->isChecked());
×
262
  QtPassSettings::setAddGPGId(ui->checkBoxAddGPGId->isChecked());
×
263
  QtPassSettings::setUseTrayIcon(ui->checkBoxUseTrayIcon->isEnabled() &&
×
264
                                 ui->checkBoxUseTrayIcon->isChecked());
×
265
  QtPassSettings::setHideOnClose(ui->checkBoxHideOnClose->isEnabled() &&
×
266
                                 ui->checkBoxHideOnClose->isChecked());
×
267
  QtPassSettings::setStartMinimized(ui->checkBoxStartMinimized->isEnabled() &&
×
268
                                    ui->checkBoxStartMinimized->isChecked());
×
269
  QtPassSettings::setProfiles(getProfiles());
×
270
  QtPassSettings::setUseGit(ui->checkBoxUseGit->isChecked());
×
271
  QtPassSettings::setUseOtp(ui->checkBoxUseOtp->isChecked());
×
272
  QtPassSettings::setUseQrencode(ui->checkBoxUseQrencode->isChecked());
×
273
  QtPassSettings::setPwgenExecutable(ui->pwgenPath->text());
×
274
  QtPassSettings::setUsePwgen(ui->checkBoxUsePwgen->isChecked());
×
275
  QtPassSettings::setAvoidCapitals(ui->checkBoxAvoidCapitals->isChecked());
×
276
  QtPassSettings::setAvoidNumbers(ui->checkBoxAvoidNumbers->isChecked());
×
277
  QtPassSettings::setLessRandom(ui->checkBoxLessRandom->isChecked());
×
278
  QtPassSettings::setUseSymbols(ui->checkBoxUseSymbols->isChecked());
×
279
  QtPassSettings::setPasswordConfiguration(getPasswordConfiguration());
×
280
  QtPassSettings::setUseTemplate(ui->checkBoxUseTemplate->isChecked());
×
281
  QtPassSettings::setPassTemplate(ui->plainTextEditTemplate->toPlainText());
×
282
  QtPassSettings::setTemplateAllFields(
×
283
      ui->checkBoxTemplateAllFields->isChecked());
×
284
  QtPassSettings::setAutoPush(ui->checkBoxAutoPush->isChecked());
×
285
  QtPassSettings::setAutoPull(ui->checkBoxAutoPull->isChecked());
×
286
  QtPassSettings::setAlwaysOnTop(ui->checkBoxAlwaysOnTop->isChecked());
×
287

288
  QtPassSettings::setVersion(VERSION);
×
289
}
×
290

291
/**
292
 * @brief Automatically detects required external binaries in the system PATH
293
 * and updates the dialog fields.
294
 * @example
295
 * ConfigDialog configDialog;
296
 * configDialog.on_autodetectButton_clicked();
297
 *
298
 * @return void - This function does not return a value.
299
 */
300
void ConfigDialog::on_autodetectButton_clicked() {
×
301
  QString pass = Util::findBinaryInPath("pass");
×
302
  if (!pass.isEmpty()) {
×
303
    ui->passPath->setText(pass);
×
304
  }
305
  usePass(!pass.isEmpty());
×
306
  QString gpg = Util::findBinaryInPath("gpg2");
×
307
  if (gpg.isEmpty()) {
×
308
    gpg = Util::findBinaryInPath("gpg");
×
309
  }
310
  if (!gpg.isEmpty()) {
×
311
    ui->gpgPath->setText(gpg);
×
312
  }
313
  QString git = Util::findBinaryInPath("git");
×
314
  if (!git.isEmpty()) {
×
315
    ui->gitPath->setText(git);
×
316
  }
317
  QString pwgen = Util::findBinaryInPath("pwgen");
×
318
  if (!pwgen.isEmpty()) {
×
319
    ui->pwgenPath->setText(pwgen);
×
320
  }
321
}
×
322

323
/**
324
 * @brief ConfigDialog::on_radioButtonNative_clicked wrapper for
325
 * ConfigDialog::setGroupBoxState()
326
 */
327
void ConfigDialog::on_radioButtonNative_clicked() { setGroupBoxState(); }
×
328

329
/**
330
 * @brief ConfigDialog::on_radioButtonPass_clicked wrapper for
331
 * ConfigDialog::setGroupBoxState()
332
 */
333
void ConfigDialog::on_radioButtonPass_clicked() { setGroupBoxState(); }
×
334

335
/**
336
 * @brief ConfigDialog::getSecretKeys get list of secret/private keys
337
 * @return QStringList keys
338
 */
339
auto ConfigDialog::getSecretKeys() -> QStringList {
×
340
  QList<UserInfo> keys = QtPassSettings::getPass()->listKeys("", true);
×
341
  QStringList names;
×
342

343
  if (keys.empty()) {
×
344
    return names;
345
  }
346

347
  foreach (const UserInfo &sec, keys)
×
348
    names << sec.name;
×
349

350
  return names;
×
351
}
352

353
/**
354
 * @brief ConfigDialog::setGroupBoxState update checkboxes.
355
 */
356
void ConfigDialog::setGroupBoxState() {
×
357
  bool state = ui->radioButtonPass->isChecked();
×
358
  ui->groupBoxNative->setEnabled(!state);
×
359
  ui->groupBoxPass->setEnabled(state);
×
360
}
×
361

362
/**
363
 * @brief ConfigDialog::selectExecutable pop-up to choose an executable.
364
 * @return
365
 */
366
auto ConfigDialog::selectExecutable() -> QString {
×
367
  QFileDialog dialog(this);
×
368
  dialog.setFileMode(QFileDialog::ExistingFile);
×
369
  dialog.setOption(QFileDialog::ReadOnly);
×
370
  if (dialog.exec()) {
×
371
    return dialog.selectedFiles().constFirst();
×
372
  }
373

374
  return {};
375
}
×
376

377
/**
378
 * @brief ConfigDialog::selectFolder pop-up to choose a folder.
379
 * @return
380
 */
381
auto ConfigDialog::selectFolder() -> QString {
×
382
  QFileDialog dialog(this);
×
383
  dialog.setFileMode(QFileDialog::Directory);
×
384
  dialog.setFilter(QDir::NoFilter);
×
385
  dialog.setOption(QFileDialog::ShowDirsOnly);
×
386
  if (dialog.exec()) {
×
387
    return dialog.selectedFiles().constFirst();
×
388
  }
389

390
  return {};
391
}
×
392

393
/**
394
 * @brief ConfigDialog::on_toolButtonGit_clicked get git application.
395
 * Enable checkboxes if found.
396
 */
397
void ConfigDialog::on_toolButtonGit_clicked() {
×
398
  QString git = selectExecutable();
×
399
  bool state = !git.isEmpty();
×
400
  if (state) {
×
401
    ui->gitPath->setText(git);
×
402
  } else {
403
    useGit(false);
×
404
  }
405

406
  ui->checkBoxUseGit->setEnabled(state);
×
407
}
×
408

409
/**
410
 * @brief ConfigDialog::on_toolButtonGpg_clicked get gpg application.
411
 */
412
void ConfigDialog::on_toolButtonGpg_clicked() {
×
413
  QString gpg = selectExecutable();
×
414
  if (!gpg.isEmpty()) {
×
415
    ui->gpgPath->setText(gpg);
×
416
  }
417
}
×
418

419
/**
420
 * @brief ConfigDialog::on_pushButtonGenerateKey_clicked open keygen dialog.
421
 */
422
void ConfigDialog::on_pushButtonGenerateKey_clicked() {
×
423
  KeygenDialog d(this);
×
424
  d.exec();
×
425
}
×
426

427
/**
428
 * @brief ConfigDialog::on_toolButtonPass_clicked get pass application.
429
 */
430
void ConfigDialog::on_toolButtonPass_clicked() {
×
431
  QString pass = selectExecutable();
×
432
  if (!pass.isEmpty()) {
×
433
    ui->passPath->setText(pass);
×
434
  }
435
}
×
436

437
/**
438
 * @brief ConfigDialog::on_toolButtonStore_clicked get .password-store
439
 * location.s
440
 */
441
void ConfigDialog::on_toolButtonStore_clicked() {
×
442
  QString store = selectFolder();
×
443
  if (!store.isEmpty()) {
×
444
    ui->storePath->setText(store);
×
445
  }
446
}
×
447

448
/**
449
 * @brief ConfigDialog::on_comboBoxClipboard_activated show and hide options.
450
 * @param index of selectbox (0 = no clipboard).
451
 */
452
void ConfigDialog::on_comboBoxClipboard_activated(int index) {
×
453
  bool state = index > 0;
×
454

455
  ui->checkBoxSelection->setEnabled(state);
×
456
  ui->checkBoxAutoclear->setEnabled(state);
×
457
  ui->checkBoxHidePassword->setEnabled(state);
×
458
  ui->checkBoxHideContent->setEnabled(state);
×
459
  if (state) {
×
460
    ui->spinBoxAutoclearSeconds->setEnabled(ui->checkBoxAutoclear->isChecked());
×
461
    ui->labelSeconds->setEnabled(ui->checkBoxAutoclear->isChecked());
×
462
  } else {
463
    ui->spinBoxAutoclearSeconds->setEnabled(false);
×
464
    ui->labelSeconds->setEnabled(false);
×
465
  }
466
}
×
467

468
/**
469
 * @brief ConfigDialog::on_checkBoxAutoclearPanel_clicked enable and disable
470
 * options based on autoclear use.
471
 */
472
void ConfigDialog::on_checkBoxAutoclearPanel_clicked() {
×
473
  bool state = ui->checkBoxAutoclearPanel->isChecked();
×
474
  ui->spinBoxAutoclearPanelSeconds->setEnabled(state);
×
475
  ui->labelPanelSeconds->setEnabled(state);
×
476
}
×
477

478
/**
479
 * @brief ConfigDialog::useSelection set the clipboard type use from
480
 * MainWindow.
481
 * @param useSelection
482
 */
483
void ConfigDialog::useSelection(bool useSelection) {
×
484
  ui->checkBoxSelection->setChecked(useSelection);
×
485
  on_checkBoxSelection_clicked();
×
486
}
×
487

488
/**
489
 * @brief ConfigDialog::useAutoclear set the clipboard autoclear use from
490
 * MainWindow.
491
 * @param useAutoclear
492
 */
493
void ConfigDialog::useAutoclear(bool useAutoclear) {
×
494
  ui->checkBoxAutoclear->setChecked(useAutoclear);
×
495
  on_checkBoxAutoclear_clicked();
×
496
}
×
497

498
/**
499
 * @brief ConfigDialog::useAutoclearPanel set the panel autoclear use from
500
 * MainWindow.
501
 * @param useAutoclearPanel
502
 */
503
void ConfigDialog::useAutoclearPanel(bool useAutoclearPanel) {
×
504
  ui->checkBoxAutoclearPanel->setChecked(useAutoclearPanel);
×
505
  on_checkBoxAutoclearPanel_clicked();
×
506
}
×
507

508
/**
509
 * @brief ConfigDialog::on_checkBoxSelection_clicked checkbox clicked, update
510
 * state via ConfigDialog::on_comboBoxClipboard_activated
511
 */
512
void ConfigDialog::on_checkBoxSelection_clicked() {
×
513
  on_comboBoxClipboard_activated(ui->comboBoxClipboard->currentIndex());
×
514
}
×
515

516
/**
517
 * @brief ConfigDialog::on_checkBoxAutoclear_clicked checkbox clicked, update
518
 * state via ConfigDialog::on_comboBoxClipboard_activated
519
 */
520
void ConfigDialog::on_checkBoxAutoclear_clicked() {
×
521
  on_comboBoxClipboard_activated(ui->comboBoxClipboard->currentIndex());
×
522
}
×
523

524
/**
525
 * @brief ConfigDialog::genKey tunnel function to make MainWindow generate a
526
 * gpg key pair.
527
 * @todo refactor the process to not be entangled so much.
528
 * @param batch
529
 * @param dialog
530
 */
531
void ConfigDialog::genKey(const QString &batch, QDialog *dialog) {
×
532
  mainWindow->generateKeyPair(batch, dialog);
×
533
}
×
534

535
/**
536
 * @brief ConfigDialog::setProfiles set the profiles and chosen profile from
537
 * MainWindow.
538
 * @param profiles
539
 * @param profile
540
 */
541
void ConfigDialog::setProfiles(QHash<QString, QHash<QString, QString>> profiles,
×
542
                               const QString &currentProfile) {
543
  if (profiles.contains("")) {
×
544
    profiles.remove("");
×
545
    // remove weird "" key value pairs
546
  }
547

548
  ui->profileTable->setRowCount(profiles.count());
×
549
  QHashIterator<QString, QHash<QString, QString>> i(profiles);
×
550
  int n = 0;
551
  while (i.hasNext()) {
×
552
    i.next();
553
    if (!i.value().isEmpty() && !i.key().isEmpty()) {
×
554
      ui->profileTable->setItem(n, 0, new QTableWidgetItem(i.key()));
×
555
      ui->profileTable->setItem(n, 1,
×
556
                                new QTableWidgetItem(i.value().value("path")));
×
557
      ui->profileTable->setItem(
×
558
          n, 2, new QTableWidgetItem(i.value().value("signingKey")));
×
559
      if (i.key() == currentProfile) {
×
560
        ui->profileTable->selectRow(n);
×
561
      }
562
    }
563
    ++n;
×
564
  }
565
}
×
566

567
/**
568
 * @brief ConfigDialog::getProfiles return profile list.
569
 * @return
570
 */
571
auto ConfigDialog::getProfiles() -> QHash<QString, QHash<QString, QString>> {
×
572
  QHash<QString, QHash<QString, QString>> profiles;
×
573
  // Check?
574
  for (int i = 0; i < ui->profileTable->rowCount(); ++i) {
×
575
    QHash<QString, QString> profile;
×
576
    QTableWidgetItem *pathItem = ui->profileTable->item(i, 1);
×
577
    if (nullptr != pathItem) {
×
578
      QTableWidgetItem *item = ui->profileTable->item(i, 0);
×
579
      if (item == nullptr) {
×
580
        continue;
581
      }
582
      profile["path"] = pathItem->text();
×
583
      QTableWidgetItem *signingKeyItem = ui->profileTable->item(i, 2);
×
584
      if (nullptr != signingKeyItem) {
×
585
        profile["signingKey"] = signingKeyItem->text();
×
586
      }
587
      profiles.insert(item->text(), profile);
×
588
    }
589
  }
×
590
  return profiles;
×
591
}
×
592

593
/**
594
 * @brief ConfigDialog::on_addButton_clicked add a profile row.
595
 */
596
void ConfigDialog::on_addButton_clicked() {
×
597
  int n = ui->profileTable->rowCount();
×
598
  ui->profileTable->insertRow(n);
×
599
  ui->profileTable->setItem(n, 0, new QTableWidgetItem());
×
600
  ui->profileTable->setItem(n, 1, new QTableWidgetItem(ui->storePath->text()));
×
601
  ui->profileTable->setItem(n, 2, new QTableWidgetItem());
×
602
  ui->profileTable->selectRow(n);
×
603
  ui->deleteButton->setEnabled(true);
×
604

605
  validate();
×
606
}
×
607

608
/**
609
 * @brief ConfigDialog::on_deleteButton_clicked remove a profile row.
610
 */
611
void ConfigDialog::on_deleteButton_clicked() {
×
612
  QSet<int> selectedRows; //  we use a set to prevent doubles
613
  QList<QTableWidgetItem *> itemList = ui->profileTable->selectedItems();
×
614
  if (itemList.count() == 0) {
×
615
    QMessageBox::warning(this, tr("No profile selected"),
×
616
                         tr("No profile selected to delete"));
×
617
    return;
618
  }
619
  QTableWidgetItem *item;
620
  foreach (item, itemList)
×
621
    selectedRows.insert(item->row());
×
622
  // get a list, and sort it big to small
623
  QList<int> rows = selectedRows.values();
×
624
  std::sort(rows.begin(), rows.end());
×
625
  // now actually do the removing:
626
  foreach (int row, rows)
×
627
    ui->profileTable->removeRow(row);
×
628
  if (ui->profileTable->rowCount() < 1) {
×
629
    ui->deleteButton->setEnabled(false);
×
630
  }
631

632
  validate();
×
633
}
634

635
/**
636
 * @brief ConfigDialog::criticalMessage weapper for showing critical messages
637
 * in a popup.
638
 * @param title
639
 * @param text
640
 */
641
void ConfigDialog::criticalMessage(const QString &title, const QString &text) {
×
642
  QMessageBox::critical(this, title, text, QMessageBox::Ok, QMessageBox::Ok);
×
643
}
×
644

645
/**
646
 * @brief Checks whether the qrencode executable is available on the system.
647
 * @example
648
 * bool result = ConfigDialog::isQrencodeAvailable();
649
 * std::cout << result << std::endl; // Expected output: true if qrencode is
650
 * found, otherwise false
651
 *
652
 * @return bool - True if qrencode is available; otherwise false. On Windows,
653
 * always returns false.
654
 */
655
auto ConfigDialog::isQrencodeAvailable() -> bool {
×
656
#ifdef Q_OS_WIN
657
  return false;
658
#else
659
  QProcess which;
×
660
  which.start("which", QStringList() << "qrencode");
×
661
  which.waitForFinished();
×
662
  QtPassSettings::setQrencodeExecutable(
×
663
      which.readAllStandardOutput().trimmed());
×
664
  return which.exitCode() == 0;
×
665
#endif
666
}
×
667

668
auto ConfigDialog::isPassOtpAvailable() -> bool {
×
669
#ifdef Q_OS_WIN
670
  return false;
671
#else
672
  QProcess pass;
×
673
  pass.start(QtPassSettings::getPassExecutable(), QStringList() << "otp"
×
674
                                                                << "--help");
×
675
  pass.waitForFinished(2000);
×
676
  return pass.exitCode() == 0;
×
677
#endif
678
}
×
679

680
/**
681
 * @brief ConfigDialog::wizard first-time use wizard.
682
 * @todo make this thing more reliable.
683
 */
684
void ConfigDialog::wizard() {
×
685
  (void)Util::configIsValid();
×
686
  on_autodetectButton_clicked();
×
687

688
  if (!checkGpgExistence()) {
×
689
    return;
690
  }
691
  if (!checkSecretKeys()) {
×
692
    return;
693
  }
694
  if (!checkPasswordStore()) {
×
695
    return;
696
  }
697
  handleGpgIdFile();
×
698

699
  ui->checkBoxHidePassword->setCheckState(Qt::Checked);
×
700
}
701

702
/**
703
 * @brief Checks whether the configured GnuPG executable exists.
704
 * @example
705
 * bool result = ConfigDialog::checkGpgExistence();
706
 * std::cout << result << std::endl; // Expected output: true if GnuPG is found,
707
 * false otherwise
708
 *
709
 * @return bool - True if the GnuPG path is valid or uses a WSL command prefix;
710
 * false if the executable cannot be found and an error message is shown.
711
 */
712
auto ConfigDialog::checkGpgExistence() -> bool {
×
713
  QString gpg = ui->gpgPath->text();
×
714
  if (!gpg.startsWith("wsl ") && !QFile(gpg).exists()) {
×
715
    criticalMessage(
×
716
        tr("GnuPG not found"),
×
717
#ifdef Q_OS_WIN
718
#ifdef WINSTORE
719
        tr("Please install GnuPG on your system.<br>Install "
720
           "<strong>Ubuntu</strong> from the Microsoft Store to get it.<br>"
721
           "If you already did so, make sure you started it once and<br>"
722
           "click \"Autodetect\" in the next dialog.")
723
#else
724
        tr("Please install GnuPG on your system.<br>Install "
725
           "<strong>Ubuntu</strong> from the Microsoft Store<br>or <a "
726
           "href=\"https://www.gnupg.org/download/#sec-1-2\">download</a> it "
727
           "from GnuPG.org")
728
#endif
729
#else
730
        tr("Please install GnuPG on your system.<br>Install "
×
731
           "<strong>gpg</strong> using your favorite package manager<br>or "
732
           "<a "
733
           "href=\"https://www.gnupg.org/download/#sec-1-2\">download</a> it "
734
           "from GnuPG.org")
735
#endif
736
    );
737
    return false;
×
738
  }
739
  return true;
740
}
741

742
/**
743
 * @brief Checks whether secret keys are available and, if needed, prompts the
744
 * user to generate them.
745
 * @example
746
 * bool result = ConfigDialog.checkSecretKeys();
747
 * std::cout << result << std::endl; // Expected output: true if keys are
748
 * present or key generation dialog is accepted, false otherwise
749
 *
750
 * @return bool - Returns true when secret keys are already available or the key
751
 * generation dialog is accepted; false if the dialog is rejected.
752
 */
753
auto ConfigDialog::checkSecretKeys() -> bool {
×
754
  QString gpg = ui->gpgPath->text();
×
755
  QStringList names = getSecretKeys();
×
756

757
#ifdef QT_DEBUG
758
  dbg() << names;
759
#endif
760

761
  if ((gpg.startsWith("wsl ") || QFile(gpg).exists()) && names.empty()) {
×
762
    KeygenDialog d(this);
×
763
    return d.exec();
×
764
  }
×
765
  return true;
766
}
767

768
/**
769
 * @brief Checks whether the password-store path exists and prompts the user to
770
 * create it if it does not.
771
 * @example
772
 * bool result = ConfigDialog::checkPasswordStore();
773
 * std::cout << std::boolalpha << result << std::endl; // Expected output: true
774
 * if the store exists or is created successfully, false if creation fails
775
 *
776
 * @return bool - True if the password-store exists or is successfully created,
777
 * false if creation fails.
778
 */
779
auto ConfigDialog::checkPasswordStore() -> bool {
×
780
  QString passStore = ui->storePath->text();
×
781

782
  if (!QFile(passStore).exists()) {
×
783
    if (QMessageBox::question(
×
784
            this, tr("Create password-store?"),
×
785
            tr("Would you like to create a password-store at %1?")
×
786
                .arg(passStore),
×
787
            QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
788
      if (!QDir().mkdir(passStore)) {
×
789
        QMessageBox::warning(
×
790
            this, tr("Error"),
×
791
            tr("Failed to create password-store at: %1").arg(passStore));
×
792
        return false;
×
793
      }
794
#ifdef Q_OS_WIN
795
      SetFileAttributes(passStore.toStdWString().c_str(),
796
                        FILE_ATTRIBUTE_HIDDEN);
797
#endif
798
      if (ui->checkBoxUseGit->isChecked()) {
×
799
        emit mainWindow->passGitInitNeeded();
×
800
      }
801
      mainWindow->userDialog(passStore);
×
802
    }
803
  }
804
  return true;
805
}
806

807
/**
808
 * @brief Handles selection and validation of the password store's .gpg-id file.
809
 * @example
810
 * ConfigDialog dialog;
811
 * dialog.handleGpgIdFile();
812
 *
813
 * @return void - This method does not return a value; it updates the UI flow
814
 * and may prompt the user to choose a valid password store.
815
 */
816
void ConfigDialog::handleGpgIdFile() {
×
817
  QString passStore = ui->storePath->text();
×
818
  if (!QFile(QDir(passStore).filePath(".gpg-id")).exists()) {
×
819
#ifdef QT_DEBUG
820
    dbg() << ".gpg-id file does not exist";
821
#endif
822
    criticalMessage(tr("Password store not initialised"),
×
823
                    tr("The folder %1 doesn't seem to be a password store or "
×
824
                       "is not yet initialised.")
825
                        .arg(passStore));
×
826

827
    while (!QFile(passStore).exists()) {
×
828
      on_toolButtonStore_clicked();
×
829
      if (passStore == ui->storePath->text()) {
×
830
        return;
831
      }
832
      passStore = ui->storePath->text();
×
833
    }
834
    if (!QFile(passStore + ".gpg-id").exists()) {
×
835
#ifdef QT_DEBUG
836
      dbg() << ".gpg-id file still does not exist :/";
837
#endif
838
      mainWindow->userDialog(passStore);
×
839
    }
840
  }
841
}
842

843
/**
844
 * @brief ConfigDialog::useTrayIcon set preference for using trayicon.
845
 * Enable or disable related checkboxes accordingly.
846
 * @param useSystray
847
 */
848
void ConfigDialog::useTrayIcon(bool useSystray) {
×
849
  if (QSystemTrayIcon::isSystemTrayAvailable()) {
×
850
    ui->checkBoxUseTrayIcon->setChecked(useSystray);
×
851
    ui->checkBoxHideOnClose->setEnabled(useSystray);
×
852
    ui->checkBoxStartMinimized->setEnabled(useSystray);
×
853

854
    if (!useSystray) {
×
855
      ui->checkBoxHideOnClose->setChecked(false);
×
856
      ui->checkBoxStartMinimized->setChecked(false);
×
857
    }
858
  }
859
}
×
860

861
/**
862
 * @brief ConfigDialog::on_checkBoxUseTrayIcon_clicked enable and disable
863
 * related checkboxes.
864
 */
865
void ConfigDialog::on_checkBoxUseTrayIcon_clicked() {
×
866
  bool state = ui->checkBoxUseTrayIcon->isChecked();
×
867
  ui->checkBoxHideOnClose->setEnabled(state);
×
868
  ui->checkBoxStartMinimized->setEnabled(state);
×
869
}
×
870

871
/**
872
 * @brief ConfigDialog::closeEvent close this window.
873
 * @param event
874
 */
875
void ConfigDialog::closeEvent(QCloseEvent *event) {
×
876
  QtPassSettings::setDialogGeometry("configDialog", saveGeometry());
×
NEW
877
  if (!isMaximized()) {
×
NEW
878
    QtPassSettings::setDialogPos("configDialog", pos());
×
NEW
879
    QtPassSettings::setDialogSize("configDialog", size());
×
880
  }
UNCOV
881
  QtPassSettings::setDialogMaximized("configDialog", isMaximized());
×
882
  event->accept();
883
}
×
884

885
/**
886
 * @brief ConfigDialog::useGit set preference for using git.
887
 * @param useGit
888
 */
889
void ConfigDialog::useGit(bool useGit) {
×
890
  ui->checkBoxUseGit->setChecked(useGit);
×
891
  on_checkBoxUseGit_clicked();
×
892
}
×
893

894
/**
895
 * @brief ConfigDialog::useOtp set preference for using otp plugin.
896
 * @param useOtp
897
 */
898
void ConfigDialog::useOtp(bool useOtp) {
×
899
  ui->checkBoxUseOtp->setChecked(useOtp);
×
900
}
×
901

902
/**
903
 * @brief ConfigDialog::useOtp set preference for using otp plugin.
904
 * @param useOtp
905
 */
906
void ConfigDialog::useQrencode(bool useQrencode) {
×
907
  ui->checkBoxUseQrencode->setChecked(useQrencode);
×
908
}
×
909

910
/**
911
 * @brief ConfigDialog::on_checkBoxUseGit_clicked enable or disable related
912
 * checkboxes.
913
 */
914
void ConfigDialog::on_checkBoxUseGit_clicked() {
×
915
  ui->checkBoxAddGPGId->setEnabled(ui->checkBoxUseGit->isChecked());
×
916
  ui->checkBoxAutoPull->setEnabled(ui->checkBoxUseGit->isChecked());
×
917
  ui->checkBoxAutoPush->setEnabled(ui->checkBoxUseGit->isChecked());
×
918
}
×
919

920
/**
921
 * @brief ConfigDialog::on_toolButtonPwgen_clicked enable or disable related
922
 * options in the interface.
923
 */
924
void ConfigDialog::on_toolButtonPwgen_clicked() {
×
925
  QString pwgen = selectExecutable();
×
926
  if (!pwgen.isEmpty()) {
×
927
    ui->pwgenPath->setText(pwgen);
×
928
    ui->checkBoxUsePwgen->setEnabled(true);
×
929
  } else {
930
    ui->checkBoxUsePwgen->setEnabled(false);
×
931
    ui->checkBoxUsePwgen->setChecked(false);
×
932
  }
933
}
×
934

935
/**
936
 * @brief ConfigDialog::setPwgenPath set pwgen executable path.
937
 * Enable or disable related options in the interface.
938
 * @param pwgen
939
 */
940
void ConfigDialog::setPwgenPath(const QString &pwgen) {
×
941
  ui->pwgenPath->setText(pwgen);
×
942
  if (pwgen.isEmpty()) {
×
943
    ui->checkBoxUsePwgen->setChecked(false);
×
944
    ui->checkBoxUsePwgen->setEnabled(false);
×
945
  }
946
  on_checkBoxUsePwgen_clicked();
×
947
}
×
948

949
/**
950
 * @brief ConfigDialog::on_checkBoxUsPwgen_clicked enable or disable related
951
 * options in the interface.
952
 */
953
void ConfigDialog::on_checkBoxUsePwgen_clicked() {
×
954
  bool usePwgen = ui->checkBoxUsePwgen->isChecked();
×
955
  ui->checkBoxAvoidCapitals->setEnabled(usePwgen);
×
956
  ui->checkBoxAvoidNumbers->setEnabled(usePwgen);
×
957
  ui->checkBoxLessRandom->setEnabled(usePwgen);
×
958
  ui->checkBoxUseSymbols->setEnabled(usePwgen);
×
959
  ui->lineEditPasswordChars->setEnabled(!usePwgen);
×
960
  ui->labelPasswordChars->setEnabled(!usePwgen);
×
961
  ui->passwordCharTemplateSelector->setEnabled(!usePwgen);
×
962
}
×
963

964
/**
965
 * @brief ConfigDialog::usePwgen set preference for using pwgen (can be
966
 * overruled buy empty pwgenPath).
967
 * enable or disable related options in the interface via
968
 * ConfigDialog::on_checkBoxUsePwgen_clicked
969
 * @param usePwgen
970
 */
971
void ConfigDialog::usePwgen(bool usePwgen) {
×
972
  if (ui->pwgenPath->text().isEmpty()) {
×
973
    usePwgen = false;
974
  }
975
  ui->checkBoxUsePwgen->setChecked(usePwgen);
×
976
  on_checkBoxUsePwgen_clicked();
×
977
}
×
978

979
void ConfigDialog::setPasswordConfiguration(
×
980
    const PasswordConfiguration &config) {
981
  ui->spinBoxPasswordLength->setValue(config.length);
×
982
  ui->passwordCharTemplateSelector->setCurrentIndex(config.selected);
×
983
  if (config.selected != PasswordConfiguration::CUSTOM) {
×
984
    ui->lineEditPasswordChars->setEnabled(false);
×
985
  }
986
  ui->lineEditPasswordChars->setText(config.Characters[config.selected]);
×
987
}
×
988

989
auto ConfigDialog::getPasswordConfiguration() -> PasswordConfiguration {
×
990
  PasswordConfiguration config;
×
991
  config.length = ui->spinBoxPasswordLength->value();
×
992
  config.selected = static_cast<PasswordConfiguration::characterSet>(
×
993
      ui->passwordCharTemplateSelector->currentIndex());
×
994
  config.Characters[PasswordConfiguration::CUSTOM] =
995
      ui->lineEditPasswordChars->text();
×
996
  return config;
×
997
}
×
998

999
/**
1000
 * @brief ConfigDialog::on_passwordCharTemplateSelector_activated sets the
1001
 * passwordChar Template
1002
 * combo box to the desired entry
1003
 * @param entry of
1004
 */
1005
void ConfigDialog::on_passwordCharTemplateSelector_activated(int index) {
×
1006
  ui->lineEditPasswordChars->setText(
×
1007
      QtPassSettings::getPasswordConfiguration().Characters[index]);
×
1008
  if (index == 3) {
×
1009
    ui->lineEditPasswordChars->setEnabled(true);
×
1010
  } else {
1011
    ui->lineEditPasswordChars->setEnabled(false);
×
1012
  }
1013
}
×
1014

1015
/**
1016
 * @brief ConfigDialog::on_checkBoxUseTemplate_clicked enable or disable the
1017
 * template field and options.
1018
 */
1019
void ConfigDialog::on_checkBoxUseTemplate_clicked() {
×
1020
  ui->plainTextEditTemplate->setEnabled(ui->checkBoxUseTemplate->isChecked());
×
1021
  ui->checkBoxTemplateAllFields->setEnabled(
×
1022
      ui->checkBoxUseTemplate->isChecked());
×
1023
}
×
1024

1025
void ConfigDialog::onProfileTableItemChanged(QTableWidgetItem *item) {
×
1026
  validate(item);
×
1027
}
×
1028

1029
/**
1030
 * @brief ConfigDialog::useTemplate set preference for using templates.
1031
 * @param useTemplate
1032
 */
1033
void ConfigDialog::useTemplate(bool useTemplate) {
×
1034
  ui->checkBoxUseTemplate->setChecked(useTemplate);
×
1035
  on_checkBoxUseTemplate_clicked();
×
1036
}
×
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