Refactor DetailsWidget

This commit is contained in:
frostasm 2017-12-25 15:53:22 +02:00
parent 3ba42ee32e
commit 6007e0de71
9 changed files with 872 additions and 739 deletions

View File

@ -48,7 +48,7 @@ bool EntryAttributes::hasKey(const QString& key) const
return m_attributes.contains(key);
}
QList<QString> EntryAttributes::customKeys()
QList<QString> EntryAttributes::customKeys() const
{
QList<QString> customKeys;
const QList<QString> keyList = keys();

View File

@ -33,7 +33,7 @@ public:
explicit EntryAttributes(QObject* parent = nullptr);
QList<QString> keys() const;
bool hasKey(const QString& key) const;
QList<QString> customKeys();
QList<QString> customKeys() const;
QString value(const QString& key) const;
bool contains(const QString& key) const;
bool containsValue(const QString& value) const;

View File

@ -439,11 +439,11 @@ void Group::setParent(Database* db)
QObject::setParent(db);
}
QStringList Group::hierarchy()
QStringList Group::hierarchy() const
{
QStringList hierarchy;
Group* group = this;
Group* parent = m_parent;
const Group* group = this;
const Group* parent = m_parent;
hierarchy.prepend(group->name());
while (parent) {

View File

@ -117,7 +117,7 @@ public:
Group* parentGroup();
const Group* parentGroup() const;
void setParent(Group* parent, int index = -1);
QStringList hierarchy();
QStringList hierarchy() const;
Database* database();
const Database* database() const;

View File

@ -111,9 +111,13 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent)
"border-radius: 5px;");
m_detailsView = new DetailsWidget(this);
connect(m_detailsView, &DetailsWidget::errorOccurred, this, [this](const QString& error) {
showMessage(error, MessageWidget::MessageType::Error);
});
m_detailsView->hide();
connect(this, SIGNAL(pressedEntry(Entry*)), m_detailsView, SLOT(setEntry(Entry*)));
connect(this, SIGNAL(pressedGroup(Group*)), m_detailsView, SLOT(setGroup(Group*)));
connect(this, SIGNAL(currentModeChanged(DatabaseWidget::Mode)),
m_detailsView, SLOT(setDatabaseMode(DatabaseWidget::Mode)));
connect(m_detailsView, SIGNAL(errorOccurred(QString)), this, SLOT(showErrorMessage(QString)));
QVBoxLayout* vLayout = new QVBoxLayout(rightHandSideWidget);
vLayout->setMargin(0);
@ -1488,6 +1492,11 @@ void DatabaseWidget::showMessage(const QString& text, MessageWidget::MessageType
m_messageWidget->showMessage(text, type, autoHideTimeout);
}
void DatabaseWidget::showErrorMessage(const QString& errorMessage)
{
showMessage(errorMessage, MessageWidget::MessageType::Error);
}
void DatabaseWidget::hideMessage()
{
if (m_messageWidget->isVisible()) {

View File

@ -178,6 +178,7 @@ public slots:
void showMessage(const QString& text, MessageWidget::MessageType type, bool showClosebutton = true,
int autoHideTimeout = MessageWidget::DefaultAutoHideTimeout);
void showErrorMessage(const QString& errorMessage);
void hideMessage();
private slots:

View File

@ -28,105 +28,150 @@
#include "gui/Clipboard.h"
#include "entry/EntryAttachmentsModel.h"
namespace {
constexpr int GeneralTabIndex = 0;
const QString hierarchySeparator(" / ");
}
DetailsWidget::DetailsWidget(QWidget* parent)
: QWidget(parent)
, m_ui(new Ui::DetailsWidget())
, m_locked(false)
, m_currentEntry(nullptr)
, m_currentGroup(nullptr)
, m_timer(nullptr)
, m_attributesTabWidget(nullptr)
, m_attachmentsTabWidget(nullptr)
, m_autotypeTabWidget(nullptr)
, m_step(0)
, m_totpTimer(nullptr)
, m_selectedTabEntry(0)
, m_selectedTabGroup(0)
{
m_ui->setupUi(this);
connect(parent, SIGNAL(pressedEntry(Entry*)), SLOT(getSelectedEntry(Entry*)));
connect(parent, SIGNAL(pressedGroup(Group*)), SLOT(getSelectedGroup(Group*)));
connect(parent, SIGNAL(currentModeChanged(DatabaseWidget::Mode)), SLOT(setDatabaseMode(DatabaseWidget::Mode)));
// Entry
m_ui->entryTotpButton->setIcon(filePath()->icon("actions", "chronometer"));
m_ui->entryCloseButton->setIcon(filePath()->icon("actions", "dialog-close"));
m_ui->totpButton->setIcon(filePath()->icon("actions", "chronometer"));
m_ui->closeButton->setIcon(filePath()->icon("actions", "dialog-close"));
m_ui->entryAttachmentsWidget->setReadOnly(true);
m_ui->entryAttachmentsWidget->setButtonsVisible(false);
connect(m_ui->totpButton, SIGNAL(toggled(bool)), SLOT(showTotp(bool)));
connect(m_ui->closeButton, SIGNAL(toggled(bool)), SLOT(hideDetails()));
connect(m_ui->tabWidget, SIGNAL(tabBarClicked(int)), SLOT(updateTabIndex(int)));
connect(m_ui->entryTotpButton, SIGNAL(toggled(bool)), m_ui->entryTotpWidget, SLOT(setVisible(bool)));
connect(m_ui->entryCloseButton, SIGNAL(toggled(bool)), SLOT(hide()));
connect(m_ui->entryTabWidget, SIGNAL(tabBarClicked(int)), SLOT(updateTabIndexes()), Qt::QueuedConnection);
m_ui->attachmentsWidget->setReadOnly(true);
m_ui->attachmentsWidget->setButtonsVisible(false);
m_attributesTabWidget = m_ui->tabWidget->widget(AttributesTab);
m_attachmentsTabWidget = m_ui->tabWidget->widget(AttachmentsTab);
m_autotypeTabWidget = m_ui->tabWidget->widget(AutotypeTab);
this->hide();
// Group
m_ui->groupCloseButton->setIcon(filePath()->icon("actions", "dialog-close"));
connect(m_ui->groupCloseButton, SIGNAL(toggled(bool)), SLOT(hide()));
connect(m_ui->groupTabWidget, SIGNAL(tabBarClicked(int)), SLOT(updateTabIndexes()), Qt::QueuedConnection);
}
DetailsWidget::~DetailsWidget()
{
if (m_timer) {
delete m_timer;
}
deleteTotpTimer();
}
void DetailsWidget::getSelectedEntry(Entry* selectedEntry)
void DetailsWidget::setEntry(Entry* selectedEntry)
{
if (!selectedEntry) {
hideDetails();
hide();
return;
}
m_currentEntry = selectedEntry;
if (!config()->get("GUI/HideDetailsView").toBool()) {
this->show();
updateEntryHeaderLine();
updateEntryTotp();
updateEntryGeneralTab();
updateEntryNotesTab();
updateEntryAttributesTab();
updateEntryAttachmentsTab();
updateEntryAutotypeTab();
setVisible(!config()->get("GUI/HideDetailsView").toBool());
m_ui->stackedWidget->setCurrentWidget(m_ui->pageEntry);
const int tabIndex = m_ui->entryTabWidget->isTabEnabled(m_selectedTabEntry) ? m_selectedTabEntry
: GeneralTabIndex;
Q_ASSERT(m_ui->entryTabWidget->isTabEnabled(GeneralTabIndex));
m_ui->entryTabWidget->setCurrentIndex(tabIndex);
}
m_ui->stackedWidget->setCurrentIndex(EntryPreview);
if (m_ui->tabWidget->count() < 5) {
m_ui->tabWidget->insertTab(static_cast<int>(AttributesTab), m_attributesTabWidget, tr("Attributes"));
m_ui->tabWidget->insertTab(static_cast<int>(AttachmentsTab), m_attachmentsTabWidget, tr("Attachments"));
m_ui->tabWidget->insertTab(static_cast<int>(AutotypeTab), m_autotypeTabWidget, tr("Autotype"));
void DetailsWidget::setGroup(Group* selectedGroup)
{
if (!selectedGroup) {
hide();
return;
}
m_ui->tabWidget->setTabEnabled(AttributesTab, false);
m_ui->tabWidget->setTabEnabled(NotesTab, false);
m_ui->tabWidget->setTabEnabled(AutotypeTab, false);
m_currentGroup = selectedGroup;
updateGroupHeaderLine();
updateGroupGeneralTab();
updateGroupNotesTab();
m_ui->totpButton->hide();
m_ui->totpWidget->hide();
m_ui->totpButton->setChecked(false);
setVisible(!config()->get("GUI/HideDetailsView").toBool());
auto icon = m_currentEntry->iconPixmap();
if (icon.width() > 16 || icon.height() > 16) {
icon = icon.scaled(16, 16);
m_ui->stackedWidget->setCurrentWidget(m_ui->pageGroup);
const int tabIndex = m_ui->groupTabWidget->isTabEnabled(m_selectedTabGroup) ? m_selectedTabGroup
: GeneralTabIndex;
Q_ASSERT(m_ui->groupTabWidget->isTabEnabled(GeneralTabIndex));
m_ui->groupTabWidget->setCurrentIndex(tabIndex);
}
m_ui->entryIcon->setPixmap(icon);
QString title = QString(" / ");
Group* entry_group = m_currentEntry->group();
if (entry_group) {
QStringList hierarchy = entry_group->hierarchy();
hierarchy.removeFirst();
title += hierarchy.join(" / ");
if (hierarchy.size() > 0) {
title += " / ";
void DetailsWidget::setDatabaseMode(DatabaseWidget::Mode mode)
{
m_locked = mode == DatabaseWidget::LockedMode;
if (m_locked) {
return;
}
if (mode == DatabaseWidget::ViewMode) {
if (m_ui->stackedWidget->currentWidget() == m_ui->pageGroup) {
setGroup(m_currentGroup);
} else {
setEntry(m_currentEntry);
}
}
}
title.append(m_currentEntry->resolveMultiplePlaceholders(m_currentEntry->title()));
m_ui->titleLabel->setText(title);
m_ui->usernameLabel->setText(m_currentEntry->resolveMultiplePlaceholders(m_currentEntry->username()));
void DetailsWidget::updateEntryHeaderLine()
{
Q_ASSERT(m_currentEntry);
const QString title = m_currentEntry->resolveMultiplePlaceholders(m_currentEntry->title());
m_ui->entryTitleLabel->setText(hierarchy(m_currentEntry->group(), title));
m_ui->entryIcon->setPixmap(preparePixmap(m_currentEntry->iconPixmap(), 16));
}
void DetailsWidget::updateEntryTotp()
{
Q_ASSERT(m_currentEntry);
const bool hasTotp = m_currentEntry->hasTotp();
m_ui->entryTotpButton->setVisible(hasTotp);
m_ui->entryTotpWidget->hide();
m_ui->entryTotpButton->setChecked(false);
if (hasTotp) {
deleteTotpTimer();
m_totpTimer = new QTimer(m_currentEntry);
connect(m_totpTimer, SIGNAL(timeout()), this, SLOT(updateTotpLabel()));
m_totpTimer->start(1000);
m_step = m_currentEntry->totpStep();
updateTotpLabel();
} else {
m_ui->entryTotpLabel->clear();
stopTotpTimer();
}
}
void DetailsWidget::updateEntryGeneralTab()
{
Q_ASSERT(m_currentEntry);
m_ui->entryUsernameLabel->setText(m_currentEntry->resolveMultiplePlaceholders(m_currentEntry->username()));
if (!config()->get("security/hidepassworddetails").toBool()) {
m_ui->passwordLabel->setText(
shortPassword(m_currentEntry->resolveMultiplePlaceholders(m_currentEntry->password())));
m_ui->passwordLabel->setToolTip(m_currentEntry->resolveMultiplePlaceholders(m_currentEntry->password()));
const QString password = m_currentEntry->resolveMultiplePlaceholders(m_currentEntry->password());
m_ui->entryPasswordLabel->setText(shortPassword(password));
m_ui->entryPasswordLabel->setToolTip(password);
} else {
m_ui->passwordLabel->setText(QString("\u25cf").repeated(6));
m_ui->entryPasswordLabel->setText(QString("\u25cf").repeated(6));
}
QString url = m_currentEntry->webUrl();
@ -135,46 +180,38 @@ void DetailsWidget::getSelectedEntry(Entry* selectedEntry)
// create a new display url that masks password placeholders
// the actual link will use the password
url = QString("<a href=\"%1\">%2</a>").arg(url).arg(shortUrl(m_currentEntry->displayUrl()));
m_ui->urlLabel->setOpenExternalLinks(true);
m_ui->entryUrlLabel->setOpenExternalLinks(true);
} else {
// Fallback to the raw url string
url = shortUrl(m_currentEntry->resolveMultiplePlaceholders(m_currentEntry->url()));
m_ui->urlLabel->setOpenExternalLinks(false);
m_ui->entryUrlLabel->setOpenExternalLinks(false);
}
m_ui->urlLabel->setText(url);
m_ui->entryUrlLabel->setText(url);
TimeInfo entryTime = m_currentEntry->timeInfo();
if (entryTime.expires()) {
m_ui->expirationLabel->setText(entryTime.expiryTime().toString(Qt::DefaultLocaleShortDate));
} else {
m_ui->expirationLabel->setText(tr("Never"));
const TimeInfo entryTime = m_currentEntry->timeInfo();
const QString expires = entryTime.expires() ? entryTime.expiryTime().toString(Qt::DefaultLocaleShortDate)
: tr("Never");
m_ui->entryExpirationLabel->setText(expires);
}
if (m_currentEntry->hasTotp()) {
m_step = m_currentEntry->totpStep();
if (m_timer) {
delete m_timer;
}
m_timer = new QTimer(selectedEntry);
connect(m_timer, SIGNAL(timeout()), this, SLOT(updateTotp()));
updateTotp();
m_timer->start(m_step * 1000);
m_ui->totpButton->show();
void DetailsWidget::updateEntryNotesTab()
{
Q_ASSERT(m_currentEntry);
const QString notes = m_currentEntry->notes();
setTabEnabled(m_ui->entryTabWidget, m_ui->entryNotesTab, !notes.isEmpty());
m_ui->entryNotesEdit->setText(m_currentEntry->resolveMultiplePlaceholders(notes));
}
QString notes = m_currentEntry->notes();
if (!notes.isEmpty()) {
m_ui->tabWidget->setTabEnabled(NotesTab, true);
m_ui->notesEdit->setText(m_currentEntry->resolveMultiplePlaceholders(notes));
}
QStringList customAttributes = m_currentEntry->attributes()->customKeys();
if (customAttributes.size() > 0) {
m_ui->tabWidget->setTabEnabled(AttributesTab, true);
m_ui->attributesEdit->clear();
QString attributesText = QString();
void DetailsWidget::updateEntryAttributesTab()
{
Q_ASSERT(m_currentEntry);
m_ui->entryAttributesEdit->clear();
const EntryAttributes* attributes = m_currentEntry->attributes();
const QStringList customAttributes = attributes->customKeys();
const bool haveAttributes = customAttributes.size() > 0;
setTabEnabled(m_ui->entryTabWidget, m_ui->entryAttributesTab, haveAttributes);
if (haveAttributes) {
QString attributesText;
for (const QString& key : customAttributes) {
QString value = m_currentEntry->attributes()->value(key);
if (m_currentEntry->attributes()->isProtected(key)) {
@ -182,127 +219,117 @@ void DetailsWidget::getSelectedEntry(Entry* selectedEntry)
}
attributesText.append(QString("<b>%1</b>: %2<br/>").arg(key, value));
}
m_ui->attributesEdit->setText(attributesText);
m_ui->entryAttributesEdit->setText(attributesText);
}
}
void DetailsWidget::updateEntryAttachmentsTab()
{
Q_ASSERT(m_currentEntry);
const bool hasAttachments = !m_currentEntry->attachments()->isEmpty();
m_ui->tabWidget->setTabEnabled(AttachmentsTab, hasAttachments);
m_ui->attachmentsWidget->setEntryAttachments(m_currentEntry->attachments());
setTabEnabled(m_ui->entryTabWidget, m_ui->entryAttachmentsTab, hasAttachments);
m_ui->entryAttachmentsWidget->setEntryAttachments(m_currentEntry->attachments());
}
m_ui->autotypeTree->clear();
AutoTypeAssociations* autotypeAssociations = m_currentEntry->autoTypeAssociations();
void DetailsWidget::updateEntryAutotypeTab()
{
Q_ASSERT(m_currentEntry);
m_ui->entryAutotypeTree->clear();
QList<QTreeWidgetItem*> items;
for (auto assoc : autotypeAssociations->getAll()) {
QStringList association = QStringList() << assoc.window << assoc.sequence;
if (association.at(1).isEmpty()) {
association.replace(1, m_currentEntry->effectiveAutoTypeSequence());
}
items.append(new QTreeWidgetItem(m_ui->autotypeTree, association));
}
if (items.count() > 0) {
m_ui->autotypeTree->addTopLevelItems(items);
m_ui->tabWidget->setTabEnabled(AutotypeTab, true);
const AutoTypeAssociations* autotypeAssociations = m_currentEntry->autoTypeAssociations();
const auto associations = autotypeAssociations->getAll();
for (const auto& assoc : associations) {
const QString sequence = assoc.sequence.isEmpty() ? m_currentEntry->effectiveAutoTypeSequence()
: assoc.sequence;
items.append(new QTreeWidgetItem(m_ui->entryAutotypeTree, {assoc.window, sequence}));
}
if (m_ui->tabWidget->isTabEnabled(m_selectedTabEntry)) {
m_ui->tabWidget->setCurrentIndex(m_selectedTabEntry);
}
m_ui->entryAutotypeTree->addTopLevelItems(items);
setTabEnabled(m_ui->entryTabWidget, m_ui->entryAutotypeTab, !items.isEmpty());
}
void DetailsWidget::getSelectedGroup(Group* selectedGroup)
void DetailsWidget::updateGroupHeaderLine()
{
if (!selectedGroup) {
hideDetails();
return;
Q_ASSERT(m_currentGroup);
m_ui->groupTitleLabel->setText(hierarchy(m_currentGroup, QString()));
m_ui->groupIcon->setPixmap(preparePixmap(m_currentGroup->iconPixmap(), 32));
}
m_currentGroup = selectedGroup;
void DetailsWidget::updateGroupGeneralTab()
{
Q_ASSERT(m_currentGroup);
const QString searchingText = m_currentGroup->resolveSearchingEnabled() ? tr("Enabled") : tr("Disabled");
m_ui->groupSearchingLabel->setText(searchingText);
if (!config()->get("GUI/HideDetailsView").toBool()) {
this->show();
const QString autotypeText = m_currentGroup->resolveAutoTypeEnabled() ? tr("Enabled") : tr("Disabled");
m_ui->groupAutotypeLabel->setText(autotypeText);
const TimeInfo groupTime = m_currentGroup->timeInfo();
const QString expiresText = groupTime.expires() ? groupTime.expiryTime().toString(Qt::DefaultLocaleShortDate)
: tr("Never");
m_ui->groupExpirationLabel->setText(expiresText);
}
m_ui->stackedWidget->setCurrentIndex(GroupPreview);
if (m_ui->tabWidget->count() > 2) {
m_ui->tabWidget->removeTab(AutotypeTab);
m_ui->tabWidget->removeTab(AttachmentsTab);
m_ui->tabWidget->removeTab(AttributesTab);
void DetailsWidget::updateGroupNotesTab()
{
Q_ASSERT(m_currentGroup);
const QString notes = m_currentGroup->notes();
setTabEnabled(m_ui->groupTabWidget, m_ui->groupNotesTab, !notes.isEmpty());
m_ui->groupNotesEdit->setText(notes);
}
m_ui->tabWidget->setTabEnabled(GroupNotesTab, false);
m_ui->totpButton->hide();
m_ui->totpWidget->hide();
auto icon = m_currentGroup->iconPixmap();
if (icon.width() > 32 || icon.height() > 32) {
icon = icon.scaled(32, 32);
void DetailsWidget::stopTotpTimer()
{
if (m_totpTimer) {
m_totpTimer->stop();
}
m_ui->entryIcon->setPixmap(icon);
QString title = " / ";
QStringList hierarchy = m_currentGroup->hierarchy();
hierarchy.removeFirst();
title += hierarchy.join(" / ");
if (hierarchy.size() > 0) {
title += " / ";
}
m_ui->titleLabel->setText(title);
QString notes = m_currentGroup->notes();
if (!notes.isEmpty()) {
m_ui->tabWidget->setTabEnabled(GroupNotesTab, true);
m_ui->notesEdit->setText(notes);
}
QString searching = tr("Disabled");
if (m_currentGroup->resolveSearchingEnabled()) {
searching = tr("Enabled");
void DetailsWidget::deleteTotpTimer()
{
if (m_totpTimer) {
delete m_totpTimer;
}
m_ui->searchingLabel->setText(searching);
QString autotype = tr("Disabled");
if (m_currentGroup->resolveAutoTypeEnabled()) {
autotype = tr("Enabled");
}
m_ui->autotypeLabel->setText(autotype);
TimeInfo groupTime = m_currentGroup->timeInfo();
if (groupTime.expires()) {
m_ui->groupExpirationLabel->setText(groupTime.expiryTime().toString(Qt::DefaultLocaleShortDate));
void DetailsWidget::updateTotpLabel()
{
if (!m_locked && m_currentEntry) {
const QString totpCode = m_currentEntry->totp();
const QString firstHalf = totpCode.left(totpCode.size() / 2);
const QString secondHalf = totpCode.mid(totpCode.size() / 2);
m_ui->entryTotpLabel->setText(firstHalf + " " + secondHalf);
} else {
m_ui->groupExpirationLabel->setText(tr("Never"));
}
if (m_ui->tabWidget->isTabEnabled(m_selectedTabGroup)) {
m_ui->tabWidget->setCurrentIndex(m_selectedTabGroup);
m_ui->entryTotpLabel->clear();
stopTotpTimer();
}
}
void DetailsWidget::updateTotp()
void DetailsWidget::updateTabIndexes()
{
if (!m_locked) {
QString totpCode = m_currentEntry->totp();
QString firstHalf = totpCode.left(totpCode.size() / 2);
QString secondHalf = totpCode.mid(totpCode.size() / 2);
m_ui->totpLabel->setText(firstHalf + " " + secondHalf);
} else if (m_timer) {
m_timer->stop();
}
m_selectedTabEntry = m_ui->entryTabWidget->currentIndex();
m_selectedTabGroup = m_ui->groupTabWidget->currentIndex();
}
void DetailsWidget::showTotp(bool visible)
void DetailsWidget::setTabEnabled(QTabWidget* tabWidget, QWidget* widget, bool enabled)
{
if (visible) {
m_ui->totpWidget->show();
} else {
m_ui->totpWidget->hide();
}
const int tabIndex = tabWidget->indexOf(widget);
Q_ASSERT(tabIndex != -1);
tabWidget->setTabEnabled(tabIndex, enabled);
}
QString DetailsWidget::shortUrl(QString url)
QPixmap DetailsWidget::preparePixmap(const QPixmap& pixmap, int size)
{
if (pixmap.width() > size || pixmap.height() > size) {
return pixmap.scaled(size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
}
return pixmap;
}
QString DetailsWidget::shortUrl(const QString& url)
{
// TODO: create elided text
QString newurl = "";
if (url.length() > 60) {
newurl.append(url.left(20));
@ -313,43 +340,19 @@ QString DetailsWidget::shortUrl(QString url)
return url;
}
QString DetailsWidget::shortPassword(QString password)
QString DetailsWidget::shortPassword(const QString& password)
{
QString newpassword = "";
// TODO: create elided text
if (password.length() > 60) {
newpassword.append(password.left(50));
newpassword.append("");
return newpassword;
return QString("%1…").arg(password.left(60));
}
return password;
}
void DetailsWidget::hideDetails()
QString DetailsWidget::hierarchy(const Group* group, const QString& title)
{
this->hide();
}
void DetailsWidget::setDatabaseMode(DatabaseWidget::Mode mode)
{
m_locked = false;
if (mode == DatabaseWidget::LockedMode) {
m_locked = true;
return;
}
if (mode == DatabaseWidget::ViewMode) {
if (m_ui->stackedWidget->currentIndex() == GroupPreview) {
getSelectedGroup(m_currentGroup);
} else {
getSelectedEntry(m_currentEntry);
}
}
}
void DetailsWidget::updateTabIndex(int index)
{
if (m_ui->stackedWidget->currentIndex() == GroupPreview) {
m_selectedTabGroup = index;
} else {
m_selectedTabEntry = index;
}
QStringList hierarchy = group->hierarchy();
hierarchy.removeFirst();
hierarchy.append(title);
return QString("%1%2").arg(hierarchySeparator, hierarchy.join(hierarchySeparator));
}

View File

@ -19,8 +19,8 @@
#define KEEPASSX_DETAILSWIDGET_H
#include "gui/DatabaseWidget.h"
#include <QWidget>
#include <QTimer>
namespace Ui {
class DetailsWidget;
@ -34,48 +34,47 @@ public:
explicit DetailsWidget(QWidget* parent = nullptr);
~DetailsWidget();
enum StackedWidgetIndex
{
EntryPreview = 0,
GroupPreview = 1,
};
enum TabWidgetIndex
{
GeneralTab = 0,
AttributesTab = 1,
GroupNotesTab = 1,
AttachmentsTab = 2,
NotesTab = 3,
AutotypeTab = 4,
};
public slots:
void setEntry(Entry* selectedEntry);
void setGroup(Group* selectedGroup);
void setDatabaseMode(DatabaseWidget::Mode mode);
signals:
void errorOccurred(const QString& error);
private slots:
void getSelectedEntry(Entry* selectedEntry);
void getSelectedGroup(Group* selectedGroup);
void showTotp(bool visible);
void updateTotp();
void hideDetails();
void setDatabaseMode(DatabaseWidget::Mode mode);
void updateTabIndex(int index);
void updateEntryHeaderLine();
void updateEntryTotp();
void updateEntryGeneralTab();
void updateEntryNotesTab();
void updateEntryAttributesTab();
void updateEntryAttachmentsTab();
void updateEntryAutotypeTab();
void updateGroupHeaderLine();
void updateGroupGeneralTab();
void updateGroupNotesTab();
void stopTotpTimer();
void deleteTotpTimer();
void updateTotpLabel();
void updateTabIndexes();
private:
void setTabEnabled(QTabWidget *tabWidget, QWidget* widget, bool enabled);
static QPixmap preparePixmap(const QPixmap& pixmap, int size);
static QString shortUrl(const QString& url);
static QString shortPassword(const QString& password);
static QString hierarchy(const Group* group, const QString& title);
const QScopedPointer<Ui::DetailsWidget> m_ui;
bool m_locked;
Entry* m_currentEntry;
Group* m_currentGroup;
quint8 m_step;
QPointer<QTimer> m_timer = nullptr;
QWidget* m_attributesTabWidget;
QWidget* m_attachmentsTabWidget;
QWidget* m_autotypeTabWidget;
QPointer<QTimer> m_totpTimer;
quint8 m_selectedTabEntry;
quint8 m_selectedTabGroup;
QString shortUrl(QString url);
QString shortPassword(QString password);
};
#endif // KEEPASSX_DETAILSWIDGET_H

View File

@ -2,9 +2,31 @@
<ui version="4.0">
<class>DetailsWidget</class>
<widget class="QWidget" name="DetailsWidget">
<layout class="QVBoxLayout" name="verticalLayout_7">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="pageEntry">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0,0,0,0,0,0">
<layout class="QHBoxLayout" name="entryHorizontalLayout" stretch="0,0,0,0,0,0">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
@ -22,7 +44,7 @@
</widget>
</item>
<item>
<widget class="QLabel" name="titleLabel">
<widget class="QLabel" name="entryTitleLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
@ -40,7 +62,7 @@
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<spacer name="entryHorizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
@ -53,7 +75,7 @@
</spacer>
</item>
<item>
<widget class="QWidget" name="totpWidget" native="true">
<widget class="QWidget" name="entryTotpWidget" native="true">
<layout class="QGridLayout" name="gridLayout_3">
<property name="leftMargin">
<number>0</number>
@ -65,7 +87,7 @@
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="totpLabel">
<widget class="QLabel" name="entryTotpLabel">
<property name="font">
<font>
<pointsize>10</pointsize>
@ -82,7 +104,7 @@
</widget>
</item>
<item>
<widget class="QToolButton" name="totpButton">
<widget class="QToolButton" name="entryTotpButton">
<property name="toolTip">
<string>Generate TOTP Token</string>
</property>
@ -95,7 +117,7 @@
</widget>
</item>
<item>
<widget class="QToolButton" name="closeButton">
<widget class="QToolButton" name="entryCloseButton">
<property name="toolTip">
<string>Close</string>
</property>
@ -107,20 +129,10 @@
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTabWidget" name="tabWidget">
<widget class="QTabWidget" name="entryTabWidget">
<property name="currentIndex">
<number>0</number>
</property>
@ -133,49 +145,16 @@
<property name="movable">
<bool>false</bool>
</property>
<widget class="QWidget" name="generalTab">
<widget class="QWidget" name="entryGeneralTab">
<attribute name="title">
<string>General</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="entryPage">
<widget class="QWidget" name="entryGeneralWidget" native="true">
<layout class="QGridLayout" name="gridLayout" columnstretch="0,0,1">
<item row="0" column="2">
<widget class="QLabel" name="usernameLabel">
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="passwordLabel_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Password</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="0">
<spacer name="horizontalSpacer_2">
<spacer name="entryLeftHorizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
@ -190,87 +169,8 @@
</property>
</spacer>
</item>
<item row="3" column="2">
<widget class="QLabel" name="expirationLabel"/>
</item>
<item row="4" column="2">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="1">
<widget class="QLabel" name="urlLabel_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>URL</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="urlLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="expirationLabel_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Expiration</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="passwordLabel"/>
</item>
<item row="0" column="1">
<widget class="QLabel" name="usernameLabel_2">
<widget class="QLabel" name="entryUsernameTitleLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
@ -294,12 +194,15 @@
</property>
</widget>
</item>
</layout>
<item row="0" column="2">
<widget class="QLabel" name="entryUsernameLabel">
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
<widget class="QWidget" name="groupPage">
<layout class="QGridLayout" name="gridLayout_4" columnstretch="0,0,1">
<item row="0" column="1">
<widget class="QLabel" name="autotypeLabel_2">
</item>
<item row="1" column="1">
<widget class="QLabel" name="entryPasswordTitleLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
@ -313,7 +216,32 @@
</font>
</property>
<property name="text">
<string>Autotype</string>
<string>Password</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="entryPasswordLabel"/>
</item>
<item row="2" column="1">
<widget class="QLabel" name="entryUrlTitleLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>URL</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
@ -321,72 +249,23 @@
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="groupExpirationLabel">
<widget class="QLabel" name="entryUrlLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="3" column="2">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="2">
<widget class="QLabel" name="searchingLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="searchingLabel_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="text">
<string>Searching</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
<string/>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="autotypeLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="groupExpirationLabel_2">
<item row="3" column="1">
<widget class="QLabel" name="entryExpirationTitleLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
@ -407,35 +286,34 @@
</property>
</widget>
</item>
<item row="0" column="0">
<spacer name="horizontalSpacer_3">
<item row="3" column="2">
<widget class="QLabel" name="entryExpirationLabel"/>
</item>
<item row="4" column="2">
<spacer name="entryBottomVerticalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="attributesTab">
<widget class="QWidget" name="entryAttributesTab">
<attribute name="title">
<string>Attributes</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTextEdit" name="attributesEdit">
<widget class="QTextEdit" name="entryAttributesEdit">
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
@ -446,23 +324,23 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="attachmentsTab">
<widget class="QWidget" name="entryAttachmentsTab">
<attribute name="title">
<string>Attachments</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="EntryAttachmentsWidget" name="attachmentsWidget" native="true"/>
<widget class="EntryAttachmentsWidget" name="entryAttachmentsWidget" native="true"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="notesTab">
<widget class="QWidget" name="entryNotesTab">
<attribute name="title">
<string>Notes</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QTextEdit" name="notesEdit">
<widget class="QTextEdit" name="entryNotesEdit">
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
@ -473,13 +351,13 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="autotypeTab">
<widget class="QWidget" name="entryAutotypeTab">
<attribute name="title">
<string>Autotype</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QTreeWidget" name="autotypeTree">
<widget class="QTreeWidget" name="entryAutotypeTree">
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
@ -524,6 +402,249 @@
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="pageGroup">
<layout class="QVBoxLayout" name="verticalLayout_13">
<item>
<layout class="QHBoxLayout" name="groupHorizontalLayout" stretch="0,0,0,0">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<item>
<widget class="QLabel" name="groupIcon">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="groupTitleLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="textFormat">
<enum>Qt::AutoText</enum>
</property>
</widget>
</item>
<item>
<spacer name="groupHorizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="groupCloseButton">
<property name="toolTip">
<string>Close</string>
</property>
<property name="text">
<string/>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTabWidget" name="groupTabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<property name="documentMode">
<bool>false</bool>
</property>
<property name="tabsClosable">
<bool>false</bool>
</property>
<property name="movable">
<bool>false</bool>
</property>
<widget class="QWidget" name="groupGeneralTab">
<attribute name="title">
<string>General</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_8">
<item>
<widget class="QWidget" name="groupGeneralWidget" native="true">
<layout class="QGridLayout" name="gridLayout_2" columnstretch="0,0,1">
<item row="0" column="0">
<spacer name="groupLeftHorizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="1">
<widget class="QLabel" name="groupAutotypeTitleLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Autotype</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="groupAutotypeLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="groupSearchingTitleLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Searching</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="groupSearchingLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="groupExpirationTitleLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Expiration</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="groupExpirationLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="3" column="2">
<spacer name="groupBottomVerticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="groupNotesTab">
<attribute name="title">
<string>Notes</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_11">
<item>
<widget class="QTextEdit" name="groupNotesEdit">
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
<action name="searchIcon">
<property name="text">
<string>Search</string>