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

IJHack / QtPass / 25579973242

08 May 2026 09:16PM UTC coverage: 28.036% (+0.005%) from 28.031%
25579973242

push

github

web-flow
refactor: enable bugprone-* in clang-tidy + fix narrowing conversions (#1435)

After clearing the modernize-* / performance-* findings in #1432,
extended the .clang-tidy check set to include bugprone-* with two
exclusions for noise that doesn't fit the project's idioms:

- bugprone-throwing-static-initialization (67 findings): every
  static QString / QRegularExpression in the codebase. Standard Qt
  pattern; these constructors don't throw in practice. Disabling
  matches the convention in most Qt projects.

- bugprone-easily-swappable-parameters (22 findings): adjacent
  same-typed parameters. Stylistic; would require API-breaking
  signature changes that Qt's slot wiring expects.

That left 10 real bugprone-narrowing-conversions findings, all
qsizetype → int truncations (Qt 6 returns 64-bit qsizetype from
indexOf/size/count, but a lot of the codebase uses int).

Pattern: prefer qsizetype where the value stays internal; cast at
Qt-API boundaries that take int.

- configdialog.cpp setRowCount: static_cast<int> (Qt API)
- executor.cpp var.indexOf: int → qsizetype
- filecontent.cpp line.indexOf: int → qsizetype
- mainwindow.cpp tr count: static_cast<int> (Qt API)
- pass.cpp lastIndexOf: int → qsizetype
- pass.cpp *count = recipients.size(): static_cast<int> (out-param)
- passworddialog.cpp template indices: int → qsizetype
- qtpass.cpp prompt index: int → qsizetype

Validation:
- clang-tidy with new config: 0 findings in QtPass-owned code.
- tst_util: 106/106 pass.
- Full build clean.

cert-* yields no unique findings beyond bugprone aliases (the only
one was cert-err58-cpp = bugprone-throwing-static-initialization),
so not separately enabled.

4 of 7 new or added lines in 6 files covered. (57.14%)

1 existing line in 1 file now uncovered.

1854 of 6613 relevant lines covered (28.04%)

27.12 hits per line

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

296
  QtPassSettings::setVersion(VERSION);
×
297

298
  // Initialize new profiles that need pass/git initialization
299
  initializeNewProfiles(existingProfiles);
×
300
}
×
301

302
/**
303
 * @brief Automatically detects required external binaries in the system PATH
304
 * and updates the dialog fields.
305
 * @example
306
 * ConfigDialog configDialog;
307
 * configDialog.on_autodetectButton_clicked();
308
 *
309
 * @return void - This function does not return a value.
310
 */
311
void ConfigDialog::on_autodetectButton_clicked() {
×
312
  QString pass = Util::findBinaryInPath("pass");
×
313
  if (!pass.isEmpty()) {
×
314
    ui->passPath->setText(pass);
×
315
  }
316
  usePass(!pass.isEmpty());
×
317
  QString gpg = Util::findBinaryInPath("gpg2");
×
318
  if (gpg.isEmpty()) {
×
319
    gpg = Util::findBinaryInPath("gpg");
×
320
  }
321
  if (!gpg.isEmpty()) {
×
322
    ui->gpgPath->setText(gpg);
×
323
  }
324
  QString git = Util::findBinaryInPath("git");
×
325
  if (!git.isEmpty()) {
×
326
    ui->gitPath->setText(git);
×
327
  }
328
  QString pwgen = Util::findBinaryInPath("pwgen");
×
329
  if (!pwgen.isEmpty()) {
×
330
    ui->pwgenPath->setText(pwgen);
×
331
  }
332
}
×
333

334
/**
335
 * @brief ConfigDialog::on_radioButtonNative_clicked wrapper for
336
 * ConfigDialog::setGroupBoxState()
337
 */
338
void ConfigDialog::on_radioButtonNative_clicked() { setGroupBoxState(); }
×
339

340
/**
341
 * @brief ConfigDialog::on_radioButtonPass_clicked wrapper for
342
 * ConfigDialog::setGroupBoxState()
343
 */
344
void ConfigDialog::on_radioButtonPass_clicked() { setGroupBoxState(); }
×
345

346
/**
347
 * @brief ConfigDialog::getSecretKeys get list of secret/private keys
348
 * @return QStringList keys
349
 */
350
auto ConfigDialog::getSecretKeys() -> QStringList {
×
351
  QList<UserInfo> keys = QtPassSettings::getPass()->listKeys("", true);
×
352
  QStringList names;
×
353

354
  if (keys.empty()) {
×
355
    return names;
356
  }
357

358
  foreach (const UserInfo &sec, keys)
×
359
    names << sec.name;
×
360

361
  return names;
×
362
}
363

364
/**
365
 * @brief ConfigDialog::setGroupBoxState update checkboxes.
366
 */
367
void ConfigDialog::setGroupBoxState() {
×
368
  bool state = ui->radioButtonPass->isChecked();
×
369
  ui->groupBoxNative->setEnabled(!state);
×
370
  ui->groupBoxPass->setEnabled(state);
×
371
  if (state) {
×
372
    // pass mode: disable all password generation controls
373
    ui->spinBoxPasswordLength->setEnabled(false);
×
374
    ui->checkBoxUsePwgen->setEnabled(false);
×
375
    ui->checkBoxAvoidCapitals->setEnabled(false);
×
376
    ui->checkBoxUseSymbols->setEnabled(false);
×
377
    ui->checkBoxLessRandom->setEnabled(false);
×
378
    ui->checkBoxAvoidNumbers->setEnabled(false);
×
379
    ui->labelPasswordChars->setEnabled(false);
×
380
    ui->passwordCharTemplateSelector->setEnabled(false);
×
381
    ui->lineEditPasswordChars->setEnabled(false);
×
382
  } else {
383
    // native mode: restore pwgen/charset state from existing handlers
384
    ui->spinBoxPasswordLength->setEnabled(true);
×
385
    ui->checkBoxUsePwgen->setEnabled(!ui->pwgenPath->text().isEmpty());
×
386
    on_checkBoxUsePwgen_clicked();
×
387
  }
388
}
×
389

