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

IJHack / QtPass / 25793206586

13 May 2026 10:23AM UTC coverage: 29.083% (+0.1%) from 28.988%
25793206586

push

github

web-flow
test: cover SSH_AUTH_SOCK override soft-validation via Util helper (#1469)

The Settings dialog warning logic added in #1439 (configdialog.cpp
on_accepted) had no unit tests. Extract the validation rules into a
pure helper so they're testable without a Qt event loop or
QMessageBox spy.

Production change (mechanical, no semantic diff):

- New Util::sshAuthSockOverrideStatus(QString) returning an enum:
  Valid / DoesNotExist / NotReadable / NotUnixDomainSocket. Same three
  checks as before (exists, readable, S_ISSOCK on Unix), now reusable
  and unit-testable.
- ConfigDialog::on_accepted switches the three inline if/else branches
  to a switch over the enum. Translation strings stay in ConfigDialog
  (Util has no tr() callers).
- Dropped now-unused #include <QFileInfo> and #include <sys/stat.h>
  from configdialog.cpp.

Tests (tst_util.cpp):

- sshAuthSockOverrideStatusDoesNotExist — non-existent path
- sshAuthSockOverrideStatusRegularFileRejected — regular file (Unix)
- sshAuthSockOverrideStatusNotReadable — chmod 0 (Unix; skipped under
  root since euid 0 ignores mode bits)
- sshAuthSockOverrideStatusValid — bind(2) a real Unix domain socket
  and assert Valid

POSIX socket(2)/bind(2) instead of QLocalServer to avoid pulling
QtNetwork into the util test target. Unix-only tests SKIP on Windows
where the production code also skips the socket check (ssh-agent uses
a named pipe there).

Build clean, 124/124 util tests pass (was 120, +4).

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

9 of 17 new or added lines in 2 files covered. (52.94%)

2 existing lines in 1 file now uncovered.

1957 of 6729 relevant lines covered (29.08%)

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

23
#ifdef QT_DEBUG
24
#include "debughelper.h"
25
#endif
26

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

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

48
  ui->passPath->setText(QtPassSettings::getPassExecutable());
×
49
  setGitPath(QtPassSettings::getGitExecutable());
×
50
  ui->gpgPath->setText(QtPassSettings::getGpgExecutable());
×
51
  ui->storePath->setText(QtPassSettings::getPassStore());
×
52
  ui->sshAuthSockOverride->setText(QtPassSettings::getSshAuthSockOverride());
×
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->checkBoxShowProcessOutput->setChecked(
×
82
      QtPassSettings::isShowProcessOutput());
×
83
  ui->checkBoxAutoPull->setChecked(QtPassSettings::isAutoPull());
×
84
  ui->checkBoxAutoPush->setChecked(QtPassSettings::isAutoPush());
×
85
  ui->checkBoxAlwaysOnTop->setChecked(QtPassSettings::isAlwaysOnTop());
×
86

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

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

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

104
  usePass(QtPassSettings::isUsePass());
×
105
  useAutoclear(QtPassSettings::isUseAutoclear());
×
106
  useAutoclearPanel(QtPassSettings::isUseAutoclearPanel());
×
107
  useTrayIcon(QtPassSettings::isUseTrayIcon());
×
108

109
  setProfiles(QtPassSettings::getProfiles(), QtPassSettings::getProfile());
×
110

111
  useGit(QtPassSettings::isUseGit());
×
112

113
  useOtp(QtPassSettings::isUseOtp());
×
114
  useGrepSearch(QtPassSettings::isUseGrepSearch());
×
115
  useQrencode(QtPassSettings::isUseQrencode());
×
116

117
  usePwgen(QtPassSettings::isUsePwgen());
×
118
  useTemplate(QtPassSettings::isUseTemplate());
×
119

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

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

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

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

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

148
  connect(ui->profileTable, &QTableWidget::itemChanged, this,
×
149
          &ConfigDialog::onProfileTableItemChanged);
×
150
  connect(ui->profileTable, &QTableWidget::itemSelectionChanged, this,
×
151
          &ConfigDialog::onProfileTableSelectionChanged);
×
152
  connect(this, &ConfigDialog::accepted, this, &ConfigDialog::on_accepted);
×
153
}
×
154

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

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

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

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

204
  if (item == nullptr) {
×
205
    for (int i = 0; i < ui->profileTable->rowCount(); i++) {
×
206
      for (int j = 0; j < ui->profileTable->columnCount(); j++) {
×
207
        QTableWidgetItem *_item = ui->profileTable->item(i, j);
×
208

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

220
      if (!status) {
221
        break;
222
      }
223
    }
224
  } else {
225
    if (item->text().isEmpty() && item->column() != 2) {
×
226
      item->setBackground(Qt::red);
×
227
      item->setToolTip(tr("This field is required"));
×
228
      status = false;
229
    } else {
230
      item->setBackground(QBrush());
×
231
      item->setToolTip(QString());
×
232
    }
233
  }
234

235
  ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(status);
×
236
}
×
237

238
/**
239
 * @brief Saves the configuration dialog settings to persistent application
240
 * settings.
241
 * @example
242
 * ConfigDialog dialog;
243
 * dialog.on_accepted();
244
 * // Expected output: All UI-selected configuration values are stored via
245
 * QtPassSettings.
246
 *
247
 * @return void - This method does not return a value.
248
 */
