Code cleanup

Comprehensive code cleanup:
- formatting
- comments
- obsolete code
This commit is contained in:
Fonic 2018-01-16 13:20:45 +01:00
parent 9ff648177c
commit 8c78aca69e
11 changed files with 220 additions and 657 deletions

View File

@ -138,13 +138,8 @@ void Config::init(const QString& fileName)
m_defaults.insert("GUI/DarkTrayIcon", false); m_defaults.insert("GUI/DarkTrayIcon", false);
m_defaults.insert("GUI/MinimizeToTray", false); m_defaults.insert("GUI/MinimizeToTray", false);
m_defaults.insert("GUI/MinimizeOnClose", false); m_defaults.insert("GUI/MinimizeOnClose", false);
/** m_defaults.insert("GUI/HideUsernames", false);
* @author Fonic <https://github.com/fonic> m_defaults.insert("GUI/HidePasswords", true);
* Set defaults for state of 'Hide Usernames'/'Hide Passwords' settings
* of entry view
*/
m_defaults.insert("GUI/EntryHideUsernames", false);
m_defaults.insert("GUI/EntryHidePasswords", true);
} }
Config* Config::instance() Config* Config::instance()

View File

@ -181,13 +181,7 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent)
connect(m_mainSplitter, SIGNAL(splitterMoved(int,int)), SIGNAL(mainSplitterSizesChanged())); connect(m_mainSplitter, SIGNAL(splitterMoved(int,int)), SIGNAL(mainSplitterSizesChanged()));
connect(m_detailSplitter, SIGNAL(splitterMoved(int,int)), SIGNAL(detailSplitterSizesChanged())); connect(m_detailSplitter, SIGNAL(splitterMoved(int,int)), SIGNAL(detailSplitterSizesChanged()));
/**
* @author Fonic <https://github.com/fonic>
* Connect signal to pass through state changes of entry view view
*/
connect(m_entryView, SIGNAL(viewStateChanged()), SIGNAL(entryViewStateChanged())); connect(m_entryView, SIGNAL(viewStateChanged()), SIGNAL(entryViewStateChanged()));
connect(m_groupView, SIGNAL(groupChanged(Group*)), this, SLOT(onGroupChanged(Group*))); connect(m_groupView, SIGNAL(groupChanged(Group*)), this, SLOT(onGroupChanged(Group*)));
connect(m_groupView, SIGNAL(groupChanged(Group*)), SIGNAL(groupChanged())); connect(m_groupView, SIGNAL(groupChanged(Group*)), SIGNAL(groupChanged()));
connect(m_entryView, SIGNAL(entryActivated(Entry*, EntryModel::ModelColumn)), connect(m_entryView, SIGNAL(entryActivated(Entry*, EntryModel::ModelColumn)),
@ -297,55 +291,49 @@ void DatabaseWidget::setDetailSplitterSizes(const QList<int> &sizes)
} }
/** /**
* @author Fonic <https://github.com/fonic>
* Get current state of entry view 'Hide Usernames' setting * Get current state of entry view 'Hide Usernames' setting
*/ */
bool DatabaseWidget::entryViewHideUsernames() const bool DatabaseWidget::isUsernamesHidden() const
{ {
return m_entryView->hideUsernames(); return m_entryView->isUsernamesHidden();
} }
/** /**
* @author Fonic <https://github.com/fonic>
* Set state of entry view 'Hide Usernames' setting * Set state of entry view 'Hide Usernames' setting
*/ */
void DatabaseWidget::setEntryViewHideUsernames(const bool hide) void DatabaseWidget::setUsernamesHidden(const bool hide)
{ {
m_entryView->setHideUsernames(hide); m_entryView->setUsernamesHidden(hide);
} }
/** /**
* @author Fonic <https://github.com/fonic>
* Get current state of entry view 'Hide Passwords' setting * Get current state of entry view 'Hide Passwords' setting
*/ */
bool DatabaseWidget::entryViewHidePasswords() const bool DatabaseWidget::isPasswordsHidden() const
{ {
return m_entryView->hidePasswords(); return m_entryView->isPasswordsHidden();
} }
/** /**
* @author Fonic <https://github.com/fonic>
* Set state of entry view 'Hide Passwords' setting * Set state of entry view 'Hide Passwords' setting
*/ */
void DatabaseWidget::setEntryViewHidePasswords(const bool hide) void DatabaseWidget::setPasswordsHidden(const bool hide)
{ {
m_entryView->setHidePasswords(hide); m_entryView->setPasswordsHidden(hide);
} }
/** /**
* @author Fonic <https://github.com/fonic> * Get current view state of entry view
* Get current state of entry view view
*/ */
QByteArray DatabaseWidget::entryViewViewState() const QByteArray DatabaseWidget::entryViewState() const
{ {
return m_entryView->viewState(); return m_entryView->viewState();
} }
/** /**
* @author Fonic <https://github.com/fonic> * Set view state of entry view
* Set state of entry view view
*/ */
bool DatabaseWidget::setEntryViewViewState(const QByteArray& state) const bool DatabaseWidget::setEntryViewState(const QByteArray& state) const
{ {
return m_entryView->setViewState(state); return m_entryView->setViewState(state);
} }
@ -696,8 +684,8 @@ void DatabaseWidget::openUrlForEntry(Entry* entry)
void DatabaseWidget::createGroup() void DatabaseWidget::createGroup()
{ {
Q_ASSERT(m_groupView->currentGroup());
if (!m_groupView->currentGroup()) { if (!m_groupView->currentGroup()) {
Q_ASSERT(false);
return; return;
} }
@ -710,8 +698,8 @@ void DatabaseWidget::createGroup()
void DatabaseWidget::deleteGroup() void DatabaseWidget::deleteGroup()
{ {
Group* currentGroup = m_groupView->currentGroup(); Group* currentGroup = m_groupView->currentGroup();
Q_ASSERT(currentGroup && canDeleteCurrentGroup());
if (!currentGroup || !canDeleteCurrentGroup()) { if (!currentGroup || !canDeleteCurrentGroup()) {
Q_ASSERT(false);
return; return;
} }
@ -944,23 +932,14 @@ void DatabaseWidget::unlockDatabase(bool accepted)
} }
} }
/**
* @author Fonic <https://github.com/fonic>
* Add 'copy-on-doubleclick' functionality for certain columns
*
* TODO:
* If pull request #1298 gets merged, double-clicking column 'Attachments'
* could open the new details view (see second screenshot)
*/
void DatabaseWidget::entryActivationSignalReceived(Entry* entry, EntryModel::ModelColumn column) void DatabaseWidget::entryActivationSignalReceived(Entry* entry, EntryModel::ModelColumn column)
{ {
/* Should never happen */ Q_ASSERT(entry);
if (!entry) { if (!entry) {
Q_ASSERT(false);
return; return;
} }
/* Decide what to do based on specified column */ // Implement 'copy-on-doubleclick' functionality for certain columns
switch (column) { switch (column) {
case EntryModel::Username: case EntryModel::Username:
setClipboardTextAndMinimize(entry->resolveMultiplePlaceholders(entry->username())); setClipboardTextAndMinimize(entry->resolveMultiplePlaceholders(entry->username()));
@ -973,9 +952,12 @@ void DatabaseWidget::entryActivationSignalReceived(Entry* entry, EntryModel::Mod
openUrlForEntry(entry); openUrlForEntry(entry);
} }
break; break;
case EntryModel::Notes: // TODO: switch to 'Notes' tab in details view/pane
setClipboardTextAndMinimize(entry->resolveMultiplePlaceholders(entry->notes())); //case EntryModel::Notes:
break; // break;
// TODO: switch to 'Attachments' tab in details view/pane
//case EntryModel::Attachments:
// break;
default: default:
switchToEntryEdit(entry); switchToEntryEdit(entry);
} }

View File

@ -92,23 +92,12 @@ public:
void setMainSplitterSizes(const QList<int>& sizes); void setMainSplitterSizes(const QList<int>& sizes);
QList<int> detailSplitterSizes() const; QList<int> detailSplitterSizes() const;
void setDetailSplitterSizes(const QList<int>& sizes); void setDetailSplitterSizes(const QList<int>& sizes);
bool isUsernamesHidden() const;
/** void setUsernamesHidden(const bool hide);
* @author Fonic <https://github.com/fonic> bool isPasswordsHidden() const;
* Methods to get/set state of entry view 'Hide Usernames'/'Hide void setPasswordsHidden(const bool hide);
* Passwords' settings QByteArray entryViewState() const;
*/ bool setEntryViewState(const QByteArray& state) const;
bool entryViewHideUsernames() const;
void setEntryViewHideUsernames(const bool hide);
bool entryViewHidePasswords() const;
void setEntryViewHidePasswords(const bool hide);
/**
* @author Fonic <https://github.com/fonic>
* Methods to get/set state of entry view view state
*/
QByteArray entryViewViewState() const;
bool setEntryViewViewState(const QByteArray& state) const;
void clearAllWidgets(); void clearAllWidgets();
bool currentEntryHasTitle(); bool currentEntryHasTitle();
bool currentEntryHasUsername(); bool currentEntryHasUsername();
@ -142,13 +131,7 @@ signals:
void searchModeActivated(); void searchModeActivated();
void mainSplitterSizesChanged(); void mainSplitterSizesChanged();
void detailSplitterSizesChanged(); void detailSplitterSizesChanged();
/**
* @author Fonic <https://github.com/fonic>
* Signal to notify about state changes entry view view
*/
void entryViewStateChanged(); void entryViewStateChanged();
void updateSearch(QString text); void updateSearch(QString text);
public slots: public slots:

View File

@ -27,40 +27,20 @@ DatabaseWidgetStateSync::DatabaseWidgetStateSync(QObject* parent)
{ {
m_mainSplitterSizes = variantToIntList(config()->get("GUI/SplitterState")); m_mainSplitterSizes = variantToIntList(config()->get("GUI/SplitterState"));
m_detailSplitterSizes = variantToIntList(config()->get("GUI/DetailSplitterState")); m_detailSplitterSizes = variantToIntList(config()->get("GUI/DetailSplitterState"));
m_hideUsernames = config()->get("GUI/HideUsernames").toBool();
/** m_hidePasswords = config()->get("GUI/HidePasswords").toBool();
* @author Fonic <https://github.com/fonic> m_listViewState = config()->get("GUI/ListViewState").toByteArray();
* Load state of entry view 'Hide Usernames'/'Hide Passwords' settings m_searchViewState = config()->get("GUI/SearchViewState").toByteArray();
*/
m_entryHideUsernames = config()->get("GUI/EntryHideUsernames").toBool();
m_entryHidePasswords = config()->get("GUI/EntryHidePasswords").toBool();
/**
* @author Fonic <https://github.com/fonic>
* Load states of entry view list/search view
*/
m_entryListViewState = config()->get("GUI/EntryListViewState").toByteArray();
m_entrySearchViewState = config()->get("GUI/EntrySearchViewState").toByteArray();
} }
DatabaseWidgetStateSync::~DatabaseWidgetStateSync() DatabaseWidgetStateSync::~DatabaseWidgetStateSync()
{ {
config()->set("GUI/SplitterState", intListToVariant(m_mainSplitterSizes)); config()->set("GUI/SplitterState", intListToVariant(m_mainSplitterSizes));
config()->set("GUI/DetailSplitterState", intListToVariant(m_detailSplitterSizes)); config()->set("GUI/DetailSplitterState", intListToVariant(m_detailSplitterSizes));
config()->set("GUI/HideUsernames", m_hideUsernames);
/** config()->set("GUI/HidePasswords", m_hidePasswords);
* @author Fonic <https://github.com/fonic> config()->set("GUI/ListViewState", m_listViewState);
* Save state of entry view 'Hide Usernames'/'Hide Passwords' settings config()->set("GUI/SearchViewState", m_searchViewState);
*/
config()->set("GUI/EntryHideUsernames", m_entryHideUsernames);
config()->set("GUI/EntryHidePasswords", m_entryHidePasswords);
/**
* @author Fonic <https://github.com/fonic>
* Save states of entry view list/search view
*/
config()->set("GUI/EntryListViewState", m_entryListViewState);
config()->set("GUI/EntrySearchViewState", m_entrySearchViewState);
} }
void DatabaseWidgetStateSync::setActive(DatabaseWidget* dbWidget) void DatabaseWidgetStateSync::setActive(DatabaseWidget* dbWidget)
@ -94,14 +74,8 @@ void DatabaseWidgetStateSync::setActive(DatabaseWidget* dbWidget)
SLOT(updateSplitterSizes())); SLOT(updateSplitterSizes()));
connect(m_activeDbWidget, SIGNAL(detailSplitterSizesChanged()), connect(m_activeDbWidget, SIGNAL(detailSplitterSizesChanged()),
SLOT(updateSplitterSizes())); SLOT(updateSplitterSizes()));
/**
* @author Fonic <https://github.com/fonic>
* Connect signal to receive state changes of entry view view
*/
connect(m_activeDbWidget, SIGNAL(entryViewStateChanged()), connect(m_activeDbWidget, SIGNAL(entryViewStateChanged()),
SLOT(updateViewState())); SLOT(updateViewState()));
connect(m_activeDbWidget, SIGNAL(listModeActivated()), connect(m_activeDbWidget, SIGNAL(listModeActivated()),
SLOT(restoreListView())); SLOT(restoreListView()));
connect(m_activeDbWidget, SIGNAL(searchModeActivated()), connect(m_activeDbWidget, SIGNAL(searchModeActivated()),
@ -114,58 +88,54 @@ void DatabaseWidgetStateSync::setActive(DatabaseWidget* dbWidget)
} }
/** /**
* @author Fonic <https://github.com/fonic>
* Restore entry view list view state * Restore entry view list view state
* *
* NOTE: * NOTE:
* States of entry view 'Hide Usernames'/'Hide Passwords' settings are considered * States of entry view 'Hide Usernames'/'Hide Passwords' settings are global,
* 'global', i.e. they are the same for both list and search mode * i.e. they are the same for both list and search mode
* *
* NOTE: * NOTE:
* If m_entryListViewState is empty, it is the first time after clean/invalid * If m_listViewState is empty, the list view has been activated for the first
* config that list view is activated. Thus, save its current state. Without * time after starting with a clean (or invalid) config. Thus, save the current
* this, m_entryListViewState would remain empty until there is an actual view * state. Without this, m_listViewState would remain empty until there is an
* state change (e.g. column is resized) * actual view state change (e.g. column is resized)
*/ */
void DatabaseWidgetStateSync::restoreListView() void DatabaseWidgetStateSync::restoreListView()
{ {
m_activeDbWidget->setEntryViewHideUsernames(m_entryHideUsernames); m_activeDbWidget->setUsernamesHidden(m_hideUsernames);
m_activeDbWidget->setEntryViewHidePasswords(m_entryHidePasswords); m_activeDbWidget->setPasswordsHidden(m_hidePasswords);
if (!m_entryListViewState.isEmpty()) { if (!m_listViewState.isEmpty()) {
m_activeDbWidget->setEntryViewViewState(m_entryListViewState); m_activeDbWidget->setEntryViewState(m_listViewState);
} } else {
else { m_listViewState = m_activeDbWidget->entryViewState();
m_entryListViewState = m_activeDbWidget->entryViewViewState();
} }
m_blockUpdates = false; m_blockUpdates = false;
} }
/** /**
* @author Fonic <https://github.com/fonic>
* Restore entry view search view state * Restore entry view search view state
* *
* NOTE: * NOTE:
* States of entry view 'Hide Usernames'/'Hide Passwords' settings are considered * States of entry view 'Hide Usernames'/'Hide Passwords' settings are global,
* 'global', i.e. they are the same for both list and search mode * i.e. they are the same for both list and search mode
* *
* NOTE: * NOTE:
* If m_entrySearchViewState is empty, it is the first time after clean/invalid * If m_searchViewState is empty, the search view has been activated for the
* config that search view is activated. Thus, save its current state. Without * first time after starting with a clean (or invalid) config. Thus, save the
* this, m_entrySearchViewState would remain empty until there is an actual view * current state. Without this, m_searchViewState would remain empty until
* state change (e.g. column is resized) * there is an actual view state change (e.g. column is resized)
*/ */
void DatabaseWidgetStateSync::restoreSearchView() void DatabaseWidgetStateSync::restoreSearchView()
{ {
m_activeDbWidget->setEntryViewHideUsernames(m_entryHideUsernames); m_activeDbWidget->setUsernamesHidden(m_hideUsernames);
m_activeDbWidget->setEntryViewHidePasswords(m_entryHidePasswords); m_activeDbWidget->setPasswordsHidden(m_hidePasswords);
if (!m_entrySearchViewState.isEmpty()) { if (!m_searchViewState.isEmpty()) {
m_activeDbWidget->setEntryViewViewState(m_entrySearchViewState); m_activeDbWidget->setEntryViewState(m_searchViewState);
} } else {
else { m_searchViewState = m_activeDbWidget->entryViewState();
m_entrySearchViewState = m_activeDbWidget->entryViewViewState();
} }
m_blockUpdates = false; m_blockUpdates = false;
@ -187,10 +157,11 @@ void DatabaseWidgetStateSync::updateSplitterSizes()
} }
/** /**
* @author Fonic <https://github.com/fonic> * Update entry view list/search view state
* Update entry view list/search view state (NOTE: states of entry view *
* 'Hide Usernames'/'Hide Passwords' settings are considered 'global', * NOTE:
* i.e. they are the same for both list and search mode) * States of entry view 'Hide Usernames'/'Hide Passwords' settings are global,
* i.e. they are the same for both list and search mode
*/ */
void DatabaseWidgetStateSync::updateViewState() void DatabaseWidgetStateSync::updateViewState()
{ {
@ -198,14 +169,13 @@ void DatabaseWidgetStateSync::updateViewState()
return; return;
} }
m_entryHideUsernames = m_activeDbWidget->entryViewHideUsernames(); m_hideUsernames = m_activeDbWidget->isUsernamesHidden();
m_entryHidePasswords = m_activeDbWidget->entryViewHidePasswords(); m_hidePasswords = m_activeDbWidget->isPasswordsHidden();
if (m_activeDbWidget->isInSearchMode()) { if (m_activeDbWidget->isInSearchMode()) {
m_entrySearchViewState = m_activeDbWidget->entryViewViewState(); m_searchViewState = m_activeDbWidget->entryViewState();
} } else {
else { m_listViewState = m_activeDbWidget->entryViewState();
m_entryListViewState = m_activeDbWidget->entryViewViewState();
} }
} }
@ -219,8 +189,7 @@ QList<int> DatabaseWidgetStateSync::variantToIntList(const QVariant& variant)
int size = var.toInt(&ok); int size = var.toInt(&ok);
if (ok) { if (ok) {
result.append(size); result.append(size);
} } else {
else {
result.clear(); result.clear();
break; break;
} }
@ -239,4 +208,3 @@ QVariant DatabaseWidgetStateSync::intListToVariant(const QList<int>& list)
return result; return result;
} }

