2010-08-07 09:10:44 -04:00
|
|
|
/*
|
2012-04-27 04:50:32 -04:00
|
|
|
* Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
|
2017-06-09 17:40:36 -04:00
|
|
|
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
2012-04-27 04:50:32 -04: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.
|
|
|
|
*
|
|
|
|
* 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/>.
|
|
|
|
*/
|
2010-08-07 09:10:44 -04:00
|
|
|
|
|
|
|
#include "Group.h"
|
|
|
|
|
2018-09-30 08:45:06 -04:00
|
|
|
#include "core/Clock.h"
|
2012-05-28 14:16:49 -04:00
|
|
|
#include "core/Config.h"
|
2011-07-08 08:51:14 -04:00
|
|
|
#include "core/DatabaseIcons.h"
|
2017-05-21 13:05:44 -04:00
|
|
|
#include "core/Global.h"
|
2011-07-08 08:51:14 -04:00
|
|
|
#include "core/Metadata.h"
|
2010-08-12 15:38:59 -04:00
|
|
|
|
2012-07-01 15:58:45 -04:00
|
|
|
const int Group::DefaultIconNumber = 48;
|
2012-07-21 05:57:00 -04:00
|
|
|
const int Group::RecycleBinIconNumber = 43;
|
2018-01-18 17:45:09 -05:00
|
|
|
const QString Group::RootAutoTypeSequence = "{USERNAME}{TAB}{PASSWORD}{ENTER}";
|
2012-07-01 15:58:45 -04:00
|
|
|
|
2018-03-31 16:01:30 -04:00
|
|
|
Group::CloneFlags Group::DefaultCloneFlags =
|
|
|
|
static_cast<Group::CloneFlags>(Group::CloneNewUuid | Group::CloneResetTimeInfo | Group::CloneIncludeEntries);
|
|
|
|
Entry::CloneFlags Group::DefaultEntryCloneFlags =
|
|
|
|
static_cast<Entry::CloneFlags>(Entry::CloneNewUuid | Entry::CloneResetTimeInfo);
|
2017-11-21 14:02:21 -05:00
|
|
|
|
2010-08-13 12:08:06 -04:00
|
|
|
Group::Group()
|
2018-02-06 10:37:06 -05:00
|
|
|
: m_customData(new CustomData(this))
|
|
|
|
, m_updateTimeinfo(true)
|
2010-08-12 15:38:59 -04:00
|
|
|
{
|
2013-04-04 15:48:55 -04:00
|
|
|
m_data.iconNumber = DefaultIconNumber;
|
|
|
|
m_data.isExpanded = true;
|
|
|
|
m_data.autoTypeEnabled = Inherit;
|
|
|
|
m_data.searchingEnabled = Inherit;
|
2018-09-30 08:45:06 -04:00
|
|
|
m_data.mergeMode = Default;
|
2018-02-06 10:37:06 -05:00
|
|
|
|
|
|
|
connect(m_customData, SIGNAL(modified()), this, SIGNAL(modified()));
|
|
|
|
connect(this, SIGNAL(modified()), SLOT(updateTimeinfo()));
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
2010-08-18 10:22:48 -04:00
|
|
|
Group::~Group()
|
|
|
|
{
|
2018-09-30 08:45:06 -04:00
|
|
|
setUpdateTimeinfo(false);
|
2012-07-19 17:32:31 -04:00
|
|
|
// Destroy entries and children manually so DeletedObjects can be added
|
|
|
|
// to database.
|
2016-09-02 13:51:51 -04:00
|
|
|
const QList<Entry*> entries = m_entries;
|
|
|
|
for (Entry* entry : entries) {
|
2013-03-22 15:53:10 -04:00
|
|
|
delete entry;
|
|
|
|
}
|
2012-04-21 13:06:28 -04:00
|
|
|
|
2016-09-02 13:51:51 -04:00
|
|
|
const QList<Group*> children = m_children;
|
|
|
|
for (Group* group : children) {
|
2013-03-22 15:53:10 -04:00
|
|
|
delete group;
|
|
|
|
}
|
2012-04-21 13:06:28 -04:00
|
|
|
|
2013-03-22 15:53:10 -04:00
|
|
|
if (m_db && m_parent) {
|
2012-04-21 13:06:28 -04:00
|
|
|
DeletedObject delGroup;
|
2018-09-30 08:45:06 -04:00
|
|
|
delGroup.deletionTime = Clock::currentDateTimeUtc();
|
2012-04-21 13:06:28 -04:00
|
|
|
delGroup.uuid = m_uuid;
|
|
|
|
m_db->addDeletedObject(delGroup);
|
|
|
|
}
|
2013-03-22 15:57:18 -04:00
|
|
|
|
|
|
|
cleanupParent();
|
2010-08-18 10:22:48 -04:00
|
|
|
}
|
|
|
|
|
2012-07-21 05:57:00 -04:00
|
|
|
Group* Group::createRecycleBin()
|
|
|
|
{
|
|
|
|
Group* recycleBin = new Group();
|
2018-03-22 17:56:05 -04:00
|
|
|
recycleBin->setUuid(QUuid::createUuid());
|
2012-07-21 05:57:00 -04:00
|
|
|
recycleBin->setName(tr("Recycle Bin"));
|
|
|
|
recycleBin->setIcon(RecycleBinIconNumber);
|
|
|
|
recycleBin->setSearchingEnabled(Group::Disable);
|
|
|
|
recycleBin->setAutoTypeEnabled(Group::Disable);
|
|
|
|
return recycleBin;
|
|
|
|
}
|
|
|
|
|
2018-03-31 16:01:30 -04:00
|
|
|
template <class P, class V> inline bool Group::set(P& property, const V& value)
|
|
|
|
{
|
2012-04-11 12:00:28 -04:00
|
|
|
if (property != value) {
|
|
|
|
property = value;
|
2017-03-10 09:58:42 -05:00
|
|
|
emit modified();
|
2012-04-11 12:00:28 -04:00
|
|
|
return true;
|
2017-11-03 16:29:42 -04:00
|
|
|
} else {
|
2012-04-11 12:00:28 -04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-30 08:45:06 -04:00
|
|
|
bool Group::canUpdateTimeinfo() const
|
|
|
|
{
|
|
|
|
return m_updateTimeinfo;
|
|
|
|
}
|
|
|
|
|
2012-04-17 16:43:21 -04:00
|
|
|
void Group::updateTimeinfo()
|
|
|
|
{
|
|
|
|
if (m_updateTimeinfo) {
|
2018-09-30 08:45:06 -04:00
|
|
|
m_data.timeInfo.setLastModificationTime(Clock::currentDateTimeUtc());
|
|
|
|
m_data.timeInfo.setLastAccessTime(Clock::currentDateTimeUtc());
|
2012-04-17 16:43:21 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-29 18:22:07 -04:00
|
|
|
void Group::setUpdateTimeinfo(bool value)
|
|
|
|
{
|
2012-04-11 12:00:28 -04:00
|
|
|
m_updateTimeinfo = value;
|
|
|
|
}
|
|
|
|
|
2018-03-22 17:56:05 -04:00
|
|
|
const QUuid& Group::uuid() const
|
2010-08-12 15:38:59 -04:00
|
|
|
{
|
|
|
|
return m_uuid;
|
|
|
|
}
|
|
|
|
|
2018-09-30 08:45:06 -04:00
|
|
|
const QString Group::uuidToHex() const
|
|
|
|
{
|
|
|
|
return QString::fromLatin1(m_uuid.toRfc4122().toHex());
|
|
|
|
}
|
|
|
|
|
2010-08-12 15:38:59 -04:00
|
|
|
QString Group::name() const
|
|
|
|
{
|
2013-04-04 15:48:55 -04:00
|
|
|
return m_data.name;
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
QString Group::notes() const
|
|
|
|
{
|
2013-04-04 15:48:55 -04:00
|
|
|
return m_data.notes;
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
2012-01-01 15:52:54 -05:00
|
|
|
QImage Group::icon() const
|
2010-08-12 15:38:59 -04:00
|
|
|
{
|
2013-04-04 15:48:55 -04:00
|
|
|
if (m_data.customIcon.isNull()) {
|
|
|
|
return databaseIcons()->icon(m_data.iconNumber);
|
2018-03-31 16:01:30 -04:00
|
|
|
} else {
|
2013-07-04 07:31:38 -04:00
|
|
|
Q_ASSERT(m_db);
|
|
|
|
|
|
|
|
if (m_db) {
|
|
|
|
return m_db->metadata()->customIcon(m_data.customIcon);
|
2018-03-31 16:01:30 -04:00
|
|
|
} else {
|
2013-07-04 07:31:38 -04:00
|
|
|
return QImage();
|
|
|
|
}
|
2010-08-24 17:12:01 -04:00
|
|
|
}
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
2012-01-01 15:52:54 -05:00
|
|
|
QPixmap Group::iconPixmap() const
|
|
|
|
{
|
2013-04-04 15:48:55 -04:00
|
|
|
if (m_data.customIcon.isNull()) {
|
|
|
|
return databaseIcons()->iconPixmap(m_data.iconNumber);
|
2018-03-31 16:01:30 -04:00
|
|
|
} else {
|
2013-07-04 07:31:38 -04:00
|
|
|
Q_ASSERT(m_db);
|
|
|
|
|
2016-01-24 11:56:35 -05:00
|
|
|
if (m_db) {
|
|
|
|
return m_db->metadata()->customIconPixmap(m_data.customIcon);
|
2018-03-31 16:01:30 -04:00
|
|
|
} else {
|
2016-01-24 11:56:35 -05:00
|
|
|
return QPixmap();
|
2012-01-01 15:52:54 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-24 12:45:10 -05:00
|
|
|
QPixmap Group::iconScaledPixmap() const
|
|
|
|
{
|
|
|
|
if (m_data.customIcon.isNull()) {
|
|
|
|
// built-in icons are 16x16 so don't need to be scaled
|
|
|
|
return databaseIcons()->iconPixmap(m_data.iconNumber);
|
2018-03-31 16:01:30 -04:00
|
|
|
} else {
|
2016-01-24 12:45:10 -05:00
|
|
|
Q_ASSERT(m_db);
|
2012-01-01 15:52:54 -05:00
|
|
|
|
2016-01-24 12:45:10 -05:00
|
|
|
if (m_db) {
|
|
|
|
return m_db->metadata()->customIconScaledPixmap(m_data.customIcon);
|
2018-03-31 16:01:30 -04:00
|
|
|
} else {
|
2016-01-24 12:45:10 -05:00
|
|
|
return QPixmap();
|
|
|
|
}
|
2012-01-01 15:52:54 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-08-18 16:57:26 -04:00
|
|
|
int Group::iconNumber() const
|
|
|
|
{
|
2013-04-04 15:48:55 -04:00
|
|
|
return m_data.iconNumber;
|
2010-08-18 16:57:26 -04:00
|
|
|
}
|
|
|
|
|
2018-03-22 17:56:05 -04:00
|
|
|
const QUuid& Group::iconUuid() const
|
2010-08-18 16:57:26 -04:00
|
|
|
{
|
2013-04-04 15:48:55 -04:00
|
|
|
return m_data.customIcon;
|
2010-08-18 16:57:26 -04:00
|
|
|
}
|
|
|
|
|
2018-09-30 08:45:06 -04:00
|
|
|
const TimeInfo& Group::timeInfo() const
|
2010-08-12 15:38:59 -04:00
|
|
|
{
|
2013-04-04 15:48:55 -04:00
|
|
|
return m_data.timeInfo;
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Group::isExpanded() const
|
|
|
|
{
|
2013-04-04 15:48:55 -04:00
|
|
|
return m_data.isExpanded;
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
QString Group::defaultAutoTypeSequence() const
|
|
|
|
{
|
2013-04-04 15:48:55 -04:00
|
|
|
return m_data.defaultAutoTypeSequence;
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
2018-01-16 11:23:29 -05:00
|
|
|
/**
|
|
|
|
* Determine the effective sequence that will be injected
|
|
|
|
* This function return an empty string if the current group or any parent has autotype disabled
|
|
|
|
*/
|
2016-11-11 16:26:07 -05:00
|
|
|
QString Group::effectiveAutoTypeSequence() const
|
|
|
|
{
|
|
|
|
QString sequence;
|
|
|
|
|
|
|
|
const Group* group = this;
|
|
|
|
do {
|
|
|
|
if (group->autoTypeEnabled() == Group::Disable) {
|
|
|
|
return QString();
|
|
|
|
}
|
|
|
|
|
|
|
|
sequence = group->defaultAutoTypeSequence();
|
|
|
|
group = group->parentGroup();
|
|
|
|
} while (group && sequence.isEmpty());
|
|
|
|
|
2016-11-25 12:26:59 -05:00
|
|
|
if (sequence.isEmpty()) {
|
2018-01-18 17:45:09 -05:00
|
|
|
sequence = RootAutoTypeSequence;
|
2016-11-25 12:26:59 -05:00
|
|
|
}
|
|
|
|
|
2016-11-11 16:26:07 -05:00
|
|
|
return sequence;
|
|
|
|
}
|
|
|
|
|
2011-07-07 06:45:14 -04:00
|
|
|
Group::TriState Group::autoTypeEnabled() const
|
2010-08-19 08:03:54 -04:00
|
|
|
{
|
2013-04-04 15:48:55 -04:00
|
|
|
return m_data.autoTypeEnabled;
|
2010-08-19 08:03:54 -04:00
|
|
|
}
|
|
|
|
|
2012-05-13 07:33:55 -04:00
|
|
|
Group::TriState Group::searchingEnabled() const
|
2010-08-19 08:03:54 -04:00
|
|
|
{
|
2013-04-04 15:48:55 -04:00
|
|
|
return m_data.searchingEnabled;
|
2010-08-19 08:03:54 -04:00
|
|
|
}
|
|
|
|
|
2016-11-07 22:37:42 -05:00
|
|
|
Group::MergeMode Group::mergeMode() const
|
|
|
|
{
|
2018-09-30 08:45:06 -04:00
|
|
|
if (m_data.mergeMode == Group::MergeMode::Default) {
|
2016-11-07 22:37:42 -05:00
|
|
|
if (m_parent) {
|
|
|
|
return m_parent->mergeMode();
|
|
|
|
}
|
2018-09-30 08:45:06 -04:00
|
|
|
return Group::MergeMode::KeepNewer; // fallback
|
2016-11-07 22:37:42 -05:00
|
|
|
}
|
2018-09-30 08:45:06 -04:00
|
|
|
return m_data.mergeMode;
|
2016-11-07 22:37:42 -05:00
|
|
|
}
|
|
|
|
|
2010-08-12 15:38:59 -04:00
|
|
|
Entry* Group::lastTopVisibleEntry() const
|
|
|
|
{
|
|
|
|
return m_lastTopVisibleEntry;
|
|
|
|
}
|
|
|
|
|
2012-05-15 13:58:10 -04:00
|
|
|
bool Group::isExpired() const
|
|
|
|
{
|
2018-09-30 08:45:06 -04:00
|
|
|
return m_data.timeInfo.expires() && m_data.timeInfo.expiryTime() < Clock::currentDateTimeUtc();
|
2012-05-15 13:58:10 -04:00
|
|
|
}
|
|
|
|
|
2018-02-18 15:01:22 -05:00
|
|
|
CustomData* Group::customData()
|
2018-02-06 10:37:06 -05:00
|
|
|
{
|
|
|
|
return m_customData;
|
|
|
|
}
|
|
|
|
|
2018-02-18 15:01:22 -05:00
|
|
|
const CustomData* Group::customData() const
|
2018-02-06 10:37:06 -05:00
|
|
|
{
|
|
|
|
return m_customData;
|
|
|
|
}
|
|
|
|
|
2018-09-30 08:45:06 -04:00
|
|
|
bool Group::equals(const Group* other, CompareItemOptions options) const
|
|
|
|
{
|
|
|
|
if (!other) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (m_uuid != other->m_uuid) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!m_data.equals(other->m_data, options)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (m_customData != other->m_customData) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (m_children.count() != other->m_children.count()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (m_entries.count() != other->m_entries.count()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
for (int i = 0; i < m_children.count(); ++i) {
|
|
|
|
if (m_children[i]->uuid() != other->m_children[i]->uuid()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (int i = 0; i < m_entries.count(); ++i) {
|
|
|
|
if (m_entries[i]->uuid() != other->m_entries[i]->uuid()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-03-22 17:56:05 -04:00
|
|
|
void Group::setUuid(const QUuid& uuid)
|
2010-08-12 15:38:59 -04:00
|
|
|
{
|
2012-04-11 12:00:28 -04:00
|
|
|
set(m_uuid, uuid);
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Group::setName(const QString& name)
|
|
|
|
{
|
2013-04-04 15:48:55 -04:00
|
|
|
if (set(m_data.name, name)) {
|
2017-03-10 09:58:42 -05:00
|
|
|
emit dataChanged(this);
|
2012-04-11 12:00:28 -04:00
|
|
|
}
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Group::setNotes(const QString& notes)
|
|
|
|
{
|
2013-04-04 15:48:55 -04:00
|
|
|
set(m_data.notes, notes);
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Group::setIcon(int iconNumber)
|
|
|
|
{
|
2018-10-30 08:42:35 -04:00
|
|
|
if (iconNumber >= 0 && (m_data.iconNumber != iconNumber || !m_data.customIcon.isNull())) {
|
2013-04-04 15:48:55 -04:00
|
|
|
m_data.iconNumber = iconNumber;
|
2018-03-22 17:56:05 -04:00
|
|
|
m_data.customIcon = QUuid();
|
2017-03-10 09:58:42 -05:00
|
|
|
emit modified();
|
|
|
|
emit dataChanged(this);
|
2012-04-11 12:00:28 -04:00
|
|
|
}
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
2018-03-22 17:56:05 -04:00
|
|
|
void Group::setIcon(const QUuid& uuid)
|
2010-08-12 15:38:59 -04:00
|
|
|
{
|
2018-10-30 08:42:35 -04:00
|
|
|
if (!uuid.isNull() && m_data.customIcon != uuid) {
|
2013-04-04 15:48:55 -04:00
|
|
|
m_data.customIcon = uuid;
|
|
|
|
m_data.iconNumber = 0;
|
2017-03-10 09:58:42 -05:00
|
|
|
emit modified();
|
|
|
|
emit dataChanged(this);
|
2012-04-11 12:00:28 -04:00
|
|
|
}
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Group::setTimeInfo(const TimeInfo& timeInfo)
|
|
|
|
{
|
2013-04-04 15:48:55 -04:00
|
|
|
m_data.timeInfo = timeInfo;
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Group::setExpanded(bool expanded)
|
|
|
|
{
|
2013-04-04 15:48:55 -04:00
|
|
|
if (m_data.isExpanded != expanded) {
|
|
|
|
m_data.isExpanded = expanded;
|
2017-04-04 10:21:45 -04:00
|
|
|
if (config()->get("IgnoreGroupExpansion").toBool()) {
|
2018-02-06 10:37:06 -05:00
|
|
|
updateTimeinfo();
|
2017-04-04 10:21:45 -04:00
|
|
|
return;
|
|
|
|
}
|
2017-03-10 09:58:42 -05:00
|
|
|
emit modified();
|
2012-05-28 14:16:49 -04:00
|
|
|
}
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Group::setDefaultAutoTypeSequence(const QString& sequence)
|
|
|
|
{
|
2013-04-04 15:48:55 -04:00
|
|
|
set(m_data.defaultAutoTypeSequence, sequence);
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
2011-07-07 06:45:14 -04:00
|
|
|
void Group::setAutoTypeEnabled(TriState enable)
|
2010-08-19 08:03:54 -04:00
|
|
|
{
|
2013-04-04 15:48:55 -04:00
|
|
|
set(m_data.autoTypeEnabled, enable);
|
2010-08-19 08:03:54 -04:00
|
|
|
}
|
|
|
|
|
2011-07-07 06:45:14 -04:00
|
|
|
void Group::setSearchingEnabled(TriState enable)
|
2010-08-19 08:03:54 -04:00
|
|
|
{
|
2013-04-04 15:48:55 -04:00
|
|
|
set(m_data.searchingEnabled, enable);
|
2010-08-19 08:03:54 -04:00
|
|
|
}
|
|
|
|
|
2010-08-12 15:38:59 -04:00
|
|
|
void Group::setLastTopVisibleEntry(Entry* entry)
|
|
|
|
{
|
2012-04-11 12:00:28 -04:00
|
|
|
set(m_lastTopVisibleEntry, entry);
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
2013-03-26 18:53:34 -04:00
|
|
|
void Group::setExpires(bool value)
|
2012-05-18 04:52:05 -04:00
|
|
|
{
|
2013-04-04 15:48:55 -04:00
|
|
|
if (m_data.timeInfo.expires() != value) {
|
|
|
|
m_data.timeInfo.setExpires(value);
|
2017-03-10 09:58:42 -05:00
|
|
|
emit modified();
|
2012-05-18 04:52:05 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Group::setExpiryTime(const QDateTime& dateTime)
|
|
|
|
{
|
2013-04-04 15:48:55 -04:00
|
|
|
if (m_data.timeInfo.expiryTime() != dateTime) {
|
|
|
|
m_data.timeInfo.setExpiryTime(dateTime);
|
2017-03-10 09:58:42 -05:00
|
|
|
emit modified();
|
2012-05-18 04:52:05 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-07 22:37:42 -05:00
|
|
|
void Group::setMergeMode(MergeMode newMode)
|
|
|
|
{
|
|
|
|
set(m_data.mergeMode, newMode);
|
|
|
|
}
|
|
|
|
|
2010-08-23 15:30:20 -04:00
|
|
|
Group* Group::parentGroup()
|
|
|
|
{
|
|
|
|
return m_parent;
|
|
|
|
}
|
|
|
|
|
2010-08-15 06:31:48 -04:00
|
|
|
const Group* Group::parentGroup() const
|
|
|
|
{
|
|
|
|
return m_parent;
|
|
|
|
}
|
|
|
|
|
2010-08-18 04:27:40 -04:00
|
|
|
void Group::setParent(Group* parent, int index)
|
2010-08-12 15:38:59 -04:00
|
|
|
{
|
2010-08-18 04:27:40 -04:00
|
|
|
Q_ASSERT(parent);
|
|
|
|
Q_ASSERT(index >= -1 && index <= parent->children().size());
|
2011-07-09 15:54:01 -04:00
|
|
|
// setting a new parent for root groups is not allowed
|
|
|
|
Q_ASSERT(!m_db || (m_db->rootGroup() != this));
|
2010-08-18 04:27:40 -04:00
|
|
|
|
2012-04-23 10:57:08 -04:00
|
|
|
bool moveWithinDatabase = (m_db && m_db == parent->m_db);
|
|
|
|
|
2010-08-18 04:27:40 -04:00
|
|
|
if (index == -1) {
|
|
|
|
index = parent->children().size();
|
2012-04-24 18:10:06 -04:00
|
|
|
|
|
|
|
if (parentGroup() == parent) {
|
|
|
|
index--;
|
|
|
|
}
|
2010-08-18 04:27:40 -04:00
|
|
|
}
|
|
|
|
|
2012-04-23 10:57:08 -04:00
|
|
|
if (m_parent == parent && parent->children().indexOf(this) == index) {
|
2012-04-22 06:09:12 -04:00
|
|
|
return;
|
|
|
|
}
|
2012-04-11 12:00:28 -04:00
|
|
|
|
2012-04-23 10:57:08 -04:00
|
|
|
if (!moveWithinDatabase) {
|
|
|
|
cleanupParent();
|
|
|
|
m_parent = parent;
|
2012-04-27 04:50:32 -04:00
|
|
|
if (m_db) {
|
2012-04-23 10:57:08 -04:00
|
|
|
recCreateDelObjects();
|
2012-04-27 05:22:02 -04:00
|
|
|
|
|
|
|
// copy custom icon to the new database
|
2018-03-31 16:01:30 -04:00
|
|
|
if (!iconUuid().isNull() && parent->m_db && m_db->metadata()->containsCustomIcon(iconUuid())
|
|
|
|
&& !parent->m_db->metadata()->containsCustomIcon(iconUuid())) {
|
2012-04-27 05:22:02 -04:00
|
|
|
parent->m_db->metadata()->addCustomIcon(iconUuid(), icon());
|
|
|
|
}
|
2012-04-27 04:50:32 -04:00
|
|
|
}
|
|
|
|
if (m_db != parent->m_db) {
|
2012-04-23 10:57:08 -04:00
|
|
|
recSetDatabase(parent->m_db);
|
|
|
|
}
|
|
|
|
QObject::setParent(parent);
|
2017-03-10 09:58:42 -05:00
|
|
|
emit aboutToAdd(this, index);
|
2012-04-24 18:10:06 -04:00
|
|
|
Q_ASSERT(index <= parent->m_children.size());
|
2012-04-23 10:57:08 -04:00
|
|
|
parent->m_children.insert(index, this);
|
2018-03-31 16:01:30 -04:00
|
|
|
} else {
|
2017-03-10 09:58:42 -05:00
|
|
|
emit aboutToMove(this, parent, index);
|
2012-04-23 10:57:08 -04:00
|
|
|
m_parent->m_children.removeAll(this);
|
|
|
|
m_parent = parent;
|
|
|
|
QObject::setParent(parent);
|
2012-04-24 18:10:06 -04:00
|
|
|
Q_ASSERT(index <= parent->m_children.size());
|
2012-04-23 10:57:08 -04:00
|
|
|
parent->m_children.insert(index, this);
|
2010-08-14 06:24:35 -04:00
|
|
|
}
|
2010-08-18 04:27:40 -04:00
|
|
|
|
2012-04-22 06:09:12 -04:00
|
|
|
if (m_updateTimeinfo) {
|
2018-09-30 08:45:06 -04:00
|
|
|
m_data.timeInfo.setLocationChanged(Clock::currentDateTimeUtc());
|
2012-04-22 06:09:12 -04:00
|
|
|
}
|
|
|
|
|
2017-03-10 09:58:42 -05:00
|
|
|
emit modified();
|
2012-04-23 10:57:08 -04:00
|
|
|
|
|
|
|
if (!moveWithinDatabase) {
|
2017-03-10 09:58:42 -05:00
|
|
|
emit added();
|
2018-03-31 16:01:30 -04:00
|
|
|
} else {
|
2017-03-10 09:58:42 -05:00
|
|
|
emit moved();
|
2012-04-23 10:57:08 -04:00
|
|
|
}
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Group::setParent(Database* db)
|
|
|
|
{
|
2010-08-18 04:27:40 -04:00
|
|
|
Q_ASSERT(db);
|
2012-04-27 04:50:32 -04:00
|
|
|
Q_ASSERT(db->rootGroup() == this);
|
2010-08-18 04:27:40 -04:00
|
|
|
|
2011-07-09 15:54:01 -04:00
|
|
|
cleanupParent();
|
2010-08-18 04:27:40 -04:00
|
|
|
|
2015-07-24 12:28:12 -04:00
|
|
|
m_parent = nullptr;
|
2010-08-14 06:24:35 -04:00
|
|
|
recSetDatabase(db);
|
|
|
|
|
2010-08-12 15:38:59 -04:00
|
|
|
QObject::setParent(db);
|
|
|
|
}
|
|
|
|
|
2017-12-25 08:53:22 -05:00
|
|
|
QStringList Group::hierarchy() const
|
2017-08-17 15:02:21 -04:00
|
|
|
{
|
|
|
|
QStringList hierarchy;
|
2017-12-25 08:53:22 -05:00
|
|
|
const Group* group = this;
|
|
|
|
const Group* parent = m_parent;
|
2017-09-27 13:18:13 -04:00
|
|
|
hierarchy.prepend(group->name());
|
2018-03-31 16:01:30 -04:00
|
|
|
|
2017-08-30 18:52:05 -04:00
|
|
|
while (parent) {
|
2017-08-17 15:02:21 -04:00
|
|
|
group = group->parentGroup();
|
2017-08-30 18:52:05 -04:00
|
|
|
parent = group->parentGroup();
|
|
|
|
|
2017-09-27 13:18:13 -04:00
|
|
|
hierarchy.prepend(group->name());
|
2017-08-17 15:02:21 -04:00
|
|
|
}
|
2017-09-27 13:18:13 -04:00
|
|
|
|
2017-08-17 15:02:21 -04:00
|
|
|
return hierarchy;
|
|
|
|
}
|
|
|
|
|
2012-04-21 18:29:39 -04:00
|
|
|
Database* Group::database()
|
|
|
|
{
|
|
|
|
return m_db;
|
|
|
|
}
|
|
|
|
|
2010-08-14 06:24:35 -04:00
|
|
|
const Database* Group::database() const
|
|
|
|
{
|
|
|
|
return m_db;
|
|
|
|
}
|
|
|
|
|
2010-08-15 09:03:47 -04:00
|
|
|
QList<Group*> Group::children()
|
|
|
|
{
|
|
|
|
return m_children;
|
|
|
|
}
|
|
|
|
|
2010-08-25 08:00:46 -04:00
|
|
|
const QList<Group*>& Group::children() const
|
2010-08-15 06:31:48 -04:00
|
|
|
{
|
2010-08-25 08:00:46 -04:00
|
|
|
return m_children;
|
2010-08-15 06:31:48 -04:00
|
|
|
}
|
|
|
|
|
2010-08-15 09:03:47 -04:00
|
|
|
QList<Entry*> Group::entries()
|
2010-08-12 15:38:59 -04:00
|
|
|
{
|
2010-08-15 09:03:47 -04:00
|
|
|
return m_entries;
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
2010-08-25 08:00:46 -04:00
|
|
|
const QList<Entry*>& Group::entries() const
|
2010-08-15 06:31:48 -04:00
|
|
|
{
|
2010-08-25 08:00:46 -04:00
|
|
|
return m_entries;
|
2010-08-15 06:31:48 -04:00
|
|
|
}
|
|
|
|
|
2012-05-11 18:01:58 -04:00
|
|
|
QList<Entry*> Group::entriesRecursive(bool includeHistoryItems) const
|
2012-04-21 10:45:46 -04:00
|
|
|
{
|
|
|
|
QList<Entry*> entryList;
|
|
|
|
|
|
|
|
entryList.append(m_entries);
|
|
|
|
|
|
|
|
if (includeHistoryItems) {
|
2016-09-02 13:51:51 -04:00
|
|
|
for (Entry* entry : m_entries) {
|
2012-04-21 10:45:46 -04:00
|
|
|
entryList.append(entry->historyItems());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-02 13:51:51 -04:00
|
|
|
for (Group* group : m_children) {
|
2012-04-21 10:45:46 -04:00
|
|
|
entryList.append(group->entriesRecursive(includeHistoryItems));
|
|
|
|
}
|
|
|
|
|
|
|
|
return entryList;
|
|
|
|
}
|
|
|
|
|
2018-10-30 08:42:35 -04:00
|
|
|
Entry* Group::findEntryByUuid(const QUuid& uuid) const
|
2017-05-19 14:04:11 -04:00
|
|
|
{
|
2018-10-30 08:42:35 -04:00
|
|
|
if (uuid.isNull()) {
|
|
|
|
return nullptr;
|
2017-05-21 13:51:16 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
for (Entry* entry : entriesRecursive(false)) {
|
2018-10-30 08:42:35 -04:00
|
|
|
if (entry->uuid() == uuid) {
|
2017-05-21 13:51:16 -04:00
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
2017-05-19 14:04:11 -04:00
|
|
|
}
|
|
|
|
|
2018-10-28 07:49:32 -04:00
|
|
|
Entry* Group::findEntryByPath(const QString& entryPath)
|
2016-11-07 22:37:42 -05:00
|
|
|
{
|
2018-10-30 08:42:35 -04:00
|
|
|
if (entryPath.isEmpty()) {
|
|
|
|
return nullptr;
|
2016-11-07 22:37:42 -05:00
|
|
|
}
|
|
|
|
|
2018-10-30 08:42:35 -04:00
|
|
|
// Add a beginning slash if the search string contains a slash
|
|
|
|
// We don't add a slash by default to allow searching by entry title
|
|
|
|
QString normalizedEntryPath = entryPath;
|
|
|
|
if (!normalizedEntryPath.startsWith("/") && normalizedEntryPath.contains("/")) {
|
|
|
|
normalizedEntryPath = "/" + normalizedEntryPath;
|
|
|
|
}
|
|
|
|
return findEntryByPathRecursive(normalizedEntryPath, "/");
|
2016-11-07 22:37:42 -05:00
|
|
|
}
|
|
|
|
|
2018-10-28 07:49:32 -04:00
|
|
|
Entry* Group::findEntryByPathRecursive(const QString& entryPath, const QString& basePath)
|
2017-05-19 14:04:11 -04:00
|
|
|
{
|
2018-10-30 08:42:35 -04:00
|
|
|
// Return the first entry that matches the full path OR if there is no leading
|
|
|
|
// slash, return the first entry title that matches
|
|
|
|
for (Entry* entry : entries()) {
|
|
|
|
if (entryPath == (basePath + entry->title())
|
|
|
|
|| (!entryPath.startsWith("/") && entry->title() == entryPath)) {
|
2017-05-19 14:04:11 -04:00
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-30 08:42:35 -04:00
|
|
|
for (Group* group : children()) {
|
|
|
|
Entry* entry = group->findEntryByPathRecursive(entryPath, basePath + group->name() + "/");
|
2017-05-19 14:04:11 -04:00
|
|
|
if (entry != nullptr) {
|
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2018-10-28 07:49:32 -04:00
|
|
|
Group* Group::findGroupByPath(const QString& groupPath)
|
2017-06-21 17:34:49 -04:00
|
|
|
{
|
2018-09-18 22:44:39 -04:00
|
|
|
// normalize the groupPath by adding missing front and rear slashes. once.
|
|
|
|
QString normalizedGroupPath;
|
|
|
|
|
2018-10-30 08:42:35 -04:00
|
|
|
if (groupPath.isEmpty()) {
|
2018-09-18 22:44:39 -04:00
|
|
|
normalizedGroupPath = QString("/"); // root group
|
|
|
|
} else {
|
2018-10-30 08:42:35 -04:00
|
|
|
normalizedGroupPath = (groupPath.startsWith("/") ? "" : "/")
|
2018-09-18 22:44:39 -04:00
|
|
|
+ groupPath
|
2018-10-30 08:42:35 -04:00
|
|
|
+ (groupPath.endsWith("/") ? "" : "/");
|
2017-06-21 17:34:49 -04:00
|
|
|
}
|
2018-10-30 08:42:35 -04:00
|
|
|
return findGroupByPathRecursive(normalizedGroupPath, "/");
|
2018-09-18 22:44:39 -04:00
|
|
|
}
|
|
|
|
|
2018-10-28 07:49:32 -04:00
|
|
|
Group* Group::findGroupByPathRecursive(const QString& groupPath, const QString& basePath)
|
2018-09-18 22:44:39 -04:00
|
|
|
{
|
|
|
|
// paths must be normalized
|
|
|
|
Q_ASSERT(groupPath.startsWith("/") && groupPath.endsWith("/"));
|
|
|
|
Q_ASSERT(basePath.startsWith("/") && basePath.endsWith("/"));
|
2017-06-21 17:34:49 -04:00
|
|
|
|
2018-09-18 22:44:39 -04:00
|
|
|
if (groupPath == basePath) {
|
2017-06-21 17:34:49 -04:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (Group* innerGroup : children()) {
|
|
|
|
QString innerBasePath = basePath + innerGroup->name() + "/";
|
2018-10-30 08:42:35 -04:00
|
|
|
Group* group = innerGroup->findGroupByPathRecursive(groupPath, innerBasePath);
|
2017-06-21 17:34:49 -04:00
|
|
|
if (group != nullptr) {
|
|
|
|
return group;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2017-07-17 15:16:53 -04:00
|
|
|
QString Group::print(bool recursive, int depth)
|
2017-05-21 13:05:44 -04:00
|
|
|
{
|
|
|
|
|
|
|
|
QString response;
|
|
|
|
QString indentation = QString(" ").repeated(depth);
|
|
|
|
|
|
|
|
if (entries().isEmpty() && children().isEmpty()) {
|
2018-03-13 16:02:38 -04:00
|
|
|
response += indentation + tr("[empty]", "group has no children") + "\n";
|
2017-05-21 13:05:44 -04:00
|
|
|
return response;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (Entry* entry : entries()) {
|
2017-07-17 15:16:53 -04:00
|
|
|
response += indentation + entry->title() + "\n";
|
2017-05-21 13:05:44 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
for (Group* innerGroup : children()) {
|
2017-07-17 15:16:53 -04:00
|
|
|
response += indentation + innerGroup->name() + "/\n";
|
|
|
|
if (recursive) {
|
|
|
|
response += innerGroup->print(recursive, depth + 1);
|
2017-05-21 13:05:44 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return response;
|
|
|
|
}
|
|
|
|
|
2012-05-11 19:51:41 -04:00
|
|
|
QList<const Group*> Group::groupsRecursive(bool includeSelf) const
|
|
|
|
{
|
|
|
|
QList<const Group*> groupList;
|
2015-01-11 10:20:24 -05:00
|
|
|
if (includeSelf) {
|
|
|
|
groupList.append(this);
|
|
|
|
}
|
|
|
|
|
2018-10-30 08:42:35 -04:00
|
|
|
for (const Group* group : asConst(m_children)) {
|
2015-01-11 10:20:24 -05:00
|
|
|
groupList.append(group->groupsRecursive(true));
|
|
|
|
}
|
|
|
|
|
|
|
|
return groupList;
|
|
|
|
}
|
|
|
|
|
|
|
|
QList<Group*> Group::groupsRecursive(bool includeSelf)
|
|
|
|
{
|
|
|
|
QList<Group*> groupList;
|
2012-05-11 19:51:41 -04:00
|
|
|
if (includeSelf) {
|
|
|
|
groupList.append(this);
|
|
|
|
}
|
|
|
|
|
2016-09-02 13:51:51 -04:00
|
|
|
for (Group* group : asConst(m_children)) {
|
2012-05-11 19:51:41 -04:00
|
|
|
groupList.append(group->groupsRecursive(true));
|
|
|
|
}
|
|
|
|
|
|
|
|
return groupList;
|
|
|
|
}
|
|
|
|
|
2018-03-22 17:56:05 -04:00
|
|
|
QSet<QUuid> Group::customIconsRecursive() const
|
2013-04-07 12:32:43 -04:00
|
|
|
{
|
2018-03-22 17:56:05 -04:00
|
|
|
QSet<QUuid> result;
|
2013-04-07 12:32:43 -04:00
|
|
|
|
|
|
|
if (!iconUuid().isNull()) {
|
|
|
|
result.insert(iconUuid());
|
|
|
|
}
|
|
|
|
|
2016-09-02 13:51:51 -04:00
|
|
|
const QList<Entry*> entryList = entriesRecursive(true);
|
|
|
|
for (Entry* entry : entryList) {
|
2013-04-07 12:32:43 -04:00
|
|
|
if (!entry->iconUuid().isNull()) {
|
|
|
|
result.insert(entry->iconUuid());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-02 13:51:51 -04:00
|
|
|
for (Group* group : m_children) {
|
2013-04-07 12:32:43 -04:00
|
|
|
result.unite(group->customIconsRecursive());
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2018-09-30 08:45:06 -04:00
|
|
|
Group* Group::findGroupByUuid(const QUuid& uuid)
|
2017-11-03 16:29:42 -04:00
|
|
|
{
|
2018-10-30 08:42:35 -04:00
|
|
|
if (uuid.isNull()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2017-11-03 16:29:42 -04:00
|
|
|
for (Group* group : groupsRecursive(true)) {
|
|
|
|
if (group->uuid() == uuid) {
|
|
|
|
return group;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2016-11-07 22:37:42 -05:00
|
|
|
Group* Group::findChildByName(const QString& name)
|
|
|
|
{
|
|
|
|
for (Group* group : asConst(m_children)) {
|
|
|
|
if (group->name() == name) {
|
|
|
|
return group;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2018-10-30 08:42:35 -04:00
|
|
|
/**
|
|
|
|
* Creates a duplicate of this group.
|
|
|
|
* Note that you need to copy the custom icons manually when inserting the
|
|
|
|
* new group into another database.
|
|
|
|
*/
|
2017-11-03 16:29:42 -04:00
|
|
|
Group* Group::clone(Entry::CloneFlags entryFlags, Group::CloneFlags groupFlags) const
|
2013-04-04 15:48:55 -04:00
|
|
|
{
|
|
|
|
Group* clonedGroup = new Group();
|
|
|
|
|
|
|
|
clonedGroup->setUpdateTimeinfo(false);
|
|
|
|
|
2017-11-03 16:29:42 -04:00
|
|
|
if (groupFlags & Group::CloneNewUuid) {
|
2018-03-22 17:56:05 -04:00
|
|
|
clonedGroup->setUuid(QUuid::createUuid());
|
2017-11-03 16:29:42 -04:00
|
|
|
} else {
|
|
|
|
clonedGroup->setUuid(this->uuid());
|
|
|
|
}
|
|
|
|
|
2013-04-04 15:48:55 -04:00
|
|
|
clonedGroup->m_data = m_data;
|
2018-02-06 10:37:06 -05:00
|
|
|
clonedGroup->m_customData->copyDataFrom(m_customData);
|
2013-04-04 15:48:55 -04:00
|
|
|
|
2017-11-03 16:29:42 -04:00
|
|
|
if (groupFlags & Group::CloneIncludeEntries) {
|
2017-09-05 10:28:47 -04:00
|
|
|
const QList<Entry*> entryList = entries();
|
|
|
|
for (Entry* entry : entryList) {
|
|
|
|
Entry* clonedEntry = entry->clone(entryFlags);
|
|
|
|
clonedEntry->setGroup(clonedGroup);
|
|
|
|
}
|
2013-04-04 15:48:55 -04:00
|
|
|
|
2017-09-05 10:28:47 -04:00
|
|
|
const QList<Group*> childrenGroups = children();
|
|
|
|
for (Group* groupChild : childrenGroups) {
|
2017-11-03 16:29:42 -04:00
|
|
|
Group* clonedGroupChild = groupChild->clone(entryFlags, groupFlags);
|
2017-09-05 10:28:47 -04:00
|
|
|
clonedGroupChild->setParent(clonedGroup);
|
|
|
|
}
|
2013-04-04 15:48:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
clonedGroup->setUpdateTimeinfo(true);
|
2017-11-03 16:29:42 -04:00
|
|
|
if (groupFlags & Group::CloneResetTimeInfo) {
|
2013-04-04 15:48:55 -04:00
|
|
|
|
2018-09-30 08:45:06 -04:00
|
|
|
QDateTime now = Clock::currentDateTimeUtc();
|
2017-11-03 16:29:42 -04:00
|
|
|
clonedGroup->m_data.timeInfo.setCreationTime(now);
|
|
|
|
clonedGroup->m_data.timeInfo.setLastModificationTime(now);
|
|
|
|
clonedGroup->m_data.timeInfo.setLastAccessTime(now);
|
|
|
|
clonedGroup->m_data.timeInfo.setLocationChanged(now);
|
|
|
|
}
|
2013-04-04 15:48:55 -04:00
|
|
|
|
|
|
|
return clonedGroup;
|
|
|
|
}
|
|
|
|
|
2013-04-14 08:54:56 -04:00
|
|
|
void Group::copyDataFrom(const Group* other)
|
|
|
|
{
|
|
|
|
m_data = other->m_data;
|
2018-02-06 10:37:06 -05:00
|
|
|
m_customData->copyDataFrom(other->m_customData);
|
2013-04-14 08:54:56 -04:00
|
|
|
m_lastTopVisibleEntry = other->m_lastTopVisibleEntry;
|
|
|
|
}
|
|
|
|
|
2012-05-02 11:04:03 -04:00
|
|
|
void Group::addEntry(Entry* entry)
|
2010-08-12 15:38:59 -04:00
|
|
|
{
|
2010-08-18 10:22:48 -04:00
|
|
|
Q_ASSERT(entry);
|
2012-04-27 04:50:32 -04:00
|
|
|
Q_ASSERT(!m_entries.contains(entry));
|
2010-08-18 10:22:48 -04:00
|
|
|
|
2017-03-10 09:58:42 -05:00
|
|
|
emit entryAboutToAdd(entry);
|
2010-08-14 06:24:35 -04:00
|
|
|
|
2010-08-12 15:38:59 -04:00
|
|
|
m_entries << entry;
|
2010-08-23 15:30:20 -04:00
|
|
|
connect(entry, SIGNAL(dataChanged(Entry*)), SIGNAL(entryDataChanged(Entry*)));
|
2012-04-18 09:16:38 -04:00
|
|
|
if (m_db) {
|
2012-06-24 11:53:01 -04:00
|
|
|
connect(entry, SIGNAL(modified()), m_db, SIGNAL(modifiedImmediate()));
|
2012-04-18 09:16:38 -04:00
|
|
|
}
|
2010-08-18 10:22:48 -04:00
|
|
|
|
2017-03-10 09:58:42 -05:00
|
|
|
emit modified();
|
|
|
|
emit entryAdded(entry);
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Group::removeEntry(Entry* entry)
|
|
|
|
{
|
2018-09-30 08:45:06 -04:00
|
|
|
Q_ASSERT_X(m_entries.contains(entry),
|
|
|
|
Q_FUNC_INFO,
|
2018-10-28 07:23:06 -04:00
|
|
|
QString("Group %1 does not contain %2").arg(this->name(), entry->title()).toLatin1());
|
2012-04-27 04:50:32 -04:00
|
|
|
|
2017-03-10 09:58:42 -05:00
|
|
|
emit entryAboutToRemove(entry);
|
2010-08-18 10:22:48 -04:00
|
|
|
|
|
|
|
entry->disconnect(this);
|
2012-04-18 11:07:40 -04:00
|
|
|
if (m_db) {
|
|
|
|
entry->disconnect(m_db);
|
|
|
|
}
|
2010-08-12 15:38:59 -04:00
|
|
|
m_entries.removeAll(entry);
|
2017-03-10 09:58:42 -05:00
|
|
|
emit modified();
|
|
|
|
emit entryRemoved(entry);
|
2010-08-12 15:38:59 -04:00
|
|
|
}
|
2010-08-14 06:24:35 -04:00
|
|
|
|
|
|
|
void Group::recSetDatabase(Database* db)
|
|
|
|
{
|
2012-04-18 11:07:40 -04:00
|
|
|
if (m_db) {
|
|
|
|
disconnect(SIGNAL(dataChanged(Group*)), m_db);
|
|
|
|
disconnect(SIGNAL(aboutToRemove(Group*)), m_db);
|
|
|
|
disconnect(SIGNAL(removed()), m_db);
|
2018-03-31 16:01:30 -04:00
|
|
|
disconnect(SIGNAL(aboutToAdd(Group*, int)), m_db);
|
2012-04-18 11:07:40 -04:00
|
|
|
disconnect(SIGNAL(added()), m_db);
|
2018-03-31 16:01:30 -04:00
|
|
|
disconnect(SIGNAL(aboutToMove(Group*, Group*, int)), m_db);
|
2012-04-23 10:57:08 -04:00
|
|
|
disconnect(SIGNAL(moved()), m_db);
|
2012-04-18 11:07:40 -04:00
|
|
|
disconnect(SIGNAL(modified()), m_db);
|
|
|
|
}
|
2010-08-18 04:27:40 -04:00
|
|
|
|
2016-09-02 13:51:51 -04:00
|
|
|
for (Entry* entry : asConst(m_entries)) {
|
2012-04-18 11:07:40 -04:00
|
|
|
if (m_db) {
|
|
|
|
entry->disconnect(m_db);
|
|
|
|
}
|
2012-04-27 04:50:32 -04:00
|
|
|
if (db) {
|
2012-06-24 11:53:01 -04:00
|
|
|
connect(entry, SIGNAL(modified()), db, SIGNAL(modifiedImmediate()));
|
2012-04-27 04:50:32 -04:00
|
|
|
}
|
2012-04-18 09:16:38 -04:00
|
|
|
}
|
|
|
|
|
2012-04-27 04:50:32 -04:00
|
|
|
if (db) {
|
|
|
|
connect(this, SIGNAL(dataChanged(Group*)), db, SIGNAL(groupDataChanged(Group*)));
|
|
|
|
connect(this, SIGNAL(aboutToRemove(Group*)), db, SIGNAL(groupAboutToRemove(Group*)));
|
|
|
|
connect(this, SIGNAL(removed()), db, SIGNAL(groupRemoved()));
|
2018-10-28 18:06:27 -04:00
|
|
|
connect(this, SIGNAL(aboutToAdd(Group*,int)), db, SIGNAL(groupAboutToAdd(Group*,int)));
|
2012-04-27 04:50:32 -04:00
|
|
|
connect(this, SIGNAL(added()), db, SIGNAL(groupAdded()));
|
2018-10-28 18:06:27 -04:00
|
|
|
connect(this, SIGNAL(aboutToMove(Group*,Group*,int)), db, SIGNAL(groupAboutToMove(Group*,Group*,int)));
|
2012-04-27 04:50:32 -04:00
|
|
|
connect(this, SIGNAL(moved()), db, SIGNAL(groupMoved()));
|
2012-06-24 11:53:01 -04:00
|
|
|
connect(this, SIGNAL(modified()), db, SIGNAL(modifiedImmediate()));
|
2012-04-27 04:50:32 -04:00
|
|
|
}
|
2010-08-15 09:03:47 -04:00
|
|
|
|
2010-08-14 06:24:35 -04:00
|
|
|
m_db = db;
|
|
|
|
|
2016-09-02 13:51:51 -04:00
|
|
|
for (Group* group : asConst(m_children)) {
|
2010-08-14 06:24:35 -04:00
|
|
|
group->recSetDatabase(db);
|
|
|
|
}
|
|
|
|
}
|
2011-07-09 15:54:01 -04:00
|
|
|
|
|
|
|
void Group::cleanupParent()
|
|
|
|
{
|
|
|
|
if (m_parent) {
|
2017-03-10 09:58:42 -05:00
|
|
|
emit aboutToRemove(this);
|
2011-07-09 15:54:01 -04:00
|
|
|
m_parent->m_children.removeAll(this);
|
2017-03-10 09:58:42 -05:00
|
|
|
emit modified();
|
|
|
|
emit removed();
|
2012-04-21 13:06:28 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Group::recCreateDelObjects()
|
|
|
|
{
|
|
|
|
if (m_db) {
|
2016-09-02 13:51:51 -04:00
|
|
|
for (Entry* entry : asConst(m_entries)) {
|
2012-04-21 18:29:39 -04:00
|
|
|
m_db->addDeletedObject(entry->uuid());
|
2012-04-21 13:06:28 -04:00
|
|
|
}
|
|
|
|
|
2016-09-02 13:51:51 -04:00
|
|
|
for (Group* group : asConst(m_children)) {
|
2012-04-21 13:06:28 -04:00
|
|
|
group->recCreateDelObjects();
|
|
|
|
}
|
2012-04-22 03:52:12 -04:00
|
|
|
m_db->addDeletedObject(m_uuid);
|
2012-04-21 13:06:28 -04:00
|
|
|
}
|
|
|
|
}
|
2012-05-12 07:22:41 -04:00
|
|
|
|
2014-04-26 12:27:52 -04:00
|
|
|
bool Group::resolveSearchingEnabled() const
|
2012-05-12 07:22:41 -04:00
|
|
|
{
|
2013-04-04 15:48:55 -04:00
|
|
|
switch (m_data.searchingEnabled) {
|
2012-05-12 07:22:41 -04:00
|
|
|
case Inherit:
|
|
|
|
if (!m_parent) {
|
|
|
|
return true;
|
2018-03-31 16:01:30 -04:00
|
|
|
} else {
|
2014-04-26 12:27:52 -04:00
|
|
|
return m_parent->resolveSearchingEnabled();
|
2012-05-12 07:22:41 -04:00
|
|
|
}
|
|
|
|
case Enable:
|
|
|
|
return true;
|
|
|
|
case Disable:
|
|
|
|
return false;
|
|
|
|
default:
|
|
|
|
Q_ASSERT(false);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2014-04-26 12:30:22 -04:00
|
|
|
|
|
|
|
bool Group::resolveAutoTypeEnabled() const
|
|
|
|
{
|
|
|
|
switch (m_data.autoTypeEnabled) {
|
|
|
|
case Inherit:
|
|
|
|
if (!m_parent) {
|
|
|
|
return true;
|
2018-03-31 16:01:30 -04:00
|
|
|
} else {
|
2014-04-26 12:30:22 -04:00
|
|
|
return m_parent->resolveAutoTypeEnabled();
|
|
|
|
}
|
|
|
|
case Enable:
|
|
|
|
return true;
|
|
|
|
case Disable:
|
|
|
|
return false;
|
|
|
|
default:
|
|
|
|
Q_ASSERT(false);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2016-11-07 22:37:42 -05:00
|
|
|
|
2018-10-28 07:49:32 -04:00
|
|
|
QStringList Group::locate(const QString& locateTerm, const QString& currentPath) const
|
2017-08-05 12:20:26 -04:00
|
|
|
{
|
2018-10-30 08:42:35 -04:00
|
|
|
// TODO: Replace with EntrySearcher
|
2017-08-05 12:20:26 -04:00
|
|
|
QStringList response;
|
2018-10-30 08:42:35 -04:00
|
|
|
if (locateTerm.isEmpty()) {
|
|
|
|
return response;
|
|
|
|
}
|
2017-08-05 12:20:26 -04:00
|
|
|
|
2018-10-27 17:23:34 -04:00
|
|
|
for (const Entry* entry : asConst(m_entries)) {
|
2017-08-05 12:20:26 -04:00
|
|
|
QString entryPath = currentPath + entry->title();
|
|
|
|
if (entryPath.toLower().contains(locateTerm.toLower())) {
|
|
|
|
response << entryPath;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-27 17:23:34 -04:00
|
|
|
for (const Group* group : asConst(m_children)) {
|
|
|
|
for (const QString& path : group->locate(locateTerm, currentPath + group->name() + QString("/"))) {
|
2017-08-05 12:20:26 -04:00
|
|
|
response << path;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return response;
|
|
|
|
}
|
2017-09-06 09:14:41 -04:00
|
|
|
|
2018-10-28 07:49:32 -04:00
|
|
|
Entry* Group::addEntryWithPath(const QString& entryPath)
|
2017-09-06 09:14:41 -04:00
|
|
|
{
|
2018-10-30 08:42:35 -04:00
|
|
|
if (entryPath.isEmpty() || findEntryByPath(entryPath)) {
|
2017-09-06 09:14:41 -04:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
QStringList groups = entryPath.split("/");
|
|
|
|
QString entryTitle = groups.takeLast();
|
|
|
|
QString groupPath = groups.join("/");
|
|
|
|
|
2018-10-30 08:42:35 -04:00
|
|
|
Group* group = findGroupByPath(groupPath);
|
2017-09-06 09:14:41 -04:00
|
|
|
if (!group) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
Entry* entry = new Entry();
|
|
|
|
entry->setTitle(entryTitle);
|
2018-03-22 17:56:05 -04:00
|
|
|
entry->setUuid(QUuid::createUuid());
|
2017-09-06 09:14:41 -04:00
|
|
|
entry->setGroup(group);
|
|
|
|
|
|
|
|
return entry;
|
|
|
|
}
|
2018-09-30 08:45:06 -04:00
|
|
|
|
|
|
|
bool Group::GroupData::operator==(const Group::GroupData& other) const
|
|
|
|
{
|
|
|
|
return equals(other, CompareItemDefault);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Group::GroupData::operator!=(const Group::GroupData& other) const
|
|
|
|
{
|
|
|
|
return !(*this == other);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Group::GroupData::equals(const Group::GroupData& other, CompareItemOptions options) const
|
|
|
|
{
|
|
|
|
if (::compare(name, other.name, options) != 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (::compare(notes, other.notes, options) != 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (::compare(iconNumber, other.iconNumber) != 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (::compare(customIcon, other.customIcon) != 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (timeInfo.equals(other.timeInfo, options) != 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// TODO HNH: Some properties are configurable - should they be ignored?
|
|
|
|
if (::compare(isExpanded, other.isExpanded, options) != 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (::compare(defaultAutoTypeSequence, other.defaultAutoTypeSequence, options) != 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (::compare(autoTypeEnabled, other.autoTypeEnabled, options) != 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (::compare(searchingEnabled, other.searchingEnabled, options) != 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (::compare(mergeMode, other.mergeMode, options) != 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|