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

IJHack / QtPass / 24574931090

17 Apr 2026 04:11PM UTC coverage: 21.249% (-0.08%) from 21.329%
24574931090

Pull #1032

github

web-flow
Merge 153c521b1 into 89ef5cc93
Pull Request #1032: fix: set PASSWORD_STORE_* env vars and grey out pass-managed controls

2 of 27 new or added lines in 2 files covered. (7.41%)

227 existing lines in 2 files now uncovered.

1143 of 5379 relevant lines covered (21.25%)

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

21
#ifdef QT_DEBUG
22
#include "debughelper.h"
23
#endif
24

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

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

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

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

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

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

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

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

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

99
  setProfiles(QtPassSettings::getProfiles(), QtPassSettings::getProfile());
×
100
  setPwgenPath(QtPassSettings::getPwgenExecutable());
×
101
  setPasswordConfiguration(QtPassSettings::getPasswordConfiguration());
×
102

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

109
  useOtp(QtPassSettings::isUseOtp());
×
110
  useQrencode(QtPassSettings::isUseQrencode());
×
111

112
  usePwgen(QtPassSettings::isUsePwgen());
×
113
  useTemplate(QtPassSettings::isUseTemplate());
×
114

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

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

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

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

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

143
  connect(ui->profileTable, &QTableWidget::itemChanged, this,
×
144
          &ConfigDialog::onProfileTableItemChanged);
×
145
  connect(this, &ConfigDialog::accepted, this, &ConfigDialog::on_accepted);
×
146
}
×
147

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

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

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

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

