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 
36 MainWindow::MainWindow(const QString &searchText, QWidget *parent)
37  : QMainWindow(parent), ui(new Ui::MainWindow), keygen(nullptr),
38  tray(nullptr) {
39 #ifdef __APPLE__
40  // extra treatment for mac os
41  // see http://doc.qt.io/qt-5/qkeysequence.html#qt_set_sequence_auto_mnemonic
42  qt_set_sequence_auto_mnemonic(true);
43 #endif
44  ui->setupUi(this);
45 
46  m_qtPass = new QtPass();
47  m_qtPass->setMainWindow(this);
48 
49  // register shortcut ctrl/cmd + Q to close the main window
50  new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q), this, SLOT(close()));
51  // register shortcut ctrl/cmd + C to copy the currently selected password
52  new QShortcut(QKeySequence(QKeySequence::StandardKey::Copy), this,
53  SLOT(copyPasswordFromTreeview()));
54 
55  model.setNameFilters(QStringList() << "*.gpg");
56  model.setNameFilterDisables(false);
57 
58  /*
59  * I added this to solve Windows bug but now on GNU/Linux the main folder,
60  * if hidden, disappear
61  *
62  * model.setFilter(QDir::NoDot);
63  */
64 
66 
67  proxyModel.setSourceModel(&model);
68  proxyModel.setModelAndStore(&model, passStore);
69  // proxyModel.sort(0, Qt::AscendingOrder);
70  selectionModel.reset(new QItemSelectionModel(&proxyModel));
71  model.fetchMore(model.setRootPath(passStore));
72  // model.sort(0, Qt::AscendingOrder);
73 
74  ui->treeView->setModel(&proxyModel);
75  ui->treeView->setRootIndex(
76  proxyModel.mapFromSource(model.setRootPath(passStore)));
77  ui->treeView->setColumnHidden(1, true);
78  ui->treeView->setColumnHidden(2, true);
79  ui->treeView->setColumnHidden(3, true);
80  ui->treeView->setHeaderHidden(true);
81  ui->treeView->setIndentation(15);
82  ui->treeView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
83  ui->treeView->setContextMenuPolicy(Qt::CustomContextMenu);
84  ui->treeView->header()->setSectionResizeMode(0, QHeaderView::Stretch);
85  ui->treeView->sortByColumn(0, Qt::AscendingOrder);
86  connect(ui->treeView, &QWidget::customContextMenuRequested, this,
87  &MainWindow::showContextMenu);
88  connect(ui->treeView, &DeselectableTreeView::emptyClicked, this,
90 
91  ui->textBrowser->setOpenExternalLinks(true);
92  ui->textBrowser->setContextMenuPolicy(Qt::CustomContextMenu);
93  connect(ui->textBrowser, &QWidget::customContextMenuRequested, this,
94  &MainWindow::showBrowserContextMenu);
95 
96  updateProfileBox();
97 
99  clearPanelTimer.setInterval(1000 *
101  clearPanelTimer.setSingleShot(true);
102  connect(&clearPanelTimer, SIGNAL(timeout()), this, SLOT(clearPanel()));
103 
104  searchTimer.setInterval(350);
105  searchTimer.setSingleShot(true);
106 
107  connect(&searchTimer, &QTimer::timeout, this, &MainWindow::onTimeoutSearch);
108 
109  initToolBarButtons();
110  initStatusBar();
111 
112 #if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
113  ui->lineEdit->setClearButtonEnabled(true);
114 #endif
115 
116  setUiElementsEnabled(true);
117 
118  qsrand(static_cast<uint>(QTime::currentTime().msec()));
119  QTimer::singleShot(10, this, SLOT(focusInput()));
120 
121  ui->lineEdit->setText(searchText);
122 }
123 
124 MainWindow::~MainWindow() { delete m_qtPass; }
125 
132 void MainWindow::focusInput() {
133  ui->lineEdit->selectAll();
134  ui->lineEdit->setFocus();
135 }
136 
141 void MainWindow::changeEvent(QEvent *event) {
142  QWidget::changeEvent(event);
143  if (event->type() == QEvent::ActivationChange) {
144  if (isActiveWindow()) {
145  focusInput();
146  }
147  }
148 }
149 
153 void MainWindow::initToolBarButtons() {
154  connect(ui->actionAddPassword, &QAction::triggered, this,
155  &MainWindow::addPassword);
156  connect(ui->actionAddFolder, &QAction::triggered, this,
157  &MainWindow::addFolder);
158  connect(ui->actionEdit, &QAction::triggered, this, &MainWindow::onEdit);
159  connect(ui->actionDelete, &QAction::triggered, this, &MainWindow::onDelete);
160  connect(ui->actionPush, &QAction::triggered, this, &MainWindow::onPush);
161  connect(ui->actionUpdate, &QAction::triggered, this, &MainWindow::onUpdate);
162  connect(ui->actionUsers, &QAction::triggered, this, &MainWindow::onUsers);
163  connect(ui->actionConfig, &QAction::triggered, this, &MainWindow::onConfig);
164  connect(ui->actionOtp, &QAction::triggered, this, &MainWindow::onOtp);
165 
166  ui->actionAddPassword->setIcon(
167  QIcon::fromTheme("document-new", QIcon(":/icons/document-new.svg")));
168  ui->actionAddFolder->setIcon(
169  QIcon::fromTheme("folder-new", QIcon(":/icons/folder-new.svg")));
170  ui->actionEdit->setIcon(QIcon::fromTheme(
171  "document-properties", QIcon(":/icons/document-properties.svg")));
172  ui->actionDelete->setIcon(
173  QIcon::fromTheme("edit-delete", QIcon(":/icons/edit-delete.svg")));
174  ui->actionPush->setIcon(
175  QIcon::fromTheme("go-up", QIcon(":/icons/go-top.svg")));
176  ui->actionUpdate->setIcon(
177  QIcon::fromTheme("go-down", QIcon(":/icons/go-bottom.svg")));
178  ui->actionUsers->setIcon(QIcon::fromTheme(
179  "x-office-address-book", QIcon(":/icons/x-office-address-book.svg")));
180  ui->actionConfig->setIcon(QIcon::fromTheme(
181  "applications-system", QIcon(":/icons/applications-system.svg")));
182 }
183 
187 void MainWindow::initStatusBar() {
188  ui->statusBar->showMessage(tr("Welcome to QtPass %1").arg(VERSION), 2000);
189 
190  QPixmap logo = QPixmap::fromImage(QImage(":/artwork/icon.svg"))
191  .scaledToHeight(statusBar()->height());
192  QLabel *logoApp = new QLabel(statusBar());
193  logoApp->setPixmap(logo);
194  statusBar()->addPermanentWidget(logoApp);
195 }
196 
198  return ui->treeView->currentIndex();
199 }
200 
202  this->keygen->close();
203  this->keygen = nullptr;
204 }
205 
206 void MainWindow::setTextTextBrowser(const QString &text) {
207  ui->textBrowser->setText(text);
208 }
209 
210 void MainWindow::flashText(const QString &text, const bool isError,
211  const bool isHtml) {
212  if (isError)
213  ui->textBrowser->setTextColor(Qt::red);
214 
215  if (isHtml) {
216  QString _text = text;
217  if (!ui->textBrowser->toPlainText().isEmpty())
218  _text = ui->textBrowser->toHtml() + _text;
219  ui->textBrowser->setHtml(_text);
220  } else {
221  ui->textBrowser->setText(text);
222  ui->textBrowser->setTextColor(Qt::black);
223  }
224 }
225 
231  QScopedPointer<ConfigDialog> d(new ConfigDialog(this));
232  d->setModal(true);
233  // Automatically default to pass if it's available
234  if (m_qtPass->isFreshStart() &&
235  QFile(QtPassSettings::getPassExecutable()).exists()) {
237  }
238 
239  if (m_qtPass->isFreshStart())
240  d->wizard(); // does shit
241  if (d->exec()) {
242  if (d->result() == QDialog::Accepted) {
244  Qt::WindowFlags flags = windowFlags();
245  this->setWindowFlags(flags | Qt::WindowStaysOnTopHint);
246  } else {
247  this->setWindowFlags(Qt::Window);
248  }
249  this->show();
250 
251  updateProfileBox();
252  ui->treeView->setRootIndex(proxyModel.mapFromSource(
253  model.setRootPath(QtPassSettings::getPassStore())));
254 
255  if (m_qtPass->isFreshStart() && Util::checkConfig())
256  config();
258  clearPanelTimer.setInterval(1000 *
260  m_qtPass->setClipboardTimer();
261 
262  updateGitButtonVisibility();
263  updateOtpButtonVisibility();
264  if (QtPassSettings::isUseTrayIcon() && tray == nullptr)
265  initTrayIcon();
266  else if (!QtPassSettings::isUseTrayIcon() && tray != nullptr) {
267  destroyTrayIcon();
268  }
269  }
270 
271  m_qtPass->setFreshStart(false);
272  }
273 }
274 
278 void MainWindow::onUpdate(bool block) {
279  ui->statusBar->showMessage(tr("Updating password-store"), 2000);
280  if (block)
282  else
284 }
285 
290  if (QtPassSettings::isUseGit()) {
291  ui->statusBar->showMessage(tr("Updating password-store"), 2000);
293  }
294 }
295 
303 QString MainWindow::getFile(const QModelIndex &index, bool forPass) {
304  if (!index.isValid() ||
305  !model.fileInfo(proxyModel.mapToSource(index)).isFile())
306  return QString();
307  QString filePath = model.filePath(proxyModel.mapToSource(index));
308  if (forPass) {
309  filePath = QDir(QtPassSettings::getPassStore()).relativeFilePath(filePath);
310  filePath.replace(QRegExp("\\.gpg$"), "");
311  }
312  return filePath;
313 }
314 
319 void MainWindow::on_treeView_clicked(const QModelIndex &index) {
320  bool cleared = ui->treeView->currentIndex().flags() == Qt::NoItemFlags;
321  currentDir =
322  Util::getDir(ui->treeView->currentIndex(), false, model, proxyModel);
323  // TODO(bezet): "Could not decrypt";
324  m_qtPass->clearClippedText();
325  QString file = getFile(index, true);
326  ui->passwordName->setText(getFile(index, true));
327  if (!file.isEmpty() && !cleared) {
328  QtPassSettings::getPass()->Show(file);
329  } else {
330  clearPanel(false);
331  ui->actionEdit->setEnabled(false);
332  ui->actionDelete->setEnabled(true);
333  }
334 }
335 
341 void MainWindow::on_treeView_doubleClicked(const QModelIndex &index) {
342  QFileInfo fileOrFolder =
343  model.fileInfo(proxyModel.mapToSource(ui->treeView->currentIndex()));
344 
345  if (fileOrFolder.isFile()) {
346  editPassword(getFile(index, true));
347  }
348 }
349 
354  currentDir = "";
355  m_qtPass->clearClipboard();
356  ui->treeView->clearSelection();
357  ui->actionEdit->setEnabled(false);
358  ui->actionDelete->setEnabled(false);
359  ui->passwordName->setText("");
360  ui->actionDelete->setEnabled(false);
361  ui->actionEdit->setEnabled(false);
362  clearPanel(false);
363 }
364 
366  clearTemplateWidgets();
367  ui->textBrowser->clear();
368  setUiElementsEnabled(false);
369  clearPanelTimer.stop();
370 }
371 
372 void MainWindow::passShowHandler(const QString &p_output) {
373  QStringList templ = QtPassSettings::isUseTemplate()
374  ? QtPassSettings::getPassTemplate().split("\n")
375  : QStringList();
376  bool allFields =
378  FileContent fileContent = FileContent::parse(p_output, templ, allFields);
379  QString output = p_output;
380  QString password = fileContent.getPassword();
381 
382  // set clipped text
383  m_qtPass->setClippedText(password, p_output);
384 
385  // first clear the current view:
386  clearTemplateWidgets();
387 
388  // show what is needed:
390  output = "***" + tr("Content hidden") + "***";
391  } else {
392  if (!password.isEmpty()) {
393  // set the password, it is hidden if needed in addToGridLayout
394  addToGridLayout(0, tr("Password"), password);
395  }
396 
397  NamedValues namedValues = fileContent.getNamedValues();
398  for (int j = 0; j < namedValues.length(); ++j) {
399  NamedValue nv = namedValues.at(j);
400  addToGridLayout(j + 1, nv.name, nv.value);
401  }
402  if (ui->gridLayout->count() == 0)
403  ui->verticalLayoutPassword->setSpacing(0);
404  else
405  ui->verticalLayoutPassword->setSpacing(6);
406  output = fileContent.getRemainingData();
407  }
408 
410  clearPanelTimer.start();
411  }
412 
413  setUiElementsEnabled(true);
414 }
415 
416 void MainWindow::passOtpHandler(const QString &p_output) {
417  if (!p_output.isEmpty()) {
418  addToGridLayout(ui->gridLayout->count() + 1, tr("OTP Code"), p_output);
419  m_qtPass->copyTextToClipboard(p_output);
420  }
422  clearPanelTimer.start();
423  }
424  setUiElementsEnabled(true);
425 }
426 
430 void MainWindow::clearPanel(bool notify) {
431  while (ui->gridLayout->count() > 0) {
432  QLayoutItem *item = ui->gridLayout->takeAt(0);
433  delete item->widget();
434  delete item;
435  }
436  if (notify) {
437  QString output = "***" + tr("Password and Content hidden") + "***";
438  ui->textBrowser->setHtml(output);
439  } else {
440  ui->textBrowser->setHtml("");
441  }
442 }
443 
450  ui->treeView->setEnabled(state);
451  ui->lineEdit->setEnabled(state);
452  ui->lineEdit->installEventFilter(this);
453  ui->actionAddPassword->setEnabled(state);
454  ui->actionAddFolder->setEnabled(state);
455  ui->actionUsers->setEnabled(state);
456  ui->actionConfig->setEnabled(state);
457  // is a file selected?
458  state &= ui->treeView->currentIndex().isValid();
459  ui->actionDelete->setEnabled(state);
460  ui->actionEdit->setEnabled(state);
461  updateGitButtonVisibility();
462  updateOtpButtonVisibility();
463 }
464 
466  QByteArray geometry = QtPassSettings::getGeometry(saveGeometry());
467  restoreGeometry(geometry);
468  QByteArray savestate = QtPassSettings::getSavestate(saveState());
469  restoreState(savestate);
470  QPoint position = QtPassSettings::getPos(pos());
471  move(position);
472  QSize newSize = QtPassSettings::getSize(size());
473  resize(newSize);
474  if (QtPassSettings::isMaximized(isMaximized())) {
475  showMaximized();
476  }
477 
479  Qt::WindowFlags flags = windowFlags();
480  setWindowFlags(flags | Qt::WindowStaysOnTopHint);
481  show();
482  }
483 
484  if (QtPassSettings::isUseTrayIcon() && tray == nullptr) {
485  initTrayIcon();
486  if (m_qtPass->isFreshStart() && QtPassSettings::isStartMinimized()) {
487  // since we are still in constructor, can't directly hide
488  QTimer::singleShot(10, this, SLOT(hide()));
489  }
490  } /*else if (!QtPassSettings::isUseTrayIcon() && tray != NULL) {
491  destroyTrayIcon();
492  }*/
493 }
494 
498 void MainWindow::onConfig() { config(); }
499 
505 void MainWindow::on_lineEdit_textChanged(const QString &arg1) {
506  ui->statusBar->showMessage(tr("Looking for: %1").arg(arg1), 1000);
507  ui->treeView->expandAll();
508 
509  searchTimer.start();
510 }
511 
516 void MainWindow::onTimeoutSearch() {
517  QString query = ui->lineEdit->text();
518 
519  if (query.isEmpty())
520  ui->treeView->collapseAll();
521 
522  query.replace(QRegExp(" "), ".*");
523  QRegExp regExp(query, Qt::CaseInsensitive);
524  proxyModel.setFilterRegExp(regExp);
525  ui->treeView->setRootIndex(proxyModel.mapFromSource(
526  model.setRootPath(QtPassSettings::getPassStore())));
527 
528  if (proxyModel.rowCount() > 0 && !query.isEmpty()) {
529  selectFirstFile();
530  } else {
531  ui->actionEdit->setEnabled(false);
532  ui->actionDelete->setEnabled(false);
533  }
534 }
535 
541 void MainWindow::on_lineEdit_returnPressed() {
542 #ifdef QT_DEBUG
543  dbg() << "on_lineEdit_returnPressed" << proxyModel.rowCount();
544 #endif
545 
546  if (proxyModel.rowCount() > 0) {
547  selectFirstFile();
548  on_treeView_clicked(ui->treeView->currentIndex());
549  }
550 }
551 
556 void MainWindow::selectFirstFile() {
557  QModelIndex index = proxyModel.mapFromSource(
558  model.setRootPath(QtPassSettings::getPassStore()));
559  index = firstFile(index);
560  ui->treeView->setCurrentIndex(index);
561 }
562 
568 QModelIndex MainWindow::firstFile(QModelIndex parentIndex) {
569  QModelIndex index = parentIndex;
570  int numRows = proxyModel.rowCount(parentIndex);
571  for (int row = 0; row < numRows; ++row) {
572  index = proxyModel.index(row, 0, parentIndex);
573  if (model.fileInfo(proxyModel.mapToSource(index)).isFile())
574  return index;
575  if (proxyModel.hasChildren(index))
576  return firstFile(index);
577  }
578  return index;
579 }
580 
586 void MainWindow::setPassword(QString file, bool isNew) {
587  PasswordDialog d(file, isNew, this);
588 
589  if (!d.exec()) {
590  ui->treeView->setFocus();
591  }
592 }
593 
598 void MainWindow::addPassword() {
599  bool ok;
600  QString dir =
601  Util::getDir(ui->treeView->currentIndex(), true, model, proxyModel);
602  QString file =
603  QInputDialog::getText(this, tr("New file"),
604  tr("New password file: \n(Will be placed in %1 )")
606  Util::getDir(ui->treeView->currentIndex(),
607  true, model, proxyModel)),
608  QLineEdit::Normal, "", &ok);
609  if (!ok || file.isEmpty())
610  return;
611  file = dir + file;
612  setPassword(file);
613 }
614 
619 void MainWindow::onDelete() {
620  QFileInfo fileOrFolder =
621  model.fileInfo(proxyModel.mapToSource(ui->treeView->currentIndex()));
622  QString file = "";
623  bool isDir = false;
624 
625  if (fileOrFolder.isFile()) {
626  file = getFile(ui->treeView->currentIndex(), true);
627  } else {
628  file = Util::getDir(ui->treeView->currentIndex(), true, model, proxyModel);
629  isDir = true;
630  }
631 
632  QString dirMessage = tr(" and the whole content?");
633  if (isDir) {
634  QDirIterator it(model.rootPath() + QDir::separator() + file,
635  QDirIterator::Subdirectories);
636  bool okDir = true;
637  while (it.hasNext() && okDir) {
638  it.next();
639  if (QFileInfo(it.filePath()).isFile()) {
640  if (QFileInfo(it.filePath()).suffix() != "gpg") {
641  okDir = false;
642  dirMessage = tr(" and the whole content? <br><strong>Attention: "
643  "there are unexpected files in the given folder, "
644  "check them before continue.</strong>");
645  }
646  }
647  }
648  }
649 
650  if (QMessageBox::question(
651  this, isDir ? tr("Delete folder?") : tr("Delete password?"),
652  tr("Are you sure you want to delete %1%2")
653  .arg(QDir::separator() + file)
654  .arg(isDir ? dirMessage : "?"),
655  QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
656  return;
657 
658  QtPassSettings::getPass()->Remove(file, isDir);
659 }
660 
664 void MainWindow::onOtp() {
665  QString file = getFile(ui->treeView->currentIndex(), true);
666  if (!file.isEmpty()) {
669  }
670 }
671 
675 void MainWindow::onEdit() {
676  QString file = getFile(ui->treeView->currentIndex(), true);
677  editPassword(file);
678 }
679 
684 void MainWindow::userDialog(QString dir) {
685  if (!dir.isEmpty())
686  currentDir = dir;
687  onUsers();
688 }
689 
695 void MainWindow::onUsers() {
696  QString dir =
697  currentDir.isEmpty()
698  ? Util::getDir(ui->treeView->currentIndex(), false, model, proxyModel)
699  : currentDir;
700 
701  UsersDialog d(dir, this);
702  d.exec();
703 }
704 
709 void MainWindow::messageAvailable(QString message) {
710  if (message.isEmpty()) {
711  focusInput();
712  } else {
713  ui->treeView->expandAll();
714  ui->lineEdit->setText(message);
715  on_lineEdit_returnPressed();
716  }
717  show();
718  raise();
719 }
720 
726 void MainWindow::generateKeyPair(QString batch, QDialog *keygenWindow) {
727  keygen = keygenWindow;
728  emit generateGPGKeyPair(batch);
729 }
730 
735 void MainWindow::updateProfileBox() {
736  QHash<QString, QString> profiles = QtPassSettings::getProfiles();
737 
738  if (profiles.isEmpty()) {
739  ui->profileWidget->hide();
740  } else {
741  ui->profileWidget->show();
742  ui->profileBox->setEnabled(profiles.size() > 1);
743  ui->profileBox->clear();
744  QHashIterator<QString, QString> i(profiles);
745  while (i.hasNext()) {
746  i.next();
747  if (!i.key().isEmpty())
748  ui->profileBox->addItem(i.key());
749  }
750  }
751  int index = ui->profileBox->findText(QtPassSettings::getProfile());
752  if (index != -1) // -1 for not found
753  ui->profileBox->setCurrentIndex(index);
754 }
755 
761 void MainWindow::on_profileBox_currentIndexChanged(QString name) {
762  if (m_qtPass->isFreshStart() || name == QtPassSettings::getProfile())
763  return;
765 
767  ui->statusBar->showMessage(tr("Profile changed to %1").arg(name), 2000);
768 
770 
771  ui->treeView->selectionModel()->clear();
772  ui->treeView->setRootIndex(proxyModel.mapFromSource(
773  model.setRootPath(QtPassSettings::getPassStore())));
774 
775  ui->actionEdit->setEnabled(false);
776  ui->actionDelete->setEnabled(false);
777 }
778 
784 void MainWindow::initTrayIcon() {
785  this->tray = new TrayIcon(this);
786  // Setup tray icon
787 
788  if (tray == nullptr) {
789 #ifdef QT_DEBUG
790  dbg() << "Allocating tray icon failed.";
791 #endif
792  }
793 
794  if (!tray->getIsAllocated()) {
795  destroyTrayIcon();
796  }
797 }
798 
802 void MainWindow::destroyTrayIcon() {
803  delete this->tray;
804  tray = nullptr;
805 }
806 
811 void MainWindow::closeEvent(QCloseEvent *event) {
813  this->hide();
814  event->ignore();
815  } else {
816  m_qtPass->clearClipboard();
817 
818  QtPassSettings::setGeometry(saveGeometry());
819  QtPassSettings::setSavestate(saveState());
820  QtPassSettings::setMaximized(isMaximized());
821  if (!isMaximized()) {
822  QtPassSettings::setPos(pos());
823  QtPassSettings::setSize(size());
824  }
825  event->accept();
826  }
827 }
828 
836 bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
837  if (obj == ui->lineEdit && event->type() == QEvent::KeyPress) {
838  QKeyEvent *key = static_cast<QKeyEvent *>(event);
839  if (key->key() == Qt::Key_Down) {
840  ui->treeView->setFocus();
841  }
842  }
843  return QObject::eventFilter(obj, event);
844 }
845 
850 void MainWindow::keyPressEvent(QKeyEvent *event) {
851  switch (event->key()) {
852  case Qt::Key_Delete:
853  onDelete();
854  break;
855  case Qt::Key_Return:
856  case Qt::Key_Enter:
857  if (proxyModel.rowCount() > 0)
858  on_treeView_clicked(ui->treeView->currentIndex());
859  break;
860  case Qt::Key_Escape:
861  ui->lineEdit->clear();
862  break;
863  default:
864  break;
865  }
866 }
867 
873 void MainWindow::showContextMenu(const QPoint &pos) {
874  QModelIndex index = ui->treeView->indexAt(pos);
875  bool selected = true;
876  if (!index.isValid()) {
877  ui->treeView->clearSelection();
878  ui->actionDelete->setEnabled(false);
879  ui->actionEdit->setEnabled(false);
880  currentDir = "";
881  selected = false;
882  }
883 
884  ui->treeView->setCurrentIndex(index);
885 
886  QPoint globalPos = ui->treeView->viewport()->mapToGlobal(pos);
887 
888  QFileInfo fileOrFolder =
889  model.fileInfo(proxyModel.mapToSource(ui->treeView->currentIndex()));
890 
891  QMenu contextMenu;
892  if (!selected || fileOrFolder.isDir()) {
893  QAction *openFolder =
894  contextMenu.addAction(tr("Open folder with file manager"));
895  QAction *addFolder = contextMenu.addAction(tr("Add folder"));
896  QAction *addPassword = contextMenu.addAction(tr("Add password"));
897  QAction *users = contextMenu.addAction(tr("Users"));
898  connect(openFolder, &QAction::triggered, this, &MainWindow::openFolder);
899  connect(addFolder, &QAction::triggered, this, &MainWindow::addFolder);
900  connect(addPassword, &QAction::triggered, this, &MainWindow::addPassword);
901  connect(users, &QAction::triggered, this, &MainWindow::onUsers);
902  } else if (fileOrFolder.isFile()) {
903  QAction *edit = contextMenu.addAction(tr("Edit"));
904  connect(edit, &QAction::triggered, this, &MainWindow::onEdit);
905  }
906  if (selected) {
907  // if (useClipboard != CLIPBOARD_NEVER) {
908  // contextMenu.addSeparator();
909  // QAction* copyItem = contextMenu.addAction(tr("Copy Password"));
910  // if (getClippedPassword().length() == 0) copyItem->setEnabled(false);
911  // connect(copyItem, SIGNAL(triggered()), this,
912  // SLOT(copyPasswordToClipboard()));
913  // }
914  contextMenu.addSeparator();
915  QAction *deleteItem = contextMenu.addAction(tr("Delete"));
916  connect(deleteItem, &QAction::triggered, this, &MainWindow::onDelete);
917  }
918  contextMenu.exec(globalPos);
919 }
920 
926 void MainWindow::showBrowserContextMenu(const QPoint &pos) {
927  QMenu *contextMenu = ui->textBrowser->createStandardContextMenu(pos);
928  QPoint globalPos = ui->textBrowser->viewport()->mapToGlobal(pos);
929 
930  contextMenu->exec(globalPos);
931 }
932 
936 void MainWindow::openFolder() {
937  QString dir =
938  Util::getDir(ui->treeView->currentIndex(), false, model, proxyModel);
939 
940  QString path = QDir::toNativeSeparators(dir);
941  QDesktopServices::openUrl(QUrl::fromLocalFile(path));
942 }
943 
947 void MainWindow::addFolder() {
948  bool ok;
949  QString dir =
950  Util::getDir(ui->treeView->currentIndex(), false, model, proxyModel);
951  QString newdir =
952  QInputDialog::getText(this, tr("New file"),
953  tr("New Folder: \n(Will be placed in %1 )")
955  Util::getDir(ui->treeView->currentIndex(),
956  true, model, proxyModel)),
957  QLineEdit::Normal, "", &ok);
958  if (!ok || newdir.isEmpty())
959  return;
960  newdir.prepend(dir);
961  // dbg()<< newdir;
962  QDir().mkdir(newdir);
963 }
964 
969 void MainWindow::editPassword(const QString &file) {
970  if (!file.isEmpty()) {
972  onUpdate(true);
973  setPassword(file, false);
974  }
975 }
976 
981 void MainWindow::clearTemplateWidgets() {
982  while (ui->gridLayout->count() > 0) {
983  QLayoutItem *item = ui->gridLayout->takeAt(0);
984  delete item->widget();
985  delete item;
986  }
987  ui->verticalLayoutPassword->setSpacing(0);
988 }
989 
990 void MainWindow::copyPasswordFromTreeview() {
991  QFileInfo fileOrFolder =
992  model.fileInfo(proxyModel.mapToSource(ui->treeView->currentIndex()));
993 
994  if (fileOrFolder.isFile()) {
995  QString file = getFile(ui->treeView->currentIndex(), true);
997  &MainWindow::passwordFromFileToClipboard);
998  QtPassSettings::getPass()->Show(file);
999  }
1000 }
1001 
1002 void MainWindow::passwordFromFileToClipboard(const QString &text) {
1003  QStringList tokens = text.split('\n');
1004  m_qtPass->copyTextToClipboard(tokens[0]);
1005 }
1006 
1013 void MainWindow::addToGridLayout(int position, const QString &field,
1014  const QString &value) {
1015  QString trimmedField = field.trimmed();
1016  QString trimmedValue = value.trimmed();
1017 
1018  // Combine the Copy button and the line edit in one widget
1019  QFrame *frame = new QFrame();
1020  QLayout *ly = new QHBoxLayout();
1021  ly->setContentsMargins(5, 2, 2, 2);
1022  frame->setLayout(ly);
1024  QPushButtonWithClipboard *fieldLabel =
1025  new QPushButtonWithClipboard(trimmedValue, this);
1026  connect(fieldLabel, &QPushButtonWithClipboard::clicked, m_qtPass,
1028 
1029  fieldLabel->setStyleSheet("border-style: none ; background: transparent;");
1030  // fieldLabel->setContentsMargins(0,5,5,0);
1031  frame->layout()->addWidget(fieldLabel);
1032  }
1033 
1034  // set the echo mode to password, if the field is "password"
1035  if (QtPassSettings::isHidePassword() && trimmedField == tr("Password")) {
1036  QLineEdit *line = new QLineEdit();
1037  line->setObjectName(trimmedField);
1038  line->setText(trimmedValue);
1039  line->setReadOnly(true);
1040  line->setStyleSheet("border-style: none ; background: transparent;");
1041  line->setContentsMargins(0, 0, 0, 0);
1042  line->setEchoMode(QLineEdit::Password);
1043  frame->layout()->addWidget(line);
1044  } else {
1045  QTextBrowser *line = new QTextBrowser();
1046  line->setOpenExternalLinks(true);
1047  line->setOpenLinks(true);
1048  line->setMaximumHeight(26);
1049  line->setMinimumHeight(26);
1050  line->setSizePolicy(
1051  QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum));
1052  line->setObjectName(trimmedField);
1053  trimmedValue.replace(
1054  QRegExp("((?:https?|ftp|ssh|sftp|ftps|webdav|webdavs)://\\S+)"),
1055  "<a href=\"\\1\">\\1</a>");
1056  line->setText(trimmedValue);
1057  line->setReadOnly(true);
1058  line->setStyleSheet("border-style: none ; background: transparent;");
1059  line->setContentsMargins(0, 0, 0, 0);
1060  frame->layout()->addWidget(line);
1061  }
1062 
1063  frame->setStyleSheet(
1064  ".QFrame{border: 1px solid lightgrey; border-radius: 5px;}");
1065 
1066  // set into the layout
1067  ui->gridLayout->addWidget(new QLabel(trimmedField), position, 0);
1068  ui->gridLayout->addWidget(frame, position, 1);
1069 }
1070 
1077 void MainWindow::showStatusMessage(QString msg, int timeout) {
1078  ui->statusBar->showMessage(msg, timeout);
1079 }
1080 
1085  setUiElementsEnabled(false);
1086  ui->treeView->setDisabled(true);
1087 }
1088 
1093 
1094 void MainWindow::updateGitButtonVisibility() {
1095  if (!QtPassSettings::isUseGit() ||
1096  (QtPassSettings::getGitExecutable().isEmpty() &&
1097  QtPassSettings::getPassExecutable().isEmpty())) {
1098  enableGitButtons(false);
1099  } else {
1100  enableGitButtons(true);
1101  }
1102 }
1103 
1104 void MainWindow::updateOtpButtonVisibility() {
1105 #if defined(Q_OS_WIN) || defined(__APPLE__)
1106  ui->actionOtp->setVisible(false);
1107 #endif
1108  if (!QtPassSettings::isUseOtp())
1109  ui->actionOtp->setEnabled(false);
1110  else
1111  ui->actionOtp->setEnabled(true);
1112 }
1113 
1114 void MainWindow::enableGitButtons(const bool &state) {
1115  // Following GNOME guidelines is preferable disable buttons instead of hide
1116  ui->actionPush->setEnabled(state);
1117  ui->actionUpdate->setEnabled(state);
1118 }
1119 
1125 void MainWindow::critical(QString title, QString msg) {
1126  QMessageBox::critical(this, title, msg);
1127 }
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:197
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:319
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:206
NamedValues getNamedValues() const
Definition: filecontent.cpp:30
Definition: configdialog.h:9
void setFreshStart(const bool &fs)
Definition: qtpass.h:22
void restoreWindow()
Definition: mainwindow.cpp:465
static void setPos(const QPoint &pos)
static bool isUseAutoclearPanel(const bool &defaultValue=QVariant().toBool())
void clearClippedText()
Definition: qtpass.cpp:358
static void setSize(const QSize &size)
void passShowHandler(const QString &)
Definition: mainwindow.cpp:372
void setUiElementsEnabled(bool state)
MainWindow::setUiElementsEnabled enable or disable the relevant UI elements.
Definition: mainwindow.cpp:449
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:836
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:230
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...
Definition: filecontent.cpp:3
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:684
virtual void Remove(QString file, bool isDir)=0
void setClipboardTimer()
Definition: qtpass.cpp:360
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:80
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:350
static bool isUseOtp(const bool &defaultValue=QVariant().toBool())
void closeEvent(QCloseEvent *event)
MainWindow::closeEvent hide or quit.
Definition: mainwindow.cpp:811
MainWindow(const QString &searchText=QString(), QWidget *parent=nullptr)
MainWindow::MainWindow handles all of the main functionality and also the main window.
Definition: mainwindow.cpp:36
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:210
virtual void GitPull_b()=0
void changeEvent(QEvent *event)
MainWindow::changeEvent sets focus to the search box.
Definition: mainwindow.cpp:141
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:850
void cleanKeygenDialog()
Definition: mainwindow.cpp:201
void messageAvailable(QString message)
MainWindow::messageAvailable we have some text/message/search to do.
Definition: mainwindow.cpp:709
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:391
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:289
void updateEnv()
Pass::updateEnv update the execution environment (used when switching profiles)
Definition: pass.cpp:236
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:416
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:148
static QHash< QString, QString > getProfiles()
void emptyClicked()
emptyClicked event
void deselect()
MainWindow::deselect clear the selection, password and copy buffer.
Definition: mainwindow.cpp:353
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:726
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:367
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:131
virtual void OtpGenerate(QString file)=0
static QString getProfile(const QString &defaultValue=QVariant().toString())
void setMainWindow(MainWindow *mW)
Definition: qtpass.cpp:110
void executeWrapperStarted()
Definition: mainwindow.cpp:365
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())