249
void ConfigDialog::on_accepted() {
×
250
  QtPassSettings::setPassExecutable(ui->passPath->text());
×
251
  QtPassSettings::setGitExecutable(ui->gitPath->text());
×
252
  QtPassSettings::setGpgExecutable(ui->gpgPath->text());
×
253
  const QString sshAuthSockOverride = ui->sshAuthSockOverride->text().trimmed();
×
254
  if (!sshAuthSockOverride.isEmpty()) {
×
255
    QString reason;
×
NEW
256
    switch (Util::sshAuthSockOverrideStatus(sshAuthSockOverride)) {
×
257
    case Util::SshAuthSockOverrideStatus::Valid:
258
      break;
NEW
259
    case Util::SshAuthSockOverrideStatus::DoesNotExist:
×
260
      reason = tr("The path does not exist.");
×
NEW
261
      break;
×
NEW
262
    case Util::SshAuthSockOverrideStatus::NotReadable:
×
UNCOV
263
      reason = tr("The path is not readable.");
×
NEW
264
      break;
×
NEW
265
    case Util::SshAuthSockOverrideStatus::NotUnixDomainSocket:
×
NEW
266
      reason = tr("The path is not a Unix domain socket.");
×
NEW
267
      break;
×
268
    }
UNCOV
269
    if (!reason.isEmpty()) {
×
270
      QMessageBox::warning(
×
271
          this, tr("Potentially invalid SSH_AUTH_SOCK override"),
×
272
          tr("The SSH_AUTH_SOCK override value may be invalid.\n\n%1\n\n"
×
273
             "The value will still be saved as entered.")
274
              .arg(reason));
×
275
    }
276
  }
277
  QtPassSettings::setSshAuthSockOverride(sshAuthSockOverride);
×
278
  QtPassSettings::setPassStore(
×
279
      Util::normalizeFolderPath(ui->storePath->text()));
×
280
  QtPassSettings::setUsePass(ui->radioButtonPass->isChecked());
×
281
  QtPassSettings::setClipBoardType(ui->comboBoxClipboard->currentIndex());
×
282
  QtPassSettings::setUseSelection(ui->checkBoxSelection->isChecked());
×
283
  QtPassSettings::setUseAutoclear(ui->checkBoxAutoclear->isChecked());
×
284
  QtPassSettings::setAutoclearSeconds(ui->spinBoxAutoclearSeconds->value());
×
285
  QtPassSettings::setUseAutoclearPanel(ui->checkBoxAutoclearPanel->isChecked());
×
286
  QtPassSettings::setAutoclearPanelSeconds(
×
287
      ui->spinBoxAutoclearPanelSeconds->value());
×
288
  QtPassSettings::setHidePassword(ui->checkBoxHidePassword->isChecked());
×
289
  QtPassSettings::setHideContent(ui->checkBoxHideContent->isChecked());
×
290
  QtPassSettings::setUseMonospace(ui->checkBoxUseMonospace->isChecked());
×
291
  QtPassSettings::setDisplayAsIs(ui->checkBoxDisplayAsIs->isChecked());
×
292
  QtPassSettings::setNoLineWrapping(ui->checkBoxNoLineWrapping->isChecked());
×
293
  QtPassSettings::setAddGPGId(ui->checkBoxAddGPGId->isChecked());
×
294
  QtPassSettings::setUseTrayIcon(ui->checkBoxUseTrayIcon->isEnabled() &&
×
295
                                 ui->checkBoxUseTrayIcon->isChecked());
×
296
  QtPassSettings::setHideOnClose(ui->checkBoxHideOnClose->isEnabled() &&
×
297
                                 ui->checkBoxHideOnClose->isChecked());
×
298
  QtPassSettings::setStartMinimized(ui->checkBoxStartMinimized->isEnabled() &&
×
299
                                    ui->checkBoxStartMinimized->isChecked());
×
300
  QHash<QString, QHash<QString, QString>> existingProfiles =
301
      QtPassSettings::getProfiles();
×
302
  QtPassSettings::setProfiles(getProfiles());
×
303
  QtPassSettings::setUseGit(ui->checkBoxUseGit->isChecked());
×
304
  QtPassSettings::setUseOtp(ui->checkBoxUseOtp->isChecked());
×
305
  QtPassSettings::setUseGrepSearch(ui->checkBoxUseGrepSearch->isChecked());
×
306
  QtPassSettings::setUseQrencode(ui->checkBoxUseQrencode->isChecked());
×
307
  QtPassSettings::setPwgenExecutable(ui->pwgenPath->text());
×
308
  QtPassSettings::setUsePwgen(ui->checkBoxUsePwgen->isChecked());
×
309
  QtPassSettings::setAvoidCapitals(ui->checkBoxAvoidCapitals->isChecked());
×
310
  QtPassSettings::setAvoidNumbers(ui->checkBoxAvoidNumbers->isChecked());
×
311
  QtPassSettings::setLessRandom(ui->checkBoxLessRandom->isChecked());
×
312
  QtPassSettings::setUseSymbols(ui->checkBoxUseSymbols->isChecked());
×
313
  QtPassSettings::setPasswordConfiguration(getPasswordConfiguration());
×
314
  QtPassSettings::setUseTemplate(ui->checkBoxUseTemplate->isChecked());
×
315
  QtPassSettings::setPassTemplate(ui->plainTextEditTemplate->toPlainText());
×
316
  QtPassSettings::setTemplateAllFields(
×
317
      ui->checkBoxTemplateAllFields->isChecked());
×
318
  QtPassSettings::setShowProcessOutput(
×
319
      ui->checkBoxShowProcessOutput->isChecked());
×
320
  QtPassSettings::setAlwaysOnTop(ui->checkBoxAlwaysOnTop->isChecked());
×
321

322
  QtPassSettings::setVersion(VERSION);
×
323

324
  // Initialize new profiles that need pass/git initialization
325
  initializeNewProfiles(existingProfiles);
×
326
}
×
327

328
/**
329
 * @brief Automatically detects required external binaries in the system PATH
330
 * and updates the dialog fields.
331
 * @example
332
 * ConfigDialog configDialog;
333
 * configDialog.on_autodetectButton_clicked();
334
 *
335
 * @return void - This function does not return a value.
336
 */
337
void ConfigDialog::on_autodetectButton_clicked() {
×
338
  QString pass = Util::findBinaryInPath("pass");
×
339
  if (!pass.isEmpty()) {
×
340
    ui->passPath->setText(pass);
×
341
  }
342
  usePass(!pass.isEmpty());
×
343
  QString gpg = Util::findBinaryInPath("gpg2");
×
344
  if (gpg.isEmpty()) {
×
345
    gpg = Util::findBinaryInPath("gpg");
×
346
  }
347
  if (!gpg.isEmpty()) {
×
348
    ui->gpgPath->setText(gpg);
×
349
  }
350
  QString git = Util::findBinaryInPath("git");
×
351
  if (!git.isEmpty()) {
×
352
    ui->gitPath->setText(git);
×
353
  }
354
  QString pwgen = Util::findBinaryInPath("pwgen");
×
355
  if (!pwgen.isEmpty()) {
×
356
    ui->pwgenPath->setText(pwgen);
×
357
  }
358
}
×
359

360
/**
361
 * @brief ConfigDialog::on_radioButtonNative_clicked wrapper for
362
 * ConfigDialog::setGroupBoxState()
363
 */
364
void ConfigDialog::on_radioButtonNative_clicked() { setGroupBoxState(); }
×
365

366
/**
367
 * @brief ConfigDialog::on_radioButtonPass_clicked wrapper for
368
 * ConfigDialog::setGroupBoxState()
369
 */
370
void ConfigDialog::on_radioButtonPass_clicked() { setGroupBoxState(); }
×
371

372
/**
373
 * @brief ConfigDialog::getSecretKeys get list of secret/private keys
374
 * @return QStringList keys
375
 */
376
auto ConfigDialog::getSecretKeys() -> QStringList {
×
377
  QList<UserInfo> keys = QtPassSettings::getPass()->listKeys("", true);
×
378
  QStringList names;
×
379

380
  if (keys.empty()) {
×
381
    return names;
382
  }
383

384
  foreach (const UserInfo &sec, keys)
×
385
    names << sec.name;
×
386

387
  return names;
×
388
}
389

