Files
keepassxc/src/autotype/AutoTypeMatchModel.cpp
Jonathan White d9ae449f04 Improve Auto-Type Select Dialog
Significant improvements to the Auto-Type select dialog. Reduce stale and unnecessary code paths.

* Close select dialog when databases are locked.
* Close open modal dialogs prior to showing the Auto-Type select dialog to prevent interference.
* Never perform Auto-Type on the KeePassXC window.
* Only filter match list based on Group, Title, and Username column data (ie, ignore sequence column)
* Always show the sequence column (revert feature)
* Show selection dialog if there are no matches to allow for a database search

* Close #3630 - Allow typing {USERNAME} and {PASSWORD} from selection dialog (right-click menu).
* Close #429 - Ability to search open databases for an entry from the Auto-Type selection dialog.
* Fix #5361 - Default size of selection dialog doesn't cut off matches
2021-02-21 16:33:54 -05:00

195 lines
5.1 KiB
C++

/*
* Copyright (C) 2015 David Wu <lightvector@gmail.com>
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
*
* 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.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "AutoTypeMatchModel.h"
#include <QFont>
#include "core/DatabaseIcons.h"
#include "core/Entry.h"
#include "core/Global.h"
#include "core/Group.h"
#include "core/Metadata.h"
AutoTypeMatchModel::AutoTypeMatchModel(QObject* parent)
: QAbstractTableModel(parent)
{
}
AutoTypeMatch AutoTypeMatchModel::matchFromIndex(const QModelIndex& index) const
{
Q_ASSERT(index.isValid() && index.row() < m_matches.size());
return m_matches.at(index.row());
}
QModelIndex AutoTypeMatchModel::indexFromMatch(const AutoTypeMatch& match) const
{
int row = m_matches.indexOf(match);
Q_ASSERT(row != -1);
return index(row, 1);
}
void AutoTypeMatchModel::setMatchList(const QList<AutoTypeMatch>& matches)
{
beginResetModel();
severConnections();
m_allGroups.clear();
m_matches = matches;
QSet<Database*> databases;
for (AutoTypeMatch& match : m_matches) {
databases.insert(match.first->group()->database());
}
for (Database* db : asConst(databases)) {
Q_ASSERT(db);
for (const Group* group : db->rootGroup()->groupsRecursive(true)) {
m_allGroups.append(group);
}
if (db->metadata()->recycleBin()) {
m_allGroups.removeOne(db->metadata()->recycleBin());
}
}
for (const Group* group : asConst(m_allGroups)) {
makeConnections(group);
}
endResetModel();
}
int AutoTypeMatchModel::rowCount(const QModelIndex& parent) const
{
if (parent.isValid()) {
return 0;
}
return m_matches.size();
}
int AutoTypeMatchModel::columnCount(const QModelIndex& parent) const
{
Q_UNUSED(parent);
return 4;
}
QVariant AutoTypeMatchModel::data(const QModelIndex& index, int role) const
{
if (!index.isValid()) {
return {};
}
AutoTypeMatch match = matchFromIndex(index);
if (role == Qt::DisplayRole) {
switch (index.column()) {
case ParentGroup:
if (match.first->group()) {
return match.first->group()->name();
}
break;
case Title:
return match.first->resolveMultiplePlaceholders(match.first->title());
case Username:
return match.first->resolveMultiplePlaceholders(match.first->username());
case Sequence:
return match.second;
}
} else if (role == Qt::DecorationRole) {
switch (index.column()) {
case ParentGroup:
if (match.first->group()) {
return match.first->group()->iconPixmap();
}
break;
case Title:
return match.first->iconPixmap();
}
} else if (role == Qt::FontRole) {
QFont font;
if (match.first->isExpired()) {
font.setStrikeOut(true);
}
return font;
}
return {};
}
QVariant AutoTypeMatchModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
switch (section) {
case ParentGroup:
return tr("Group");
case Title:
return tr("Title");
case Username:
return tr("Username");
case Sequence:
return tr("Sequence");
}
}
return {};
}
void AutoTypeMatchModel::entryDataChanged(Entry* entry)
{
for (int row = 0; row < m_matches.size(); ++row) {
AutoTypeMatch match = m_matches[row];
if (match.first == entry) {
emit dataChanged(index(row, 0), index(row, columnCount() - 1));
}
}
}
void AutoTypeMatchModel::entryAboutToRemove(Entry* entry)
{
for (int row = 0; row < m_matches.size(); ++row) {
AutoTypeMatch match = m_matches[row];
if (match.first == entry) {
beginRemoveRows(QModelIndex(), row, row);
m_matches.removeAt(row);
endRemoveRows();
--row;
}
}
}
void AutoTypeMatchModel::entryRemoved()
{
}
void AutoTypeMatchModel::severConnections()
{
for (const Group* group : asConst(m_allGroups)) {
disconnect(group, nullptr, this, nullptr);
}
}
void AutoTypeMatchModel::makeConnections(const Group* group)
{
connect(group, SIGNAL(entryAboutToRemove(Entry*)), SLOT(entryAboutToRemove(Entry*)));
connect(group, SIGNAL(entryRemoved(Entry*)), SLOT(entryRemoved()));
connect(group, SIGNAL(entryDataChanged(Entry*)), SLOT(entryDataChanged(Entry*)));
}