390
/**
391
 * @brief ConfigDialog::selectExecutable pop-up to choose an executable.
392
 * @return
393
 */
394
auto ConfigDialog::selectExecutable() -> QString {
×
395
  QFileDialog dialog(this);
×
396
  dialog.setFileMode(QFileDialog::ExistingFile);
×
397
  dialog.setOption(QFileDialog::ReadOnly);
×
398
  if (dialog.exec()) {
×
399
    return dialog.selectedFiles().constFirst();
×
400
  }
401

402
  return {};
403
}
×
404

405
/**
406
 * @brief ConfigDialog::selectFolder pop-up to choose a folder.
407
 * @return
408
 */
409
auto ConfigDialog::selectFolder() -> QString {
×
410
  QFileDialog dialog(this);
×
411
  dialog.setFileMode(QFileDialog::Directory);
×
412
  dialog.setFilter(QDir::NoFilter);
×
413
  dialog.setOption(QFileDialog::ShowDirsOnly);
×
414
  if (dialog.exec()) {
×
415
    return dialog.selectedFiles().constFirst();
×
416
  }
417

418
  return {};
419
}
×
420

421
/**
422
 * @brief ConfigDialog::on_toolButtonGit_clicked get git application.
423
 * Enable checkboxes if found.
424
 */
425
void ConfigDialog::on_toolButtonGit_clicked() {
×
426
  QString git = selectExecutable();
×
427
  bool state = !git.isEmpty();
×
428
  if (state) {
×
429
    ui->gitPath->setText(git);
×
430
  } else {
431
    useGit(false);
×
432
  }
433

434
  ui->checkBoxUseGit->setEnabled(state);
×
435
}
×
436

437
/**
438
 * @brief ConfigDialog::on_toolButtonGpg_clicked get gpg application.
439
 */
440
void ConfigDialog::on_toolButtonGpg_clicked() {
×
441
  QString gpg = selectExecutable();
×
442
  if (!gpg.isEmpty()) {
×
443
    ui->gpgPath->setText(gpg);
×
444
  }
445
}
×
446

447
/**
448
 * @brief ConfigDialog::on_pushButtonGenerateKey_clicked open keygen dialog.
449
 */
450
void ConfigDialog::on_pushButtonGenerateKey_clicked() {
×
451
  KeygenDialog d(this);
×
452
  d.exec();
×
453
}
×
454

455
/**
456
 * @brief ConfigDialog::on_toolButtonPass_clicked get pass application.
457
 */
458
void ConfigDialog::on_toolButtonPass_clicked() {
×
459
  QString pass = selectExecutable();
×
460
  if (!pass.isEmpty()) {
×
461
    ui->passPath->setText(pass);
×
462
  }
463
}
×
464

465
/**
466
 * @brief ConfigDialog::on_toolButtonStore_clicked get .password-store
467
 * location.
468
 */
469
void ConfigDialog::on_toolButtonStore_clicked() {
×
470
  QString store = selectFolder();
×
471
  if (!store.isEmpty()) {
×
472
    ui->storePath->setText(store);
×
473
  }
474
}
×
475

476
/**
477
 * @brief ConfigDialog::on_comboBoxClipboard_activated show and hide options.
478
 * @param index of selectbox (0 = no clipboard).
479
 */
480
void ConfigDialog::on_comboBoxClipboard_activated(int index) {
×
481
  bool state = index > 0;
×
482

483
  ui->checkBoxSelection->setEnabled(state);
×
484
  ui->checkBoxAutoclear->setEnabled(state);
×
485
  ui->checkBoxHidePassword->setEnabled(state);
×
486
  ui->checkBoxHideContent->setEnabled(state);
×
487
  if (state) {
×
488
    ui->spinBoxAutoclearSeconds->setEnabled(ui->checkBoxAutoclear->isChecked());
×
489
    ui->labelSeconds->setEnabled(ui->checkBoxAutoclear->isChecked());
×
490
  } else {
491
    ui->spinBoxAutoclearSeconds->setEnabled(false);
×
492
    ui->labelSeconds->setEnabled(false);
×
493
  }
494
}
×
495

496
/**
497
 * @brief ConfigDialog::on_checkBoxAutoclearPanel_clicked enable and disable
498
 * options based on autoclear use.
499
 */
500
void ConfigDialog::on_checkBoxAutoclearPanel_clicked() {
×
501
  bool state = ui->checkBoxAutoclearPanel->isChecked();
×
502
  ui->spinBoxAutoclearPanelSeconds->setEnabled(state);
×
503
  ui->labelPanelSeconds->setEnabled(state);
×
504
}
×
505

506
/**
507
 * @brief ConfigDialog::useSelection set the clipboard type use from
508
 * MainWindow.
509
 * @param useSelection
510
 */
511
void ConfigDialog::useSelection(bool useSelection) {
×
512
  ui->checkBoxSelection->setChecked(useSelection);
×
513
  on_checkBoxSelection_clicked();
×
514
}
×
515

516
/**
517
 * @brief ConfigDialog::useAutoclear set the clipboard autoclear use from
518
 * MainWindow.
519
 * @param useAutoclear
520
 */
