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

IJHack / QtPass / 25610586831

09 May 2026 08:05PM UTC coverage: 28.234% (-0.08%) from 28.31%
25610586831

Pull #1439

github

web-flow
Merge f6bbd0f07 into 7c8b36801
Pull Request #1439: fix(ssh-auth-sock): warn when override path looks bogus

0 of 19 new or added lines in 1 file covered. (0.0%)

9 existing lines in 1 file now uncovered.

1886 of 6680 relevant lines covered (28.23%)

27.11 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 <QFileInfo>
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 Q_OS_UNIX
25
#include <sys/stat.h>
26
#endif
27

28
#ifdef QT_DEBUG
29
#include "debughelper.h"
30
#endif
31

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

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

53
  ui->passPath->setText(QtPassSettings::getPassExecutable());
×
54
  setGitPath(QtPassSettings::getGitExecutable());
×
55
  ui->gpgPath->setText(QtPassSettings::getGpgExecutable());
×
56
  ui->storePath->setText(QtPassSettings::getPassStore());
×
57
  ui->sshAuthSockOverride->setText(QtPassSettings::getSshAuthSockOverride());
×
58

59
  ui->spinBoxAutoclearSeconds->setValue(QtPassSettings::getAutoclearSeconds());
×
60
  ui->spinBoxAutoclearPanelSeconds->setValue(
×
61
      QtPassSettings::getAutoclearPanelSeconds());
×
62
  ui->checkBoxHidePassword->setChecked(QtPassSettings::isHidePassword());
×
63
  ui->checkBoxHideContent->setChecked(QtPassSettings::isHideContent());
×
64
  ui->checkBoxUseMonospace->setChecked(QtPassSettings::isUseMonospace());
×
65
  ui->checkBoxDisplayAsIs->setChecked(QtPassSettings::isDisplayAsIs());
×
66
  ui->checkBoxNoLineWrapping->setChecked(QtPassSettings::isNoLineWrapping());
×
67
  ui->checkBoxAddGPGId->setChecked(QtPassSettings::isAddGPGId(true));
×
68

69
  if (QSystemTrayIcon::isSystemTrayAvailable()) {
×
70
    ui->checkBoxHideOnClose->setChecked(QtPassSettings::isHideOnClose());
×
71
    ui->checkBoxStartMinimized->setChecked(QtPassSettings::isStartMinimized());
×
72
  } else {
73
    ui->checkBoxUseTrayIcon->setEnabled(false);
×
74
    ui->checkBoxUseTrayIcon->setToolTip(tr("System tray is not available"));
×
75
    ui->checkBoxHideOnClose->setEnabled(false);
×
76
    ui->checkBoxStartMinimized->setEnabled(false);
×
77
  }
78

79
  ui->checkBoxAvoidCapitals->setChecked(QtPassSettings::isAvoidCapitals());
×
80
  ui->checkBoxAvoidNumbers->setChecked(QtPassSettings::isAvoidNumbers());
×
81
  ui->checkBoxLessRandom->setChecked(QtPassSettings::isLessRandom());
×
82
  ui->checkBoxUseSymbols->setChecked(QtPassSettings::isUseSymbols());
×
83
  ui->plainTextEditTemplate->setPlainText(QtPassSettings::getPassTemplate());
×
84
  ui->checkBoxTemplateAllFields->setChecked(
×
85
      QtPassSettings::isTemplateAllFields());
×
86
  ui->checkBoxShowProcessOutput->setChecked(
×
87
      QtPassSettings::isShowProcessOutput());
×
88
  ui->checkBoxAutoPull->setChecked(QtPassSettings::isAutoPull());
×
89
  ui->checkBoxAutoPush->setChecked(QtPassSettings::isAutoPush());
×
90
  ui->checkBoxAlwaysOnTop->setChecked(QtPassSettings::isAlwaysOnTop());
×
91

92
#if defined(Q_OS_WIN)
93
  ui->checkBoxUseOtp->hide();
94
  ui->checkBoxUseQrencode->hide();
95
  ui->label_10->hide();
96
#endif
97

98
  if (!isPassOtpAvailable()) {
×
99
    ui->checkBoxUseOtp->setEnabled(false);
×
100
    ui->checkBoxUseOtp->setToolTip(
×
101
        tr("Pass OTP extension needs to be installed"));
×
102
  }
103

