Files
keepassxc/src/gui/entry/EntryHistoryModel.cpp
2022-04-03 13:28:39 -04:00

258 lines
7.7 KiB
C++

/*
* Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
*
* 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 "EntryHistoryModel.h"
#include "core/Clock.h"
#include "core/Entry.h"
#include "core/Global.h"
#include "core/Tools.h"
#include <QFont>
EntryHistoryModel::EntryHistoryModel(QObject* parent)
: QAbstractTableModel(parent)
{
}
Entry* EntryHistoryModel::entryFromIndex(const QModelIndex& index) const
{
if (!index.isValid() || index.row() >= m_historyEntries.size()) {
return nullptr;
}
auto entry = m_historyEntries.at(index.row());
return entry == m_parentEntry ? nullptr : entry;
}
int EntryHistoryModel::columnCount(const QModelIndex& parent) const
{
Q_UNUSED(parent);
return 4;
}
int EntryHistoryModel::rowCount(const QModelIndex& parent) const
{
if (!parent.isValid()) {
return m_historyEntries.count();
} else {
return 0;
}
}
QVariant EntryHistoryModel::data(const QModelIndex& index, int role) const
{
if (index.row() >= m_historyEntries.size()) {
return {};
}
const auto entry = m_historyEntries[index.row()];
if (role == Qt::DisplayRole || role == Qt::UserRole) {
QDateTime lastModified = entry->timeInfo().lastModificationTime().toLocalTime();
QDateTime now = Clock::currentDateTime();
switch (index.column()) {
case 0:
if (role == Qt::DisplayRole) {
return lastModified.toString(Qt::SystemLocaleShortDate);
} else {
return lastModified;
}
case 1: {
const auto seconds = lastModified.secsTo(now);
if (role == Qt::DisplayRole) {
if (entry == m_parentEntry) {
return tr("Current (%1)").arg(Tools::humanReadableTimeDifference(seconds));
}
return Tools::humanReadableTimeDifference(seconds);
}
return seconds;
}
case 2:
if (index.row() < m_historyModifications.size()) {
return m_historyModifications[index.row()];
}
return {};
case 3:
if (role == Qt::DisplayRole) {
return Tools::humanReadableFileSize(entry->size(), 0);
}
return entry->size();
}
} else if (role == Qt::FontRole && entry == m_parentEntry) {
QFont font;
font.setBold(true);
return font;
}
return {};
}
QVariant EntryHistoryModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
switch (section) {
case 0:
return tr("Last modified");
case 1:
return tr("Age");
case 2:
return tr("Difference");
case 3:
return tr("Size");
}
}
return {};
}
void EntryHistoryModel::setEntries(const QList<Entry*>& entries, Entry* parentEntry)
{
beginResetModel();
m_parentEntry = parentEntry;
m_historyEntries = entries;
m_historyEntries << parentEntry;
// Sort the entries by last modified (newest -> oldest) so we can calculate the differences
std::sort(m_historyEntries.begin(), m_historyEntries.end(), [](const Entry* lhs, const Entry* rhs) {
return lhs->timeInfo().lastModificationTime() > rhs->timeInfo().lastModificationTime();
});
m_deletedHistoryEntries.clear();
calculateHistoryModifications();
endResetModel();
}
void EntryHistoryModel::clear()
{
beginResetModel();
m_historyEntries.clear();
m_deletedHistoryEntries.clear();
endResetModel();
}
void EntryHistoryModel::clearDeletedEntries()
{
m_deletedHistoryEntries.clear();
}
QList<Entry*> EntryHistoryModel::deletedEntries()
{
return m_deletedHistoryEntries;
}
void EntryHistoryModel::deleteIndex(QModelIndex index)
{
auto entry = entryFromIndex(index);
if (entry) {
beginRemoveRows(QModelIndex(), m_historyEntries.indexOf(entry), m_historyEntries.indexOf(entry));
m_historyEntries.removeAll(entry);
m_deletedHistoryEntries << entry;
endRemoveRows();
}
}
void EntryHistoryModel::deleteAll()
{
Q_ASSERT(m_historyEntries.count() > 0);
beginRemoveRows(QModelIndex(), 0, m_historyEntries.size() - 1);
for (Entry* entry : asConst(m_historyEntries)) {
if (entry != m_parentEntry) {
m_deletedHistoryEntries << entry;
}
}
m_historyEntries.clear();
endRemoveRows();
}
void EntryHistoryModel::calculateHistoryModifications()
{
m_historyModifications.clear();
Entry* compare = nullptr;
for (const auto curr : m_historyEntries) {
if (!compare) {
compare = curr;
continue;
}
QStringList modifiedFields;
if (*curr->attributes() != *compare->attributes()) {
bool foundAttribute = false;
if (curr->title() != compare->title()) {
modifiedFields << tr("Title");
foundAttribute = true;
}
if (curr->username() != compare->username()) {
modifiedFields << tr("Username");
foundAttribute = true;
}
if (curr->password() != compare->password()) {
modifiedFields << tr("Password");
foundAttribute = true;
}
if (curr->url() != compare->url()) {
modifiedFields << tr("URL");
foundAttribute = true;
}
if (curr->notes() != compare->notes()) {
modifiedFields << tr("Notes");
foundAttribute = true;
}
if (!foundAttribute) {
modifiedFields << tr("Custom Attributes");
}
}
if (curr->iconNumber() != compare->iconNumber() || curr->iconUuid() != compare->iconUuid()) {
modifiedFields << tr("Icon");
}
if (curr->foregroundColor() != compare->foregroundColor()
|| curr->backgroundColor() != compare->backgroundColor()) {
modifiedFields << tr("Color");
}
if (curr->timeInfo().expires() != compare->timeInfo().expires()
|| curr->timeInfo().expiryTime() != compare->timeInfo().expiryTime()) {
modifiedFields << tr("Expiration");
}
if (curr->totp() != compare->totp()) {
modifiedFields << tr("TOTP");
}
if (*curr->customData() != *compare->customData()) {
modifiedFields << tr("Custom Data");
}
if (*curr->attachments() != *compare->attachments()) {
modifiedFields << tr("Attachments");
}
if (*curr->autoTypeAssociations() != *compare->autoTypeAssociations()
|| curr->autoTypeEnabled() != compare->autoTypeEnabled()
|| curr->defaultAutoTypeSequence() != compare->defaultAutoTypeSequence()) {
modifiedFields << tr("Auto-Type");
}
if (curr->tags() != compare->tags()) {
modifiedFields << tr("Tags");
}
m_historyModifications << modifiedFields.join(", ");
compare = curr;
}
}