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

IJHack / QtPass / 24848506644

23 Apr 2026 05:11PM UTC coverage: 27.334% (+0.3%) from 27.079%
24848506644

push

github

web-flow
feat: Store git options per profile (#112) (#1140)

* feat: Store git options per profile (#112)

- Add useGit, autoPush, autoPull to profile storage in getProfiles/setProfiles
- Add getProfileUseGit, setProfileUseGit, getProfileAutoPush, setProfileAutoPush,
  getProfileAutoPull, setProfileAutoPull methods to QtPassSettings
- Load git settings when selecting profile in ConfigDialog
- Save git settings to profile when saving

Allows each profile to have its own git settings, fixing the issue
where global git settings caused errors for mixed local/remote stores.

* feat: Store git options per profile (#112)

- Fix data corruption: only save git settings for selected profile
- Fix getters to fall back to default for empty values (migration)
- Add unit tests for profile git options
- Add test cleanup for profileGitOptions

* fix: preserve git settings for non-selected profiles

- Load existing profiles in getProfiles() to preserve git settings for non-selected profiles
- Use useGit setter in loadGitSettingsForProfile so dependent controls refresh
- Add selection change handler to reload profile git settings when switching rows
- Fix test cleanup to use QtPassSettings singleton instead of raw QSettings

* fix: apply CodeRabbit auto-fixes

Fixed 4 file(s) based on 3 unresolved review comments.

Co-authored-by: CodeRabbit <noreply@coderabbit.ai>

* style: format qtpasssettings.cpp

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: CodeRabbit <noreply@coderabbit.ai>

39 of 93 new or added lines in 2 files covered. (41.94%)

18 existing lines in 2 files now uncovered.

1631 of 5967 relevant lines covered (27.33%)

29.7 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->checkBoxAutoPull->setChecked(QtPassSettings::isAutoPull());
×
81
  ui->checkBoxAutoPush->setChecked(QtPassSettings::isAutoPush());
×
82
  ui->checkBoxAlwaysOnTop->setChecked(QtPassSettings::isAlwaysOnTop());
×
83

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

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

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

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

NEW
106
  setProfiles(QtPassSettings::getProfiles(), QtPassSettings::getProfile());
×
107

UNCOV
108
  useGit(QtPassSettings::isUseGit());
×
109

110
  useOtp(QtPassSettings::isUseOtp());
×
111
  useGrepSearch(QtPassSettings::isUseGrepSearch());
×
112
  useQrencode(QtPassSettings::isUseQrencode());
×
113

114
  usePwgen(QtPassSettings::isUsePwgen());
×
115
  useTemplate(QtPassSettings::isUseTemplate());
×
116

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

292
  QtPassSettings::setVersion(VERSION);
×
293

294
  // Initialize new profiles that need pass/git initialization
295
  initializeNewProfiles(existingProfiles);
×
296
}
×
297

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

330
/**
331
 * @brief ConfigDialog::on_radioButtonNative_clicked wrapper for
332
 * ConfigDialog::setGroupBoxState()
333
 */
334
void ConfigDialog::on_radioButtonNative_clicked() { setGroupBoxState(); }
×
335

336
/**
337
 * @brief ConfigDialog::on_radioButtonPass_clicked wrapper for
338
 * ConfigDialog::setGroupBoxState()
339
 */
340
void ConfigDialog::on_radioButtonPass_clicked() { setGroupBoxState(); }
×
341

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

350
  if (keys.empty()) {
×
351
    return names;
352
  }
353

354
  foreach (const UserInfo &sec, keys)
×
355
    names << sec.name;
×
356

357
  return names;
×
358
}
359

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

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

398
  return {};
399
}
×
400

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

414
  return {};
415
}
×
416

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

430
  ui->checkBoxUseGit->setEnabled(state);
×
431
}
×
432

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

443
/**
444
 * @brief ConfigDialog::on_pushButtonGenerateKey_clicked open keygen dialog.
445
 */
446
void ConfigDialog::on_pushButtonGenerateKey_clicked() {
×
447
  KeygenDialog d(this);
×
448
  d.exec();
×
449
}
×
450

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

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

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

479
  ui->checkBoxSelection->setEnabled(state);
×
480
  ui->checkBoxAutoclear->setEnabled(state);
×
481
  ui->checkBoxHidePassword->setEnabled(state);
×
482
  ui->checkBoxHideContent->setEnabled(state);
×
483
  if (state) {
×
484
    ui->spinBoxAutoclearSeconds->setEnabled(ui->checkBoxAutoclear->isChecked());
×
485
    ui->labelSeconds->setEnabled(ui->checkBoxAutoclear->isChecked());
×
486
  } else {
487
    ui->spinBoxAutoclearSeconds->setEnabled(false);
×
488
    ui->labelSeconds->setEnabled(false);
×
489
  }
490
}
×
491

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

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

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

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

532
/**
533
 * @brief ConfigDialog::on_checkBoxSelection_clicked checkbox clicked, update
534
 * state via ConfigDialog::on_comboBoxClipboard_activated
535
 */
536
void ConfigDialog::on_checkBoxSelection_clicked() {
×
537
  on_comboBoxClipboard_activated(ui->comboBoxClipboard->currentIndex());
×
538
}
×
539