197
  if (item == nullptr) {
×
198
    for (int i = 0; i < ui->profileTable->rowCount(); i++) {
×
199
      for (int j = 0; j < ui->profileTable->columnCount(); j++) {
×
200
        QTableWidgetItem *_item = ui->profileTable->item(i, j);
×
201

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

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

228
  ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(status);
×
229
}
×
230

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

287
  QtPassSettings::setVersion(VERSION);
×
288
}
×
289

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

322
/**
323
 * @brief ConfigDialog::on_radioButtonNative_clicked wrapper for
324
 * ConfigDialog::setGroupBoxState()
325
 */
326
void ConfigDialog::on_radioButtonNative_clicked() { setGroupBoxState(); }
×
327

328
/**
329
 * @brief ConfigDialog::on_radioButtonPass_clicked wrapper for
330
 * ConfigDialog::setGroupBoxState()
331
 */
332
void ConfigDialog::on_radioButtonPass_clicked() { setGroupBoxState(); }
×
333

334
/**
335
 * @brief ConfigDialog::getSecretKeys get list of secret/private keys
336
 * @return QStringList keys
337
 */
338
auto ConfigDialog::getSecretKeys() -> QStringList {
×
339
  QList<UserInfo> keys = QtPassSettings::getPass()->listKeys("", true);
×
340
  QStringList names;
×
341

342
  if (keys.empty()) {
×
343
    return names;
344
  }
345

346
  foreach (const UserInfo &sec, keys)
×
347
    names << sec.name;
×
348

349
  return names;
×
350
}
351

352
/**
353
 * @brief ConfigDialog::setGroupBoxState update checkboxes.
354
 */
355
void ConfigDialog::setGroupBoxState() {
×
356
  bool state = ui->radioButtonPass->isChecked();
×
357
  ui->groupBoxNative->setEnabled(!state);
×
358
  ui->groupBoxPass->setEnabled(state);
×
359
  // password generation controls are pass-managed when using pass binary
360
  bool enablePwgen = !state;
NEW
361
  ui->spinBoxPasswordLength->setEnabled(enablePwgen);
×
NEW
362
  ui->labelPasswordChars->setEnabled(enablePwgen);
×
NEW
363
  ui->passwordCharTemplateSelector->setEnabled(enablePwgen);
×
NEW
364
  ui->lineEditPasswordChars->setEnabled(enablePwgen);
×
NEW
365
  ui->checkBoxUsePwgen->setEnabled(enablePwgen);
×
NEW
366
  ui->checkBoxAvoidCapitals->setEnabled(enablePwgen);
×
NEW
367
  ui->checkBoxUseSymbols->setEnabled(enablePwgen);
×
NEW
368
  ui->checkBoxLessRandom->setEnabled(enablePwgen);
×
NEW
369
  ui->checkBoxAvoidNumbers->setEnabled(enablePwgen);
×
NEW
370
}
×
371

372
/**
373
 * @brief ConfigDialog::selectExecutable pop-up to choose an executable.
374
 * @return
375
 */
UNCOV
376
auto ConfigDialog::selectExecutable() -> QString {
×
UNCOV
377
  QFileDialog dialog(this);
×
UNCOV
378
  dialog.setFileMode(QFileDialog::ExistingFile);
×
UNCOV
379
  dialog.setOption(QFileDialog::ReadOnly);
×
UNCOV
380
  if (dialog.exec()) {
×
UNCOV
381
    return dialog.selectedFiles().constFirst();
×
382
  }
383

384
  return {};
385
}
×
386

387
/**
388
 * @brief ConfigDialog::selectFolder pop-up to choose a folder.
389
 * @return
390
 */
391
auto ConfigDialog::selectFolder() -> QString {
×
UNCOV
392
  QFileDialog dialog(this);
×
UNCOV
393
  dialog.setFileMode(QFileDialog::Directory);
×
UNCOV
394
  dialog.setFilter(QDir::NoFilter);
×
UNCOV
395
  dialog.setOption(QFileDialog::ShowDirsOnly);
×
UNCOV
396
  if (dialog.exec()) {
×
397
    return dialog.selectedFiles().constFirst();
×
398
  }
399

400
  return {};
401
}
×
402

403
/**
404
 * @brief ConfigDialog::on_toolButtonGit_clicked get git application.
405
 * Enable checkboxes if found.
406
 */
407
void ConfigDialog::on_toolButtonGit_clicked() {
×
UNCOV
408
  QString git = selectExecutable();
×
UNCOV
409
  bool state = !git.isEmpty();
×
UNCOV
410
  if (state) {
×
UNCOV
411
    ui->gitPath->setText(git);
×
412
  } else {
413
    useGit(false);
×
414
  }
415

416
  ui->checkBoxUseGit->setEnabled(state);
×
417
}
×
418

419
/**
420
 * @brief ConfigDialog::on_toolButtonGpg_clicked get gpg application.
421
 */
422
void ConfigDialog::on_toolButtonGpg_clicked() {
×
423
  QString gpg = selectExecutable();
×
UNCOV
424
  if (!gpg.isEmpty()) {
×
UNCOV
425
    ui->gpgPath->setText(gpg);
×
426
  }
UNCOV
427
}
×
428

429
/**
430
 * @brief ConfigDialog::on_pushButtonGenerateKey_clicked open keygen dialog.
431
 */
UNCOV
432
void ConfigDialog::on_pushButtonGenerateKey_clicked() {
×
433
  KeygenDialog d(this);
×
UNCOV
434
  d.exec();
×
UNCOV
435
}
×
436

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

447
/**
448
 * @brief ConfigDialog::on_toolButtonStore_clicked get .password-store
449
 * location.
450
 */
451
void ConfigDialog::on_toolButtonStore_clicked() {
×
UNCOV
452
  QString store = selectFolder();
×
UNCOV
453
  if (!store.isEmpty()) {
×
UNCOV
454
    ui->storePath->setText(store);
×
455
  }
UNCOV
456
}
×
457

458
/**
459
 * @brief ConfigDialog::on_comboBoxClipboard_activated show and hide options.
460
 * @param index of selectbox (0 = no clipboard).
461
 */
462
void ConfigDialog::on_comboBoxClipboard_activated(int index) {
×
UNCOV
463
  bool state = index > 0;
×
464

UNCOV
465
  ui->checkBoxSelection->setEnabled(state);
×
UNCOV
466
  ui->checkBoxAutoclear->setEnabled(state);
×
UNCOV
467
  ui->checkBoxHidePassword->setEnabled(state);
×
468
  ui->checkBoxHideContent->setEnabled(state);
×
469
  if (state) {
×
UNCOV
470
    ui->spinBoxAutoclearSeconds->setEnabled(ui->checkBoxAutoclear->isChecked());
×
471
    ui->labelSeconds->setEnabled(ui->checkBoxAutoclear->isChecked());
×
472
  } else {
473
    ui->spinBoxAutoclearSeconds->setEnabled(false);
×
474
    ui->labelSeconds->setEnabled(false);
×
475
  }
476
}
×
477

478
/**
479
 * @brief ConfigDialog::on_checkBoxAutoclearPanel_clicked enable and disable
480
 * options based on autoclear use.
481
 */
482
void ConfigDialog::on_checkBoxAutoclearPanel_clicked() {
×
UNCOV
483
  bool state = ui->checkBoxAutoclearPanel->isChecked();
×
UNCOV
484
  ui->spinBoxAutoclearPanelSeconds->setEnabled(state);
×
UNCOV
485
  ui->labelPanelSeconds->setEnabled(state);
×
UNCOV
486
}
×
487

488
/**
489
 * @brief ConfigDialog::useSelection set the clipboard type use from
490
 * MainWindow.
491
 * @param useSelection
492
 */
UNCOV
493
void ConfigDialog::useSelection(bool useSelection) {
×
UNCOV
494
  ui->checkBoxSelection->setChecked(useSelection);
×
UNCOV
495
  on_checkBoxSelection_clicked();
×
UNCOV
496
}
×
497

498
/**
499
 * @brief ConfigDialog::useAutoclear set the clipboard autoclear use from
500
 * MainWindow.
501
 * @param useAutoclear
502
 */
UNCOV
503
void ConfigDialog::useAutoclear(bool useAutoclear) {
×
UNCOV
504
  ui->checkBoxAutoclear->setChecked(useAutoclear);
×
UNCOV
505
  on_checkBoxAutoclear_clicked();
×
UNCOV
506
}
×
507

508
/**
509
 * @brief ConfigDialog::useAutoclearPanel set the panel autoclear use from
510
 * MainWindow.
511
 * @param useAutoclearPanel
512
 */
UNCOV
513
void ConfigDialog::useAutoclearPanel(bool useAutoclearPanel) {
×
UNCOV
514
  ui->checkBoxAutoclearPanel->setChecked(useAutoclearPanel);
×
UNCOV
515
  on_checkBoxAutoclearPanel_clicked();
×
UNCOV
516
}
×
517

518
/**
519
 * @brief ConfigDialog::on_checkBoxSelection_clicked checkbox clicked, update
520
 * state via ConfigDialog::on_comboBoxClipboard_activated
521
 */
522
void ConfigDialog::on_checkBoxSelection_clicked() {
×
UNCOV
523
  on_comboBoxClipboard_activated(ui->comboBoxClipboard->currentIndex());
×
UNCOV
524
}
×
525

526
/**
527
 * @brief ConfigDialog::on_checkBoxAutoclear_clicked checkbox clicked, update
528
 * state via ConfigDialog::on_comboBoxClipboard_activated
529
 */
530
void ConfigDialog::on_checkBoxAutoclear_clicked() {
×
UNCOV
531
  on_comboBoxClipboard_activated(ui->comboBoxClipboard->currentIndex());
×
UNCOV
532
}
×
533

534
/**
535
 * @brief ConfigDialog::genKey tunnel function to make MainWindow generate a
536
 * gpg key pair.
537
 * @todo refactor the process to not be entangled so much.
538
 * @param batch
539
 * @param dialog
540
 */
UNCOV
541
void ConfigDialog::genKey(const QString &batch, QDialog *dialog) {
×
UNCOV
542
  mainWindow->generateKeyPair(batch, dialog);
×
UNCOV
543
}
×
544

545
/**
546
 * @brief ConfigDialog::setProfiles set the profiles and chosen profile from
547
 * MainWindow.
548
 * @param profiles
549
 * @param profile
550
 */
UNCOV
551
void ConfigDialog::setProfiles(QHash<QString, QHash<QString, QString>> profiles,
×
552
                               const QString &currentProfile) {
UNCOV
553
  if (profiles.contains("")) {
×
UNCOV
554
    profiles.remove("");
×
555
    // remove weird "" key value pairs
556
  }
557

UNCOV
558
  ui->profileTable->setRowCount(profiles.count());
×
559
  QHashIterator<QString, QHash<QString, QString>> i(profiles);
×
560
  int n = 0;
UNCOV
561
  while (i.hasNext()) {
×
562
    i.next();
UNCOV
563
    if (!i.value().isEmpty() && !i.key().isEmpty()) {
×
564
      ui->profileTable->setItem(n, 0, new QTableWidgetItem(i.key()));
×
565
      ui->profileTable->setItem(n, 1,
×
UNCOV
566
                                new QTableWidgetItem(i.value().value("path")));
×
567
      ui->profileTable->setItem(
×
UNCOV
568
          n, 2, new QTableWidgetItem(i.value().value("signingKey")));
×
569
      if (i.key() == currentProfile) {
×
570
        ui->profileTable->selectRow(n);
×
571
      }
572
    }
573
    ++n;
×
574
  }
575
}
×
576

577
/**
578
 * @brief ConfigDialog::getProfiles return profile list.
579
 * @return
580
 */
581
auto ConfigDialog::getProfiles() -> QHash<QString, QHash<QString, QString>> {
×
UNCOV
582
  QHash<QString, QHash<QString, QString>> profiles;
×
583
  // Check?
UNCOV
584
  for (int i = 0; i < ui->profileTable->rowCount(); ++i) {
×
UNCOV
585
    QHash<QString, QString> profile;
×
UNCOV
586
    QTableWidgetItem *pathItem = ui->profileTable->item(i, 1);
×
587
    if (nullptr != pathItem) {
×
588
      QTableWidgetItem *item = ui->profileTable->item(i, 0);
×
UNCOV
589
      if (item == nullptr) {
×
590
        continue;
591
      }
592
      profile["path"] = pathItem->text();
×
593
      QTableWidgetItem *signingKeyItem = ui->profileTable->item(i, 2);
×
594
      if (nullptr != signingKeyItem) {
×
595
        profile["signingKey"] = signingKeyItem->text();
×
596
      }
UNCOV
597
      profiles.insert(item->text(), profile);
×
598
    }
599
  }
×
600
  return profiles;
×
601
}
×
602

603
/**
604
 * @brief ConfigDialog::on_addButton_clicked add a profile row.
605
 */
606
void ConfigDialog::on_addButton_clicked() {
×
607
  int n = ui->profileTable->rowCount();
×
UNCOV
608
  ui->profileTable->insertRow(n);
×
UNCOV
609
  ui->profileTable->setItem(n, 0, new QTableWidgetItem());
×
UNCOV
610
  ui->profileTable->setItem(n, 1, new QTableWidgetItem(ui->storePath->text()));
×
UNCOV
611
  ui->profileTable->setItem(n, 2, new QTableWidgetItem());
×
612
  ui->profileTable->selectRow(n);
×
613
  ui->deleteButton->setEnabled(true);
×
614

615
  validate();
×
616
}
×
617

618
/**
619
 * @brief ConfigDialog::on_deleteButton_clicked remove a profile row.
620
 */
621
void ConfigDialog::on_deleteButton_clicked() {
×
622
  QSet<int> selectedRows; //  we use a set to prevent doubles
UNCOV
623
  QList<QTableWidgetItem *> itemList = ui->profileTable->selectedItems();
×
UNCOV
624
  if (itemList.count() == 0) {
×
UNCOV
625
    QMessageBox::warning(this, tr("No profile selected"),
×
UNCOV
626
                         tr("No profile selected to delete"));
×
627
    return;
628
  }
629
  QTableWidgetItem *item;
630
  foreach (item, itemList)
×
631
    selectedRows.insert(item->row());
×
632
  // get a list, and sort it big to small
UNCOV
633
  QList<int> rows = selectedRows.values();
×
UNCOV
634
  std::sort(rows.begin(), rows.end());
×
635
  // now actually do the removing:
636
  foreach (int row, rows)
×
637
    ui->profileTable->removeRow(row);
×
UNCOV
638
  if (ui->profileTable->rowCount() < 1) {
×
639
    ui->deleteButton->setEnabled(false);
×
640
  }
641

642
  validate();
×
643
}
644

645
/**
646
 * @brief ConfigDialog::criticalMessage wrapper for showing critical messages
647
 * in a popup.
648
 * @param title
649
 * @param text
650
 */
UNCOV
651
void ConfigDialog::criticalMessage(const QString &title, const QString &text) {
×
UNCOV
652
  QMessageBox::critical(this, title, text, QMessageBox::Ok, QMessageBox::Ok);
×
UNCOV
653
}
×
654

655
/**
656
 * @brief Checks whether the qrencode executable is available on the system.
657
 * @example
658
 * bool result = ConfigDialog::isQrencodeAvailable();
659
 * std::cout << result << std::endl; // Expected output: true if qrencode is
660
 * found, otherwise false
661
 *
662
 * @return bool - True if qrencode is available; otherwise false. On Windows,
663
 * always returns false.
664
 */
UNCOV
665
auto ConfigDialog::isQrencodeAvailable() -> bool {
×
666
#ifdef Q_OS_WIN
667
  return false;
668
#else
UNCOV
669
  QProcess which;
×
UNCOV
670
  which.start("which", QStringList() << "qrencode");
×
671
  which.waitForFinished();
×
UNCOV
672
  QtPassSettings::setQrencodeExecutable(
×
UNCOV
673
      which.readAllStandardOutput().trimmed());
×
UNCOV
674
  return which.exitCode() == 0;
×
675
#endif
676
}
×
677

678
auto ConfigDialog::isPassOtpAvailable() -> bool {
×
679
#ifdef Q_OS_WIN
680
  return false;
681
#else
682
  QProcess pass;
×
UNCOV
683
  pass.start(QtPassSettings::getPassExecutable(), QStringList() << "otp"
×
684
                                                                << "--help");
×
UNCOV
685
  pass.waitForFinished(2000);
×
UNCOV
686
  return pass.exitCode() == 0;
×
687
#endif
688
}
×
689

690
/**
691
 * @brief ConfigDialog::wizard first-time use wizard.
692
 * @todo make this thing more reliable.
693
 */
694
void ConfigDialog::wizard() {
×
UNCOV
695
  (void)Util::configIsValid();
×
UNCOV
696
  on_autodetectButton_clicked();
×
697

UNCOV
698
  if (!checkGpgExistence()) {
×
699
    return;
700
  }
701
  if (!checkSecretKeys()) {
×
702
    return;
703
  }
704
  if (!checkPasswordStore()) {
×
705
    return;
706
  }
707
  handleGpgIdFile();
×
708

UNCOV
709
  ui->checkBoxHidePassword->setCheckState(Qt::Checked);
×
710
}
711

712
/**
713
 * @brief Checks whether the configured GnuPG executable exists.
714
 * @example
715
 * bool result = ConfigDialog::checkGpgExistence();
716
 * std::cout << result << std::endl; // Expected output: true if GnuPG is found,
717
 * false otherwise
718
 *
719
 * @return bool - True if the GnuPG path is valid or uses a WSL command prefix;
720
 * false if the executable cannot be found and an error message is shown.
721
 */
UNCOV
722
auto ConfigDialog::checkGpgExistence() -> bool {
×
UNCOV
723
  QString gpg = ui->gpgPath->text();
×
UNCOV
724
  if (!gpg.startsWith("wsl ") && !QFile(gpg).exists()) {
×
UNCOV
725
    criticalMessage(
×
UNCOV
726
        tr("GnuPG not found"),
×
727
#ifdef Q_OS_WIN
728
#ifdef WINSTORE
729
        tr("Please install GnuPG on your system.<br>Install "
730
           "<strong>Ubuntu</strong> from the Microsoft Store to get it.<br>"
731
           "If you already did so, make sure you started it once and<br>"
732
           "click \"Autodetect\" in the next dialog.")
733
#else
734
        tr("Please install GnuPG on your system.<br>Install "
735
           "<strong>Ubuntu</strong> from the Microsoft Store<br>or <a "
736
           "href=\"https://www.gnupg.org/download/#sec-1-2\">download</a> it "
737
           "from GnuPG.org")
738
#endif
739
#else
UNCOV
740
        tr("Please install GnuPG on your system.<br>Install "
×
741
           "<strong>gpg</strong> using your favorite package manager<br>or "
742
           "<a "
743
           "href=\"https://www.gnupg.org/download/#sec-1-2\">download</a> it "
744
           "from GnuPG.org")
745
#endif
746
    );
UNCOV
747
    return false;
×
748
  }
749
  return true;
750
}
751

752
/**
753
 * @brief Checks whether secret keys are available and, if needed, prompts the
754
 * user to generate them.
755
 * @example
756
 * bool result = ConfigDialog.checkSecretKeys();
757
 * std::cout << result << std::endl; // Expected output: true if keys are
758
 * present or key generation dialog is accepted, false otherwise
759
 *
760
 * @return bool - Returns true when secret keys are already available or the key
761
 * generation dialog is accepted; false if the dialog is rejected.
762
 */
UNCOV
763
auto ConfigDialog::checkSecretKeys() -> bool {
×
UNCOV
764
  QString gpg = ui->gpgPath->text();
×
UNCOV
765
  QStringList names = getSecretKeys();
×
766

767
#ifdef QT_DEBUG
768
  dbg() << names;
769
#endif
770

771
  if ((gpg.startsWith("wsl ") || QFile(gpg).exists()) && names.empty()) {
×
UNCOV
772
    KeygenDialog d(this);
×
UNCOV
773
    return d.exec();
×
UNCOV
774
  }
×
775
  return true;
776
}
777

778
/**
779
 * @brief Checks whether the password-store path exists and prompts the user to
780
 * create it if it does not.
781
 * @example
782
 * bool result = ConfigDialog::checkPasswordStore();
783
 * std::cout << std::boolalpha << result << std::endl; // Expected output: true
784
 * if the store exists or is created successfully, false if creation fails
785
 *
786
 * @return bool - True if the password-store exists or is successfully created,
787
 * false if creation fails.
788
 */
UNCOV
789
auto ConfigDialog::checkPasswordStore() -> bool {
×
UNCOV
790
  QString passStore = ui->storePath->text();
×
791

UNCOV
792
  if (!QFile(passStore).exists()) {
×
UNCOV
793
    if (QMessageBox::question(
×
UNCOV
794
            this, tr("Create password-store?"),
×
795
            tr("Would you like to create a password-store at %1?")
×
796
                .arg(passStore),
×
797
            QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
798
      if (!QDir().mkdir(passStore)) {
×
799
        QMessageBox::warning(
×
800
            this, tr("Error"),
×
801
            tr("Failed to create password-store at: %1").arg(passStore));
×
802
        return false;
×
803
      }
804
#ifdef Q_OS_WIN
805
      SetFileAttributes(passStore.toStdWString().c_str(),
806
                        FILE_ATTRIBUTE_HIDDEN);
807
#endif
808
      if (ui->checkBoxUseGit->isChecked()) {
×
UNCOV
809
        emit mainWindow->passGitInitNeeded();
×
810
      }
UNCOV
811
      mainWindow->userDialog(passStore);
×
812
    }
813
  }
814
  return true;
815
}
816

817
/**
818
 * @brief Handles selection and validation of the password store's .gpg-id file.
819
 * @example
820
 * ConfigDialog dialog;
821
 * dialog.handleGpgIdFile();
822
 *
823
 * @return void - This method does not return a value; it updates the UI flow
824
 * and may prompt the user to choose a valid password store.
825
 */
UNCOV
826
void ConfigDialog::handleGpgIdFile() {
×
UNCOV
827
  QString passStore = ui->storePath->text();
×
UNCOV
828
  if (!QFile(QDir(passStore).filePath(".gpg-id")).exists()) {
×
829
#ifdef QT_DEBUG
830
    dbg() << ".gpg-id file does not exist";
831
#endif
832
    criticalMessage(tr("Password store not initialised"),
×
833
                    tr("The folder %1 doesn't seem to be a password store or "
×
834
                       "is not yet initialised.")
UNCOV
835
                        .arg(passStore));
×
836

UNCOV
837
    while (!QFile(passStore).exists()) {
×
838
      on_toolButtonStore_clicked();
×
839
      if (passStore == ui->storePath->text()) {
×
840
        return;
841
      }
UNCOV
842
      passStore = ui->storePath->text();
×
843
    }
844
    if (!QFile(passStore + ".gpg-id").exists()) {
×
845
#ifdef QT_DEBUG
846
      dbg() << ".gpg-id file still does not exist :/";
847
#endif
848
      mainWindow->userDialog(passStore);
×
849
    }
850
  }
851
}
852

853
/**
854
 * @brief ConfigDialog::useTrayIcon set preference for using trayicon.
855
 * Enable or disable related checkboxes accordingly.
856
 * @param useSystray
857
 */
UNCOV
858
void ConfigDialog::useTrayIcon(bool useSystray) {
×
UNCOV
859
  if (QSystemTrayIcon::isSystemTrayAvailable()) {
×
UNCOV
860
    ui->checkBoxUseTrayIcon->setChecked(useSystray);
×
UNCOV
861
    ui->checkBoxHideOnClose->setEnabled(useSystray);
×
UNCOV
862
    ui->checkBoxStartMinimized->setEnabled(useSystray);
×
863

864
    if (!useSystray) {
×
865
      ui->checkBoxHideOnClose->setChecked(false);
×
866
      ui->checkBoxStartMinimized->setChecked(false);
×
867
    }
868
  }
UNCOV
869
}
×
870

871
/**
872
 * @brief ConfigDialog::on_checkBoxUseTrayIcon_clicked enable and disable
873
 * related checkboxes.
874
 */
875
void ConfigDialog::on_checkBoxUseTrayIcon_clicked() {
×
UNCOV
876
  bool state = ui->checkBoxUseTrayIcon->isChecked();
×
UNCOV
877
  ui->checkBoxHideOnClose->setEnabled(state);
×
UNCOV
878
  ui->checkBoxStartMinimized->setEnabled(state);
×
UNCOV
879
}
×
880

881
/**
882
 * @brief ConfigDialog::closeEvent close this window.
883
 * @param event
884
 */
885
void ConfigDialog::closeEvent(QCloseEvent *event) {
×
UNCOV
886
  QtPassSettings::setDialogGeometry("configDialog", saveGeometry());
×
UNCOV
887
  if (!isMaximized()) {
×
UNCOV
888
    QtPassSettings::setDialogPos("configDialog", pos());
×
UNCOV
889
    QtPassSettings::setDialogSize("configDialog", size());
×
890
  }
891
  QtPassSettings::setDialogMaximized("configDialog", isMaximized());
×
892
  event->accept();
893
}
×
894

895
/**
896
 * @brief ConfigDialog::useGit set preference for using git.
897
 * @param useGit
898
 */
899
void ConfigDialog::useGit(bool useGit) {
×
UNCOV
900
  ui->checkBoxUseGit->setChecked(useGit);
×
UNCOV
901
  on_checkBoxUseGit_clicked();
×
UNCOV
902
}
×
903

904
/**
905
 * @brief ConfigDialog::useOtp set preference for using otp plugin.
906
 * @param useOtp
907
 */
908
void ConfigDialog::useOtp(bool useOtp) {
×
UNCOV
909
  ui->checkBoxUseOtp->setChecked(useOtp);
×
UNCOV
910
}
×
911

912
/**
913
 * @brief ConfigDialog::useQrencode set preference for using qrencode plugin.
914
 * @param useQrencode
915
 */
916
void ConfigDialog::useQrencode(bool useQrencode) {
×
UNCOV
917
  ui->checkBoxUseQrencode->setChecked(useQrencode);
×
UNCOV
918
}
×
919

920
/**
921
 * @brief ConfigDialog::on_checkBoxUseGit_clicked enable or disable related
922
 * checkboxes.
923
 */
924
void ConfigDialog::on_checkBoxUseGit_clicked() {
×
UNCOV
925
  ui->checkBoxAddGPGId->setEnabled(ui->checkBoxUseGit->isChecked());
×
UNCOV
926
  ui->checkBoxAutoPull->setEnabled(ui->checkBoxUseGit->isChecked());
×
UNCOV
927
  ui->checkBoxAutoPush->setEnabled(ui->checkBoxUseGit->isChecked());
×
UNCOV
928
}
×
929

930
/**
931
 * @brief ConfigDialog::on_toolButtonPwgen_clicked enable or disable related
932
 * options in the interface.
933
 */
934
void ConfigDialog::on_toolButtonPwgen_clicked() {
×
UNCOV
935
  QString pwgen = selectExecutable();
×
UNCOV
936
  if (!pwgen.isEmpty()) {
×
UNCOV
937
    ui->pwgenPath->setText(pwgen);
×
UNCOV
938
    ui->checkBoxUsePwgen->setEnabled(true);
×
939
  } else {
940
    ui->checkBoxUsePwgen->setEnabled(false);
×
941
    ui->checkBoxUsePwgen->setChecked(false);
×
942
  }
943
}
×
944

945
/**
946
 * @brief ConfigDialog::setPwgenPath set pwgen executable path.
947
 * Enable or disable related options in the interface.
948
 * @param pwgen
949
 */
UNCOV
950
void ConfigDialog::setPwgenPath(const QString &pwgen) {
×
UNCOV
951
  ui->pwgenPath->setText(pwgen);
×
UNCOV
952
  if (pwgen.isEmpty()) {
×
UNCOV
953
    ui->checkBoxUsePwgen->setChecked(false);
×
UNCOV
954
    ui->checkBoxUsePwgen->setEnabled(false);
×
955
  }
956
  on_checkBoxUsePwgen_clicked();
×
957
}
×
958

959
/**
960
 * @brief ConfigDialog::on_checkBoxUsePwgen_clicked enable or disable related
961
 * options in the interface.
962
 */
963
void ConfigDialog::on_checkBoxUsePwgen_clicked() {
×
UNCOV
964
  bool usePwgen = ui->checkBoxUsePwgen->isChecked();
×
UNCOV
965
  ui->checkBoxAvoidCapitals->setEnabled(usePwgen);
×
UNCOV
966
  ui->checkBoxAvoidNumbers->setEnabled(usePwgen);
×
UNCOV
967
  ui->checkBoxLessRandom->setEnabled(usePwgen);
×
UNCOV
968
  ui->checkBoxUseSymbols->setEnabled(usePwgen);
×
969
  ui->lineEditPasswordChars->setEnabled(!usePwgen);
×
NEW
970
  ui->labelPasswordChars->setEnabled(!usePwgen);
×
NEW
971
  ui->passwordCharTemplateSelector->setEnabled(!usePwgen);
×
972
}
×
973

974
/**
975
 * @brief ConfigDialog::usePwgen set preference for using pwgen (can be
976
 * overruled by empty pwgenPath).
977
 * enable or disable related options in the interface via
978
 * ConfigDialog::on_checkBoxUsePwgen_clicked
979
 * @param usePwgen
980
 */
UNCOV
981
void ConfigDialog::usePwgen(bool usePwgen) {
×
UNCOV
982
  if (ui->pwgenPath->text().isEmpty()) {
×
983
    usePwgen = false;
984
  }
UNCOV
985
  ui->checkBoxUsePwgen->setChecked(usePwgen);
×
UNCOV
986
  on_checkBoxUsePwgen_clicked();
×
UNCOV
987
}
×
988

989
void ConfigDialog::setPasswordConfiguration(
×
990
    const PasswordConfiguration &config) {
UNCOV
991
  ui->spinBoxPasswordLength->setValue(config.length);
×
UNCOV
992
  ui->passwordCharTemplateSelector->setCurrentIndex(config.selected);
×
993
  if (config.selected != PasswordConfiguration::CUSTOM) {
×
994
    ui->lineEditPasswordChars->setEnabled(false);
×
995
  }
UNCOV
996
  ui->lineEditPasswordChars->setText(config.Characters[config.selected]);
×
997
}
×
998

999
auto ConfigDialog::getPasswordConfiguration() -> PasswordConfiguration {
×
1000
  PasswordConfiguration config;
×
1001
  config.length = ui->spinBoxPasswordLength->value();
×
1002
  config.selected = static_cast<PasswordConfiguration::characterSet>(
×
UNCOV
1003
      ui->passwordCharTemplateSelector->currentIndex());
×
1004
  config.Characters[PasswordConfiguration::CUSTOM] =
1005
      ui->lineEditPasswordChars->text();
×
UNCOV
1006
  return config;
×
1007
}
×
1008

1009
/**
1010
 * @brief ConfigDialog::on_passwordCharTemplateSelector_activated sets the
1011
 * passwordChar Template
1012
 * combo box to the desired entry
1013
 * @param entry of
1014
 */
1015
void ConfigDialog::on_passwordCharTemplateSelector_activated(int index) {
×
UNCOV
1016
  ui->lineEditPasswordChars->setText(
×
UNCOV
1017
      QtPassSettings::getPasswordConfiguration().Characters[index]);
×
UNCOV
1018
  if (index == PasswordConfiguration::CUSTOM) {
×
UNCOV
1019
    ui->lineEditPasswordChars->setEnabled(true);
×
1020
  } else {
UNCOV
1021
    ui->lineEditPasswordChars->setEnabled(false);
×
1022
  }
1023
}
×
1024

1025
/**
1026
 * @brief ConfigDialog::on_checkBoxUseTemplate_clicked enable or disable the
1027
 * template field and options.
1028
 */
1029
void ConfigDialog::on_checkBoxUseTemplate_clicked() {
×
UNCOV
1030
  ui->plainTextEditTemplate->setEnabled(ui->checkBoxUseTemplate->isChecked());
×
1031
  ui->checkBoxTemplateAllFields->setEnabled(
×
UNCOV
1032
      ui->checkBoxUseTemplate->isChecked());
×
UNCOV
1033
}
×
1034

UNCOV
1035
void ConfigDialog::onProfileTableItemChanged(QTableWidgetItem *item) {
×
UNCOV
1036
  validate(item);
×
1037
}
×
1038

1039
/**
1040
 * @brief ConfigDialog::useTemplate set preference for using templates.
1041
 * @param useTemplate
1042
 */
1043
void ConfigDialog::useTemplate(bool useTemplate) {
×
1044
  ui->checkBoxUseTemplate->setChecked(useTemplate);
×
1045
  on_checkBoxUseTemplate_clicked();
×
UNCOV
1046
}
×
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