LCOV - code coverage report
Current view: top level - src - mainwindow.cpp (source / functions) Coverage Total Hit
Test: .lcov.total Lines: 0.0 % 684 0
Test Date: 2026-03-23 21:55:57 Functions: 0.0 % 62 0

            Line data    Source code
       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            0 : MainWindow::MainWindow(const QString &searchText, QWidget *parent)
      41            0 :     : QMainWindow(parent), ui(new Ui::MainWindow), keygen(nullptr),
      42            0 :       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            0 :   ui->setupUi(this);
      49              : 
      50            0 :   m_qtPass = new QtPass(this);
      51              : 
      52              :   // register shortcut ctrl/cmd + Q to close the main window
      53            0 :   new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_Q), this, SLOT(close()));
      54              :   // register shortcut ctrl/cmd + C to copy the currently selected password
      55            0 :   new QShortcut(QKeySequence(QKeySequence::StandardKey::Copy), this,
      56            0 :                 SLOT(copyPasswordFromTreeview()));
      57              : 
      58            0 :   model.setNameFilters(QStringList() << "*.gpg");
      59            0 :   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            0 :   QString passStore = QtPassSettings::getPassStore(Util::findPasswordStore());
      69              : 
      70            0 :   QModelIndex rootDir = model.setRootPath(passStore);
      71            0 :   model.fetchMore(rootDir);
      72              : 
      73            0 :   proxyModel.setModelAndStore(&model, passStore);
      74            0 :   selectionModel.reset(new QItemSelectionModel(&proxyModel));
      75              : 
      76            0 :   ui->treeView->setModel(&proxyModel);
      77            0 :   ui->treeView->setRootIndex(proxyModel.mapFromSource(rootDir));
      78            0 :   ui->treeView->setColumnHidden(1, true);
      79            0 :   ui->treeView->setColumnHidden(2, true);
      80            0 :   ui->treeView->setColumnHidden(3, true);
      81            0 :   ui->treeView->setHeaderHidden(true);
      82            0 :   ui->treeView->setIndentation(15);
      83            0 :   ui->treeView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
      84            0 :   ui->treeView->setContextMenuPolicy(Qt::CustomContextMenu);
      85            0 :   ui->treeView->header()->setSectionResizeMode(0, QHeaderView::Stretch);
      86            0 :   ui->treeView->sortByColumn(0, Qt::AscendingOrder);
      87            0 :   connect(ui->treeView, &QWidget::customContextMenuRequested, this,
      88            0 :           &MainWindow::showContextMenu);
      89            0 :   connect(ui->treeView, &DeselectableTreeView::emptyClicked, this,
      90            0 :           &MainWindow::deselect);
      91              : 
      92            0 :   if (QtPassSettings::isUseMonospace()) {
      93            0 :     QFont monospace("Monospace");
      94            0 :     monospace.setStyleHint(QFont::Monospace);
      95            0 :     ui->textBrowser->setFont(monospace);
      96            0 :   }
      97            0 :   if (QtPassSettings::isNoLineWrapping()) {
      98            0 :     ui->textBrowser->setLineWrapMode(QTextBrowser::NoWrap);
      99              :   }
     100            0 :   ui->textBrowser->setOpenExternalLinks(true);
     101            0 :   ui->textBrowser->setContextMenuPolicy(Qt::CustomContextMenu);
     102            0 :   connect(ui->textBrowser, &QWidget::customContextMenuRequested, this,
     103            0 :           &MainWindow::showBrowserContextMenu);
     104              : 
     105            0 :   updateProfileBox();
     106              : 
     107            0 :   QtPassSettings::getPass()->updateEnv();
     108            0 :   clearPanelTimer.setInterval(MS_PER_SECOND *
     109            0 :                               QtPassSettings::getAutoclearPanelSeconds());
     110            0 :   clearPanelTimer.setSingleShot(true);
     111            0 :   connect(&clearPanelTimer, &QTimer::timeout, this,
     112            0 :           [this]() -> void { clearPanel(); });
     113              : 
     114            0 :   searchTimer.setInterval(350);
     115            0 :   searchTimer.setSingleShot(true);
     116              : 
     117            0 :   connect(&searchTimer, &QTimer::timeout, this, &MainWindow::onTimeoutSearch);
     118              : 
     119            0 :   initToolBarButtons();
     120            0 :   initStatusBar();
     121              : 
     122            0 :   ui->lineEdit->setClearButtonEnabled(true);
     123              : 
     124            0 :   setUiElementsEnabled(true);
     125              : 
     126              :   QTimer::singleShot(10, this, SLOT(focusInput()));
     127              : 
     128            0 :   ui->lineEdit->setText(searchText);
     129              : 
     130            0 :   if (!m_qtPass->init()) {
     131              :     // no working config so this should just quit
     132            0 :     QApplication::quit();
     133              :   }
     134            0 : }
     135              : 
     136            0 : 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            0 : void MainWindow::focusInput() {
     145            0 :   ui->lineEdit->selectAll();
     146            0 :   ui->lineEdit->setFocus();
     147            0 : }
     148              : 
     149              : /**
     150              :  * @brief MainWindow::changeEvent sets focus to the search box
     151              :  * @param event
     152              :  */
     153            0 : void MainWindow::changeEvent(QEvent *event) {
     154            0 :   QWidget::changeEvent(event);
     155            0 :   if (event->type() == QEvent::ActivationChange) {
     156            0 :     if (isActiveWindow()) {
     157            0 :       focusInput();
     158              :     }
     159              :   }
     160            0 : }
     161              : 
     162              : /**
     163              :  * @brief MainWindow::initToolBarButtons init main ToolBar and connect actions
     164              :  */
     165            0 : void MainWindow::initToolBarButtons() {
     166            0 :   connect(ui->actionAddPassword, &QAction::triggered, this,
     167            0 :           &MainWindow::addPassword);
     168            0 :   connect(ui->actionAddFolder, &QAction::triggered, this,
     169            0 :           &MainWindow::addFolder);
     170            0 :   connect(ui->actionEdit, &QAction::triggered, this, &MainWindow::onEdit);
     171            0 :   connect(ui->actionDelete, &QAction::triggered, this, &MainWindow::onDelete);
     172            0 :   connect(ui->actionPush, &QAction::triggered, this, &MainWindow::onPush);
     173            0 :   connect(ui->actionUpdate, &QAction::triggered, this, &MainWindow::onUpdate);
     174            0 :   connect(ui->actionUsers, &QAction::triggered, this, &MainWindow::onUsers);
     175            0 :   connect(ui->actionConfig, &QAction::triggered, this, &MainWindow::onConfig);
     176            0 :   connect(ui->actionOtp, &QAction::triggered, this, &MainWindow::onOtp);
     177              : 
     178            0 :   ui->actionAddPassword->setIcon(
     179            0 :       QIcon::fromTheme("document-new", QIcon(":/icons/document-new.svg")));
     180            0 :   ui->actionAddFolder->setIcon(
     181            0 :       QIcon::fromTheme("folder-new", QIcon(":/icons/folder-new.svg")));
     182            0 :   ui->actionEdit->setIcon(QIcon::fromTheme(
     183            0 :       "document-properties", QIcon(":/icons/document-properties.svg")));
     184            0 :   ui->actionDelete->setIcon(
     185            0 :       QIcon::fromTheme("edit-delete", QIcon(":/icons/edit-delete.svg")));
     186            0 :   ui->actionPush->setIcon(
     187            0 :       QIcon::fromTheme("go-up", QIcon(":/icons/go-top.svg")));
     188            0 :   ui->actionUpdate->setIcon(
     189            0 :       QIcon::fromTheme("go-down", QIcon(":/icons/go-bottom.svg")));
     190            0 :   ui->actionUsers->setIcon(QIcon::fromTheme(
     191            0 :       "x-office-address-book", QIcon(":/icons/x-office-address-book.svg")));
     192            0 :   ui->actionConfig->setIcon(QIcon::fromTheme(
     193            0 :       "applications-system", QIcon(":/icons/applications-system.svg")));
     194            0 : }
     195              : 
     196              : /**
     197              :  * @brief MainWindow::initStatusBar init statusBar with default message and logo
     198              :  */
     199            0 : void MainWindow::initStatusBar() {
     200            0 :   ui->statusBar->showMessage(tr("Welcome to QtPass %1").arg(VERSION), 2000);
     201              : 
     202            0 :   QPixmap logo = QPixmap::fromImage(QImage(":/artwork/icon.svg"))
     203            0 :                      .scaledToHeight(statusBar()->height());
     204            0 :   auto *logoApp = new QLabel(statusBar());
     205            0 :   logoApp->setPixmap(logo);
     206            0 :   statusBar()->addPermanentWidget(logoApp);
     207            0 : }
     208              : 
     209            0 : auto MainWindow::getCurrentTreeViewIndex() -> QModelIndex {
     210            0 :   return ui->treeView->currentIndex();
     211              : }
     212              : 
     213            0 : void MainWindow::cleanKeygenDialog() {
     214            0 :   this->keygen->close();
     215            0 :   this->keygen = nullptr;
     216            0 : }
     217              : 
     218            0 : void MainWindow::flashText(const QString &text, const bool isError,
     219              :                            const bool isHtml) {
     220            0 :   if (isError) {
     221            0 :     ui->textBrowser->setTextColor(Qt::red);
     222              :   }
     223              : 
     224            0 :   if (isHtml) {
     225              :     QString _text = text;
     226            0 :     if (!ui->textBrowser->toPlainText().isEmpty()) {
     227            0 :       _text = ui->textBrowser->toHtml() + _text;
     228              :     }
     229            0 :     ui->textBrowser->setHtml(_text);
     230              :   } else {
     231            0 :     ui->textBrowser->setText(text);
     232            0 :     ui->textBrowser->setTextColor(Qt::black);
     233              :   }
     234            0 : }
     235              : 
     236              : /**
     237              :  * @brief MainWindow::config pops up the configuration screen and handles all
     238              :  * inter-window communication
     239              :  */
     240            0 : void MainWindow::config() {
     241            0 :   QScopedPointer<ConfigDialog> d(new ConfigDialog(this));
     242            0 :   d->setModal(true);
     243              :   // Automatically default to pass if it's available
     244            0 :   if (m_qtPass->isFreshStart() &&
     245            0 :       QFile(QtPassSettings::getPassExecutable()).exists()) {
     246            0 :     QtPassSettings::setUsePass(true);
     247              :   }
     248              : 
     249            0 :   if (m_qtPass->isFreshStart()) {
     250            0 :     d->wizard(); //  does shit
     251              :   }
     252            0 :   if (d->exec()) {
     253            0 :     if (d->result() == QDialog::Accepted) {
     254              :       // Update the textBrowser font
     255            0 :       if (QtPassSettings::isUseMonospace()) {
     256            0 :         QFont monospace("Monospace");
     257            0 :         monospace.setStyleHint(QFont::Monospace);
     258            0 :         ui->textBrowser->setFont(monospace);
     259            0 :       } else {
     260            0 :         ui->textBrowser->setFont(QFont());
     261              :       }
     262              :       // Update the textBrowser line wrap mode
     263            0 :       if (QtPassSettings::isNoLineWrapping()) {
     264            0 :         ui->textBrowser->setLineWrapMode(QTextBrowser::NoWrap);
     265              :       } else {
     266            0 :         ui->textBrowser->setLineWrapMode(QTextBrowser::WidgetWidth);
     267              :       }
     268              : 
     269            0 :       if (QtPassSettings::isAlwaysOnTop()) {
     270              :         Qt::WindowFlags flags = windowFlags();
     271            0 :         this->setWindowFlags(flags | Qt::WindowStaysOnTopHint);
     272              :       } else {
     273            0 :         this->setWindowFlags(Qt::Window);
     274              :       }
     275            0 :       this->show();
     276              : 
     277            0 :       updateProfileBox();
     278            0 :       ui->treeView->setRootIndex(proxyModel.mapFromSource(
     279            0 :           model.setRootPath(QtPassSettings::getPassStore())));
     280              : 
     281            0 :       if (m_qtPass->isFreshStart() && Util::checkConfig()) {
     282            0 :         config();
     283              :       }
     284            0 :       QtPassSettings::getPass()->updateEnv();
     285            0 :       clearPanelTimer.setInterval(MS_PER_SECOND *
     286            0 :                                   QtPassSettings::getAutoclearPanelSeconds());
     287            0 :       m_qtPass->setClipboardTimer();
     288              : 
     289            0 :       updateGitButtonVisibility();
     290            0 :       updateOtpButtonVisibility();
     291            0 :       if (QtPassSettings::isUseTrayIcon() && tray == nullptr) {
     292            0 :         initTrayIcon();
     293            0 :       } else if (!QtPassSettings::isUseTrayIcon() && tray != nullptr) {
     294            0 :         destroyTrayIcon();
     295              :       }
     296              :     }
     297              : 
     298            0 :     m_qtPass->setFreshStart(false);
     299              :   }
     300            0 : }
     301              : 
     302              : /**
     303              :  * @brief MainWindow::onUpdate do a git pull
     304              :  */
     305            0 : void MainWindow::onUpdate(bool block) {
     306            0 :   ui->statusBar->showMessage(tr("Updating password-store"), 2000);
     307            0 :   if (block) {
     308            0 :     QtPassSettings::getPass()->GitPull_b();
     309              :   } else {
     310            0 :     QtPassSettings::getPass()->GitPull();
     311              :   }
     312            0 : }
     313              : 
     314              : /**
     315              :  * @brief MainWindow::onPush do a git push
     316              :  */
     317            0 : void MainWindow::onPush() {
     318            0 :   if (QtPassSettings::isUseGit()) {
     319            0 :     ui->statusBar->showMessage(tr("Updating password-store"), 2000);
     320            0 :     QtPassSettings::getPass()->GitPush();
     321              :   }
     322            0 : }
     323              : 
     324              : /**
     325              :  * @brief MainWindow::getFile get the selected file path
     326              :  * @param index
     327              :  * @param forPass returns relative path without '.gpg' extension
     328              :  * @return path
     329              :  * @return
     330              :  */
     331            0 : auto MainWindow::getFile(const QModelIndex &index, bool forPass) -> QString {
     332            0 :   if (!index.isValid() ||
     333            0 :       !model.fileInfo(proxyModel.mapToSource(index)).isFile()) {
     334            0 :     return {};
     335              :   }
     336            0 :   QString filePath = model.filePath(proxyModel.mapToSource(index));
     337            0 :   if (forPass) {
     338            0 :     filePath = QDir(QtPassSettings::getPassStore()).relativeFilePath(filePath);
     339            0 :     filePath.replace(Util::endsWithGpg(), "");
     340              :   }
     341              :   return filePath;
     342              : }
     343              : 
     344              : /**
     345              :  * @brief MainWindow::on_treeView_clicked read the selected password file
     346              :  * @param index
     347              :  */
     348            0 : void MainWindow::on_treeView_clicked(const QModelIndex &index) {
     349            0 :   bool cleared = ui->treeView->currentIndex().flags() == Qt::NoItemFlags;
     350              :   currentDir =
     351            0 :       Util::getDir(ui->treeView->currentIndex(), false, model, proxyModel);
     352              :   // TODO(bezet): "Could not decrypt";
     353            0 :   m_qtPass->clearClippedText();
     354            0 :   QString file = getFile(index, true);
     355            0 :   ui->passwordName->setText(getFile(index, true));
     356            0 :   if (!file.isEmpty() && !cleared) {
     357            0 :     QtPassSettings::getPass()->Show(file);
     358              :   } else {
     359            0 :     clearPanel(false);
     360            0 :     ui->actionEdit->setEnabled(false);
     361            0 :     ui->actionDelete->setEnabled(true);
     362              :   }
     363            0 : }
     364              : 
     365              : /**
     366              :  * @brief MainWindow::on_treeView_doubleClicked when doubleclicked on
     367              :  * TreeViewItem, open the edit Window
     368              :  * @param index
     369              :  */
     370            0 : void MainWindow::on_treeView_doubleClicked(const QModelIndex &index) {
     371              :   QFileInfo fileOrFolder =
     372            0 :       model.fileInfo(proxyModel.mapToSource(ui->treeView->currentIndex()));
     373              : 
     374            0 :   if (fileOrFolder.isFile()) {
     375            0 :     editPassword(getFile(index, true));
     376              :   }
     377            0 : }
     378              : 
     379              : /**
     380              :  * @brief MainWindow::deselect clear the selection, password and copy buffer
     381              :  */
     382            0 : void MainWindow::deselect() {
     383            0 :   currentDir = "";
     384            0 :   m_qtPass->clearClipboard();
     385            0 :   ui->treeView->clearSelection();
     386            0 :   ui->actionEdit->setEnabled(false);
     387            0 :   ui->actionDelete->setEnabled(false);
     388            0 :   ui->passwordName->setText("");
     389            0 :   clearPanel(false);
     390            0 : }
     391              : 
     392            0 : void MainWindow::executeWrapperStarted() {
     393            0 :   clearTemplateWidgets();
     394            0 :   ui->textBrowser->clear();
     395            0 :   setUiElementsEnabled(false);
     396            0 :   clearPanelTimer.stop();
     397            0 : }
     398              : 
     399            0 : void MainWindow::passShowHandler(const QString &p_output) {
     400            0 :   QStringList templ = QtPassSettings::isUseTemplate()
     401            0 :                           ? QtPassSettings::getPassTemplate().split("\n")
     402            0 :                           : QStringList();
     403              :   bool allFields =
     404            0 :       QtPassSettings::isUseTemplate() && QtPassSettings::isTemplateAllFields();
     405            0 :   FileContent fileContent = FileContent::parse(p_output, templ, allFields);
     406              :   QString output = p_output;
     407            0 :   QString password = fileContent.getPassword();
     408              : 
     409              :   // set clipped text
     410            0 :   m_qtPass->setClippedText(password, p_output);
     411              : 
     412              :   // first clear the current view:
     413            0 :   clearTemplateWidgets();
     414              : 
     415              :   // show what is needed:
     416            0 :   if (QtPassSettings::isHideContent()) {
     417            0 :     output = "***" + tr("Content hidden") + "***";
     418            0 :   } else if (!QtPassSettings::isDisplayAsIs()) {
     419            0 :     if (!password.isEmpty()) {
     420              :       // set the password, it is hidden if needed in addToGridLayout
     421            0 :       addToGridLayout(0, tr("Password"), password);
     422              :     }
     423              : 
     424            0 :     NamedValues namedValues = fileContent.getNamedValues();
     425            0 :     for (int j = 0; j < namedValues.length(); ++j) {
     426              :       const NamedValue &nv = namedValues.at(j);
     427            0 :       addToGridLayout(j + 1, nv.name, nv.value);
     428              :     }
     429            0 :     if (ui->gridLayout->count() == 0) {
     430            0 :       ui->verticalLayoutPassword->setSpacing(0);
     431              :     } else {
     432            0 :       ui->verticalLayoutPassword->setSpacing(6);
     433              :     }
     434              : 
     435            0 :     output = fileContent.getRemainingDataForDisplay();
     436              :   }
     437              : 
     438            0 :   if (QtPassSettings::isUseAutoclearPanel()) {
     439            0 :     clearPanelTimer.start();
     440              :   }
     441              : 
     442            0 :   emit passShowHandlerFinished(output);
     443            0 :   setUiElementsEnabled(true);
     444            0 : }
     445              : 
     446            0 : void MainWindow::passOtpHandler(const QString &p_output) {
     447            0 :   if (!p_output.isEmpty()) {
     448            0 :     addToGridLayout(ui->gridLayout->count() + 1, tr("OTP Code"), p_output);
     449            0 :     m_qtPass->copyTextToClipboard(p_output);
     450            0 :     showStatusMessage(tr("OTP code copied to clipboard"));
     451              :   } else {
     452            0 :     flashText(tr("No OTP code found in this password entry"), true);
     453              :   }
     454            0 :   if (QtPassSettings::isUseAutoclearPanel()) {
     455            0 :     clearPanelTimer.start();
     456              :   }
     457            0 :   setUiElementsEnabled(true);
     458            0 : }
     459              : 
     460              : /**
     461              :  * @brief MainWindow::clearPanel hide the information from shoulder surfers
     462              :  */
     463            0 : void MainWindow::clearPanel(bool notify) {
     464            0 :   while (ui->gridLayout->count() > 0) {
     465            0 :     QLayoutItem *item = ui->gridLayout->takeAt(0);
     466            0 :     delete item->widget();
     467            0 :     delete item;
     468              :   }
     469            0 :   if (notify) {
     470            0 :     QString output = "***" + tr("Password and Content hidden") + "***";
     471            0 :     ui->textBrowser->setHtml(output);
     472              :   } else {
     473            0 :     ui->textBrowser->setHtml("");
     474              :   }
     475            0 : }
     476              : 
     477              : /**
     478              :  * @brief MainWindow::setUiElementsEnabled enable or disable the relevant UI
     479              :  * elements
     480              :  * @param state
     481              :  */
     482            0 : void MainWindow::setUiElementsEnabled(bool state) {
     483            0 :   ui->treeView->setEnabled(state);
     484            0 :   ui->lineEdit->setEnabled(state);
     485            0 :   ui->lineEdit->installEventFilter(this);
     486            0 :   ui->actionAddPassword->setEnabled(state);
     487            0 :   ui->actionAddFolder->setEnabled(state);
     488            0 :   ui->actionUsers->setEnabled(state);
     489            0 :   ui->actionConfig->setEnabled(state);
     490              :   // is a file selected?
     491            0 :   state &= ui->treeView->currentIndex().isValid();
     492            0 :   ui->actionDelete->setEnabled(state);
     493            0 :   ui->actionEdit->setEnabled(state);
     494            0 :   updateGitButtonVisibility();
     495            0 :   updateOtpButtonVisibility();
     496            0 : }
     497              : 
     498            0 : void MainWindow::restoreWindow() {
     499            0 :   QByteArray geometry = QtPassSettings::getGeometry(saveGeometry());
     500            0 :   restoreGeometry(geometry);
     501            0 :   QByteArray savestate = QtPassSettings::getSavestate(saveState());
     502            0 :   restoreState(savestate);
     503            0 :   QPoint position = QtPassSettings::getPos(pos());
     504            0 :   move(position);
     505            0 :   QSize newSize = QtPassSettings::getSize(size());
     506            0 :   resize(newSize);
     507            0 :   if (QtPassSettings::isMaximized(isMaximized())) {
     508            0 :     showMaximized();
     509              :   }
     510              : 
     511            0 :   if (QtPassSettings::isAlwaysOnTop()) {
     512              :     Qt::WindowFlags flags = windowFlags();
     513            0 :     setWindowFlags(flags | Qt::WindowStaysOnTopHint);
     514            0 :     show();
     515              :   }
     516              : 
     517            0 :   if (QtPassSettings::isUseTrayIcon() && tray == nullptr) {
     518            0 :     initTrayIcon();
     519            0 :     if (QtPassSettings::isStartMinimized()) {
     520              :       // since we are still in constructor, can't directly hide
     521            0 :       QTimer::singleShot(10, this, SLOT(hide()));
     522              :     }
     523            0 :   } else if (!QtPassSettings::isUseTrayIcon() && tray != nullptr) {
     524            0 :     destroyTrayIcon();
     525              :   }
     526            0 : }
     527              : 
     528              : /**
     529              :  * @brief MainWindow::on_configButton_clicked run Mainwindow::config
     530              :  */
     531            0 : void MainWindow::onConfig() { config(); }
     532              : 
     533              : /**
     534              :  * @brief Executes when the string in the search box changes, collapses the
     535              :  * TreeView
     536              :  * @param arg1
     537              :  */
     538            0 : void MainWindow::on_lineEdit_textChanged(const QString &arg1) {
     539            0 :   ui->statusBar->showMessage(tr("Looking for: %1").arg(arg1), 1000);
     540            0 :   ui->treeView->expandAll();
     541            0 :   clearPanel(false);
     542            0 :   ui->passwordName->setText("");
     543            0 :   ui->actionEdit->setEnabled(false);
     544            0 :   ui->actionDelete->setEnabled(false);
     545            0 :   searchTimer.start();
     546            0 : }
     547              : 
     548              : /**
     549              :  * @brief MainWindow::onTimeoutSearch Fired when search is finished or too much
     550              :  * time from two keypresses is elapsed
     551              :  */
     552            0 : void MainWindow::onTimeoutSearch() {
     553            0 :   QString query = ui->lineEdit->text();
     554              : 
     555            0 :   if (query.isEmpty()) {
     556            0 :     ui->treeView->collapseAll();
     557            0 :     deselect();
     558              :   }
     559              : 
     560            0 :   query.replace(QStringLiteral(" "), ".*");
     561            0 :   QRegularExpression regExp(query, QRegularExpression::CaseInsensitiveOption);
     562            0 :   proxyModel.setFilterRegularExpression(regExp);
     563            0 :   ui->treeView->setRootIndex(proxyModel.mapFromSource(
     564            0 :       model.setRootPath(QtPassSettings::getPassStore())));
     565              : 
     566            0 :   if (proxyModel.rowCount() > 0 && !query.isEmpty()) {
     567            0 :     selectFirstFile();
     568              :   } else {
     569            0 :     ui->actionEdit->setEnabled(false);
     570            0 :     ui->actionDelete->setEnabled(false);
     571              :   }
     572            0 : }
     573              : 
     574              : /**
     575              :  * @brief MainWindow::on_lineEdit_returnPressed get searching
     576              :  *
     577              :  * Select the first possible file in the tree
     578              :  */
     579            0 : void MainWindow::on_lineEdit_returnPressed() {
     580              : #ifdef QT_DEBUG
     581              :   dbg() << "on_lineEdit_returnPressed" << proxyModel.rowCount();
     582              : #endif
     583              : 
     584            0 :   if (proxyModel.rowCount() > 0) {
     585            0 :     selectFirstFile();
     586            0 :     on_treeView_clicked(ui->treeView->currentIndex());
     587              :   }
     588            0 : }
     589              : 
     590              : /**
     591              :  * @brief MainWindow::selectFirstFile select the first possible file in the
     592              :  * tree
     593              :  */
     594            0 : void MainWindow::selectFirstFile() {
     595            0 :   QModelIndex index = proxyModel.mapFromSource(
     596            0 :       model.setRootPath(QtPassSettings::getPassStore()));
     597            0 :   index = firstFile(index);
     598            0 :   ui->treeView->setCurrentIndex(index);
     599            0 : }
     600              : 
     601              : /**
     602              :  * @brief MainWindow::firstFile return location of first possible file
     603              :  * @param parentIndex
     604              :  * @return QModelIndex
     605              :  */
     606            0 : auto MainWindow::firstFile(QModelIndex parentIndex) -> QModelIndex {
     607            0 :   QModelIndex index = parentIndex;
     608            0 :   int numRows = proxyModel.rowCount(parentIndex);
     609            0 :   for (int row = 0; row < numRows; ++row) {
     610            0 :     index = proxyModel.index(row, 0, parentIndex);
     611            0 :     if (model.fileInfo(proxyModel.mapToSource(index)).isFile()) {
     612            0 :       return index;
     613              :     }
     614            0 :     if (proxyModel.hasChildren(index)) {
     615            0 :       return firstFile(index);
     616              :     }
     617              :   }
     618            0 :   return index;
     619              : }
     620              : 
     621              : /**
     622              :  * @brief MainWindow::setPassword open passworddialog
     623              :  * @param file which pgp file
     624              :  * @param isNew insert (not update)
     625              :  */
     626            0 : void MainWindow::setPassword(const QString &file, bool isNew) {
     627            0 :   PasswordDialog d(file, isNew, this);
     628              : 
     629            0 :   if (!d.exec()) {
     630            0 :     ui->treeView->setFocus();
     631              :   }
     632            0 : }
     633              : 
     634              : /**
     635              :  * @brief MainWindow::addPassword add a new password by showing a
     636              :  * number of dialogs.
     637              :  */
     638            0 : void MainWindow::addPassword() {
     639              :   bool ok;
     640              :   QString dir =
     641            0 :       Util::getDir(ui->treeView->currentIndex(), true, model, proxyModel);
     642              :   QString file =
     643            0 :       QInputDialog::getText(this, tr("New file"),
     644            0 :                             tr("New password file: \n(Will be placed in %1 )")
     645            0 :                                 .arg(QtPassSettings::getPassStore() +
     646            0 :                                      Util::getDir(ui->treeView->currentIndex(),
     647            0 :                                                   true, model, proxyModel)),
     648            0 :                             QLineEdit::Normal, "", &ok);
     649            0 :   if (!ok || file.isEmpty()) {
     650              :     return;
     651              :   }
     652            0 :   file = dir + file;
     653            0 :   setPassword(file);
     654              : }
     655              : 
     656              : /**
     657              :  * @brief MainWindow::onDelete remove password, if you are
     658              :  * sure.
     659              :  */
     660            0 : void MainWindow::onDelete() {
     661            0 :   QModelIndex currentIndex = ui->treeView->currentIndex();
     662              :   if (!currentIndex.isValid()) {
     663              :     // This fixes https://github.com/IJHack/QtPass/issues/556
     664              :     // Otherwise the entire password directory would be deleted if
     665              :     // nothing is selected in the tree view.
     666            0 :     return;
     667              :   }
     668              : 
     669              :   QFileInfo fileOrFolder =
     670            0 :       model.fileInfo(proxyModel.mapToSource(ui->treeView->currentIndex()));
     671            0 :   QString file = "";
     672              :   bool isDir = false;
     673              : 
     674            0 :   if (fileOrFolder.isFile()) {
     675            0 :     file = getFile(ui->treeView->currentIndex(), true);
     676              :   } else {
     677            0 :     file = Util::getDir(ui->treeView->currentIndex(), true, model, proxyModel);
     678              :     isDir = true;
     679              :   }
     680              : 
     681              :   QString dirMessage = tr(" and the whole content?");
     682            0 :   if (isDir) {
     683            0 :     QDirIterator it(model.rootPath() + QDir::separator() + file,
     684            0 :                     QDirIterator::Subdirectories);
     685              :     bool okDir = true;
     686            0 :     while (it.hasNext() && okDir) {
     687            0 :       it.next();
     688            0 :       if (QFileInfo(it.filePath()).isFile()) {
     689            0 :         if (QFileInfo(it.filePath()).suffix() != "gpg") {
     690              :           okDir = false;
     691            0 :           dirMessage = tr(" and the whole content? <br><strong>Attention: "
     692              :                           "there are unexpected files in the given folder, "
     693              :                           "check them before continue.</strong>");
     694              :         }
     695              :       }
     696              :     }
     697            0 :   }
     698              : 
     699            0 :   if (QMessageBox::question(
     700            0 :           this, isDir ? tr("Delete folder?") : tr("Delete password?"),
     701            0 :           tr("Are you sure you want to delete %1%2?")
     702            0 :               .arg(QDir::separator() + file, isDir ? dirMessage : "?"),
     703              :           QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) {
     704              :     return;
     705              :   }
     706              : 
     707            0 :   QtPassSettings::getPass()->Remove(file, isDir);
     708            0 : }
     709              : 
     710              : /**
     711              :  * @brief MainWindow::onOTP try and generate (selected) OTP code.
     712              :  */
     713            0 : void MainWindow::onOtp() {
     714            0 :   QString file = getFile(ui->treeView->currentIndex(), true);
     715            0 :   if (!file.isEmpty()) {
     716            0 :     if (QtPassSettings::isUseOtp()) {
     717            0 :       setUiElementsEnabled(false);
     718            0 :       QtPassSettings::getPass()->OtpGenerate(file);
     719              :     }
     720              :   } else {
     721            0 :     flashText(tr("No password selected for OTP generation"), true);
     722              :   }
     723            0 : }
     724              : 
     725              : /**
     726              :  * @brief MainWindow::onEdit try and edit (selected) password.
     727              :  */
     728            0 : void MainWindow::onEdit() {
     729            0 :   QString file = getFile(ui->treeView->currentIndex(), true);
     730            0 :   editPassword(file);
     731            0 : }
     732              : 
     733              : /**
     734              :  * @brief MainWindow::userDialog see MainWindow::onUsers()
     735              :  * @param dir folder to edit users for.
     736              :  */
     737            0 : void MainWindow::userDialog(const QString &dir) {
     738            0 :   if (!dir.isEmpty()) {
     739            0 :     currentDir = dir;
     740              :   }
     741            0 :   onUsers();
     742            0 : }
     743              : 
     744              : /**
     745              :  * @brief MainWindow::onUsers edit users for the current
     746              :  * folder,
     747              :  * gets lists and opens UserDialog.
     748              :  */
     749            0 : void MainWindow::onUsers() {
     750              :   QString dir =
     751              :       currentDir.isEmpty()
     752            0 :           ? Util::getDir(ui->treeView->currentIndex(), false, model, proxyModel)
     753            0 :           : currentDir;
     754              : 
     755            0 :   UsersDialog d(dir, this);
     756            0 :   if (!d.exec()) {
     757            0 :     ui->treeView->setFocus();
     758              :   }
     759            0 : }
     760              : 
     761              : /**
     762              :  * @brief MainWindow::messageAvailable we have some text/message/search to do.
     763              :  * @param message
     764              :  */
     765            0 : void MainWindow::messageAvailable(const QString &message) {
     766            0 :   if (message.isEmpty()) {
     767            0 :     focusInput();
     768              :   } else {
     769            0 :     ui->treeView->expandAll();
     770            0 :     ui->lineEdit->setText(message);
     771            0 :     on_lineEdit_returnPressed();
     772              :   }
     773            0 :   show();
     774            0 :   raise();
     775            0 : }
     776              : 
     777              : /**
     778              :  * @brief MainWindow::generateKeyPair internal gpg keypair generator . .
     779              :  * @param batch
     780              :  * @param keygenWindow
     781              :  */
     782            0 : void MainWindow::generateKeyPair(const QString &batch, QDialog *keygenWindow) {
     783            0 :   keygen = keygenWindow;
     784            0 :   emit generateGPGKeyPair(batch);
     785            0 : }
     786              : 
     787              : /**
     788              :  * @brief MainWindow::updateProfileBox update the list of profiles, optionally
     789              :  * select a more appropriate one to view too
     790              :  */
     791            0 : void MainWindow::updateProfileBox() {
     792              :   QHash<QString, QHash<QString, QString>> profiles =
     793            0 :       QtPassSettings::getProfiles();
     794              : 
     795              :   if (profiles.isEmpty()) {
     796            0 :     ui->profileWidget->hide();
     797              :   } else {
     798            0 :     ui->profileWidget->show();
     799            0 :     ui->profileBox->setEnabled(profiles.size() > 1);
     800            0 :     ui->profileBox->clear();
     801            0 :     QHashIterator<QString, QHash<QString, QString>> i(profiles);
     802            0 :     while (i.hasNext()) {
     803              :       i.next();
     804            0 :       if (!i.key().isEmpty()) {
     805            0 :         ui->profileBox->addItem(i.key());
     806              :       }
     807              :     }
     808            0 :     ui->profileBox->model()->sort(0);
     809              :   }
     810            0 :   int index = ui->profileBox->findText(QtPassSettings::getProfile());
     811            0 :   if (index != -1) { //  -1 for not found
     812            0 :     ui->profileBox->setCurrentIndex(index);
     813              :   }
     814            0 : }
     815              : 
     816              : /**
     817              :  * @brief MainWindow::on_profileBox_currentIndexChanged make sure we show the
     818              :  * correct "profile"
     819              :  * @param name
     820              :  */
     821              : #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
     822              : void MainWindow::on_profileBox_currentIndexChanged(QString name) {
     823              : #else
     824            0 : void MainWindow::on_profileBox_currentTextChanged(const QString &name) {
     825              : #endif
     826            0 :   if (m_qtPass->isFreshStart() || name == QtPassSettings::getProfile()) {
     827              :     return;
     828              :   }
     829              : 
     830            0 :   ui->lineEdit->clear();
     831              : 
     832            0 :   QtPassSettings::setProfile(name);
     833              : 
     834            0 :   QtPassSettings::setPassStore(
     835            0 :       QtPassSettings::getProfiles().value(name).value("path"));
     836            0 :   QtPassSettings::setPassSigningKey(
     837            0 :       QtPassSettings::getProfiles().value(name).value("signingKey"));
     838            0 :   ui->statusBar->showMessage(tr("Profile changed to %1").arg(name), 2000);
     839              : 
     840            0 :   QtPassSettings::getPass()->updateEnv();
     841              : 
     842            0 :   ui->treeView->selectionModel()->clear();
     843            0 :   ui->treeView->setRootIndex(proxyModel.mapFromSource(
     844            0 :       model.setRootPath(QtPassSettings::getPassStore())));
     845              : 
     846            0 :   ui->actionEdit->setEnabled(false);
     847            0 :   ui->actionDelete->setEnabled(false);
     848              : }
     849              : 
     850              : /**
     851              :  * @brief MainWindow::initTrayIcon show a nice tray icon on systems that
     852              :  * support
     853              :  * it
     854              :  */
     855            0 : void MainWindow::initTrayIcon() {
     856            0 :   this->tray = new TrayIcon(this);
     857              :   // Setup tray icon
     858              : 
     859              :   if (tray == nullptr) {
     860              : #ifdef QT_DEBUG
     861              :     dbg() << "Allocating tray icon failed.";
     862              : #endif
     863              :   }
     864              : 
     865            0 :   if (!tray->getIsAllocated()) {
     866            0 :     destroyTrayIcon();
     867              :   }
     868            0 : }
     869              : 
     870              : /**
     871              :  * @brief MainWindow::destroyTrayIcon remove that pesky tray icon
     872              :  */
     873            0 : void MainWindow::destroyTrayIcon() {
     874            0 :   delete this->tray;
     875            0 :   tray = nullptr;
     876            0 : }
     877              : 
     878              : /**
     879              :  * @brief MainWindow::closeEvent hide or quit
     880              :  * @param event
     881              :  */
     882            0 : void MainWindow::closeEvent(QCloseEvent *event) {
     883            0 :   if (QtPassSettings::isHideOnClose()) {
     884            0 :     this->hide();
     885              :     event->ignore();
     886              :   } else {
     887            0 :     m_qtPass->clearClipboard();
     888              : 
     889            0 :     QtPassSettings::setGeometry(saveGeometry());
     890            0 :     QtPassSettings::setSavestate(saveState());
     891            0 :     QtPassSettings::setMaximized(isMaximized());
     892            0 :     if (!isMaximized()) {
     893            0 :       QtPassSettings::setPos(pos());
     894            0 :       QtPassSettings::setSize(size());
     895              :     }
     896              :     event->accept();
     897              :   }
     898            0 : }
     899              : 
     900              : /**
     901              :  * @brief MainWindow::eventFilter filter out some events and focus the
     902              :  * treeview
     903              :  * @param obj
     904              :  * @param event
     905              :  * @return
     906              :  */
     907            0 : auto MainWindow::eventFilter(QObject *obj, QEvent *event) -> bool {
     908            0 :   if (obj == ui->lineEdit && event->type() == QEvent::KeyPress) {
     909            0 :     auto *key = dynamic_cast<QKeyEvent *>(event);
     910            0 :     if (key != nullptr && key->key() == Qt::Key_Down) {
     911            0 :       ui->treeView->setFocus();
     912              :     }
     913              :   }
     914            0 :   return QObject::eventFilter(obj, event);
     915              : }
     916              : 
     917              : /**
     918              :  * @brief MainWindow::keyPressEvent did anyone press return, enter or escape?
     919              :  * @param event
     920              :  */
     921            0 : void MainWindow::keyPressEvent(QKeyEvent *event) {
     922            0 :   switch (event->key()) {
     923            0 :   case Qt::Key_Delete:
     924            0 :     onDelete();
     925            0 :     break;
     926            0 :   case Qt::Key_Return:
     927              :   case Qt::Key_Enter:
     928            0 :     if (proxyModel.rowCount() > 0) {
     929            0 :       on_treeView_clicked(ui->treeView->currentIndex());
     930              :     }
     931              :     break;
     932            0 :   case Qt::Key_Escape:
     933            0 :     ui->lineEdit->clear();
     934            0 :     break;
     935              :   default:
     936              :     break;
     937              :   }
     938            0 : }
     939              : 
     940              : /**
     941              :  * @brief MainWindow::showContextMenu show us the (file or folder) context
     942              :  * menu
     943              :  * @param pos
     944              :  */
     945            0 : void MainWindow::showContextMenu(const QPoint &pos) {
     946            0 :   QModelIndex index = ui->treeView->indexAt(pos);
     947              :   bool selected = true;
     948              :   if (!index.isValid()) {
     949            0 :     ui->treeView->clearSelection();
     950            0 :     ui->actionDelete->setEnabled(false);
     951            0 :     ui->actionEdit->setEnabled(false);
     952            0 :     currentDir = "";
     953              :     selected = false;
     954              :   }
     955              : 
     956            0 :   ui->treeView->setCurrentIndex(index);
     957              : 
     958            0 :   QPoint globalPos = ui->treeView->viewport()->mapToGlobal(pos);
     959              : 
     960              :   QFileInfo fileOrFolder =
     961            0 :       model.fileInfo(proxyModel.mapToSource(ui->treeView->currentIndex()));
     962              : 
     963            0 :   QMenu contextMenu;
     964            0 :   if (!selected || fileOrFolder.isDir()) {
     965              :     QAction *openFolder =
     966            0 :         contextMenu.addAction(tr("Open folder with file manager"));
     967            0 :     QAction *addFolder = contextMenu.addAction(tr("Add folder"));
     968            0 :     QAction *addPassword = contextMenu.addAction(tr("Add password"));
     969            0 :     QAction *users = contextMenu.addAction(tr("Users"));
     970            0 :     connect(openFolder, &QAction::triggered, this, &MainWindow::openFolder);
     971            0 :     connect(addFolder, &QAction::triggered, this, &MainWindow::addFolder);
     972            0 :     connect(addPassword, &QAction::triggered, this, &MainWindow::addPassword);
     973            0 :     connect(users, &QAction::triggered, this, &MainWindow::onUsers);
     974            0 :   } else if (fileOrFolder.isFile()) {
     975            0 :     QAction *edit = contextMenu.addAction(tr("Edit"));
     976            0 :     connect(edit, &QAction::triggered, this, &MainWindow::onEdit);
     977              :   }
     978            0 :   if (selected) {
     979              :     // if (useClipboard != CLIPBOARD_NEVER) {
     980              :     // contextMenu.addSeparator();
     981              :     // QAction* copyItem = contextMenu.addAction(tr("Copy Password"));
     982              :     // if (getClippedPassword().length() == 0) copyItem->setEnabled(false);
     983              :     // connect(copyItem, SIGNAL(triggered()), this,
     984              :     // SLOT(copyPasswordToClipboard()));
     985              :     // }
     986            0 :     contextMenu.addSeparator();
     987            0 :     if (fileOrFolder.isDir()) {
     988            0 :       QAction *renameFolder = contextMenu.addAction(tr("Rename folder"));
     989            0 :       connect(renameFolder, &QAction::triggered, this,
     990            0 :               &MainWindow::renameFolder);
     991            0 :     } else if (fileOrFolder.isFile()) {
     992            0 :       QAction *renamePassword = contextMenu.addAction(tr("Rename password"));
     993            0 :       connect(renamePassword, &QAction::triggered, this,
     994            0 :               &MainWindow::renamePassword);
     995              :     }
     996            0 :     QAction *deleteItem = contextMenu.addAction(tr("Delete"));
     997            0 :     connect(deleteItem, &QAction::triggered, this, &MainWindow::onDelete);
     998              :   }
     999            0 :   contextMenu.exec(globalPos);
    1000            0 : }
    1001              : 
    1002              : /**
    1003              :  * @brief MainWindow::showBrowserContextMenu show us the context menu in
    1004              :  * password window
    1005              :  * @param pos
    1006              :  */
    1007            0 : void MainWindow::showBrowserContextMenu(const QPoint &pos) {
    1008            0 :   QMenu *contextMenu = ui->textBrowser->createStandardContextMenu(pos);
    1009            0 :   QPoint globalPos = ui->textBrowser->viewport()->mapToGlobal(pos);
    1010              : 
    1011            0 :   contextMenu->exec(globalPos);
    1012            0 :   delete contextMenu;
    1013            0 : }
    1014              : 
    1015              : /**
    1016              :  * @brief MainWindow::openFolder open the folder in the default file manager
    1017              :  */
    1018            0 : void MainWindow::openFolder() {
    1019              :   QString dir =
    1020            0 :       Util::getDir(ui->treeView->currentIndex(), false, model, proxyModel);
    1021              : 
    1022            0 :   QString path = QDir::toNativeSeparators(dir);
    1023            0 :   QDesktopServices::openUrl(QUrl::fromLocalFile(path));
    1024            0 : }
    1025              : 
    1026              : /**
    1027              :  * @brief MainWindow::addFolder add a new folder to store passwords in
    1028              :  */
    1029            0 : void MainWindow::addFolder() {
    1030              :   bool ok;
    1031              :   QString dir =
    1032            0 :       Util::getDir(ui->treeView->currentIndex(), false, model, proxyModel);
    1033              :   QString newdir =
    1034            0 :       QInputDialog::getText(this, tr("New file"),
    1035            0 :                             tr("New Folder: \n(Will be placed in %1 )")
    1036            0 :                                 .arg(QtPassSettings::getPassStore() +
    1037            0 :                                      Util::getDir(ui->treeView->currentIndex(),
    1038            0 :                                                   true, model, proxyModel)),
    1039            0 :                             QLineEdit::Normal, "", &ok);
    1040            0 :   if (!ok || newdir.isEmpty()) {
    1041            0 :     return;
    1042              :   }
    1043              :   newdir.prepend(dir);
    1044              :   // dbg()<< newdir;
    1045            0 :   if (!QDir().mkdir(newdir)) {
    1046            0 :     QMessageBox::warning(this, tr("Error"),
    1047            0 :                          tr("Failed to create folder: %1").arg(newdir));
    1048            0 :     return;
    1049              :   }
    1050            0 :   if (QtPassSettings::isAddGPGId(true)) {
    1051            0 :     QString gpgIdFile = newdir + "/.gpg-id";
    1052            0 :     QFile gpgId(gpgIdFile);
    1053            0 :     if (!gpgId.open(QIODevice::WriteOnly)) {
    1054            0 :       QMessageBox::warning(
    1055            0 :           this, tr("Error"),
    1056            0 :           tr("Failed to create .gpg-id file in: %1").arg(newdir));
    1057              :       return;
    1058              :     }
    1059            0 :     QList<UserInfo> users = QtPassSettings::getPass()->listKeys("", true);
    1060            0 :     for (const UserInfo &user : users) {
    1061            0 :       if (user.enabled) {
    1062            0 :         gpgId.write((user.key_id + "\n").toUtf8());
    1063              :       }
    1064              :     }
    1065            0 :     gpgId.close();
    1066            0 :   }
    1067              : }
    1068              : 
    1069              : /**
    1070              :  * @brief MainWindow::renameFolder rename an existing folder
    1071              :  */
    1072            0 : void MainWindow::renameFolder() {
    1073              :   bool ok;
    1074              :   QString srcDir = QDir::cleanPath(
    1075            0 :       Util::getDir(ui->treeView->currentIndex(), false, model, proxyModel));
    1076            0 :   QString srcDirName = QDir(srcDir).dirName();
    1077              :   QString newName =
    1078            0 :       QInputDialog::getText(this, tr("Rename file"), tr("Rename Folder To: "),
    1079            0 :                             QLineEdit::Normal, srcDirName, &ok);
    1080            0 :   if (!ok || newName.isEmpty()) {
    1081              :     return;
    1082              :   }
    1083              :   QString destDir = srcDir;
    1084            0 :   destDir.replace(srcDir.lastIndexOf(srcDirName), srcDirName.length(), newName);
    1085            0 :   QtPassSettings::getPass()->Move(srcDir, destDir);
    1086              : }
    1087              : 
    1088              : /**
    1089              :  * @brief MainWindow::editPassword read password and open edit window via
    1090              :  * MainWindow::onEdit()
    1091              :  */
    1092            0 : void MainWindow::editPassword(const QString &file) {
    1093            0 :   if (!file.isEmpty()) {
    1094            0 :     if (QtPassSettings::isUseGit() && QtPassSettings::isAutoPull()) {
    1095            0 :       onUpdate(true);
    1096              :     }
    1097            0 :     setPassword(file, false);
    1098              :   }
    1099            0 : }
    1100              : 
    1101              : /**
    1102              :  * @brief MainWindow::renamePassword rename an existing password
    1103              :  */
    1104            0 : void MainWindow::renamePassword() {
    1105              :   bool ok;
    1106            0 :   QString file = getFile(ui->treeView->currentIndex(), false);
    1107            0 :   QString filePath = QFileInfo(file).path();
    1108            0 :   QString fileName = QFileInfo(file).fileName();
    1109            0 :   if (fileName.endsWith(".gpg", Qt::CaseInsensitive)) {
    1110            0 :     fileName.chop(4);
    1111              :   }
    1112              : 
    1113              :   QString newName =
    1114            0 :       QInputDialog::getText(this, tr("Rename file"), tr("Rename File To: "),
    1115            0 :                             QLineEdit::Normal, fileName, &ok);
    1116            0 :   if (!ok || newName.isEmpty()) {
    1117              :     return;
    1118              :   }
    1119            0 :   QString newFile = QDir(filePath).filePath(newName);
    1120            0 :   QtPassSettings::getPass()->Move(file, newFile);
    1121              : }
    1122              : 
    1123              : /**
    1124              :  * @brief MainWindow::clearTemplateWidgets empty the template widget fields in
    1125              :  * the UI
    1126              :  */
    1127            0 : void MainWindow::clearTemplateWidgets() {
    1128            0 :   while (ui->gridLayout->count() > 0) {
    1129            0 :     QLayoutItem *item = ui->gridLayout->takeAt(0);
    1130            0 :     delete item->widget();
    1131            0 :     delete item;
    1132              :   }
    1133            0 :   ui->verticalLayoutPassword->setSpacing(0);
    1134            0 : }
    1135              : 
    1136            0 : void MainWindow::copyPasswordFromTreeview() {
    1137              :   QFileInfo fileOrFolder =
    1138            0 :       model.fileInfo(proxyModel.mapToSource(ui->treeView->currentIndex()));
    1139              : 
    1140            0 :   if (fileOrFolder.isFile()) {
    1141            0 :     QString file = getFile(ui->treeView->currentIndex(), true);
    1142            0 :     connect(QtPassSettings::getPass(), &Pass::finishedShow, this,
    1143            0 :             &MainWindow::passwordFromFileToClipboard);
    1144            0 :     QtPassSettings::getPass()->Show(file);
    1145              :   }
    1146            0 : }
    1147              : 
    1148            0 : void MainWindow::passwordFromFileToClipboard(const QString &text) {
    1149            0 :   QStringList tokens = text.split('\n');
    1150            0 :   m_qtPass->copyTextToClipboard(tokens[0]);
    1151            0 : }
    1152              : 
    1153              : /**
    1154              :  * @brief MainWindow::addToGridLayout add a field to the template grid
    1155              :  * @param position
    1156              :  * @param field
    1157              :  * @param value
    1158              :  */
    1159            0 : void MainWindow::addToGridLayout(int position, const QString &field,
    1160              :                                  const QString &value) {
    1161              :   QString trimmedField = field.trimmed();
    1162              :   QString trimmedValue = value.trimmed();
    1163              : 
    1164              :   // Combine the Copy button and the line edit in one widget
    1165            0 :   auto *frame = new QFrame();
    1166            0 :   QLayout *ly = new QHBoxLayout();
    1167            0 :   ly->setContentsMargins(5, 2, 2, 2);
    1168            0 :   ly->setSpacing(0);
    1169            0 :   frame->setLayout(ly);
    1170            0 :   if (QtPassSettings::getClipBoardType() != Enums::CLIPBOARD_NEVER) {
    1171            0 :     auto *fieldLabel = new QPushButtonWithClipboard(trimmedValue, this);
    1172            0 :     connect(fieldLabel, &QPushButtonWithClipboard::clicked, m_qtPass,
    1173            0 :             &QtPass::copyTextToClipboard);
    1174              : 
    1175            0 :     fieldLabel->setStyleSheet(
    1176            0 :         "border-style: none ; background: transparent; padding: 0; margin: 0;");
    1177            0 :     frame->layout()->addWidget(fieldLabel);
    1178              :   }
    1179              : 
    1180            0 :   if (QtPassSettings::isUseQrencode()) {
    1181            0 :     auto *qrbutton = new QPushButtonAsQRCode(trimmedValue, this);
    1182            0 :     connect(qrbutton, &QPushButtonAsQRCode::clicked, m_qtPass,
    1183            0 :             &QtPass::showTextAsQRCode);
    1184            0 :     qrbutton->setStyleSheet(
    1185            0 :         "border-style: none ; background: transparent; padding: 0; margin: 0;");
    1186            0 :     frame->layout()->addWidget(qrbutton);
    1187              :   }
    1188              : 
    1189              :   // set the echo mode to password, if the field is "password"
    1190              :   const QString lineStyle =
    1191            0 :       QtPassSettings::isUseMonospace()
    1192              :           ? "border-style: none; background: transparent; font-family: "
    1193              :             "monospace;"
    1194            0 :           : "border-style: none; background: transparent;";
    1195              : 
    1196            0 :   if (QtPassSettings::isHidePassword() && trimmedField == tr("Password")) {
    1197            0 :     auto *line = new QLineEdit();
    1198            0 :     line->setObjectName(trimmedField);
    1199            0 :     line->setText(trimmedValue);
    1200            0 :     line->setReadOnly(true);
    1201            0 :     line->setStyleSheet(lineStyle);
    1202            0 :     line->setContentsMargins(0, 0, 0, 0);
    1203            0 :     line->setEchoMode(QLineEdit::Password);
    1204            0 :     auto *showButton = new QPushButtonShowPassword(line, this);
    1205            0 :     showButton->setStyleSheet(
    1206            0 :         "border-style: none ; background: transparent; padding: 0; margin: 0;");
    1207            0 :     showButton->setContentsMargins(0, 0, 0, 0);
    1208            0 :     frame->layout()->addWidget(showButton);
    1209            0 :     frame->layout()->addWidget(line);
    1210              :   } else {
    1211            0 :     auto *line = new QTextBrowser();
    1212            0 :     line->setOpenExternalLinks(true);
    1213            0 :     line->setOpenLinks(true);
    1214            0 :     line->setMaximumHeight(26);
    1215            0 :     line->setMinimumHeight(26);
    1216            0 :     line->setSizePolicy(
    1217              :         QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum));
    1218            0 :     line->setObjectName(trimmedField);
    1219            0 :     trimmedValue.replace(Util::protocolRegex(), R"(<a href="\1">\1</a>)");
    1220            0 :     line->setText(trimmedValue);
    1221            0 :     line->setReadOnly(true);
    1222            0 :     line->setStyleSheet(lineStyle);
    1223            0 :     line->setContentsMargins(0, 0, 0, 0);
    1224            0 :     frame->layout()->addWidget(line);
    1225              :   }
    1226              : 
    1227            0 :   frame->setStyleSheet(
    1228            0 :       ".QFrame{border: 1px solid lightgrey; border-radius: 5px;}");
    1229              : 
    1230              :   // set into the layout
    1231            0 :   ui->gridLayout->addWidget(new QLabel(trimmedField), position, 0);
    1232            0 :   ui->gridLayout->addWidget(frame, position, 1);
    1233            0 : }
    1234              : 
    1235              : /**
    1236              :  * @brief Displays message in status bar
    1237              :  *
    1238              :  * @param msg     text to be displayed
    1239              :  * @param timeout time for which msg shall be visible
    1240              :  */
    1241            0 : void MainWindow::showStatusMessage(const QString &msg, int timeout) {
    1242            0 :   ui->statusBar->showMessage(msg, timeout);
    1243            0 : }
    1244              : 
    1245              : /**
    1246              :  * @brief MainWindow::startReencryptPath disable ui elements and treeview
    1247              :  */
    1248            0 : void MainWindow::startReencryptPath() {
    1249            0 :   setUiElementsEnabled(false);
    1250            0 :   ui->treeView->setDisabled(true);
    1251            0 : }
    1252              : 
    1253              : /**
    1254              :  * @brief MainWindow::endReencryptPath re-enable ui elements
    1255              :  */
    1256            0 : void MainWindow::endReencryptPath() { setUiElementsEnabled(true); }
    1257              : 
    1258            0 : void MainWindow::updateGitButtonVisibility() {
    1259            0 :   if (!QtPassSettings::isUseGit() ||
    1260            0 :       (QtPassSettings::getGitExecutable().isEmpty() &&
    1261            0 :        QtPassSettings::getPassExecutable().isEmpty())) {
    1262            0 :     enableGitButtons(false);
    1263              :   } else {
    1264            0 :     enableGitButtons(true);
    1265              :   }
    1266            0 : }
    1267              : 
    1268            0 : void MainWindow::updateOtpButtonVisibility() {
    1269              : #if defined(Q_OS_WIN) || defined(__APPLE__)
    1270              :   ui->actionOtp->setVisible(false);
    1271              : #endif
    1272            0 :   if (!QtPassSettings::isUseOtp()) {
    1273            0 :     ui->actionOtp->setEnabled(false);
    1274              :   } else {
    1275            0 :     ui->actionOtp->setEnabled(true);
    1276              :   }
    1277            0 : }
    1278              : 
    1279            0 : void MainWindow::enableGitButtons(const bool &state) {
    1280              :   // Following GNOME guidelines is preferable disable buttons instead of hide
    1281            0 :   ui->actionPush->setEnabled(state);
    1282            0 :   ui->actionUpdate->setEnabled(state);
    1283            0 : }
    1284              : 
    1285              : /**
    1286              :  * @brief MainWindow::critical critical message popup wrapper.
    1287              :  * @param title
    1288              :  * @param msg
    1289              :  */
    1290            0 : void MainWindow::critical(const QString &title, const QString &msg) {
    1291            0 :   QMessageBox::critical(this, title, msg);
    1292            0 : }
        

Generated by: LCOV version 2.4-beta