View File

@ -37,11 +37,6 @@ public slots:
private slots: private slots:
void blockUpdates(); void blockUpdates();
void updateSplitterSizes(); void updateSplitterSizes();
/**
* @author Fonic <https://github.com/fonic>
* Slot to update entry view view state
*/
void updateViewState(); void updateViewState();
private: private:
@ -54,21 +49,11 @@ private:
QList<int> m_mainSplitterSizes; QList<int> m_mainSplitterSizes;
QList<int> m_detailSplitterSizes; QList<int> m_detailSplitterSizes;
/** bool m_hideUsernames;
* @author Fonic <https://github.com/fonic> bool m_hidePasswords;
* Properties to store state of entry view 'Hide Usernames'/'Hide
* Passwords' settings
*/
bool m_entryHideUsernames;
bool m_entryHidePasswords;
/** QByteArray m_listViewState;
* @author Fonic <https://github.com/fonic> QByteArray m_searchViewState;
* Properties to store states of entry view list/search view (replaces
* m_columnSizesList/m_columnSizesSearch)
*/
QByteArray m_entryListViewState;
QByteArray m_entrySearchViewState;
}; };
#endif // KEEPASSX_DATABASEWIDGETSTATESYNC_H #endif // KEEPASSX_DATABASEWIDGETSTATESYNC_H

