QtPass 1.6.0
Multi-platform GUI for pass, the standard unix password manager.
Loading...
Searching...
No Matches
mainwindow.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2014 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"
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 <QApplication>
22#include <QCloseEvent>
23#include <QDesktopServices>
24#include <QDialog>
25#include <QDirIterator>
26#include <QFileInfo>
27#include <QInputDialog>
28#include <QLabel>
29#include <QMenu>
30#include <QMessageBox>
31#include <QShortcut>
32#include <QTimer>
33#include <QTreeWidget>
34#include <utility>
35
42MainWindow::MainWindow(const QString &searchText, QWidget *parent)
43 : QMainWindow(parent), ui(new Ui::MainWindow), keygen(nullptr),
44 tray(nullptr) {
45#ifdef __APPLE__
46 // extra treatment for mac os
47 // see http://doc.qt.io/qt-5/qkeysequence.html#qt_set_sequence_auto_mnemonic
48 qt_set_sequence_auto_mnemonic(true);
49#endif
50 ui->setupUi(this);
51
52 m_qtPass = new QtPass(this);
53
54 // register shortcut ctrl/cmd + Q to close the main window
55 new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_Q), this, SLOT(close()));
56 // register shortcut ctrl/cmd + C to copy the currently selected password
57 new QShortcut(QKeySequence(QKeySequence::StandardKey::Copy), this,
58 SLOT(copyPasswordFromTreeview()));
59
60 model.setNameFilters(QStringList() << "*.gpg");
61 model.setNameFilterDisables(false);
62
63 /*
64 * I added this to solve Windows bug but now on GNU/Linux the main folder,
65 * if hidden, disappear
66 *
67 * model.setFilter(QDir::NoDot);
68 */
69
71
72 QModelIndex rootDir = model.setRootPath(passStore);
73 model.fetchMore(rootDir);
74
75 proxyModel.setModelAndStore(&model, passStore);
76 selectionModel.reset(new QItemSelectionModel(&proxyModel));
77
78 ui->treeView->setModel(&proxyModel);
79 ui->treeView->setRootIndex(proxyModel.mapFromSource(rootDir));
80 ui->treeView->setColumnHidden(1, true);
81 ui->treeView->setColumnHidden(2, true);
82 ui->treeView->setColumnHidden(3, true);
83 ui->treeView->setHeaderHidden(true);
84 ui->treeView->setIndentation(15);
85 ui->treeView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
86 ui->treeView->setContextMenuPolicy(Qt::CustomContextMenu);
87 ui->treeView->header()->setSectionResizeMode(0, QHeaderView::Stretch);
88 ui->treeView->sortByColumn(0, Qt::AscendingOrder);
89 connect(ui->treeView, &QWidget::customContextMenuRequested, this,
90 &MainWindow::showContextMenu);
91 connect(ui->treeView, &DeselectableTreeView::emptyClicked, this,
93
95 QFont monospace("Monospace");
96 monospace.setStyleHint(QFont::Monospace);
97 ui->textBrowser->setFont(monospace);
98 }
100 ui->textBrowser->setLineWrapMode(QTextBrowser::NoWrap);
101 }
102 ui->textBrowser->setOpenExternalLinks(true);
103 ui->textBrowser->setContextMenuPolicy(Qt::CustomContextMenu);
104 connect(ui->textBrowser, &QWidget::customContextMenuRequested, this,
105 &MainWindow::showBrowserContextMenu);
106
107 updateProfileBox();
108
109 QtPassSettings::getPass()->updateEnv();
110 clearPanelTimer.setInterval(MS_PER_SECOND *
112 clearPanelTimer.setSingleShot(true);
113 connect(&clearPanelTimer, &QTimer::timeout, this, [this]() { clearPanel(); });
114
115 searchTimer.setInterval(350);
116 searchTimer.setSingleShot(true);
117
118 connect(&searchTimer, &QTimer::timeout, this, &MainWindow::onTimeoutSearch);
119
120 initToolBarButtons();
121 initStatusBar();
122
123 ui->lineEdit->setClearButtonEnabled(true);
124 updateGrepButtonVisibility();
125
127
128 QTimer::singleShot(10, this, SLOT(focusInput()));
129
130 ui->lineEdit->setText(searchText);
131
132 if (!m_qtPass->init()) {
133 // no working config so this should just quit
134 QApplication::quit();
135 }
136}
137
138MainWindow::~MainWindow() { delete m_qtPass; }
139
146void MainWindow::focusInput() {
147 ui->lineEdit->selectAll();
148 ui->lineEdit->setFocus();
149}
150
155void MainWindow::changeEvent(QEvent *event) {
156 QWidget::changeEvent(event);
157 if (event->type() == QEvent::ActivationChange) {
158 if (isActiveWindow()) {
159 focusInput();
160 }
161 }
162}
163
167void MainWindow::initToolBarButtons() {
168 connect(ui->actionAddPassword, &QAction::triggered, this,
169 &MainWindow::addPassword);
170 connect(ui->actionAddFolder, &QAction::triggered, this,
171 &MainWindow::addFolder);
172 connect(ui->actionEdit, &QAction::triggered, this, &MainWindow::onEdit);
173 connect(ui->actionDelete, &QAction::triggered, this, &MainWindow::onDelete);
174 connect(ui->actionPush, &QAction::triggered, this, &MainWindow::onPush);
175 connect(ui->actionUpdate, &QAction::triggered, this, &MainWindow::onUpdate);
176 connect(ui->actionUsers, &QAction::triggered, this, &MainWindow::onUsers);
177 connect(ui->actionConfig, &QAction::triggered, this, &MainWindow::onConfig);
178 connect(ui->actionOtp, &QAction::triggered, this, &MainWindow::onOtp);
179
180 ui->actionAddPassword->setIcon(
181 QIcon::fromTheme("document-new", QIcon(":/icons/document-new.svg")));
182 ui->actionAddFolder->setIcon(
183 QIcon::fromTheme("folder-new", QIcon(":/icons/folder-new.svg")));
184 ui->actionEdit->setIcon(QIcon::fromTheme(
185 "document-properties", QIcon(":/icons/document-properties.svg")));
186 ui->actionDelete->setIcon(
187 QIcon::fromTheme("edit-delete", QIcon(":/icons/edit-delete.svg")));
188 ui->actionPush->setIcon(
189 QIcon::fromTheme("go-up", QIcon(":/icons/go-top.svg")));
190 ui->actionUpdate->setIcon(
191 QIcon::fromTheme("go-down", QIcon(":/icons/go-bottom.svg")));
192 ui->actionUsers->setIcon(QIcon::fromTheme(
193 "x-office-address-book", QIcon(":/icons/x-office-address-book.svg")));
194 ui->actionConfig->setIcon(QIcon::fromTheme(
195 "applications-system", QIcon(":/icons/applications-system.svg")));
196}
197
201void MainWindow::initStatusBar() {
202 ui->statusBar->showMessage(tr("Welcome to QtPass %1").arg(VERSION), 2000);
203
204 QPixmap logo = QPixmap::fromImage(QImage(":/artwork/icon.svg"))
205 .scaledToHeight(statusBar()->height());
206 auto *logoApp = new QLabel(statusBar());
207 logoApp->setPixmap(logo);
208 statusBar()->addPermanentWidget(logoApp);
209}
210
212 return ui->treeView->currentIndex();
213}
214
216 this->keygen->close();
217 this->keygen = nullptr;
218}
219
234void MainWindow::flashText(const QString &text, const bool isError,
235 const bool isHtml) {
236 if (isError) {
237 ui->textBrowser->setTextColor(Qt::red);
238 }
239
240 if (isHtml) {
241 QString _text = text;
242 if (!ui->textBrowser->toPlainText().isEmpty()) {
243 _text = ui->textBrowser->toHtml() + _text;
244 }
245 ui->textBrowser->setHtml(_text);
246 } else {
247 ui->textBrowser->setText(text);
248 }
249}
250
255void MainWindow::applyTextBrowserSettings() {
257 QFont monospace("Monospace");
258 monospace.setStyleHint(QFont::Monospace);
259 ui->textBrowser->setFont(monospace);
260 } else {
261 ui->textBrowser->setFont(QFont());
262 }
263
265 ui->textBrowser->setLineWrapMode(QTextBrowser::NoWrap);
266 } else {
267 ui->textBrowser->setLineWrapMode(QTextBrowser::WidgetWidth);
268 }
269}
270
271void MainWindow::applyWindowFlagsSettings() {
273 Qt::WindowFlags flags = windowFlags();
274 this->setWindowFlags(flags | Qt::WindowStaysOnTopHint);
275 } else {
276 this->setWindowFlags(Qt::Window);
277 }
278 this->show();
279}
280
290 QScopedPointer<ConfigDialog> d(new ConfigDialog(this));
291 d->setModal(true);
292 // Automatically default to pass if it's available
293 if (m_qtPass->isFreshStart() &&
294 QFile(QtPassSettings::getPassExecutable()).exists()) {
296 }
297
298 if (m_qtPass->isFreshStart()) {
299 d->wizard(); // does shit
300 }
301 if (d->exec()) {
302 if (d->result() == QDialog::Accepted) {
303 applyTextBrowserSettings();
304 applyWindowFlagsSettings();
305
306 updateProfileBox();
307 const QString passStore = QtPassSettings::getPassStore();
308 proxyModel.setStore(passStore);
309 ui->treeView->setRootIndex(
310 proxyModel.mapFromSource(model.setRootPath(passStore)));
311 deselect();
312 ui->treeView->setCurrentIndex(QModelIndex());
313
314 if (m_qtPass->isFreshStart() && !Util::configIsValid()) {
315 config();
316 }
317 QtPassSettings::getPass()->updateEnv();
318 clearPanelTimer.setInterval(MS_PER_SECOND *
320 m_qtPass->setClipboardTimer();
321
322 updateGitButtonVisibility();
323 updateOtpButtonVisibility();
324 updateGrepButtonVisibility();
325 if (QtPassSettings::isUseTrayIcon() && tray == nullptr) {
326 initTrayIcon();
327 } else if (!QtPassSettings::isUseTrayIcon() && tray != nullptr) {
328 destroyTrayIcon();
329 }
330 }
331
332 m_qtPass->setFreshStart(false);
333 }
334}
335
339void MainWindow::onUpdate(bool block) {
340 ui->statusBar->showMessage(tr("Updating password-store"), 2000);
341 if (block) {
342 QtPassSettings::getPass()->GitPull_b();
343 } else {
344 QtPassSettings::getPass()->GitPull();
345 }
346}
347
353 ui->statusBar->showMessage(tr("Updating password-store"), 2000);
354 QtPassSettings::getPass()->GitPush();
355 }
356}
357
365auto MainWindow::getFile(const QModelIndex &index, bool forPass) -> QString {
366 if (!index.isValid() ||
367 !model.fileInfo(proxyModel.mapToSource(index)).isFile()) {
368 return {};
369 }
370 QString filePath = model.filePath(proxyModel.mapToSource(index));
371 if (forPass) {
372 filePath = QDir(QtPassSettings::getPassStore()).relativeFilePath(filePath);
373 filePath.replace(Util::endsWithGpg(), "");
374 }
375 return filePath;
376}
377
382void MainWindow::on_treeView_clicked(const QModelIndex &index) {
383 bool cleared = ui->treeView->currentIndex().flags() == Qt::NoItemFlags;
384 currentDir =
385 Util::getDir(ui->treeView->currentIndex(), false, model, proxyModel);
386 // Clear any previously cached clipped text before showing new password
387 m_qtPass->clearClippedText();
388 QString file = getFile(index, true);
389 ui->passwordName->setText(file);
390 if (!file.isEmpty() && !cleared) {
391 QtPassSettings::getPass()->Show(file);
392 } else {
393 clearPanel(false);
394 ui->actionEdit->setEnabled(false);
395 ui->actionDelete->setEnabled(true);
396 }
397}
398
404void MainWindow::on_treeView_doubleClicked(const QModelIndex &index) {
405 QFileInfo fileOrFolder =
406 model.fileInfo(proxyModel.mapToSource(ui->treeView->currentIndex()));
407
408 if (fileOrFolder.isFile()) {
409 editPassword(getFile(index, true));
410 }
411}
412
417 currentDir = "";
418 m_qtPass->clearClipboard();
419 ui->treeView->clearSelection();
420 ui->actionEdit->setEnabled(false);
421 ui->actionDelete->setEnabled(false);
422 ui->passwordName->setText("");
423 clearPanel(false);
424}
425
427 clearTemplateWidgets();
428 ui->textBrowser->clear();
430 clearPanelTimer.stop();
431}
432
443void MainWindow::passShowHandler(const QString &p_output) {
444 QStringList templ = QtPassSettings::isUseTemplate()
445 ? QtPassSettings::getPassTemplate().split("\n")
446 : QStringList();
447 bool allFields =
449 FileContent fileContent = FileContent::parse(p_output, templ, allFields);
450 QString output = p_output;
451 QString password = fileContent.getPassword();
452
453 // set clipped text
454 m_qtPass->setClippedText(password, p_output);
455
456 // first clear the current view:
457 clearTemplateWidgets();
458
459 // show what is needed:
461 output = "***" + tr("Content hidden") + "***";
462 } else if (!QtPassSettings::isDisplayAsIs()) {
463 if (!password.isEmpty()) {
464 // set the password, it is hidden if needed in addToGridLayout
465 addToGridLayout(0, tr("Password"), password);
466 }
467
468 NamedValues namedValues = fileContent.getNamedValues();
469 for (int j = 0; j < namedValues.length(); ++j) {
470 const NamedValue &nv = namedValues.at(j);
471 addToGridLayout(j + 1, nv.name, nv.value);
472 }
473 if (ui->gridLayout->count() == 0) {
474 ui->verticalLayoutPassword->setSpacing(0);
475 } else {
476 ui->verticalLayoutPassword->setSpacing(6);
477 }
478
479 output = fileContent.getRemainingDataForDisplay();
480 }
481
483 clearPanelTimer.start();
484 }
485
486 emit passShowHandlerFinished(output);
488}
489
500void MainWindow::passOtpHandler(const QString &p_output) {
501 if (!p_output.isEmpty()) {
502 addToGridLayout(ui->gridLayout->count() + 1, tr("OTP Code"), p_output);
503 m_qtPass->copyTextToClipboard(p_output);
504 showStatusMessage(tr("OTP code copied to clipboard"));
505 } else {
506 flashText(tr("No OTP code found in this password entry"), true);
507 }
509 clearPanelTimer.start();
510 }
512}
513
517void MainWindow::clearPanel(bool notify) {
518 while (ui->gridLayout->count() > 0) {
519 QLayoutItem *item = ui->gridLayout->takeAt(0);
520 delete item->widget();
521 delete item;
522 }
523 const bool grepWasVisible = ui->grepResultsList->isVisible();
524 ui->grepResultsList->clear();
525 if (grepWasVisible) {
526 ui->grepResultsList->setVisible(false);
527 ui->treeView->setVisible(true);
528 if (m_grepMode) {
529 m_grepMode = false;
530 ui->grepButton->blockSignals(true);
531 ui->grepButton->setChecked(false);
532 ui->grepButton->blockSignals(false);
533 ui->lineEdit->blockSignals(true);
534 ui->lineEdit->clear();
535 ui->lineEdit->blockSignals(false);
536 ui->lineEdit->setPlaceholderText(tr("Search Password"));
537 }
538 }
539 if (notify) {
540 QString output = "***" + tr("Password and Content hidden") + "***";
541 ui->textBrowser->setHtml(output);
542 } else {
543 ui->textBrowser->setHtml("");
544 }
545}
546
553 ui->treeView->setEnabled(state);
554 ui->lineEdit->setEnabled(state);
555 ui->lineEdit->installEventFilter(this);
556 ui->actionAddPassword->setEnabled(state);
557 ui->actionAddFolder->setEnabled(state);
558 ui->actionUsers->setEnabled(state);
559 ui->actionConfig->setEnabled(state);
560 // is a file selected?
561 state &= ui->treeView->currentIndex().isValid();
562 ui->actionDelete->setEnabled(state);
563 ui->actionEdit->setEnabled(state);
564 updateGitButtonVisibility();
565 updateOtpButtonVisibility();
566}
567
578 QByteArray geometry = QtPassSettings::getGeometry(saveGeometry());
579 restoreGeometry(geometry);
580 QByteArray savestate = QtPassSettings::getSavestate(saveState());
581 restoreState(savestate);
582 QPoint position = QtPassSettings::getPos(pos());
583 move(position);
584 QSize newSize = QtPassSettings::getSize(size());
585 resize(newSize);
586 if (QtPassSettings::isMaximized(isMaximized())) {
587 showMaximized();
588 }
589
591 Qt::WindowFlags flags = windowFlags();
592 setWindowFlags(flags | Qt::WindowStaysOnTopHint);
593 show();
594 }
595
596 if (QtPassSettings::isUseTrayIcon() && tray == nullptr) {
597 initTrayIcon();
599 // since we are still in constructor, can't directly hide
600 QTimer::singleShot(10, this, SLOT(hide()));
601 }
602 } else if (!QtPassSettings::isUseTrayIcon() && tray != nullptr) {
603 destroyTrayIcon();
604 }
605}
606
610void MainWindow::onConfig() { config(); }
611
617void MainWindow::on_lineEdit_textChanged(const QString &arg1) {
618 if (m_grepMode)
619 return;
620 ui->statusBar->showMessage(tr("Looking for: %1").arg(arg1), 1000);
621 ui->treeView->expandAll();
622 clearPanel(false);
623 ui->passwordName->setText("");
624 ui->actionEdit->setEnabled(false);
625 ui->actionDelete->setEnabled(false);
626 searchTimer.start();
627}
628
633void MainWindow::onTimeoutSearch() {
634 QString query = ui->lineEdit->text();
635
636 if (query.isEmpty()) {
637 ui->treeView->collapseAll();
638 deselect();
639 }
640
641 query.replace(QStringLiteral(" "), ".*");
642 QRegularExpression regExp(query, QRegularExpression::CaseInsensitiveOption);
643 proxyModel.setFilterRegularExpression(regExp);
644 ui->treeView->setRootIndex(proxyModel.mapFromSource(
645 model.setRootPath(QtPassSettings::getPassStore())));
646
647 if (proxyModel.rowCount() > 0 && !query.isEmpty()) {
648 selectFirstFile();
649 } else {
650 ui->actionEdit->setEnabled(false);
651 ui->actionDelete->setEnabled(false);
652 }
653}
654
660void MainWindow::on_lineEdit_returnPressed() {
661#ifdef QT_DEBUG
662 dbg() << "on_lineEdit_returnPressed" << proxyModel.rowCount();
663#endif
664
665 if (m_grepMode) {
666 const QString query = ui->lineEdit->text();
667 if (!query.isEmpty()) {
668 m_grepCancelled = false;
669 ui->grepResultsList->clear();
670 ui->statusBar->showMessage(tr("Searching…"));
671 if (!m_grepBusy) {
672 m_grepBusy = true;
673 QApplication::setOverrideCursor(Qt::WaitCursor);
674 }
675 QtPassSettings::getPass()->Grep(query, ui->grepCaseButton->isChecked());
676 } else {
677 m_grepCancelled = true;
678 if (m_grepBusy) {
679 m_grepBusy = false;
680 QApplication::restoreOverrideCursor();
681 }
682 ui->grepResultsList->clear();
683 ui->grepResultsList->setVisible(false);
684 ui->treeView->setVisible(true);
685 }
686 return;
687 }
688
689 if (proxyModel.rowCount() > 0) {
690 selectFirstFile();
691 on_treeView_clicked(ui->treeView->currentIndex());
692 }
693}
694
698void MainWindow::on_grepButton_toggled(bool checked) {
699 m_grepMode = checked;
700 if (checked) {
701 ui->lineEdit->setPlaceholderText(tr("Search content (regex)"));
702 ui->lineEdit->clear();
703 searchTimer.stop();
704 proxyModel.setFilterRegularExpression(QRegularExpression());
705 ui->treeView->setRootIndex(proxyModel.mapFromSource(
706 model.setRootPath(QtPassSettings::getPassStore())));
707 ui->grepResultsList->setVisible(false);
708 // Keep treeView visible until results arrive
709 } else {
710 if (m_grepBusy) {
711 m_grepBusy = false;
712 m_grepCancelled = true;
713 QApplication::restoreOverrideCursor();
714 }
715 searchTimer.stop();
716 ui->lineEdit->blockSignals(true);
717 ui->lineEdit->clear();
718 ui->lineEdit->blockSignals(false);
719 ui->lineEdit->setPlaceholderText(tr("Search Password"));
720 ui->grepResultsList->clear();
721 ui->grepResultsList->setVisible(false);
722 ui->treeView->setVisible(true);
723 proxyModel.setFilterRegularExpression(QRegularExpression());
724 ui->treeView->setRootIndex(proxyModel.mapFromSource(
725 model.setRootPath(QtPassSettings::getPassStore())));
726 }
727}
728
733 const QList<QPair<QString, QStringList>> &results) {
734 if (m_grepBusy) {
735 m_grepBusy = false;
736 QApplication::restoreOverrideCursor();
737 }
738 if (m_grepCancelled) {
739 m_grepCancelled = false;
740 return;
741 }
743 if (!m_grepMode)
744 return;
745 ui->grepResultsList->clear();
746 if (results.isEmpty()) {
747 ui->statusBar->showMessage(tr("No matches found."), 3000);
748 ui->grepResultsList->setVisible(false);
749 ui->treeView->setVisible(true);
750 return;
751 }
752 const bool hideContent = QtPassSettings::isHideContent();
753 int totalLines = 0;
754 for (const auto &pair : results) {
755 QTreeWidgetItem *entryItem = new QTreeWidgetItem(ui->grepResultsList);
756 entryItem->setText(0, pair.first);
757 entryItem->setData(0, Qt::UserRole, pair.first);
758 for (const QString &line : pair.second) {
759 QTreeWidgetItem *lineItem = new QTreeWidgetItem(entryItem);
760 lineItem->setText(0, hideContent ? "***" + tr("Content hidden") + "***"
761 : line);
762 lineItem->setData(0, Qt::UserRole, pair.first);
763 ++totalLines;
764 }
765 }
766 ui->grepResultsList->expandAll();
767 ui->treeView->setVisible(false);
768 ui->grepResultsList->setVisible(true);
769 ui->statusBar->showMessage(
770 tr("Found %n match(es) in %1 entr(ies).", nullptr, totalLines)
771 .arg(results.size()),
772 3000);
774 clearPanelTimer.start();
775}
776
780void MainWindow::on_grepResultsList_itemClicked(QTreeWidgetItem *item,
781 int /*column*/) {
782 const QString entry = item->data(0, Qt::UserRole).toString();
783 if (entry.isEmpty())
784 return;
785 const QString fullPath = QDir::cleanPath(
786 QDir(QtPassSettings::getPassStore()).filePath(entry + ".gpg"));
787 QModelIndex srcIndex = model.index(fullPath);
788 if (!srcIndex.isValid())
789 return;
790 QModelIndex proxyIndex = proxyModel.mapFromSource(srcIndex);
791 if (!proxyIndex.isValid())
792 return;
793 ui->treeView->setCurrentIndex(proxyIndex);
794 on_treeView_clicked(proxyIndex);
796 ui->grepResultsList->clear();
797 ui->grepResultsList->setVisible(false);
798 ui->treeView->setVisible(true);
799 ui->treeView->scrollTo(proxyIndex);
800 ui->treeView->setFocus();
801}
802
807void MainWindow::selectFirstFile() {
808 QModelIndex index = proxyModel.mapFromSource(
809 model.setRootPath(QtPassSettings::getPassStore()));
810 index = firstFile(index);
811 ui->treeView->setCurrentIndex(index);
812}
813
819auto MainWindow::firstFile(QModelIndex parentIndex) -> QModelIndex {
820 QModelIndex index = parentIndex;
821 int numRows = proxyModel.rowCount(parentIndex);
822 for (int row = 0; row < numRows; ++row) {
823 index = proxyModel.index(row, 0, parentIndex);
824 if (model.fileInfo(proxyModel.mapToSource(index)).isFile()) {
825 return index;
826 }
827 if (proxyModel.hasChildren(index)) {
828 return firstFile(index);
829 }
830 }
831 return index;
832}
833
839void MainWindow::setPassword(const QString &file, bool isNew) {
840 PasswordDialog d(file, isNew, this);
841
842 if (!d.exec()) {
843 ui->treeView->setFocus();
844 }
845}
846
851void MainWindow::addPassword() {
852 bool ok;
853 QString dir =
854 Util::getDir(ui->treeView->currentIndex(), true, model, proxyModel);
855 QString file =
856 QInputDialog::getText(this, tr("New file"),
857 tr("New password file: \n(Will be placed in %1 )")
859 Util::getDir(ui->treeView->currentIndex(),
860 true, model, proxyModel)),
861 QLineEdit::Normal, "", &ok);
862 if (!ok || file.isEmpty()) {
863 return;
864 }
865 file = dir + file;
866 setPassword(file);
867}
868
873void MainWindow::onDelete() {
874 QModelIndex currentIndex = ui->treeView->currentIndex();
875 if (!currentIndex.isValid()) {
876 // This fixes https://github.com/IJHack/QtPass/issues/556
877 // Otherwise the entire password directory would be deleted if
878 // nothing is selected in the tree view.
879 return;
880 }
881
882 QFileInfo fileOrFolder =
883 model.fileInfo(proxyModel.mapToSource(ui->treeView->currentIndex()));
884 QString file = "";
885 bool isDir = false;
886
887 if (fileOrFolder.isFile()) {
888 file = getFile(ui->treeView->currentIndex(), true);
889 } else {
890 file = Util::getDir(ui->treeView->currentIndex(), true, model, proxyModel);
891 isDir = true;
892 }
893
894 QString dirMessage = tr(" and the whole content?");
895 if (isDir) {
896 QDirIterator it(model.rootPath() + QDir::separator() + file,
897 QDirIterator::Subdirectories);
898 bool okDir = true;
899 while (it.hasNext() && okDir) {
900 it.next();
901 if (QFileInfo(it.filePath()).isFile()) {
902 if (QFileInfo(it.filePath()).suffix() != "gpg") {
903 okDir = false;
904 dirMessage = tr(" and the whole content? <br><strong>Attention: "
905 "there are unexpected files in the given folder, "
906 "check them before continue.</strong>");
907 }
908 }
909 }
910 }
911
912 if (QMessageBox::question(
913 this, isDir ? tr("Delete folder?") : tr("Delete password?"),
914 tr("Are you sure you want to delete %1%2?")
915 .arg(QDir::separator() + file, isDir ? dirMessage : "?"),
916 QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) {
917 return;
918 }
919
920 QtPassSettings::getPass()->Remove(file, isDir);
921}
922
926void MainWindow::onOtp() {
927 QString file = getFile(ui->treeView->currentIndex(), true);
928 if (!file.isEmpty()) {
931 QtPassSettings::getPass()->OtpGenerate(file);
932 }
933 } else {
934 flashText(tr("No password selected for OTP generation"), true);
935 }
936}
937
941void MainWindow::onEdit() {
942 QString file = getFile(ui->treeView->currentIndex(), true);
943 editPassword(file);
944}
945
950void MainWindow::userDialog(const QString &dir) {
951 if (!dir.isEmpty()) {
952 currentDir = dir;
953 }
954 onUsers();
955}
956
962void MainWindow::onUsers() {
963 QString dir =
964 currentDir.isEmpty()
965 ? Util::getDir(ui->treeView->currentIndex(), false, model, proxyModel)
966 : currentDir;
967
968 UsersDialog d(dir, this);
969 if (!d.exec()) {
970 ui->treeView->setFocus();
971 }
972}
973
978void MainWindow::messageAvailable(const QString &message) {
979 if (message.isEmpty()) {
980 focusInput();
981 } else {
982 ui->treeView->expandAll();
983 ui->lineEdit->setText(message);
984 on_lineEdit_returnPressed();
985 }
986 show();
987 raise();
988}
989
995void MainWindow::generateKeyPair(const QString &batch, QDialog *keygenWindow) {
996 keygen = keygenWindow;
997 emit generateGPGKeyPair(batch);
998}
999
1004void MainWindow::updateProfileBox() {
1005 QHash<QString, QHash<QString, QString>> profiles =
1007
1008 if (profiles.isEmpty()) {
1009 ui->profileWidget->hide();
1010 } else {
1011 ui->profileWidget->show();
1012 ui->profileBox->setEnabled(profiles.size() > 1);
1013 ui->profileBox->clear();
1014 QHashIterator<QString, QHash<QString, QString>> i(profiles);
1015 while (i.hasNext()) {
1016 i.next();
1017 if (!i.key().isEmpty()) {
1018 ui->profileBox->addItem(i.key());
1019 }
1020 }
1021 ui->profileBox->model()->sort(0);
1022 }
1023 int index = ui->profileBox->findText(QtPassSettings::getProfile());
1024 if (index != -1) { // -1 for not found
1025 ui->profileBox->setCurrentIndex(index);
1026 }
1027}
1028
1034#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1035void MainWindow::on_profileBox_currentIndexChanged(QString name) {
1036#else
1048void MainWindow::on_profileBox_currentTextChanged(const QString &name) {
1049#endif
1050 if (m_qtPass->isFreshStart() || name == QtPassSettings::getProfile()) {
1051 return;
1052 }
1053
1054 ui->lineEdit->clear();
1055
1057
1059 QtPassSettings::getProfiles().value(name).value("path"));
1061 QtPassSettings::getProfiles().value(name).value("signingKey"));
1062 ui->statusBar->showMessage(tr("Profile changed to %1").arg(name), 2000);
1063
1064 QtPassSettings::getPass()->updateEnv();
1065
1066 const QString passStore = QtPassSettings::getPassStore();
1067 proxyModel.setStore(passStore);
1068 ui->treeView->setRootIndex(
1069 proxyModel.mapFromSource(model.setRootPath(passStore)));
1070 deselect();
1071 ui->treeView->setCurrentIndex(QModelIndex());
1072}
1073
1079void MainWindow::initTrayIcon() {
1080 this->tray = new TrayIcon(this);
1081 // Setup tray icon
1082
1083 if (tray == nullptr) {
1084#ifdef QT_DEBUG
1085 dbg() << "Allocating tray icon failed.";
1086#endif
1087 }
1088
1089 if (!tray->getIsAllocated()) {
1090 destroyTrayIcon();
1091 }
1092}
1093
1097void MainWindow::destroyTrayIcon() {
1098 delete this->tray;
1099 tray = nullptr;
1100}
1101
1106void MainWindow::closeEvent(QCloseEvent *event) {
1108 this->hide();
1109 event->ignore();
1110 } else {
1111 m_qtPass->clearClipboard();
1112
1113 QtPassSettings::setGeometry(saveGeometry());
1114 QtPassSettings::setSavestate(saveState());
1115 QtPassSettings::setMaximized(isMaximized());
1116 if (!isMaximized()) {
1119 }
1120 event->accept();
1121 }
1122}
1123
1131auto MainWindow::eventFilter(QObject *obj, QEvent *event) -> bool {
1132 if (obj == ui->lineEdit && event->type() == QEvent::KeyPress) {
1133 auto *key = dynamic_cast<QKeyEvent *>(event);
1134 if (key != nullptr && key->key() == Qt::Key_Down) {
1135 ui->treeView->setFocus();
1136 }
1137 }
1138 return QObject::eventFilter(obj, event);
1139}
1140
1145void MainWindow::keyPressEvent(QKeyEvent *event) {
1146 switch (event->key()) {
1147 case Qt::Key_Delete:
1148 onDelete();
1149 break;
1150 case Qt::Key_Return:
1151 case Qt::Key_Enter:
1152 if (proxyModel.rowCount() > 0) {
1153 on_treeView_clicked(ui->treeView->currentIndex());
1154 }
1155 break;
1156 case Qt::Key_Escape:
1157 ui->lineEdit->clear();
1158 break;
1159 default:
1160 break;
1161 }
1162}
1163
1169void MainWindow::showContextMenu(const QPoint &pos) {
1170 QModelIndex index = ui->treeView->indexAt(pos);
1171 bool selected = true;
1172 if (!index.isValid()) {
1173 ui->treeView->clearSelection();
1174 ui->actionDelete->setEnabled(false);
1175 ui->actionEdit->setEnabled(false);
1176 currentDir = "";
1177 selected = false;
1178 }
1179
1180 ui->treeView->setCurrentIndex(index);
1181
1182 QPoint globalPos = ui->treeView->viewport()->mapToGlobal(pos);
1183
1184 QFileInfo fileOrFolder =
1185 model.fileInfo(proxyModel.mapToSource(ui->treeView->currentIndex()));
1186
1187 QMenu contextMenu;
1188 if (!selected || fileOrFolder.isDir()) {
1189 QAction *openFolder =
1190 contextMenu.addAction(tr("Open folder with file manager"));
1191 QAction *addFolder = contextMenu.addAction(tr("Add folder"));
1192 QAction *addPassword = contextMenu.addAction(tr("Add password"));
1193 QAction *users = contextMenu.addAction(tr("Users"));
1194 connect(openFolder, &QAction::triggered, this, &MainWindow::openFolder);
1195 connect(addFolder, &QAction::triggered, this, &MainWindow::addFolder);
1196 connect(addPassword, &QAction::triggered, this, &MainWindow::addPassword);
1197 connect(users, &QAction::triggered, this, &MainWindow::onUsers);
1198 } else if (fileOrFolder.isFile()) {
1199 QAction *edit = contextMenu.addAction(tr("Edit"));
1200 connect(edit, &QAction::triggered, this, &MainWindow::onEdit);
1201 }
1202 if (selected) {
1203 contextMenu.addSeparator();
1204 if (fileOrFolder.isDir()) {
1205 QAction *renameFolder = contextMenu.addAction(tr("Rename folder"));
1206 connect(renameFolder, &QAction::triggered, this,
1207 &MainWindow::renameFolder);
1208 } else if (fileOrFolder.isFile()) {
1209 QAction *renamePassword = contextMenu.addAction(tr("Rename password"));
1210 connect(renamePassword, &QAction::triggered, this,
1211 &MainWindow::renamePassword);
1212 }
1213 QAction *deleteItem = contextMenu.addAction(tr("Delete"));
1214 connect(deleteItem, &QAction::triggered, this, &MainWindow::onDelete);
1215 if (fileOrFolder.isDir()) {
1216 QString dirPath = QDir::cleanPath(
1217 Util::getDir(ui->treeView->currentIndex(), false, model, proxyModel));
1218 QAction *reencrypt = contextMenu.addAction(tr("Re-encrypt"));
1219 connect(reencrypt, &QAction::triggered, this,
1220 [this, dirPath]() { reencryptPath(dirPath); });
1221 }
1222 }
1223 contextMenu.exec(globalPos);
1224}
1225
1231void MainWindow::showBrowserContextMenu(const QPoint &pos) {
1232 QMenu *contextMenu = ui->textBrowser->createStandardContextMenu(pos);
1233 QPoint globalPos = ui->textBrowser->viewport()->mapToGlobal(pos);
1234
1235 contextMenu->exec(globalPos);
1236 delete contextMenu;
1237}
1238
1242void MainWindow::openFolder() {
1243 QString dir =
1244 Util::getDir(ui->treeView->currentIndex(), false, model, proxyModel);
1245
1246 QString path = QDir::toNativeSeparators(dir);
1247 QDesktopServices::openUrl(QUrl::fromLocalFile(path));
1248}
1249
1253void MainWindow::addFolder() {
1254 bool ok;
1255 QString dir =
1256 Util::getDir(ui->treeView->currentIndex(), false, model, proxyModel);
1257 QString newdir =
1258 QInputDialog::getText(this, tr("New file"),
1259 tr("New Folder: \n(Will be placed in %1 )")
1261 Util::getDir(ui->treeView->currentIndex(),
1262 true, model, proxyModel)),
1263 QLineEdit::Normal, "", &ok);
1264 if (!ok || newdir.isEmpty()) {
1265 return;
1266 }
1267 newdir.prepend(dir);
1268 if (!QDir().mkdir(newdir)) {
1269 QMessageBox::warning(this, tr("Error"),
1270 tr("Failed to create folder: %1").arg(newdir));
1271 return;
1272 }
1273 if (QtPassSettings::isAddGPGId(true)) {
1274 QString gpgIdFile = newdir + "/.gpg-id";
1275 QFile gpgId(gpgIdFile);
1276 if (!gpgId.open(QIODevice::WriteOnly)) {
1277 QMessageBox::warning(
1278 this, tr("Error"),
1279 tr("Failed to create .gpg-id file in: %1").arg(newdir));
1280 return;
1281 }
1282 QList<UserInfo> users = QtPassSettings::getPass()->listKeys("", true);
1283 for (const UserInfo &user : users) {
1284 if (user.enabled) {
1285 gpgId.write((user.key_id + "\n").toUtf8());
1286 }
1287 }
1288 gpgId.close();
1289 }
1290}
1291
1295void MainWindow::renameFolder() {
1296 bool ok;
1297 QString srcDir = QDir::cleanPath(
1298 Util::getDir(ui->treeView->currentIndex(), false, model, proxyModel));
1299 QString srcDirName = QDir(srcDir).dirName();
1300 QString newName =
1301 QInputDialog::getText(this, tr("Rename file"), tr("Rename Folder To: "),
1302 QLineEdit::Normal, srcDirName, &ok);
1303 if (!ok || newName.isEmpty()) {
1304 return;
1305 }
1306 QString destDir = srcDir;
1307 destDir.replace(srcDir.lastIndexOf(srcDirName), srcDirName.length(), newName);
1308 QtPassSettings::getPass()->Move(srcDir, destDir);
1309}
1310
1315void MainWindow::editPassword(const QString &file) {
1316 if (!file.isEmpty()) {
1318 onUpdate(true);
1319 }
1320 setPassword(file, false);
1321 }
1322}
1323
1327void MainWindow::renamePassword() {
1328 bool ok;
1329 QString file = getFile(ui->treeView->currentIndex(), false);
1330 QString filePath = QFileInfo(file).path();
1331 QString fileName = QFileInfo(file).fileName();
1332 if (fileName.endsWith(".gpg", Qt::CaseInsensitive)) {
1333 fileName.chop(4);
1334 }
1335
1336 QString newName =
1337 QInputDialog::getText(this, tr("Rename file"), tr("Rename File To: "),
1338 QLineEdit::Normal, fileName, &ok);
1339 if (!ok || newName.isEmpty()) {
1340 return;
1341 }
1342 QString newFile = QDir(filePath).filePath(newName);
1343 QtPassSettings::getPass()->Move(file, newFile);
1344}
1345
1350void MainWindow::clearTemplateWidgets() {
1351 while (ui->gridLayout->count() > 0) {
1352 QLayoutItem *item = ui->gridLayout->takeAt(0);
1353 delete item->widget();
1354 delete item;
1355 }
1356 ui->verticalLayoutPassword->setSpacing(0);
1357}
1358
1367void MainWindow::copyPasswordFromTreeview() {
1368 QFileInfo fileOrFolder =
1369 model.fileInfo(proxyModel.mapToSource(ui->treeView->currentIndex()));
1370
1371 if (fileOrFolder.isFile()) {
1372 QString file = getFile(ui->treeView->currentIndex(), true);
1373 // Disconnect any previous connection to avoid accumulation
1374 disconnect(QtPassSettings::getPass(), &Pass::finishedShow, this,
1375 &MainWindow::passwordFromFileToClipboard);
1377 &MainWindow::passwordFromFileToClipboard);
1378 QtPassSettings::getPass()->Show(file);
1379 }
1380}
1381
1382void MainWindow::passwordFromFileToClipboard(const QString &text) {
1383 QStringList tokens = text.split('\n');
1384 m_qtPass->copyTextToClipboard(tokens[0]);
1385}
1386
1393void MainWindow::addToGridLayout(int position, const QString &field,
1394 const QString &value) {
1395 QString trimmedField = field.trimmed();
1396 QString trimmedValue = value.trimmed();
1397
1398 const QString buttonStyle =
1399 "border-style: none; background: transparent; padding: 0; margin: 0; "
1400 "icon-size: 16px; color: inherit;";
1401
1402 // Combine the Copy button and the line edit in one widget
1403 auto *frame = new QFrame();
1404 QLayout *ly = new QHBoxLayout();
1405 ly->setContentsMargins(5, 2, 2, 2);
1406 ly->setSpacing(0);
1407 frame->setLayout(ly);
1409 auto *fieldLabel = new QPushButtonWithClipboard(trimmedValue, this);
1410 connect(fieldLabel, &QPushButtonWithClipboard::clicked, m_qtPass,
1412
1413 fieldLabel->setStyleSheet(buttonStyle);
1414 frame->layout()->addWidget(fieldLabel);
1415 }
1416
1418 auto *qrbutton = new QPushButtonAsQRCode(trimmedValue, this);
1419 connect(qrbutton, &QPushButtonAsQRCode::clicked, m_qtPass,
1421 qrbutton->setStyleSheet(buttonStyle);
1422 frame->layout()->addWidget(qrbutton);
1423 }
1424
1425 // set the echo mode to password, if the field is "password"
1426 const QString lineStyle =
1428 ? "border-style: none; background: transparent; font-family: "
1429 "monospace;"
1430 : "border-style: none; background: transparent;";
1431
1432 if (QtPassSettings::isHidePassword() && trimmedField == tr("Password")) {
1433 auto *line = new QLineEdit();
1434 line->setObjectName(trimmedField);
1435 line->setText(trimmedValue);
1436 line->setReadOnly(true);
1437 line->setStyleSheet(lineStyle);
1438 line->setContentsMargins(0, 0, 0, 0);
1439 line->setEchoMode(QLineEdit::Password);
1440 auto *showButton = new QPushButtonShowPassword(line, this);
1441 showButton->setStyleSheet(buttonStyle);
1442 showButton->setContentsMargins(0, 0, 0, 0);
1443 frame->layout()->addWidget(showButton);
1444 frame->layout()->addWidget(line);
1445 } else {
1446 auto *line = new QTextBrowser();
1447 line->setOpenExternalLinks(true);
1448 line->setOpenLinks(true);
1449 line->setMaximumHeight(26);
1450 line->setMinimumHeight(26);
1451 line->setSizePolicy(
1452 QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum));
1453 line->setObjectName(trimmedField);
1454 trimmedValue.replace(Util::protocolRegex(), R"(<a href="\1">\1</a>)");
1455 line->setText(trimmedValue);
1456 line->setReadOnly(true);
1457 line->setStyleSheet(lineStyle);
1458 line->setContentsMargins(0, 0, 0, 0);
1459 frame->layout()->addWidget(line);
1460 }
1461
1462 frame->setStyleSheet(
1463 ".QFrame{border: 1px solid lightgrey; border-radius: 5px;}");
1464
1465 // set into the layout
1466 ui->gridLayout->addWidget(new QLabel(trimmedField), position, 0);
1467 ui->gridLayout->addWidget(frame, position, 1);
1468}
1469
1476void MainWindow::showStatusMessage(const QString &msg, int timeout) {
1477 ui->statusBar->showMessage(msg, timeout);
1478}
1479
1484void MainWindow::reencryptPath(const QString &dir) {
1485 QDir checkDir(dir);
1486 if (!checkDir.exists()) {
1487 QMessageBox::critical(this, tr("Error"),
1488 tr("Directory does not exist: %1").arg(dir));
1489 return;
1490 }
1491
1492 int ret = QMessageBox::question(
1493 this, tr("Re-encrypt passwords"),
1494 tr("Re-encrypt all passwords in %1?\n\n"
1495 "This will re-encrypt ALL password files in this folder "
1496 "using the current recipients defined in .gpg-id.\n\n"
1497 "This may rewrite many files and cannot be undone easily.\n\n"
1498 "Continue?")
1499 .arg(QDir(dir).dirName()),
1500 QMessageBox::Yes | QMessageBox::No);
1501
1502 if (ret != QMessageBox::Yes)
1503 return;
1504
1505 // Prevent double execution - use same method as startReencryptPath
1506 setUiElementsEnabled(false);
1507 ui->treeView->setDisabled(true);
1508
1509 QtPassSettings::getImitatePass()->reencryptPath(
1510 QDir::cleanPath(QDir(dir).absolutePath()));
1511}
1512
1517 setUiElementsEnabled(false);
1518 ui->treeView->setDisabled(true);
1519}
1520
1525
1526void MainWindow::updateGitButtonVisibility() {
1527 if (!QtPassSettings::isUseGit() ||
1528 (QtPassSettings::getGitExecutable().isEmpty() &&
1529 QtPassSettings::getPassExecutable().isEmpty())) {
1530 enableGitButtons(false);
1531 } else {
1532 enableGitButtons(true);
1533 }
1534}
1535
1536void MainWindow::updateOtpButtonVisibility() {
1537#if defined(Q_OS_WIN) || defined(__APPLE__)
1538 ui->actionOtp->setVisible(false);
1539#endif
1540 if (!QtPassSettings::isUseOtp()) {
1541 ui->actionOtp->setEnabled(false);
1542 } else {
1543 ui->actionOtp->setEnabled(true);
1544 }
1545}
1546
1547void MainWindow::updateGrepButtonVisibility() {
1548 const bool enabled = QtPassSettings::isUseGrepSearch();
1549 ui->grepButton->setVisible(enabled);
1550 ui->grepCaseButton->setVisible(enabled);
1551 if (!enabled && m_grepMode) {
1552 ui->grepButton->setChecked(false);
1553 }
1554}
1555
1556void MainWindow::enableGitButtons(const bool &state) {
1557 // Following GNOME guidelines is preferable disable buttons instead of hide
1558 ui->actionPush->setEnabled(state);
1559 ui->actionUpdate->setEnabled(state);
1560}
1561
1567void MainWindow::critical(const QString &title, const QString &msg) {
1568 QMessageBox::critical(this, title, msg);
1569}
The ConfigDialog handles the configuration interface.
void emptyClicked()
emptyClicked event
auto getNamedValues() const -> NamedValues
Gets named value pairs from the parsed file.
auto getRemainingDataForDisplay() const -> QString
Gets remaining data for display (excludes hidden fields like OTP).
auto getPassword() const -> QString
Gets the password from the parsed file.
static auto parse(const QString &fileContent, const QStringList &templateFields, bool allFields) -> FileContent
parse parses the given fileContent in a FileContent object. The password is accessible through getPas...
void startReencryptPath()
MainWindow::startReencryptPath disable ui elements and treeview.
void closeEvent(QCloseEvent *event) override
MainWindow::closeEvent hide or quit.
void passShowHandler(const QString &)
~MainWindow() override
void endReencryptPath()
MainWindow::endReencryptPath re-enable ui elements.
void onGrepFinished(const QList< QPair< QString, QStringList > > &results)
Display grep results in grepResultsList.
void executeWrapperStarted()
void generateKeyPair(const QString &, QDialog *)
MainWindow::generateKeyPair internal gpg keypair generator . .
void changeEvent(QEvent *event) override
MainWindow::changeEvent sets focus to the search box.
void critical(const QString &, const QString &)
MainWindow::critical critical message popup wrapper.
void messageAvailable(const QString &message)
MainWindow::messageAvailable we have some text/message/search to do.
MainWindow(const QString &searchText=QString(), QWidget *parent=nullptr)
MainWindow::MainWindow handles all of the main functionality and also the main window.
void keyPressEvent(QKeyEvent *event) override
MainWindow::keyPressEvent did anyone press return, enter or escape?
void onPush()
MainWindow::onPush do a git push.
void showStatusMessage(const QString &msg, int timeout=2000)
Displays message in status bar.
void passOtpHandler(const QString &)
void passShowHandlerFinished(const QString &output)
void generateGPGKeyPair(const QString &batch)
void restoreWindow()
auto eventFilter(QObject *obj, QEvent *event) -> bool override
MainWindow::eventFilter filter out some events and focus the treeview.
void flashText(const QString &text, const bool isError, const bool isHtml=false)
void userDialog(const QString &="")
MainWindow::userDialog see MainWindow::onUsers().
void setUiElementsEnabled(bool state)
MainWindow::setUiElementsEnabled enable or disable the relevant UI elements.
auto getCurrentTreeViewIndex() -> QModelIndex
void cleanKeygenDialog()
void deselect()
MainWindow::deselect clear the selection, password and copy buffer.
void on_treeView_clicked(const QModelIndex &index)
MainWindow::on_treeView_clicked read the selected password file.
void config()
The NamedValues class is mostly a list of NamedValue but also has a method to take a specific NamedVa...
Definition filecontent.h:23
void finishedShow(const QString &)
Emitted when show finishes.
void clicked(const QString &)
void clicked(const QString &)
void copyTextToClipboard(const QString &text)
MainWindow::copyTextToClipboard copies text to your clipboard.
Definition qtpass.cpp:500
void showTextAsQRCode(const QString &text)
displays the text as qrcode
Definition qtpass.cpp:522
static void setMaximized(const bool &maximized)
Save maximized state.
static auto isStartMinimized(const bool &defaultValue=QVariant().toBool()) -> bool
Check whether application should start minimized.
static auto isUseOtp(const bool &defaultValue=QVariant().toBool()) -> bool
Check whether OTP support is enabled.
static void setProfile(const QString &profile)
Save active profile name.
static auto isNoLineWrapping(const bool &defaultValue=QVariant().toBool()) -> bool
Get whether to disable line wrapping.
static void setPassStore(const QString &passStore)
Save password store path.
static auto isHideContent(const bool &defaultValue=QVariant().toBool()) -> bool
Get whether to hide content (password + username).
static auto getSize(const QSize &defaultValue=QVariant().toSize()) -> QSize
Get saved window size.
static auto getPass() -> Pass *
Get currently active pass backend instance.
static auto isUseQrencode(const bool &defaultValue=QVariant().toBool()) -> bool
Check whether qrencode support is enabled.
static auto isUseAutoclearPanel(const bool &defaultValue=QVariant().toBool()) -> bool
Get whether to use panel autoclear.
static auto isAutoPull(const bool &defaultValue=QVariant().toBool()) -> bool
Check whether automatic pull is enabled.
static auto isUseGit(const bool &defaultValue=QVariant().toBool()) -> bool
Check whether Git integration is enabled.
static auto getClipBoardType(const Enums::clipBoardType &defaultValue=Enums::CLIPBOARD_NEVER) -> Enums::clipBoardType
Get clipboard type as enum.
static auto isTemplateAllFields(const bool &defaultValue=QVariant().toBool()) -> bool
Check whether template applies to all fields.
static void setPassSigningKey(const QString &passSigningKey)
Save GPG signing key.
static auto getPassStore(const QString &defaultValue=QVariant().toString()) -> QString
Get password store directory path.
static auto isUseTemplate(const bool &defaultValue=QVariant().toBool()) -> bool
Check whether template usage is enabled.
static auto isUseTrayIcon(const bool &defaultValue=QVariant().toBool()) -> bool
Check whether tray icon support is enabled.
static auto isAddGPGId(const bool &defaultValue=QVariant().toBool()) -> bool
Get whether to auto-add GPG ID when receiving files.
static auto isUseGrepSearch(const bool &defaultValue=false) -> bool
Check whether content search (pass grep) is enabled.
static void setPos(const QPoint &pos)
Save window position.
static auto getPassTemplate(const QString &defaultValue=QVariant().toString()) -> QString
Get pass entry template.
static auto isMaximized(const bool &defaultValue=QVariant().toBool()) -> bool
Get maximized state.
static auto getProfile(const QString &defaultValue=QVariant().toString()) -> QString
Get active profile name.
static auto isUseMonospace(const bool &defaultValue=QVariant().toBool()) -> bool
Get whether to use monospace font.
static void setUsePass(const bool &usePass)
Save use pass setting.
static auto getAutoclearPanelSeconds(const int &defaultValue=QVariant().toInt()) -> int
Get panel autoclear delay in seconds.
static void setSavestate(const QByteArray &saveState)
Save window state.
static auto isAlwaysOnTop(const bool &defaultValue=QVariant().toBool()) -> bool
Check whether main window should stay always on top.
static auto isHidePassword(const bool &defaultValue=QVariant().toBool()) -> bool
Get whether to hide password in UI.
static auto getPassExecutable(const QString &defaultValue=QVariant().toString()) -> QString
Get pass executable path.
static auto getPos(const QPoint &defaultValue=QVariant().toPoint()) -> QPoint
Get saved window position.
static auto getImitatePass() -> ImitatePass *
Get imitate pass backend instance.
static auto isHideOnClose(const bool &defaultValue=QVariant().toBool()) -> bool
Check whether closing the window hides the application.
static auto getProfiles() -> QHash< QString, QHash< QString, QString > >
Get all configured profiles.
static void setSize(const QSize &size)
Save window size.
static void setGeometry(const QByteArray &geometry)
Save window geometry.
static auto getGitExecutable(const QString &defaultValue=QVariant().toString()) -> QString
Get git executable path.
static auto getGeometry(const QByteArray &defaultValue=QVariant().toByteArray()) -> QByteArray
Get saved window geometry.
static auto isDisplayAsIs(const bool &defaultValue=QVariant().toBool()) -> bool
Get whether to display password as-is (no modification).
static auto getSavestate(const QByteArray &defaultValue=QVariant().toByteArray()) -> QByteArray
Get saved window state.
Dialog for selecting GPG recipients for password encryption.
Definition usersdialog.h:28
static auto protocolRegex() -> const QRegularExpression &
Returns a regex to match URL protocols.
Definition util.cpp:269
static auto endsWithGpg() -> const QRegularExpression &
Returns a regex to match .gpg file extensions.
Definition util.cpp:252
static auto findPasswordStore() -> QString
Locate the password store directory.
Definition util.cpp:77
static auto getDir(const QModelIndex &index, bool forPass, const QFileSystemModel &model, const StoreModel &storeModel) -> QString
Get the selected folder path, either relative to the configured pass store or absolute.
Definition util.cpp:234
static auto configIsValid() -> bool
Verify that the required configuration is complete.
Definition util.cpp:191
Debug utilities for QtPass.
#define dbg()
Simple debug macro that includes file and line number.
Definition debughelper.h:21
@ CLIPBOARD_NEVER
Definition enums.h:17
QString name
Definition filecontent.h:11
QString value
Definition filecontent.h:12
constexpr int MS_PER_SECOND
Definition util.h:12