BibleTime
btbookmarksmodel.cpp
Go to the documentation of this file.
1/*********
2*
3* In the name of the Father, and of the Son, and of the Holy Spirit.
4*
5* This file is part of BibleTime's source code, https://bibletime.info/
6*
7* Copyright 1999-2025 by the BibleTime developers.
8* The BibleTime source code is licensed under the GNU General Public License
9* version 2.0.
10*
11**********/
12
13/**
14 Total change list that should be applied after refactoring complete:
15 non latin bookmark titles shown with unrecognized symbols
16 feature request: hold Shift and Ctrl upon dragging item
17 move loader to private class
18 add ability to create bookmarks data with setData/insertRows
19 unrecognized characters increaases in size file each save/load
20 root folder for bookmarks
21*/
22
23#include "btbookmarksmodel.h"
24
25#include <algorithm>
26#include <memory>
27#include <QDir>
28#include <QDomElement>
29#include <QDomNode>
30#include <QFile>
31#include <QIODevice>
32#include <QList>
33#include <QString>
34#include <QTextStream>
35#include <QTime>
36#include <QTimer>
37#include <utility>
38#include "../util/btassert.h"
39#include "../util/btconnect.h"
40#include "../util/cresmgr.h"
41#include "../util/directory.h"
42#include "../util/tool.h"
43#include "btglobal.h"
44#include "config/btconfig.h"
46#include "keys/cswordversekey.h"
48
49
50#define CURRENT_SYNTAX_VERSION 1
51
52
53namespace {
54
55inline QString toHeader(QString const & key, QString const & moduleName)
56{ return QStringLiteral("%1 (%2)").arg(key).arg(moduleName); }
57
58class BookmarkFolder;
59
61
62public: // methods:
63
64 BookmarkItemBase(BookmarkFolder * parent = nullptr);
65
67 : m_parent(other.m_parent)
68 , m_text(other.m_text)
69 {}
70
71 virtual ~BookmarkItemBase() {}
72
73 void setText(QString const & text) { m_text = text; }
74
75 QString const & text() const { return m_text; }
76
77 virtual QString toolTip() const { return {}; }
78
79 virtual Qt::ItemFlags flags() const noexcept { return Qt::NoItemFlags; }
80
81 virtual QIcon const & icon() const noexcept {
82 static QIcon const noIcon;
83 return noIcon;
84 }
85
86 void setParent(BookmarkFolder * parent) { m_parent = parent; }
87
88 BookmarkFolder * parent() const { return m_parent; }
89
90 /** \returns index of this item in parent's child array. */
91 int index() const;
92
93private:
94
96 QString m_text;
97
98};
99
100class BookmarkItem final : public BookmarkItemBase {
101public:
102
104
105 /** Creates a bookmark with module, key and description. */
106 BookmarkItem(const CSwordModuleInfo & module, const QString & key,
107 const QString & description, const QString & title);
108
109 /** Creates a copy. */
110 BookmarkItem(const BookmarkItem & other);
111
112 Qt::ItemFlags flags() const noexcept final override {
113 return Qt::ItemIsSelectable // | Qt::ItemIsEditable
114 | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled
115 | Qt::ItemIsEnabled;
116 }
117
118 QIcon const & icon() const noexcept final override
119 { return CResMgr::mainIndex::bookmark::icon(); }
120
121 /** Returns the used module, 0 if there is no such module. */
123
124 /** Returns the used key. */
125 QString key() const;
126
127 void setKey(QString const & key) { m_key = key; }
128
129 /** Returns the used description. */
130 QString const & description() const { return m_description; }
131
132 void setDescription(QString const & description)
134
135 /** Returns a tooltip for this bookmark. */
136 QString toolTip() const override;
137
138 /** Returns the english key.*/
139 QString const & englishKey() const { return m_key; }
140
141 void setModule(QString const & moduleName)
142 { m_moduleName = moduleName; }
143
144 QString const & moduleName() const { return m_moduleName; }
145
146private:
147 QString m_key;
150
151};
152
153class BookmarkFolder final : public BookmarkItemBase {
154public:
155
156 BookmarkFolder(const QString & name, BookmarkFolder * parent = nullptr);
157
158 ~BookmarkFolder() final override { qDeleteAll(m_children); }
159
160 Qt::ItemFlags flags() const noexcept final override {
161 return Qt::ItemIsSelectable | Qt::ItemIsEditable
162 | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled
163 | Qt::ItemIsEnabled;
164 }
165
166 QIcon const & icon() const noexcept final override
167 { return CResMgr::mainIndex::closedFolder::icon(); }
168
169 /** Children routines. */
171 child->setParent(this);
172 BT_ASSERT(!m_children.contains(child));
173 m_children.append(child);
174 }
175
176 QList<BookmarkItemBase *> & children() noexcept { return m_children; }
177 QList<BookmarkItemBase *> const & children() const noexcept
178 { return m_children; }
179
180 void insertChild(int index, BookmarkItemBase * child) {
181 child->setParent(this);
182 BT_ASSERT(!m_children.contains(child));
183 m_children.insert(index, child);
184 }
185
186 void insertChildren(int index, QList<BookmarkItemBase *> children) {
187 for (auto * const c : children)
188 insertChild(index++, c);
189 }
190
191 void removeChild(int index) {
192 delete m_children[index];
193 m_children.removeAt(index);
194 }
195
196 /** Returns true if the given item is this or a direct or indirect subitem of this. */
197 bool hasDescendant(BookmarkItemBase const * item) const;
198
199 /** Creates a deep copy of this item. */
200 BookmarkFolder * deepCopy() const;
201
202private: // Fields:
203
204 QList<BookmarkItemBase *> m_children;
205
206};
207
208
215
218 return m_parent->children().indexOf(
219 const_cast<BookmarkItemBase *>(this));
220}
221
222}
223
225
226public: // methods:
227
229 : m_rootItem(new BookmarkFolder(QStringLiteral("Root")))
230 , q_ptr(parent)
231 {
232 m_saveTimer.setInterval(30 * 1000);
233 m_saveTimer.setSingleShot(true);
234 }
235 ~BtBookmarksModelPrivate() { delete m_rootItem; }
236
237 static QString defaultBookmarksFile() {
238 return util::directory::getUserBaseDir().absolutePath()
239 + QStringLiteral("/bookmarks.xml");
240 }
241
242 BookmarkItemBase * item(const QModelIndex & index) const {
243 if (index.isValid())
244 return static_cast<BookmarkItemBase *>(index.internalPointer());
245 return m_rootItem;
246 }
247
248 template <typename T>
249 T * itemAs(QModelIndex const & index) const
250 { return dynamic_cast<T *>(item(index)); }
251
252 void needSave(){
253 if(m_defaultModel == q_ptr){
254 if(!m_saveTimer.isActive())
255 m_saveTimer.start();
256 }
257 }
258
259 /** Loads a list of items (with subitem trees) from a named file
260 * or from the default bookmarks file. */
261 QList<BookmarkItemBase *> loadTree(QString fileName = QString()) {
262 QList<BookmarkItemBase*> itemList;
263
264 QDomDocument doc;
265 QString bookmarksFile = loadXmlFromFile(fileName);
266 if (bookmarksFile.isNull())
267 return QList<BookmarkItemBase*>();
268
269 doc.setContent(bookmarksFile);
270 QDomElement document = doc.documentElement();
271 if (document.tagName() != QStringLiteral("SwordBookmarks")) {
272 qWarning("Not a BibleTime Bookmark XML file");
273 return QList<BookmarkItemBase*>();
274 }
275
276 QDomElement child = document.firstChild().toElement();
277
278 while ( !child.isNull() && child.parentNode() == document) {
279 BookmarkItemBase* i = handleXmlElement(child, nullptr);
280 itemList.append(i);
281 if (!child.nextSibling().isNull()) {
282 child = child.nextSibling().toElement();
283 }
284 else {
285 child = QDomElement(); //null
286 }
287
288 }
289
290 return itemList;
291 }
292
293 /** Create a new item from a document element. */
294 BookmarkItemBase * handleXmlElement(QDomElement & element, BookmarkFolder * parent) {
295 if (element.tagName() == QStringLiteral("Folder")) {
296 BookmarkFolder* newFolder = new BookmarkFolder(QString(), parent);
297 if (element.hasAttribute(QStringLiteral("caption"))) {
298 newFolder->setText(element.attribute(
299 QStringLiteral("caption")));
300 }
301 QDomNodeList childList = element.childNodes();
302 for (int i = 0; i < childList.length(); i++) {
303 QDomElement newElement = childList.at(i).toElement();
304 handleXmlElement(newElement, newFolder); // passing parent in constructor will add items to tree
305 }
306 return newFolder;
307 }
308 if (element.tagName() == QStringLiteral("Bookmark")) {
309 BookmarkItem* newBookmarkItem = new BookmarkItem(parent);
310 if (element.hasAttribute(QStringLiteral("modulename"))) {
311 //we use the name in all cases, even if the module isn't installed anymore
312 newBookmarkItem->setModule(
313 element.attribute(QStringLiteral("modulename")));
314 }
315 if (element.hasAttribute(QStringLiteral("key"))) {
316 newBookmarkItem->setKey(
317 element.attribute(QStringLiteral("key")));
318 }
319 if (element.hasAttribute(QStringLiteral("description"))) {
320 newBookmarkItem->setDescription(
321 element.attribute(QStringLiteral("description")));
322 }
323 if (element.hasAttribute(QStringLiteral("title"))) {
324 newBookmarkItem->setText(
325 element.attribute(QStringLiteral("title")));
326 }
327 return newBookmarkItem;
328 }
329 return nullptr;
330 }
331
332 /** Loads a bookmark XML document from a named file or from the default bookmarks file. */
333 QString loadXmlFromFile(QString fileName = QString()) {
334 if (fileName.isEmpty())
335 fileName = defaultBookmarksFile();
336
337 QTextStream t;
338 t.setAutoDetectUnicode(false);
339 t.setEncoding(QStringConverter::Utf8);
340
341 QFile file(fileName);
342 if (!file.open(QIODevice::ReadOnly))
343 return {};
344
345 t.setDevice(&file);
346 return t.readAll();
347 }
348
349 /** Takes one item and saves the tree which is under it to a named file
350 * or to the default bookmarks file, asking the user about overwriting if necessary. */
352 BT_ASSERT(rootItem);
353
354 QDomDocument doc(QStringLiteral("DOC"));
355 doc.appendChild(
356 doc.createProcessingInstruction(
357 QStringLiteral("xml"),
358 QStringLiteral("version=\"1.0\" encoding=\"UTF-8\"")));
359
360 auto content =
361 doc.createElement(QStringLiteral("SwordBookmarks"));
362 content.setAttribute(QStringLiteral("syntaxVersion"),
364 doc.appendChild(content);
365
366 //append the XML nodes of all child items
367
368 for (auto const * const itemPtr : rootItem->children())
369 saveItem(itemPtr, content);
370 return doc.toString();
371 }
372
373 /** Writes one item to a document element. */
374 void saveItem(BookmarkItemBase const * item, QDomElement & parentElement) {
375 BookmarkFolder const * folderItem = nullptr;
376 BookmarkItem const * bookmarkItem = nullptr;
377
378 if ((folderItem = dynamic_cast<BookmarkFolder const *>(item))) {
379 auto elem =
380 parentElement.ownerDocument().createElement(
381 QStringLiteral("Folder"));
382 elem.setAttribute(QStringLiteral("caption"), folderItem->text());
383
384 parentElement.appendChild(elem);
385
386 for (auto const * const itemPtr : folderItem->children())
387 saveItem(itemPtr, elem);
388 }
389 else if ((bookmarkItem = dynamic_cast<BookmarkItem const *>(item))) {
390 auto elem =
391 parentElement.ownerDocument().createElement(
392 QStringLiteral("Bookmark"));
393
394 elem.setAttribute(QStringLiteral("key"),
395 bookmarkItem->englishKey());
396 elem.setAttribute(QStringLiteral("description"),
397 bookmarkItem->description());
398 elem.setAttribute(QStringLiteral("modulename"),
399 bookmarkItem->moduleName());
400 elem.setAttribute(QStringLiteral("moduledescription"),
401 bookmarkItem->module()
402 ? bookmarkItem->module()->config(
404 : QString());
405 if (!bookmarkItem->text().isEmpty()) {
406 elem.setAttribute(QStringLiteral("title"), bookmarkItem->text());
407 }
408 parentElement.appendChild(elem);
409 }
410 }
411
412
413public: // fields:
414
418
419 Q_DECLARE_PUBLIC(BtBookmarksModel)
420 BtBookmarksModel * const q_ptr;
421
422};
423
424BtBookmarksModel * BtBookmarksModelPrivate::m_defaultModel = nullptr;
425
426BookmarkFolder::BookmarkFolder(const QString & name, BookmarkFolder * parent)
427 : BookmarkItemBase(parent) {
428 setText(name);
429}
430
431bool BookmarkFolder::hasDescendant(BookmarkItemBase const * const item) const {
432 if (this == item)
433 return true;
434 if (children().indexOf(const_cast<BookmarkItemBase *>(item)) > -1)
435 return true;
436 for (auto const * const childItem : children())
437 if (BookmarkFolder const * const folder =
438 dynamic_cast<BookmarkFolder const *>(childItem))
439 if (folder->hasDescendant(childItem))
440 return true;
441 return false;
442}
443
445 BookmarkFolder* newFolder = new BookmarkFolder(this->text());
446 for (auto const * const subitem : children()) {
447 if (BookmarkItem const * const bmItem =
448 dynamic_cast<BookmarkItem const *>(subitem))
449 {
450 newFolder->addChild(new BookmarkItem(*bmItem));
451 } else if (BookmarkFolder const * const bmFolder =
452 dynamic_cast<BookmarkFolder const *>(subitem))
453 {
454 newFolder->addChild(bmFolder->deepCopy());
455 }
456 }
457 return newFolder;
458}
459
461 const QString & key,
462 const QString & description,
463 const QString & title)
464 : m_description(description)
465 , m_moduleName(module.name())
466{
467 Q_UNUSED(title)
468
469 if (((module.type() == CSwordModuleInfo::Bible) || (module.type() == CSwordModuleInfo::Commentary))) {
470 /// here we only translate \param key into english
471 sword::VerseKey vk(key.toUtf8().constData(), key.toUtf8().constData(),
472 static_cast<sword::VerseKey *>(module.swordModule().getKey())->getVersificationSystem());
473 CSwordVerseKey k(&vk, &module);
474 k.setLocale("en");
475 m_key = k.key();
476 }
477 else {
478 m_key = key;
479 }
480
481 setText(toHeader(key, module.name()));
482}
483
485 : BookmarkItemBase(parent) {
486 setText(toHeader(key(), module() ? module()->name() : QObject::tr("unknown")));
487}
488
490 : BookmarkItemBase(other)
491 , m_key(other.m_key)
492 , m_description(other.m_description)
493 , m_moduleName(other.m_moduleName)
494{
495 setText(toHeader(key(), module() ? module()->name() : QObject::tr("unknown")));
496}
497
500
501QString BookmarkItem::key() const {
502 const QString englishKeyName = englishKey();
503 if (!module()) {
504 return englishKeyName;
505 }
506
507 QString returnKeyName = englishKeyName;
508 if ((module()->type() == CSwordModuleInfo::Bible) || (module()->type() == CSwordModuleInfo::Commentary)) {
509 /// here we only translate \param key into current book name language
510 sword::VerseKey vk(englishKeyName.toUtf8().constData(), englishKeyName.toUtf8().constData(),
511 static_cast<sword::VerseKey *>(module()->swordModule().getKey())->getVersificationSystem());
512 CSwordVerseKey k(&vk, module());
513 k.setLocale(CSwordBackend::instance().booknameLanguage().toLatin1());
514 returnKeyName = k.key();
515 }
516
517 return returnKeyName;
518}
519
520QString BookmarkItem::toolTip() const {
521 auto const * const m = module();
522 if (!m)
523 return QString();
524
525 FilterOptions filterOptions = btConfig().getFilterOptions();
526 filterOptions.footnotes = false;
527 filterOptions.scriptureReferences = false;
529
530 std::unique_ptr<CSwordKey> k(m->createKey());
531 BT_ASSERT(k);
532 k->setKey(key());
533
534 // Language const * lang = m->language();
535 // BtConfig::FontSettingsPair fontPair = getBtConfig().getFontForLanguage(lang);
536
537 auto const header = toHeader(key(), m->name());
538 auto const & txt = text();
539 if (txt == header)
540 return QStringLiteral("<b>%1)</b><hr>%2").arg(header, description());
541 return QStringLiteral("<b>%1)</b><br>%2<hr>%3")
542 .arg(header, txt, description());
543}
544
545
547 : QAbstractItemModel(parent)
548 , d_ptr(new BtBookmarksModelPrivate(this))
549{
550 load();
551}
552
555 , d_ptr(new BtBookmarksModelPrivate(this))
556{ load(fileName); }
557
559 Q_D(BtBookmarksModel);
560
561 if(d->m_saveTimer.isActive())
562 save();
563
564 delete d_ptr;
565}
566
567int BtBookmarksModel::rowCount(const QModelIndex & parent) const {
568 Q_D(const BtBookmarksModel);
569
570 if (auto const * const f = dynamic_cast<BookmarkFolder *>(d->item(parent)))
571 return f->children().size();
572 return 0;
573}
574
575int BtBookmarksModel::columnCount(const QModelIndex & parent) const {
576 Q_UNUSED(parent)
577 return 1;
578}
579
580bool BtBookmarksModel::hasChildren(const QModelIndex & parent) const {
581 return rowCount(parent) > 0;
582}
583QModelIndex BtBookmarksModel::index(int row, int column, const QModelIndex & parent) const {
584 Q_D(const BtBookmarksModel);
585
586 auto const * const f = dynamic_cast<BookmarkFolder *>(d->item(parent));
587 if (f && f->children().size() > row && row >= 0)
588 return createIndex(row, column, f->children()[row]);
589 return QModelIndex();
590}
591
592QModelIndex BtBookmarksModel::parent(const QModelIndex & index) const {
593 Q_D(const BtBookmarksModel);
594
595 const BookmarkItemBase * i = d->item(index);
596 return (i->parent() == nullptr || i->parent()->parent() == nullptr) ?
597 QModelIndex() : createIndex(i->parent()->index(), 0, i->parent());
598}
599
600QVariant BtBookmarksModel::data(const QModelIndex & index, int role) const {
601 Q_D(const BtBookmarksModel);
602
603 const BookmarkItemBase * i = d->item(index);
604
605 switch(role)
606 {
607 case Qt::DisplayRole:
608 case Qt::EditRole:
609 return i->text();
610 case Qt::ToolTipRole:
611 return i->toolTip();
612 case Qt::DecorationRole:
613 return i->icon();
614 case TypeRole:
615 {
616 if(isBookmark(index))
617 return QStringLiteral("bookmark");
618 else
619 return QStringLiteral("folder");
620 }
621 }
622 return QVariant();
623}
624
625Qt::ItemFlags BtBookmarksModel::flags(const QModelIndex & index) const {
626 Q_D(const BtBookmarksModel);
627
628 return d->item(index)->flags();
629}
630
631QVariant BtBookmarksModel::headerData(int section, Qt::Orientation orientation, int role) const {
632 Q_UNUSED(section)
633 Q_UNUSED(orientation)
634 Q_UNUSED(role)
635
636 return QVariant();
637}
638
639bool BtBookmarksModel::setData(const QModelIndex & index, const QVariant & val, int role) {
640 Q_D(BtBookmarksModel);
641
642 BookmarkItemBase * i = d->item(index);
643 if (role == Qt::DisplayRole || role == Qt::EditRole) {
644 i->setText(val.toString());
645 if(dynamic_cast<BookmarkFolder *>(i) || dynamic_cast<BookmarkItem *>(i))
646 d->needSave();
647 return true;
648 }
649 return false;
650}
651
652bool BtBookmarksModel::removeRows(int row, int count, const QModelIndex & parent)
653{
654 Q_D(BtBookmarksModel);
655 if (auto * const f = dynamic_cast<BookmarkFolder *>(d->item(parent))) {
656 BT_ASSERT(row + count <= f->children().size());
657 beginRemoveRows(parent, row, row + count - 1);
658 for(int i = 0; i < count; ++i)
659 f->removeChild(row);
660 endRemoveRows();
661 d->needSave();
662 return true;
663 }
664 return false;
665}
666
667bool BtBookmarksModel::insertRows(int row, int count, const QModelIndex &parent)
668{
669 Q_D(BtBookmarksModel);
670 if (auto * const f = dynamic_cast<BookmarkFolder *>(d->item(parent))) {
671 BT_ASSERT(row <= f->children().size());
672 beginInsertRows(parent, row, row + count - 1);
673 for(int i = 0; i < count; ++i)
674 f->insertChild(row, new BookmarkItemBase);
675 endInsertRows();
676 return true;
677 }
678 return false;
679}
680
681bool BtBookmarksModel::save(QString fileName, const QModelIndex & rootItem) {
682 Q_D(BtBookmarksModel);
683 BT_ASSERT(dynamic_cast<BookmarkFolder *>(d->item(rootItem)));
684 auto const serializedTree =
685 d->serializeTreeFromRootItem(
686 static_cast<BookmarkFolder *>(d->item(rootItem)));
687 if (fileName.isEmpty())
689
690 util::tool::savePlainFile(fileName, serializedTree);
691
692 if(d->m_saveTimer.isActive())
693 d->m_saveTimer.stop();
694
695 return true;
696}
697
698bool BtBookmarksModel::load(QString fileName, const QModelIndex & rootItem) {
699 Q_D(BtBookmarksModel);
700 BT_ASSERT(dynamic_cast<BookmarkFolder *>(d->item(rootItem)));
701 auto * const i = static_cast<BookmarkFolder *>(d->item(rootItem));
702 QList<BookmarkItemBase *> items = d->loadTree(fileName);
703
704 if(!rootItem.isValid() && fileName.isEmpty()) {
705 BT_ASSERT(!d->m_defaultModel && "Only one default model allowed!");
706 BT_CONNECT(&d->m_saveTimer, &QTimer::timeout,
708 d->m_defaultModel = this;
709 }
710
711 if(items.size() == 0)
712 return false;
713
714 auto const numChildren = i->children().size();
715 beginInsertRows(rootItem, numChildren, numChildren + items.size() - 1);
716 i->insertChildren(numChildren, items);
717 endInsertRows();
718 return true;
719}
720
721bool BtBookmarksModel::isFolder(const QModelIndex &index) const
722{
723 Q_D(const BtBookmarksModel);
724 return d->itemAs<BookmarkFolder const>(index);
725}
726
727bool BtBookmarksModel::isBookmark(const QModelIndex &index) const
728{
729 Q_D(const BtBookmarksModel);
730 return d->itemAs<BookmarkItem const>(index);
731}
732
733void BtBookmarksModel::copyItems(int row, const QModelIndex & parent, const QModelIndexList & toCopy)
734{
735 Q_D(BtBookmarksModel);
736 BT_ASSERT(dynamic_cast<BookmarkFolder *>(d->item(parent)));
737 auto * const targetFolder = static_cast<BookmarkFolder *>(d->item(parent));
738
739 bool bookmarksOnly = true;
740 bool targetIncluded = false;
741 bool moreThanOneFolder = false;
742
743 QList<BookmarkItemBase *> newList;
744
745 for (auto const & index : toCopy) {
746 if (BookmarkFolder const * const folder =
747 d->itemAs<BookmarkFolder const>(index))
748 {
749 bookmarksOnly = false;
750 if (toCopy.count() > 1) { // only one item allowed if a folder is selected
751 moreThanOneFolder = true;
752 break;
753 }
754 if (folder->hasDescendant(d->item(parent))) { // dropping to self or descendand not allowed
755 targetIncluded = true;
756 break;
757 }
758 }
759 else {
760 newList.append(new BookmarkItem(*(d->itemAs<BookmarkItem>(index))));
761 }
762 }
763
764 if (!bookmarksOnly && toCopy.count() == 1)
765 newList.append(d->itemAs<BookmarkFolder const>(toCopy[0])->deepCopy());
766 if (!bookmarksOnly && toCopy.count() > 1) {
767 // wrong amount of items
768 moreThanOneFolder = true;
769 }
770
771 if (moreThanOneFolder || targetIncluded) {
772 return;
773 }
774
775
776 beginInsertRows(parent, row, row + newList.size() - 1);
777
778 targetFolder->insertChildren(row, newList);
779
780 endInsertRows();
781
782 d->needSave();
783}
784
786{
787 Q_D(const BtBookmarksModel);
788
789 if (BookmarkItem const * const i = d->itemAs<BookmarkItem const>(index))
790 return i->module();
791 return nullptr;
792}
793
794QString BtBookmarksModel::key(const QModelIndex & index) const
795{
796 Q_D(const BtBookmarksModel);
797
798 if (BookmarkItem const * const i = d->itemAs<BookmarkItem const>(index))
799 return i->key();
800 return QString();
801}
802
803QString BtBookmarksModel::description(const QModelIndex &index) const
804{
805 Q_D(const BtBookmarksModel);
806
807 if (BookmarkItem const * const i = d->itemAs<BookmarkItem const>(index))
808 return i->description();
809 return QString();
810}
811
812void BtBookmarksModel::setDescription(const QModelIndex &index, const QString &description)
813{
814 Q_D(BtBookmarksModel);
815
816 if (BookmarkItem * const i = d->itemAs<BookmarkItem>(index)) {
817 i->setDescription(description);
818 d->needSave();
819 }
820}
821
822QModelIndex BtBookmarksModel::addBookmark(int const row,
823 QModelIndex const & parent,
824 CSwordModuleInfo const & module,
825 QString const & key,
826 QString const & description,
827 QString const & title)
828{
829 Q_D(BtBookmarksModel);
830
831 if (BookmarkFolder * const i = d->itemAs<BookmarkFolder>(parent)) {
832 int r = row < 0 ? row + rowCount(parent) + 1 : row;
833
834 beginInsertRows(parent, r, r);
835
836 BookmarkItem * c = new BookmarkItem(module, key, description, title);
837 i->insertChild(r, c);
838
839 endInsertRows();
840
841 d->needSave();
842
843 return createIndex(c->index(), 0, c);
844 }
845 return QModelIndex();
846}
847
848QModelIndex BtBookmarksModel::addFolder(int row, const QModelIndex &parent, const QString &name)
849{
850 Q_D(BtBookmarksModel);
851
852 if (BookmarkFolder * const i = d->itemAs<BookmarkFolder>(parent)) {
853 beginInsertRows(parent, row, row);
854
855 BookmarkFolder * c = new BookmarkFolder(name.isEmpty() ? QObject::tr("New folder") : name);
856 i->insertChild(row, c);
857
858 endInsertRows();
859
860 d->needSave();
861
862 return createIndex(c->index(), 0, c);
863 }
864 return QModelIndex();
865}
866
867bool BtBookmarksModel::hasDescendant(const QModelIndex &baseIndex, const QModelIndex &testIndex) const
868{
869 Q_D(const BtBookmarksModel);
870
871 if (BookmarkFolder const * const f =
872 d->itemAs<BookmarkFolder const>(baseIndex))
873 return f->hasDescendant(d->item(testIndex));
874 return false;
875}
876
877bool BtBookmarksModelSortAscending(BookmarkItemBase * i1, BookmarkItemBase * i2)
878{
879 return i1->text().localeAwareCompare(i2->text()) < 0;
880}
881
882bool BtBookmarksModelSortDescending(BookmarkItemBase * i1, BookmarkItemBase * i2)
883{
884 return i1->text().localeAwareCompare(i2->text()) > 0;
885}
886
887void BtBookmarksModel::sortItems(QModelIndex const & parent,
888 Qt::SortOrder const order)
889{
890 Q_D(BtBookmarksModel);
891
892 if(BookmarkFolder * const f = d->itemAs<BookmarkFolder>(parent)) {
893 QList<BookmarkFolder *> parents;
894 if(f == d->m_rootItem) {
895 QList<BookmarkItemBase *> items;
896 items.append(f);
897 for(int i = 0; i < items.size(); ++i) {
898 if (auto * const ff = dynamic_cast<BookmarkFolder *>(items[i])){
899 items.append(ff->children());
900 parents.append(ff);
901 }
902 }
903 }
904 else
905 parents.append(f);
906
907 for (auto * const f : parents) {
908 Q_EMIT layoutAboutToBeChanged();
909
910 QModelIndexList indexes;
911 for(int i = 0; i < f->children().size(); ++i)
912 indexes.append(createIndex(i, 0, f->children()[i]));
913
914 std::sort(f->children().begin(),
915 f->children().end(),
916 order == Qt::AscendingOrder
919
920 for(int i = 0; i < f->children().size(); ++i) {
921 BookmarkItemBase * iii = f->children()[i];
922 for(int ii = 0; ii < indexes.size(); ++ii)
923 if(iii == indexes[ii].internalPointer())
924 changePersistentIndex(createIndex(ii, 0, iii), createIndex(i, 0, iii));
925 }
926 Q_EMIT layoutChanged();
927
928 d->needSave();
929 }
930 }
931}
932
934 static auto const staticRoleNames = {
935 std::make_pair(static_cast<int>(Qt::DisplayRole),
936 QByteArrayLiteral("display")),
937 std::make_pair(static_cast<int>(Qt::DecorationRole),
938 QByteArrayLiteral("icon")),
939 std::make_pair(static_cast<int>(Qt::EditRole),
940 QByteArrayLiteral("edit")),
941 std::make_pair(static_cast<int>(TypeRole),
942 QByteArrayLiteral("itemtype"))
943 };
944 return staticRoleNames;
945}
#define BT_ASSERT(...)
Definition btassert.h:17
bool BtBookmarksModelSortAscending(BookmarkItemBase *i1, BookmarkItemBase *i2)
#define CURRENT_SYNTAX_VERSION
bool BtBookmarksModelSortDescending(BookmarkItemBase *i1, BookmarkItemBase *i2)
BtConfig & btConfig()
This is a shortchand for BtConfig::getInstance().
Definition btconfig.h:305
#define BT_CONNECT(...)
Definition btconnect.h:20
QString const & key() const noexcept
QString m_moduleName
QString m_key
QString const & description() const noexcept
QString m_description
QString const & module() const noexcept
QList< BookmarkItemBase * > loadTree(QString fileName=QString())
BtBookmarksModelPrivate(BtBookmarksModel *parent)
T * itemAs(QModelIndex const &index) const
static QString defaultBookmarksFile()
QString loadXmlFromFile(QString fileName=QString())
BookmarkItemBase * handleXmlElement(QDomElement &element, BookmarkFolder *parent)
void saveItem(BookmarkItemBase const *item, QDomElement &parentElement)
QString serializeTreeFromRootItem(BookmarkFolder *rootItem)
static BtBookmarksModel * m_defaultModel
BookmarkItemBase * item(const QModelIndex &index) const
int rowCount(const QModelIndex &parent=QModelIndex()) const override
void copyItems(int row, const QModelIndex &parent, const QModelIndexList &toCopy)
Copies item to target position.
bool isBookmark(const QModelIndex &index) const
bool hasDescendant(const QModelIndex &baseIndex, const QModelIndex &testIndex) const
bool isFolder(const QModelIndex &index) const
void sortItems(QModelIndex const &parent=QModelIndex(), Qt::SortOrder const order=Qt::AscendingOrder)
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
QString description(const QModelIndex &index) const
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
Qt::ItemFlags flags(const QModelIndex &index) const override
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
QHash< int, QByteArray > roleNames() const override
bool removeRows(int row, int count, const QModelIndex &parent=QModelIndex()) override
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
void setDescription(const QModelIndex &index, const QString &description)
set descritpion for index.
QModelIndex parent(const QModelIndex &index) const override
bool load(QString fileName=QString(), const QModelIndex &rootItem=QModelIndex())
Import bookmarks from file.
BtBookmarksModel(QObject *parent=nullptr)
bool hasChildren(const QModelIndex &parent=QModelIndex()) const override
CSwordModuleInfo * module(const QModelIndex &index) const
bool insertRows(int row, int count, const QModelIndex &parent=QModelIndex()) override
QModelIndex addFolder(int row, const QModelIndex &parent, const QString &name=QString())
add new folder.
bool save(QString fileName=QString(), const QModelIndex &rootItem=QModelIndex())
Save bookmarks or specified branch to file.
BtBookmarksModelPrivate *const d_ptr
QModelIndex addBookmark(int const row, QModelIndex const &parent, CSwordModuleInfo const &module, QString const &key, QString const &description=QString(), QString const &title=QString())
add new item with given parameters
int columnCount(const QModelIndex &parent=QModelIndex()) const override
QString key(const QModelIndex &index) const
FilterOptions getFilterOptions() const
Definition btconfig.h:160
CSwordModuleInfo * findModuleByName(const QString &name) const
Searches for a module with the given name.
static CSwordBackend & instance() noexcept
void setFilterOptions(const FilterOptions &options)
QString config(const CSwordModuleInfo::ConfigEntry entry) const
ModuleType type() const
QString const & name() const
sword::SWModule & swordModule() const
CSwordKey implementation for Sword's VerseKey.
void insertChild(int index, BookmarkItemBase *child)
QList< BookmarkItemBase * > const & children() const noexcept
bool hasDescendant(BookmarkItemBase const *item) const
void insertChildren(int index, QList< BookmarkItemBase * > children)
Qt::ItemFlags flags() const noexcept final override
QIcon const & icon() const noexcept final override
Qt::ItemFlags flags() const noexcept final override
BookmarkItem(const CSwordModuleInfo &module, const QString &key, const QString &description, const QString &title)
QIcon const & icon() const noexcept final override
#define T(f)
QStringList r(content.left(bodyIndex))
QString toHeader(QString const &key, QString const &moduleName)
const QDir & getUserBaseDir()
bool savePlainFile(const QString &filename, void(&writer)(QTextStream &, void *), void *userPtr)
Definition tool.cpp:35
int scriptureReferences
Definition btglobal.h:36