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

IJHack / QtPass / 27504813959

14 Jun 2026 04:19PM UTC coverage: 57.555%. First build
27504813959

Pull #1539

github

web-flow
Merge 573462c63 into 03360931f
Pull Request #1539: refactor: add QtPassSettings::load/save AppSettings facade (#1511)

7 of 9 new or added lines in 2 files covered. (77.78%)

3988 of 6929 relevant lines covered (57.56%)

36.23 hits per line

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

36.1
/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 "appsettings.h"
5
#include "keygendialog.h"
6
#include "mainwindow.h"
7
#include "profileinit.h"
8
#include "qtpasssettings.h"
9
#include "sshauthsock.h"
10
#include "ui_configdialog.h"
11
#include "usersdialog.h"
12
#include "util.h"
13
#include <QClipboard>
14
#include <QDir>
15
#include <QFileDialog>
16
#include <QMessageBox>
17
#include <QPushButton>
18
#include <QSystemTrayIcon>
19
#include <QTableWidgetItem>
20
#include <utility>
21
#ifdef Q_OS_WIN
22
#include <windows.h>
23
#endif
24

25
#ifdef QT_DEBUG
26
#include "debughelper.h"
27
#endif
28

29
/**
30
 * @brief ConfigDialog::ConfigDialog this sets up the configuration screen.
31
 * @param parent
32
 */
33
ConfigDialog::ConfigDialog(MainWindow *parent)
13✔
34
    : QDialog(parent), ui(new Ui::ConfigDialog) {
13✔
35
  mainWindow = parent;
13✔
36
  ui->setupUi(this);
13✔
37

38
  // Restore dialog state
39
  QByteArray savedGeometry = QtPassSettings::getDialogGeometry("configDialog");
26✔
40
  bool hasSavedGeometry = !savedGeometry.isEmpty();
41
  if (hasSavedGeometry) {
13✔
42
    restoreGeometry(savedGeometry);
×
43
  }
44
  if (QtPassSettings::isDialogMaximized("configDialog")) {
26✔
45
    showMaximized();
×
46
  } else if (!hasSavedGeometry) {
47
    // Let window manager handle positioning for first launch
48
  }
49

50
  applySettings(QtPassSettings::load());
13✔
51

52
  if (!QSystemTrayIcon::isSystemTrayAvailable()) {
13✔
53
    ui->checkBoxUseTrayIcon->setEnabled(false);
13✔
54
    ui->checkBoxUseTrayIcon->setToolTip(tr("System tray is not available"));
26✔
55
    ui->checkBoxHideOnClose->setEnabled(false);
13✔
56
    ui->checkBoxStartMinimized->setEnabled(false);
13✔
57
  }
58

59
#if defined(Q_OS_WIN)
60
  ui->checkBoxUseOtp->hide();
61
  ui->checkBoxUseQrencode->hide();
62
  ui->label_10->hide();
63
#endif
64

65
  if (!isPassOtpAvailable()) {
13✔
66
    ui->checkBoxUseOtp->setEnabled(false);
13✔
67
    ui->checkBoxUseOtp->setToolTip(
13✔
68
        tr("Pass OTP extension needs to be installed"));
13✔
69
  }
70

71
  if (!isQrencodeAvailable()) {
13✔
72
    ui->checkBoxUseQrencode->setEnabled(false);
13✔
73
    ui->checkBoxUseQrencode->setToolTip(tr("qrencode needs to be installed"));
26✔
74
  }
75

76
  setProfiles(QtPassSettings::getProfiles(), QtPassSettings::getProfile());
26✔
77

78
  ui->profileTable->verticalHeader()->hide();
13✔
79
  ui->profileTable->horizontalHeader()->setSectionResizeMode(
13✔
80
      1, QHeaderView::Stretch);
81
  ui->label->setText(ui->label->text() + VERSION);
26✔
82
  ui->comboBoxClipboard->clear();
13✔
83

84
  ui->comboBoxClipboard->addItem(tr("No Clipboard"));
26✔
85
  ui->comboBoxClipboard->addItem(tr("Always copy to clipboard"));
26✔
86
  ui->comboBoxClipboard->addItem(tr("On-demand copy to clipboard"));
26✔
87

88
  int currentIndex = QtPassSettings::getClipBoardTypeRaw();
13✔
89
  ui->comboBoxClipboard->setCurrentIndex(currentIndex);
13✔
90
  on_comboBoxClipboard_activated(currentIndex);
13✔
91

92
  QClipboard *clip = QApplication::clipboard();
13✔
93
  if (!clip->supportsSelection()) {
13✔
94
    useSelection(false);
13✔
95
    ui->checkBoxSelection->setVisible(false);
13✔
96
  } else {
97
    useSelection(QtPassSettings::isUseSelection());
×
98
  }
99

100
  if (!Util::configIsValid()) {
13✔
101
    // Show Programs tab, which is likely
102
    // what the user needs to fix now.
103
    ui->tabWidget->setCurrentIndex(1);
13✔
104
  }
105

106
  connect(ui->profileTable, &QTableWidget::itemChanged, this,
13✔
107
          &ConfigDialog::onProfileTableItemChanged);
13✔
108
  connect(ui->profileTable, &QTableWidget::itemSelectionChanged, this,
13✔
109
          &ConfigDialog::onProfileTableSelectionChanged);
13✔
110
  connect(this, &ConfigDialog::accepted, this, &ConfigDialog::on_accepted);
13✔
111
}
13✔
112

113
void ConfigDialog::applySettings(const AppSettings &settings) {
13✔
114
  ui->passPath->setText(settings.passExecutable);
13✔
115
  setGitPath(settings.gitExecutable);
13✔
116
  ui->gpgPath->setText(settings.gpgExecutable);
13✔
117
  ui->storePath->setText(settings.passStore);
13✔
118
  ui->sshAuthSockOverride->setText(settings.sshAuthSockOverride);
13✔
119

120
  ui->spinBoxAutoclearSeconds->setValue(settings.autoclearSeconds);
13✔
121
  ui->spinBoxAutoclearPanelSeconds->setValue(settings.autoclearPanelSeconds);
13✔
122
  ui->checkBoxHidePassword->setChecked(settings.hidePassword);
13✔
123
  ui->checkBoxHideContent->setChecked(settings.hideContent);
13✔
124
  ui->checkBoxUseMonospace->setChecked(settings.useMonospace);
13✔
125
  ui->checkBoxDisplayAsIs->setChecked(settings.displayAsIs);
13✔
126
  ui->checkBoxNoLineWrapping->setChecked(settings.noLineWrapping);
13✔
127
  ui->checkBoxAddGPGId->setChecked(settings.addGPGId);
13✔
128
  ui->checkBoxHideOnClose->setChecked(settings.hideOnClose);
13✔
129
  ui->checkBoxStartMinimized->setChecked(settings.startMinimized);
13✔
130

131
  ui->checkBoxAvoidCapitals->setChecked(settings.avoidCapitals);
13✔
132
  ui->checkBoxAvoidNumbers->setChecked(settings.avoidNumbers);
13✔
133
  ui->checkBoxLessRandom->setChecked(settings.lessRandom);
13✔
134
  ui->checkBoxUseSymbols->setChecked(settings.useSymbols);
13✔
135
  ui->plainTextEditTemplate->setPlainText(settings.passTemplate);
13✔
136
  ui->checkBoxTemplateAllFields->setChecked(settings.templateAllFields);
13✔
137
  ui->checkBoxShowProcessOutput->setChecked(settings.showProcessOutput);
13✔
138
  ui->checkBoxAutoPull->setChecked(settings.autoPull);
13✔
139
  ui->checkBoxAutoPush->setChecked(settings.autoPush);
13✔
140
  ui->checkBoxAlwaysOnTop->setChecked(settings.alwaysOnTop);
13✔
141

142
  // Dependent helpers: set after the plain values above so the enable/disable
143
  // logic they trigger sees the final widget states.
144
  usePass(settings.usePass);
13✔
145
  useAutoclear(settings.useAutoclear);
13✔
146
  useAutoclearPanel(settings.useAutoclearPanel);
13✔
147
  useTrayIcon(settings.useTrayIcon);
13✔
148
  useGit(settings.useGit);
13✔
149
  useOtp(settings.useOtp);
13✔
150
  useGrepSearch(settings.useGrepSearch);
13✔
151
  useQrencode(settings.useQrencode);
13✔
152
  usePwgen(settings.usePwgen);
13✔
153
  useTemplate(settings.useTemplate);
13✔
154
}
13✔
155