521
void ConfigDialog::useAutoclear(bool useAutoclear) {
×
522
  ui->checkBoxAutoclear->setChecked(useAutoclear);
×
523
  on_checkBoxAutoclear_clicked();
×
524
}
×
525

526
/**
527
 * @brief ConfigDialog::useAutoclearPanel set the panel autoclear use from
528
 * MainWindow.
529
 * @param useAutoclearPanel
530
 */
531
void ConfigDialog::useAutoclearPanel(bool useAutoclearPanel) {
×
532
  ui->checkBoxAutoclearPanel->setChecked(useAutoclearPanel);
×
533
  on_checkBoxAutoclearPanel_clicked();
×
534
}
×
535

536
/**
537
 * @brief ConfigDialog::on_checkBoxSelection_clicked checkbox clicked, update
538
 * state via ConfigDialog::on_comboBoxClipboard_activated
539
 */
540
void ConfigDialog::on_checkBoxSelection_clicked() {
×
541
  on_comboBoxClipboard_activated(ui->comboBoxClipboard->currentIndex());
×
542
}
×
543

544
/**
545
 * @brief ConfigDialog::on_checkBoxAutoclear_clicked checkbox clicked, update
546
 * state via ConfigDialog::on_comboBoxClipboard_activated
547
 */
548
void ConfigDialog::on_checkBoxAutoclear_clicked() {
×
549
  on_comboBoxClipboard_activated(ui->comboBoxClipboard->currentIndex());
×
550
}
×
551

552
/**
553
 * @brief ConfigDialog::genKey tunnel function to make MainWindow generate a
554
 * gpg key pair.
555
 * @param batch
556
 * @param dialog
557
 */
558
void ConfigDialog::genKey(const QString &batch, QDialog *dialog) {
×
559
  mainWindow->generateKeyPair(batch, dialog);
×
560
}
×
561

562
/**
563
 * @brief ConfigDialog::setProfiles set the profiles and chosen profile from
564
 * MainWindow.
565
 * @param profiles
566
 * @param profile
567
 */
568
void ConfigDialog::setProfiles(QHash<QString, QHash<QString, QString>> profiles,
×
569
                               const QString &currentProfile) {
570
  if (profiles.contains("")) {
×
571
    profiles.remove("");
×
572
    // remove weird "" key value pairs
573
  }
574

575
  // Cache profiles for use in onProfileTableSelectionChanged
576
  m_profiles = profiles;
×
577

NEW
578
  ui->profileTable->setRowCount(static_cast<int>(profiles.count()));
×
579
  QHashIterator<QString, QHash<QString, QString>> i(profiles);
×
580
  int n = 0;
581
  while (i.hasNext()) {
×
582
    i.next();
583
    if (!i.value().isEmpty() && !i.key().isEmpty()) {
×
584
      ui->profileTable->setItem(n, 0, new QTableWidgetItem(i.key()));
×
585
      ui->profileTable->setItem(n, 1,
×
586
                                new QTableWidgetItem(i.value().value("path")));
×
587
      ui->profileTable->setItem(
×
588
          n, 2, new QTableWidgetItem(i.value().value("signingKey")));
×
589
      if (i.key() == currentProfile) {
×
590
        ui->profileTable->selectRow(n);
×
591
        // Load git settings for current profile
592
        loadGitSettingsForProfile(currentProfile, m_profiles);
×
593
      }
594
    }
595
    ++n;
×
596
  }
597
}
×
598

599
/**
600
 * @brief Load git settings for a specific profile.
601
 * @param profileName The profile name.
602
 * @param profiles The profiles hash containing git settings.
603
 */
604
void ConfigDialog::loadGitSettingsForProfile(
×
605
    const QString &profileName,
606
    const QHash<QString, QHash<QString, QString>> &profiles) {
607
  if (profiles.contains(profileName)) {
×
608
    const QHash<QString, QString> &profile = profiles.value(profileName);
×
609
    QString useGitStr = profile.value("useGit");
×
610
    QString autoPushStr = profile.value("autoPush");
×
611
    QString autoPullStr = profile.value("autoPull");
×
612

613
    // Load profile-specific git settings if set, otherwise use global settings
614
    if (!useGitStr.isEmpty()) {
×
615
      useGit(useGitStr == "true");
×
616
      ui->checkBoxAutoPush->setEnabled(ui->checkBoxUseGit->isChecked());
×
617
      ui->checkBoxAutoPull->setEnabled(ui->checkBoxUseGit->isChecked());
×
618
      if (autoPushStr == "true" || autoPushStr == "false") {
×
619
        ui->checkBoxAutoPush->setChecked(autoPushStr == "true");
×
620
      }
621
      if (autoPullStr == "true" || autoPullStr == "false") {
×
622
        ui->checkBoxAutoPull->setChecked(autoPullStr == "true");
×
623
      }
624
    }
625
    // If not set (empty), leave global settings as-is for migration
626
  }
×
627
}
×
628

629
/**
630
 * @brief ConfigDialog::getProfiles return profile list.
631
 * @return
632
 */
633
auto ConfigDialog::getProfiles() -> QHash<QString, QHash<QString, QString>> {
×
634
  // Get currently selected profile name
635
  QList<QTableWidgetItem *> selected = ui->profileTable->selectedItems();
×
636
  QString selectedProfile;
×
637
  if (!selected.isEmpty()) {
×
638
    selectedProfile =
639
        ui->profileTable->item(selected.first()->row(), 0)->text();
×
640
  }
641

642
  // Use cached m_profiles to preserve git settings for non-selected profiles
643
  QHash<QString, QHash<QString, QString>> existingProfiles = m_profiles;
644

645
  QHash<QString, QHash<QString, QString>> profiles;
×
646
  // Check?
647
  for (int i = 0; i < ui->profileTable->rowCount(); ++i) {
×
648
    QHash<QString, QString> profile;
×
649
    QTableWidgetItem *pathItem = ui->profileTable->item(i, 1);
×
650
    if (nullptr != pathItem) {
×
651
      QTableWidgetItem *item = ui->profileTable->item(i, 0);
×
652
      if (item == nullptr) {
×
653
        continue;
654
      }
655
      profile["path"] = pathItem->text();
×
656
      QTableWidgetItem *signingKeyItem = ui->profileTable->item(i, 2);
×
657
      if (nullptr != signingKeyItem) {
×
658
        profile["signingKey"] = signingKeyItem->text();
×
659
      }
660

661
      // Only update git settings for the currently selected profile
662
      // Preserve existing git settings for other profiles
663
      if (item->text() == selectedProfile) {
×
664
        profile["useGit"] = ui->checkBoxUseGit->isChecked() ? "true" : "false";
×
665
        profile["autoPush"] =
×
666
            ui->checkBoxAutoPush->isChecked() ? "true" : "false";
×
667
        profile["autoPull"] =
×
668
            ui->checkBoxAutoPull->isChecked() ? "true" : "false";
×
669
      } else if (existingProfiles.contains(item->text())) {
×
670
        // Preserve existing git settings for non-selected profiles
671
        const QHash<QString, QString> &existing =
672
            existingProfiles.value(item->text());
×
673
        if (existing.contains("useGit")) {
×
674
          profile["useGit"] = existing.value("useGit");
×
675
        }
676
        if (existing.contains("autoPush")) {
×
677
          profile["autoPush"] = existing.value("autoPush");
×
678
        }
679
        if (existing.contains("autoPull")) {
×
680
          profile["autoPull"] = existing.value("autoPull");
×
681
        }
682
      }
×
683
      profiles.insert(item->text(), profile);
×
684
    }
685
  }
×
686
  // Update cache with current in-dialog state
687
  m_profiles = profiles;
×
688
  return profiles;
×
689
}
×
690

691
/**
692
 * @brief Initialize new profiles that need pass/git initialization.
693
 * @param existingProfiles The profiles that existed before the dialog was
694
 * opened.
695
 */