104
  if (!isQrencodeAvailable()) {
×
105
    ui->checkBoxUseQrencode->setEnabled(false);
×
106
    ui->checkBoxUseQrencode->setToolTip(tr("qrencode needs to be installed"));
×
107
  }
108

109
  usePass(QtPassSettings::isUsePass());
×
110
  useAutoclear(QtPassSettings::isUseAutoclear());
×
111
  useAutoclearPanel(QtPassSettings::isUseAutoclearPanel());
×
112
  useTrayIcon(QtPassSettings::isUseTrayIcon());
×
113

114
  setProfiles(QtPassSettings::getProfiles(), QtPassSettings::getProfile());
×
115

116
  useGit(QtPassSettings::isUseGit());
×
117

118
  useOtp(QtPassSettings::isUseOtp());
×
119
  useGrepSearch(QtPassSettings::isUseGrepSearch());
×
120
  useQrencode(QtPassSettings::isUseQrencode());
×
121

122
  usePwgen(QtPassSettings::isUsePwgen());
×
123
  useTemplate(QtPassSettings::isUseTemplate());
×
124

125
  ui->profileTable->verticalHeader()->hide();
×
126
  ui->profileTable->horizontalHeader()->setSectionResizeMode(
×
127
      1, QHeaderView::Stretch);
128
  ui->label->setText(ui->label->text() + VERSION);
×
129
  ui->comboBoxClipboard->clear();
×
130

131
  ui->comboBoxClipboard->addItem(tr("No Clipboard"));
×
132
  ui->comboBoxClipboard->addItem(tr("Always copy to clipboard"));
×
133
  ui->comboBoxClipboard->addItem(tr("On-demand copy to clipboard"));
×
134

135
  int currentIndex = QtPassSettings::getClipBoardTypeRaw();
×
136
  ui->comboBoxClipboard->setCurrentIndex(currentIndex);
×
137
  on_comboBoxClipboard_activated(currentIndex);
×
138

139
  QClipboard *clip = QApplication::clipboard();
×
140
  if (!clip->supportsSelection()) {
×
141
    useSelection(false);
×
142
    ui->checkBoxSelection->setVisible(false);
×
143
  } else {
144
    useSelection(QtPassSettings::isUseSelection());
×
145
  }
146

147
  if (!Util::configIsValid()) {
×
148
    // Show Programs tab, which is likely
149
    // what the user needs to fix now.
150
    ui->tabWidget->setCurrentIndex(1);
×
151
  }
152

153
  connect(ui->profileTable, &QTableWidget::itemChanged, this,
×
154
          &ConfigDialog::onProfileTableItemChanged);
×
155
  connect(ui->profileTable, &QTableWidget::itemSelectionChanged, this,
×
156
          &ConfigDialog::onProfileTableSelectionChanged);
×
157
  connect(this, &ConfigDialog::accepted, this, &ConfigDialog::on_accepted);
×
158
}
×
159

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

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

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

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