156
auto ConfigDialog::readSettings() -> AppSettings {
×
157
  // Start from the persisted settings so keys this dialog does not own
158
  // (window geometry, WebDAV, profiles, etc.) survive the save.
NEW
159
  AppSettings settings = QtPassSettings::load();
×
160

161
  settings.passExecutable = ui->passPath->text();
×
162
  settings.gitExecutable = ui->gitPath->text();
×
163
  settings.gpgExecutable = ui->gpgPath->text();
×
164
  settings.sshAuthSockOverride = ui->sshAuthSockOverride->text().trimmed();
×
165
  settings.passStore = Util::normalizeFolderPath(ui->storePath->text());
×
166
  settings.usePass = ui->radioButtonPass->isChecked();
×
167
  settings.clipBoardType =
×
168
      static_cast<Enums::clipBoardType>(ui->comboBoxClipboard->currentIndex());
×
169
  settings.useSelection = ui->checkBoxSelection->isChecked();
×
170
  settings.useAutoclear = ui->checkBoxAutoclear->isChecked();
×
171
  settings.autoclearSeconds = ui->spinBoxAutoclearSeconds->value();
×
172
  settings.useAutoclearPanel = ui->checkBoxAutoclearPanel->isChecked();
×
173
  settings.autoclearPanelSeconds = ui->spinBoxAutoclearPanelSeconds->value();
×
174
  settings.hidePassword = ui->checkBoxHidePassword->isChecked();
×
175
  settings.hideContent = ui->checkBoxHideContent->isChecked();
×
176
  settings.useMonospace = ui->checkBoxUseMonospace->isChecked();
×
177
  settings.displayAsIs = ui->checkBoxDisplayAsIs->isChecked();
×
178
  settings.noLineWrapping = ui->checkBoxNoLineWrapping->isChecked();
×
179
  settings.addGPGId = ui->checkBoxAddGPGId->isChecked();
×
180
  // Only overwrite these environment-gated preferences when their control is
181
  // enabled. When disabled (e.g. no system tray available) keep the persisted
182
  // value loaded above rather than clobbering it to false, so the preference
183
  // survives until the user can change it on a capable environment.
184
  if (ui->checkBoxUseTrayIcon->isEnabled()) {
×
185
    settings.useTrayIcon = ui->checkBoxUseTrayIcon->isChecked();
×
186
  }
187
  if (ui->checkBoxHideOnClose->isEnabled()) {
×
188
    settings.hideOnClose = ui->checkBoxHideOnClose->isChecked();
×
189
  }
190
  if (ui->checkBoxStartMinimized->isEnabled()) {
×
191
    settings.startMinimized = ui->checkBoxStartMinimized->isChecked();
×
192
  }
193
  settings.useGit = ui->checkBoxUseGit->isChecked();
×
194
  settings.useOtp = ui->checkBoxUseOtp->isChecked();
×
195
  settings.useGrepSearch = ui->checkBoxUseGrepSearch->isChecked();
×
196
  settings.useQrencode = ui->checkBoxUseQrencode->isChecked();
×
197
  settings.pwgenExecutable = ui->pwgenPath->text();
×
198
  settings.usePwgen = ui->checkBoxUsePwgen->isChecked();
×
199
  settings.avoidCapitals = ui->checkBoxAvoidCapitals->isChecked();
×
200
  settings.avoidNumbers = ui->checkBoxAvoidNumbers->isChecked();
×
201
  settings.lessRandom = ui->checkBoxLessRandom->isChecked();
×
202
  settings.useSymbols = ui->checkBoxUseSymbols->isChecked();
×
203
  settings.passwordConfiguration = getPasswordConfiguration();
×
204
  settings.useTemplate = ui->checkBoxUseTemplate->isChecked();
×
205
  settings.passTemplate = ui->plainTextEditTemplate->toPlainText();
×
206
  settings.templateAllFields = ui->checkBoxTemplateAllFields->isChecked();
×
207
  settings.showProcessOutput = ui->checkBoxShowProcessOutput->isChecked();
×
208
  settings.alwaysOnTop = ui->checkBoxAlwaysOnTop->isChecked();
×
209
  settings.version = VERSION;
×
210

211
  return settings;
×
212
}
×
213

214
/**
215
 * @brief ConfigDialog::~ConfigDialog config destructor, makes sure the
216
 * mainWindow knows about git, gpg and pass executables.
217
 */
218
ConfigDialog::~ConfigDialog() {
13✔
219
  QtPassSettings::setGitExecutable(ui->gitPath->text());
26✔
220
  QtPassSettings::setGpgExecutable(ui->gpgPath->text());
26✔
221
  QtPassSettings::setPassExecutable(ui->passPath->text());
13✔
222
}
13✔
223

224
/**
225
 * @brief ConfigDialog::setGitPath set the git executable path.
226
 * Make sure the checkBoxUseGit is updated.
227
 * @param path
228
 */
229
void ConfigDialog::setGitPath(const QString &path) {
13✔
230
  ui->gitPath->setText(path);
13✔
231
  ui->checkBoxUseGit->setEnabled(!path.isEmpty());
13✔
232
  if (path.isEmpty()) {
13✔
233
    useGit(false);
×
234
  }
235
}
13✔
236

237
/**
238
 * @brief ConfigDialog::usePass set whether or not we want to use pass.
239
 * Update radio buttons accordingly.
240
 * @param usePass
241
 */
242
void ConfigDialog::usePass(bool usePass) {
13✔
243
  ui->radioButtonNative->setChecked(!usePass);
13✔
244
  ui->radioButtonPass->setChecked(usePass);
13✔
245
  setGroupBoxState();
13✔
246
}
13✔
247

248
/**
249
 * @brief Validates the configuration table fields and enables or disables the
250
 * OK button accordingly.
251
 * @example
252
 * ConfigDialog dialog;
253
 * QTableWidgetItem *item = dialog.findChild<QTableWidgetItem*>();
254
 * dialog.validate(item);
255
 *
256
 * @param QTableWidgetItem *item - The table item to validate; if null,
257
 * validates all relevant items in the profile table.
258
 * @return void - This function does not return a value.
259
 */
260
void ConfigDialog::validate(QTableWidgetItem *item) {
×
261
  bool status = true;
262

263
  if (item == nullptr) {
×
264
    for (int i = 0; i < ui->profileTable->rowCount(); i++) {
×
265
      for (int j = 0; j < ui->profileTable->columnCount(); j++) {
×
266
        QTableWidgetItem *_item = ui->profileTable->item(i, j);
×
267

268
        if (_item->text().isEmpty() && j != 2) {
×
269
          _item->setBackground(Qt::red);
×
270
          _item->setToolTip(tr("This field is required"));
×
271
          status = false;
272
          break;
273
        } else {
274
          _item->setBackground(QBrush());
×
275
          _item->setToolTip(QString());
×
276
        }
277
      }
278

279
      if (!status) {
280
        break;
281
      }
282
    }
283
  } else {
284
    if (item->text().isEmpty() && item->column() != 2) {
×
285
      item->setBackground(Qt::red);
×
286
      item->setToolTip(tr("This field is required"));
×
287
      status = false;
288
    } else {
289
      item->setBackground(QBrush());
×
290
      item->setToolTip(QString());
×
291
    }
292
  }
293

294
  ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(status);
×
295
}
×
296