696
void ConfigDialog::initializeNewProfiles(
×
697
    const QHash<QString, QHash<QString, QString>> &existingProfiles) {
698
  QHash<QString, QHash<QString, QString>> newProfiles = getProfiles();
×
699

700
  // Collect keys and sort for deterministic iteration
701
  QStringList keys = newProfiles.keys();
×
702
  keys.sort();
703

704
  for (const QString &name : keys) {
×
705
    const QString &path = newProfiles.value(name).value("path");
×
706

707
    // Skip if already existed before (check by name and path)
708
    if (existingProfiles.contains(name) &&
×
709
        existingProfiles.value(name).value("path") == path) {
×
710
      continue;
×
711
    }
712

713
    // This is a new profile - create directory if needed and initialize
714
    // Note: needsInit returns false for non-existent directories, so we
715
    // must create the directory first.
716
    QString cleanPath = QDir::cleanPath(path);
×
717
    QDir dir(cleanPath);
×
718
    if (!dir.exists()) {
×
719
      if (QMessageBox::question(
×
720
              this, tr("Create profile directory?"),
×
721
              tr("Would you like to create a password store at %1?")
×
722
                  .arg(cleanPath),
×
723
              QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) {
724
        continue;
×
725
      }
726
      if (!QDir().mkpath(cleanPath)) {
×
727
        QMessageBox::warning(
×
728
            this, tr("Error"),
×
729
            tr("Could not create profile directory: %1").arg(cleanPath));
×
730
        continue;
×
731
      }
732
    }
733

734
    // Now check if initialization is needed (directory exists with no .gpg-id)
735
    if (!ProfileInit::needsInit(cleanPath)) {
×
736
      continue;
×
737
    }
738

739
    // Temporarily switch the active store so pass/git init operate on
740
    // the new profile's directory rather than the currently-saved one.
741
    const QString prevStore = QtPassSettings::getPassStore();
×
742
    QtPassSettings::setPassStore(cleanPath);
×
743

744
    // Show user selection dialog for GPG recipients
745
    // UsersDialog will run pass init when accepted
746
    UsersDialog usersDialog(cleanPath, this);
×
747
    usersDialog.setWindowTitle(tr("Select recipients for %1").arg(name));
×
748
    const int result = usersDialog.exec();
×
749

750
    // Use per-profile useGit setting, falling back to global if not set
751
    QString useGitStr = newProfiles.value(name).value("useGit");
×
752
    bool useGit =
753
        useGitStr.isEmpty() ? QtPassSettings::isUseGit() : useGitStr == "true";
×
754

755
    if (result == QDialog::Accepted && useGit) {
×
756
      QtPassSettings::getPass()->GitInit();
×
757
    }
758

759
    // Restore previous store setting
760
    QtPassSettings::setPassStore(prevStore);
×
761
  }
×
762
}
×
763

764
/**
765
 * @brief ConfigDialog::on_addButton_clicked add a profile row.
766
 */
767
void ConfigDialog::on_addButton_clicked() {
×
768
  bool sortingEnabled = ui->profileTable->isSortingEnabled();
×
769
  ui->profileTable->setSortingEnabled(false);
×
770

771
  int n = ui->profileTable->rowCount();
×
772
  ui->profileTable->insertRow(n);
×
773
  ui->profileTable->setItem(n, 0, new QTableWidgetItem(tr("New Profile")));
×
774
  ui->profileTable->setItem(n, 1, new QTableWidgetItem(ui->storePath->text()));
×
775
  ui->profileTable->setItem(n, 2, new QTableWidgetItem());
×
776

777
  ui->profileTable->setSortingEnabled(sortingEnabled);
×
778

779
  int currentRow = ui->profileTable->row(ui->profileTable->item(n, 0));
×
780
  ui->profileTable->selectRow(currentRow);
×
781
  ui->deleteButton->setEnabled(true);
×
782

783
  ui->profileTable->editItem(ui->profileTable->item(currentRow, 0));
×
784
  ui->profileTable->item(currentRow, 0)->setSelected(true);
×
785

786
  validate();
×
787
  updateProfileStatus(currentRow);
×
788
}
×
789

790
/**
791
 * @brief ConfigDialog::on_profileTable_cellDoubleClicked open folder browser
792
 * for path column (column 1).
793
 */
794
void ConfigDialog::on_profileTable_cellDoubleClicked(int row, int column) {
×
795
  if (column == 1) {
×
796
    QString dir = selectFolder();
×
797
    if (!dir.isEmpty()) {
×
798
      ui->profileTable->item(row, 1)->setText(dir);
×
799
    }
800
  }
801
}
×
802

803
/**
804
 * @brief ConfigDialog::on_deleteButton_clicked remove a profile row.
805
 */
806
void ConfigDialog::on_deleteButton_clicked() {
×
807
  QSet<int> selectedRows; //  we use a set to prevent doubles
808
  QList<QTableWidgetItem *> itemList = ui->profileTable->selectedItems();
×
809
  if (itemList.count() == 0) {
×
810
    QMessageBox::warning(this, tr("No profile selected"),
×
811
                         tr("No profile selected to delete"));
×
812
    return;
813
  }
814
  QTableWidgetItem *item;
815
  foreach (item, itemList)
×
816
    selectedRows.insert(item->row());
×
817
  // get a list, and sort it big to small
818
  QList<int> rows = selectedRows.values();
×
819
  std::sort(rows.begin(), rows.end());
×
820
  // now actually do the removing:
821
  foreach (int row, rows)
×
822
    ui->profileTable->removeRow(row);
×
823
  if (ui->profileTable->rowCount() < 1) {
×
824
    ui->deleteButton->setEnabled(false);
×
825
  }
826

827
  validate();
×
828
  updateProfileStatus(-1);
×
829
}
830

831
/**
832
 * @brief ConfigDialog::criticalMessage wrapper for showing critical messages
833
 * in a popup.
834
 * @param title
835
 * @param text
836
 */
837
void ConfigDialog::criticalMessage(const QString &title, const QString &text) {
×
838
  QMessageBox::critical(this, title, text, QMessageBox::Ok, QMessageBox::Ok);
×
839
}
×
840

841
/**
842
 * @brief Checks whether the qrencode executable is available on the system.
843
 * @example
844
 * bool result = ConfigDialog::isQrencodeAvailable();
845
 * std::cout << result << std::endl; // Expected output: true if qrencode is
846
 * found, otherwise false
847
 *
848
 * @return bool - True if qrencode is available; otherwise false. On Windows,
849
 * always returns false.
850
 */
851
auto ConfigDialog::isQrencodeAvailable() -> bool {
×
852
#ifdef Q_OS_WIN
853
  return false;
854
#else
855
  QProcess which;
×
856
  which.start("which", QStringList() << "qrencode");
×
857
  which.waitForFinished();
×
858
  QtPassSettings::setQrencodeExecutable(
×
859
      which.readAllStandardOutput().trimmed());
×
860
  return which.exitCode() == 0;
×
861
#endif
862
}
×
863

864
auto ConfigDialog::isPassOtpAvailable() -> bool {
×
865
#ifdef Q_OS_WIN
866
  return false;
867
#else
868
  QProcess pass;
×
869
  pass.start(QtPassSettings::getPassExecutable(), QStringList() << "otp"
×
870
                                                                << "--help");
×
871
  pass.waitForFinished(2000);
×
872
  return pass.exitCode() == 0;
×
873
#endif
874
}
×
875

876
/**
877
 * @brief ConfigDialog::wizard first-time use wizard.
878
 */
879
void ConfigDialog::wizard() {
×
880
  (void)Util::configIsValid();
×
881
  on_autodetectButton_clicked();
×
882

883
  if (!checkGpgExistence()) {
×
884
    return;
885
  }
886
  if (!checkSecretKeys()) {
×
887
    return;
888
  }
889
  if (!checkPasswordStore()) {
×
890
    return;
891
  }
892
  handleGpgIdFile();
×
893

894
  ui->checkBoxHidePassword->setCheckState(Qt::Checked);
×
895
}
896

897
/**
898
 * @brief Checks whether the configured GnuPG executable exists.
899
 * @example
900
 * bool result = ConfigDialog::checkGpgExistence();
901
 * std::cout << result << std::endl; // Expected output: true if GnuPG is found,
902
 * false otherwise
903
 *
904
 * @return bool - True if the GnuPG path is valid or uses a WSL command prefix;
905
 * false if the executable cannot be found and an error message is shown.
906
 */
907
auto ConfigDialog::checkGpgExistence() -> bool {
×
908
  QString gpg = ui->gpgPath->text();
×
909
  if (!gpg.startsWith("wsl ") && !QFile(gpg).exists()) {
×
910
    criticalMessage(
×
911
        tr("GnuPG not found"),
×
912
#ifdef Q_OS_WIN
913
#ifdef WINSTORE
914
        tr("Please install GnuPG on your system.<br>Install "
915
           "<strong>Ubuntu</strong> from the Microsoft Store to get it.<br>"
916
           "If you already did so, make sure you started it once and<br>"
917
           "click \"Autodetect\" in the next dialog.")
918
#else
919
        tr("Please install GnuPG on your system.<br>Install "
920
           "<strong>Ubuntu</strong> from the Microsoft Store<br>or <a "
921
           "href=\"https://www.gnupg.org/download/#sec-1-2\">download</a> it "
922
           "from GnuPG.org")
923
#endif
924
#else
925
        tr("Please install GnuPG on your system.<br>Install "
×
926
           "<strong>gpg</strong> using your favorite package manager<br>or "
927
           "<a "
928
           "href=\"https://www.gnupg.org/download/#sec-1-2\">download</a> it "
929
           "from GnuPG.org")
930
#endif
931
    );
932
    return false;
×
933
  }
934
  return true;
935
}
936

937
/**
938
 * @brief Checks whether secret keys are available and, if needed, prompts the
939
 * user to generate them.
940
 * @example
941
 * bool result = ConfigDialog.checkSecretKeys();
942
 * std::cout << result << std::endl; // Expected output: true if keys are
943
 * present or key generation dialog is accepted, false otherwise
944
 *
945
 * @return bool - Returns true when secret keys are already available or the key
946
 * generation dialog is accepted; false if the dialog is rejected.
947
 */
948
auto ConfigDialog::checkSecretKeys() -> bool {
×
949
  QString gpg = ui->gpgPath->text();
×
950
  QStringList names = getSecretKeys();
×
951

952
#ifdef QT_DEBUG
953
  dbg() << names;
954
#endif
955

956
  if ((gpg.startsWith("wsl ") || QFile(gpg).exists()) && names.empty()) {
×
957
    KeygenDialog d(this);
×
958
    return d.exec();
×
959
  }
×
960
  return true;
961
}
962

963
/**
964
 * @brief Checks whether the password-store path exists and prompts the user to
965
 * create it if it does not.
966
 * @example
967
 * bool result = ConfigDialog::checkPasswordStore();
968
 * std::cout << std::boolalpha << result << std::endl; // Expected output: true
969
 * if the store exists or is created successfully, false if creation fails
970
 *
971
 * @return bool - True if the password-store exists or is successfully created,
972
 * false if creation fails.
973
 */
974
auto ConfigDialog::checkPasswordStore() -> bool {
×
975
  QString passStore = ui->storePath->text();
×
976

977
  if (!QFile(passStore).exists()) {
×
978
    if (QMessageBox::question(
×
979
            this, tr("Create password-store?"),
×
980
            tr("Would you like to create a password-store at %1?")
×
981
                .arg(passStore),
×
982
            QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
983
      if (!QDir().mkdir(passStore)) {
×
984
        QMessageBox::warning(
×
985
            this, tr("Error"),
×
986
            tr("Failed to create password-store at: %1").arg(passStore));
×
987
        return false;
×
988
      }
989
#ifdef Q_OS_WIN
990
      SetFileAttributes(passStore.toStdWString().c_str(),
991
                        FILE_ATTRIBUTE_HIDDEN);
992
#endif
993
      if (ui->checkBoxUseGit->isChecked()) {
×
994
        emit mainWindow->passGitInitNeeded();
×
995
      }
996
      mainWindow->userDialog(passStore);
×
997
    }
998
  }
999
  return true;
1000
}
1001

1002
/**
1003
 * @brief Handles selection and validation of the password store's .gpg-id file.
1004
 * @example
1005
 * ConfigDialog dialog;
1006
 * dialog.handleGpgIdFile();
1007
 *
1008
 * @return void - This method does not return a value; it updates the UI flow
1009
 * and may prompt the user to choose a valid password store.
1010
 */
1011
void ConfigDialog::handleGpgIdFile() {
×
1012
  QString passStore = ui->storePath->text();
×
1013
  if (!QFile(QDir(passStore).filePath(".gpg-id")).exists()) {
×
1014
#ifdef QT_DEBUG
1015
    dbg() << ".gpg-id file does not exist";
1016
#endif
1017
    criticalMessage(tr("Password store not initialised"),
×
1018
                    tr("The folder %1 doesn't seem to be a password store or "
×
1019
                       "is not yet initialised.")
1020
                        .arg(passStore));
×
1021

1022
    while (!QFile(passStore).exists()) {
×
1023
      on_toolButtonStore_clicked();
×
1024
      if (passStore == ui->storePath->text()) {
×
1025
        return;
1026
      }
1027
      passStore = ui->storePath->text();
×
1028
    }
1029
    if (!QFile(passStore + ".gpg-id").exists()) {
×
1030
#ifdef QT_DEBUG
1031
      dbg() << ".gpg-id file still does not exist :/";
1032
#endif
1033
      mainWindow->userDialog(passStore);
×
1034
    }
1035
  }
1036
}
1037

1038
/**
1039
 * @brief ConfigDialog::useTrayIcon set preference for using trayicon.
1040
 * Enable or disable related checkboxes accordingly.
1041
 * @param useSystray
1042
 */
1043
void ConfigDialog::useTrayIcon(bool useSystray) {
×
1044
  if (QSystemTrayIcon::isSystemTrayAvailable()) {
×
1045
    ui->checkBoxUseTrayIcon->setChecked(useSystray);
×
1046
    ui->checkBoxHideOnClose->setEnabled(useSystray);
×
1047
    ui->checkBoxStartMinimized->setEnabled(useSystray);
×
1048

1049
    if (!useSystray) {
×
1050
      ui->checkBoxHideOnClose->setChecked(false);
×
1051
      ui->checkBoxStartMinimized->setChecked(false);
×
1052
    }
1053
  }
1054
}
×
1055

1056
/**
1057
 * @brief ConfigDialog::on_checkBoxUseTrayIcon_clicked enable and disable
1058
 * related checkboxes.
1059
 */
1060
void ConfigDialog::on_checkBoxUseTrayIcon_clicked() {
×
1061
  bool state = ui->checkBoxUseTrayIcon->isChecked();
×
1062
  ui->checkBoxHideOnClose->setEnabled(state);
×
1063
  ui->checkBoxStartMinimized->setEnabled(state);
×
1064
}
×
1065

1066
/**
1067
 * @brief ConfigDialog::closeEvent close this window.
1068
 * @param event
1069
 */
1070
void ConfigDialog::closeEvent(QCloseEvent *event) {
×
1071
  QtPassSettings::setDialogGeometry("configDialog", saveGeometry());
×
1072
  if (!isMaximized()) {
×
1073
    QtPassSettings::setDialogPos("configDialog", pos());
×
1074
    QtPassSettings::setDialogSize("configDialog", size());
×
1075
  }
1076
  QtPassSettings::setDialogMaximized("configDialog", isMaximized());
×
1077
  event->accept();
1078
}
×
1079

1080
/**
1081
 * @brief ConfigDialog::useGit set preference for using git.
1082
 * @param useGit
1083
 */
1084
void ConfigDialog::useGit(bool useGit) {
×
1085
  ui->checkBoxUseGit->setChecked(useGit);
×
1086
  on_checkBoxUseGit_clicked();
×
1087
}
×
1088

1089
/**
1090
 * @brief ConfigDialog::useOtp set preference for using otp plugin.
1091
 * @param useOtp
1092
 */
1093
void ConfigDialog::useOtp(bool useOtp) {
×
1094
  ui->checkBoxUseOtp->setChecked(useOtp);
×
1095
}
×
1096

1097
void ConfigDialog::useGrepSearch(bool useGrepSearch) {
×
1098
  ui->checkBoxUseGrepSearch->setChecked(useGrepSearch);
×
1099
}
×
1100

1101
/**
1102
 * @brief ConfigDialog::useQrencode set preference for using qrencode plugin.
1103
 * @param useQrencode
1104
 */
1105
void ConfigDialog::useQrencode(bool useQrencode) {
×
1106
  ui->checkBoxUseQrencode->setChecked(useQrencode);
×
1107
}
×
1108

1109
/**
1110
 * @brief ConfigDialog::on_checkBoxUseGit_clicked enable or disable related
1111
 * checkboxes.
1112
 */
1113
void ConfigDialog::on_checkBoxUseGit_clicked() {
×
1114
  ui->checkBoxAddGPGId->setEnabled(ui->checkBoxUseGit->isChecked());
×
1115
  ui->checkBoxAutoPull->setEnabled(ui->checkBoxUseGit->isChecked());
×
1116
  ui->checkBoxAutoPush->setEnabled(ui->checkBoxUseGit->isChecked());
×
1117
}
×
1118

1119
/**
1120
 * @brief ConfigDialog::on_toolButtonPwgen_clicked enable or disable related
1121
 * options in the interface.
1122
 */
1123
void ConfigDialog::on_toolButtonPwgen_clicked() {
×
1124
  QString pwgen = selectExecutable();
×
1125
  if (!pwgen.isEmpty()) {
×
1126
    ui->pwgenPath->setText(pwgen);
×
1127
    ui->checkBoxUsePwgen->setEnabled(true);
×
1128
  } else {
1129
    ui->checkBoxUsePwgen->setEnabled(false);
×
1130
    ui->checkBoxUsePwgen->setChecked(false);
×
1131
  }
1132
}
×
1133

1134
/**
1135
 * @brief ConfigDialog::setPwgenPath set pwgen executable path.
1136
 * Enable or disable related options in the interface.
1137
 * @param pwgen
1138
 */
1139
void ConfigDialog::setPwgenPath(const QString &pwgen) {
×
1140
  ui->pwgenPath->setText(pwgen);
×
1141
  if (pwgen.isEmpty()) {
×
1142
    ui->checkBoxUsePwgen->setChecked(false);
×
1143
    ui->checkBoxUsePwgen->setEnabled(false);
×
1144
  }
1145
  on_checkBoxUsePwgen_clicked();
×
1146
}
×
1147

1148
/**
1149
 * @brief ConfigDialog::on_checkBoxUsePwgen_clicked enable or disable related
1150
 * options in the interface.
1151
 */
1152
void ConfigDialog::on_checkBoxUsePwgen_clicked() {
×
1153
  if (ui->radioButtonPass->isChecked())
×
1154
    return;
1155
  bool usePwgen = ui->checkBoxUsePwgen->isChecked();
×
1156
  ui->checkBoxAvoidCapitals->setEnabled(usePwgen);
×
1157
  ui->checkBoxAvoidNumbers->setEnabled(usePwgen);
×
1158
  ui->checkBoxLessRandom->setEnabled(usePwgen);
×
1159
  ui->checkBoxUseSymbols->setEnabled(usePwgen);
×
1160
  ui->lineEditPasswordChars->setEnabled(!usePwgen);
×
1161
  ui->labelPasswordChars->setEnabled(!usePwgen);
×
1162
  ui->passwordCharTemplateSelector->setEnabled(!usePwgen);
×
1163
}
1164

1165
/**
1166
 * @brief ConfigDialog::usePwgen set preference for using pwgen (can be
1167
 * overruled by empty pwgenPath).
1168
 * enable or disable related options in the interface via
1169
 * ConfigDialog::on_checkBoxUsePwgen_clicked
1170
 * @param usePwgen
1171
 */
1172
void ConfigDialog::usePwgen(bool usePwgen) {
×
1173
  if (ui->pwgenPath->text().isEmpty()) {
×
1174
    usePwgen = false;
1175
  }
1176
  ui->checkBoxUsePwgen->setChecked(usePwgen);
×
1177
  on_checkBoxUsePwgen_clicked();
×
1178
}
×
1179

1180
void ConfigDialog::setPasswordConfiguration(
×
1181
    const PasswordConfiguration &config) {
1182
  ui->spinBoxPasswordLength->setValue(config.length);
×
1183
  ui->passwordCharTemplateSelector->setCurrentIndex(config.selected);
×
1184
  if (config.selected != PasswordConfiguration::CUSTOM) {
×
1185
    ui->lineEditPasswordChars->setEnabled(false);
×
1186
  }
1187
  ui->lineEditPasswordChars->setText(config.Characters[config.selected]);
×
1188
}
×
1189

1190
auto ConfigDialog::getPasswordConfiguration() -> PasswordConfiguration {
×
1191
  PasswordConfiguration config;
×
1192
  config.length = ui->spinBoxPasswordLength->value();
×
1193
  config.selected = static_cast<PasswordConfiguration::characterSet>(
×
1194
      ui->passwordCharTemplateSelector->currentIndex());
×
1195
  config.Characters[PasswordConfiguration::CUSTOM] =
1196
      ui->lineEditPasswordChars->text();
×
1197
  return config;
×
1198
}
×
1199

1200
/**
1201
 * @brief ConfigDialog::on_passwordCharTemplateSelector_activated sets the
1202
 * passwordChar Template
1203
 * combo box to the desired entry
1204
 * @param entry of
1205
 */
1206
void ConfigDialog::on_passwordCharTemplateSelector_activated(int index) {
×
1207
  ui->lineEditPasswordChars->setText(
×
1208
      QtPassSettings::getPasswordConfiguration().Characters[index]);
×
1209
  if (index == PasswordConfiguration::CUSTOM) {
×
1210
    ui->lineEditPasswordChars->setEnabled(true);
×
1211
  } else {
1212
    ui->lineEditPasswordChars->setEnabled(false);
×
1213
  }
1214
}
×
1215

1216
/**
1217
 * @brief ConfigDialog::on_checkBoxUseTemplate_clicked enable or disable the
1218
 * template field and options.
1219
 */
1220
void ConfigDialog::on_checkBoxUseTemplate_clicked() {
×
1221
  ui->plainTextEditTemplate->setEnabled(ui->checkBoxUseTemplate->isChecked());
×
1222
  ui->checkBoxTemplateAllFields->setEnabled(
×
1223
      ui->checkBoxUseTemplate->isChecked());
×
1224
}
×
1225

1226
void ConfigDialog::onProfileTableItemChanged(QTableWidgetItem *item) {
×
1227
  validate(item);
×
1228
  updateProfileStatus(item ? item->row() : -1);
×
1229
}
×
1230

1231
void ConfigDialog::onProfileTableSelectionChanged() {
×
1232
  QList<QTableWidgetItem *> selected = ui->profileTable->selectedItems();
×
1233
  if (selected.isEmpty()) {
×
1234
    return;
1235
  }
1236
  QTableWidgetItem *nameItem =
1237
      ui->profileTable->item(selected.first()->row(), 0);
×
1238
  if (nameItem == nullptr) {
×
1239
    return;
1240
  }
1241
  QString profileName = nameItem->text();
×
1242
  loadGitSettingsForProfile(profileName, m_profiles);
×
1243
}
1244

1245
/**
1246
 * @brief Update status bar with profile preview for given row.
1247
 * @param row The row index to preview, or -1 to clear.
1248
 */
1249
void ConfigDialog::updateProfileStatus(int row) {
×
1250
  if (row < 0 || row >= ui->profileTable->rowCount()) {
×
1251
    ui->statusLabel->setText(QString());
×
1252
    return;
×
1253
  }
1254

1255
  QTableWidgetItem *nameItem = ui->profileTable->item(row, 0);
×
1256
  QTableWidgetItem *pathItem = ui->profileTable->item(row, 1);
×
1257

1258
  QString statusMessage;
×
1259
  if (nameItem && !nameItem->text().isEmpty() && pathItem &&
×
1260
      !pathItem->text().isEmpty()) {
×
1261
    QDir dir(QDir::cleanPath(pathItem->text()));
×
1262
    if (!dir.exists()) {
×
1263
      statusMessage = tr("New profile: %1 at %2")
×
1264
                          .arg(nameItem->text())
×
1265
                          .arg(QDir::cleanPath(pathItem->text()));
×
1266
    } else {
1267
      statusMessage = tr("Profile: %1 at %2")
×
1268
                          .arg(nameItem->text())
×
1269
                          .arg(QDir::cleanPath(pathItem->text()));
×
1270
    }
1271
  } else {
×
1272
    statusMessage = tr("Fill in all required fields");
×
1273
  }
1274

1275
  ui->statusLabel->setText(statusMessage);
×
1276
}
1277

1278
/**
1279
 * @brief ConfigDialog::useTemplate set preference for using templates.
1280
 * @param useTemplate
1281
 */
1282
void ConfigDialog::useTemplate(bool useTemplate) {
×
1283
  ui->checkBoxUseTemplate->setChecked(useTemplate);
×
1284
  on_checkBoxUseTemplate_clicked();
×
1285
}
×
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