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

IJHack / QtPass / 27475851299

13 Jun 2026 06:50PM UTC coverage: 55.57%. Remained the same
27475851299

Pull #1523

github

web-flow
Merge 74298220f into 90fc7c4d6
Pull Request #1523: refactor: extract SSH_AUTH_SOCK logic into SshAuthSock module (#1514)

33 of 37 new or added lines in 2 files covered. (89.19%)

3731 of 6714 relevant lines covered (55.57%)

36.9 hits per line

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

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

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

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

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

49
  ui->passPath->setText(QtPassSettings::getPassExecutable());
26✔
50
  setGitPath(QtPassSettings::getGitExecutable());
26✔
51
  ui->gpgPath->setText(QtPassSettings::getGpgExecutable());
26✔
52
  ui->storePath->setText(QtPassSettings::getPassStore());
26✔
53
  ui->sshAuthSockOverride->setText(QtPassSettings::getSshAuthSockOverride());
26✔
54

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

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

75
  ui->checkBoxAvoidCapitals->setChecked(QtPassSettings::isAvoidCapitals());
13✔
76
  ui->checkBoxAvoidNumbers->setChecked(QtPassSettings::isAvoidNumbers());
13✔
77
  ui->checkBoxLessRandom->setChecked(QtPassSettings::isLessRandom());
13✔
78
  ui->checkBoxUseSymbols->setChecked(QtPassSettings::isUseSymbols());
13✔
79
  ui->plainTextEditTemplate->setPlainText(QtPassSettings::getPassTemplate());
26✔
80
  ui->checkBoxTemplateAllFields->setChecked(
26✔
81
      QtPassSettings::isTemplateAllFields());
26✔
82
  ui->checkBoxShowProcessOutput->setChecked(
13✔
83
      QtPassSettings::isShowProcessOutput());
26✔
84
  ui->checkBoxAutoPull->setChecked(QtPassSettings::isAutoPull());
13✔
85
  ui->checkBoxAutoPush->setChecked(QtPassSettings::isAutoPush());
13✔
86
  ui->checkBoxAlwaysOnTop->setChecked(QtPassSettings::isAlwaysOnTop());
13✔
87

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

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

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

105
  usePass(QtPassSettings::isUsePass());
13✔
106
  useAutoclear(QtPassSettings::isUseAutoclear());
13✔
107
  useAutoclearPanel(QtPassSettings::isUseAutoclearPanel());
13✔
108
  useTrayIcon(QtPassSettings::isUseTrayIcon());
13✔
109

110
  setProfiles(QtPassSettings::getProfiles(), QtPassSettings::getProfile());
26✔
111

112
  useGit(QtPassSettings::isUseGit());
13✔
113

114
  useOtp(QtPassSettings::isUseOtp());
13✔
115
  useGrepSearch(QtPassSettings::isUseGrepSearch());
13✔
116
  useQrencode(QtPassSettings::isUseQrencode());
13✔
117

118
  usePwgen(QtPassSettings::isUsePwgen());
13✔
119
  useTemplate(QtPassSettings::isUseTemplate());
13✔
120

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

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

131
  int currentIndex = QtPassSettings::getClipBoardTypeRaw();
13✔
132
  ui->comboBoxClipboard->setCurrentIndex(currentIndex);
13✔
133
  on_comboBoxClipboard_activated(currentIndex);
13✔
134

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

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

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

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

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

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

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

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

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

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

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

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

323
  QtPassSettings::setVersion(VERSION);
×
324

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

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

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

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

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

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

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

388
  return names;
×
389
}
390

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

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

429
  return {};
430
}
×
431

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

445
  return {};
446
}
×
447

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

602
  // Cache profiles for use in onProfileTableSelectionChanged
603
  m_profiles = profiles;
13✔
604

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1124
void ConfigDialog::useGrepSearch(bool useGrepSearch) {
15✔
1125
  ui->checkBoxUseGrepSearch->setChecked(useGrepSearch);
15✔
1126
}
15✔
1127

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1305
/**
1306
 * @brief ConfigDialog::useTemplate set preference for using templates.
1307
 * @param useTemplate
1308
 */
1309
void ConfigDialog::useTemplate(bool useTemplate) {
15✔
1310
  ui->checkBoxUseTemplate->setChecked(useTemplate);
15✔
1311
  on_checkBoxUseTemplate_clicked();
15✔
1312
}
15✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc