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

IJHack / QtPass / 23572495042

26 Mar 2026 01:12AM UTC coverage: 17.849% (+0.3%) from 17.516%
23572495042

Pull #769

github

web-flow
Merge 192acec4d into 8c5fc9456
Pull Request #769: CodeFactor: Fix complex method and style recommendations

4 of 141 new or added lines in 2 files covered. (2.84%)

6 existing lines in 1 file now uncovered.

888 of 4975 relevant lines covered (17.85%)

7.22 hits per line

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

0.0
/src/mainwindow.cpp
1
// SPDX-FileCopyrightText: 2016 Anne Jan Brouwer
2
// SPDX-License-Identifier: GPL-3.0-or-later
3
#include "mainwindow.h"
4

5
#ifdef QT_DEBUG
6
#include "debughelper.h"
7
#endif
8

9
#include "configdialog.h"
10
#include "filecontent.h"
11
#include "passworddialog.h"
12
#include "qpushbuttonasqrcode.h"
13
#include "qpushbuttonshowpassword.h"
14
#include "qpushbuttonwithclipboard.h"
15
#include "qtpass.h"
16
#include "qtpasssettings.h"
17
#include "trayicon.h"
18
#include "ui_mainwindow.h"
19
#include "usersdialog.h"
20
#include "util.h"
21
#include <QCloseEvent>
22
#include <QDesktopServices>
23
#include <QDialog>
24
#include <QDirIterator>
25
#include <QFileInfo>
26
#include <QInputDialog>
27
#include <QLabel>
28
#include <QMenu>
29
#include <QMessageBox>
30
#include <QShortcut>
31
#include <QTimer>
32
#include <utility>
33

34
/**
35
 * @brief MainWindow::MainWindow handles all of the main functionality and also
36
 * the main window.
37
 * @param searchText for searching from cli
38
 * @param parent pointer
39
 */
40
MainWindow::MainWindow(const QString &searchText, QWidget *parent)
×
41
    : QMainWindow(parent), ui(new Ui::MainWindow), keygen(nullptr),
×
42
      tray(nullptr) {
×
43
#ifdef __APPLE__
44
  // extra treatment for mac os
45
  // see http://doc.qt.io/qt-5/qkeysequence.html#qt_set_sequence_auto_mnemonic
46
  qt_set_sequence_auto_mnemonic(true);
47
#endif
48
  ui->setupUi(this);
×
49

50
  m_qtPass = new QtPass(this);
×
51

52
  // register shortcut ctrl/cmd + Q to close the main window
53
  new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_Q), this, SLOT(close()));
×
54
  // register shortcut ctrl/cmd + C to copy the currently selected password
55
  new QShortcut(QKeySequence(QKeySequence::StandardKey::Copy), this,
×
56
                SLOT(copyPasswordFromTreeview()));
×
57

58
  model.setNameFilters(QStringList() << "*.gpg");
×
59
  model.setNameFilterDisables(false);
×
60

61
  /*
62
   * I added this to solve Windows bug but now on GNU/Linux the main folder,
63
   * if hidden, disappear
64
   *
65
   * model.setFilter(QDir::NoDot);
66
   */
67

68
  QString passStore = QtPassSettings::getPassStore(Util::findPasswordStore());
×
69

70
  QModelIndex rootDir = model.setRootPath(passStore);
×
71
  model.fetchMore(rootDir);
×
72

73
  proxyModel.setModelAndStore(&model, passStore);
×
74
  selectionModel.reset(new QItemSelectionModel(&proxyModel));
×
75

76
  ui->treeView->setModel(&proxyModel);
×
77
  ui->treeView->setRootIndex(proxyModel.mapFromSource(rootDir));
×
78
  ui->treeView->setColumnHidden(1, true);
×
79
  ui->treeView->setColumnHidden(2, true);
×
80
  ui->treeView->setColumnHidden(3, true);
×
81
  ui->treeView->setHeaderHidden(true);
×
82
  ui->treeView->setIndentation(15);
×
83
  ui->treeView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
×
84
  ui->treeView->setContextMenuPolicy(Qt::CustomContextMenu);
×
85
  ui->treeView->header()->setSectionResizeMode(0, QHeaderView::Stretch);
×
86
  ui->treeView->sortByColumn(0, Qt::AscendingOrder);
×
87
  connect(ui->treeView, &QWidget::customContextMenuRequested, this,
×
88
          &MainWindow::showContextMenu);
×
89
  connect(ui->treeView, &DeselectableTreeView::emptyClicked, this,
×
90
          &MainWindow::deselect);
×
91

92
  if (QtPassSettings::isUseMonospace()) {
×
93
    QFont monospace("Monospace");
×
94
    monospace.setStyleHint(QFont::Monospace);
×
95
    ui->textBrowser->setFont(monospace);
×
96
  }
×
97
  if (QtPassSettings::isNoLineWrapping()) {
×
98
    ui->textBrowser->setLineWrapMode(QTextBrowser::NoWrap);
×
99
  }
100
  ui->textBrowser->setOpenExternalLinks(true);
×
101
  ui->textBrowser->setContextMenuPolicy(Qt::CustomContextMenu);
×
102
  connect(ui->textBrowser, &QWidget::customContextMenuRequested, this,
×
103
          &MainWindow::showBrowserContextMenu);
×
104

105
  updateProfileBox();
×
106

107
  QtPassSettings::getPass()->updateEnv();
×
108
  clearPanelTimer.setInterval(MS_PER_SECOND *
×
109
                              QtPassSettings::getAutoclearPanelSeconds());
×
110
  clearPanelTimer.setSingleShot(true);
×
111
  connect(&clearPanelTimer, &QTimer::timeout, this,
×
112
          [this]() -> void { clearPanel(); });
×
113

114
  searchTimer.setInterval(350);
×
115
  searchTimer.setSingleShot(true);
×
116

117
  connect(&searchTimer, &QTimer::timeout, this, &MainWindow::onTimeoutSearch);
×
118

119
  initToolBarButtons();
×
120
  initStatusBar();
×
121

122
  ui->lineEdit->setClearButtonEnabled(true);
×
123

124
  setUiElementsEnabled(true);
×
125

126
  QTimer::singleShot(10, this, SLOT(focusInput()));
127

128
  ui->lineEdit->setText(searchText);
×
129

130
  if (!m_qtPass->init()) {
×
131
    // no working config so this should just quit
132
    QApplication::quit();
×
133
  }
134
}
×
135

136
MainWindow::~MainWindow() { delete m_qtPass; }
×
137

138
/**
139
 * @brief MainWindow::focusInput selects any text (if applicable) in the search
140
 * box and sets focus to it. Allows for easy searching, called at application
141
 * start and when receiving empty message in MainWindow::messageAvailable when
142
 * compiled with SINGLE_APP=1 (default).
143
 */
144
void MainWindow::focusInput() {
×
145
  ui->lineEdit->selectAll();
×
146
  ui->lineEdit->setFocus();
×
147
}
×
148

149
/**
150
 * @brief MainWindow::changeEvent sets focus to the search box
151
 * @param event
152
 */
153
void MainWindow::changeEvent(QEvent *event) {
×
154
  QWidget::changeEvent(event);
×
155
  if (event->type() == QEvent::ActivationChange) {
×
156
    if (isActiveWindow()) {
×
157
      focusInput();
×
158
    }
159
  }
160
}
×
161

162
/**
163
 * @brief MainWindow::initToolBarButtons init main ToolBar and connect actions
164
 */
165
void MainWindow::initToolBarButtons() {
×
166
  connect(ui->actionAddPassword, &QAction::triggered, this,
×
167
          &MainWindow::addPassword);
×
168
  connect(ui->actionAddFolder, &QAction::triggered, this,
×
169
          &MainWindow::addFolder);
×
170
  connect(ui->actionEdit, &QAction::triggered, this, &MainWindow::onEdit);
×
171
  connect(ui->actionDelete, &QAction::triggered, this, &MainWindow::onDelete);
×
172
  connect(ui->actionPush, &QAction::triggered, this, &MainWindow::onPush);
×
173
  connect(ui->actionUpdate, &QAction::triggered, this, &MainWindow::onUpdate);
×
174
  connect(ui->actionUsers, &QAction::triggered, this, &MainWindow::onUsers);
×
175
  connect(ui->actionConfig, &QAction::triggered, this, &MainWindow::onConfig);
×
176
  connect(ui->actionOtp, &QAction::triggered, this, &MainWindow::onOtp);
×
177

178
  ui->actionAddPassword->setIcon(
×
179
      QIcon::fromTheme("document-new", QIcon(":/icons/document-new.svg")));
×
180
  ui->actionAddFolder->setIcon(
×
181
      QIcon::fromTheme("folder-new", QIcon(":/icons/folder-new.svg")));
×
182
  ui->actionEdit->setIcon(QIcon::fromTheme(
×
183
      "document-properties", QIcon(":/icons/document-properties.svg")));
×
184
  ui->actionDelete->setIcon(
×
185
      QIcon::fromTheme("edit-delete", QIcon(":/icons/edit-delete.svg")));
×
186
  ui->actionPush->setIcon(
×
187
      QIcon::fromTheme("go-up", QIcon(":/icons/go-top.svg")));
×
188
  ui->actionUpdate->setIcon(
×
189
      QIcon::fromTheme("go-down", QIcon(":/icons/go-bottom.svg")));
×
190
  ui->actionUsers->setIcon(QIcon::fromTheme(
×
191
      "x-office-address-book", QIcon(":/icons/x-office-address-book.svg")));
×
192
  ui->actionConfig->setIcon(QIcon::fromTheme(
×
193
      "applications-system", QIcon(":/icons/applications-system.svg")));
×
194
}
×
195

196
/**
197
 * @brief MainWindow::initStatusBar init statusBar with default message and logo
198
 */
199
void MainWindow::initStatusBar() {
×
200
  ui->statusBar->showMessage(tr("Welcome to QtPass %1").arg(VERSION), 2000);
×
201

202
  QPixmap logo = QPixmap::fromImage(QImage(":/artwork/icon.svg"))
×
203
                     .scaledToHeight(statusBar()->height());
×
204
  auto *logoApp = new QLabel(statusBar());
×
205
  logoApp->setPixmap(logo);
×
206
  statusBar()->addPermanentWidget(logoApp);
×
207
}
×
208

209
auto MainWindow::getCurrentTreeViewIndex() -> QModelIndex {
×
210
  return ui->treeView->currentIndex();
×
211
}
212

213
void MainWindow::cleanKeygenDialog() {
×
214
  this->keygen->close();
×
215
  this->keygen = nullptr;
×
216
}
×
217

218
void MainWindow::flashText(const QString &text, const bool isError,
×
219
                           const bool isHtml) {
220
  if (isError) {
×
221
    ui->textBrowser->setTextColor(Qt::red);
×
222
  }
223

224
  if (isHtml) {
×
225
    QString _text = text;
226
    if (!ui->textBrowser->toPlainText().isEmpty()) {
×
227
      _text = ui->textBrowser->toHtml() + _text;
×
228
    }
229
    ui->textBrowser->setHtml(_text);
×
230
  } else {
231
    ui->textBrowser->setText(text);
×
232
    ui->textBrowser->setTextColor(Qt::black);
×
233
  }
234
}
×
235

236
/**
237
 * @brief MainWindow::config pops up the configuration screen and handles all
238
 * inter-window communication
239
 */
NEW
240
void MainWindow::applyTextBrowserSettings() {
×
NEW
241
  if (QtPassSettings::isUseMonospace()) {
×
NEW
242
    QFont monospace("Monospace");
×
NEW
243
    monospace.setStyleHint(QFont::Monospace);
×
NEW
244
    ui->textBrowser->setFont(monospace);
×
NEW
245
  } else {
×
NEW
246
    ui->textBrowser->setFont(QFont());
×
247
  }
248

NEW
249
  if (QtPassSettings::isNoLineWrapping()) {
×
NEW
250
    ui->textBrowser->setLineWrapMode(QTextBrowser::NoWrap);
×
251
  } else {
NEW
252
    ui->textBrowser->setLineWrapMode(QTextBrowser::WidgetWidth);
×
253
  }
NEW
254
}
×
255

NEW
256
void MainWindow::applyWindowFlagsSettings() {
×
NEW
257
  if (QtPassSettings::isAlwaysOnTop()) {
×
258
    Qt::WindowFlags flags = windowFlags();
NEW
259
    this->setWindowFlags(flags | Qt::WindowStaysOnTopHint);
×
260
  } else {
NEW
261
    this->setWindowFlags(Qt::Window);
×
262
  }
NEW
263
  this->show();
×
NEW
264
}
×
265

266
void MainWindow::config() {
×
267
  QScopedPointer<ConfigDialog> d(new ConfigDialog(this));
×
268
  d->setModal(true);
×
269
  // Automatically default to pass if it's available
270
  if (m_qtPass->isFreshStart() &&
×
271
      QFile(QtPassSettings::getPassExecutable()).exists()) {
×
272
    QtPassSettings::setUsePass(true);
×
273
  }
274

275
  if (m_qtPass->isFreshStart()) {
×
276
    d->wizard(); //  does shit
×
277
  }
278
  if (d->exec()) {
×
279
    if (d->result() == QDialog::Accepted) {
×
NEW
280
      applyTextBrowserSettings();
×
NEW
281
      applyWindowFlagsSettings();
×
282

283
      updateProfileBox();
×
284
      ui->treeView->setRootIndex(proxyModel.mapFromSource(
×
285
          model.setRootPath(QtPassSettings::getPassStore())));
×
286

287
      if (m_qtPass->isFreshStart() && Util::checkConfig()) {
×
288
        config();
×
289
      }
290
      QtPassSettings::getPass()->updateEnv();
×
291
      clearPanelTimer.setInterval(MS_PER_SECOND *
×
292
                                  QtPassSettings::getAutoclearPanelSeconds());
×
293
      m_qtPass->setClipboardTimer();
×
294

295
      updateGitButtonVisibility();
×
296
      updateOtpButtonVisibility();
×
297
      if (QtPassSettings::isUseTrayIcon() && tray == nullptr) {
×
298
        initTrayIcon();
×
299
      } else if (!QtPassSettings::isUseTrayIcon() && tray != nullptr) {
×
300
        destroyTrayIcon();
×
301
      }
302
    }
303

304
    m_qtPass->setFreshStart(false);
×
305
  }
306
}
×
307

308
/**
309
 * @brief MainWindow::onUpdate do a git pull
310
 */
311
void MainWindow::onUpdate(bool block) {
×
312
  ui->statusBar->showMessage(tr("Updating password-store"), 2000);
×
313
  if (block) {
×
314
    QtPassSettings::getPass()->GitPull_b();
×
315
  } else {
316
    QtPassSettings::getPass()->GitPull();
×
317
  }
318
}
×
319

320
/**
321
 * @brief MainWindow::onPush do a git push
322
 */
323
void MainWindow::onPush() {
×
324
  if (QtPassSettings::isUseGit()) {
×
325
    ui->statusBar->showMessage(tr("Updating password-store"), 2000);
×
326
    QtPassSettings::getPass()->GitPush();
×
327
  }
328
}
×
329

330
/**
331
 * @brief MainWindow::getFile get the selected file path
332
 * @param index
333
 * @param forPass returns relative path without '.gpg' extension
334
 * @return path
335
 * @return
336
 */
337
auto MainWindow::getFile(const QModelIndex &index, bool forPass) -> QString {
×
338
  if (!index.isValid() ||
×
339
      !model.fileInfo(proxyModel.mapToSource(index)).isFile()) {
×
340
    return {};
341
  }
342
  QString filePath = model.filePath(proxyModel.mapToSource(index));
×
343
  if (forPass) {
×
344
    filePath = QDir(QtPassSettings::getPassStore()).relativeFilePath(filePath);
×
345
    filePath.replace(Util::endsWithGpg(), "");
×
346
  }
347
  return filePath;
348
}
349

350
/**
351
 * @brief MainWindow::on_treeView_clicked read the selected password file
352
 * @param index
353
 */
354
void MainWindow::on_treeView_clicked(const QModelIndex &index) {
×
355
  bool cleared = ui->treeView->currentIndex().flags() == Qt::NoItemFlags;
×
356
  currentDir =
357
      Util::getDir(ui->treeView->currentIndex(), false, model, proxyModel);
×
358
  // TODO(bezet): "Could not decrypt";
359
  m_qtPass->clearClippedText();
×
360
  QString file = getFile(index, true);
×
361
  ui->passwordName->setText(getFile(index, true));
×
362
  if (!file.isEmpty() && !cleared) {
×
363
    QtPassSettings::getPass()->Show(file);
×
364
  } else {
365
    clearPanel(false);
×
366
    ui->actionEdit->setEnabled(false);
×
367
    ui->actionDelete->setEnabled(true);
×
368
  }
369
}
×
370

371
/**
372
 * @brief MainWindow::on_treeView_doubleClicked when doubleclicked on
373
 * TreeViewItem, open the edit Window
374
 * @param index
375
 */
376
void MainWindow::on_treeView_doubleClicked(const QModelIndex &index) {
×
377
  QFileInfo fileOrFolder =
378
      model.fileInfo(proxyModel.mapToSource(ui->treeView->currentIndex()));
×
379

380
  if (fileOrFolder.isFile()) {
×
381
    editPassword(getFile(index, true));
×
382
  }
383
}
×
384

385
/**
386
 * @brief MainWindow::deselect clear the selection, password and copy buffer
387
 */
388
void MainWindow::deselect() {
×
389
  currentDir = "";
×
390
  m_qtPass->clearClipboard();
×
391
  ui->treeView->clearSelection();
×
392
  ui->actionEdit->setEnabled(false);
×
393
  ui->actionDelete->setEnabled(false);
×
394
  ui->passwordName->setText("");
×
395
  clearPanel(false);
×
396
}
×
397

398
void MainWindow::executeWrapperStarted() {
×
399
  clearTemplateWidgets();
×
400
  ui->textBrowser->clear();
×
401
  setUiElementsEnabled(false);
×
402
  clearPanelTimer.stop();
×
403
}
×
404

405
void MainWindow::passShowHandler(const QString &p_output) {
×
406
  QStringList templ = QtPassSettings::isUseTemplate()
×
407
                          ? QtPassSettings::getPassTemplate().split("\n")
×
408
                          : QStringList();
×
409
  bool allFields =
410
      QtPassSettings::isUseTemplate() && QtPassSettings::isTemplateAllFields();
×
411
  FileContent fileContent = FileContent::parse(p_output, templ, allFields);
×
412
  QString output = p_output;
413
  QString password = fileContent.getPassword();
×
414

415
  // set clipped text
416
  m_qtPass->setClippedText(password, p_output);
×
417

418
  // first clear the current view:
419
  clearTemplateWidgets();
×
420

421
  // show what is needed:
422
  if (QtPassSettings::isHideContent()) {
×
423
    output = "***" + tr("Content hidden") + "***";
×
424
  } else if (!QtPassSettings::isDisplayAsIs()) {
×
425
    if (!password.isEmpty()) {
×
426
      // set the password, it is hidden if needed in addToGridLayout
427
      addToGridLayout(0, tr("Password"), password);
×
428
    }
429

430
    NamedValues namedValues = fileContent.getNamedValues();
×
431
    for (int j = 0; j < namedValues.length(); ++j) {
×
432
      const NamedValue &nv = namedValues.at(j);
433
      addToGridLayout(j + 1, nv.name, nv.value);
×
434
    }
435
    if (ui->gridLayout->count() == 0) {
×
436
      ui->verticalLayoutPassword->setSpacing(0);
×
437
    } else {
438
      ui->verticalLayoutPassword->setSpacing(6);
×
439
    }
440

441
    output = fileContent.getRemainingDataForDisplay();
×
442
  }
443

444
  if (QtPassSettings::isUseAutoclearPanel()) {
×
445
    clearPanelTimer.start();
×
446
  }
447

448
  emit passShowHandlerFinished(output);
×
449
  setUiElementsEnabled(true);
×
450
}
×
451

452
void MainWindow::passOtpHandler(const QString &p_output) {
×
453
  if (!p_output.isEmpty()) {
×
454
    addToGridLayout(ui->gridLayout->count() + 1, tr("OTP Code"), p_output);
×
455
    m_qtPass->copyTextToClipboard(p_output);
×
456
    showStatusMessage(tr("OTP code copied to clipboard"));
×
457
  } else {
458
    flashText(tr("No OTP code found in this password entry"), true);
×
459
  }
460
  if (QtPassSettings::isUseAutoclearPanel()) {
×
461
    clearPanelTimer.start();
×
462
  }
463
  setUiElementsEnabled(true);
×
464
}
×
465

466
/**
467
 * @brief MainWindow::clearPanel hide the information from shoulder surfers
468
 */
469
void MainWindow::clearPanel(bool notify) {
×
470
  while (ui->gridLayout->count() > 0) {
×
471
    QLayoutItem *item = ui->gridLayout->takeAt(0);
×
472
    delete item->widget();
×
473
    delete item;
×
474
  }
475
  if (notify) {
×
476
    QString output = "***" + tr("Password and Content hidden") + "***";
×
477
    ui->textBrowser->setHtml(output);
×
478
  } else {
479
    ui->textBrowser->setHtml("");
×
480
  }
481
}
×
482

483
/**
484
 * @brief MainWindow::setUiElementsEnabled enable or disable the relevant UI
485
 * elements
486
 * @param state
487
 */
488
void MainWindow::setUiElementsEnabled(bool state) {
×
489
  ui->treeView->setEnabled(state);
×
490
  ui->lineEdit->setEnabled(state);
×
491
  ui->lineEdit->installEventFilter(this);
×
492
  ui->actionAddPassword->setEnabled(state);
×
493
  ui->actionAddFolder->setEnabled(state);
×
494
  ui->actionUsers->setEnabled(state);
×
495
  ui->actionConfig->setEnabled(state);
×
496
  // is a file selected?
497
  state &= ui->treeView->currentIndex().isValid();
×
498
  ui->actionDelete->setEnabled(state);
×
499
  ui->actionEdit->setEnabled(state);
×
500
  updateGitButtonVisibility();
×
501
  updateOtpButtonVisibility();
×
502
}
×
503

504
void MainWindow::restoreWindow() {
×
505
  QByteArray geometry = QtPassSettings::getGeometry(saveGeometry());
×
506
  restoreGeometry(geometry);
×
507
  QByteArray savestate = QtPassSettings::getSavestate(saveState());
×
508
  restoreState(savestate);
×
509
  QPoint position = QtPassSettings::getPos(pos());
×
510
  move(position);
×
511
  QSize newSize = QtPassSettings::getSize(size());
×
512
  resize(newSize);
×
513
  if (QtPassSettings::isMaximized(isMaximized())) {
×
514
    showMaximized();
×
515
  }
516

517
  if (QtPassSettings::isAlwaysOnTop()) {
×
518
    Qt::WindowFlags flags = windowFlags();
519
    setWindowFlags(flags | Qt::WindowStaysOnTopHint);
×
520
    show();
×
521
  }
522

523
  if (QtPassSettings::isUseTrayIcon() && tray == nullptr) {
×
524
    initTrayIcon();
×
525
    if (QtPassSettings::isStartMinimized()) {
×
526
      // since we are still in constructor, can't directly hide
527
      QTimer::singleShot(10, this, SLOT(hide()));
×
528
    }
529
  } else if (!QtPassSettings::isUseTrayIcon() && tray != nullptr) {
×
530
    destroyTrayIcon();
×
531
  }
532
}
×
533

534
/**
535
 * @brief MainWindow::on_configButton_clicked run Mainwindow::config
536
 */
537
void MainWindow::onConfig() { config(); }
×
538

539
/**
540
 * @brief Executes when the string in the search box changes, collapses the
541
 * TreeView
542
 * @param arg1
543
 */
544
void MainWindow::on_lineEdit_textChanged(const QString &arg1) {
×
545
  ui->statusBar->showMessage(tr("Looking for: %1").arg(arg1), 1000);
×
546
  ui->treeView->expandAll();
×
547
  clearPanel(false);
×
548
  ui->passwordName->setText("");
×
549
  ui->actionEdit->setEnabled(false);
×
550
  ui->actionDelete->setEnabled(false);
×
551
  searchTimer.start();
×
552
}
×
553

554
/**
555
 * @brief MainWindow::onTimeoutSearch Fired when search is finished or too much
556
 * time from two keypresses is elapsed
557
 */
558
void MainWindow::onTimeoutSearch() {
×
559
  QString query = ui->lineEdit->text();
×
560

561
  if (query.isEmpty()) {
×
562
    ui->treeView->collapseAll();
×
563
    deselect();
×
564
  }
565

566
  query.replace(QStringLiteral(" "), ".*");
×
567
  QRegularExpression regExp(query, QRegularExpression::CaseInsensitiveOption);
×
568
  proxyModel.setFilterRegularExpression(regExp);
×
569
  ui->treeView->setRootIndex(proxyModel.mapFromSource(
×
570
      model.setRootPath(QtPassSettings::getPassStore())));
×
571

572
  if (proxyModel.rowCount() > 0 && !query.isEmpty()) {
×
573
    selectFirstFile();
×
574
  } else {
575
    ui->actionEdit->setEnabled(false);
×
576
    ui->actionDelete->setEnabled(false);
×
577
  }
578
}
×
579

580
/**
581
 * @brief MainWindow::on_lineEdit_returnPressed get searching
582
 *
583
 * Select the first possible file in the tree
584
 */
585
void MainWindow::on_lineEdit_returnPressed() {
×
586
#ifdef QT_DEBUG
587
  dbg() << "on_lineEdit_returnPressed" << proxyModel.rowCount();
588
#endif
589

590
  if (proxyModel.rowCount() > 0) {
×
591
    selectFirstFile();
×
592
    on_treeView_clicked(ui->treeView->currentIndex());
×
593
  }
594
}
×
595

596
/**
597
 * @brief MainWindow::selectFirstFile select the first possible file in the
598
 * tree
599
 */
600
void MainWindow::selectFirstFile() {
×
601
  QModelIndex index = proxyModel.mapFromSource(
×
602
      model.setRootPath(QtPassSettings::getPassStore()));
×
603
  index = firstFile(index);
×
604
  ui->treeView->setCurrentIndex(index);
×
605
}
×
606

607
/**
608
 * @brief MainWindow::firstFile return location of first possible file
609
 * @param parentIndex
610
 * @return QModelIndex
611
 */
612
auto MainWindow::firstFile(QModelIndex parentIndex) -> QModelIndex {
×
613
  QModelIndex index = parentIndex;
×
614
  int numRows = proxyModel.rowCount(parentIndex);
×
615
  for (int row = 0; row < numRows; ++row) {
×
616
    index = proxyModel.index(row, 0, parentIndex);
×
617
    if (model.fileInfo(proxyModel.mapToSource(index)).isFile()) {
×
618
      return index;
×
619
    }
620
    if (proxyModel.hasChildren(index)) {
×
621
      return firstFile(index);
×
622
    }
623
  }
624
  return index;
×
625
}
626

627
/**
628
 * @brief MainWindow::setPassword open passworddialog
629
 * @param file which pgp file
630
 * @param isNew insert (not update)
631
 */
632
void MainWindow::setPassword(const QString &file, bool isNew) {
×
633
  PasswordDialog d(file, isNew, this);
×
634

635
  if (!d.exec()) {
×
636
    ui->treeView->setFocus();
×
637
  }
638
}
×
639

640
/**
641
 * @brief MainWindow::addPassword add a new password by showing a
642
 * number of dialogs.
643
 */
644
void MainWindow::addPassword() {
×
645
  bool ok;
646
  QString dir =
647
      Util::getDir(ui->treeView->currentIndex(), true, model, proxyModel);
×
648
  QString file =
649
      QInputDialog::getText(this, tr("New file"),
×
650
                            tr("New password file: \n(Will be placed in %1 )")
×
651
                                .arg(QtPassSettings::getPassStore() +
×
652
                                     Util::getDir(ui->treeView->currentIndex(),
×
653
                                                  true, model, proxyModel)),
654
                            QLineEdit::Normal, "", &ok);
×
655
  if (!ok || file.isEmpty()) {
×
656
    return;
657
  }
658
  file = dir + file;
×
659
  setPassword(file);
×
660
}
661

662
/**
663
 * @brief MainWindow::onDelete remove password, if you are
664
 * sure.
665
 */
666
void MainWindow::onDelete() {
×
667
  QModelIndex currentIndex = ui->treeView->currentIndex();
×
668
  if (!currentIndex.isValid()) {
669
    // This fixes https://github.com/IJHack/QtPass/issues/556
670
    // Otherwise the entire password directory would be deleted if
671
    // nothing is selected in the tree view.
672
    return;
×
673
  }
674

675
  QFileInfo fileOrFolder =
676
      model.fileInfo(proxyModel.mapToSource(ui->treeView->currentIndex()));
×
677
  QString file = "";
×
678
  bool isDir = false;
679

680
  if (fileOrFolder.isFile()) {
×
681
    file = getFile(ui->treeView->currentIndex(), true);
×
682
  } else {
683
    file = Util::getDir(ui->treeView->currentIndex(), true, model, proxyModel);
×
684
    isDir = true;
685
  }
686

687
  QString dirMessage = tr(" and the whole content?");
688
  if (isDir) {
×
689
    QDirIterator it(model.rootPath() + QDir::separator() + file,
×
690
                    QDirIterator::Subdirectories);
×
691
    bool okDir = true;
692
    while (it.hasNext() && okDir) {
×
693
      it.next();
×
694
      if (QFileInfo(it.filePath()).isFile()) {
×
695
        if (QFileInfo(it.filePath()).suffix() != "gpg") {
×
696
          okDir = false;
697
          dirMessage = tr(" and the whole content? <br><strong>Attention: "
×
698
                          "there are unexpected files in the given folder, "
699
                          "check them before continue.</strong>");
700
        }
701
      }
702
    }
703
  }
×
704

705
  if (QMessageBox::question(
×
706
          this, isDir ? tr("Delete folder?") : tr("Delete password?"),
×
707
          tr("Are you sure you want to delete %1%2?")
×
708
              .arg(QDir::separator() + file, isDir ? dirMessage : "?"),
×
709
          QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) {
710
    return;
711
  }
712

713
  QtPassSettings::getPass()->Remove(file, isDir);
×
714
}
×
715

716
/**
717
 * @brief MainWindow::onOTP try and generate (selected) OTP code.
718
 */
719
void MainWindow::onOtp() {
×
720
  QString file = getFile(ui->treeView->currentIndex(), true);
×
721
  if (!file.isEmpty()) {
×
722
    if (QtPassSettings::isUseOtp()) {
×
723
      setUiElementsEnabled(false);
×
724
      QtPassSettings::getPass()->OtpGenerate(file);
×
725
    }
726
  } else {
727
    flashText(tr("No password selected for OTP generation"), true);
×
728
  }
729
}
×
730

731
/**
732
 * @brief MainWindow::onEdit try and edit (selected) password.
733
 */
734
void MainWindow::onEdit() {
×
735
  QString file = getFile(ui->treeView->currentIndex(), true);
×
736
  editPassword(file);
×
737
}
×
738

739
/**
740
 * @brief MainWindow::userDialog see MainWindow::onUsers()
741
 * @param dir folder to edit users for.
742
 */
743
void MainWindow::userDialog(const QString &dir) {
×
744
  if (!dir.isEmpty()) {
×
745
    currentDir = dir;
×
746
  }
747
  onUsers();
×
748
}
×
749

750
/**
751
 * @brief MainWindow::onUsers edit users for the current
752
 * folder,
753
 * gets lists and opens UserDialog.
754
 */
755
void MainWindow::onUsers() {
×
756
  QString dir =
757
      currentDir.isEmpty()
758
          ? Util::getDir(ui->treeView->currentIndex(), false, model, proxyModel)
×
759
          : currentDir;
×
760

761
  UsersDialog d(dir, this);
×
762
  if (!d.exec()) {
×
763
    ui->treeView->setFocus();
×
764
  }
765
}
×
766

767
/**
768
 * @brief MainWindow::messageAvailable we have some text/message/search to do.
769
 * @param message
770
 */
771
void MainWindow::messageAvailable(const QString &message) {
×
772
  if (message.isEmpty()) {
×
773
    focusInput();
×
774
  } else {
775
    ui->treeView->expandAll();
×
776
    ui->lineEdit->setText(message);
×
777
    on_lineEdit_returnPressed();
×
778
  }
779
  show();
×
780
  raise();
×
781
}
×
782

783
/**
784
 * @brief MainWindow::generateKeyPair internal gpg keypair generator . .
785
 * @param batch
786
 * @param keygenWindow
787
 */
788
void MainWindow::generateKeyPair(const QString &batch, QDialog *keygenWindow) {
×
789
  keygen = keygenWindow;
×
790
  emit generateGPGKeyPair(batch);
×
791
}
×
792

793
/**
794
 * @brief MainWindow::updateProfileBox update the list of profiles, optionally
795
 * select a more appropriate one to view too
796
 */
797
void MainWindow::updateProfileBox() {
×
798
  QHash<QString, QHash<QString, QString>> profiles =
799
      QtPassSettings::getProfiles();
×
800

801
  if (profiles.isEmpty()) {
802
    ui->profileWidget->hide();
×
803
  } else {
804
    ui->profileWidget->show();
×
805
    ui->profileBox->setEnabled(profiles.size() > 1);
×
806
    ui->profileBox->clear();
×
807
    QHashIterator<QString, QHash<QString, QString>> i(profiles);
×
808
    while (i.hasNext()) {
×
809
      i.next();
810
      if (!i.key().isEmpty()) {
×
811
        ui->profileBox->addItem(i.key());
×
812
      }
813
    }
814
    ui->profileBox->model()->sort(0);
×
815
  }
816
  int index = ui->profileBox->findText(QtPassSettings::getProfile());
×
817
  if (index != -1) { //  -1 for not found
×
818
    ui->profileBox->setCurrentIndex(index);
×
819
  }
820
}
×
821

822
/**
823
 * @brief MainWindow::on_profileBox_currentIndexChanged make sure we show the
824
 * correct "profile"
825
 * @param name
826
 */
827
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
828
void MainWindow::on_profileBox_currentIndexChanged(QString name) {
829
#else
830
void MainWindow::on_profileBox_currentTextChanged(const QString &name) {
×
831
#endif
832
  if (m_qtPass->isFreshStart() || name == QtPassSettings::getProfile()) {
×
833
    return;
834
  }
835

836
  ui->lineEdit->clear();
×
837

838
  QtPassSettings::setProfile(name);
×
839

840
  QtPassSettings::setPassStore(
×
841
      QtPassSettings::getProfiles().value(name).value("path"));
×
842
  QtPassSettings::setPassSigningKey(
×
843
      QtPassSettings::getProfiles().value(name).value("signingKey"));
×
844
  ui->statusBar->showMessage(tr("Profile changed to %1").arg(name), 2000);
×
845

846
  QtPassSettings::getPass()->updateEnv();
×
847

848
  ui->treeView->selectionModel()->clear();
×
849
  ui->treeView->setRootIndex(proxyModel.mapFromSource(
×
850
      model.setRootPath(QtPassSettings::getPassStore())));
×
851

852
  ui->actionEdit->setEnabled(false);
×
853
  ui->actionDelete->setEnabled(false);
×
854
}
855

856
/**
857
 * @brief MainWindow::initTrayIcon show a nice tray icon on systems that
858
 * support
859
 * it
860
 */
861
void MainWindow::initTrayIcon() {
×
862
  this->tray = new TrayIcon(this);
×
863
  // Setup tray icon
864

865
  if (tray == nullptr) {
866
#ifdef QT_DEBUG
867
    dbg() << "Allocating tray icon failed.";
868
#endif
869
  }
870

871
  if (!tray->getIsAllocated()) {
×
872
    destroyTrayIcon();
×
873
  }
874
}
×
875

876
/**
877
 * @brief MainWindow::destroyTrayIcon remove that pesky tray icon
878
 */
879
void MainWindow::destroyTrayIcon() {
×
880
  delete this->tray;
×
881
  tray = nullptr;
×
882
}
×
883

884
/**
885
 * @brief MainWindow::closeEvent hide or quit
886
 * @param event
887
 */
888
void MainWindow::closeEvent(QCloseEvent *event) {
×
889
  if (QtPassSettings::isHideOnClose()) {
×
890
    this->hide();
×
891
    event->ignore();
892
  } else {
893
    m_qtPass->clearClipboard();
×
894

895
    QtPassSettings::setGeometry(saveGeometry());
×
896
    QtPassSettings::setSavestate(saveState());
×
897
    QtPassSettings::setMaximized(isMaximized());
×
898
    if (!isMaximized()) {
×
899
      QtPassSettings::setPos(pos());
×
900
      QtPassSettings::setSize(size());
×
901
    }
902
    event->accept();
903
  }
904
}
×
905

906
/**
907
 * @brief MainWindow::eventFilter filter out some events and focus the
908
 * treeview
909
 * @param obj
910
 * @param event
911
 * @return
912
 */
913
auto MainWindow::eventFilter(QObject *obj, QEvent *event) -> bool {
×
914
  if (obj == ui->lineEdit && event->type() == QEvent::KeyPress) {
×
915
    auto *key = dynamic_cast<QKeyEvent *>(event);
×
916
    if (key != nullptr && key->key() == Qt::Key_Down) {
×
917
      ui->treeView->setFocus();
×
918
    }
919
  }
920
  return QObject::eventFilter(obj, event);
×
921
}
922

923
/**
924
 * @brief MainWindow::keyPressEvent did anyone press return, enter or escape?
925
 * @param event
926
 */
927
void MainWindow::keyPressEvent(QKeyEvent *event) {
×
928
  switch (event->key()) {
×
929
  case Qt::Key_Delete:
×
930
    onDelete();
×
931
    break;
×
932
  case Qt::Key_Return:
×
933
  case Qt::Key_Enter:
934
    if (proxyModel.rowCount() > 0) {
×
935
      on_treeView_clicked(ui->treeView->currentIndex());
×
936
    }
937
    break;
938
  case Qt::Key_Escape:
×
939
    ui->lineEdit->clear();
×
940
    break;
×
941
  default:
942
    break;
943
  }
944
}
×
945

946
/**
947
 * @brief MainWindow::showContextMenu show us the (file or folder) context
948
 * menu
949
 * @param pos
950
 */
951
void MainWindow::showContextMenu(const QPoint &pos) {
×
952
  QModelIndex index = ui->treeView->indexAt(pos);
×
953
  bool selected = true;
954
  if (!index.isValid()) {
955
    ui->treeView->clearSelection();
×
956
    ui->actionDelete->setEnabled(false);
×
957
    ui->actionEdit->setEnabled(false);
×
958
    currentDir = "";
×
959
    selected = false;
960
  }
961

962
  ui->treeView->setCurrentIndex(index);
×
963

964
  QPoint globalPos = ui->treeView->viewport()->mapToGlobal(pos);
×
965

966
  QFileInfo fileOrFolder =
967
      model.fileInfo(proxyModel.mapToSource(ui->treeView->currentIndex()));
×
968

969
  QMenu contextMenu;
×
970
  if (!selected || fileOrFolder.isDir()) {
×
971
    QAction *openFolder =
972
        contextMenu.addAction(tr("Open folder with file manager"));
×
973
    QAction *addFolder = contextMenu.addAction(tr("Add folder"));
×
974
    QAction *addPassword = contextMenu.addAction(tr("Add password"));
×
975
    QAction *users = contextMenu.addAction(tr("Users"));
×
976
    connect(openFolder, &QAction::triggered, this, &MainWindow::openFolder);
×
977
    connect(addFolder, &QAction::triggered, this, &MainWindow::addFolder);
×
978
    connect(addPassword, &QAction::triggered, this, &MainWindow::addPassword);
×
979
    connect(users, &QAction::triggered, this, &MainWindow::onUsers);
×
980
  } else if (fileOrFolder.isFile()) {
×
981
    QAction *edit = contextMenu.addAction(tr("Edit"));
×
982
    connect(edit, &QAction::triggered, this, &MainWindow::onEdit);
×
983
  }
984
  if (selected) {
×
985
    // if (useClipboard != CLIPBOARD_NEVER) {
986
    // contextMenu.addSeparator();
987
    // QAction* copyItem = contextMenu.addAction(tr("Copy Password"));
988
    // if (getClippedPassword().length() == 0) copyItem->setEnabled(false);
989
    // connect(copyItem, SIGNAL(triggered()), this,
990
    // SLOT(copyPasswordToClipboard()));
991
    // }
992
    contextMenu.addSeparator();
×
993
    if (fileOrFolder.isDir()) {
×
994
      QAction *renameFolder = contextMenu.addAction(tr("Rename folder"));
×
995
      connect(renameFolder, &QAction::triggered, this,
×
996
              &MainWindow::renameFolder);
×
997
    } else if (fileOrFolder.isFile()) {
×
998
      QAction *renamePassword = contextMenu.addAction(tr("Rename password"));
×
999
      connect(renamePassword, &QAction::triggered, this,
×
1000
              &MainWindow::renamePassword);
×
1001
    }
1002
    QAction *deleteItem = contextMenu.addAction(tr("Delete"));
×
1003
    connect(deleteItem, &QAction::triggered, this, &MainWindow::onDelete);
×
1004
  }
1005
  contextMenu.exec(globalPos);
×
1006
}
×
1007

1008
/**
1009
 * @brief MainWindow::showBrowserContextMenu show us the context menu in
1010
 * password window
1011
 * @param pos
1012
 */
1013
void MainWindow::showBrowserContextMenu(const QPoint &pos) {
×
1014
  QMenu *contextMenu = ui->textBrowser->createStandardContextMenu(pos);
×
1015
  QPoint globalPos = ui->textBrowser->viewport()->mapToGlobal(pos);
×
1016

1017
  contextMenu->exec(globalPos);
×
1018
  delete contextMenu;
×
1019
}
×
1020

1021
/**
1022
 * @brief MainWindow::openFolder open the folder in the default file manager
1023
 */
1024
void MainWindow::openFolder() {
×
1025
  QString dir =
1026
      Util::getDir(ui->treeView->currentIndex(), false, model, proxyModel);
×
1027

1028
  QString path = QDir::toNativeSeparators(dir);
×
1029
  QDesktopServices::openUrl(QUrl::fromLocalFile(path));
×
1030
}
×
1031

1032
/**
1033
 * @brief MainWindow::addFolder add a new folder to store passwords in
1034
 */
1035
void MainWindow::addFolder() {
×
1036
  bool ok;
1037
  QString dir =
1038
      Util::getDir(ui->treeView->currentIndex(), false, model, proxyModel);
×
1039
  QString newdir =
1040
      QInputDialog::getText(this, tr("New file"),
×
1041
                            tr("New Folder: \n(Will be placed in %1 )")
×
1042
                                .arg(QtPassSettings::getPassStore() +
×
1043
                                     Util::getDir(ui->treeView->currentIndex(),
×
1044
                                                  true, model, proxyModel)),
1045
                            QLineEdit::Normal, "", &ok);
×
1046
  if (!ok || newdir.isEmpty()) {
×
1047
    return;
1048
  }
1049
  newdir.prepend(dir);
1050
  // dbg()<< newdir;
1051
  if (!QDir().mkdir(newdir)) {
×
1052
    QMessageBox::warning(this, tr("Error"),
×
1053
                         tr("Failed to create folder: %1").arg(newdir));
×
1054
    return;
×
1055
  }
1056
  if (QtPassSettings::isAddGPGId(true)) {
×
1057
    QString gpgIdFile = newdir + "/.gpg-id";
×
1058
    QFile gpgId(gpgIdFile);
×
1059
    if (!gpgId.open(QIODevice::WriteOnly)) {
×
1060
      QMessageBox::warning(
×
1061
          this, tr("Error"),
×
1062
          tr("Failed to create .gpg-id file in: %1").arg(newdir));
×
1063
      return;
1064
    }
1065
    QList<UserInfo> users = QtPassSettings::getPass()->listKeys("", true);
×
1066
    for (const UserInfo &user : users) {
×
1067
      if (user.enabled) {
×
1068
        gpgId.write((user.key_id + "\n").toUtf8());
×
1069
      }
1070
    }
1071
    gpgId.close();
×
1072
  }
×
1073
}
1074

1075
/**
1076
 * @brief MainWindow::renameFolder rename an existing folder
1077
 */
1078
void MainWindow::renameFolder() {
×
1079
  bool ok;
1080
  QString srcDir = QDir::cleanPath(
1081
      Util::getDir(ui->treeView->currentIndex(), false, model, proxyModel));
×
1082
  QString srcDirName = QDir(srcDir).dirName();
×
1083
  QString newName =
1084
      QInputDialog::getText(this, tr("Rename file"), tr("Rename Folder To: "),
×
1085
                            QLineEdit::Normal, srcDirName, &ok);
×
1086
  if (!ok || newName.isEmpty()) {
×
1087
    return;
1088
  }
1089
  QString destDir = srcDir;
1090
  destDir.replace(srcDir.lastIndexOf(srcDirName), srcDirName.length(), newName);
×
1091
  QtPassSettings::getPass()->Move(srcDir, destDir);
×
1092
}
1093

1094
/**
1095
 * @brief MainWindow::editPassword read password and open edit window via
1096
 * MainWindow::onEdit()
1097
 */
1098
void MainWindow::editPassword(const QString &file) {
×
1099
  if (!file.isEmpty()) {
×
1100
    if (QtPassSettings::isUseGit() && QtPassSettings::isAutoPull()) {
×
1101
      onUpdate(true);
×
1102
    }
1103
    setPassword(file, false);
×
1104
  }
1105
}
×
1106

1107
/**
1108
 * @brief MainWindow::renamePassword rename an existing password
1109
 */
1110
void MainWindow::renamePassword() {
×
1111
  bool ok;
1112
  QString file = getFile(ui->treeView->currentIndex(), false);
×
1113
  QString filePath = QFileInfo(file).path();
×
1114
  QString fileName = QFileInfo(file).fileName();
×
1115
  if (fileName.endsWith(".gpg", Qt::CaseInsensitive)) {
×
1116
    fileName.chop(4);
×
1117
  }
1118

1119
  QString newName =
1120
      QInputDialog::getText(this, tr("Rename file"), tr("Rename File To: "),
×
1121
                            QLineEdit::Normal, fileName, &ok);
×
1122
  if (!ok || newName.isEmpty()) {
×
1123
    return;
1124
  }
1125
  QString newFile = QDir(filePath).filePath(newName);
×
1126
  QtPassSettings::getPass()->Move(file, newFile);
×
1127
}
1128

1129
/**
1130
 * @brief MainWindow::clearTemplateWidgets empty the template widget fields in
1131
 * the UI
1132
 */
1133
void MainWindow::clearTemplateWidgets() {
×
1134
  while (ui->gridLayout->count() > 0) {
×
1135
    QLayoutItem *item = ui->gridLayout->takeAt(0);
×
1136
    delete item->widget();
×
1137
    delete item;
×
1138
  }
1139
  ui->verticalLayoutPassword->setSpacing(0);
×
1140
}
×
1141

1142
void MainWindow::copyPasswordFromTreeview() {
×
1143
  QFileInfo fileOrFolder =
1144
      model.fileInfo(proxyModel.mapToSource(ui->treeView->currentIndex()));
×
1145

1146
  if (fileOrFolder.isFile()) {
×
1147
    QString file = getFile(ui->treeView->currentIndex(), true);
×
1148
    connect(QtPassSettings::getPass(), &Pass::finishedShow, this,
×
1149
            &MainWindow::passwordFromFileToClipboard);
×
1150
    QtPassSettings::getPass()->Show(file);
×
1151
  }
1152
}
×
1153

1154
void MainWindow::passwordFromFileToClipboard(const QString &text) {
×
1155
  QStringList tokens = text.split('\n');
×
1156
  m_qtPass->copyTextToClipboard(tokens[0]);
×
1157
}
×
1158

1159
/**
1160
 * @brief MainWindow::addToGridLayout add a field to the template grid
1161
 * @param position
1162
 * @param field
1163
 * @param value
1164
 */
1165
void MainWindow::addToGridLayout(int position, const QString &field,
×
1166
                                 const QString &value) {
1167
  QString trimmedField = field.trimmed();
1168
  QString trimmedValue = value.trimmed();
1169

1170
  // Combine the Copy button and the line edit in one widget
1171
  auto *frame = new QFrame();
×
1172
  QLayout *ly = new QHBoxLayout();
×
1173
  ly->setContentsMargins(5, 2, 2, 2);
×
1174
  ly->setSpacing(0);
×
1175
  frame->setLayout(ly);
×
1176
  if (QtPassSettings::getClipBoardType() != Enums::CLIPBOARD_NEVER) {
×
1177
    auto *fieldLabel = new QPushButtonWithClipboard(trimmedValue, this);
×
1178
    connect(fieldLabel, &QPushButtonWithClipboard::clicked, m_qtPass,
×
1179
            &QtPass::copyTextToClipboard);
×
1180

1181
    fieldLabel->setStyleSheet(
×
1182
        "border-style: none ; background: transparent; padding: 0; margin: 0;");
1183
    frame->layout()->addWidget(fieldLabel);
×
1184
  }
1185

1186
  if (QtPassSettings::isUseQrencode()) {
×
1187
    auto *qrbutton = new QPushButtonAsQRCode(trimmedValue, this);
×
1188
    connect(qrbutton, &QPushButtonAsQRCode::clicked, m_qtPass,
×
1189
            &QtPass::showTextAsQRCode);
×
1190
    qrbutton->setStyleSheet(
×
1191
        "border-style: none ; background: transparent; padding: 0; margin: 0;");
1192
    frame->layout()->addWidget(qrbutton);
×
1193
  }
1194

1195
  // set the echo mode to password, if the field is "password"
1196
  const QString lineStyle =
1197
      QtPassSettings::isUseMonospace()
×
1198
          ? "border-style: none; background: transparent; font-family: "
1199
            "monospace;"
1200
          : "border-style: none; background: transparent;";
×
1201

1202
  if (QtPassSettings::isHidePassword() && trimmedField == tr("Password")) {
×
1203
    auto *line = new QLineEdit();
×
1204
    line->setObjectName(trimmedField);
×
1205
    line->setText(trimmedValue);
×
1206
    line->setReadOnly(true);
×
1207
    line->setStyleSheet(lineStyle);
×
1208
    line->setContentsMargins(0, 0, 0, 0);
×
1209
    line->setEchoMode(QLineEdit::Password);
×
1210
    auto *showButton = new QPushButtonShowPassword(line, this);
×
1211
    showButton->setStyleSheet(
×
1212
        "border-style: none ; background: transparent; padding: 0; margin: 0;");
1213
    showButton->setContentsMargins(0, 0, 0, 0);
×
1214
    frame->layout()->addWidget(showButton);
×
1215
    frame->layout()->addWidget(line);
×
1216
  } else {
1217
    auto *line = new QTextBrowser();
×
1218
    line->setOpenExternalLinks(true);
×
1219
    line->setOpenLinks(true);
×
1220
    line->setMaximumHeight(26);
×
1221
    line->setMinimumHeight(26);
×
1222
    line->setSizePolicy(
×
1223
        QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum));
1224
    line->setObjectName(trimmedField);
×
1225
    trimmedValue.replace(Util::protocolRegex(), R"(<a href="\1">\1</a>)");
×
1226
    line->setText(trimmedValue);
×
1227
    line->setReadOnly(true);
×
1228
    line->setStyleSheet(lineStyle);
×
1229
    line->setContentsMargins(0, 0, 0, 0);
×
1230
    frame->layout()->addWidget(line);
×
1231
  }
1232

1233
  frame->setStyleSheet(
×
1234
      ".QFrame{border: 1px solid lightgrey; border-radius: 5px;}");
1235

1236
  // set into the layout
1237
  ui->gridLayout->addWidget(new QLabel(trimmedField), position, 0);
×
1238
  ui->gridLayout->addWidget(frame, position, 1);
×
1239
}
×
1240

1241
/**
1242
 * @brief Displays message in status bar
1243
 *
1244
 * @param msg     text to be displayed
1245
 * @param timeout time for which msg shall be visible
1246
 */
1247
void MainWindow::showStatusMessage(const QString &msg, int timeout) {
×
1248
  ui->statusBar->showMessage(msg, timeout);
×
1249
}
×
1250

1251
/**
1252
 * @brief MainWindow::startReencryptPath disable ui elements and treeview
1253
 */
1254
void MainWindow::startReencryptPath() {
×
1255
  setUiElementsEnabled(false);
×
1256
  ui->treeView->setDisabled(true);
×
1257
}
×
1258

1259
/**
1260
 * @brief MainWindow::endReencryptPath re-enable ui elements
1261
 */
1262
void MainWindow::endReencryptPath() { setUiElementsEnabled(true); }
×
1263

1264
void MainWindow::updateGitButtonVisibility() {
×
1265
  if (!QtPassSettings::isUseGit() ||
×
1266
      (QtPassSettings::getGitExecutable().isEmpty() &&
×
1267
       QtPassSettings::getPassExecutable().isEmpty())) {
×
1268
    enableGitButtons(false);
×
1269
  } else {
1270
    enableGitButtons(true);
×
1271
  }
1272
}
×
1273

1274
void MainWindow::updateOtpButtonVisibility() {
×
1275
#if defined(Q_OS_WIN) || defined(__APPLE__)
1276
  ui->actionOtp->setVisible(false);
1277
#endif
1278
  if (!QtPassSettings::isUseOtp()) {
×
1279
    ui->actionOtp->setEnabled(false);
×
1280
  } else {
1281
    ui->actionOtp->setEnabled(true);
×
1282
  }
1283
}
×
1284

1285
void MainWindow::enableGitButtons(const bool &state) {
×
1286
  // Following GNOME guidelines is preferable disable buttons instead of hide
1287
  ui->actionPush->setEnabled(state);
×
1288
  ui->actionUpdate->setEnabled(state);
×
1289
}
×
1290

1291
/**
1292
 * @brief MainWindow::critical critical message popup wrapper.
1293
 * @param title
1294
 * @param msg
1295
 */
1296
void MainWindow::critical(const QString &title, const QString &msg) {
×
1297
  QMessageBox::critical(this, title, msg);
×
1298
}
×
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