297
/**
298
 * @brief Saves the configuration dialog settings to persistent application
299
 * settings.
300
 * @example
301
 * ConfigDialog dialog;
302
 * dialog.on_accepted();
303
 * // Expected output: All UI-selected configuration values are stored via
304
 * QtPassSettings.
305
 *
306
 * @return void - This method does not return a value.
307
 */
308
void ConfigDialog::on_accepted() {
×
309
  const QString sshAuthSockOverride = ui->sshAuthSockOverride->text().trimmed();
×
310
  if (!sshAuthSockOverride.isEmpty()) {
×
311
    QString reason;
×
312
    switch (SshAuthSock::overrideStatus(sshAuthSockOverride)) {
×
313
    case SshAuthSock::OverrideStatus::Valid:
314
      break;
315
    case SshAuthSock::OverrideStatus::DoesNotExist:
×
316
      reason = tr("The path does not exist.");
×
317
      break;
×
318
    case SshAuthSock::OverrideStatus::NotReadable:
×
319
      reason = tr("The path is not readable.");
×
320
      break;
×
321
    case SshAuthSock::OverrideStatus::NotUnixDomainSocket:
×
322
      reason = tr("The path is not a Unix domain socket.");
×
323
      break;
×
324
    }
325
    if (!reason.isEmpty()) {
×
326
      QMessageBox::warning(
×
327
          this, tr("Potentially invalid SSH_AUTH_SOCK override"),
×
328
          tr("The SSH_AUTH_SOCK override value may be invalid.\n\n%1\n\n"
×
329
             "The value will still be saved as entered.")
330
              .arg(reason));
×
331
    }
332
  }
333

334
  const QHash<QString, QHash<QString, QString>> existingProfiles =
335
      QtPassSettings::getProfiles();
×
336

337
  // Persist via the facade, which also invalidates the cached Pass backend so
338
  // a changed "use pass" mode takes effect.
NEW
339
  QtPassSettings::save(readSettings());
×
340

341
  // Profiles are not part of AppSettings yet, so persist them separately.
342
  QtPassSettings::setProfiles(getProfiles());
×
343

344
  // Initialize new profiles that need pass/git initialization
345
  initializeNewProfiles(existingProfiles);
×
346
}
×
347

348
/**
349
 * @brief Automatically detects required external binaries in the system PATH
350
 * and updates the dialog fields.
351
 * @example
352
 * ConfigDialog configDialog;
353
 * configDialog.on_autodetectButton_clicked();
354
 *
355
 * @return void - This function does not return a value.
356
 */
357
void ConfigDialog::on_autodetectButton_clicked() {
×
358
  QString pass = Util::findBinaryInPath("pass");
×
359
  if (!pass.isEmpty()) {
×
360
    ui->passPath->setText(pass);
×
361
  }
362
  usePass(!pass.isEmpty());
×
363
  QString gpg = Util::findBinaryInPath("gpg2");
×
364
  if (gpg.isEmpty()) {
×
365
    gpg = Util::findBinaryInPath("gpg");
×
366
  }
367
  if (!gpg.isEmpty()) {
×
368
    ui->gpgPath->setText(gpg);
×
369
  }
370
  QString git = Util::findBinaryInPath("git");
×
371
  if (!git.isEmpty()) {
×
372
    ui->gitPath->setText(git);
×
373
  }
374
  QString pwgen = Util::findBinaryInPath("pwgen");
×
375
  if (!pwgen.isEmpty()) {
×
376
    ui->pwgenPath->setText(pwgen);
×
377
  }
378
}
×
379

380
/**
381
 * @brief ConfigDialog::on_radioButtonNative_clicked wrapper for
382
 * ConfigDialog::setGroupBoxState()
383
 */
384
void ConfigDialog::on_radioButtonNative_clicked() { setGroupBoxState(); }
×
385

386
/**
387
 * @brief ConfigDialog::on_radioButtonPass_clicked wrapper for
388
 * ConfigDialog::setGroupBoxState()
389
 */
390
void ConfigDialog::on_radioButtonPass_clicked() { setGroupBoxState(); }
×
391

392
/**
393
 * @brief ConfigDialog::getSecretKeys get list of secret/private keys
394
 * @return QStringList keys
395
 */
396
auto ConfigDialog::getSecretKeys() -> QStringList {
×
397
  QList<UserInfo> keys = QtPassSettings::getPass()->listKeys("", true);
×
398
  QStringList names;
×
399

400
  if (keys.empty()) {
×
401
    return names;
402
  }
403

404
  foreach (const UserInfo &sec, keys)
×
405
    names << sec.name;
×
406

407
  return names;
×
408
}
409

410
/**
411
 * @brief ConfigDialog::setGroupBoxState update checkboxes.
412
 */
413
void ConfigDialog::setGroupBoxState() {
13✔
414
  bool state = ui->radioButtonPass->isChecked();
13✔
415
  ui->groupBoxNative->setEnabled(!state);
13✔
416
  ui->groupBoxPass->setEnabled(state);
13✔
417
  if (state) {
13✔
418
    // pass mode: disable all password generation controls
419
    ui->spinBoxPasswordLength->setEnabled(false);
×
420
    ui->checkBoxUsePwgen->setEnabled(false);
×
421
    ui->checkBoxAvoidCapitals->setEnabled(false);
×
422
    ui->checkBoxUseSymbols->setEnabled(false);
×
423
    ui->checkBoxLessRandom->setEnabled(false);
×
424
    ui->checkBoxAvoidNumbers->setEnabled(false);
×
425
    ui->labelPasswordChars->setEnabled(false);
×
426
    ui->passwordCharTemplateSelector->setEnabled(false);
×
427
    ui->lineEditPasswordChars->setEnabled(false);
×
428
  } else {
429
    // native mode: restore pwgen/charset state from existing handlers
430
    ui->spinBoxPasswordLength->setEnabled(true);
13✔
431
    ui->checkBoxUsePwgen->setEnabled(!ui->pwgenPath->text().isEmpty());
13✔
432
    on_checkBoxUsePwgen_clicked();
13✔
433
  }
434
}
13✔
435

436
/**
437
 * @brief ConfigDialog::selectExecutable pop-up to choose an executable.
438
 * @return
439
 */
440
auto ConfigDialog::selectExecutable() -> QString {
×
441
  QFileDialog dialog(this);
×
442
  dialog.setFileMode(QFileDialog::ExistingFile);
×
443
  dialog.setOption(QFileDialog::ReadOnly);
×
444
  if (dialog.exec()) {
×
445
    return dialog.selectedFiles().constFirst();
×
446
  }
447

448
  return {};
449
}
×
450

451
/**
452
 * @brief ConfigDialog::selectFolder pop-up to choose a folder.
453
 * @return
454
 */
455
auto ConfigDialog::selectFolder() -> QString {
×
456
  QFileDialog dialog(this);
×
457
  dialog.setFileMode(QFileDialog::Directory);
×
458
  dialog.setFilter(QDir::NoFilter);
×
459
  dialog.setOption(QFileDialog::ShowDirsOnly);
×
460
  if (dialog.exec()) {
×
461
    return dialog.selectedFiles().constFirst();
×
462
  }
463

464
  return {};
465
}
×
466