390
/**
391
 * @brief ConfigDialog::setGroupBoxState update checkboxes.
392
 */
393
void ConfigDialog::setGroupBoxState() {
×
394
  bool state = ui->radioButtonPass->isChecked();
×
395
  ui->groupBoxNative->setEnabled(!state);
×
396
  ui->groupBoxPass->setEnabled(state);
×
397
  if (state) {
×
398
    // pass mode: disable all password generation controls
399
    ui->spinBoxPasswordLength->setEnabled(false);
×
400
    ui->checkBoxUsePwgen->setEnabled(false);
×
401
    ui->checkBoxAvoidCapitals->setEnabled(false);
×
402
    ui->checkBoxUseSymbols->setEnabled(false);
×
403
    ui->checkBoxLessRandom->setEnabled(false);
×
404
    ui->checkBoxAvoidNumbers->setEnabled(false);
×
405
    ui->labelPasswordChars->setEnabled(false);
×
406
    ui->passwordCharTemplateSelector->setEnabled(false);
×
407
    ui->lineEditPasswordChars->setEnabled(false);
×
408
  } else {
409
    // native mode: restore pwgen/charset state from existing handlers
410
    ui->spinBoxPasswordLength->setEnabled(true);
×
411
    ui->checkBoxUsePwgen->setEnabled(!ui->pwgenPath->text().isEmpty());
×
412
    on_checkBoxUsePwgen_clicked();
×
413
  }
414
}
×
415

416
/**
417
 * @brief ConfigDialog::selectExecutable pop-up to choose an executable.
418
 * @return
419
 */
420
auto ConfigDialog::selectExecutable() -> QString {
×
421
  QFileDialog dialog(this);
×
422
  dialog.setFileMode(QFileDialog::ExistingFile);
×
423
  dialog.setOption(QFileDialog::ReadOnly);
×
424
  if (dialog.exec()) {
×
425
    return dialog.selectedFiles().constFirst();
×
426
  }
427

428
  return {};
429
}
×
430

431
/**
432
 * @brief ConfigDialog::selectFolder pop-up to choose a folder.
433
 * @return
434
 */
435
auto ConfigDialog::selectFolder() -> QString {
×
436
  QFileDialog dialog(this);
×
437
  dialog.setFileMode(QFileDialog::Directory);
×
438
  dialog.setFilter(QDir::NoFilter);
×
439
  dialog.setOption(QFileDialog::ShowDirsOnly);
×
440
  if (dialog.exec()) {
×
441
    return dialog.selectedFiles().constFirst();
×
442
  }
443

444
  return {};
445
}
×
446

447
/**
448
 * @brief ConfigDialog::on_toolButtonGit_clicked get git application.
449
 * Enable checkboxes if found.
450
 */
451
void ConfigDialog::on_toolButtonGit_clicked() {
×
452
  QString git = selectExecutable();
×
453
  bool state = !git.isEmpty();
×
454
  if (state) {
×
455
    ui->gitPath->setText(git);
×
456
  } else {
457
    useGit(false);
×
458
  }
459

460
  ui->checkBoxUseGit->setEnabled(state);
×
461
}
×
462

463
/**
464
 * @brief ConfigDialog::on_toolButtonGpg_clicked get gpg application.
465
 */
466
void ConfigDialog::on_toolButtonGpg_clicked() {
×
467
  QString gpg = selectExecutable();
×
468
  if (!gpg.isEmpty()) {
×
469
    ui->gpgPath->setText(gpg);
×
470
  }
471
}
×
472

473
/**
474
 * @brief ConfigDialog::on_pushButtonGenerateKey_clicked open keygen dialog.
475
 */
476
void ConfigDialog::on_pushButtonGenerateKey_clicked() {
×
477
  KeygenDialog d(this);
×
478
  d.exec();
×
479
}
×
480

481
/**
482
 * @brief ConfigDialog::on_toolButtonPass_clicked get pass application.
483
 */
484
void ConfigDialog::on_toolButtonPass_clicked() {
×
485
  QString pass = selectExecutable();
×
486
  if (!pass.isEmpty()) {
×
487
    ui->passPath->setText(pass);
×
488
  }
489
}
×
490

491
/**
492
 * @brief ConfigDialog::on_toolButtonStore_clicked get .password-store
493
 * location.
494
 */
495
void ConfigDialog::on_toolButtonStore_clicked() {
×
496
  QString store = selectFolder();
×
497
  if (!store.isEmpty()) {
×
498
    ui->storePath->setText(store);
×
499
  }
500
}
×
501

502
/**
503
 * @brief ConfigDialog::on_comboBoxClipboard_activated show and hide options.
504
 * @param index of selectbox (0 = no clipboard).
505
 */
506
void ConfigDialog::on_comboBoxClipboard_activated(int index) {
×
507
  bool state = index > 0;
×
508

509
  ui->checkBoxSelection->setEnabled(state);
×
510
  ui->checkBoxAutoclear->setEnabled(state);
×
511
  ui->checkBoxHidePassword->setEnabled(state);
×
512
  ui->checkBoxHideContent->setEnabled(state);
×
513
  if (state) {
×
514
    ui->spinBoxAutoclearSeconds->setEnabled(ui->checkBoxAutoclear->isChecked());
×
515
    ui->labelSeconds->setEnabled(ui->checkBoxAutoclear->isChecked());
×
516
  } else {
517
    ui->spinBoxAutoclearSeconds->setEnabled(false);
×
518
    ui->labelSeconds->setEnabled(false);
×
519
  }
520
}
×
521

522
/**
523
 * @brief ConfigDialog::on_checkBoxAutoclearPanel_clicked enable and disable
524
 * options based on autoclear use.
525
 */
526
void ConfigDialog::on_checkBoxAutoclearPanel_clicked() {
×
527
  bool state = ui->checkBoxAutoclearPanel->isChecked();
×
528
  ui->spinBoxAutoclearPanelSeconds->setEnabled(state);
×
529
  ui->labelPanelSeconds->setEnabled(state);
×
530
}
×
531

532
/**
533
 * @brief ConfigDialog::useSelection set the clipboard type use from
534
 * MainWindow.
535
 * @param useSelection
536
 */
537
void ConfigDialog::useSelection(bool useSelection) {
×
538
  ui->checkBoxSelection->setChecked(useSelection);
×
539
  on_checkBoxSelection_clicked();
×
540
}
×
541

542
/**
543
 * @brief ConfigDialog::useAutoclear set the clipboard autoclear use from
544
 * MainWindow.
545
 * @param useAutoclear
546
 */
