2010-08-24 22:26:52 +02:00
|
|
|
/*
|
2018-02-04 13:18:59 +01:00
|
|
|
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
|
|
|
|
* Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
|
2010-08-24 22:26:52 +02:00
|
|
|
*
|
2018-02-04 13:18:59 +01:00
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 2 or (at your option)
|
|
|
|
* version 3 of the License.
|
2010-08-24 22:26:52 +02:00
|
|
|
*
|
2018-02-04 13:18:59 +01:00
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
2010-08-24 22:26:52 +02:00
|
|
|
*
|
2018-02-04 13:18:59 +01:00
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2010-08-24 22:26:52 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "EntryView.h"
|
|
|
|
|
2014-05-17 19:01:43 +02:00
|
|
|
#include <QHeaderView>
|
2013-10-03 15:18:16 +02:00
|
|
|
#include <QKeyEvent>
|
2017-12-21 10:01:47 +01:00
|
|
|
#include <QMenu>
|
2013-09-29 17:33:45 +02:00
|
|
|
|
2018-02-24 07:44:28 -05:00
|
|
|
#include "core/FilePath.h"
|
2012-07-22 21:56:31 +02:00
|
|
|
#include "gui/SortFilterHideProxyModel.h"
|
2010-08-24 22:26:52 +02:00
|
|
|
|
|
|
|
EntryView::EntryView(QWidget* parent)
|
|
|
|
: QTreeView(parent)
|
2012-04-23 19:44:43 +02:00
|
|
|
, m_model(new EntryModel(this))
|
2012-07-22 21:56:31 +02:00
|
|
|
, m_sortModel(new SortFilterHideProxyModel(this))
|
2017-12-23 08:40:00 +01:00
|
|
|
, m_inSearchMode(false)
|
2010-08-24 22:26:52 +02:00
|
|
|
{
|
2012-05-11 12:01:01 +02:00
|
|
|
m_sortModel->setSourceModel(m_model);
|
|
|
|
m_sortModel->setDynamicSortFilter(true);
|
|
|
|
m_sortModel->setSortLocaleAware(true);
|
|
|
|
m_sortModel->setSortCaseSensitivity(Qt::CaseInsensitive);
|
2018-01-16 13:20:45 +01:00
|
|
|
// Use Qt::UserRole as sort role, see EntryModel::data()
|
2017-12-19 10:47:51 +01:00
|
|
|
m_sortModel->setSortRole(Qt::UserRole);
|
2012-05-11 12:01:01 +02:00
|
|
|
QTreeView::setModel(m_sortModel);
|
2010-08-24 23:06:35 +02:00
|
|
|
|
|
|
|
setUniformRowHeights(true);
|
|
|
|
setRootIsDecorated(false);
|
2012-05-15 20:04:20 +02:00
|
|
|
setAlternatingRowColors(true);
|
2012-04-26 16:35:13 +02:00
|
|
|
setDragEnabled(true);
|
2012-05-11 12:01:01 +02:00
|
|
|
setSortingEnabled(true);
|
2012-05-25 13:25:46 +02:00
|
|
|
setSelectionMode(QAbstractItemView::ExtendedSelection);
|
2010-10-06 19:40:50 +02:00
|
|
|
|
2013-04-07 12:36:53 +02:00
|
|
|
// QAbstractItemView::startDrag() uses this property as the default drag action
|
|
|
|
setDefaultDropAction(Qt::MoveAction);
|
|
|
|
|
2013-09-29 17:33:45 +02:00
|
|
|
connect(this, SIGNAL(doubleClicked(QModelIndex)), SLOT(emitEntryActivated(QModelIndex)));
|
2018-03-31 16:01:30 -04:00
|
|
|
connect(
|
2018-10-28 23:06:27 +01:00
|
|
|
selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SIGNAL(entrySelectionChanged()));
|
2017-12-23 08:40:00 +01:00
|
|
|
connect(m_model, SIGNAL(switchedToListMode()), SLOT(switchToListMode()));
|
|
|
|
connect(m_model, SIGNAL(switchedToSearchMode()), SLOT(switchToSearchMode()));
|
2018-01-16 13:20:45 +01:00
|
|
|
connect(m_model, SIGNAL(usernamesHiddenChanged()), SIGNAL(viewStateChanged()));
|
|
|
|
connect(m_model, SIGNAL(passwordsHiddenChanged()), SIGNAL(viewStateChanged()));
|
2017-08-17 21:02:21 +02:00
|
|
|
connect(this, SIGNAL(clicked(QModelIndex)), SLOT(emitEntryPressed(QModelIndex)));
|
2017-12-21 10:01:47 +01:00
|
|
|
|
|
|
|
m_headerMenu = new QMenu(this);
|
|
|
|
m_headerMenu->setTitle(tr("Customize View"));
|
|
|
|
m_headerMenu->addSection(tr("Customize View"));
|
|
|
|
|
2018-01-16 13:20:45 +01:00
|
|
|
m_hideUsernamesAction = m_headerMenu->addAction(tr("Hide Usernames"), m_model, SLOT(toggleUsernamesHidden(bool)));
|
2017-12-21 10:01:47 +01:00
|
|
|
m_hideUsernamesAction->setCheckable(true);
|
2018-01-16 13:20:45 +01:00
|
|
|
m_hidePasswordsAction = m_headerMenu->addAction(tr("Hide Passwords"), m_model, SLOT(togglePasswordsHidden(bool)));
|
2017-12-21 10:01:47 +01:00
|
|
|
m_hidePasswordsAction->setCheckable(true);
|
|
|
|
m_headerMenu->addSeparator();
|
|
|
|
|
2018-01-16 13:20:45 +01:00
|
|
|
// Actions to toggle column visibility, each carrying the corresponding
|
|
|
|
// colummn index as data
|
2017-12-21 10:01:47 +01:00
|
|
|
m_columnActions = new QActionGroup(this);
|
|
|
|
m_columnActions->setExclusive(false);
|
2018-01-16 13:20:45 +01:00
|
|
|
for (int columnIndex = 1; columnIndex < header()->count(); ++columnIndex) {
|
|
|
|
QString caption = m_model->headerData(columnIndex, Qt::Horizontal, Qt::DisplayRole).toString();
|
2018-02-22 23:19:26 -05:00
|
|
|
if (columnIndex == EntryModel::Paperclip) {
|
|
|
|
caption = tr("Attachments (icon)");
|
|
|
|
}
|
|
|
|
|
2017-12-21 10:01:47 +01:00
|
|
|
QAction* action = m_headerMenu->addAction(caption);
|
|
|
|
action->setCheckable(true);
|
2018-01-16 13:20:45 +01:00
|
|
|
action->setData(columnIndex);
|
2017-12-21 10:01:47 +01:00
|
|
|
m_columnActions->addAction(action);
|
|
|
|
}
|
|
|
|
connect(m_columnActions, SIGNAL(triggered(QAction*)), this, SLOT(toggleColumnVisibility(QAction*)));
|
|
|
|
|
|
|
|
m_headerMenu->addSeparator();
|
|
|
|
m_headerMenu->addAction(tr("Fit to window"), this, SLOT(fitColumnsToWindow()));
|
|
|
|
m_headerMenu->addAction(tr("Fit to contents"), this, SLOT(fitColumnsToContents()));
|
|
|
|
m_headerMenu->addSeparator();
|
|
|
|
m_headerMenu->addAction(tr("Reset to defaults"), this, SLOT(resetViewToDefaults()));
|
|
|
|
|
2018-02-21 21:25:04 -05:00
|
|
|
header()->setMinimumSectionSize(24);
|
2017-12-21 10:01:47 +01:00
|
|
|
header()->setDefaultSectionSize(100);
|
|
|
|
header()->setStretchLastSection(false);
|
|
|
|
header()->setContextMenuPolicy(Qt::CustomContextMenu);
|
2018-02-21 21:25:04 -05:00
|
|
|
|
2018-02-04 13:18:59 +01:00
|
|
|
connect(header(), SIGNAL(customContextMenuRequested(QPoint)), SLOT(showHeaderMenu(QPoint)));
|
2018-10-28 23:06:27 +01:00
|
|
|
connect(header(), SIGNAL(sectionCountChanged(int,int)), SIGNAL(viewStateChanged()));
|
|
|
|
connect(header(), SIGNAL(sectionMoved(int,int,int)), SIGNAL(viewStateChanged()));
|
|
|
|
connect(header(), SIGNAL(sectionResized(int,int,int)), SIGNAL(viewStateChanged()));
|
|
|
|
connect(header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), SIGNAL(viewStateChanged()));
|
2017-12-21 10:01:47 +01:00
|
|
|
|
2018-02-21 21:25:04 -05:00
|
|
|
resetFixedColumns();
|
2017-12-23 08:40:00 +01:00
|
|
|
|
2018-01-16 13:20:45 +01:00
|
|
|
// Configure default search view state and save for later use
|
2017-12-23 08:40:00 +01:00
|
|
|
header()->showSection(EntryModel::ParentGroup);
|
|
|
|
m_sortModel->sort(EntryModel::ParentGroup, Qt::AscendingOrder);
|
|
|
|
sortByColumn(EntryModel::ParentGroup, Qt::AscendingOrder);
|
|
|
|
m_defaultSearchViewState = header()->saveState();
|
|
|
|
|
2018-01-16 13:20:45 +01:00
|
|
|
// Configure default list view state and save for later use
|
2017-12-23 08:40:00 +01:00
|
|
|
header()->hideSection(EntryModel::ParentGroup);
|
|
|
|
m_sortModel->sort(EntryModel::Title, Qt::AscendingOrder);
|
|
|
|
sortByColumn(EntryModel::Title, Qt::AscendingOrder);
|
|
|
|
m_defaultListViewState = header()->saveState();
|
2018-02-24 07:44:28 -05:00
|
|
|
|
|
|
|
m_model->setPaperClipPixmap(filePath()->icon("actions", "paperclip").pixmap(16));
|
2010-08-24 22:26:52 +02:00
|
|
|
}
|
|
|
|
|
2013-09-29 17:33:45 +02:00
|
|
|
void EntryView::keyPressEvent(QKeyEvent* event)
|
|
|
|
{
|
|
|
|
if ((event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) && currentIndex().isValid()) {
|
2013-10-10 22:47:32 +02:00
|
|
|
emitEntryActivated(currentIndex());
|
2018-10-26 15:19:04 +02:00
|
|
|
#ifdef Q_OS_MACOS
|
2016-11-08 22:13:57 +01:00
|
|
|
// Pressing return does not emit the QTreeView::activated signal on mac os
|
2017-03-10 15:58:42 +01:00
|
|
|
emit activated(currentIndex());
|
2016-11-08 22:13:57 +01:00
|
|
|
#endif
|
2013-09-29 17:33:45 +02:00
|
|
|
}
|
2013-10-10 22:47:32 +02:00
|
|
|
|
2018-01-22 21:51:09 +01:00
|
|
|
int last = m_model->rowCount() - 1;
|
|
|
|
|
|
|
|
if (event->key() == Qt::Key_Up && currentIndex().row() == 0) {
|
|
|
|
QModelIndex index = m_sortModel->mapToSource(m_sortModel->index(last, 0));
|
|
|
|
setCurrentEntry(m_model->entryFromIndex(index));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (event->key() == Qt::Key_Down && currentIndex().row() == last) {
|
|
|
|
QModelIndex index = m_sortModel->mapToSource(m_sortModel->index(0, 0));
|
|
|
|
setCurrentEntry(m_model->entryFromIndex(index));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-10-10 22:47:32 +02:00
|
|
|
QTreeView::keyPressEvent(event);
|
2013-09-29 17:33:45 +02:00
|
|
|
}
|
|
|
|
|
2010-08-24 22:26:52 +02:00
|
|
|
void EntryView::setGroup(Group* group)
|
|
|
|
{
|
|
|
|
m_model->setGroup(group);
|
2014-05-16 19:10:30 +02:00
|
|
|
setFirstEntryActive();
|
2010-08-24 22:26:52 +02:00
|
|
|
}
|
|
|
|
|
2012-07-21 18:35:24 +02:00
|
|
|
void EntryView::setEntryList(const QList<Entry*>& entries)
|
2012-05-12 13:22:41 +02:00
|
|
|
{
|
2012-07-21 18:35:24 +02:00
|
|
|
m_model->setEntryList(entries);
|
2014-05-16 19:10:30 +02:00
|
|
|
setFirstEntryActive();
|
|
|
|
}
|
|
|
|
|
|
|
|
void EntryView::setFirstEntryActive()
|
|
|
|
{
|
2015-10-15 18:02:31 +02:00
|
|
|
if (m_model->rowCount() > 0) {
|
2014-05-16 19:10:30 +02:00
|
|
|
QModelIndex index = m_sortModel->mapToSource(m_sortModel->index(0, 0));
|
|
|
|
setCurrentEntry(m_model->entryFromIndex(index));
|
2018-01-16 13:20:45 +01:00
|
|
|
} else {
|
2017-03-10 15:58:42 +01:00
|
|
|
emit entrySelectionChanged();
|
2014-05-16 19:10:30 +02:00
|
|
|
}
|
2012-05-12 13:22:41 +02:00
|
|
|
}
|
|
|
|
|
2017-12-23 08:40:00 +01:00
|
|
|
bool EntryView::inSearchMode()
|
2012-05-13 18:08:22 +02:00
|
|
|
{
|
2017-12-23 08:40:00 +01:00
|
|
|
return m_inSearchMode;
|
2012-05-13 18:08:22 +02:00
|
|
|
}
|
|
|
|
|
2013-04-14 19:14:06 +02:00
|
|
|
void EntryView::emitEntryActivated(const QModelIndex& index)
|
2010-09-19 19:45:14 +02:00
|
|
|
{
|
2013-04-07 21:05:52 +02:00
|
|
|
Entry* entry = entryFromIndex(index);
|
|
|
|
|
2017-03-10 15:58:42 +01:00
|
|
|
emit entryActivated(entry, static_cast<EntryModel::ModelColumn>(m_sortModel->mapToSource(index).column()));
|
2010-09-19 19:45:14 +02:00
|
|
|
}
|
|
|
|
|
2017-08-17 21:02:21 +02:00
|
|
|
void EntryView::emitEntryPressed(const QModelIndex& index)
|
|
|
|
{
|
|
|
|
emit entryPressed(entryFromIndex(index));
|
|
|
|
}
|
|
|
|
|
2010-08-24 22:26:52 +02:00
|
|
|
void EntryView::setModel(QAbstractItemModel* model)
|
|
|
|
{
|
|
|
|
Q_UNUSED(model);
|
|
|
|
Q_ASSERT(false);
|
|
|
|
}
|
2011-12-29 19:01:58 +01:00
|
|
|
|
|
|
|
Entry* EntryView::currentEntry()
|
|
|
|
{
|
2012-05-25 13:25:46 +02:00
|
|
|
QModelIndexList list = selectionModel()->selectedRows();
|
|
|
|
if (list.size() == 1) {
|
|
|
|
return m_model->entryFromIndex(m_sortModel->mapToSource(list.first()));
|
2018-01-16 13:20:45 +01:00
|
|
|
} else {
|
2015-07-24 18:28:12 +02:00
|
|
|
return nullptr;
|
2012-05-25 13:25:46 +02:00
|
|
|
}
|
2011-12-29 19:01:58 +01:00
|
|
|
}
|
|
|
|
|
2014-05-15 18:05:58 +02:00
|
|
|
int EntryView::numberOfSelectedEntries()
|
|
|
|
{
|
|
|
|
return selectionModel()->selectedRows().size();
|
|
|
|
}
|
|
|
|
|
2012-05-02 19:33:37 +02:00
|
|
|
void EntryView::setCurrentEntry(Entry* entry)
|
|
|
|
{
|
2012-07-23 22:27:02 +02:00
|
|
|
selectionModel()->setCurrentIndex(m_sortModel->mapFromSource(m_model->indexFromEntry(entry)),
|
|
|
|
QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
|
2012-05-11 12:01:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Entry* EntryView::entryFromIndex(const QModelIndex& index)
|
|
|
|
{
|
|
|
|
if (index.isValid()) {
|
|
|
|
return m_model->entryFromIndex(m_sortModel->mapToSource(index));
|
2018-01-16 13:20:45 +01:00
|
|
|
} else {
|
2015-07-24 18:28:12 +02:00
|
|
|
return nullptr;
|
2012-05-11 12:01:01 +02:00
|
|
|
}
|
2012-05-02 19:33:37 +02:00
|
|
|
}
|
2012-05-12 13:22:41 +02:00
|
|
|
|
2017-12-23 08:40:00 +01:00
|
|
|
/**
|
2018-01-16 13:20:45 +01:00
|
|
|
* Switch to list mode, i.e. list entries of group
|
2017-12-23 08:40:00 +01:00
|
|
|
*/
|
|
|
|
void EntryView::switchToListMode()
|
2012-05-12 13:22:41 +02:00
|
|
|
{
|
2017-12-23 08:40:00 +01:00
|
|
|
if (!m_inSearchMode) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
header()->hideSection(EntryModel::ParentGroup);
|
|
|
|
m_inSearchMode = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-01-16 13:20:45 +01:00
|
|
|
* Switch to search mode, i.e. list search results
|
2017-12-23 08:40:00 +01:00
|
|
|
*/
|
|
|
|
void EntryView::switchToSearchMode()
|
|
|
|
{
|
|
|
|
if (m_inSearchMode) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-12-21 10:01:47 +01:00
|
|
|
header()->showSection(EntryModel::ParentGroup);
|
2017-02-17 14:18:18 +01:00
|
|
|
|
2018-01-16 13:20:45 +01:00
|
|
|
// Always set sorting to column 'Group', as it does not feel right to
|
|
|
|
// have the last known sort configuration of search view restored by
|
|
|
|
// 'DatabaseWidgetStateSync', which is what happens without this
|
2017-12-23 08:40:00 +01:00
|
|
|
m_sortModel->sort(EntryModel::ParentGroup, Qt::AscendingOrder);
|
2017-12-21 10:01:47 +01:00
|
|
|
sortByColumn(EntryModel::ParentGroup, Qt::AscendingOrder);
|
2017-02-17 14:18:18 +01:00
|
|
|
|
2017-12-23 08:40:00 +01:00
|
|
|
m_inSearchMode = true;
|
2012-05-12 13:22:41 +02:00
|
|
|
}
|
|
|
|
|
2017-12-23 08:40:00 +01:00
|
|
|
/**
|
|
|
|
* Get current state of 'Hide Usernames' setting (NOTE: just pass-through for
|
|
|
|
* m_model)
|
|
|
|
*/
|
2018-01-16 13:20:45 +01:00
|
|
|
bool EntryView::isUsernamesHidden() const
|
2012-05-12 13:22:41 +02:00
|
|
|
{
|
2018-01-16 13:20:45 +01:00
|
|
|
return m_model->isUsernamesHidden();
|
2017-12-23 08:40:00 +01:00
|
|
|
}
|
2017-02-17 14:18:18 +01:00
|
|
|
|
2017-12-23 08:40:00 +01:00
|
|
|
/**
|
|
|
|
* Set state of 'Hide Usernames' setting (NOTE: just pass-through for m_model)
|
|
|
|
*/
|
2018-01-16 13:20:45 +01:00
|
|
|
void EntryView::setUsernamesHidden(const bool hide)
|
2017-12-23 08:40:00 +01:00
|
|
|
{
|
2018-01-16 13:20:45 +01:00
|
|
|
m_model->setUsernamesHidden(hide);
|
2017-12-23 08:40:00 +01:00
|
|
|
}
|
2017-02-17 14:18:18 +01:00
|
|
|
|
2017-12-23 08:40:00 +01:00
|
|
|
/**
|
|
|
|
* Get current state of 'Hide Passwords' setting (NOTE: just pass-through for
|
|
|
|
* m_model)
|
|
|
|
*/
|
2018-01-16 13:20:45 +01:00
|
|
|
bool EntryView::isPasswordsHidden() const
|
2017-12-23 08:40:00 +01:00
|
|
|
{
|
2018-01-16 13:20:45 +01:00
|
|
|
return m_model->isPasswordsHidden();
|
2017-12-23 08:40:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set state of 'Hide Passwords' setting (NOTE: just pass-through for m_model)
|
|
|
|
*/
|
2018-01-16 13:20:45 +01:00
|
|
|
void EntryView::setPasswordsHidden(const bool hide)
|
2017-12-23 08:40:00 +01:00
|
|
|
{
|
2018-01-16 13:20:45 +01:00
|
|
|
m_model->setPasswordsHidden(hide);
|
2017-12-23 08:40:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-01-16 13:20:45 +01:00
|
|
|
* Get current view state
|
2017-12-23 08:40:00 +01:00
|
|
|
*/
|
|
|
|
QByteArray EntryView::viewState() const
|
|
|
|
{
|
|
|
|
return header()->saveState();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-01-16 13:20:45 +01:00
|
|
|
* Set view state
|
2017-12-23 08:40:00 +01:00
|
|
|
*/
|
2018-02-21 21:25:04 -05:00
|
|
|
bool EntryView::setViewState(const QByteArray& state)
|
2017-12-23 08:40:00 +01:00
|
|
|
{
|
2018-02-21 21:25:04 -05:00
|
|
|
bool status = header()->restoreState(state);
|
|
|
|
resetFixedColumns();
|
|
|
|
return status;
|
2012-05-12 13:22:41 +02:00
|
|
|
}
|
2017-12-21 10:01:47 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Sync checkable menu actions to current state and display header context
|
|
|
|
* menu at specified position
|
|
|
|
*/
|
|
|
|
void EntryView::showHeaderMenu(const QPoint& position)
|
|
|
|
{
|
2018-01-16 13:20:45 +01:00
|
|
|
m_hideUsernamesAction->setChecked(m_model->isUsernamesHidden());
|
|
|
|
m_hidePasswordsAction->setChecked(m_model->isPasswordsHidden());
|
|
|
|
const QList<QAction*> actions = m_columnActions->actions();
|
|
|
|
for (auto& action : actions) {
|
|
|
|
Q_ASSERT(static_cast<QMetaType::Type>(action->data().type()) == QMetaType::Int);
|
2017-12-21 10:01:47 +01:00
|
|
|
if (static_cast<QMetaType::Type>(action->data().type()) != QMetaType::Int) {
|
|
|
|
continue;
|
|
|
|
}
|
2018-01-16 13:20:45 +01:00
|
|
|
int columnIndex = action->data().toInt();
|
|
|
|
bool hidden = header()->isSectionHidden(columnIndex) || (header()->sectionSize(columnIndex) == 0);
|
2017-12-21 10:01:47 +01:00
|
|
|
action->setChecked(!hidden);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_headerMenu->popup(mapToGlobal(position));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Toggle visibility of column referenced by triggering action
|
|
|
|
*/
|
2018-03-31 16:01:30 -04:00
|
|
|
void EntryView::toggleColumnVisibility(QAction* action)
|
2017-12-21 10:01:47 +01:00
|
|
|
{
|
2018-01-16 13:20:45 +01:00
|
|
|
// Verify action carries a column index as data. Since QVariant.toInt()
|
|
|
|
// below will accept anything that's interpretable as int, perform a type
|
|
|
|
// check here to make sure data actually IS int
|
|
|
|
Q_ASSERT(static_cast<QMetaType::Type>(action->data().type()) == QMetaType::Int);
|
2017-12-21 10:01:47 +01:00
|
|
|
if (static_cast<QMetaType::Type>(action->data().type()) != QMetaType::Int) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-01-16 13:20:45 +01:00
|
|
|
// Toggle column visibility. Visible columns will only be hidden if at
|
|
|
|
// least one visible column remains, as the table header will disappear
|
|
|
|
// entirely when all columns are hidden
|
|
|
|
int columnIndex = action->data().toInt();
|
2017-12-21 10:01:47 +01:00
|
|
|
if (action->isChecked()) {
|
2018-01-16 13:20:45 +01:00
|
|
|
header()->showSection(columnIndex);
|
|
|
|
if (header()->sectionSize(columnIndex) == 0) {
|
|
|
|
header()->resizeSection(columnIndex, header()->defaultSectionSize());
|
2017-12-21 10:01:47 +01:00
|
|
|
}
|
2018-01-16 13:20:45 +01:00
|
|
|
return;
|
2017-12-21 10:01:47 +01:00
|
|
|
}
|
2018-01-16 13:20:45 +01:00
|
|
|
if ((header()->count() - header()->hiddenSectionCount()) > 1) {
|
|
|
|
header()->hideSection(columnIndex);
|
|
|
|
return;
|
2017-12-21 10:01:47 +01:00
|
|
|
}
|
2018-01-16 13:20:45 +01:00
|
|
|
action->setChecked(true);
|
2017-12-21 10:01:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Resize columns to fit all visible columns within the available space
|
|
|
|
*
|
|
|
|
* NOTE:
|
|
|
|
* If EntryView::resizeEvent() is overridden at some point in the future,
|
|
|
|
* its implementation MUST call the corresponding parent method using
|
|
|
|
* 'QTreeView::resizeEvent(event)'. Without this, fitting to window will
|
|
|
|
* be broken and/or work unreliably (stumbled upon during testing)
|
2017-12-23 08:40:00 +01:00
|
|
|
*
|
|
|
|
* NOTE:
|
|
|
|
* Testing showed that it is absolutely necessary to emit signal 'viewState
|
|
|
|
* Changed' here. Without this, an incomplete view state might get saved by
|
|
|
|
* 'DatabaseWidgetStateSync' (e.g. only some columns resized)
|
2017-12-21 10:01:47 +01:00
|
|
|
*/
|
|
|
|
void EntryView::fitColumnsToWindow()
|
|
|
|
{
|
|
|
|
header()->resizeSections(QHeaderView::Stretch);
|
2018-02-21 21:25:04 -05:00
|
|
|
resetFixedColumns();
|
|
|
|
fillRemainingWidth(true);
|
2017-12-23 08:40:00 +01:00
|
|
|
emit viewStateChanged();
|
2017-12-21 10:01:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Resize columns to fit current table contents, i.e. make all contents
|
|
|
|
* entirely visible
|
|
|
|
*/
|
|
|
|
void EntryView::fitColumnsToContents()
|
|
|
|
{
|
2018-01-16 13:20:45 +01:00
|
|
|
// Resize columns to fit contents
|
2017-12-21 10:01:47 +01:00
|
|
|
header()->resizeSections(QHeaderView::ResizeToContents);
|
2018-02-21 21:25:04 -05:00
|
|
|
resetFixedColumns();
|
|
|
|
fillRemainingWidth(false);
|
2017-12-23 08:40:00 +01:00
|
|
|
emit viewStateChanged();
|
2017-12-21 10:01:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reset view to defaults
|
|
|
|
*/
|
|
|
|
void EntryView::resetViewToDefaults()
|
|
|
|
{
|
2018-01-16 13:20:45 +01:00
|
|
|
m_model->setUsernamesHidden(false);
|
|
|
|
m_model->setPasswordsHidden(true);
|
2017-12-21 10:01:47 +01:00
|
|
|
|
2017-12-23 08:40:00 +01:00
|
|
|
if (m_inSearchMode) {
|
|
|
|
header()->restoreState(m_defaultSearchViewState);
|
2018-01-16 13:20:45 +01:00
|
|
|
} else {
|
2017-12-23 08:40:00 +01:00
|
|
|
header()->restoreState(m_defaultListViewState);
|
2017-12-21 10:01:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fitColumnsToWindow();
|
|
|
|
}
|
2018-02-21 21:25:04 -05:00
|
|
|
|
|
|
|
void EntryView::fillRemainingWidth(bool lastColumnOnly)
|
|
|
|
{
|
|
|
|
// Determine total width of currently visible columns
|
|
|
|
int width = 0;
|
|
|
|
int lastColumnIndex = 0;
|
|
|
|
for (int columnIndex = 0; columnIndex < header()->count(); ++columnIndex) {
|
|
|
|
if (!header()->isSectionHidden(columnIndex)) {
|
|
|
|
width += header()->sectionSize(columnIndex);
|
|
|
|
}
|
|
|
|
if (header()->visualIndex(columnIndex) > lastColumnIndex) {
|
|
|
|
lastColumnIndex = header()->visualIndex(columnIndex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int numColumns = header()->count() - header()->hiddenSectionCount();
|
|
|
|
int availWidth = header()->width() - width;
|
|
|
|
if ((numColumns <= 0) || (availWidth <= 0)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!lastColumnOnly) {
|
|
|
|
// Equally distribute remaining width to visible columns
|
|
|
|
int add = availWidth / numColumns;
|
|
|
|
width = 0;
|
|
|
|
for (int columnIndex = 0; columnIndex < header()->count(); ++columnIndex) {
|
|
|
|
if (!header()->isSectionHidden(columnIndex)) {
|
|
|
|
header()->resizeSection(columnIndex, header()->sectionSize(columnIndex) + add);
|
|
|
|
width += header()->sectionSize(columnIndex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add remaining width to last column
|
2018-03-31 16:01:30 -04:00
|
|
|
header()->resizeSection(header()->logicalIndex(lastColumnIndex),
|
|
|
|
header()->sectionSize(lastColumnIndex) + (header()->width() - width));
|
2018-02-21 21:25:04 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void EntryView::resetFixedColumns()
|
|
|
|
{
|
|
|
|
header()->setSectionResizeMode(EntryModel::Paperclip, QHeaderView::Fixed);
|
|
|
|
header()->resizeSection(EntryModel::Paperclip, header()->minimumSectionSize());
|
|
|
|
}
|