467
/**
468
 * @brief ConfigDialog::on_toolButtonGit_clicked get git application.
469
 * Enable checkboxes if found.
470
 */
471
void ConfigDialog::on_toolButtonGit_clicked() {
×
472
  QString git = selectExecutable();
×
473
  bool state = !git.isEmpty();
×
474
  if (state) {
×
475
    ui->gitPath->setText(git);
×
476
  } else {
477
    useGit(false);
×
478
  }
479

480
  ui->checkBoxUseGit->setEnabled(state);
×
481
}
×
482

483
/**
484
 * @brief ConfigDialog::on_toolButtonGpg_clicked get gpg application.
485
 */
486
void ConfigDialog::on_toolButtonGpg_clicked() {
×
487
  QString gpg = selectExecutable();
×
488
  if (!gpg.isEmpty()) {
×
489
    ui->gpgPath->setText(gpg);
×
490
  }
491
}
×
492

493
/**
494
 * @brief ConfigDialog::on_pushButtonGenerateKey_clicked open keygen dialog.
495
 */
496
void ConfigDialog::on_pushButtonGenerateKey_clicked() {
×
497
  KeygenDialog d(this);
×
498
  d.exec();
×
499
}
×
500

501
/**
502
 * @brief ConfigDialog::on_toolButtonPass_clicked get pass application.
503
 */
504
void ConfigDialog::on_toolButtonPass_clicked() {
×
505
  QString pass = selectExecutable();
×
506
  if (!pass.isEmpty()) {
×
507
    ui->passPath->setText(pass);
×
508
  }
509
}
×
510

511
/**
512
 * @brief ConfigDialog::on_toolButtonStore_clicked get .password-store
513
 * location.
514
 */
515
void ConfigDialog::on_toolButtonStore_clicked() {
×
516
  QString store = selectFolder();
×
517
  if (!store.isEmpty()) {
×
518
    ui->storePath->setText(store);
×
519
  }
520
}
×
521

522
/**
523
 * @brief ConfigDialog::on_comboBoxClipboard_activated show and hide options.
524
 * @param index of selectbox (0 = no clipboard).
525
 */
526
void ConfigDialog::on_comboBoxClipboard_activated(int index) {
43✔
527
  bool state = index > 0;
43✔
528

529
  ui->checkBoxSelection->setEnabled(state);
43✔
530
  ui->checkBoxAutoclear->setEnabled(state);
43✔
531
  ui->checkBoxHidePassword->setEnabled(state);
43✔
532
  ui->checkBoxHideContent->setEnabled(state);
43✔
533
  if (state) {
43✔
534
    ui->spinBoxAutoclearSeconds->setEnabled(ui->checkBoxAutoclear->isChecked());
30✔
535
    ui->labelSeconds->setEnabled(ui->checkBoxAutoclear->isChecked());
30✔
536
  } else {
537
    ui->spinBoxAutoclearSeconds->setEnabled(false);
13✔
538
    ui->labelSeconds->setEnabled(false);
13✔
539
  }
540
}
43✔
541

542
/**
543
 * @brief ConfigDialog::on_checkBoxAutoclearPanel_clicked enable and disable
544
 * options based on autoclear use.
545
 */
546
void ConfigDialog::on_checkBoxAutoclearPanel_clicked() {
15✔
547
  bool state = ui->checkBoxAutoclearPanel->isChecked();
15✔
548
  ui->spinBoxAutoclearPanelSeconds->setEnabled(state);
15✔
549
  ui->labelPanelSeconds->setEnabled(state);
15✔
550
}
15✔
551

552
/**
553
 * @brief ConfigDialog::useSelection set the clipboard type use from
554
 * MainWindow.
555
 * @param useSelection
556
 */
557
void ConfigDialog::useSelection(bool useSelection) {
15✔
558
  ui->checkBoxSelection->setChecked(useSelection);
15✔
559
  on_checkBoxSelection_clicked();
15✔
560
}
15✔
561

562
/**
563
 * @brief ConfigDialog::useAutoclear set the clipboard autoclear use from
564
 * MainWindow.
565
 * @param useAutoclear
566
 */
567
void ConfigDialog::useAutoclear(bool useAutoclear) {
15✔
568
  ui->checkBoxAutoclear->setChecked(useAutoclear);
15✔
569
  on_checkBoxAutoclear_clicked();
15✔
570
}
15✔
571

572
/**
573
 * @brief ConfigDialog::useAutoclearPanel set the panel autoclear use from
574
 * MainWindow.
575
 * @param useAutoclearPanel
576
 */
577
void ConfigDialog::useAutoclearPanel(bool useAutoclearPanel) {
15✔
578
  ui->checkBoxAutoclearPanel->setChecked(useAutoclearPanel);
15✔
579
  on_checkBoxAutoclearPanel_clicked();
15✔
580
}
15✔
581

582
/**
583
 * @brief ConfigDialog::on_checkBoxSelection_clicked checkbox clicked, update
584
 * state via ConfigDialog::on_comboBoxClipboard_activated
585
 */
586
void ConfigDialog::on_checkBoxSelection_clicked() {
15✔
587
  on_comboBoxClipboard_activated(ui->comboBoxClipboard->currentIndex());
15✔
588
}
15✔
589

590
/**
591
 * @brief ConfigDialog::on_checkBoxAutoclear_clicked checkbox clicked, update
592
 * state via ConfigDialog::on_comboBoxClipboard_activated
593
 */
594
void ConfigDialog::on_checkBoxAutoclear_clicked() {
15✔
595
  on_comboBoxClipboard_activated(ui->comboBoxClipboard->currentIndex());
15✔
596
}
15✔
597

598
/**
599
 * @brief ConfigDialog::genKey tunnel function to make MainWindow generate a
600
 * gpg key pair.
601
 * @param batch
602
 * @param dialog
603
 */
604
void ConfigDialog::genKey(const QString &batch, QDialog *dialog) {
×
605
  mainWindow->generateKeyPair(batch, dialog);
×
606
}
×
607

608
/**
609
 * @brief ConfigDialog::setProfiles set the profiles and chosen profile from
610
 * MainWindow.
611
 * @param profiles
612
 * @param profile
613
 */
614
void ConfigDialog::setProfiles(QHash<QString, QHash<QString, QString>> profiles,
13✔
615
                               const QString &currentProfile) {
616
  if (profiles.contains("")) {
13✔
617
    profiles.remove("");
×
618
    // remove weird "" key value pairs
619
  }
620

621
  // Cache profiles for use in onProfileTableSelectionChanged
622
  m_profiles = profiles;
13✔
623

624
  ui->profileTable->setRowCount(static_cast<int>(profiles.count()));
26✔
625
  QHashIterator<QString, QHash<QString, QString>> i(profiles);
13✔
626
  int n = 0;
627
  while (i.hasNext()) {
13✔
628
    i.next();
629
    if (!i.value().isEmpty() && !i.key().isEmpty()) {
13✔
630
      ui->profileTable->setItem(n, 0, new QTableWidgetItem(i.key()));
13✔
631
      ui->profileTable->setItem(n, 1,
13✔
632
                                new QTableWidgetItem(i.value().value("path")));
26✔
633
      ui->profileTable->setItem(
13✔
634
          n, 2, new QTableWidgetItem(i.value().value("signingKey")));
26✔
635
      if (i.key() == currentProfile) {
13✔
636
        ui->profileTable->selectRow(n);
×
637
        // Load git settings for current profile
638
        loadGitSettingsForProfile(currentProfile, m_profiles);
×
639
      }
640
    }
641
    ++n;
13✔
642
  }
643
}
13✔
644