209
  if (item == nullptr) {
×
210
    for (int i = 0; i < ui->profileTable->rowCount(); i++) {
×
211
      for (int j = 0; j < ui->profileTable->columnCount(); j++) {
×
212
        QTableWidgetItem *_item = ui->profileTable->item(i, j);
×
213

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

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

240
  ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(status);
×
241
}
×
242

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

329
  QtPassSettings::setVersion(VERSION);
×
330

331
  // Initialize new profiles that need pass/git initialization
332
  initializeNewProfiles(existingProfiles);
×
333
}
×
334

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

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

373
/**
374
 * @brief ConfigDialog::on_radioButtonPass_clicked wrapper for
375
 * ConfigDialog::setGroupBoxState()
376
 */
377
void ConfigDialog::on_radioButtonPass_clicked() { setGroupBoxState(); }
×
378

379
/**
380
 * @brief ConfigDialog::getSecretKeys get list of secret/private keys
381
 * @return QStringList keys
382
 */
383
auto ConfigDialog::getSecretKeys() -> QStringList {
×
384
  QList<UserInfo> keys = QtPassSettings::getPass()->listKeys("", true);
×
385
  QStringList names;
×
386

387
  if (keys.empty()) {
×
388
    return names;
389
  }
390

391
  foreach (const UserInfo &sec, keys)
×
392
    names << sec.name;
×
393

394
  return names;
×
395
}
396

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

423
/**
424
 * @brief ConfigDialog::selectExecutable pop-up to choose an executable.
425
 * @return
426
 */
427
auto ConfigDialog::selectExecutable() -> QString {
×
428
  QFileDialog dialog(this);
×
429
  dialog.setFileMode(QFileDialog::ExistingFile);
×
430
  dialog.setOption(QFileDialog::ReadOnly);
×
431
  if (dialog.exec()) {
×
432
    return dialog.selectedFiles().constFirst();
×
433
  }
434

435
  return {};
436
}
×
437

438
/**
439
 * @brief ConfigDialog::selectFolder pop-up to choose a folder.
440
 * @return
441
 */
442
auto ConfigDialog::selectFolder() -> QString {
×
443
  QFileDialog dialog(this);
×
444
  dialog.setFileMode(QFileDialog::Directory);
×
445
  dialog.setFilter(QDir::NoFilter);
×
446
  dialog.setOption(QFileDialog::ShowDirsOnly);
×
447
  if (dialog.exec()) {
×
448
    return dialog.selectedFiles().constFirst();
×
449
  }
450

451
  return {};
452
}
×
453

454
/**
455
 * @brief ConfigDialog::on_toolButtonGit_clicked get git application.
456
 * Enable checkboxes if found.
457
 */
458
void ConfigDialog::on_toolButtonGit_clicked() {
×
459
  QString git = selectExecutable();
×
460
  bool state = !git.isEmpty();
×
461
  if (state) {
×
462
    ui->gitPath->setText(git);
×
463
  } else {
464
    useGit(false);
×
465
  }
466

467
  ui->checkBoxUseGit->setEnabled(state);
×
468
}
×
469

470
/**
471
 * @brief ConfigDialog::on_toolButtonGpg_clicked get gpg application.
472
 */
473
void ConfigDialog::on_toolButtonGpg_clicked() {
×
474
  QString gpg = selectExecutable();
×
475
  if (!gpg.isEmpty()) {
×
476
    ui->gpgPath->setText(gpg);
×
477
  }
478
}
×
479

480
/**
481
 * @brief ConfigDialog::on_pushButtonGenerateKey_clicked open keygen dialog.
482
 */
483
void ConfigDialog::on_pushButtonGenerateKey_clicked() {
×
484
  KeygenDialog d(this);
×
485
  d.exec();
×
486
}
×
487

488
/**
489
 * @brief ConfigDialog::on_toolButtonPass_clicked get pass application.
490
 */
491
void ConfigDialog::on_toolButtonPass_clicked() {
×
492
  QString pass = selectExecutable();
×
493
  if (!pass.isEmpty()) {
×
494
    ui->passPath->setText(pass);
×
495
  }
496
}
×
497

498
/**
499
 * @brief ConfigDialog::on_toolButtonStore_clicked get .password-store
500
 * location.
501
 */
502
void ConfigDialog::on_toolButtonStore_clicked() {
×
503
  QString store = selectFolder();
×
504
  if (!store.isEmpty()) {
×
505
    ui->storePath->setText(store);
×
506
  }
507
}
×
508

509
/**
510
 * @brief ConfigDialog::on_comboBoxClipboard_activated show and hide options.
511
 * @param index of selectbox (0 = no clipboard).
512
 */
513
void ConfigDialog::on_comboBoxClipboard_activated(int index) {
×
514
  bool state = index > 0;
×
515

516
  ui->checkBoxSelection->setEnabled(state);
×
517
  ui->checkBoxAutoclear->setEnabled(state);
×
518
  ui->checkBoxHidePassword->setEnabled(state);
×
519
  ui->checkBoxHideContent->setEnabled(state);
×
520
  if (state) {
×
521
    ui->spinBoxAutoclearSeconds->setEnabled(ui->checkBoxAutoclear->isChecked());
×
522
    ui->labelSeconds->setEnabled(ui->checkBoxAutoclear->isChecked());
×
523
  } else {
524
    ui->spinBoxAutoclearSeconds->setEnabled(false);
×
525
    ui->labelSeconds->setEnabled(false);
×
526
  }
527
}
×
528

529
/**
530
 * @brief ConfigDialog::on_checkBoxAutoclearPanel_clicked enable and disable
531
 * options based on autoclear use.
532
 */
533
void ConfigDialog::on_checkBoxAutoclearPanel_clicked() {
×
534
  bool state = ui->checkBoxAutoclearPanel->isChecked();
×
535
  ui->spinBoxAutoclearPanelSeconds->setEnabled(state);
×
536
  ui->labelPanelSeconds->setEnabled(state);
×
537
}
×
538

539
/**
540
 * @brief ConfigDialog::useSelection set the clipboard type use from
541
 * MainWindow.
542
 * @param useSelection
543
 */
544
void ConfigDialog::useSelection(bool useSelection) {
×
545
  ui->checkBoxSelection->setChecked(useSelection);
×
546
  on_checkBoxSelection_clicked();
×
547
}
×
548

549
/**
550
 * @brief ConfigDialog::useAutoclear set the clipboard autoclear use from
551
 * MainWindow.
552
 * @param useAutoclear
553
 */
554
void ConfigDialog::useAutoclear(bool useAutoclear) {
×
555
  ui->checkBoxAutoclear->setChecked(useAutoclear);
×
556
  on_checkBoxAutoclear_clicked();
×
557
}
×
558

559
/**
560
 * @brief ConfigDialog::useAutoclearPanel set the panel autoclear use from
561
 * MainWindow.
562
 * @param useAutoclearPanel
563
 */
564
void ConfigDialog::useAutoclearPanel(bool useAutoclearPanel) {
×
565
  ui->checkBoxAutoclearPanel->setChecked(useAutoclearPanel);
×
566
  on_checkBoxAutoclearPanel_clicked();
×
567
}
×
568

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

577
/**
578
 * @brief ConfigDialog::on_checkBoxAutoclear_clicked checkbox clicked, update
579
 * state via ConfigDialog::on_comboBoxClipboard_activated
580
 */
581
void ConfigDialog::on_checkBoxAutoclear_clicked() {
×
582
  on_comboBoxClipboard_activated(ui->comboBoxClipboard->currentIndex());
×
583
}
×
584

585
/**
586
 * @brief ConfigDialog::genKey tunnel function to make MainWindow generate a
587
 * gpg key pair.
588
 * @param batch
589
 * @param dialog
590
 */
591
void ConfigDialog::genKey(const QString &batch, QDialog *dialog) {
×
592
  mainWindow->generateKeyPair(batch, dialog);
×
593
}
×
594

595
/**
596
 * @brief ConfigDialog::setProfiles set the profiles and chosen profile from
597
 * MainWindow.
598
 * @param profiles
599
 * @param profile
600
 */
601
void ConfigDialog::setProfiles(QHash<QString, QHash<QString, QString>> profiles,
×
602
                               const QString &currentProfile) {
603
  if (profiles.contains("")) {
×
604
    profiles.remove("");
×
605
    // remove weird "" key value pairs
606
  }
607

608
  // Cache profiles for use in onProfileTableSelectionChanged
609
  m_profiles = profiles;
×
610

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

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

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

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

675
  // Use cached m_profiles to preserve git settings for non-selected profiles
676
  QHash<QString, QHash<QString, QString>> existingProfiles = m_profiles;
677

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

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

724
/**
725
 * @brief Initialize new profiles that need pass/git initialization.
726
 * @param existingProfiles The profiles that existed before the dialog was
727
 * opened.
728
 */
729
void ConfigDialog::initializeNewProfiles(
×
730
    const QHash<QString, QHash<QString, QString>> &existingProfiles) {
731
  QHash<QString, QHash<QString, QString>> newProfiles = getProfiles();
×
732

733
  // Collect keys and sort for deterministic iteration
734
  QStringList keys = newProfiles.keys();
×
735
  keys.sort();
736

737
  for (const QString &name : keys) {
×
738
    const QString &path = newProfiles.value(name).value("path");
×
739

740
    // Skip if already existed before (check by name and path)
741
    if (existingProfiles.contains(name) &&
×
742
        existingProfiles.value(name).value("path") == path) {
×
743
      continue;
×
744
    }
745

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

767
    // Now check if initialization is needed (directory exists with no .gpg-id)
768
    if (!ProfileInit::needsInit(cleanPath)) {
×
769
      continue;
×
770
    }
771

772
    // Temporarily switch the active store so pass/git init operate on
773
    // the new profile's directory rather than the currently-saved one.
774
    const QString prevStore = QtPassSettings::getPassStore();
×
775
    QtPassSettings::setPassStore(cleanPath);
×
776

777
    // Show user selection dialog for GPG recipients
778
    // UsersDialog will run pass init when accepted
779
    UsersDialog usersDialog(cleanPath, this);
×
780
    usersDialog.setWindowTitle(tr("Select recipients for %1").arg(name));
×
781
    const int result = usersDialog.exec();
×
782

783
    // Use per-profile useGit setting, falling back to global if not set
784
    QString useGitStr = newProfiles.value(name).value("useGit");
×
785
    bool useGit =
786
        useGitStr.isEmpty() ? QtPassSettings::isUseGit() : useGitStr == "true";
×
787

788
    if (result == QDialog::Accepted && useGit) {
×
789
      QtPassSettings::getPass()->GitInit();
×
790
    }
791

792
    // Restore previous store setting
793
    QtPassSettings::setPassStore(prevStore);
×
794
  }
×
795
}
×
796

797
/**
798
 * @brief ConfigDialog::on_addButton_clicked add a profile row.
799
 */
800
void ConfigDialog::on_addButton_clicked() {
×
801
  bool sortingEnabled = ui->profileTable->isSortingEnabled();
×
802
  ui->profileTable->setSortingEnabled(false);
×
803

804
  int n = ui->profileTable->rowCount();
×
805
  ui->profileTable->insertRow(n);
×
806
  ui->profileTable->setItem(n, 0, new QTableWidgetItem(tr("New Profile")));
×
807
  ui->profileTable->setItem(n, 1, new QTableWidgetItem(ui->storePath->text()));
×
808
  ui->profileTable->setItem(n, 2, new QTableWidgetItem());
×
809

810
  ui->profileTable->setSortingEnabled(sortingEnabled);
×
811

812
  int currentRow = ui->profileTable->row(ui->profileTable->item(n, 0));
×
813
  ui->profileTable->selectRow(currentRow);
×
814
  ui->deleteButton->setEnabled(true);
×
815

816
  ui->profileTable->editItem(ui->profileTable->item(currentRow, 0));
×
817
  ui->profileTable->item(currentRow, 0)->setSelected(true);
×
818

819
  validate();
×
820
  updateProfileStatus(currentRow);
×
821
}
×
822

823
/**
824
 * @brief ConfigDialog::on_profileTable_cellDoubleClicked open folder browser
825
 * for path column (column 1).
826
 */
827
void ConfigDialog::on_profileTable_cellDoubleClicked(int row, int column) {
×
828
  if (column == 1) {
×
829
    QString dir = selectFolder();
×
830
    if (!dir.isEmpty()) {
×
831
      ui->profileTable->item(row, 1)->setText(dir);
×
832
    }
833
  }
834
}
×
835

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

860
  validate();
×
861
  updateProfileStatus(-1);
×
862
}
863

864
/**
865
 * @brief ConfigDialog::criticalMessage wrapper for showing critical messages
866
 * in a popup.
867
 * @param title
868
 * @param text
869
 */
870
void ConfigDialog::criticalMessage(const QString &title, const QString &text) {
×
871
  QMessageBox::critical(this, title, text, QMessageBox::Ok, QMessageBox::Ok);
×
872
}
×
873

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

897
auto ConfigDialog::isPassOtpAvailable() -> bool {
×
898
#ifdef Q_OS_WIN
899
  return false;
900
#else
901
  QProcess pass;
×
902
  pass.start(QtPassSettings::getPassExecutable(), QStringList() << "otp"
×
903
                                                                << "--help");
×
904
  pass.waitForFinished(2000);
×
905
  return pass.exitCode() == 0;
×
906
#endif
907
}
×
908

909
/**
910
 * @brief ConfigDialog::wizard first-time use wizard.
911
 */
912
void ConfigDialog::wizard() {
×
913
  (void)Util::configIsValid();
×
914
  on_autodetectButton_clicked();
×
915

916
  if (!checkGpgExistence()) {
×
917
    return;
918
  }
919
  if (!checkSecretKeys()) {
×
920
    return;
921
  }
922
  if (!checkPasswordStore()) {
×
923
    return;
924
  }
925
  handleGpgIdFile();
×
926

927
  ui->checkBoxHidePassword->setCheckState(Qt::Checked);
×
928
}
929

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

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

985
#ifdef QT_DEBUG
986
  dbg() << names;
987
#endif
988

989
  if ((gpg.startsWith("wsl ") || QFile(gpg).exists()) && names.empty()) {
×
990
    KeygenDialog d(this);
×
991
    return d.exec();
×
992
  }
×
993
  return true;
994
}
995

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

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

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

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

1071
/**
1072
 * @brief ConfigDialog::useTrayIcon set preference for using trayicon.
1073
 * Enable or disable related checkboxes accordingly.
1074
 * @param useSystray
1075
 */
1076
void ConfigDialog::useTrayIcon(bool useSystray) {
×
1077
  if (QSystemTrayIcon::isSystemTrayAvailable()) {
×
1078
    ui->checkBoxUseTrayIcon->setChecked(useSystray);
×
1079
    ui->checkBoxHideOnClose->setEnabled(useSystray);
×
1080
    ui->checkBoxStartMinimized->setEnabled(useSystray);
×
1081

1082
    if (!useSystray) {
×
1083
      ui->checkBoxHideOnClose->setChecked(false);
×
1084
      ui->checkBoxStartMinimized->setChecked(false);
×
1085
    }
1086
  }
1087
}
×
1088

1089
/**
1090
 * @brief ConfigDialog::on_checkBoxUseTrayIcon_clicked enable and disable
1091
 * related checkboxes.
1092
 */
1093
void ConfigDialog::on_checkBoxUseTrayIcon_clicked() {
×
1094
  bool state = ui->checkBoxUseTrayIcon->isChecked();
×
1095
  ui->checkBoxHideOnClose->setEnabled(state);
×
1096
  ui->checkBoxStartMinimized->setEnabled(state);
×
1097
}
×
1098

1099
/**
1100
 * @brief ConfigDialog::closeEvent close this window.
1101
 * @param event
1102
 */
1103
void ConfigDialog::closeEvent(QCloseEvent *event) {
×
1104
  QtPassSettings::setDialogGeometry("configDialog", saveGeometry());
×
1105
  if (!isMaximized()) {
×
1106
    QtPassSettings::setDialogPos("configDialog", pos());
×
1107
    QtPassSettings::setDialogSize("configDialog", size());
×
1108
  }
1109
  QtPassSettings::setDialogMaximized("configDialog", isMaximized());
×
1110
  event->accept();
1111
}
×
1112

1113
/**
1114
 * @brief ConfigDialog::useGit set preference for using git.
1115
 * @param useGit
1116
 */
1117
void ConfigDialog::useGit(bool useGit) {
×
1118
  ui->checkBoxUseGit->setChecked(useGit);
×
1119
  on_checkBoxUseGit_clicked();
×
1120
}
×
1121

1122
/**
1123
 * @brief ConfigDialog::useOtp set preference for using otp plugin.
1124
 * @param useOtp
1125
 */
1126
void ConfigDialog::useOtp(bool useOtp) {
×
1127
  ui->checkBoxUseOtp->setChecked(useOtp);
×
1128
}
×
1129