547
void ConfigDialog::useAutoclear(bool useAutoclear) {
×
548
  ui->checkBoxAutoclear->setChecked(useAutoclear);
×
549
  on_checkBoxAutoclear_clicked();
×
550
}
×
551

552
/**
553
 * @brief ConfigDialog::useAutoclearPanel set the panel autoclear use from
554
 * MainWindow.
555
 * @param useAutoclearPanel
556
 */
557
void ConfigDialog::useAutoclearPanel(bool useAutoclearPanel) {
×
558
  ui->checkBoxAutoclearPanel->setChecked(useAutoclearPanel);
×
559
  on_checkBoxAutoclearPanel_clicked();
×
560
}
×
561

562
/**
563
 * @brief ConfigDialog::on_checkBoxSelection_clicked checkbox clicked, update
564
 * state via ConfigDialog::on_comboBoxClipboard_activated
565
 */
566
void ConfigDialog::on_checkBoxSelection_clicked() {
×
567
  on_comboBoxClipboard_activated(ui->comboBoxClipboard->currentIndex());
×
568
}
×
569

570
/**
571
 * @brief ConfigDialog::on_checkBoxAutoclear_clicked checkbox clicked, update
572
 * state via ConfigDialog::on_comboBoxClipboard_activated
573
 */
574
void ConfigDialog::on_checkBoxAutoclear_clicked() {
×
575
  on_comboBoxClipboard_activated(ui->comboBoxClipboard->currentIndex());
×
576
}
×
577

578
/**
579
 * @brief ConfigDialog::genKey tunnel function to make MainWindow generate a
580
 * gpg key pair.
581
 * @param batch
582
 * @param dialog
583
 */
584
void ConfigDialog::genKey(const QString &batch, QDialog *dialog) {
×
585
  mainWindow->generateKeyPair(batch, dialog);
×
586
}
×
587

588
/**
589
 * @brief ConfigDialog::setProfiles set the profiles and chosen profile from
590
 * MainWindow.
591
 * @param profiles
592
 * @param profile
593
 */
594
void ConfigDialog::setProfiles(QHash<QString, QHash<QString, QString>> profiles,
×
595
                               const QString &currentProfile) {
596
  if (profiles.contains("")) {
×
597
    profiles.remove("");
×
598
    // remove weird "" key value pairs
599
  }
600

601
  // Cache profiles for use in onProfileTableSelectionChanged
602
  m_profiles = profiles;
×
603

604
  ui->profileTable->setRowCount(static_cast<int>(profiles.count()));
×
605
  QHashIterator<QString, QHash<QString, QString>> i(profiles);
×
606
  int n = 0;
607
  while (i.hasNext()) {
×
608
    i.next();
609
    if (!i.value().isEmpty() && !i.key().isEmpty()) {
×
610
      ui->profileTable->setItem(n, 0, new QTableWidgetItem(i.key()));
×
611
      ui->profileTable->setItem(n, 1,
×
612
                                new QTableWidgetItem(i.value().value("path")));
×
613
      ui->profileTable->setItem(
×
614
          n, 2, new QTableWidgetItem(i.value().value("signingKey")));
×
615
      if (i.key() == currentProfile) {
×
616
        ui->profileTable->selectRow(n);
×
617
        // Load git settings for current profile
618
        loadGitSettingsForProfile(currentProfile, m_profiles);
×
619
      }
620
    }
621
    ++n;
×
622
  }
623
}
×
624

625
/**
626
 * @brief Load git settings for a specific profile.
627
 * @param profileName The profile name.
628
 * @param profiles The profiles hash containing git settings.
629
 */
630
void ConfigDialog::loadGitSettingsForProfile(
×
631
    const QString &profileName,
632
    const QHash<QString, QHash<QString, QString>> &profiles) {
633
  if (profiles.contains(profileName)) {
×
634
    const QHash<QString, QString> &profile = profiles.value(profileName);
×
635
    QString useGitStr = profile.value("useGit");
×
636
    QString autoPushStr = profile.value("autoPush");
×
637
    QString autoPullStr = profile.value("autoPull");
×
638

639
    // Load profile-specific git settings if set, otherwise use global settings
640
    if (!useGitStr.isEmpty()) {
×
641
      useGit(useGitStr == "true");
×
642
      ui->checkBoxAutoPush->setEnabled(ui->checkBoxUseGit->isChecked());
×
643
      ui->checkBoxAutoPull->setEnabled(ui->checkBoxUseGit->isChecked());
×
644
      if (autoPushStr == "true" || autoPushStr == "false") {
×
645
        ui->checkBoxAutoPush->setChecked(autoPushStr == "true");
×
646
      }
647
      if (autoPullStr == "true" || autoPullStr == "false") {
×
648
        ui->checkBoxAutoPull->setChecked(autoPullStr == "true");
×
649
      }
650
    }
651
    // If not set (empty), leave global settings as-is for migration
652
  }
×
653
}
×
654

655
/**
656
 * @brief ConfigDialog::getProfiles return profile list.
657
 * @return
658
 */
659
auto ConfigDialog::getProfiles() -> QHash<QString, QHash<QString, QString>> {
×
660
  // Get currently selected profile name
661
  QList<QTableWidgetItem *> selected = ui->profileTable->selectedItems();
×
662
  QString selectedProfile;
×
663
  if (!selected.isEmpty()) {
×
664
    selectedProfile =
665
        ui->profileTable->item(selected.first()->row(), 0)->text();
×
666
  }
667

668
  // Use cached m_profiles to preserve git settings for non-selected profiles
669
  QHash<QString, QHash<QString, QString>> existingProfiles = m_profiles;
670

671
  QHash<QString, QHash<QString, QString>> profiles;
×
672
  // Check?
673
  for (int i = 0; i < ui->profileTable->rowCount(); ++i) {
×
674
    QHash<QString, QString> profile;
×
675
    QTableWidgetItem *pathItem = ui->profileTable->item(i, 1);
×
676
    if (nullptr != pathItem) {
×
677
      QTableWidgetItem *item = ui->profileTable->item(i, 0);
×
678
      if (item == nullptr) {
×
679
        continue;
680
      }
681
      profile["path"] = pathItem->text();
×
682
      QTableWidgetItem *signingKeyItem = ui->profileTable->item(i, 2);
×
683
      if (nullptr != signingKeyItem) {
×
684
        profile["signingKey"] = signingKeyItem->text();
×
685
      }
686

687
      // Only update git settings for the currently selected profile
688
      // Preserve existing git settings for other profiles
689
      if (item->text() == selectedProfile) {
×
690
        profile["useGit"] = ui->checkBoxUseGit->isChecked() ? "true" : "false";
×
691
        profile["autoPush"] =
×
692
            ui->checkBoxAutoPush->isChecked() ? "true" : "false";
×
693
        profile["autoPull"] =
×
694
            ui->checkBoxAutoPull->isChecked() ? "true" : "false";
×
695
      } else if (existingProfiles.contains(item->text())) {
×
696
        // Preserve existing git settings for non-selected profiles
697
        const QHash<QString, QString> &existing =
698
            existingProfiles.value(item->text());
×
699
        if (existing.contains("useGit")) {
×
700
          profile["useGit"] = existing.value("useGit");
×
701
        }
702
        if (existing.contains("autoPush")) {
×
703
          profile["autoPush"] = existing.value("autoPush");
×
704
        }
705
        if (existing.contains("autoPull")) {
×
706
          profile["autoPull"] = existing.value("autoPull");
×
707
        }
708
      }
×
709
      profiles.insert(item->text(), profile);
×
710
    }
711
  }
×
712
  // Update cache with current in-dialog state
713
  m_profiles = profiles;
×
714
  return profiles;
×
715
}
×
716