645
/**
646
 * @brief Load git settings for a specific profile.
647
 * @param profileName The profile name.
648
 * @param profiles The profiles hash containing git settings.
649
 */
650
void ConfigDialog::loadGitSettingsForProfile(
×
651
    const QString &profileName,
652
    const QHash<QString, QHash<QString, QString>> &profiles) {
653
  if (profiles.contains(profileName)) {
×
654
    const QHash<QString, QString> &profile = profiles.value(profileName);
×
655
    QString useGitStr = profile.value("useGit");
×
656
    QString autoPushStr = profile.value("autoPush");
×
657
    QString autoPullStr = profile.value("autoPull");
×
658

659
    // Load profile-specific git settings if set, otherwise use global settings
660
    if (!useGitStr.isEmpty()) {
×
661
      useGit(useGitStr == "true");
×
662
      ui->checkBoxAutoPush->setEnabled(ui->checkBoxUseGit->isChecked());
×
663
      ui->checkBoxAutoPull->setEnabled(ui->checkBoxUseGit->isChecked());
×
664
      if (autoPushStr == "true" || autoPushStr == "false") {
×
665
        ui->checkBoxAutoPush->setChecked(autoPushStr == "true");
×
666
      }
667
      if (autoPullStr == "true" || autoPullStr == "false") {
×
668
        ui->checkBoxAutoPull->setChecked(autoPullStr == "true");
×
669
      }
670
    }
671
    // If not set (empty), leave global settings as-is for migration
672
  }
×
673
}
×
674

675
/**
676
 * @brief ConfigDialog::getProfiles return profile list.
677
 * @return
678
 */
679
auto ConfigDialog::getProfiles() -> QHash<QString, QHash<QString, QString>> {
×
680
  // Get currently selected profile name
681
  QList<QTableWidgetItem *> selected = ui->profileTable->selectedItems();
×
682
  QString selectedProfile;
×
683
  if (!selected.isEmpty()) {
×
684
    selectedProfile =
685
        ui->profileTable->item(selected.first()->row(), 0)->text();
×
686
  }
687

688
  // Use cached m_profiles to preserve git settings for non-selected profiles
689
  QHash<QString, QHash<QString, QString>> existingProfiles = m_profiles;
690

691
  QHash<QString, QHash<QString, QString>> profiles;
×
692
  // Check?
693
  for (int i = 0; i < ui->profileTable->rowCount(); ++i) {
×
694
    QHash<QString, QString> profile;
×
695
    QTableWidgetItem *pathItem = ui->profileTable->item(i, 1);
×
696
    if (nullptr != pathItem) {
×
697
      QTableWidgetItem *item = ui->profileTable->item(i, 0);
×
698
      if (item == nullptr) {
×
699
        continue;
700
      }
701
      profile["path"] = pathItem->text();
×
702
      QTableWidgetItem *signingKeyItem = ui->profileTable->item(i, 2);
×
703
      if (nullptr != signingKeyItem) {
×
704
        profile["signingKey"] = signingKeyItem->text();
×
705
      }
706

707
      // Only update git settings for the currently selected profile
708
      // Preserve existing git settings for other profiles
709
      if (item->text() == selectedProfile) {
×
710
        profile["useGit"] = ui->checkBoxUseGit->isChecked() ? "true" : "false";
×
711
        profile["autoPush"] =
×
712
            ui->checkBoxAutoPush->isChecked() ? "true" : "false";
×
713
        profile["autoPull"] =
×
714
            ui->checkBoxAutoPull->isChecked() ? "true" : "false";
×
715
      } else if (existingProfiles.contains(item->text())) {
×
716
        // Preserve existing git settings for non-selected profiles
717
        const QHash<QString, QString> &existing =
718
            existingProfiles.value(item->text());
×
719
        if (existing.contains("useGit")) {
×
720
          profile["useGit"] = existing.value("useGit");
×
721
        }
722
        if (existing.contains("autoPush")) {
×
723
          profile["autoPush"] = existing.value("autoPush");
×
724
        }
725
        if (existing.contains("autoPull")) {
×
726
          profile["autoPull"] = existing.value("autoPull");
×
727
        }
728
      }
×
729
      profiles.insert(item->text(), profile);
×
730
    }
731
  }
×
732
  // Update cache with current in-dialog state
733
  m_profiles = profiles;
×
734
  return profiles;
×
735
}
×
736

737
/**
738
 * @brief Initialize new profiles that need pass/git initialization.
739
 * @param existingProfiles The profiles that existed before the dialog was
740
 * opened.
741
 */