1130
void ConfigDialog::useGrepSearch(bool useGrepSearch) {
×
1131
  ui->checkBoxUseGrepSearch->setChecked(useGrepSearch);
×
1132
}
×
1133

1134
/**
1135
 * @brief ConfigDialog::useQrencode set preference for using qrencode plugin.
1136
 * @param useQrencode
1137
 */
1138
void ConfigDialog::useQrencode(bool useQrencode) {
×
1139
  ui->checkBoxUseQrencode->setChecked(useQrencode);
×
1140
}
×
1141

1142
/**
1143
 * @brief ConfigDialog::on_checkBoxUseGit_clicked enable or disable related
1144
 * checkboxes.
1145
 */
1146
void ConfigDialog::on_checkBoxUseGit_clicked() {
×
1147
  ui->checkBoxAddGPGId->setEnabled(ui->checkBoxUseGit->isChecked());
×
1148
  ui->checkBoxAutoPull->setEnabled(ui->checkBoxUseGit->isChecked());
×
1149
  ui->checkBoxAutoPush->setEnabled(ui->checkBoxUseGit->isChecked());
×
1150
}
×
1151

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

1167
/**
1168
 * @brief ConfigDialog::setPwgenPath set pwgen executable path.
1169
 * Enable or disable related options in the interface.
1170
 * @param pwgen
1171
 */
1172
void ConfigDialog::setPwgenPath(const QString &pwgen) {
×
1173
  ui->pwgenPath->setText(pwgen);
×
1174
  if (pwgen.isEmpty()) {
×
1175
    ui->checkBoxUsePwgen->setChecked(false);
×
1176
    ui->checkBoxUsePwgen->setEnabled(false);
×
1177
  }
1178
  on_checkBoxUsePwgen_clicked();
×
1179
}
×
1180