View File

@ -127,7 +127,7 @@ void DetailsWidget::getSelectedEntry(Entry* selectedEntry)
shortPassword(m_currentEntry->resolveMultiplePlaceholders(m_currentEntry->password()))); shortPassword(m_currentEntry->resolveMultiplePlaceholders(m_currentEntry->password())));
m_ui->passwordLabel->setToolTip(m_currentEntry->resolveMultiplePlaceholders(m_currentEntry->password())); m_ui->passwordLabel->setToolTip(m_currentEntry->resolveMultiplePlaceholders(m_currentEntry->password()));
} else { } else {
m_ui->passwordLabel->setText("****"); m_ui->passwordLabel->setText(QString("\u25cf").repeated(6));
} }
QString url = m_currentEntry->webUrl(); QString url = m_currentEntry->webUrl();

View File

@ -20,11 +20,6 @@
#include <QFont> #include <QFont>
#include <QMimeData> #include <QMimeData>
#include <QPalette> #include <QPalette>
/**
* @author Fonic <https://github.com/fonic>
* Add include required for additional columns 'Expires', 'Created', 'Modified'
* and 'Accessed'
*/
#include <QDateTime> #include <QDateTime>
#include "core/DatabaseIcons.h" #include "core/DatabaseIcons.h"
@ -33,43 +28,15 @@
#include "core/Group.h" #include "core/Group.h"
#include "core/Metadata.h" #include "core/Metadata.h"
/** // String being displayed when hiding content
* @author Fonic <https://github.com/fonic> const QString EntryModel::HiddenContentDisplay(QString("\u25cf").repeated(6));
* Define constant string used to display hidden content in columns 'Username'
* and 'Password'
*
* TODO:
* Decide which of the proposed options should be used (stars, bullet, black
* circle)
*/
//const QString EntryModel::HiddenContentDisplay("******");
//const QString EntryModel::HiddenContentDisplay(QString(QChar(0x2022)).repeated(6));
const QString EntryModel::HiddenContentDisplay(QString(QChar(0x25CF)).repeated(6));
/** // Format used to display dates
* @author Fonic <https://github.com/fonic>
* Define date format used to display dates in columns 'Expires', 'Created',
* 'Modified' and 'Accessed'
*/
const Qt::DateFormat EntryModel::DateFormat = Qt::DefaultLocaleShortDate; const Qt::DateFormat EntryModel::DateFormat = Qt::DefaultLocaleShortDate;
/** // Paperclip symbol
* @author Fonic <https://github.com/fonic> const QString EntryModel::PaperClipDisplay("\U0001f4ce");
* Define constant string used to display header and data of column 'Paper-
* clip'
*
* TODO:
* When using unicode, ASAN reports memory leaks, but when using a plain
* string like 'x', no leaks are reported. Check if this is something to
* worry about, might as well be a Qt bug
*/
const QString EntryModel::PaperClipDisplay(QString("\U0001f4ce"));
/**
* @author Fonic <https://github.com/fonic>
* Initialize 'Hide Usernames' and 'Hide Passwords' settings using sane
* defaults (usernames visible, passwords hidden)
*/
EntryModel::EntryModel(QObject* parent) EntryModel::EntryModel(QObject* parent)
: QAbstractTableModel(parent) : QAbstractTableModel(parent)
, m_group(nullptr) , m_group(nullptr)
@ -153,27 +120,19 @@ int EntryModel::rowCount(const QModelIndex& parent) const
{ {
if (parent.isValid()) { if (parent.isValid()) {
return 0; return 0;
} } else {
else {
return m_entries.size(); return m_entries.size();
} }
} }
int EntryModel::columnCount(const QModelIndex& parent) const int EntryModel::columnCount(const QModelIndex& parent) const
{ {
/** // Advised by Qt documentation
* @author Fonic <https://github.com/fonic>
* Change column count to include additional columns 'Password', 'Notes',
* 'Expires', 'Created', 'Modified', 'Accessed', 'Paperclip' and
* 'Attachments'. Also, return 0 when parent is valid as advised by Qt
* documentation
*/
if (parent.isValid()) { if (parent.isValid()) {
return 0; return 0;
} }
else {
return 12; return 12;
}
} }
QVariant EntryModel::data(const QModelIndex& index, int role) const QVariant EntryModel::data(const QModelIndex& index, int role) const
@ -185,26 +144,6 @@ QVariant EntryModel::data(const QModelIndex& index, int role) const
Entry* entry = entryFromIndex(index); Entry* entry = entryFromIndex(index);
EntryAttributes* attr = entry->attributes(); EntryAttributes* attr = entry->attributes();
/**
* @author Fonic <https://github.com/fonic>
*
* Add display data providers for additional columns 'Password', 'Notes',
* 'Expires', 'Created', 'Modified', 'Accessed', 'Paperclip' and
* 'Attachments'
*
* Add ability to display usernames and passwords hidden or visible
* depending on current state of 'Hide Usernames' and 'Hide Passwords'
* settings
*
* TODO:
* Decide which of the additional columns should expand placeholders
* -> code added where applicable, but currently commented out
*
* Check what attr->isReference() does and if it applies to any of the
* additional columns
* -> code added for columns 'Password' and 'Notes', as EntryAttributes::
* PasswordKey and EntryAttributes::NotesKey already existed
*/
if (role == Qt::DisplayRole) { if (role == Qt::DisplayRole) {
QString result; QString result;
switch (index.column()) { switch (index.column()) {
@ -216,63 +155,44 @@ QVariant EntryModel::data(const QModelIndex& index, int role) const
case Title: case Title:
result = entry->resolveMultiplePlaceholders(entry->title()); result = entry->resolveMultiplePlaceholders(entry->title());
if (attr->isReference(EntryAttributes::TitleKey)) { if (attr->isReference(EntryAttributes::TitleKey)) {
result.prepend(tr("Ref: ","Reference abbreviation")); result.prepend(tr("Ref: ", "Reference abbreviation"));
} }
return result; return result;
case Username: case Username:
/*
* Display usernames hidden or visible according to current state
* of 'Hide Usernames' setting
*/
if (m_hideUsernames) { if (m_hideUsernames) {
result = EntryModel::HiddenContentDisplay; result = EntryModel::HiddenContentDisplay;
} } else {
else {
//result = entry->username();
result = entry->resolveMultiplePlaceholders(entry->username()); result = entry->resolveMultiplePlaceholders(entry->username());
} }
if (attr->isReference(EntryAttributes::UserNameKey)) { if (attr->isReference(EntryAttributes::UserNameKey)) {
result.prepend(tr("Ref: ","Reference abbreviation")); result.prepend(tr("Ref: ", "Reference abbreviation"));
} }
return result; return result;
case Password: case Password:
/*
* Display passwords hidden or visible according to current state
* of 'Hide Passwords' setting
*/
if (m_hidePasswords) { if (m_hidePasswords) {
result = EntryModel::HiddenContentDisplay; result = EntryModel::HiddenContentDisplay;
} } else {
else { result = entry->resolveMultiplePlaceholders(entry->password());
//result = entry->resolveMultiplePlaceholders(entry->password());
result = entry->password();
} }
if (attr->isReference(EntryAttributes::PasswordKey)) { if (attr->isReference(EntryAttributes::PasswordKey)) {
result.prepend(tr("Ref: ","Reference abbreviation")); result.prepend(tr("Ref: ", "Reference abbreviation"));
} }
return result; return result;
case Url: case Url:
//result = entry->resolveMultiplePlaceholders(entry->displayUrl()); result = entry->resolveMultiplePlaceholders(entry->displayUrl());
result = entry->displayUrl();
if (attr->isReference(EntryAttributes::URLKey)) { if (attr->isReference(EntryAttributes::URLKey)) {
result.prepend(tr("Ref: ","Reference abbreviation")); result.prepend(tr("Ref: ", "Reference abbreviation"));
} }
return result; return result;
case Notes: case Notes:
/* // Display only first line of notes in simplified format
* Display only first line of notes in simplified format like result = entry->resolveMultiplePlaceholders(entry->notes().section("\n", 0, 0).simplified());
* KeePassX does
*/
//result = entry->resolveMultiplePlaceholders(entry->notes().section("\n", 0, 0).simplified());
result = entry->notes().section("\n", 0, 0).simplified();
if (attr->isReference(EntryAttributes::NotesKey)) { if (attr->isReference(EntryAttributes::NotesKey)) {
result.prepend(tr("Ref: ","Reference abbreviation")); result.prepend(tr("Ref: ", "Reference abbreviation"));
} }
return result; return result;
case Expires: case Expires:
/* // Display either date of expiry or 'Never'
* Display either date of expiry or 'Never' like KeePassX does
*/
result = entry->timeInfo().expires() ? entry->timeInfo().expiryTime().toLocalTime().toString(EntryModel::DateFormat) : tr("Never"); result = entry->timeInfo().expires() ? entry->timeInfo().expiryTime().toLocalTime().toString(EntryModel::DateFormat) : tr("Never");
return result; return result;
case Created: case Created:
@ -288,65 +208,25 @@ QVariant EntryModel::data(const QModelIndex& index, int role) const
result = entry->attachments()->keys().isEmpty() ? QString() : EntryModel::PaperClipDisplay; result = entry->attachments()->keys().isEmpty() ? QString() : EntryModel::PaperClipDisplay;
return result; return result;
case Attachments: case Attachments:
/* // Display comma-separated list of attachments
* Display comma-separated list of attachments
*
* TODO:
* 'entry->attachments()->keys().join()' works locally, yet it fails
* on GitHub/Travis CI, most likely due to an older Qt version, thus
* using loop for now (http://doc.qt.io/qt-5/qlist.html#more-members)
*/
//result = entry->resolveMultiplePlaceholders(entry->attachments()->keys().join(", "));
//result = entry->attachments()->keys().join(", ");
QList<QString> attachments = entry->attachments()->keys(); QList<QString> attachments = entry->attachments()->keys();
for (int i=0; i < attachments.size(); i++) { for (int i = 0; i < attachments.size(); ++i) {
if (result.isEmpty()) { if (result.isEmpty()) {
result.append(attachments.at(i)); result.append(attachments.at(i));
continue;
} }
else { result.append(QString(", ") + attachments.at(i));
result.append(QString(", ") + attachments.at(i));
}
} }
//result = entry->resolveMultiplePlaceholders(result);
return result; return result;
} }
} else if (role == Qt::UserRole) { // Qt::UserRole is used as sort role, see EntryView::EntryView()
}
/**
* @author Fonic <https://github.com/fonic>
*
* Add sort data providers for columns 'Username' and 'Password', required
* for correct sorting even if displayed hidden (i.e. settings 'Hide User-
* names' and/or 'Hide Passwords' are enabled)
*
* Add sort data providers for columns 'Expires', 'Created', 'Modified'
* and 'Accessed', required for correct sorting of dates (without this,
* sorting would be based on string representation of dates, yielding un-
* desired results)
*
* Add sort data provider for column 'Paperclip', required to display
* entries with attachments above those without when sorting ascendingly
* (and vice versa when sorting descendingly)
*
* NOTE:
* Qt::UserRole is used as sort role, using 'm_sortModel->setSortRole(Qt::
* UserRole)' in EntryView.cpp, EntryView::EntryView()
*/
else if (role == Qt::UserRole) {
switch (index.column()) { switch (index.column()) {
case Username: case Username:
//return entry->username();
return entry->resolveMultiplePlaceholders(entry->username()); return entry->resolveMultiplePlaceholders(entry->username());
case Password: case Password:
//return entry->resolveMultiplePlaceholders(entry->password()); return entry->resolveMultiplePlaceholders(entry->password());
return entry->password();
case Expires: case Expires:
/* // There seems to be no better way of expressing 'infinity'
* TODO:
* Is there any better way to return a QDateTime representing
* 'Never' / infinity / end of all time?
*/
return entry->timeInfo().expires() ? entry->timeInfo().expiryTime() : QDateTime(QDate(9999, 1, 1)); return entry->timeInfo().expires() ? entry->timeInfo().expiryTime() : QDateTime(QDate(9999, 1, 1));
case Created: case Created:
return entry->timeInfo().creationTime(); return entry->timeInfo().creationTime();
@ -355,16 +235,15 @@ QVariant EntryModel::data(const QModelIndex& index, int role) const
case Accessed: case Accessed:
return entry->timeInfo().lastAccessTime(); return entry->timeInfo().lastAccessTime();
case Paperclip: case Paperclip:
// Display entries with attachments above those without when
// sorting ascendingly (and vice versa when sorting descendingly)
return entry->attachments()->keys().isEmpty() ? 1 : 0; return entry->attachments()->keys().isEmpty() ? 1 : 0;
default: default:
/* // For all other columns, simply use data provided by Qt::Display-
* For all other columns, simply use data provided by Qt::Display- // Role for sorting
* Role for sorting
*/
return data(index, Qt::DisplayRole); return data(index, Qt::DisplayRole);
} }
} } else if (role == Qt::DecorationRole) {
else if (role == Qt::DecorationRole) {
switch (index.column()) { switch (index.column()) {
case ParentGroup: case ParentGroup:
if (entry->group()) { if (entry->group()) {
@ -374,20 +253,17 @@ QVariant EntryModel::data(const QModelIndex& index, int role) const
case Title: case Title:
if (entry->isExpired()) { if (entry->isExpired()) {
return databaseIcons()->iconPixmap(DatabaseIcons::ExpiredIconIndex); return databaseIcons()->iconPixmap(DatabaseIcons::ExpiredIconIndex);
} } else {
else {
return entry->iconScaledPixmap(); return entry->iconScaledPixmap();
} }
} }
} } else if (role == Qt::FontRole) {
else if (role == Qt::FontRole) {
QFont font; QFont font;
if (entry->isExpired()) { if (entry->isExpired()) {
font.setStrikeOut(true); font.setStrikeOut(true);
} }
return font; return font;
} } else if (role == Qt::TextColorRole) {
else if (role == Qt::TextColorRole) {
if (entry->hasReferences()) { if (entry->hasReferences()) {
QPalette p; QPalette p;
return QVariant(p.color(QPalette::Active, QPalette::Mid)); return QVariant(p.color(QPalette::Active, QPalette::Mid));
@ -399,11 +275,6 @@ QVariant EntryModel::data(const QModelIndex& index, int role) const
QVariant EntryModel::headerData(int section, Qt::Orientation orientation, int role) const QVariant EntryModel::headerData(int section, Qt::Orientation orientation, int role) const
{ {
/**
* @author Fonic <https://github.com/fonic>
* Add captions for additional columns 'Password', 'Notes', 'Expires',
* 'Created', 'Modified', 'Accessed', 'Paperclip' and 'Attachments'
*/
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
switch (section) { switch (section) {
case ParentGroup: case ParentGroup:
@ -450,8 +321,7 @@ Qt::ItemFlags EntryModel::flags(const QModelIndex& modelIndex) const
{ {
if (!modelIndex.isValid()) { if (!modelIndex.isValid()) {
return Qt::NoItemFlags; return Qt::NoItemFlags;
} } else {
else {
return QAbstractItemModel::flags(modelIndex) | Qt::ItemIsDragEnabled; return QAbstractItemModel::flags(modelIndex) | Qt::ItemIsDragEnabled;
} }
} }
@ -492,8 +362,7 @@ QMimeData* EntryModel::mimeData(const QModelIndexList& indexes) const
if (seenEntries.isEmpty()) { if (seenEntries.isEmpty()) {
delete data; delete data;
return nullptr; return nullptr;
} } else {
else {
data->setData(mimeTypes().at(0), encoded); data->setData(mimeTypes().at(0), encoded);
return data; return data;
} }
@ -567,57 +436,51 @@ void EntryModel::makeConnections(const Group* group)
} }
/** /**
* @author Fonic <https://github.com/fonic>
* Get current state of 'Hide Usernames' setting * Get current state of 'Hide Usernames' setting
*/ */
bool EntryModel::hideUsernames() const bool EntryModel::isUsernamesHidden() const
{ {
return m_hideUsernames; return m_hideUsernames;
} }
/** /**
* @author Fonic <https://github.com/fonic>
* Set state of 'Hide Usernames' setting and signal change * Set state of 'Hide Usernames' setting and signal change
*/ */
void EntryModel::setHideUsernames(const bool hide) void EntryModel::setUsernamesHidden(const bool hide)
{ {
m_hideUsernames = hide; m_hideUsernames = hide;
emit hideUsernamesChanged(); emit usernamesHiddenChanged();
} }
/** /**
* @author Fonic <https://github.com/fonic>
* Get current state of 'Hide Passwords' setting * Get current state of 'Hide Passwords' setting
*/ */
bool EntryModel::hidePasswords() const bool EntryModel::isPasswordsHidden() const
{ {
return m_hidePasswords; return m_hidePasswords;
} }
/** /**
* @author Fonic <https://github.com/fonic>
* Set state of 'Hide Passwords' setting and signal change * Set state of 'Hide Passwords' setting and signal change
*/ */
void EntryModel::setHidePasswords(const bool hide) void EntryModel::setPasswordsHidden(const bool hide)
{ {
m_hidePasswords = hide; m_hidePasswords = hide;
emit hidePasswordsChanged(); emit passwordsHiddenChanged();
} }
/** /**
* @author Fonic <https://github.com/fonic>
* Toggle state of 'Hide Usernames' setting * Toggle state of 'Hide Usernames' setting
*/ */
void EntryModel::toggleHideUsernames(const bool hide) void EntryModel::toggleUsernamesHidden(const bool hide)
{ {
setHideUsernames(hide); setUsernamesHidden(hide);
} }
/** /**
* @author Fonic <https://github.com/fonic>
* Toggle state of 'Hide Passwords' setting * Toggle state of 'Hide Passwords' setting
*/ */
void EntryModel::toggleHidePasswords(const bool hide) void EntryModel::togglePasswordsHidden(const bool hide)
{ {
setHidePasswords(hide); setPasswordsHidden(hide);
} }

View File

@ -28,11 +28,6 @@ class EntryModel : public QAbstractTableModel
Q_OBJECT Q_OBJECT
public: public:
/**
* @author Fonic <https://github.com/fonic>
* Add entries for additional columns 'Password', 'Notes', 'Expires',
* 'Created', 'Modified', 'Accessed' and 'Attachments'
*/
enum ModelColumn enum ModelColumn
{ {
ParentGroup = 0, ParentGroup = 0,
@ -65,42 +60,21 @@ public:
void setEntryList(const QList<Entry*>& entries); void setEntryList(const QList<Entry*>& entries);
/** bool isUsernamesHidden() const;
* @author Fonic <https://github.com/fonic> void setUsernamesHidden(const bool hide);
* Methods to get/set state of 'Hide Usernames' and 'Hide Passwords' bool isPasswordsHidden() const;
* settings void setPasswordsHidden(const bool hide);
*/
bool hideUsernames() const;
void setHideUsernames(const bool hide);
bool hidePasswords() const;
void setHidePasswords(const bool hide);
signals: signals:
/**
* @author Fonic <https://github.com/fonic>
* Signals to notify about list/search mode switches (NOTE: previously
* named 'switchedToGroupMode'/'switchedToEntryListMode')
*/
void switchedToListMode(); void switchedToListMode();
void switchedToSearchMode(); void switchedToSearchMode();
void usernamesHiddenChanged();
/** void passwordsHiddenChanged();
* @author Fonic <https://github.com/fonic>
* Signals to notify about state changes of 'Hide Usernames' and 'Hide
* Passwords' settings
*/
void hideUsernamesChanged();
void hidePasswordsChanged();
public slots: public slots:
void setGroup(Group* group); void setGroup(Group* group);
void toggleUsernamesHidden(const bool hide);
/** void togglePasswordsHidden(const bool hide);
* @author Fonic <https://github.com/fonic>
* Slots to toggle state of 'Hide Usernames' and 'Hide Passwords' settings
*/
void toggleHideUsernames(const bool hide);
void toggleHidePasswords(const bool hide);
private slots: private slots:
void entryAboutToAdd(Entry* entry); void entryAboutToAdd(Entry* entry);
@ -118,33 +92,11 @@ private:
QList<Entry*> m_orgEntries; QList<Entry*> m_orgEntries;
QList<const Group*> m_allGroups; QList<const Group*> m_allGroups;
/**
* @author Fonic <https://github.com/fonic>
* Properties to store state of 'Hide Usernames' and 'Hide Passwords'
* settings
*/
bool m_hideUsernames; bool m_hideUsernames;
bool m_hidePasswords; bool m_hidePasswords;
/**
* @author Fonic <https://github.com/fonic>
* Constant string used to display hidden content in columns 'Username'
* and 'Password'
*/
static const QString HiddenContentDisplay; static const QString HiddenContentDisplay;
/**
* @author Fonic <https://github.com/fonic>
* Date format used to display dates in columns 'Expires', 'Created',
* 'Modified' and 'Accessed'
*/
static const Qt::DateFormat DateFormat; static const Qt::DateFormat DateFormat;
/**
* @author Fonic <https://github.com/fonic>
* Constant string used to display header and data of column 'Paper-
* clip'
*/
static const QString PaperClipDisplay; static const QString PaperClipDisplay;
}; };

View File

@ -19,26 +19,10 @@
#include <QHeaderView> #include <QHeaderView>
#include <QKeyEvent> #include <QKeyEvent>
/**
* @author Fonic <https://github.com/fonic>
* Add include required for header context menu
*/
#include <QMenu> #include <QMenu>
#include "gui/SortFilterHideProxyModel.h" #include "gui/SortFilterHideProxyModel.h"
/**
* @author Fonic <https://github.com/fonic>
*
* TODO NOTE:
* Currently, 'zombie' columns which are not hidden but have width == 0
* (rendering them invisible) may appear. This is caused by DatabaseWidget
* StateSync. Corresponding checks/workarounds may be removed once sync
* code is updated accordingly
* -> relevant code pieces: if (header()->sectionSize(...) == 0) { ... }
*
*/
EntryView::EntryView(QWidget* parent) EntryView::EntryView(QWidget* parent)
: QTreeView(parent) : QTreeView(parent)
, m_model(new EntryModel(this)) , m_model(new EntryModel(this))
@ -49,12 +33,7 @@ EntryView::EntryView(QWidget* parent)
m_sortModel->setDynamicSortFilter(true); m_sortModel->setDynamicSortFilter(true);
m_sortModel->setSortLocaleAware(true); m_sortModel->setSortLocaleAware(true);
m_sortModel->setSortCaseSensitivity(Qt::CaseInsensitive); m_sortModel->setSortCaseSensitivity(Qt::CaseInsensitive);
/** // Use Qt::UserRole as sort role, see EntryModel::data()
* @author Fonic <https://github.com/fonic>
* Set Qt::UserRole as sort role
* -> refer to 'if (role == Qt::UserRole)', EntryModel.cpp, EntryModel::
* data() for details
*/
m_sortModel->setSortRole(Qt::UserRole); m_sortModel->setSortRole(Qt::UserRole);
QTreeView::setModel(m_sortModel); QTreeView::setModel(m_sortModel);
@ -70,49 +49,31 @@ EntryView::EntryView(QWidget* parent)
connect(this, SIGNAL(doubleClicked(QModelIndex)), SLOT(emitEntryActivated(QModelIndex))); connect(this, SIGNAL(doubleClicked(QModelIndex)), SLOT(emitEntryActivated(QModelIndex)));
connect(selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SIGNAL(entrySelectionChanged())); connect(selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SIGNAL(entrySelectionChanged()));
/**
* @author Fonic <https://github.com/fonic>
* Connect signals to get notified about list/search mode switches (NOTE:
* previously named 'switch[ed]ToGroupMode'/'switch[ed]ToEntryListMode')
*/
connect(m_model, SIGNAL(switchedToListMode()), SLOT(switchToListMode())); connect(m_model, SIGNAL(switchedToListMode()), SLOT(switchToListMode()));
connect(m_model, SIGNAL(switchedToSearchMode()), SLOT(switchToSearchMode())); connect(m_model, SIGNAL(switchedToSearchMode()), SLOT(switchToSearchMode()));
/** connect(m_model, SIGNAL(usernamesHiddenChanged()), SIGNAL(viewStateChanged()));
* @author Fonic <https://github.com/fonic> connect(m_model, SIGNAL(passwordsHiddenChanged()), SIGNAL(viewStateChanged()));
* Connect signals to notify about changes of view state when state of
* 'Hide Usernames'/'Hide Passwords' settings changes in model
*/
connect(m_model, SIGNAL(hideUsernamesChanged()), SIGNAL(viewStateChanged()));
connect(m_model, SIGNAL(hidePasswordsChanged()), SIGNAL(viewStateChanged()));
connect(this, SIGNAL(clicked(QModelIndex)), SLOT(emitEntryPressed(QModelIndex))); connect(this, SIGNAL(clicked(QModelIndex)), SLOT(emitEntryPressed(QModelIndex)));
/**
* @author Fonic <https://github.com/fonic>
* Create header context menu:
* - Actions to toggle state of 'Hide Usernames'/'Hide Passwords' settings
* - Actions to toggle column visibility, with each action carrying 'its'
* column index as data
* - Actions to resize columns
* - Action to reset view to defaults
*/
m_headerMenu = new QMenu(this); m_headerMenu = new QMenu(this);
m_headerMenu->setTitle(tr("Customize View")); m_headerMenu->setTitle(tr("Customize View"));
m_headerMenu->addSection(tr("Customize View")); m_headerMenu->addSection(tr("Customize View"));
m_hideUsernamesAction = m_headerMenu->addAction(tr("Hide Usernames"), m_model, SLOT(toggleHideUsernames(bool))); m_hideUsernamesAction = m_headerMenu->addAction(tr("Hide Usernames"), m_model, SLOT(toggleUsernamesHidden(bool)));
m_hideUsernamesAction->setCheckable(true); m_hideUsernamesAction->setCheckable(true);
m_hidePasswordsAction = m_headerMenu->addAction(tr("Hide Passwords"), m_model, SLOT(toggleHidePasswords(bool))); m_hidePasswordsAction = m_headerMenu->addAction(tr("Hide Passwords"), m_model, SLOT(togglePasswordsHidden(bool)));
m_hidePasswordsAction->setCheckable(true); m_hidePasswordsAction->setCheckable(true);
m_headerMenu->addSeparator(); m_headerMenu->addSeparator();
// Actions to toggle column visibility, each carrying the corresponding
// colummn index as data
m_columnActions = new QActionGroup(this); m_columnActions = new QActionGroup(this);
m_columnActions->setExclusive(false); m_columnActions->setExclusive(false);
for (int colidx = 1; colidx < header()->count(); colidx++) { for (int columnIndex = 1; columnIndex < header()->count(); ++columnIndex) {
QString caption = m_model->headerData(colidx, Qt::Horizontal, Qt::DisplayRole).toString(); QString caption = m_model->headerData(columnIndex, Qt::Horizontal, Qt::DisplayRole).toString();
QAction* action = m_headerMenu->addAction(caption); QAction* action = m_headerMenu->addAction(caption);
action->setCheckable(true); action->setCheckable(true);
action->setData(colidx); action->setData(columnIndex);
m_columnActions->addAction(action); m_columnActions->addAction(action);
} }
connect(m_columnActions, SIGNAL(triggered(QAction*)), this, SLOT(toggleColumnVisibility(QAction*))); connect(m_columnActions, SIGNAL(triggered(QAction*)), this, SLOT(toggleColumnVisibility(QAction*)));
@ -123,17 +84,8 @@ EntryView::EntryView(QWidget* parent)
m_headerMenu->addSeparator(); m_headerMenu->addSeparator();
m_headerMenu->addAction(tr("Reset to defaults"), this, SLOT(resetViewToDefaults())); m_headerMenu->addAction(tr("Reset to defaults"), this, SLOT(resetViewToDefaults()));
/**
* @author Fonic <https://github.com/fonic>
* Configure header:
* - Set default section size
* - Disable stretching of last section (interferes with fitting columns
* to window)
* - Associate with context menu
* - Connect signals to notify about changes of view state when state
* of header changes
*/
header()->setDefaultSectionSize(100); header()->setDefaultSectionSize(100);
// Stretching of last section interferes with fitting columns to window
header()->setStretchLastSection(false); header()->setStretchLastSection(false);
header()->setContextMenuPolicy(Qt::CustomContextMenu); header()->setContextMenuPolicy(Qt::CustomContextMenu);
connect(header(), SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showHeaderMenu(QPoint))); connect(header(), SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showHeaderMenu(QPoint)));
@ -142,35 +94,17 @@ EntryView::EntryView(QWidget* parent)
connect(header(), SIGNAL(sectionResized(int, int, int)), this, SIGNAL(viewStateChanged())); connect(header(), SIGNAL(sectionResized(int, int, int)), this, SIGNAL(viewStateChanged()));
connect(header(), SIGNAL(sortIndicatorChanged(int, Qt::SortOrder)), this, SIGNAL(viewStateChanged())); connect(header(), SIGNAL(sortIndicatorChanged(int, Qt::SortOrder)), this, SIGNAL(viewStateChanged()));
/** // TODO: not working as expected, columns will end up being very small,
* @author Fonic <https://github.com/fonic> // most likely due to the widget not being sized properly at this time
* Fit columns to window
*
* TODO:
* Not working as expected, columns will end up being very small, most
* likely due to EntryView not being sized properly at this time. Find
* a way to make this work by analizing when/where EntryView is created
*/
//fitColumnsToWindow(); //fitColumnsToWindow();
/** // Configure default search view state and save for later use
* @author Fonic <https://github.com/fonic>
* Configure default search view state and save for later use
*/
header()->showSection(EntryModel::ParentGroup); header()->showSection(EntryModel::ParentGroup);
m_sortModel->sort(EntryModel::ParentGroup, Qt::AscendingOrder); m_sortModel->sort(EntryModel::ParentGroup, Qt::AscendingOrder);
sortByColumn(EntryModel::ParentGroup, Qt::AscendingOrder); sortByColumn(EntryModel::ParentGroup, Qt::AscendingOrder);
m_defaultSearchViewState = header()->saveState(); m_defaultSearchViewState = header()->saveState();
/** // Configure default list view state and save for later use
* @author Fonic <https://github.com/fonic>
* Configure default list view state and save for later use
*
* NOTE:
* Default list view is intentionally configured last since this is the
* view that's supposed to be active after initialization as m_inSearchMode
* is initialized with 'false'
*/
header()->hideSection(EntryModel::ParentGroup); header()->hideSection(EntryModel::ParentGroup);
m_sortModel->sort(EntryModel::Title, Qt::AscendingOrder); m_sortModel->sort(EntryModel::Title, Qt::AscendingOrder);
sortByColumn(EntryModel::Title, Qt::AscendingOrder); sortByColumn(EntryModel::Title, Qt::AscendingOrder);
@ -207,8 +141,7 @@ void EntryView::setFirstEntryActive()
if (m_model->rowCount() > 0) { if (m_model->rowCount() > 0) {
QModelIndex index = m_sortModel->mapToSource(m_sortModel->index(0, 0)); QModelIndex index = m_sortModel->mapToSource(m_sortModel->index(0, 0));
setCurrentEntry(m_model->entryFromIndex(index)); setCurrentEntry(m_model->entryFromIndex(index));
} } else {
else {
emit entrySelectionChanged(); emit entrySelectionChanged();
} }
} }
@ -241,8 +174,7 @@ Entry* EntryView::currentEntry()
QModelIndexList list = selectionModel()->selectedRows(); QModelIndexList list = selectionModel()->selectedRows();
if (list.size() == 1) { if (list.size() == 1) {
return m_model->entryFromIndex(m_sortModel->mapToSource(list.first())); return m_model->entryFromIndex(m_sortModel->mapToSource(list.first()));
} } else {
else {
return nullptr; return nullptr;
} }
} }
@ -262,58 +194,38 @@ Entry* EntryView::entryFromIndex(const QModelIndex& index)
{ {
if (index.isValid()) { if (index.isValid()) {
return m_model->entryFromIndex(m_sortModel->mapToSource(index)); return m_model->entryFromIndex(m_sortModel->mapToSource(index));
} } else {
else {
return nullptr; return nullptr;
} }
} }
/** /**
* @author Fonic <https://github.com/fonic> * Switch to list mode, i.e. list entries of group
* Switch to list mode, i.e. list entries of group (NOTE: previously named
* 'switchToGroupMode')
*/ */
void EntryView::switchToListMode() void EntryView::switchToListMode()
{ {
/* Check if already in this mode */
if (!m_inSearchMode) { if (!m_inSearchMode) {
return; return;
} }
/**
* Use header()->hideSection() instead of m_sortModel->hideColumn() as
* the latter messes up column indices, interfering with code relying on
* proper indices
*/
header()->hideSection(EntryModel::ParentGroup); header()->hideSection(EntryModel::ParentGroup);
m_inSearchMode = false; m_inSearchMode = false;
} }
/** /**
* @author Fonic <https://github.com/fonic> * Switch to search mode, i.e. list search results
* Switch to search mode, i.e. list search results (NOTE: previously named
* 'switchToEntryListMode')
*/ */
void EntryView::switchToSearchMode() void EntryView::switchToSearchMode()
{ {
/* Check if already in this mode */
if (m_inSearchMode) { if (m_inSearchMode) {
return; return;
} }
/*
* Use header()->showSection() instead of m_sortModel->hideColumn() as
* the latter messes up column indices, interfering with code relying on
* proper indices
*/
header()->showSection(EntryModel::ParentGroup); header()->showSection(EntryModel::ParentGroup);
/* // Always set sorting to column 'Group', as it does not feel right to
* Always set sorting to column 'Group', as it does not feel right to have // have the last known sort configuration of search view restored by
* the last known sort configuration of search view restored by 'Database // 'DatabaseWidgetStateSync', which is what happens without this
* WidgetStateSync', which is what happens without this
*/
m_sortModel->sort(EntryModel::ParentGroup, Qt::AscendingOrder); m_sortModel->sort(EntryModel::ParentGroup, Qt::AscendingOrder);
sortByColumn(EntryModel::ParentGroup, Qt::AscendingOrder); sortByColumn(EntryModel::ParentGroup, Qt::AscendingOrder);
@ -321,46 +233,41 @@ void EntryView::switchToSearchMode()
} }
/** /**
* @author Fonic <https://github.com/fonic>
* Get current state of 'Hide Usernames' setting (NOTE: just pass-through for * Get current state of 'Hide Usernames' setting (NOTE: just pass-through for
* m_model) * m_model)
*/ */
bool EntryView::hideUsernames() const bool EntryView::isUsernamesHidden() const
{ {
return m_model->hideUsernames(); return m_model->isUsernamesHidden();
} }
/** /**
* @author Fonic <https://github.com/fonic>
* Set state of 'Hide Usernames' setting (NOTE: just pass-through for m_model) * Set state of 'Hide Usernames' setting (NOTE: just pass-through for m_model)
*/ */
void EntryView::setHideUsernames(const bool hide) void EntryView::setUsernamesHidden(const bool hide)
{ {
m_model->setHideUsernames(hide); m_model->setUsernamesHidden(hide);
} }
/** /**
* @author Fonic <https://github.com/fonic>
* Get current state of 'Hide Passwords' setting (NOTE: just pass-through for * Get current state of 'Hide Passwords' setting (NOTE: just pass-through for
* m_model) * m_model)
*/ */
bool EntryView::hidePasswords() const bool EntryView::isPasswordsHidden() const
{ {
return m_model->hidePasswords(); return m_model->isPasswordsHidden();
} }
/** /**
* @author Fonic <https://github.com/fonic>
* Set state of 'Hide Passwords' setting (NOTE: just pass-through for m_model) * Set state of 'Hide Passwords' setting (NOTE: just pass-through for m_model)
*/ */
void EntryView::setHidePasswords(const bool hide) void EntryView::setPasswordsHidden(const bool hide)
{ {
m_model->setHidePasswords(hide); m_model->setPasswordsHidden(hide);
} }
/** /**
* @author Fonic <https://github.com/fonic> * Get current view state
* Get current state of view
*/ */
QByteArray EntryView::viewState() const QByteArray EntryView::viewState() const
{ {
@ -368,8 +275,7 @@ QByteArray EntryView::viewState() const
} }
/** /**
* @author Fonic <https://github.com/fonic> * Set view state
* Set state of entry view view
*/ */
bool EntryView::setViewState(const QByteArray& state) const bool EntryView::setViewState(const QByteArray& state) const
{ {
@ -377,70 +283,59 @@ bool EntryView::setViewState(const QByteArray& state) const
} }
/** /**
* @author Fonic <https://github.com/fonic>
* Sync checkable menu actions to current state and display header context * Sync checkable menu actions to current state and display header context
* menu at specified position * menu at specified position
*/ */
void EntryView::showHeaderMenu(const QPoint& position) void EntryView::showHeaderMenu(const QPoint& position)
{ {
/* Sync checked state of menu actions to current state of view */ m_hideUsernamesAction->setChecked(m_model->isUsernamesHidden());
m_hideUsernamesAction->setChecked(m_model->hideUsernames()); m_hidePasswordsAction->setChecked(m_model->isPasswordsHidden());
m_hidePasswordsAction->setChecked(m_model->hidePasswords()); const QList<QAction*> actions = m_columnActions->actions();
foreach (QAction *action, m_columnActions->actions()) { for (auto& action : actions) {
Q_ASSERT(static_cast<QMetaType::Type>(action->data().type()) == QMetaType::Int);
if (static_cast<QMetaType::Type>(action->data().type()) != QMetaType::Int) { if (static_cast<QMetaType::Type>(action->data().type()) != QMetaType::Int) {
Q_ASSERT(false);
continue; continue;
} }
int colidx = action->data().toInt(); int columnIndex = action->data().toInt();
bool hidden = header()->isSectionHidden(colidx) || (header()->sectionSize(colidx) == 0); bool hidden = header()->isSectionHidden(columnIndex) || (header()->sectionSize(columnIndex) == 0);
action->setChecked(!hidden); action->setChecked(!hidden);
} }
/* Display menu */
m_headerMenu->popup(mapToGlobal(position)); m_headerMenu->popup(mapToGlobal(position));
} }
/** /**
* @author Fonic <https://github.com/fonic>
* Toggle visibility of column referenced by triggering action * Toggle visibility of column referenced by triggering action
*/ */
void EntryView::toggleColumnVisibility(QAction *action) void EntryView::toggleColumnVisibility(QAction *action)
{ {
/* // Verify action carries a column index as data. Since QVariant.toInt()
* Verify action carries a column index as data. Since QVariant.toInt() // below will accept anything that's interpretable as int, perform a type
* below will accept anything that's interpretable as int, perform a type // check here to make sure data actually IS int
* check here to make sure data actually IS int Q_ASSERT(static_cast<QMetaType::Type>(action->data().type()) == QMetaType::Int);
*/
if (static_cast<QMetaType::Type>(action->data().type()) != QMetaType::Int) { if (static_cast<QMetaType::Type>(action->data().type()) != QMetaType::Int) {
Q_ASSERT(false);
return; return;
} }
/* // Toggle column visibility. Visible columns will only be hidden if at
* Toggle column visibility. Visible columns will only be hidden if at // least one visible column remains, as the table header will disappear
* least one visible column remains, as the table header will disappear // entirely when all columns are hidden
* entirely when all columns are hidden, rendering the context menu in- int columnIndex = action->data().toInt();
* accessible
*/
int colidx = action->data().toInt();
if (action->isChecked()) { if (action->isChecked()) {
header()->showSection(colidx); header()->showSection(columnIndex);
if (header()->sectionSize(colidx) == 0) { if (header()->sectionSize(columnIndex) == 0) {
header()->resizeSection(colidx, header()->defaultSectionSize()); header()->resizeSection(columnIndex, header()->defaultSectionSize());
} }
return;
} }
else { if ((header()->count() - header()->hiddenSectionCount()) > 1) {
if ((header()->count() - header()->hiddenSectionCount()) > 1) { header()->hideSection(columnIndex);
header()->hideSection(colidx); return;
}
else {
action->setChecked(true);
}
} }
action->setChecked(true);
} }
/** /**
* @author Fonic <https://github.com/fonic>
* Resize columns to fit all visible columns within the available space * Resize columns to fit all visible columns within the available space
* *
* NOTE: * NOTE:
@ -461,69 +356,60 @@ void EntryView::fitColumnsToWindow()
} }
/** /**
* @author Fonic <https://github.com/fonic>
* Resize columns to fit current table contents, i.e. make all contents * Resize columns to fit current table contents, i.e. make all contents
* entirely visible * entirely visible
*/ */
void EntryView::fitColumnsToContents() void EntryView::fitColumnsToContents()
{ {
/* Resize columns to fit contents */ // Resize columns to fit contents
header()->resizeSections(QHeaderView::ResizeToContents); header()->resizeSections(QHeaderView::ResizeToContents);
/* // Determine total width of currently visible columns. If there is
* Determine total width of currently visible columns. If there is // still some space available on the header, equally distribute it to
* still some space available on the header, equally distribute it to // visible columns and add remaining fraction to last visible column
* visible columns and add remaining fraction to last visible column
*/
int width = 0; int width = 0;
for (int colidx = 0; colidx < header()->count(); colidx++) { for (int columnIndex = 0; columnIndex < header()->count(); ++columnIndex) {
if (!header()->isSectionHidden(colidx)) { if (!header()->isSectionHidden(columnIndex)) {
width += header()->sectionSize(colidx); width += header()->sectionSize(columnIndex);
} }
} }
int visible = header()->count() - header()->hiddenSectionCount(); int visible = header()->count() - header()->hiddenSectionCount();
int avail = header()->width() - width; int avail = header()->width() - width;
if ((visible > 0) && (avail > 0)) { if ((visible <= 0) || (avail <= 0)) {
int add = avail / visible; return;
width = 0; }
int last = 0; int add = avail / visible;
for (int colidx = 0; colidx < header()->count(); colidx++) { width = 0;
if (!header()->isSectionHidden(colidx)) { int last = 0;
header()->resizeSection(colidx, header()->sectionSize(colidx) + add); for (int columnIndex = 0; columnIndex < header()->count(); ++columnIndex) {
width += header()->sectionSize(colidx); if (!header()->isSectionHidden(columnIndex)) {
if (header()->visualIndex(colidx) > last) { header()->resizeSection(columnIndex, header()->sectionSize(columnIndex) + add);
last = header()->visualIndex(colidx); width += header()->sectionSize(columnIndex);
} if (header()->visualIndex(columnIndex) > last) {
last = header()->visualIndex(columnIndex);
} }
} }
header()->resizeSection(header()->logicalIndex(last), header()->sectionSize(last) + (header()->width() - width));
} }
header()->resizeSection(header()->logicalIndex(last), header()->sectionSize(last) + (header()->width() - width));
/* // Shouldn't be necessary due to use of header()->resizeSection, but
* This should not be necessary due to use of header()->resizeSection, // lets just do it anyway for the sake of completeness
* but lets do it anyway for the sake of completeness
*/
emit viewStateChanged(); emit viewStateChanged();
} }
/** /**
* @author Fonic <https://github.com/fonic>
* Reset view to defaults * Reset view to defaults
*/ */
void EntryView::resetViewToDefaults() void EntryView::resetViewToDefaults()
{ {
/* Reset state of 'Hide Usernames'/'Hide Passwords' settings */ m_model->setUsernamesHidden(false);
m_model->setHideUsernames(false); m_model->setPasswordsHidden(true);
m_model->setHidePasswords(true);
/* Reset columns (size, order, sorting etc.) */
if (m_inSearchMode) { if (m_inSearchMode) {
header()->restoreState(m_defaultSearchViewState); header()->restoreState(m_defaultSearchViewState);
} } else {
else {
header()->restoreState(m_defaultListViewState); header()->restoreState(m_defaultListViewState);
} }
/* Nicely fitting columns to window feels like a sane default */
fitColumnsToWindow(); fitColumnsToWindow();
} }

View File

@ -26,10 +26,6 @@ class Entry;
class EntryModel; class EntryModel;
class Group; class Group;
class SortFilterHideProxyModel; class SortFilterHideProxyModel;
/**
* @author Fonic <https://github.com/fonic>
* Add forward declaration for QActionGroup
*/
class QActionGroup; class QActionGroup;
class EntryView : public QTreeView class EntryView : public QTreeView
@ -46,21 +42,10 @@ public:
bool inSearchMode(); bool inSearchMode();
int numberOfSelectedEntries(); int numberOfSelectedEntries();
void setFirstEntryActive(); void setFirstEntryActive();
bool isUsernamesHidden() const;
/** void setUsernamesHidden(const bool hide);
* @author Fonic <https://github.com/fonic> bool isPasswordsHidden() const;
* Methods to get/set state of 'Hide Usernames'/'Hide Passwords' settings void setPasswordsHidden(const bool hide);
* (NOTE: these are just pass-through methods to avoid exposing entry model)
*/
bool hideUsernames() const;
void setHideUsernames(const bool hide);
bool hidePasswords() const;
void setHidePasswords(const bool hide);
/**
* @author Fonic <https://github.com/fonic>
* Methods to get/set state of view
*/
QByteArray viewState() const; QByteArray viewState() const;
bool setViewState(const QByteArray& state) const; bool setViewState(const QByteArray& state) const;
@ -71,10 +56,6 @@ signals:
void entryActivated(Entry* entry, EntryModel::ModelColumn column); void entryActivated(Entry* entry, EntryModel::ModelColumn column);
void entryPressed(Entry* entry); void entryPressed(Entry* entry);
void entrySelectionChanged(); void entrySelectionChanged();
/**
* @author Fonic <https://github.com/fonic>
* Signal to notify about changes of view state
*/
void viewStateChanged(); void viewStateChanged();
protected: protected:
@ -83,18 +64,8 @@ protected:
private slots: private slots:
void emitEntryActivated(const QModelIndex& index); void emitEntryActivated(const QModelIndex& index);
void emitEntryPressed(const QModelIndex& index); void emitEntryPressed(const QModelIndex& index);
/**
* @author Fonic <https://github.com/fonic>
* Methods to switch to list/search mode (NOTE: previously named 'switch
* ToGroupMode'/'switchToEntryListMode')
*/
void switchToListMode(); void switchToListMode();
void switchToSearchMode(); void switchToSearchMode();
/**
* @author Fonic <https://github.com/fonic>
* Slots for header context menu and actions
*/
void showHeaderMenu(const QPoint& position); void showHeaderMenu(const QPoint& position);
void toggleColumnVisibility(QAction *action); void toggleColumnVisibility(QAction *action);
void fitColumnsToWindow(); void fitColumnsToWindow();
@ -106,16 +77,9 @@ private:
SortFilterHideProxyModel* const m_sortModel; SortFilterHideProxyModel* const m_sortModel;
bool m_inSearchMode; bool m_inSearchMode;
/**
* @author Fonic <https://github.com/fonic>
* Properties to store default view states used by resetViewToDefaults()
*/
QByteArray m_defaultListViewState; QByteArray m_defaultListViewState;
QByteArray m_defaultSearchViewState; QByteArray m_defaultSearchViewState;
/**
* @author Fonic <https://github.com/fonic>
* Properties to store header context menu and actions
*/
QMenu* m_headerMenu; QMenu* m_headerMenu;
QAction* m_hideUsernamesAction; QAction* m_hideUsernamesAction;
QAction* m_hidePasswordsAction; QAction* m_hidePasswordsAction;

View File

@ -686,21 +686,6 @@ void TestGui::testSearch()
QTRY_COMPARE(m_dbWidget->currentMode(), DatabaseWidget::ViewMode); QTRY_COMPARE(m_dbWidget->currentMode(), DatabaseWidget::ViewMode);
} }
/**
* @author Fonic <https://github.com/fonic>
* Update clicks within entry view referencing column indices to account
* for changed column indices due to new way of showing/hiding column Entry
* Model::ParentGroup. This column now has fixed index 0 wether it's shown
* or hidden, thus all indices need to be shifted by +1 when not in search
* mode (which is the case within this entire method)
*
* Old:
* clickIndex(entryView->model()->index(row, column), button);
*
* New:
* clickIndex(entryView->model()->index(row, column+1), button);
*
*/
void TestGui::testDeleteEntry() void TestGui::testDeleteEntry()
{ {
// Add canned entries for consistent testing // Add canned entries for consistent testing