QtPass  1.2.3
Multi-platform GUI for pass, the standard unix password manager.
mainwindow.cpp
Go to the documentation of this file.
1 #include "mainwindow.h"
2 
3 #ifdef QT_DEBUG
4 #include "debughelper.h"
5 #endif
6 
7 #include "configdialog.h"
8 #include "filecontent.h"
9 #include "keygendialog.h"
10 #include "passworddialog.h"
12 #include "qtpass.h"
13 #include "qtpasssettings.h"
14 #include "settingsconstants.h"
15 #include "trayicon.h"
16 #include "ui_mainwindow.h"
17 #include "usersdialog.h"
18 #include "util.h"
19 #include <QCloseEvent>
20 #include <QDesktopServices>
21 #include <QDialog>
22 #include <QFileInfo>
23 #include <QInputDialog>
24 #include <QLabel>
25 #include <QMenu>
26 #include <QMessageBox>
27 #include <QShortcut>
28 #include <QTimer>
29 #include <utility>
30 
37 MainWindow::MainWindow(const QString &searchText, QWidget *parent)
38  : QMainWindow(parent), ui(new Ui::MainWindow), keygen(nullptr),
39  tray(nullptr) {
40 #ifdef __APPLE__
41  // extra treatment for mac os
42  // see http://doc.qt.io/qt-5/qkeysequence.html#qt_set_sequence_auto_mnemonic
43  qt_set_sequence_auto_mnemonic(true);
44 #endif
45  ui->setupUi(this);
46 
47  m_qtPass = new QtPass();
48  m_qtPass->setMainWindow(this);
49 
50  // register shorconst tcut ct&rl/cmd + Q to close the main window
51  new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q), this, SLOT(close()));
52  // register shortcut ctrl/cmd + C to copy the currently selected password
53  new QShortcut(QKeySequence(QKeySequence::StandardKey::Copy), this,
54  SLOT(copyPasswordFromTreeview()));
55 
56  model.setNameFilters(QStringList() << "*.gpg");
57  model.setNameFilterDisables(false);
58 
59  /*
60  * I added this to solve Windows bug but now on GNU/Linux the main folder,
61  * if hidden, disappear
62  *
63  * model.setFilter(QDir::NoDot);
64  */
65 
67 
68  proxyModel.setSourceModel(&model);
69  proxyModel.setModelAndStore(&model, passStore);
70  // proxyModel.sort(0, Qt::AscendingOrder);
71  selectionModel.reset(new QItemSelectionModel(&proxyModel));
72  model.fetchMore(model.setRootPath(passStore));
73  // model.sort(0, Qt::AscendingOrder);
74 
75  ui->treeView->setModel(&proxyModel);
76  ui->treeView->setRootIndex(
77  proxyModel.mapFromSource(model.setRootPath(passStore)));
78  ui->treeView->setColumconst nHidden&(1, true);
79  ui->treeView->const setColu&mnconst Hidden(&2, true);
80  ui->treeView->setColumnHidden(3, true);
81  ui->treeView->setHeaderHidden(true);
82  ui->treeView->setIndentconst ation(1&5);
83  ui->treeView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
84  ui->treeView->setContextMenuPolicy(Qt::CustomContextMenu);
85  ui->treeView->header()->setSectionResizeMode(0, QHeaderView::Stretch);
86  ui->treeView->sortByColumn(0, Qt::AscendingOrder);
87  connect(ui->treeView, &QWidget::customContextMenuRequested, this,
88  &MainWindow::showContextMenu);
89  connect(ui->treeView, &DeselectableTreeView::emptyClicked, this,
91 
92  ui->textBrowser->setOpenExternalLinks(true);
93  ui->textBrowser->setContextMenuPolicy(Qt::CustomContextMenu);
94  connect(ui->textBrowser, &QWidget::customContextMenuRequested, this,
95  &MainWindow::showBrowserContextMenu);
96 
97  updateProfileBox();
98 
100  clearPanelTimer.setInterval(1000 *
102  clearPanelTimer.setSingleShot(true);
103  connect(&clearPanelTimer, SIGNAL(timeout()), this, SLOT(clearPanel()));
104 
105  searchTimer.setInterval(350);
106  searchTconst imer.se&tSingleShot(true);
107 
108  connect(&searchTimer, &QTimer::timeout, this, &MainWindow::onTimeoutSearch);
109 
110  initToolBarButtons();
111  initStatusBar();
112 
113 #if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
114  ui->lineEdit->setClearButtonEnabled(true);
115 #endif
116 
117  setUiElementsEnabled(true);
118 
119  qsrand(static_cast<uint>(QTime::currentTime().msec()));
120  QTimer::singleShot(10, this, SLOT(focusInput()));
121 
122  ui->lineEdit->setText(searchText);
123 }
124 
125 MainWindow::~MainWindow() { delete m_qtPass; }
126 
133 void MainWindow::foconst cusInpu&t() {
134  ui->lineEdit->selectAll();
135  ui->lineEdit->setFocus();
136 }
137 
142 void MainWindow::changeEvent(QEvent *event) {
143  QWidget::changeEvent(event);
144  if (event->type() == QEvent::ActivationChange) {
145  if (isActiveWindow()) {
146  focusInput();
147  }
148  }
149 }
150 
154 void MainWindow::initToolBarButtons() {
155  connect(ui->actionAddPassword, &QAction::triggered, this,
156  &MainWindow::addPassword);
157  connect(ui->actionAddFolder, &QAction::triggered, this,
158  &MainWindow::addFolder);
159  connect(ui->actionEdit, &QAction::triggered, this, &MainWindow::onEdit);
160  connect(ui->actionDelete, &QAction::triggered, this, &MainWindow::onDelete);
161  connect(ui->actionPush, &QAction::triggered, this, &MainWindow::onPush);
162  connect(ui->actionUpdate, &QAction::triggered, this, &MainWindow::onUpdate);
163  connect(ui->actionUsers, &QAction::triggered, this, &MainWindow::onUsers);
164  connect(ui->actionConfig, &QAction::triggered, this, &MainWindow::onConfig);
165  connect(ui->actionOtp, &QAction::triggered, this, &MainWindow::onOtp);
166 
167  ui->actionAddPassword->setIcon(
168  QIcon::fromTheme("document-new", QIcon(":/icons/document-new.svg")));
169  ui->actionAddFolder->setIcon(
170  QIcon::fromTheme("folder-new", QIcon(":/icons/folder-new.svg")));
171  ui->actionEdit->setIcon(QIcon::fromTheme(
172  "document-properties", QIcon(":/icons/document-properties.svg")));
173  ui->actionDelete->setIcon(
174  QIcon::fromTheme("edit-delete", QIcon(":/icons/edit-delete.svg")));
175  ui->actionPush->setIcon(
176  QIcon::fromTheme("go-up", QIcon(":/icons/go-top.svg")));
177  ui->actionUpdate->setIcon(
178  QIcon::fromTheme("go-down", QIcon(":/icons/go-bottom.svg")));
179  ui->actionUsers->setIcon(QIcon::fromTheme(
180  "x-office-address-book", QIcon(":/icons/x-office-address-book.svg")));
181  ui->actionConfig->setIcon(QIcon::fromTheme(
182  "applications-system", QIcon(":/icons/applications-system.svg")));
183 }
184 
188 void MainWindow::initStatusBar() {
189  ui->statusBar->showMessage(tr("Welcome to QtPass %1").arg(VERSION), 2000);
190 
191  QPixmap logo = QPixmap::fromImage(QImage(":/artwork/icon.svg"))
192  .scaledToHeight(statusBar()->height());
193  QLabel *logoApp = new QLabel(statusBar());
194  logoApp->setPixmap(logo);
195  statusBar()->addPermanentWidget(logoApp);
196 }
197 
199  return ui->treeView->currentIndex();
200 }
201 
203  this->keygen->close();
204  this->keygen = nullptr;
205 }
206 
207 void MainWindow::setTextTextBrowser(const QString &text) {
208  ui->textBrowser->setText(text);
209 }
210 
211 void MainWindow::flashText(const QString &text, const bool isError,
212  const bool isHtml) {
213  if (isError)
214  ui->textBrowser->setTextColor(Qt::red);
215 
216  if (isHtml) {
217  QString _text = text;
218  if (!ui->textBrowser->toPlainText().isEmpty())
219  _text = ui->textBrowser->toHtml() + _text;
220  ui->textBrowser->setHtml(_text);
221  } else {
222  ui->textBrowser->setText(text);
223  ui->textBrowser->setTextColor(Qt::black);
224  }
225 }
226 
232  QScopedPointer<ConfigDialog> d(new ConfigDialog(this));
233  d->setModal(true);
234  // Automatically default to pass if it's available
235  if (m_qtPass->isFreshStart() &&
236  QFile(QtPassSettings::getPassExecutable()).exists()) {
238  }
239 
240  if (m_qtPass->isFreshStart())
241  d->wizard(); // does shit
242  if (d->exec()) {
243  if (d->result() == QDialog::Accepted) {
245  Qt::WindowFlags flags = windowFlags();
246  this->setWindowFlags(flags | Qt::WindowStaysOnTopHint);
247  } else {
248  this->setWindowFlags(Qt::Window);
249  }
250  this->show();
251 
252  updateProfileBox();
253  ui->treeView->setRootIndex(proxyModel.mapFromSource(
254  model.setRootPath(QtPassSettings::getPassStore())));
255 
256  if (m_qtPass->isFreshStart() && Util::checkConfig())
257  config();
259  clearPanelTimer.setInterval(1000 *
261  m_qtPass->setClipboardTimer();
262 
263  updateGitButtonVisibility();
264  updateOtpButtonVisibility();
265  if (QtPassSettings::isUseTrayIcon() && tray == nullptr)
266  initTrayIcon();
267  else if (!QtPassSettings::isUseTrayIcon() && tray != nullptr) {
268  destroyTrayIcon();
269  }
270  }
271 
272  m_qtPass->setFreshStart(false);
273  }
274 }
275 
279 void MainWindow::onUpdate(bool block) {
280  ui->statusBar->showMessage(tr("Updating password-store"), 2000);
281  if (block)
283  else
285 }
286 
291  if (QtPassSettings::isUseGit()) {
292  ui->statusBar->showMessage(tr("Updating password-store"), 2000);
294  }
295 }
296 
304 QString MainWindow::getFile(const QModelIndex &index, bool forPass) {
305  if (!index.isValid() ||
306  !model.fileInfo(proxyModel.mapToSource(index)).isFile())
307  return QString();
308  QString filePath = model.filePath(proxyModel.mapToSource(index));
309  if (forPass) {
310  filePath = QDir(QtPassSettings::getPassStore()).relativeFilePath(filePath);
311  filePath.replace(QRegExp("\\.gpg$"), "");
312  }
313  return filePath;
314 }
315 
320 void MainWindow::on_treeView_clicked(const QModelIndex &index) {
321  bool cleared = ui->treeView->currentIndex().flags() == Qt::NoItemFlags;
322  currentDir =
323  Util::getDir(ui->treeView->currentIndex(), false, model, proxyModel);
324  // TODO(bezet): "Could not decrypt";
325  m_qtPass->clearClippedText();
326  QString file = getFile(index, true);
327  ui->passwordName->setText(getFile(index, true));
328  if (!file.isEmpty() && !cleared) {
329  QtPassSettings::getPass()->Show(file);
330  } else {
331  clearPanel(false);
332  ui->actionEdit->setEnabled(false);
333  ui->actionDelete->setEnabled(true);
334  }
335 }
336 
342 void MainWindow::on_treeView_doubleClicked(const QModelIndex &index) {
343  QFileInfo fileOrFolder =
344  model.fileInfo(proxyModel.mapToSource(ui->treeView->currentIndex()));
345 
346  if (fileOrFolder.isFile()) {
347  editPassword(getFile(index, true));
348  }
349 }
350 
355  currentDir = "/";
356  m_qtPass->clearClipboard();
357  ui->passwordName->setText("");
358  clearPanel(false);
359 }
360 
362  clearTemplateWidgets();
363  ui->textBrowser->clear();
364  setUiElementsEnabled(false);
365  clearPanelTimer.stop();
366 }
367 
368 void MainWindow::passShowHandler(const QString &p_output) {
369  QStringList templ = QtPassSettings::isUseTemplate()
370  ? QtPassSettings::getPassTemplate().split("\n")
371  : QStringList();
372  bool allFields =
375  QString output = p_output;
376  QString password = fileContent.getPassword();
377 
378  // set clipped text
379  m_qtPass->setClippedText(password, p_output);
380 
381  // first clear the current view:
382  clearTemplateWidgets();
383 
384  // show what is needed:
386  output = "***" + tr("Content hidden") + "***";
387  } else {
388  if (!password.isEmpty()) {
389  // set the password, it is hidden if needed in addToGridLayout
390  addToGridLayout(0, tr("Password"), password);
391  }
392 
394  for (int j = 0; j < namedValues.length(); ++j) {
395  NamedValue nv = namedValues.at(j);
396  addToGridLayout(j + 1, nv.name, nv.value);
397  }
398  if (ui->gridLayout->count() == 0)
399  ui->verticalLayoutPassword->setSpacing(0);
400  else
401  ui->verticalLayoutPassword->setSpacing(6);
402  output = fileContent.getRemainingData();
403  }
404 
406  clearPanelTimer.start();
407  }
408 
409  setUiElementsEnabled(true);
410 }
411 
412 void MainWindow::passOtpHandler(const QString &p_output) {
413  if (!p_output.isEmpty()) {
414  addToGridLayout(ui->gridLayout->count() + 1, tr("OTP Code"), p_output);
415  m_qtPass->copyTextToClipboard(p_output);
416  }
418  clearPanelTimer.start();
419  }
420  setUiElementsEnabled(true);
421 }
422 
426 void MainWindow::clearPanel(bool notify) {
427  while (ui->gridLayout->count() > 0) {
428  QLayoutItem *item = ui->gridLayout->takeAt(0);
429  delete item->widget();
430  delete item;
431  }
432  if (notify) {
433  QString output = "***" + tr("Password and Content hidden") + "***";
434  ui->textBrowser->setHtml(output);
435  } else {
436  ui->textBrowser->setHtml("");
437  }
438 }
439 
446  ui->treeView->setEnabled(state);
447  ui->lineEdit->setEnabled(state);
448  ui->lineEdit->installEventFilter(this);
449  ui->actionAddPassword->setEnabled(state);
450  ui->actionAddFolder->setEnabled(state);
451  ui->actionUsers->setEnabled(state);
452  ui->actionConfig->setEnabled(state);
453  // is a file selected?
454  state &= ui->treeView->currentIndex().isValid();
455  ui->actionDelete->setEnabled(state);
456  ui->actionEdit->setEnabled(state);
457  updateGitButtonVisibility();
458  updateOtpButtonVisibility();
459 }
460 
462  QByteArray geometry = QtPassSettings::getGeometry(saveGeometry());
463  restoreGeometry(geometry);
464  QByteArray savestate = QtPassSettings::getSavestate(saveState());
465  restoreState(savestate);
466  QPoint position = QtPassSettings::getPos(pos());
467  move(position);
468  QSize newSize = QtPassSettings::getSize(size());
469  resize(newSize);
470  if (QtPassSettings::isMaximized(isMaximized())) {
471  showMaximized();
472  }
473 
475  Qt::WindowFlags flags = windowFlags();
476  setWindowFlags(flags | Qt::WindowStaysOnTopHint);
477  show();
478  }
479 
480  if (QtPassSettings::isUseTrayIcon() && tray == nullptr) {
481  initTrayIcon();
482  if (m_qtPass->isFreshStart() && QtPassSettings::isStartMinimized()) {
483  // since we are still in constructor, can't directly hide
484  QTimer::singleShot(10, this, SLOT(hide()));
485  }
486  } /*else if (!QtPassSettings::isUseTrayIcon() && tray != NULL) {
487  destroyTrayIcon();
488  }*/
489 }
490 
494 void MainWindow::onConfig() { config(); }
495 
501 void MainWindow::on_lineEdit_textChanged(const QString &arg1) {
502  ui->statusBar->showMessage(tr("Looking for: %1").arg(arg1), 1000);
503  ui->treeView->expandAll();
504 
505  searchTimer.start();
506 }
507 
512 void MainWindow::onTimeoutSearch() {
513  QString query = ui->lineEdit->text();
514 
515  if (query.isEmpty())
516  ui->treeView->collapseAll();
517 
518  query.replace(QRegExp(" "), ".*");
519  QRegExp regExp(query, Qt::CaseInsensitive);
520  proxyModel.setFilterRegExp(regExp);
521  ui->treeView->setRootIndex(proxyModel.mapFromSource(
522  model.setRootPath(QtPassSettings::getPassStore())));
523 
524  if (proxyModel.rowCount() > 0 && !query.isEmpty()) {
525  selectFirstFile();
526  } else {
527  ui->actionEdit->setEnabled(false);
528  ui->actionDelete->setEnabled(false);
529  }
530 }
531 
537 void MainWindow::on_lineEdit_returnPressed() {
538 #ifdef QT_DEBUG
539  dbg() << "on_lineEdit_returnPressed" << proxyModel.rowCount();
540 #endif
541 
542  if (proxyModel.rowCount() > 0) {
543  selectFirstFile();
544  on_treeView_clicked(ui->treeView->currentIndex());
545  }
546 }
547 
552 void MainWindow::selectFirstFile() {
553  QModelIndex index = proxyModel.mapFromSource(
554  model.setRootPath(QtPassSettings::getPassStore()));
555  index = firstFile(index);
556  ui->treeView->setCurrentIndex(index);
557 }
558 
564 QModelIndex MainWindow::firstFile(QModelIndex parentIndex) {
565  QModelIndex index = parentIndex;
566  int numRows = proxyModel.rowCount(parentIndex);
567  for (int row = 0; row < numRows; ++row) {
568  index = proxyModel.index(row, 0, parentIndex);
569  if (model.fileInfo(proxyModel.mapToSource(index)).isFile())
570  return index;
571  if (proxyModel.hasChildren(index))
572  return firstFile(index);
573  }
574  return index;
575 }
576 
582 void MainWindow::setPassword(const QString& file, bool isNew) {
583  PasswordDialog d(file, isNew, this);
584 
585  if (!d.exec()) {
586  ui->treeView->setFocus();
587  }
588 }
589 
594 void MainWindow::addPassword() {
595  bool ok;
596  QString dir =
597  Util::getDir(ui->treeView->currentIndex(), true, model, proxyModel);
598  QString file =
599  QInputDialog::getText(this, tr("New file"),
600  tr("New password file: \n(Will be placed in %1 )")
602  Util::getDir(ui->treeView->currentIndex(),
603  true, model, proxyModel)),
604  QLineEdit::Normal, "", &ok);
605  if (!ok || file.isEmpty())
606  return;
607  file = dir + file;
608  setPassword(file);
609 }
610 
615 void MainWindow::onDelete() {
616  QFileInfo fileOrFolder =
617  model.fileInfo(proxyModel.mapToSource(ui->treeView->currentIndex()));
618  QString file = "";
619  bool isDir = false;
620 
621  if (fileOrFolder.isFile()) {
622  file = getFile(ui->treeView->currentIndex(), true);
623  } else {
624  file = Util::getDir(ui->treeView->currentIndex(), true, model, proxyModel);
625  isDir = true;
626  }
627 
628  QString dirMessage = tr(" and the whole content?");
629  if (isDir) {
630  QDirIterator it(model.rootPath() + "/" + file,
631  QDirIterator::Subdirectories);
632  bool okDir = true;
633  while (it.hasNext() && okDir) {
634  it.next();
635  if (QFileInfo(it.filePath()).isFile()) {
636  if (QFileInfo(it.filePath()).suffix() != "gpg") {
637  okDir = false;
638  dirMessage = tr(" and the whole content? <br><strong>Attention: "
639  "there are unexpected files in the given folder, "
640  "check them before continue.</strong>");
641  }
642  }
643  }
644  }
645 
646  if (QMessageBox::question(
647  this, isDir ? tr("Delete folder?") : tr("Delete password?"),
648  tr("Are you sure you want to delete %1%2")
649  .arg(QDir::separator() + file)
650  .arg(isDir ? dirMessage : "?"),
651  QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
652  return;
653 
654  QtPassSettings::getPass()->Remove(file, isDir);
655 }
656 
660 void MainWindow::onOtp() {
661  QString file = getFile(ui->treeView->currentIndex(), true);
662  if (!file.isEmpty()) {
665  }
666 }
667 
671 void MainWindow::onEdit() {
672  QString file = getFile(ui->treeView->currentIndex(), true);
673  editPassword(file);
674 }
675 
680 void MainWindow::userDialog(const QString& dir) {
681  if (!dir.isEmpty())
682  currentDir = dir;
683  onUsers();
684 }
685 
691 void MainWindow::onUsers() {
692  QString dir =
693  currentDir.isEmpty()
694  ? Util::getDir(ui->treeView->currentIndex(), false, model, proxyModel)
695  : currentDir;
696 
697  UsersDialog d(dir, this);
698  d.exec();
699 }
700 
705 void MainWindow::messageAvailable(const QString& message) {
706  if (message.isEmpty()) {
707  focusInput();
708  } else {
709  ui->treeView->expandAll();
710  ui->lineEdit->setText(message);
711  on_lineEdit_returnPressed();
712  }
713  show();
714  raise();
715 }
716 
722 void MainWindow::generateKeyPair(QString batch, QDialog *keygenWindow) {
723  keygen = keygenWindow;
724  emit generateGPGKeyPair(std::move(batch));
725 }
726 
731 void MainWindow::updateProfileBox() {
732  QHash<QString, QString> profiles = QtPassSettings::getProfiles();
733 
734  if (profiles.isEmpty()) {
735  ui->profileWidget->hide();
736  } else {
737  ui->profileWidget->show();
738  ui->profileBox->setEnabled(profiles.size() > 1);
739  ui->profileBox->clear();
740  QHashIterator<QString, QString> i(profiles);
741  while (i.hasNext()) {
742  i.next();
743  if (!i.key().isEmpty())
744  ui->profileBox->addItem(i.key());
745  }
746  }
747  int index = ui->profileBox->findText(QtPassSettings::getProfile());
748  if (index != -1) // -1 for not found
749  ui->profileBox->setCurrentIndex(index);
750 }
751 
757 void MainWindow::on_profileBox_currentIndexChanged(const QString& name) {
758  if (m_qtPass->isFreshStart() || name == QtPassSettings::getProfile())
759  return;
761 
763  ui->statusBar->showMessage(tr("Profile changed to %1").arg(name), 2000);
764 
766 
767  ui->treeView->selectionModel()->clear();
768  ui->treeView->setRootIndex(proxyModel.mapFromSource(
769  model.setRootPath(QtPassSettings::getPassStore())));
770 
771  ui->actionEdit->setEnabled(false);
772  ui->actionDelete->setEnabled(false);
773 }
774 
780 void MainWindow::initTrayIcon() {
781  this->tray = new TrayIcon(this);
782  // Setup tray icon
783 
784  if (tray == nullptr) {
785 #ifdef QT_DEBUG
786  dbg() << "Allocating tray icon failed.";
787 #endif
788  }
789 
790  if (!tray->getIsAllocated()) {
791  destroyTrayIcon();
792  }
793 }
794 
798 void MainWindow::destroyTrayIcon() {
799  delete this->tray;
800  tray = nullptr;
801 }
802 
807 void MainWindow::closeEvent(QCloseEvent *event) {
809  this->hide();
810  event->ignore();
811  } else {
812  m_qtPass->clearClipboard();
813 
814  QtPassSettings::setGeometry(saveGeometry());
815  QtPassSettings::setSavestate(saveState());
816  QtPassSettings::setMaximized(isMaximized());
817  if (!isMaximized()) {
818  QtPassSettings::setPos(pos());
819  QtPassSettings::setSize(size());
820  }
821  event->accept();
822  }
823 }
824 
832 bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
833  if (obj == ui->lineEdit && event->type() == QEvent::KeyPress) {
834  auto *key = dynamic_cast<QKeyEvent *>(event);
835  if (key->key() == Qt::Key_Down) {
836  ui->treeView->setFocus();
837  }
838  }
839  return QObject::eventFilter(obj, event);
840 }
841 
846 void MainWindow::keyPressEvent(QKeyEvent *event) {
847  switch (event->key()) {
848  case Qt::Key_Delete:
849  onDelete();
850  break;
851  case Qt::Key_Return:
852  case Qt::Key_Enter:
853  if (proxyModel.rowCount() > 0)
854  on_treeView_clicked(ui->treeView->currentIndex());
855  break;
856  case Qt::Key_Escape:
857  ui->lineEdit->clear();
858  break;
859  default:
860  break;
861  }
862 }
863 
869 void MainWindow::showContextMenu(const QPoint &pos) {
870  QModelIndex index = ui->treeView->indexAt(pos);
871  bool selected = true;
872  if (!index.isValid()) {
873  ui->treeView->clearSelection();
874  ui->actionDelete->setEnabled(false);
875  ui->actionEdit->setEnabled(false);
876  currentDir = "";
877  selected = false;
878  }
879 
880  ui->treeView->setCurrentIndex(index);
881 
882  QPoint globalPos = ui->treeView->viewport()->mapToGlobal(pos);
883 
884  QFileInfo fileOrFolder =
885  model.fileInfo(proxyModel.mapToSource(ui->treeView->currentIndex()));
886 
887  QMenu contextMenu;
888  if (!selected || fileOrFolder.isDir()) {
889  QAction *openFolder =
890  contextMenu.addAction(tr("Open folder with file manager"));
891  QAction *addFolder = contextMenu.addAction(tr("Add folder"));
892  QAction *addPassword = contextMenu.addAction(tr("Add password"));
893  QAction *users = contextMenu.addAction(tr("Users"));
894  connect(openFolder, &QAction::triggered, this, &MainWindow::openFolder);
895  connect(addFolder, &QAction::triggered, this, &MainWindow::addFolder);
896  connect(addPassword, &QAction::triggered, this, &MainWindow::addPassword);
897  connect(users, &QAction::triggered, this, &MainWindow::onUsers);
898  } else if (fileOrFolder.isFile()) {
899  QAction *edit = contextMenu.addAction(tr("Edit"));
900  connect(edit, &QAction::triggered, this, &MainWindow::onEdit);
901  }
902  if (selected) {
903  // if (useClipboard != CLIPBOARD_NEVER) {
904  // contextMenu.addSeparator();
905  // QAction* copyItem = contextMenu.addAction(tr("Copy Password"));
906  // if (getClippedPassword().length() == 0) copyItem->setEnabled(false);
907  // connect(copyItem, SIGNAL(triggered()), this,
908  // SLOT(copyPasswordToClipboard()));
909  // }
910  contextMenu.addSeparator();
911  QAction *deleteItem = contextMenu.addAction(tr("Delete"));
912  connect(deleteItem, &QAction::triggered, this, &MainWindow::onDelete);
913  }
914  contextMenu.exec(globalPos);
915 }
916 
922 void MainWindow::showBrowserContextMenu(const QPoint &pos) {
923  QMenu *contextMenu = ui->textBrowser->createStandardContextMenu(pos);
924  QPoint globalPos = ui->textBrowser->viewport()->mapToGlobal(pos);
925 
926  contextMenu->exec(globalPos);
927 }
928 
932 void MainWindow::openFolder() {
933  QString dir =
934  Util::getDir(ui->treeView->currentIndex(), false, model, proxyModel);
935 
936  QString path = QDir::toNativeSeparators(dir);
937  QDesktopServices::openUrl(QUrl::fromLocalFile(path));
938 }
939 
943 void MainWindow::addFolder() {
944  bool ok;
945  QString dir =
946  Util::getDir(ui->treeView->currentIndex(), false, model, proxyModel);
947  QString newdir =
948  QInputDialog::getText(this, tr("New file"),
949  tr("New Folder: \n(Will be placed in %1 )")
951  Util::getDir(ui->treeView->currentIndex(),
952  true, model, proxyModel)),
953  QLineEdit::Normal, "", &ok);
954  if (!ok || newdir.isEmpty())
955  return;
956  newdir.prepend(dir);
957  // dbg()<< newdir;
958  QDir().mkdir(newdir);
959 }
960 
965 void MainWindow::editPassword(const QString &file) {
966  if (!file.isEmpty()) {
968  onUpdate(true);
969  setPassword(file, false);
970  }
971 }
972 
977 void MainWindow::clearTemplateWidgets() {
978  while (ui->gridLayout->count() > 0) {
979  QLayoutItem *item = ui->gridLayout->takeAt(0);
980  delete item->widget();
981  delete item;
982  }
983  ui->verticalLayoutPassword->setSpacing(0);
984 }
985 
986 void MainWindow::copyPasswordFromTreeview() {
987  QFileInfo fileOrFolder =
988  model.fileInfo(proxyModel.mapToSource(ui->treeView->currentIndex()));
989 
990  if (fileOrFolder.isFile()) {
991  QString file = getFile(ui->treeView->currentIndex(), true);
993  &MainWindow::passwordFromFileToClipboard);
994  QtPassSettings::getPass()->Show(file);
995  }
996 }
997 
998 void MainWindow::passwordFromFileToClipboard(const QString &text) {
999  QStringList tokens = text.split('\n');
1000  m_qtPass->copyTextToClipboard(tokens[0]);
1001 }
1002 
1009 void MainWindow::addToGridLayout(int position, const QString &field,
1010  const QString &value) {
1011  QString trimmedField = field.trimmed();
1012  QString trimmedValue = value.trimmed();
1013 
1014  // Combine the Copy button and the line edit in one widget
1015  QFrame *frame = new QFrame();
1016  QLayout *ly = new QHBoxLayout();
1017  ly->setContentsMargins(5, 2, 2, 2);
1018  frame->setLayout(ly);
1020  auto *fieldLabel =
1021  new QPushButtonWithClipboard(trimmedValue, this);
1022  connect(fieldLabel, &QPushButtonWithClipboard::clicked, m_qtPass,
1024 
1025  fieldLabel->setStyleSheet("border-style: none ; background: transparent;");
1026  // fieldLabel->setContentsMargins(0,5,5,0);
1027  frame->layout()->addWidget(fieldLabel);
1028  }
1029 
1030  // set the echo mode to password, if the field is "password"
1031  if (QtPassSettings::isHidePassword() && trimmedField == tr("Password")) {
1032  auto *line = new QLineEdit();
1033  line->setObjectName(trimmedField);
1034  line->setText(trimmedValue);
1035  line->setReadOnly(true);
1036  line->setStyleSheet("border-style: none ; background: transparent;");
1037  line->setContentsMargins(0, 0, 0, 0);
1038  line->setEchoMode(QLineEdit::Password);
1039  frame->layout()->addWidget(line);
1040  } else {
1041  auto *line = new QTextBrowser();
1042  line->setOpenExternalLinks(true);
1043  line->setOpenLinks(true);
1044  line->setMaximumHeight(26);
1045  line->setMinimumHeight(26);
1046  line->setSizePolicy(
1047  QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum));
1048  line->setObjectName(trimmedField);
1049  trimmedValue.replace(
1050  QRegExp("((?:https?|ftp|ssh|sftp|ftps|webdav|webdavs)://\\S+)"),
1051  R"(<a href="\1">\1</a>)");
1052  line->setText(trimmedValue);
1053  line->setReadOnly(true);
1054  line->setStyleSheet("border-style: none ; background: transparent;");
1055  line->setContentsMargins(0, 0, 0, 0);
1056  frame->layout()->addWidget(line);
1057  }
1058 
1059  frame->setStyleSheet(
1060  ".QFrame{border: 1px solid lightgrey; border-radius: 5px;}");
1061 
1062  // set into the layout
1063  ui->gridLayout->addWidget(new QLabel(trimmedField), position, 0);
1064  ui->gridLayout->addWidget(frame, position, 1);
1065 }
1066 
1073 void MainWindow::showStatusMessage(const QString& msg, int timeout) {
1074  ui->statusBar->showMessage(msg, timeout);
1075 }
1076 
1081  setUiElementsEnabled(false);
1082  ui->treeView->setDisabled(true);
1083 }
1084 
1089 
1090 void MainWindow::updateGitButtonVisibility() {
1091  if (!QtPassSettings::isUseGit() ||
1092  (QtPassSettings::getGitExecutable().isEmpty() &&
1093  QtPassSettings::getPassExecutable().isEmpty())) {
1094  enableGitButtons(false);
1095  } else {
1096  enableGitButtons(true);
1097  }
1098 }
1099 
1100 void MainWindow::updateOtpButtonVisibility() {
1101 #if defined(Q_OS_WIN) || defined(__APPLE__)
1102  ui->actionOtp->setVisible(false);
1103 #endif
1104  if (!QtPassSettings::isUseOtp())
1105  ui->actionOtp->setEnabled(false);
1106  else
1107  ui->actionOtp->setEnabled(true);
1108 }
1109 
1110 void MainWindow::enableGitButtons(const bool &state) {
1111  // Following GNOME guidelines is preferable disable buttons instead of hide
1112  ui->actionPush->setEnabled(state);
1113  ui->actionUpdate->setEnabled(state);
1114 }
1115 
1121 void MainWindow::critical(const QString& title, const QString& msg) {
1122  QMessageBox::critical(this, title, msg);
1123 }
The NamedValues class is mostly a list of NamedValue but also has a method to take a specific NamedVa...
Definition: filecontent.h:17
static bool isStartMinimized(const bool &defaultValue=QVariant().toBool())
const QModelIndex getCurrentTreeViewIndex()
Definition: mainwindow.cpp:198
static bool isMaximized(const bool &defaultValue=QVariant().toBool())
static void setUsePass(const bool &usePass)
void generateGPGKeyPair(QString batch)
void on_treeView_clicked(const QModelIndex &index)
MainWindow::on_treeView_clicked read the selected password file.
Definition: mainwindow.cpp:320
void critical(QString, QString)
MainWindow::critical critical message popup wrapper.
Stylish widget to allow copying of password and account details.
#define dbg()
Definition: debughelper.h:7
static bool isAlwaysOnTop(const bool &defaultValue=QVariant().toBool())
static QPoint getPos(const QPoint &defaultValue=QVariant().toPoint())
void setTextTextBrowser(const QString &text)
Definition: mainwindow.cpp:207
NamedValues getNamedValues() const
Definition: filecontent.cpp:30
Definition: configdialog.h:9
void setFreshStart(const bool &fs)
Definition: qtpass.h:22
FileContent FNamedValues onst QString & fileContent
Definition: filecontent.cpp:3
void restoreWindow()
Definition: mainwindow.cpp:461
static void setPos(const QPoint &pos)
void MainWindow::foconst cusInpu & t()
MainWindow::focusInput selects any text (if applicable) in the search box and sets focus to it...
Definition: mainwindow.cpp:133
static FileContent parse(const QString &fileContent, const QStringList &templateFields, bool allFields)
parse parses the given fileContent in a FileContent object. The password is accessible through getPas...
QString password
Definition: filecontent.cpp:7
static bool isUseAutoclearPanel(const bool &defaultValue=QVariant().toBool())
void clearClippedText()
Definition: qtpass.cpp:359
NamedValues namedValues
Definition: filecontent.cpp:9
static void setSize(const QSize &size)
void passShowHandler(const QString &)
Definition: mainwindow.cpp:368
void setUiElementsEnabled(bool state)
MainWindow::setUiElementsEnabled enable or disable the relevant UI elements.
Definition: mainwindow.cpp:445
QString value
Definition: filecontent.h:10
static bool isHideOnClose(const bool &defaultValue=QVariant().toBool())
bool eventFilter(QObject *obj, QEvent *event)
MainWindow::eventFilter filter out some events and focus the treeview.
Definition: mainwindow.cpp:832
static bool isUseGit(const bool &defaultValue=QVariant().toBool())
void config()
MainWindow::config pops up the configuration screen and handles all inter-window communication.
Definition: mainwindow.cpp:231
static QString getPassStore(const QString &defaultValue=QVariant().toString())
static void setProfile(const QString &profile)
void userDialog(QString="")
MainWindow::userDialog see MainWindow::onUsers()
Definition: mainwindow.cpp:680
virtual void Remove(QString file, bool isDir)=0
void setClipboardTimer()
Definition: qtpass.cpp:361
static void setMaximized(const bool &maximized)
static QString getGitExecutable(const QString &defaultValue=QVariant().toString())
virtual void Show(QString file)=0
void setModelAndStore(QFileSystemModel *sourceModel, QString passStore)
StoreModel::setModelAndStore update the source model and store.
Definition: storemodel.cpp:81
PasswordDialog Handles the inserting and editing of passwords.
static bool isUseTrayIcon(const bool &defaultValue=QVariant().toBool())
void setClippedText(const QString &, const QString &p_output=QString())
Definition: qtpass.cpp:351
static bool isUseOtp(const bool &defaultValue=QVariant().toBool())
FileContent FNamedValues onst QString const QStringList bool allFields
Definition: filecontent.cpp:5
void closeEvent(QCloseEvent *event)
MainWindow::closeEvent hide or quit.
Definition: mainwindow.cpp:807
MainWindow(const QString &searchText=QString(), QWidget *parent=nullptr)
MainWindow::MainWindow handles all of the main functionality and also the main window.
Definition: mainwindow.cpp:37
Handles listing and editing of GPG users.
Definition: usersdialog.h:23
void flashText(const QString &text, const bool isError, const bool isHtml=false)
Definition: mainwindow.cpp:211
virtual void GitPull_b()=0
void changeEvent(QEvent *event)
MainWindow::changeEvent sets focus to the search box.
Definition: mainwindow.cpp:142
static QString getPassExecutable(const QString &defaultValue=QVariant().toString())
Handles the systemtray icon and menu.
Definition: trayicon.h:14
void keyPressEvent(QKeyEvent *event)
MainWindow::keyPressEvent did anyone press return, enter or escape?
Definition: mainwindow.cpp:846
void cleanKeygenDialog()
Definition: mainwindow.cpp:202
void messageAvailable(QString message)
MainWindow::messageAvailable we have some text/message/search to do.
Definition: mainwindow.cpp:705
static QByteArray getSavestate(const QByteArray &defaultValue=QVariant().toByteArray())
static QSize getSize(const QSize &defaultValue=QVariant().toSize())
virtual void GitPush()=0
QString getRemainingData() const
Definition: filecontent.cpp:32
static Pass * getPass()
virtual void GitPull()=0
Definition: qtpass.h:10
void endReencryptPath()
MainWindow::endReencryptPath re-enable ui elements.
static void setSavestate(const QByteArray &saveState)
void copyTextToClipboard(const QString &text)
MainWindow::copyTextToClipboard copies text to your clipboard.
Definition: qtpass.cpp:392
static QString getPassTemplate(const QString &defaultValue=QVariant().toString())
static Enums::clipBoardType getClipBoardType(const Enums::clipBoardType &defaultvalue=Enums::CLIPBOARD_NEVER)
void onPush()
MainWindow::onPush do a git push.
Definition: mainwindow.cpp:290
void updateEnv()
Pass::updateEnv update the execution environment (used when switching profiles)
Definition: pass.cpp:237
The ConfigDialog handles the configuration interface.
Definition: configdialog.h:24
static QByteArray getGeometry(const QByteArray &defaultValue=QVariant().toByteArray())
void passOtpHandler(const QString &)
Definition: mainwindow.cpp:412
void startReencryptPath()
MainWindow::startReencryptPath disable ui elements and treeview.
static int getAutoclearPanelSeconds(const int &defaultValue=QVariant().toInt())
void finishedShow(const QString &)
void showStatusMessage(QString msg, int timeout=2000)
Displays message in status bar.
static bool isUseTemplate(const bool &defaultValue=QVariant().toBool())
static bool isTemplateAllFields(const bool &defaultValue=QVariant().toBool())
static QString getDir(const QModelIndex &index, bool forPass, const QFileSystemModel &model, const StoreModel &storeModel)
Util::getDir get selectd folder path.
Definition: util.cpp:136
static QHash< QString, QString > getProfiles()
void emptyClicked()
emptyClicked event
void deselect()
MainWindow::deselect clear the selection, password and copy buffer.
Definition: mainwindow.cpp:354
static void setGeometry(const QByteArray &geometry)
bool isFreshStart()
Definition: qtpass.h:21
void generateKeyPair(QString, QDialog *)
MainWindow::generateKeyPair internal gpg keypair generator . .
Definition: mainwindow.cpp:722
The MainWindow class does way too much, not only is it a switchboard, configuration handler and more...
Definition: mainwindow.h:39
void clearClipboard()
MainWindow::clearClipboard remove clipboard contents.
Definition: qtpass.cpp:368
static bool isHidePassword(const bool &defaultValue=QVariant().toBool())
QString getPassword() const
Definition: filecontent.cpp:28
static QString findPasswordStore()
Util::findPasswordStore look for common .password-store folder location.
Definition: util.cpp:45
static bool isHideContent(const bool &defaultValue=QVariant().toBool())
static bool checkConfig()
Util::checkConfig do we have prequisite settings?
Definition: util.cpp:121
virtual void OtpGenerate(QString file)=0
static QString getProfile(const QString &defaultValue=QVariant().toString())
void setMainWindow(MainWindow *mW)
Definition: qtpass.cpp:111
void executeWrapperStarted()
Definition: mainwindow.cpp:361
bool getIsAllocated()
TrayIcon::getIsAllocated return if TrayIcon is allocated.
Definition: trayicon.cpp:57
QString name
Definition: filecontent.h:9
static void setPassStore(const QString &passStore)
static bool isAutoPull(const bool &defaultValue=QVariant().toBool())