1 /*
2 * Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 or (at your option)
7 * version 3 of the License.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include "Group.h"
19
20 #include "core/Config.h"
21 #include "core/Global.h"
22 #include "core/DatabaseIcons.h"
23 #include "core/Metadata.h"
24
25 const int Group::DefaultIconNumber = 48;
26 const int Group::RecycleBinIconNumber = 43;
27
Group()28 Group::Group()
29 : m_updateTimeinfo(true)
30 {
31 m_data.iconNumber = DefaultIconNumber;
32 m_data.isExpanded = true;
33 m_data.autoTypeEnabled = Inherit;
34 m_data.searchingEnabled = Inherit;
35 }
36
~Group()37 Group::~Group()
38 {
39 // Destroy entries and children manually so DeletedObjects can be added
40 // to database.
41 const QList<Entry*> entries = m_entries;
42 for (Entry* entry : entries) {
43 delete entry;
44 }
45
46 const QList<Group*> children = m_children;
47 for (Group* group : children) {
48 delete group;
49 }
50
51 if (m_db && m_parent) {
52 DeletedObject delGroup;
53 delGroup.deletionTime = QDateTime::currentDateTimeUtc();
54 delGroup.uuid = m_uuid;
55 m_db->addDeletedObject(delGroup);
56 }
57
58 cleanupParent();
59 }
60
createRecycleBin()61 Group* Group::createRecycleBin()
62 {
63 Group* recycleBin = new Group();
64 recycleBin->setUuid(Uuid::random());
65 recycleBin->setName(tr("Recycle Bin"));
66 recycleBin->setIcon(RecycleBinIconNumber);
67 recycleBin->setSearchingEnabled(Group::Disable);
68 recycleBin->setAutoTypeEnabled(Group::Disable);
69 return recycleBin;
70 }
71
set(P & property,const V & value)72 template <class P, class V> inline bool Group::set(P& property, const V& value) {
73 if (property != value) {
74 property = value;
75 updateTimeinfo();
76 Q_EMIT modified();
77 return true;
78 }
79 else {
80 return false;
81 }
82 }
83
updateTimeinfo()84 void Group::updateTimeinfo()
85 {
86 if (m_updateTimeinfo) {
87 m_data.timeInfo.setLastModificationTime(QDateTime::currentDateTimeUtc());
88 m_data.timeInfo.setLastAccessTime(QDateTime::currentDateTimeUtc());
89 }
90 }
91
setUpdateTimeinfo(bool value)92 void Group::setUpdateTimeinfo(bool value)
93 {
94 m_updateTimeinfo = value;
95 }
96
uuid() const97 Uuid Group::uuid() const
98 {
99 return m_uuid;
100 }
101
name() const102 QString Group::name() const
103 {
104 return m_data.name;
105 }
106
notes() const107 QString Group::notes() const
108 {
109 return m_data.notes;
110 }
111
icon() const112 QImage Group::icon() const
113 {
114 if (m_data.customIcon.isNull()) {
115 return databaseIcons()->icon(m_data.iconNumber);
116 }
117 else {
118 Q_ASSERT(m_db);
119
120 if (m_db) {
121 return m_db->metadata()->customIcon(m_data.customIcon);
122 }
123 else {
124 return QImage();
125 }
126 }
127 }
128
iconPixmap() const129 QPixmap Group::iconPixmap() const
130 {
131 if (m_data.customIcon.isNull()) {
132 return databaseIcons()->iconPixmap(m_data.iconNumber);
133 }
134 else {
135 Q_ASSERT(m_db);
136
137 if (m_db) {
138 return m_db->metadata()->customIconPixmap(m_data.customIcon);
139 }
140 else {
141 return QPixmap();
142 }
143 }
144 }
145
iconScaledPixmap() const146 QPixmap Group::iconScaledPixmap() const
147 {
148 if (m_data.customIcon.isNull()) {
149 // built-in icons are 16x16 so don't need to be scaled
150 return databaseIcons()->iconPixmap(m_data.iconNumber);
151 }
152 else {
153 Q_ASSERT(m_db);
154
155 if (m_db) {
156 return m_db->metadata()->customIconScaledPixmap(m_data.customIcon);
157 }
158 else {
159 return QPixmap();
160 }
161 }
162 }
163
iconNumber() const164 int Group::iconNumber() const
165 {
166 return m_data.iconNumber;
167 }
168
iconUuid() const169 Uuid Group::iconUuid() const
170 {
171 return m_data.customIcon;
172 }
173
timeInfo() const174 TimeInfo Group::timeInfo() const
175 {
176 return m_data.timeInfo;
177 }
178
isExpanded() const179 bool Group::isExpanded() const
180 {
181 return m_data.isExpanded;
182 }
183
defaultAutoTypeSequence() const184 QString Group::defaultAutoTypeSequence() const
185 {
186 return m_data.defaultAutoTypeSequence;
187 }
188
autoTypeEnabled() const189 Group::TriState Group::autoTypeEnabled() const
190 {
191 return m_data.autoTypeEnabled;
192 }
193
searchingEnabled() const194 Group::TriState Group::searchingEnabled() const
195 {
196 return m_data.searchingEnabled;
197 }
198
lastTopVisibleEntry() const199 Entry* Group::lastTopVisibleEntry() const
200 {
201 return m_lastTopVisibleEntry;
202 }
203
isExpired() const204 bool Group::isExpired() const
205 {
206 return m_data.timeInfo.expires() && m_data.timeInfo.expiryTime() < QDateTime::currentDateTimeUtc();
207 }
208
setUuid(const Uuid & uuid)209 void Group::setUuid(const Uuid& uuid)
210 {
211 set(m_uuid, uuid);
212 }
213
setName(const QString & name)214 void Group::setName(const QString& name)
215 {
216 if (set(m_data.name, name)) {
217 Q_EMIT dataChanged(this);
218 }
219 }
220
setNotes(const QString & notes)221 void Group::setNotes(const QString& notes)
222 {
223 set(m_data.notes, notes);
224 }
225
setIcon(int iconNumber)226 void Group::setIcon(int iconNumber)
227 {
228 Q_ASSERT(iconNumber >= 0);
229
230 if (m_data.iconNumber != iconNumber || !m_data.customIcon.isNull()) {
231 m_data.iconNumber = iconNumber;
232 m_data.customIcon = Uuid();
233
234 updateTimeinfo();
235 Q_EMIT modified();
236 Q_EMIT dataChanged(this);
237 }
238 }
239
setIcon(const Uuid & uuid)240 void Group::setIcon(const Uuid& uuid)
241 {
242 Q_ASSERT(!uuid.isNull());
243
244 if (m_data.customIcon != uuid) {
245 m_data.customIcon = uuid;
246 m_data.iconNumber = 0;
247
248 updateTimeinfo();
249 Q_EMIT modified();
250 Q_EMIT dataChanged(this);
251 }
252 }
253
setTimeInfo(const TimeInfo & timeInfo)254 void Group::setTimeInfo(const TimeInfo& timeInfo)
255 {
256 m_data.timeInfo = timeInfo;
257 }
258
setExpanded(bool expanded)259 void Group::setExpanded(bool expanded)
260 {
261 if (m_data.isExpanded != expanded) {
262 m_data.isExpanded = expanded;
263 updateTimeinfo();
264 Q_EMIT modified();
265 }
266 }
267
setDefaultAutoTypeSequence(const QString & sequence)268 void Group::setDefaultAutoTypeSequence(const QString& sequence)
269 {
270 set(m_data.defaultAutoTypeSequence, sequence);
271 }
272
setAutoTypeEnabled(TriState enable)273 void Group::setAutoTypeEnabled(TriState enable)
274 {
275 set(m_data.autoTypeEnabled, enable);
276 }
277
setSearchingEnabled(TriState enable)278 void Group::setSearchingEnabled(TriState enable)
279 {
280 set(m_data.searchingEnabled, enable);
281 }
282
setLastTopVisibleEntry(Entry * entry)283 void Group::setLastTopVisibleEntry(Entry* entry)
284 {
285 set(m_lastTopVisibleEntry, entry);
286 }
287
setExpires(bool value)288 void Group::setExpires(bool value)
289 {
290 if (m_data.timeInfo.expires() != value) {
291 m_data.timeInfo.setExpires(value);
292 updateTimeinfo();
293 Q_EMIT modified();
294 }
295 }
296
setExpiryTime(const QDateTime & dateTime)297 void Group::setExpiryTime(const QDateTime& dateTime)
298 {
299 if (m_data.timeInfo.expiryTime() != dateTime) {
300 m_data.timeInfo.setExpiryTime(dateTime);
301 updateTimeinfo();
302 Q_EMIT modified();
303 }
304 }
305
parentGroup()306 Group* Group::parentGroup()
307 {
308 return m_parent;
309 }
310
parentGroup() const311 const Group* Group::parentGroup() const
312 {
313 return m_parent;
314 }
315
setParent(Group * parent,int index)316 void Group::setParent(Group* parent, int index)
317 {
318 Q_ASSERT(parent);
319 Q_ASSERT(index >= -1 && index <= parent->children().size());
320 // setting a new parent for root groups is not allowed
321 Q_ASSERT(!m_db || (m_db->rootGroup() != this));
322
323 bool moveWithinDatabase = (m_db && m_db == parent->m_db);
324
325 if (index == -1) {
326 index = parent->children().size();
327
328 if (parentGroup() == parent) {
329 index--;
330 }
331 }
332
333 if (m_parent == parent && parent->children().indexOf(this) == index) {
334 return;
335 }
336
337 if (!moveWithinDatabase) {
338 cleanupParent();
339 m_parent = parent;
340 if (m_db) {
341 recCreateDelObjects();
342
343 // copy custom icon to the new database
344 if (!iconUuid().isNull() && parent->m_db
345 && m_db->metadata()->containsCustomIcon(iconUuid())
346 && !parent->m_db->metadata()->containsCustomIcon(iconUuid())) {
347 parent->m_db->metadata()->addCustomIcon(iconUuid(), icon());
348 }
349 }
350 if (m_db != parent->m_db) {
351 recSetDatabase(parent->m_db);
352 }
353 QObject::setParent(parent);
354 Q_EMIT aboutToAdd(this, index);
355 Q_ASSERT(index <= parent->m_children.size());
356 parent->m_children.insert(index, this);
357 }
358 else {
359 Q_EMIT aboutToMove(this, parent, index);
360 m_parent->m_children.removeAll(this);
361 m_parent = parent;
362 QObject::setParent(parent);
363 Q_ASSERT(index <= parent->m_children.size());
364 parent->m_children.insert(index, this);
365 }
366
367 if (m_updateTimeinfo) {
368 m_data.timeInfo.setLocationChanged(QDateTime::currentDateTimeUtc());
369 }
370
371 Q_EMIT modified();
372
373 if (!moveWithinDatabase) {
374 Q_EMIT added();
375 }
376 else {
377 Q_EMIT moved();
378 }
379 }
380
setParent(Database * db)381 void Group::setParent(Database* db)
382 {
383 Q_ASSERT(db);
384 Q_ASSERT(db->rootGroup() == this);
385
386 cleanupParent();
387
388 m_parent = nullptr;
389 recSetDatabase(db);
390
391 QObject::setParent(db);
392 }
393
database()394 Database* Group::database()
395 {
396 return m_db;
397 }
398
database() const399 const Database* Group::database() const
400 {
401 return m_db;
402 }
403
children()404 QList<Group*> Group::children()
405 {
406 return m_children;
407 }
408
children() const409 const QList<Group*>& Group::children() const
410 {
411 return m_children;
412 }
413
entries()414 QList<Entry*> Group::entries()
415 {
416 return m_entries;
417 }
418
entries() const419 const QList<Entry*>& Group::entries() const
420 {
421 return m_entries;
422 }
423
entriesRecursive(bool includeHistoryItems) const424 QList<Entry*> Group::entriesRecursive(bool includeHistoryItems) const
425 {
426 QList<Entry*> entryList;
427
428 entryList.append(m_entries);
429
430 if (includeHistoryItems) {
431 for (Entry* entry : m_entries) {
432 entryList.append(entry->historyItems());
433 }
434 }
435
436 for (Group* group : m_children) {
437 entryList.append(group->entriesRecursive(includeHistoryItems));
438 }
439
440 return entryList;
441 }
442
groupsRecursive(bool includeSelf) const443 QList<const Group*> Group::groupsRecursive(bool includeSelf) const
444 {
445 QList<const Group*> groupList;
446 if (includeSelf) {
447 groupList.append(this);
448 }
449
450 for (const Group* group : m_children) {
451 groupList.append(group->groupsRecursive(true));
452 }
453
454 return groupList;
455 }
456
groupsRecursive(bool includeSelf)457 QList<Group*> Group::groupsRecursive(bool includeSelf)
458 {
459 QList<Group*> groupList;
460 if (includeSelf) {
461 groupList.append(this);
462 }
463
464 for (Group* group : asConst(m_children)) {
465 groupList.append(group->groupsRecursive(true));
466 }
467
468 return groupList;
469 }
470
customIconsRecursive() const471 QSet<Uuid> Group::customIconsRecursive() const
472 {
473 QSet<Uuid> result;
474
475 if (!iconUuid().isNull()) {
476 result.insert(iconUuid());
477 }
478
479 const QList<Entry*> entryList = entriesRecursive(true);
480 for (Entry* entry : entryList) {
481 if (!entry->iconUuid().isNull()) {
482 result.insert(entry->iconUuid());
483 }
484 }
485
486 for (Group* group : m_children) {
487 result.unite(group->customIconsRecursive());
488 }
489
490 return result;
491 }
492
clone(Entry::CloneFlags entryFlags) const493 Group* Group::clone(Entry::CloneFlags entryFlags) const
494 {
495 Group* clonedGroup = new Group();
496
497 clonedGroup->setUpdateTimeinfo(false);
498
499 clonedGroup->setUuid(Uuid::random());
500 clonedGroup->m_data = m_data;
501
502 const QList<Entry*> entryList = entries();
503 for (Entry* entry : entryList) {
504 Entry* clonedEntry = entry->clone(entryFlags);
505 clonedEntry->setGroup(clonedGroup);
506 }
507
508 const QList<Group*> childrenGroups = children();
509 for (Group* groupChild : childrenGroups) {
510 Group* clonedGroupChild = groupChild->clone(entryFlags);
511 clonedGroupChild->setParent(clonedGroup);
512 }
513
514 clonedGroup->setUpdateTimeinfo(true);
515
516 QDateTime now = QDateTime::currentDateTimeUtc();
517 clonedGroup->m_data.timeInfo.setCreationTime(now);
518 clonedGroup->m_data.timeInfo.setLastModificationTime(now);
519 clonedGroup->m_data.timeInfo.setLastAccessTime(now);
520 clonedGroup->m_data.timeInfo.setLocationChanged(now);
521
522 return clonedGroup;
523 }
524
copyDataFrom(const Group * other)525 void Group::copyDataFrom(const Group* other)
526 {
527 m_data = other->m_data;
528 m_lastTopVisibleEntry = other->m_lastTopVisibleEntry;
529 }
530
addEntry(Entry * entry)531 void Group::addEntry(Entry* entry)
532 {
533 Q_ASSERT(entry);
534 Q_ASSERT(!m_entries.contains(entry));
535
536 Q_EMIT entryAboutToAdd(entry);
537
538 m_entries << entry;
539 connect(entry, SIGNAL(dataChanged(Entry*)), SIGNAL(entryDataChanged(Entry*)));
540 if (m_db) {
541 connect(entry, SIGNAL(modified()), m_db, SIGNAL(modifiedImmediate()));
542 }
543
544 Q_EMIT modified();
545 Q_EMIT entryAdded(entry);
546 }
547
removeEntry(Entry * entry)548 void Group::removeEntry(Entry* entry)
549 {
550 Q_ASSERT(m_entries.contains(entry));
551
552 Q_EMIT entryAboutToRemove(entry);
553
554 entry->disconnect(this);
555 if (m_db) {
556 entry->disconnect(m_db);
557 }
558 m_entries.removeAll(entry);
559 Q_EMIT modified();
560 Q_EMIT entryRemoved(entry);
561 }
562
recSetDatabase(Database * db)563 void Group::recSetDatabase(Database* db)
564 {
565 if (m_db) {
566 disconnect(SIGNAL(dataChanged(Group*)), m_db);
567 disconnect(SIGNAL(aboutToRemove(Group*)), m_db);
568 disconnect(SIGNAL(removed()), m_db);
569 disconnect(SIGNAL(aboutToAdd(Group*,int)), m_db);
570 disconnect(SIGNAL(added()), m_db);
571 disconnect(SIGNAL(aboutToMove(Group*,Group*,int)), m_db);
572 disconnect(SIGNAL(moved()), m_db);
573 disconnect(SIGNAL(modified()), m_db);
574 }
575
576 for (Entry* entry : asConst(m_entries)) {
577 if (m_db) {
578 entry->disconnect(m_db);
579 }
580 if (db) {
581 connect(entry, SIGNAL(modified()), db, SIGNAL(modifiedImmediate()));
582 }
583 }
584
585 if (db) {
586 connect(this, SIGNAL(dataChanged(Group*)), db, SIGNAL(groupDataChanged(Group*)));
587 connect(this, SIGNAL(aboutToRemove(Group*)), db, SIGNAL(groupAboutToRemove(Group*)));
588 connect(this, SIGNAL(removed()), db, SIGNAL(groupRemoved()));
589 connect(this, SIGNAL(aboutToAdd(Group*,int)), db, SIGNAL(groupAboutToAdd(Group*,int)));
590 connect(this, SIGNAL(added()), db, SIGNAL(groupAdded()));
591 connect(this, SIGNAL(aboutToMove(Group*,Group*,int)), db, SIGNAL(groupAboutToMove(Group*,Group*,int)));
592 connect(this, SIGNAL(moved()), db, SIGNAL(groupMoved()));
593 connect(this, SIGNAL(modified()), db, SIGNAL(modifiedImmediate()));
594 }
595
596 m_db = db;
597
598 for (Group* group : asConst(m_children)) {
599 group->recSetDatabase(db);
600 }
601 }
602
cleanupParent()603 void Group::cleanupParent()
604 {
605 if (m_parent) {
606 Q_EMIT aboutToRemove(this);
607 m_parent->m_children.removeAll(this);
608 Q_EMIT modified();
609 Q_EMIT removed();
610 }
611 }
612
recCreateDelObjects()613 void Group::recCreateDelObjects()
614 {
615 if (m_db) {
616 for (Entry* entry : asConst(m_entries)) {
617 m_db->addDeletedObject(entry->uuid());
618 }
619
620 for (Group* group : asConst(m_children)) {
621 group->recCreateDelObjects();
622 }
623 m_db->addDeletedObject(m_uuid);
624 }
625 }
626
resolveSearchingEnabled() const627 bool Group::resolveSearchingEnabled() const
628 {
629 switch (m_data.searchingEnabled) {
630 case Inherit:
631 if (!m_parent) {
632 return true;
633 }
634 else {
635 return m_parent->resolveSearchingEnabled();
636 }
637 case Enable:
638 return true;
639 case Disable:
640 return false;
641 default:
642 Q_ASSERT(false);
643 return false;
644 }
645 }
646
resolveAutoTypeEnabled() const647 bool Group::resolveAutoTypeEnabled() const
648 {
649 switch (m_data.autoTypeEnabled) {
650 case Inherit:
651 if (!m_parent) {
652 return true;
653 }
654 else {
655 return m_parent->resolveAutoTypeEnabled();
656 }
657 case Enable:
658 return true;
659 case Disable:
660 return false;
661 default:
662 Q_ASSERT(false);
663 return false;
664 }
665 }
666