540
/**
541
 * @brief ConfigDialog::on_checkBoxAutoclear_clicked checkbox clicked, update
542
 * state via ConfigDialog::on_comboBoxClipboard_activated
543
 */
544
void ConfigDialog::on_checkBoxAutoclear_clicked() {
×
545
  on_comboBoxClipboard_activated(ui->comboBoxClipboard->currentIndex());
×
546
}
×
547

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

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

571
  // Cache profiles for use in onProfileTableSelectionChanged
NEW
572
  m_profiles = profiles;
×
573

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

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

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

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

638
  // Use cached m_profiles to preserve git settings for non-selected profiles
639
  QHash<QString, QHash<QString, QString>> existingProfiles = m_profiles;
640

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

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

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

696
  // Collect keys and sort for deterministic iteration
697
  QStringList keys = newProfiles.keys();
×
698
  keys.sort();
699

700
  for (const QString &name : keys) {
×
701
    const QString &path = newProfiles.value(name).value("path");
×
702

703
    // Skip if already existed before (check by name and path)
704
    if (existingProfiles.contains(name) &&
×
705
        existingProfiles.value(name).value("path") == path) {
×
706
      continue;
×
707
    }
708

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

730
    // Now check if initialization is needed (directory exists with no .gpg-id)
731
    if (!ProfileInit::needsInit(cleanPath)) {
×
732
      continue;
×
733
    }
734

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

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

746
    // Use per-profile useGit setting, falling back to global if not set
NEW
747
    QString useGitStr = newProfiles.value(name).value("useGit");
×
748
    bool useGit =
NEW
749
        useGitStr.isEmpty() ? QtPassSettings::isUseGit() : useGitStr == "true";
×
750

NEW
751
    if (result == QDialog::Accepted && useGit) {
×
UNCOV
752
      QtPassSettings::getPass()->GitInit();
×
753
    }
754

755
    // Restore previous store setting
756
    QtPassSettings::setPassStore(prevStore);
×
757
  }
×
758
}
×
759

760
/**
761
 * @brief ConfigDialog::on_addButton_clicked add a profile row.
762
 */
763
void ConfigDialog::on_addButton_clicked() {
×
764
  bool sortingEnabled = ui->profileTable->isSortingEnabled();
×
765
  ui->profileTable->setSortingEnabled(false);
×
766

767
  int n = ui->profileTable->rowCount();
×
768
  ui->profileTable->insertRow(n);
×
769
  ui->profileTable->setItem(n, 0, new QTableWidgetItem(tr("New Profile")));
×
770
  ui->profileTable->setItem(n, 1, new QTableWidgetItem(ui->storePath->text()));
×
771
  ui->profileTable->setItem(n, 2, new QTableWidgetItem());
×
772

773
  ui->profileTable->setSortingEnabled(sortingEnabled);
×
774

775
  int currentRow = ui->profileTable->row(ui->profileTable->item(n, 0));
×
776
  ui->profileTable->selectRow(currentRow);
×
777
  ui->deleteButton->setEnabled(true);
×
778

779
  ui->profileTable->editItem(ui->profileTable->item(currentRow, 0));
×
780
  ui->profileTable->item(currentRow, 0)->setSelected(true);
×
781

782
  validate();
×
783
  updateProfileStatus(currentRow);
×
784
}
×
785

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

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

823
  validate();
×
824
  updateProfileStatus(-1);
×
825
}
826

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

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

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

872
/**
873
 * @brief ConfigDialog::wizard first-time use wizard.
874
 */
875
void ConfigDialog::wizard() {
×
876
  (void)Util::configIsValid();
×
877
  on_autodetectButton_clicked();
×
878

879
  if (!checkGpgExistence()) {
×
880
    return;
881
  }
882
  if (!checkSecretKeys()) {
×
883
    return;
884
  }
885
  if (!checkPasswordStore()) {
×
886
    return;
887
  }
888
  handleGpgIdFile();
×
889

890
  ui->checkBoxHidePassword->setCheckState(Qt::Checked);
×
891
}
892

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

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

948
#ifdef QT_DEBUG
949
  dbg() << names;
950
#endif
951

952
  if ((gpg.startsWith("wsl ") || QFile(gpg).exists()) && names.empty()) {
×
953
    KeygenDialog d(this);
×
954
    return d.exec();
×
955
  }
×
956
  return true;
957
}
958

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

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

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

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

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

1045
    if (!useSystray) {
×
1046
      ui->checkBoxHideOnClose->setChecked(false);
×
1047
      ui->checkBoxStartMinimized->setChecked(false);
×
1048
    }
1049
  }
1050
}
×
1051

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

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

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

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

1093
void ConfigDialog::useGrepSearch(bool useGrepSearch) {
×
1094
  ui->checkBoxUseGrepSearch->setChecked(useGrepSearch);
×
1095
}
×
1096

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

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

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

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

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

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

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

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

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

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

1222
void ConfigDialog::onProfileTableItemChanged(QTableWidgetItem *item) {
×
1223
  validate(item);
×
1224
  updateProfileStatus(item ? item->row() : -1);
×
1225
}
×
1226

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

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

1251
  QTableWidgetItem *nameItem = ui->profileTable->item(row, 0);
×
1252
  QTableWidgetItem *pathItem = ui->profileTable->item(row, 1);
×
1253

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

1271
  ui->statusLabel->setText(statusMessage);
×
1272
}
1273

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