742
void ConfigDialog::initializeNewProfiles(
×
743
    const QHash<QString, QHash<QString, QString>> &existingProfiles) {
744
  QHash<QString, QHash<QString, QString>> newProfiles = getProfiles();
×
745

746
  // Collect keys and sort for deterministic iteration
747
  QStringList keys = newProfiles.keys();
×
748
  keys.sort();
749

750
  for (const QString &name : keys) {
×
751
    const QString &path = newProfiles.value(name).value("path");
×
752

753
    // Skip if already existed before (check by name and path)
754
    if (existingProfiles.contains(name) &&
×
755
        existingProfiles.value(name).value("path") == path) {
×
756
      continue;
×
757
    }
758

759
    // This is a new profile - create directory if needed and initialize
760
    // Note: needsInit returns false for non-existent directories, so we
761
    // must create the directory first.
762
    QString cleanPath = QDir::cleanPath(path);
×
763
    QDir dir(cleanPath);
×
764
    if (!dir.exists()) {
×
765
      if (QMessageBox::question(
×
766
              this, tr("Create profile directory?"),
×
767
              tr("Would you like to create a password store at %1?")
×
768
                  .arg(cleanPath),
×
769
              QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) {
770
        continue;
×
771
      }
772
      if (!QDir().mkpath(cleanPath)) {
×
773
        QMessageBox::warning(
×
774
            this, tr("Error"),
×
775
            tr("Could not create profile directory: %1").arg(cleanPath));
×
776
        continue;
×
777
      }
778
    }
779

780
    // Now check if initialization is needed (directory exists with no .gpg-id)
781
    if (!ProfileInit::needsInit(cleanPath)) {
×
782
      continue;
×
783
    }
784

785
    // Temporarily switch the active store so pass/git init operate on
786
    // the new profile's directory rather than the currently-saved one.
787
    const QString prevStore = QtPassSettings::getPassStore();
×
788
    QtPassSettings::setPassStore(cleanPath);
×
789

790
    // Show user selection dialog for GPG recipients
791
    // UsersDialog will run pass init when accepted
792
    UsersDialog usersDialog(cleanPath, this);
×
793
    usersDialog.setWindowTitle(tr("Select recipients for %1").arg(name));
×
794
    const int result = usersDialog.exec();
×
795

796
    // Use per-profile useGit setting, falling back to global if not set
797
    QString useGitStr = newProfiles.value(name).value("useGit");
×
798
    bool useGit =
799
        useGitStr.isEmpty() ? QtPassSettings::isUseGit() : useGitStr == "true";
×
800

801
    if (result == QDialog::Accepted && useGit) {
×
802
      QtPassSettings::getPass()->GitInit();
×
803
    }
804

805
    // Restore previous store setting
806
    QtPassSettings::setPassStore(prevStore);
×
807
  }
×
808
}
×
809

810
/**
811
 * @brief ConfigDialog::on_addButton_clicked add a profile row.
812
 */
813
void ConfigDialog::on_addButton_clicked() {
×
814
  bool sortingEnabled = ui->profileTable->isSortingEnabled();
×
815
  ui->profileTable->setSortingEnabled(false);
×
816

817
  int n = ui->profileTable->rowCount();
×
818
  ui->profileTable->insertRow(n);
×
819
  ui->profileTable->setItem(n, 0, new QTableWidgetItem(tr("New Profile")));
×
820
  ui->profileTable->setItem(n, 1, new QTableWidgetItem(ui->storePath->text()));
×
821
  ui->profileTable->setItem(n, 2, new QTableWidgetItem());
×
822

823
  ui->profileTable->setSortingEnabled(sortingEnabled);
×
824

825
  int currentRow = ui->profileTable->row(ui->profileTable->item(n, 0));
×
826
  ui->profileTable->selectRow(currentRow);
×
827
  ui->deleteButton->setEnabled(true);
×
828

829
  ui->profileTable->editItem(ui->profileTable->item(currentRow, 0));
×
830
  ui->profileTable->item(currentRow, 0)->setSelected(true);
×
831

832
  validate();
×
833
  updateProfileStatus(currentRow);
×
834
}
×
835

836
/**
837
 * @brief ConfigDialog::on_profileTable_cellDoubleClicked open folder browser
838
 * for path column (column 1).
839
 */
840
void ConfigDialog::on_profileTable_cellDoubleClicked(int row, int column) {
×
841
  if (column == 1) {
×
842
    QString dir = selectFolder();
×
843
    if (!dir.isEmpty()) {
×
844
      ui->profileTable->item(row, 1)->setText(dir);
×
845
    }
846
  }
847
}
×
848

849
/**
850
 * @brief ConfigDialog::on_deleteButton_clicked remove a profile row.
851
 */
852
void ConfigDialog::on_deleteButton_clicked() {
×
853
  QSet<int> selectedRows; //  we use a set to prevent doubles
854
  QList<QTableWidgetItem *> itemList = ui->profileTable->selectedItems();
×
855
  if (itemList.count() == 0) {
×
856
    QMessageBox::warning(this, tr("No profile selected"),
×
857
                         tr("No profile selected to delete"));
×
858
    return;
859
  }
860
  QTableWidgetItem *item;
861
  foreach (item, itemList)
×
862
    selectedRows.insert(item->row());
×
863
  // get a list, and sort it big to small
864
  QList<int> rows = selectedRows.values();
×
865
  std::sort(rows.begin(), rows.end());
×
866
  // now actually do the removing:
867
  foreach (int row, rows)
×
868
    ui->profileTable->removeRow(row);
×
869
  if (ui->profileTable->rowCount() < 1) {
×
870
    ui->deleteButton->setEnabled(false);
×
871
  }
872

873
  validate();
×
874
  updateProfileStatus(-1);
×
875
}
876

877
/**
878
 * @brief ConfigDialog::criticalMessage wrapper for showing critical messages
879
 * in a popup.
880
 * @param title
881
 * @param text
882
 */
883
void ConfigDialog::criticalMessage(const QString &title, const QString &text) {
×
884
  QMessageBox::critical(this, title, text, QMessageBox::Ok, QMessageBox::Ok);
×
885
}
×
886

887
/**
888
 * @brief Checks whether the qrencode executable is available on the system.
889
 * @example
890
 * bool result = ConfigDialog::isQrencodeAvailable();
891
 * std::cout << result << std::endl; // Expected output: true if qrencode is
892
 * found, otherwise false
893
 *
894
 * @return bool - True if qrencode is available; otherwise false. On Windows,
895
 * always returns false.
896
 */
897
auto ConfigDialog::isQrencodeAvailable() -> bool {
13✔
898
#ifdef Q_OS_WIN
899
  return false;
900
#else
901
  QProcess which;
13✔
902
  which.start("which", QStringList() << "qrencode");
39✔
903
  which.waitForFinished();
13✔
904
  QtPassSettings::setQrencodeExecutable(
13✔
905
      which.readAllStandardOutput().trimmed());
13✔
906
  return which.exitCode() == 0;
26✔
907
#endif
908
}
13✔
909

910
auto ConfigDialog::isPassOtpAvailable() -> bool {
13✔
911
#ifdef Q_OS_WIN
912
  return false;
913
#else
914
  QProcess pass;
13✔
915
  pass.start(QtPassSettings::getPassExecutable(), QStringList() << "otp"
52✔
916
                                                                << "--help");
26✔
917
  pass.waitForFinished(2000);
13✔
918
  return pass.exitCode() == 0;
26✔
919
#endif
920
}
13✔
921

922
/**
923
 * @brief ConfigDialog::wizard first-time use wizard.
924
 */
925
void ConfigDialog::wizard() {
×
926
  (void)Util::configIsValid();
×
927
  on_autodetectButton_clicked();
×
928

929
  if (!checkGpgExistence()) {
×
930
    return;
931
  }
932
  if (!checkSecretKeys()) {
×
933
    return;
934
  }
935
  if (!checkPasswordStore()) {
×
936
    return;
937
  }
938
  handleGpgIdFile();
×
939

940
  ui->checkBoxHidePassword->setCheckState(Qt::Checked);
×
941
}
942

943
/**
944
 * @brief Checks whether the configured GnuPG executable exists.
945
 * @example
946
 * bool result = ConfigDialog::checkGpgExistence();
947
 * std::cout << result << std::endl; // Expected output: true if GnuPG is found,
948
 * false otherwise
949
 *
950
 * @return bool - True if the GnuPG path is valid or uses a WSL command prefix;
951
 * false if the executable cannot be found and an error message is shown.
952
 */
953
auto ConfigDialog::checkGpgExistence() -> bool {
×
954
  QString gpg = ui->gpgPath->text();
×
955
  if (!gpg.startsWith("wsl ") && !QFile(gpg).exists()) {
×
956
    criticalMessage(
×
957
        tr("GnuPG not found"),
×
958
#ifdef Q_OS_WIN
959
#ifdef WINSTORE
960
        tr("Please install GnuPG on your system.<br>Install "
961
           "<strong>Ubuntu</strong> from the Microsoft Store to get it.<br>"
962
           "If you already did so, make sure you started it once and<br>"
963
           "click \"Autodetect\" in the next dialog.")
964
#else
965
        tr("Please install GnuPG on your system.<br>Install "
966
           "<strong>Ubuntu</strong> from the Microsoft Store<br>or <a "
967
           "href=\"https://www.gnupg.org/download/#sec-1-2\">download</a> it "
968
           "from GnuPG.org")
969
#endif
970
#else
971
        tr("Please install GnuPG on your system.<br>Install "
×
972
           "<strong>gpg</strong> using your favorite package manager<br>or "
973
           "<a "
974
           "href=\"https://www.gnupg.org/download/#sec-1-2\">download</a> it "
975
           "from GnuPG.org")
976
#endif
977
    );
978
    return false;
×
979
  }
980
  return true;
981
}
982

983
/**
984
 * @brief Checks whether secret keys are available and, if needed, prompts the
985
 * user to generate them.
986
 * @example
987
 * bool result = ConfigDialog.checkSecretKeys();
988
 * std::cout << result << std::endl; // Expected output: true if keys are
989
 * present or key generation dialog is accepted, false otherwise
990
 *
991
 * @return bool - Returns true when secret keys are already available or the key
992
 * generation dialog is accepted; false if the dialog is rejected.
993
 */
994
auto ConfigDialog::checkSecretKeys() -> bool {
×
995
  QString gpg = ui->gpgPath->text();
×
996
  QStringList names = getSecretKeys();
×
997

998
#ifdef QT_DEBUG
999
  dbg() << names;
1000
#endif
1001

1002
  if ((gpg.startsWith("wsl ") || QFile(gpg).exists()) && names.empty()) {
×
1003
    KeygenDialog d(this);
×
1004
    return d.exec();
×
1005
  }
×
1006
  return true;
1007
}
1008

1009
/**
1010
 * @brief Checks whether the password-store path exists and prompts the user to
1011
 * create it if it does not.
1012
 * @example
1013
 * bool result = ConfigDialog::checkPasswordStore();
1014
 * std::cout << std::boolalpha << result << std::endl; // Expected output: true
1015
 * if the store exists or is created successfully, false if creation fails
1016
 *
1017
 * @return bool - True if the password-store exists or is successfully created,
1018
 * false if creation fails.
1019
 */
1020
auto ConfigDialog::checkPasswordStore() -> bool {
×
1021
  QString passStore = ui->storePath->text();
×
1022

1023
  if (!QFile(passStore).exists()) {
×
1024
    if (QMessageBox::question(
×
1025
            this, tr("Create password-store?"),
×
1026
            tr("Would you like to create a password-store at %1?")
×
1027
                .arg(passStore),
×
1028
            QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
1029
      if (!QDir().mkdir(passStore)) {
×
1030
        QMessageBox::warning(
×
1031
            this, tr("Error"),
×
1032
            tr("Failed to create password-store at: %1").arg(passStore));
×
1033
        return false;
×
1034
      }
1035
#ifdef Q_OS_WIN
1036
      SetFileAttributes(passStore.toStdWString().c_str(),
1037
                        FILE_ATTRIBUTE_HIDDEN);
1038
#endif
1039
      if (ui->checkBoxUseGit->isChecked()) {
×
1040
        emit mainWindow->passGitInitNeeded();
×
1041
      }
1042
      mainWindow->userDialog(passStore);
×
1043
    }
1044
  }
1045
  return true;
1046
}
1047

1048
/**
1049
 * @brief Handles selection and validation of the password store's .gpg-id file.
1050
 * @example
1051
 * ConfigDialog dialog;
1052
 * dialog.handleGpgIdFile();
1053
 *
1054
 * @return void - This method does not return a value; it updates the UI flow
1055
 * and may prompt the user to choose a valid password store.
1056
 */
1057
void ConfigDialog::handleGpgIdFile() {
×
1058
  QString passStore = ui->storePath->text();
×
1059
  if (!QFile(QDir(passStore).filePath(".gpg-id")).exists()) {
×
1060
#ifdef QT_DEBUG
1061
    dbg() << ".gpg-id file does not exist";
1062
#endif
1063
    criticalMessage(tr("Password store not initialised"),
×
1064
                    tr("The folder %1 doesn't seem to be a password store or "
×
1065
                       "is not yet initialised.")
1066
                        .arg(passStore));
×
1067

1068
    while (!QFile(passStore).exists()) {
×
1069
      on_toolButtonStore_clicked();
×
1070
      if (passStore == ui->storePath->text()) {
×
1071
        return;
1072
      }
1073
      passStore = ui->storePath->text();
×
1074
    }
1075
    if (!QFile(passStore + ".gpg-id").exists()) {
×
1076
#ifdef QT_DEBUG
1077
      dbg() << ".gpg-id file still does not exist :/";
1078
#endif
1079
      mainWindow->userDialog(passStore);
×
1080
    }
1081
  }
1082
}
1083

1084
/**
1085
 * @brief ConfigDialog::useTrayIcon set preference for using trayicon.
1086
 * Enable or disable related checkboxes accordingly.
1087
 * @param useSystray
1088
 */
1089
void ConfigDialog::useTrayIcon(bool useSystray) {
13✔
1090
  if (QSystemTrayIcon::isSystemTrayAvailable()) {
13✔
1091
    ui->checkBoxUseTrayIcon->setChecked(useSystray);
×
1092
    ui->checkBoxHideOnClose->setEnabled(useSystray);
×
1093
    ui->checkBoxStartMinimized->setEnabled(useSystray);
×
1094

1095
    if (!useSystray) {
×
1096
      ui->checkBoxHideOnClose->setChecked(false);
×
1097
      ui->checkBoxStartMinimized->setChecked(false);
×
1098
    }
1099
  }
1100
}
13✔
1101

1102
/**
1103
 * @brief ConfigDialog::on_checkBoxUseTrayIcon_clicked enable and disable
1104
 * related checkboxes.
1105
 */
1106
void ConfigDialog::on_checkBoxUseTrayIcon_clicked() {
×
1107
  bool state = ui->checkBoxUseTrayIcon->isChecked();
×
1108
  ui->checkBoxHideOnClose->setEnabled(state);
×
1109
  ui->checkBoxStartMinimized->setEnabled(state);
×
1110
}
×
1111

1112
/**
1113
 * @brief ConfigDialog::closeEvent close this window.
1114
 * @param event
1115
 */
1116
void ConfigDialog::closeEvent(QCloseEvent *event) {
×
1117
  QtPassSettings::setDialogGeometry("configDialog", saveGeometry());
×
1118
  if (!isMaximized()) {
×
1119
    QtPassSettings::setDialogPos("configDialog", pos());
×
1120
    QtPassSettings::setDialogSize("configDialog", size());
×
1121
  }
1122
  QtPassSettings::setDialogMaximized("configDialog", isMaximized());
×
1123
  event->accept();
1124
}
×
1125

1126
/**
1127
 * @brief ConfigDialog::useGit set preference for using git.
1128
 * @param useGit
1129
 */
1130
void ConfigDialog::useGit(bool useGit) {
15✔
1131
  ui->checkBoxUseGit->setChecked(useGit);
15✔
1132
  on_checkBoxUseGit_clicked();
15✔
1133
}
15✔
1134

1135
/**
1136
 * @brief ConfigDialog::useOtp set preference for using otp plugin.
1137
 * @param useOtp
1138
 */
1139
void ConfigDialog::useOtp(bool useOtp) {
15✔
1140
  ui->checkBoxUseOtp->setChecked(useOtp);
15✔
1141
}
15✔
1142

1143
void ConfigDialog::useGrepSearch(bool useGrepSearch) {
15✔
1144
  ui->checkBoxUseGrepSearch->setChecked(useGrepSearch);
15✔
1145
}
15✔
1146

1147
/**
1148
 * @brief ConfigDialog::useQrencode set preference for using qrencode plugin.
1149
 * @param useQrencode
1150
 */
1151
void ConfigDialog::useQrencode(bool useQrencode) {
15✔
1152
  ui->checkBoxUseQrencode->setChecked(useQrencode);
15✔
1153
}
15✔
1154

1155
/**
1156
 * @brief ConfigDialog::on_checkBoxUseGit_clicked enable or disable related
1157
 * checkboxes.
1158
 */
1159
void ConfigDialog::on_checkBoxUseGit_clicked() {
15✔
1160
  ui->checkBoxAddGPGId->setEnabled(ui->checkBoxUseGit->isChecked());
15✔
1161
  ui->checkBoxAutoPull->setEnabled(ui->checkBoxUseGit->isChecked());
15✔
1162
  ui->checkBoxAutoPush->setEnabled(ui->checkBoxUseGit->isChecked());
15✔
1163
}
15✔
1164

1165
/**
1166
 * @brief ConfigDialog::on_toolButtonPwgen_clicked enable or disable related
1167
 * options in the interface.
1168
 */
1169
void ConfigDialog::on_toolButtonPwgen_clicked() {
×
1170
  QString pwgen = selectExecutable();
×
1171
  if (!pwgen.isEmpty()) {
×
1172
    ui->pwgenPath->setText(pwgen);
×
1173
    ui->checkBoxUsePwgen->setEnabled(true);
×
1174
  } else {
1175
    ui->checkBoxUsePwgen->setEnabled(false);
×
1176
    ui->checkBoxUsePwgen->setChecked(false);
×
1177
  }
1178
}
×
1179

1180
/**
1181
 * @brief ConfigDialog::setPwgenPath set pwgen executable path.
1182
 * Enable or disable related options in the interface.
1183
 * @param pwgen
1184
 */
1185
void ConfigDialog::setPwgenPath(const QString &pwgen) {
2✔
1186
  ui->pwgenPath->setText(pwgen);
2✔
1187
  if (pwgen.isEmpty()) {
2✔
1188
    ui->checkBoxUsePwgen->setChecked(false);
1✔
1189
    ui->checkBoxUsePwgen->setEnabled(false);
1✔
1190
  }
1191
  on_checkBoxUsePwgen_clicked();
2✔
1192
}
2✔
1193

1194
/**
1195
 * @brief ConfigDialog::on_checkBoxUsePwgen_clicked enable or disable related
1196
 * options in the interface.
1197
 */
1198
void ConfigDialog::on_checkBoxUsePwgen_clicked() {
31✔
1199
  if (ui->radioButtonPass->isChecked())
31✔
1200
    return;
1201
  bool usePwgen = ui->checkBoxUsePwgen->isChecked();
31✔
1202
  ui->checkBoxAvoidCapitals->setEnabled(usePwgen);
31✔
1203
  ui->checkBoxAvoidNumbers->setEnabled(usePwgen);
31✔
1204
  ui->checkBoxLessRandom->setEnabled(usePwgen);
31✔
1205
  ui->checkBoxUseSymbols->setEnabled(usePwgen);
31✔
1206
  ui->lineEditPasswordChars->setEnabled(!usePwgen);
31✔
1207
  ui->labelPasswordChars->setEnabled(!usePwgen);
31✔
1208
  ui->passwordCharTemplateSelector->setEnabled(!usePwgen);
31✔
1209
}
1210

1211
/**
1212
 * @brief ConfigDialog::usePwgen set preference for using pwgen (can be
1213
 * overruled by empty pwgenPath).
1214
 * enable or disable related options in the interface via
1215
 * ConfigDialog::on_checkBoxUsePwgen_clicked
1216
 * @param usePwgen
1217
 */
1218
void ConfigDialog::usePwgen(bool usePwgen) {
16✔
1219
  if (ui->pwgenPath->text().isEmpty()) {
32✔
1220
    usePwgen = false;
1221
  }
1222
  ui->checkBoxUsePwgen->setChecked(usePwgen);
16✔
1223
  on_checkBoxUsePwgen_clicked();
16✔
1224
}
16✔
1225

1226
void ConfigDialog::setPasswordConfiguration(
1✔
1227
    const PasswordConfiguration &config) {
1228
  ui->spinBoxPasswordLength->setValue(config.length);
1✔
1229
  ui->passwordCharTemplateSelector->setCurrentIndex(config.selected);
1✔
1230
  if (config.selected != PasswordConfiguration::CUSTOM) {
1✔
1231
    ui->lineEditPasswordChars->setEnabled(false);
1✔
1232
  }
1233
  ui->lineEditPasswordChars->setText(config.Characters[config.selected]);
1✔
1234
}
1✔
1235

1236
auto ConfigDialog::getPasswordConfiguration() -> PasswordConfiguration {
1✔
1237
  PasswordConfiguration config;
1✔
1238
  config.length = ui->spinBoxPasswordLength->value();
1✔
1239
  config.selected = static_cast<PasswordConfiguration::characterSet>(
1✔
1240
      ui->passwordCharTemplateSelector->currentIndex());
1✔
1241
  config.Characters[PasswordConfiguration::CUSTOM] =
1242
      ui->lineEditPasswordChars->text();
1✔
1243
  return config;
1✔
1244
}
×
1245

1246
/**
1247
 * @brief ConfigDialog::on_passwordCharTemplateSelector_activated sets the
1248
 * passwordChar Template
1249
 * combo box to the desired entry
1250
 * @param entry of
1251
 */
1252
void ConfigDialog::on_passwordCharTemplateSelector_activated(int index) {
×
1253
  ui->lineEditPasswordChars->setText(
×
1254
      QtPassSettings::getPasswordConfiguration().Characters[index]);
×
1255
  if (index == PasswordConfiguration::CUSTOM) {
×
1256
    ui->lineEditPasswordChars->setEnabled(true);
×
1257
  } else {
1258
    ui->lineEditPasswordChars->setEnabled(false);
×
1259
  }
1260
}
×
1261

1262
/**
1263
 * @brief ConfigDialog::on_checkBoxUseTemplate_clicked enable or disable the
1264
 * template field and options.
1265
 */
1266
void ConfigDialog::on_checkBoxUseTemplate_clicked() {
15✔
1267
  ui->plainTextEditTemplate->setEnabled(ui->checkBoxUseTemplate->isChecked());
15✔
1268
  ui->checkBoxTemplateAllFields->setEnabled(
15✔
1269
      ui->checkBoxUseTemplate->isChecked());
15✔
1270
}
15✔
1271

1272
void ConfigDialog::onProfileTableItemChanged(QTableWidgetItem *item) {
×
1273
  validate(item);
×
1274
  updateProfileStatus(item ? item->row() : -1);
×
1275
}
×
1276

1277
void ConfigDialog::onProfileTableSelectionChanged() {
×
1278
  QList<QTableWidgetItem *> selected = ui->profileTable->selectedItems();
×
1279
  if (selected.isEmpty()) {
×
1280
    return;
1281
  }
1282
  QTableWidgetItem *nameItem =
1283
      ui->profileTable->item(selected.first()->row(), 0);
×
1284
  if (nameItem == nullptr) {
×
1285
    return;
1286
  }
1287
  QString profileName = nameItem->text();
×
1288
  loadGitSettingsForProfile(profileName, m_profiles);
×
1289
}
1290

1291
/**
1292
 * @brief Update status bar with profile preview for given row.
1293
 * @param row The row index to preview, or -1 to clear.
1294
 */
1295
void ConfigDialog::updateProfileStatus(int row) {
×
1296
  if (row < 0 || row >= ui->profileTable->rowCount()) {
×
1297
    ui->statusLabel->setText(QString());
×
1298
    return;
×
1299
  }
1300

1301
  QTableWidgetItem *nameItem = ui->profileTable->item(row, 0);
×
1302
  QTableWidgetItem *pathItem = ui->profileTable->item(row, 1);
×
1303

1304
  QString statusMessage;
×
1305
  if (nameItem && !nameItem->text().isEmpty() && pathItem &&
×
1306
      !pathItem->text().isEmpty()) {
×
1307
    QDir dir(QDir::cleanPath(pathItem->text()));
×
1308
    if (!dir.exists()) {
×
1309
      statusMessage = tr("New profile: %1 at %2")
×
1310
                          .arg(nameItem->text())
×
1311
                          .arg(QDir::cleanPath(pathItem->text()));
×
1312
    } else {
1313
      statusMessage = tr("Profile: %1 at %2")
×
1314
                          .arg(nameItem->text())
×
1315
                          .arg(QDir::cleanPath(pathItem->text()));
×
1316
    }
1317
  } else {
×
1318
    statusMessage = tr("Fill in all required fields");
×
1319
  }
1320

1321
  ui->statusLabel->setText(statusMessage);
×
1322
}
1323

1324
/**
1325
 * @brief ConfigDialog::useTemplate set preference for using templates.
1326
 * @param useTemplate
1327
 */
1328
void ConfigDialog::useTemplate(bool useTemplate) {
15✔
1329
  ui->checkBoxUseTemplate->setChecked(useTemplate);
15✔
1330
  on_checkBoxUseTemplate_clicked();
15✔
1331
}
15✔
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