1181
/**
1182
 * @brief ConfigDialog::on_checkBoxUsePwgen_clicked enable or disable related
1183
 * options in the interface.
1184
 */
1185
void ConfigDialog::on_checkBoxUsePwgen_clicked() {
×
1186
  if (ui->radioButtonPass->isChecked())
×
1187
    return;
1188
  bool usePwgen = ui->checkBoxUsePwgen->isChecked();
×
1189
  ui->checkBoxAvoidCapitals->setEnabled(usePwgen);
×
1190
  ui->checkBoxAvoidNumbers->setEnabled(usePwgen);
×
1191
  ui->checkBoxLessRandom->setEnabled(usePwgen);
×
1192
  ui->checkBoxUseSymbols->setEnabled(usePwgen);
×
1193
  ui->lineEditPasswordChars->setEnabled(!usePwgen);
×
1194
  ui->labelPasswordChars->setEnabled(!usePwgen);
×
1195
  ui->passwordCharTemplateSelector->setEnabled(!usePwgen);
×
1196
}
1197

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

1213
void ConfigDialog::setPasswordConfiguration(
×
1214
    const PasswordConfiguration &config) {
1215
  ui->spinBoxPasswordLength->setValue(config.length);
×
1216
  ui->passwordCharTemplateSelector->setCurrentIndex(config.selected);
×
1217
  if (config.selected != PasswordConfiguration::CUSTOM) {
×
1218
    ui->lineEditPasswordChars->setEnabled(false);
×
1219
  }
1220
  ui->lineEditPasswordChars->setText(config.Characters[config.selected]);
×
1221
}
×
1222

1223
auto ConfigDialog::getPasswordConfiguration() -> PasswordConfiguration {
×
1224
  PasswordConfiguration config;
×
1225
  config.length = ui->spinBoxPasswordLength->value();
×
1226
  config.selected = static_cast<PasswordConfiguration::characterSet>(
×
1227
      ui->passwordCharTemplateSelector->currentIndex());
×
1228
  config.Characters[PasswordConfiguration::CUSTOM] =
1229
      ui->lineEditPasswordChars->text();
×
1230
  return config;
×
1231
}
×
1232

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

1249
/**
1250
 * @brief ConfigDialog::on_checkBoxUseTemplate_clicked enable or disable the
1251
 * template field and options.
1252
 */
1253
void ConfigDialog::on_checkBoxUseTemplate_clicked() {
×
1254
  ui->plainTextEditTemplate->setEnabled(ui->checkBoxUseTemplate->isChecked());
×
1255
  ui->checkBoxTemplateAllFields->setEnabled(
×
1256
      ui->checkBoxUseTemplate->isChecked());
×
1257
}
×
1258

1259
void ConfigDialog::onProfileTableItemChanged(QTableWidgetItem *item) {
×
1260
  validate(item);
×
1261
  updateProfileStatus(item ? item->row() : -1);
×
1262
}
×
1263

1264
void ConfigDialog::onProfileTableSelectionChanged() {
×
1265
  QList<QTableWidgetItem *> selected = ui->profileTable->selectedItems();
×
1266
  if (selected.isEmpty()) {
×
1267
    return;
1268
  }
1269
  QTableWidgetItem *nameItem =
1270
      ui->profileTable->item(selected.first()->row(), 0);
×
1271
  if (nameItem == nullptr) {
×
1272
    return;
1273
  }
1274
  QString profileName = nameItem->text();
×
1275
  loadGitSettingsForProfile(profileName, m_profiles);
×
1276
}
1277

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

1288
  QTableWidgetItem *nameItem = ui->profileTable->item(row, 0);
×
1289
  QTableWidgetItem *pathItem = ui->profileTable->item(row, 1);
×
1290

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

1308
  ui->statusLabel->setText(statusMessage);
×
1309
}
1310

1311
/**
1312
 * @brief ConfigDialog::useTemplate set preference for using templates.
1313
 * @param useTemplate
1314
 */
1315
void ConfigDialog::useTemplate(bool useTemplate) {
×
1316
  ui->checkBoxUseTemplate->setChecked(useTemplate);
×
1317
  on_checkBoxUseTemplate_clicked();
×
1318
}
×
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