717
/**
718
 * @brief Initialize new profiles that need pass/git initialization.
719
 * @param existingProfiles The profiles that existed before the dialog was
720
 * opened.
721
 */
722
void ConfigDialog::initializeNewProfiles(
×
723
    const QHash<QString, QHash<QString, QString>> &existingProfiles) {
724
  QHash<QString, QHash<QString, QString>> newProfiles = getProfiles();
×
725

726
  // Collect keys and sort for deterministic iteration
727
  QStringList keys = newProfiles.keys();
×
728
  keys.sort();
729

730
  for (const QString &name : keys) {
×
731
    const QString &path = newProfiles.value(name).value("path");
×
732

733
    // Skip if already existed before (check by name and path)
734
    if (existingProfiles.contains(name) &&
×
735
        existingProfiles.value(name).value("path") == path) {
×
736
      continue;
×
737
    }
738

739
    // This is a new profile - create directory if needed and initialize
740
    // Note: needsInit returns false for non-existent directories, so we
741
    // must create the directory first.
742
    QString cleanPath = QDir::cleanPath(path);
×
743
    QDir dir(cleanPath);
×
744
    if (!dir.exists()) {
×
745
      if (QMessageBox::question(
×
746
              this, tr("Create profile directory?"),
×
747
              tr("Would you like to create a password store at %1?")
×
748
                  .arg(cleanPath),
×
749
              QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) {
750
        continue;
×
751
      }
752
      if (!QDir().mkpath(cleanPath)) {
×
753
        QMessageBox::warning(
×
754
            this, tr("Error"),
×
755
            tr("Could not create profile directory: %1").arg(cleanPath));
×
756
        continue;
×
757
      }
758
    }
759

760
    // Now check if initialization is needed (directory exists with no .gpg-id)
761
    if (!ProfileInit::needsInit(cleanPath)) {
×
762
      continue;
×
763
    }
764

765
    // Temporarily switch the active store so pass/git init operate on
766
    // the new profile's directory rather than the currently-saved one.
767
    const QString prevStore = QtPassSettings::getPassStore();
×
768
    QtPassSettings::setPassStore(cleanPath);
×
769

770
    // Show user selection dialog for GPG recipients
771
    // UsersDialog will run pass init when accepted
772
    UsersDialog usersDialog(cleanPath, this);
×
773
    usersDialog.setWindowTitle(tr("Select recipients for %1").arg(name));
×
774
    const int result = usersDialog.exec();
×
775

776
    // Use per-profile useGit setting, falling back to global if not set
777
    QString useGitStr = newProfiles.value(name).value("useGit");
×
778
    bool useGit =
779
        useGitStr.isEmpty() ? QtPassSettings::isUseGit() : useGitStr == "true";
×
780

781
    if (result == QDialog::Accepted && useGit) {
×
782
      QtPassSettings::getPass()->GitInit();
×
783
    }
784

785
    // Restore previous store setting
786
    QtPassSettings::setPassStore(prevStore);
×
787
  }
×
788
}
×
789

790
/**
791
 * @brief ConfigDialog::on_addButton_clicked add a profile row.
792
 */
793
void ConfigDialog::on_addButton_clicked() {
×
794
  bool sortingEnabled = ui->profileTable->isSortingEnabled();
×
795
  ui->profileTable->setSortingEnabled(false);
×
796

797
  int n = ui->profileTable->rowCount();
×
798
  ui->profileTable->insertRow(n);
×
799
  ui->profileTable->setItem(n, 0, new QTableWidgetItem(tr("New Profile")));
×
800
  ui->profileTable->setItem(n, 1, new QTableWidgetItem(ui->storePath->text()));
×
801
  ui->profileTable->setItem(n, 2, new QTableWidgetItem());
×
802

803
  ui->profileTable->setSortingEnabled(sortingEnabled);
×
804

805
  int currentRow = ui->profileTable->row(ui->profileTable->item(n, 0));
×
806
  ui->profileTable->selectRow(currentRow);
×
807
  ui->deleteButton->setEnabled(true);
×
808

809
  ui->profileTable->editItem(ui->profileTable->item(currentRow, 0));
×
810
  ui->profileTable->item(currentRow, 0)->setSelected(true);
×
811

812
  validate();
×
813
  updateProfileStatus(currentRow);
×
814
}
×
815

816
/**
817
 * @brief ConfigDialog::on_profileTable_cellDoubleClicked open folder browser
818
 * for path column (column 1).
819
 */
820
void ConfigDialog::on_profileTable_cellDoubleClicked(int row, int column) {
×
821
  if (column == 1) {
×
822
    QString dir = selectFolder();
×
823
    if (!dir.isEmpty()) {
×
824
      ui->profileTable->item(row, 1)->setText(dir);
×
825
    }
826
  }
827
}
×
828

829
/**
830
 * @brief ConfigDialog::on_deleteButton_clicked remove a profile row.
831
 */
832
void ConfigDialog::on_deleteButton_clicked() {
×
833
  QSet<int> selectedRows; //  we use a set to prevent doubles
834
  QList<QTableWidgetItem *> itemList = ui->profileTable->selectedItems();
×
835
  if (itemList.count() == 0) {
×
836
    QMessageBox::warning(this, tr("No profile selected"),
×
837
                         tr("No profile selected to delete"));
×
838
    return;
839
  }
840
  QTableWidgetItem *item;
841
  foreach (item, itemList)
×
842
    selectedRows.insert(item->row());
×
843
  // get a list, and sort it big to small
844
  QList<int> rows = selectedRows.values();
×
845
  std::sort(rows.begin(), rows.end());
×
846
  // now actually do the removing:
847
  foreach (int row, rows)
×
848
    ui->profileTable->removeRow(row);
×
849
  if (ui->profileTable->rowCount() < 1) {
×
850
    ui->deleteButton->setEnabled(false);
×
851
  }
852

853
  validate();
×
854
  updateProfileStatus(-1);
×
855
}
856

857
/**
858
 * @brief ConfigDialog::criticalMessage wrapper for showing critical messages
859
 * in a popup.
860
 * @param title
861
 * @param text
862
 */
863
void ConfigDialog::criticalMessage(const QString &title, const QString &text) {
×
864
  QMessageBox::critical(this, title, text, QMessageBox::Ok, QMessageBox::Ok);
×
865
}
×
866

867
/**
868
 * @brief Checks whether the qrencode executable is available on the system.
869
 * @example
870
 * bool result = ConfigDialog::isQrencodeAvailable();
871
 * std::cout << result << std::endl; // Expected output: true if qrencode is
872
 * found, otherwise false
873
 *
874
 * @return bool - True if qrencode is available; otherwise false. On Windows,
875
 * always returns false.
876
 */
877
auto ConfigDialog::isQrencodeAvailable() -> bool {
×
878
#ifdef Q_OS_WIN
879
  return false;
880
#else
881
  QProcess which;
×
882
  which.start("which", QStringList() << "qrencode");
×
883
  which.waitForFinished();
×
884
  QtPassSettings::setQrencodeExecutable(
×
885
      which.readAllStandardOutput().trimmed());
×
886
  return which.exitCode() == 0;
×
887
#endif
888
}
×
889

890
auto ConfigDialog::isPassOtpAvailable() -> bool {
×
891
#ifdef Q_OS_WIN
892
  return false;
893
#else
894
  QProcess pass;
×
895
  pass.start(QtPassSettings::getPassExecutable(), QStringList() << "otp"
×
896
                                                                << "--help");
×
897
  pass.waitForFinished(2000);
×
898
  return pass.exitCode() == 0;
×
899
#endif
900
}
×
901

902
/**
903
 * @brief ConfigDialog::wizard first-time use wizard.
904
 */
905
void ConfigDialog::wizard() {
×
906
  (void)Util::configIsValid();
×
907
  on_autodetectButton_clicked();
×
908

909
  if (!checkGpgExistence()) {
×
910
    return;
911
  }
912
  if (!checkSecretKeys()) {
×
913
    return;
914
  }
915
  if (!checkPasswordStore()) {
×
916
    return;
917
  }
918
  handleGpgIdFile();
×
919

920
  ui->checkBoxHidePassword->setCheckState(Qt::Checked);
×
921
}
922

923
/**
924
 * @brief Checks whether the configured GnuPG executable exists.
925
 * @example
926
 * bool result = ConfigDialog::checkGpgExistence();
927
 * std::cout << result << std::endl; // Expected output: true if GnuPG is found,
928
 * false otherwise
929
 *
930
 * @return bool - True if the GnuPG path is valid or uses a WSL command prefix;
931
 * false if the executable cannot be found and an error message is shown.
932
 */
933
auto ConfigDialog::checkGpgExistence() -> bool {
×
934
  QString gpg = ui->gpgPath->text();
×
935
  if (!gpg.startsWith("wsl ") && !QFile(gpg).exists()) {
×
936
    criticalMessage(
×
937
        tr("GnuPG not found"),
×
938
#ifdef Q_OS_WIN
939
#ifdef WINSTORE
940
        tr("Please install GnuPG on your system.<br>Install "
941
           "<strong>Ubuntu</strong> from the Microsoft Store to get it.<br>"
942
           "If you already did so, make sure you started it once and<br>"
943
           "click \"Autodetect\" in the next dialog.")
944
#else
945
        tr("Please install GnuPG on your system.<br>Install "
946
           "<strong>Ubuntu</strong> from the Microsoft Store<br>or <a "
947
           "href=\"https://www.gnupg.org/download/#sec-1-2\">download</a> it "
948
           "from GnuPG.org")
949
#endif
950
#else
951
        tr("Please install GnuPG on your system.<br>Install "
×
952
           "<strong>gpg</strong> using your favorite package manager<br>or "
953
           "<a "
954
           "href=\"https://www.gnupg.org/download/#sec-1-2\">download</a> it "
955
           "from GnuPG.org")
956
#endif
957
    );
958
    return false;
×
959
  }
960
  return true;
961
}
962

963
/**
964
 * @brief Checks whether secret keys are available and, if needed, prompts the
965
 * user to generate them.
966
 * @example
967
 * bool result = ConfigDialog.checkSecretKeys();
968
 * std::cout << result << std::endl; // Expected output: true if keys are
969
 * present or key generation dialog is accepted, false otherwise
970
 *
971
 * @return bool - Returns true when secret keys are already available or the key
972
 * generation dialog is accepted; false if the dialog is rejected.
973
 */
974
auto ConfigDialog::checkSecretKeys() -> bool {
×
975
  QString gpg = ui->gpgPath->text();
×
976
  QStringList names = getSecretKeys();
×
977

978
#ifdef QT_DEBUG
979
  dbg() << names;
980
#endif
981

982
  if ((gpg.startsWith("wsl ") || QFile(gpg).exists()) && names.empty()) {
×
983
    KeygenDialog d(this);
×
984
    return d.exec();
×
985
  }
×
986
  return true;
987
}
988

989
/**
990
 * @brief Checks whether the password-store path exists and prompts the user to
991
 * create it if it does not.
992
 * @example
993
 * bool result = ConfigDialog::checkPasswordStore();
994
 * std::cout << std::boolalpha << result << std::endl; // Expected output: true
995
 * if the store exists or is created successfully, false if creation fails
996
 *
997
 * @return bool - True if the password-store exists or is successfully created,
998
 * false if creation fails.
999
 */
1000
auto ConfigDialog::checkPasswordStore() -> bool {
×
1001
  QString passStore = ui->storePath->text();
×
1002

1003
  if (!QFile(passStore).exists()) {
×
1004
    if (QMessageBox::question(
×
1005
            this, tr("Create password-store?"),
×
1006
            tr("Would you like to create a password-store at %1?")
×
1007
                .arg(passStore),
×
1008
            QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
1009
      if (!QDir().mkdir(passStore)) {
×
1010
        QMessageBox::warning(
×
1011
            this, tr("Error"),
×
1012
            tr("Failed to create password-store at: %1").arg(passStore));
×
1013
        return false;
×
1014
      }
1015
#ifdef Q_OS_WIN
1016
      SetFileAttributes(passStore.toStdWString().c_str(),
1017
                        FILE_ATTRIBUTE_HIDDEN);
1018
#endif
1019
      if (ui->checkBoxUseGit->isChecked()) {
×
1020
        emit mainWindow->passGitInitNeeded();
×
1021
      }
1022
      mainWindow->userDialog(passStore);
×
1023
    }
1024
  }
1025
  return true;
1026
}
1027

1028
/**
1029
 * @brief Handles selection and validation of the password store's .gpg-id file.
1030
 * @example
1031
 * ConfigDialog dialog;
1032
 * dialog.handleGpgIdFile();
1033
 *
1034
 * @return void - This method does not return a value; it updates the UI flow
1035
 * and may prompt the user to choose a valid password store.
1036
 */
1037
void ConfigDialog::handleGpgIdFile() {
×
1038
  QString passStore = ui->storePath->text();
×
1039
  if (!QFile(QDir(passStore).filePath(".gpg-id")).exists()) {
×
1040
#ifdef QT_DEBUG
1041
    dbg() << ".gpg-id file does not exist";
1042
#endif
1043
    criticalMessage(tr("Password store not initialised"),
×
1044
                    tr("The folder %1 doesn't seem to be a password store or "
×
1045
                       "is not yet initialised.")
1046
                        .arg(passStore));
×
1047

1048
    while (!QFile(passStore).exists()) {
×
1049
      on_toolButtonStore_clicked();
×
1050
      if (passStore == ui->storePath->text()) {
×
1051
        return;
1052
      }
1053
      passStore = ui->storePath->text();
×
1054
    }
1055
    if (!QFile(passStore + ".gpg-id").exists()) {
×
1056
#ifdef QT_DEBUG
1057
      dbg() << ".gpg-id file still does not exist :/";
1058
#endif
1059
      mainWindow->userDialog(passStore);
×
1060
    }
1061
  }
1062
}
1063

1064
/**
1065
 * @brief ConfigDialog::useTrayIcon set preference for using trayicon.
1066
 * Enable or disable related checkboxes accordingly.
1067
 * @param useSystray
1068
 */
1069
void ConfigDialog::useTrayIcon(bool useSystray) {
×
1070
  if (QSystemTrayIcon::isSystemTrayAvailable()) {
×
1071
    ui->checkBoxUseTrayIcon->setChecked(useSystray);
×
1072
    ui->checkBoxHideOnClose->setEnabled(useSystray);
×
1073
    ui->checkBoxStartMinimized->setEnabled(useSystray);
×
1074

1075
    if (!useSystray) {
×
1076
      ui->checkBoxHideOnClose->setChecked(false);
×
1077
      ui->checkBoxStartMinimized->setChecked(false);
×
1078
    }
1079
  }
1080
}
×
1081

1082
/**
1083
 * @brief ConfigDialog::on_checkBoxUseTrayIcon_clicked enable and disable
1084
 * related checkboxes.
1085
 */
1086
void ConfigDialog::on_checkBoxUseTrayIcon_clicked() {
×
1087
  bool state = ui->checkBoxUseTrayIcon->isChecked();
×
1088
  ui->checkBoxHideOnClose->setEnabled(state);
×
1089
  ui->checkBoxStartMinimized->setEnabled(state);
×
1090
}
×
1091

1092
/**
1093
 * @brief ConfigDialog::closeEvent close this window.
1094
 * @param event
1095
 */
1096
void ConfigDialog::closeEvent(QCloseEvent *event) {
×
1097
  QtPassSettings::setDialogGeometry("configDialog", saveGeometry());
×
1098
  if (!isMaximized()) {
×
1099
    QtPassSettings::setDialogPos("configDialog", pos());
×
1100
    QtPassSettings::setDialogSize("configDialog", size());
×
1101
  }
1102
  QtPassSettings::setDialogMaximized("configDialog", isMaximized());
×
1103
  event->accept();
1104
}
×
1105

1106
/**
1107
 * @brief ConfigDialog::useGit set preference for using git.
1108
 * @param useGit
1109
 */
1110
void ConfigDialog::useGit(bool useGit) {
×
1111
  ui->checkBoxUseGit->setChecked(useGit);
×
1112
  on_checkBoxUseGit_clicked();
×
1113
}
×
1114

1115
/**
1116
 * @brief ConfigDialog::useOtp set preference for using otp plugin.
1117
 * @param useOtp
1118
 */
1119
void ConfigDialog::useOtp(bool useOtp) {
×
1120
  ui->checkBoxUseOtp->setChecked(useOtp);
×
1121
}
×
1122

1123
void ConfigDialog::useGrepSearch(bool useGrepSearch) {
×
1124
  ui->checkBoxUseGrepSearch->setChecked(useGrepSearch);
×
1125
}
×
1126

1127
/**
1128
 * @brief ConfigDialog::useQrencode set preference for using qrencode plugin.
1129
 * @param useQrencode
1130
 */
1131
void ConfigDialog::useQrencode(bool useQrencode) {
×
1132
  ui->checkBoxUseQrencode->setChecked(useQrencode);
×
1133
}
×
1134

1135
/**
1136
 * @brief ConfigDialog::on_checkBoxUseGit_clicked enable or disable related
1137
 * checkboxes.
1138
 */
1139
void ConfigDialog::on_checkBoxUseGit_clicked() {
×
1140
  ui->checkBoxAddGPGId->setEnabled(ui->checkBoxUseGit->isChecked());
×
1141
  ui->checkBoxAutoPull->setEnabled(ui->checkBoxUseGit->isChecked());
×
1142
  ui->checkBoxAutoPush->setEnabled(ui->checkBoxUseGit->isChecked());
×
1143
}
×
1144

1145
/**
1146
 * @brief ConfigDialog::on_toolButtonPwgen_clicked enable or disable related
1147
 * options in the interface.
1148
 */
1149
void ConfigDialog::on_toolButtonPwgen_clicked() {
×
1150
  QString pwgen = selectExecutable();
×
1151
  if (!pwgen.isEmpty()) {
×
1152
    ui->pwgenPath->setText(pwgen);
×
1153
    ui->checkBoxUsePwgen->setEnabled(true);
×
1154
  } else {
1155
    ui->checkBoxUsePwgen->setEnabled(false);
×
1156
    ui->checkBoxUsePwgen->setChecked(false);
×
1157
  }
1158
}
×
1159

1160
/**
1161
 * @brief ConfigDialog::setPwgenPath set pwgen executable path.
1162
 * Enable or disable related options in the interface.
1163
 * @param pwgen
1164
 */
1165
void ConfigDialog::setPwgenPath(const QString &pwgen) {
×
1166
  ui->pwgenPath->setText(pwgen);
×
1167
  if (pwgen.isEmpty()) {
×
1168
    ui->checkBoxUsePwgen->setChecked(false);
×
1169
    ui->checkBoxUsePwgen->setEnabled(false);
×
1170
  }
1171
  on_checkBoxUsePwgen_clicked();
×
1172
}
×
1173

1174
/**
1175
 * @brief ConfigDialog::on_checkBoxUsePwgen_clicked enable or disable related
1176
 * options in the interface.
1177
 */
1178
void ConfigDialog::on_checkBoxUsePwgen_clicked() {
×
1179
  if (ui->radioButtonPass->isChecked())
×
1180
    return;
1181
  bool usePwgen = ui->checkBoxUsePwgen->isChecked();
×
1182
  ui->checkBoxAvoidCapitals->setEnabled(usePwgen);
×
1183
  ui->checkBoxAvoidNumbers->setEnabled(usePwgen);
×
1184
  ui->checkBoxLessRandom->setEnabled(usePwgen);
×
1185
  ui->checkBoxUseSymbols->setEnabled(usePwgen);
×
1186
  ui->lineEditPasswordChars->setEnabled(!usePwgen);
×
1187
  ui->labelPasswordChars->setEnabled(!usePwgen);
×
1188
  ui->passwordCharTemplateSelector->setEnabled(!usePwgen);
×
1189
}
1190

1191
/**
1192
 * @brief ConfigDialog::usePwgen set preference for using pwgen (can be
1193
 * overruled by empty pwgenPath).
1194
 * enable or disable related options in the interface via
1195
 * ConfigDialog::on_checkBoxUsePwgen_clicked
1196
 * @param usePwgen
1197
 */
1198
void ConfigDialog::usePwgen(bool usePwgen) {
×
1199
  if (ui->pwgenPath->text().isEmpty()) {
×
1200
    usePwgen = false;
1201
  }
1202
  ui->checkBoxUsePwgen->setChecked(usePwgen);
×
1203
  on_checkBoxUsePwgen_clicked();
×
1204
}
×
1205

1206
void ConfigDialog::setPasswordConfiguration(
×
1207
    const PasswordConfiguration &config) {
1208
  ui->spinBoxPasswordLength->setValue(config.length);
×
1209
  ui->passwordCharTemplateSelector->setCurrentIndex(config.selected);
×
1210
  if (config.selected != PasswordConfiguration::CUSTOM) {
×
1211
    ui->lineEditPasswordChars->setEnabled(false);
×
1212
  }
1213
  ui->lineEditPasswordChars->setText(config.Characters[config.selected]);
×
1214
}
×
1215

1216
auto ConfigDialog::getPasswordConfiguration() -> PasswordConfiguration {
×
1217
  PasswordConfiguration config;
×
1218
  config.length = ui->spinBoxPasswordLength->value();
×
1219
  config.selected = static_cast<PasswordConfiguration::characterSet>(
×
1220
      ui->passwordCharTemplateSelector->currentIndex());
×
1221
  config.Characters[PasswordConfiguration::CUSTOM] =
1222
      ui->lineEditPasswordChars->text();
×
1223
  return config;
×
1224
}
×
1225

1226
/**
1227
 * @brief ConfigDialog::on_passwordCharTemplateSelector_activated sets the
1228
 * passwordChar Template
1229
 * combo box to the desired entry
1230
 * @param entry of
1231
 */
1232
void ConfigDialog::on_passwordCharTemplateSelector_activated(int index) {
×
1233
  ui->lineEditPasswordChars->setText(
×
1234
      QtPassSettings::getPasswordConfiguration().Characters[index]);
×
1235
  if (index == PasswordConfiguration::CUSTOM) {
×
1236
    ui->lineEditPasswordChars->setEnabled(true);
×
1237
  } else {
1238
    ui->lineEditPasswordChars->setEnabled(false);
×
1239
  }
1240
}
×
1241

1242
/**
1243
 * @brief ConfigDialog::on_checkBoxUseTemplate_clicked enable or disable the
1244
 * template field and options.
1245
 */
1246
void ConfigDialog::on_checkBoxUseTemplate_clicked() {
×
1247
  ui->plainTextEditTemplate->setEnabled(ui->checkBoxUseTemplate->isChecked());
×
1248
  ui->checkBoxTemplateAllFields->setEnabled(
×
1249
      ui->checkBoxUseTemplate->isChecked());
×
1250
}
×
1251

1252
void ConfigDialog::onProfileTableItemChanged(QTableWidgetItem *item) {
×
1253
  validate(item);
×
1254
  updateProfileStatus(item ? item->row() : -1);
×
1255
}
×
1256

1257
void ConfigDialog::onProfileTableSelectionChanged() {
×
1258
  QList<QTableWidgetItem *> selected = ui->profileTable->selectedItems();
×
1259
  if (selected.isEmpty()) {
×
1260
    return;
1261
  }
1262
  QTableWidgetItem *nameItem =
1263
      ui->profileTable->item(selected.first()->row(), 0);
×
1264
  if (nameItem == nullptr) {
×
1265
    return;
1266
  }
1267
  QString profileName = nameItem->text();
×
1268
  loadGitSettingsForProfile(profileName, m_profiles);
×
1269
}
1270

1271
/**
1272
 * @brief Update status bar with profile preview for given row.
1273
 * @param row The row index to preview, or -1 to clear.
1274
 */
1275
void ConfigDialog::updateProfileStatus(int row) {
×
1276
  if (row < 0 || row >= ui->profileTable->rowCount()) {
×
1277
    ui->statusLabel->setText(QString());
×
1278
    return;
×
1279
  }
1280

1281
  QTableWidgetItem *nameItem = ui->profileTable->item(row, 0);
×
1282
  QTableWidgetItem *pathItem = ui->profileTable->item(row, 1);
×
1283

1284
  QString statusMessage;
×
1285
  if (nameItem && !nameItem->text().isEmpty() && pathItem &&
×
1286
      !pathItem->text().isEmpty()) {
×
1287
    QDir dir(QDir::cleanPath(pathItem->text()));
×
1288
    if (!dir.exists()) {
×
1289
      statusMessage = tr("New profile: %1 at %2")
×
1290
                          .arg(nameItem->text())
×
1291
                          .arg(QDir::cleanPath(pathItem->text()));
×
1292
    } else {
1293
      statusMessage = tr("Profile: %1 at %2")
×
1294
                          .arg(nameItem->text())
×
1295
                          .arg(QDir::cleanPath(pathItem->text()));
×
1296
    }
1297
  } else {
×
1298
    statusMessage = tr("Fill in all required fields");
×
1299
  }
1300

1301
  ui->statusLabel->setText(statusMessage);
×
1302
}
1303

1304
/**
1305
 * @brief ConfigDialog::useTemplate set preference for using templates.
1306
 * @param useTemplate
1307
 */
1308
void ConfigDialog::useTemplate(bool useTemplate) {
×
1309
  ui->checkBoxUseTemplate->setChecked(useTemplate);
×
1310
  on_checkBoxUseTemplate_clicked();
×
1311
}
×
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