BibleTime
btbookshelftreemodel.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
14
15#include <QByteArray>
16#include <QDataStream>
17#include <QDebug>
18#include <QModelIndexList>
19#include <QPair>
20#include <QtGlobal>
21#include <type_traits>
22#include <utility>
23#include "../../util/btassert.h"
24#include "../../util/btconnect.h"
25#include "../../util/macros.h"
26#include "../config/btconfigcore.h"
27#include "../drivers/btconstmoduleset.h"
28#include "../drivers/btmoduleset.h"
29#include "categoryitem.h"
30#include "indexingitem.h"
31#include "languageitem.h"
32#include "moduleitem.h"
33
34
35using namespace BookshelfModel;
36
39 { GROUP_CATEGORY };
41 { GROUP_CATEGORY, GROUP_LANGUAGE };
43 { GROUP_LANGUAGE };
45 { GROUP_LANGUAGE, GROUP_CATEGORY };
47 CAT_LANG;
48
50 QString const & key)
51{
52 BT_ASSERT(!key.isNull());
53 QVariant const v = config.qVariantValue(key, QVariant());
54 if (!v.canConvert<Grouping>())
55 return false;
56
57 (*this) = v.value<Grouping>();
58 return true;
59}
60
62 QString const & key) const
63{
64 BT_ASSERT(!key.isNull());
65 config.setValue(key, QVariant::fromValue(*this));
66}
67
73
75 QString const & configKey,
76 QObject * const parent)
77 : QAbstractItemModel(parent)
78 , m_rootItem(std::make_unique<RootItem>())
79 , m_groupingOrder(
80 [](BtConfigCore const & config_, QString const & configKey_){
81 if (Grouping grouping; grouping.loadFrom(config_, configKey_))
82 return grouping;
83 return Grouping::DEFAULT;
84 }(config, configKey))
85 , m_defaultChecked(MODULE_HIDDEN)
86 , m_checkable(false)
87{}
88
90 QObject * const parent)
91 : QAbstractItemModel(parent)
92 , m_rootItem(std::make_unique<RootItem>())
93 , m_groupingOrder(grouping)
94 , m_defaultChecked(MODULE_HIDDEN)
95 , m_checkable(false) {}
96
98
99int BtBookshelfTreeModel::rowCount(QModelIndex const & parent) const {
100 return getItem(parent).children().size();
101}
102
103int BtBookshelfTreeModel::columnCount(QModelIndex const & parent) const {
104 Q_UNUSED(parent)
105
106 return 1;
107}
108
109bool BtBookshelfTreeModel::hasChildren(QModelIndex const & parent) const {
110 return !getItem(parent).children().isEmpty();
111}
112
113QModelIndex BtBookshelfTreeModel::index(int const row, int const column,
114 QModelIndex const & parent) const
115{
116 if (!hasIndex(row, column, parent)) return QModelIndex();
117
118 Item & parentItem = getItem(parent);
119 Item * const childItem = parentItem.children().at(row);
120 if (!childItem)
121 return QModelIndex();
122
123 return createIndex(row, column, childItem);
124}
125
126QModelIndex BtBookshelfTreeModel::parent(QModelIndex const & index) const {
127 if (!index.isValid())
128 return QModelIndex();
129
130 Item const * childItem(static_cast<Item*>(index.internalPointer()));
131 BT_ASSERT(childItem);
132 Item * parentItem(childItem->parent());
133 BT_ASSERT(parentItem);
134
135 if (parentItem == m_rootItem.get())
136 return QModelIndex();
137
138 return createIndex(parentItem->childIndex(), 0, parentItem);
139}
140
141QVariant
142BtBookshelfTreeModel::data(QModelIndex const & index, int const role) const {
143 if (!index.isValid() || index.column() != 0)
144 return QVariant();
145
146 Item const * const i = static_cast<Item*>(index.internalPointer());
147 BT_ASSERT(i);
148 switch (role) {
149
150 case Qt::CheckStateRole:
151 if (!m_checkable)
152 break;
153 [[fallthrough]];
154
156 return i->checkState();
157
159 /* This case is just an optimization. */
160 if (i->type() == Item::ITEM_MODULE) {
161 ModuleItem const & mi = *static_cast<ModuleItem const *>(i);
162 return QVariant::fromValue(static_cast<void *>(&mi.moduleInfo()));
163 }
164 return QVariant::fromValue(nullptr);
165
166 case Qt::DisplayRole:
167 case Qt::DecorationRole:
169 default:
170 if (i->type() == Item::ITEM_MODULE)
171 return data(static_cast<ModuleItem const *>(i)->moduleInfo(), role);
172
173 return i->data(role);
174
175 }
176 return QVariant();
177}
178
179QVariant
180BtBookshelfTreeModel::data(CSwordModuleInfo & module, int const role) const {
182 return m_sourceModel->data(m_sourceIndexMap.value(&module), role);
183}
184
185bool BtBookshelfTreeModel::setData(QModelIndex const & itemIndex,
186 QVariant const & value,
187 int const role)
188{
189 BT_ASSERT(itemIndex.isValid());
190 using IP = QPair<Item *, QModelIndex>;
191
192 if (UNLIKELY(role != Qt::CheckStateRole))
193 return false;
194
195 bool ok;
196 Qt::CheckState newState = static_cast<Qt::CheckState>(value.toInt(&ok));
197 if (UNLIKELY(!ok))
198 return false;
199
200 // Handle partially checked as checked here in setData():
201 if (newState == Qt::PartiallyChecked)
202 newState = Qt::Checked;
203
204 Item * item = static_cast<Item *>(itemIndex.internalPointer());
205 BT_ASSERT(item);
206 if (item->checkState() == newState) return false;
207
208 // Recursively (un)check all children:
209 QList<IP> q;
210 IP p(item, itemIndex);
211 for (;;) {
212 if (item->checkState() != newState) {
213 item->setCheckState(newState);
214 Q_EMIT dataChanged(p.second, p.second);
215 if (item->type() == Item::ITEM_MODULE) {
216 ModuleItem const & mItem = *static_cast<ModuleItem *>(item);
217 CSwordModuleInfo & mInfo = mItem.moduleInfo();
218 if (newState == Qt::Checked) {
219 m_checkedModulesCache.insert(&mInfo);
220 Q_EMIT moduleChecked(&mInfo, true);
221 } else {
222 m_checkedModulesCache.remove(&mInfo);
223 Q_EMIT moduleChecked(&mInfo, false);
224 }
225 } else {
226 QList<Item *> const & children = item->children();
227 for (int i = 0; i < children.size(); i++)
228 q.append(IP(children.at(i), index(i, 0, p.second)));
229 }
230 }
231 if (q.empty())
232 break;
233 p = q.takeFirst();
234 item = p.first;
235 }
236
237 // Recursively change parent check states.
238 resetParentCheckStates(itemIndex.parent(), false);
239
240 return true;
241}
242
243Qt::ItemFlags BtBookshelfTreeModel::flags(QModelIndex const & index) const {
244 if (!index.isValid())
245 return Qt::ItemFlags();
246
247 Qt::ItemFlags f(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
248
249 if (m_checkable) {
250 f |= Qt::ItemIsUserCheckable;
251
252 Item const & i = *static_cast<Item*>(index.internalPointer());
253 if (i.type() != Item::ITEM_MODULE)
254 f |= Qt::ItemIsAutoTristate;
255 }
256
257 return f;
258}
259
260QVariant BtBookshelfTreeModel::headerData(int const section,
261 Qt::Orientation const orientation,
262 int const role) const
263{
264 if (orientation == Qt::Horizontal)
265 return m_sourceModel->headerData(section, orientation, role);
266
267 return QVariant();
268}
269
271 std::shared_ptr<QAbstractItemModel> sourceModel)
272{
274 return;
275
276 beginResetModel();
277 if (m_sourceModel) {
278 auto & model = *m_sourceModel;
279 disconnect(&model, &QAbstractItemModel::rowsAboutToBeRemoved,
281 disconnect(&model, &QAbstractItemModel::rowsInserted,
283 disconnect(&model, &QAbstractItemModel::dataChanged,
285 m_rootItem = std::make_unique<RootItem>();
286 m_modules.clear();
287 m_sourceIndexMap.clear();
288 m_checkedModulesCache.clear();
289 }
290
291 m_sourceModel = std::move(sourceModel);
292
293 if (m_sourceModel) {
294 auto & model = *m_sourceModel;
295 BT_CONNECT(&model, &QAbstractItemModel::rowsAboutToBeRemoved,
297 BT_CONNECT(&model, &QAbstractItemModel::rowsInserted,
299 BT_CONNECT(&model, &QAbstractItemModel::dataChanged,
301
302 for (int i = 0; i < model.rowCount(); i++) {
303 QModelIndex const moduleIndex(model.index(i, 0));
304 CSwordModuleInfo & module = *static_cast<CSwordModuleInfo *>(
305 model.data(moduleIndex,
306 BtBookshelfModel::ModulePointerRole).value<void*>());
307
308 bool checked;
310 checked = !model.data(moduleIndex,
312 } else if (m_defaultChecked == MODULE_INDEXED) {
313 checked = !model.data(moduleIndex,
315 } else {
316 checked = (m_defaultChecked == CHECKED);
317 }
318 m_sourceIndexMap[&module] = moduleIndex;
319 addModule(module, checked, true);
320 }
321 }
322 endResetModel();
323}
324
326 bool const emitSignal)
327{
329 return;
330
332
333 if (m_sourceModel != nullptr) {
334 BtModuleSet const checked(m_checkedModulesCache);
335 m_checkedModulesCache.clear();
336
337 beginResetModel();
338 m_rootItem = std::make_unique<RootItem>();
339 m_modules.clear();
340
341 for (int i = 0; i < m_sourceModel->rowCount(); i++) {
342 QModelIndex const sourceIndex(m_sourceModel->index(i, 0));
343 CSwordModuleInfo & module = *static_cast<CSwordModuleInfo *>(
344 m_sourceModel->data(sourceIndex,
345 BtBookshelfModel::ModulePointerRole).value<void *>());
346 m_sourceIndexMap[&module] = sourceIndex;
347 addModule(module, checked.contains(&module), true);
348 }
349 endResetModel();
350 }
351
352 if (emitSignal)
354}
355
356void BtBookshelfTreeModel::setCheckable(bool const checkable) {
357 if (m_checkable == checkable)
358 return;
360 if (m_sourceModel == nullptr)
361 return;
362
363 // Notify views that flags changed for all items:
364 resetData();
365}
366
368 for (auto it = m_modules.constBegin(); it != m_modules.constEnd(); ++it) {
369 if (modules.contains(it.key())) {
370 setData(getIndex(*it.value()), Qt::Checked, Qt::CheckStateRole);
371 } else {
372 setData(getIndex(*it.value()), Qt::Unchecked, Qt::CheckStateRole);
373 }
374 }
375}
376
378 QModelIndexList queue;
379 queue.append(QModelIndex());
380 do {
381 QModelIndex const parent(queue.takeFirst());
382 Q_EMIT dataChanged(index(0, 0, parent),
383 index(rowCount(parent) - 1, columnCount() - 1, parent));
384 for (int i = 0; i < rowCount(parent); i++) {
385 QModelIndex const childIndex(index(i, 0, parent));
386 if (rowCount(childIndex) > 0)
387 queue.append(childIndex);
388 }
389 } while (!queue.isEmpty());
390}
391
393 bool const checked,
394 bool const inReset)
395{
396 if (m_modules.contains(&module))
397 return;
398
399 bool beginInsert = !inReset;
400 QModelIndex parentIndex;
401 {
402 auto intermediateGrouping = m_groupingOrder.list();
403 while (!intermediateGrouping.empty()) {
404 switch (intermediateGrouping.takeFirst()) {
405 case GROUP_CATEGORY:
406 parentIndex = getGroup<CategoryItem>(module,
409 break;
410 case GROUP_LANGUAGE:
414 break;
415 case GROUP_INDEXING:
419 break;
420 }
421 }
422 }
423
425 ModuleItem * const newItem = new ModuleItem(module, *this);
426 newItem->setCheckState(checked ? Qt::Checked : Qt::Unchecked);
427 int const newIndex(parentItem.indexFor(*newItem));
428
429 // Actually do the insertion:
430 if (beginInsert)
432 parentItem.insertChild(newIndex, newItem);
433 m_modules.insert(&module, newItem);
434 if (checked) // Add to checked modules cache
436
437 if (!inReset)
439
440 // Reset parent item check states, if needed:
442}
443
445 auto const it = m_modules.find(&module);
446 if (it == m_modules.end())
447 return;
448
449 Item const * i = it.value();
450
451 // Set i to be the lowest item (including empty groups) to remove:
452 BT_ASSERT(i->parent());
453 while (i->parent() != m_rootItem.get()
454 && i->parent()->children().size() <= 1)
455 i = i->parent();
456 BT_ASSERT(i);
457 BT_ASSERT(i->parent());
458
459 // Calculate item indexes:
460 int const index = i->childIndex();
461 QModelIndex const parentIndex(getIndex(*i->parent()));
462
463 // Actually remove the item:
465 delete i->parent()->children().takeAt(index);
466 m_modules.erase(it);
469
470 // Reset parent item check states, if needed:
472}
473
474Item & BtBookshelfTreeModel::getItem(QModelIndex const & index) const {
475 if (UNLIKELY(!index.isValid()))
476 return *m_rootItem;
477
478 Item * const item = static_cast<Item *>(index.internalPointer());
479 BT_ASSERT(item);
480 return *item;
481}
482
484 BookshelfModel::Item const * it = &item;
486 for (;;) {
487 int const i = it->childIndex();
488 if (i < 0)
489 break;
490 indexes.append(i);
491 it = it->parent();
492 }
493
494 QModelIndex i;
495 while (!indexes.isEmpty())
496 i = index(indexes.takeLast(), 0, i);
497 return i;
498}
499
501 bool const inReset)
502{
503 for ( ; parentIndex.isValid(); parentIndex = parentIndex.parent()) {
504 Item & parentItem = *static_cast<Item *>(parentIndex.internalPointer());
505
506 Qt::CheckState const oldState = parentItem.checkState();
507 bool haveCheckedChildren = false;
508 bool haveUncheckedChildren = false;
509 for (int i = 0; i < parentItem.children().size(); i++) {
510 Qt::CheckState const state = parentItem.children().at(i)->checkState();
511 if (state == Qt::PartiallyChecked) {
512 haveCheckedChildren = true;
514 break;
515 } else if (state == Qt::Checked) {
516 haveCheckedChildren = true;
518 break;
519 } else {
520 BT_ASSERT(state == Qt::Unchecked);
523 break;
524 }
525 }
526
527 Qt::CheckState newState;
530 newState = Qt::PartiallyChecked;
531 } else {
532 newState = Qt::Checked;
533 }
534 } else {
535 newState = Qt::Unchecked;
536 }
537 if (newState == oldState)
538 break;
539
540 parentItem.setCheckState(newState);
541
542 if (!inReset)
544 } // for ( ; parentIndex.isValid(); parentIndex = parentIndex.parent())
545}
546
547void BtBookshelfTreeModel::moduleDataChanged(QModelIndex const & topLeft,
548 QModelIndex const & bottomRight)
549{
550 BT_ASSERT(!topLeft.parent().isValid());
551 BT_ASSERT(!bottomRight.parent().isValid());
552 BT_ASSERT(topLeft.column() == 0);
553 BT_ASSERT(bottomRight.column() == 0);
554
555 for (int i = topLeft.row(); i <= bottomRight.row(); i++) {
556 QModelIndex const moduleIndex(m_sourceModel->index(i, 0, topLeft.parent()));
559 CSwordModuleInfo & module = *static_cast<CSwordModuleInfo *>(data.value<void *>());
560 QModelIndex itemIndex(getIndex(*m_modules[&module]));
561 BT_ASSERT(itemIndex.isValid());
562
564
565 /*
566 Also emit signals for parent items because the change might alter them
567 as well, e.g. isHidden()
568 */
569 do {
570 itemIndex = itemIndex.parent();
572 } while (itemIndex.isValid());
573 }
574}
575
576void BtBookshelfTreeModel::moduleInserted(QModelIndex const & parent,
577 int const start,
578 int const end)
579{
580 BT_ASSERT(start <= end);
581
582 for (int i = start; i <= end; i++) {
583 QModelIndex const moduleIndex(m_sourceModel->index(i, 0, parent));
586 CSwordModuleInfo & module = *static_cast<CSwordModuleInfo *>(data.value<void *>());
587
588 bool checked;
592 } else if (m_defaultChecked == MODULE_INDEXED) {
595 } else {
598 }
600 addModule(module, checked, false);
601 }
602}
603
604void BtBookshelfTreeModel::moduleRemoved(QModelIndex const & parent,
605 int const start,
606 int const end)
607{
608 BT_ASSERT(start <= end);
609
610 for (int i = start; i <= end; i++) {
611 QModelIndex const moduleIndex(m_sourceModel->index(i, 0, parent));
614 CSwordModuleInfo & module = *static_cast<CSwordModuleInfo *>(data.value<void*>());
616 m_sourceIndexMap.remove(&module);
617 }
618}
619
620QDataStream & operator <<(QDataStream & os,
622{
623 os << o.list().size();
624 for (auto const g : o.list())
625 os << static_cast<std::underlying_type_t<decltype(g)>>(g);
626 return os;
627}
628
629QDataStream & operator >>(QDataStream & is,
631{
632 using Size = decltype(o.list().size());
633 Size size = -1; // -1 stands for invalid
634
635 // Due to a serialization bug in BibleTime the grouping size may have been
636 // serialized as an int on Qt5 or as qsizetype on Qt6. The complex logic
637 // which follows works around this issue by attempting to parse both cases.
638 // This is not entirely future proof, but good enough for the foreseeable
639 // future, assuming that Qt does not change too much and users upgrade
640 // relatively often enough.
641 using U = std::underlying_type_t<BtBookshelfTreeModel::Group>;
642 if constexpr (std::is_same_v<int, Size>) {
643 is >> size;
644 } else {
645 // If other strange platforms need to be supported, please let us know:
646 static_assert(sizeof(int) == 4, "Platform not supported");
647 static_assert(sizeof(Size) == 8, "Platform not supported");
648
649 // The following relies on Qt providing us a datastream which only
650 // contains the serialized value and an optional ')' at the end. See
651 // QSettingsPrivate::stringToVariant() in Qt 6.8.2 for details. Assuming
652 // this, and the facts that the size can only be 0, 1 or 2, and that the
653 // values can only be 0 or 1, makes it possible for us to deduce the
654 // width of the serialized size field.
655 static constexpr Size const maxReadSize =
656 sizeof(Size) + sizeof(U) * 2 + 2;
657 char buf[maxReadSize];
658 auto readSize = is.device()->peek(buf, maxReadSize);
659 if (readSize < maxReadSize) { // Assumptions about Qt must hold
660 if (buf[readSize - 1] == ')')
661 --readSize;
662 if (readSize == 4) { // 4 bytes can only fit an (int) size:
663 int s;
664 is >> s; // Consume (int)
665 if (s == 0) // The (int) size can only be 0.
666 size = 0;
667 } else if (readSize == 8) {
668 // 8 bytes can be either hold a (Size) size of value 0, or a
669 // (int) size of value 1 followed by an (int) value.
670 int s;
671 is >> s; // Consume (int)
672 if (s == 1) { // (int) size of value 1
673 size = s;
674 } else if (s == 0) { // (Size) size
675 is >> s; // Consume other (int) half of (Size) size
676 if (s == 0) // Both (int) halves must be 0 for (Size) 0
677 size = 0;
678 }
679 } else if (readSize == 12) {
680 // 12 bytes either holds a (Size) size of value 1 and a value,
681 // or an (int) size of value 2, and two values:
682 int s;
683 is >> s; // Consume (int)
684 if (s == 2) { // (int) size of value 2
685 size = 2;
686 } else if (s >= 0 && s <= 1) { // First half is either 0 or 1
687 int s2;
688 is >> s2; // Consume other (int) half of (Size) size
689 if ((s2 ^ 0x1) == s) // Other half is the other way around
690 size = 1;
691 }
692 } else if (readSize == 16) { // must be (Size) 2 + (int) values
693 is >> size; // Consume (Size) size
694 if (size != 2) // The (Size) size can only by 2
695 size = -1;
696 } // else keep size as -1 (invalid)
697 }
698 }
699
700 if (size >= 0 && size <= 2) {
701 decltype(o.m_list) newList;
702 for (; size; --size) {
703 U v;
704 is >> v;
705 if (v < 0 || v > 1)
706 break;
707 newList.append(static_cast<BtBookshelfTreeModel::Group>(v));
708 }
709 if (!size) {
710 o.m_list = std::move(newList);
711 return is;
712 }
713 }
714
715 qWarning() << "Failed to deserialize BtBookshelfTreeModel::Grouping";
716 is.setStatus(QDataStream::ReadCorruptData);
717 o.m_list.clear();
718 return is;
719}
720
721#if 0
722namespace {
723
724template <typename SizeType>
725void testGroupingSerialization_(BtBookshelfTreeModel::Grouping const & expected)
726{
727 using U = std::underlying_type_t<BtBookshelfTreeModel::Group>;
728 auto const & list = expected.list();
729 QByteArray byteArray;
730 {
731 QBuffer buffer(&byteArray);
732 buffer.open(QIODevice::WriteOnly);
733 QDataStream os(&buffer);
734 SizeType s = list.size();
735 os << s;
736 for (auto const g : list)
737 os << static_cast<U>(g);
738 }
739 QBuffer buffer(&byteArray);
740 buffer.open(QIODevice::ReadOnly);
741 QDataStream is(&buffer);
743 is >> value;
744 if (value == expected) {
745 qInfo() << "SUCCESS" << Q_FUNC_INFO << byteArray << list;
746 } else {
747 qFatal() << "FAILURE" << Q_FUNC_INFO << byteArray << list;
748 }
749}
750
751void testGroupingSerialization(BtBookshelfTreeModel::Grouping const & expected){
752 testGroupingSerialization_<int>(expected);
753 testGroupingSerialization_<decltype(expected.list().size())>(expected);
754}
755
756} // anonymous namespace
757
758void testGroupingSerializations() {
760 testGroupingSerialization(G::NONE);
761 testGroupingSerialization(G::CAT);
762 testGroupingSerialization(G::CAT_LANG);
763 testGroupingSerialization(G::LANG);
764 testGroupingSerialization(G::LANG_CAT);
765}
766#endif
#define BT_ASSERT(...)
Definition btassert.h:17
QDataStream & operator>>(QDataStream &is, BtBookshelfTreeModel::Grouping &o)
QDataStream & operator<<(QDataStream &os, BtBookshelfTreeModel::Grouping const &o)
#define BT_CONNECT(...)
Definition btconnect.h:20
virtual QVariant data(int role=Qt::DisplayRole) const
Returns data for this item.
Definition item.cpp:44
QList< Item * > & children()
Returns the list of child items of this node.
Definition item.h:60
Type type() const
Returns the type of this item.
Definition item.h:49
void setCheckState(Qt::CheckState const state)
Sets the check state of this item.
Definition item.h:119
int childIndex() const
Returns the index of this item under its parent.
Definition item.h:66
Qt::CheckState checkState() const
Returns the check state of this item.
Definition item.h:113
Item * parent() const
Returns a pointer to the parent item of this item.
Definition item.h:55
CSwordModuleInfo & moduleInfo() const
Definition moduleitem.h:42
auto const & list() const noexcept
void saveTo(BtConfigCore &config, QString const &key) const
bool loadFrom(BtConfigCore const &config, QString const &key)
QVariant data(QModelIndex const &index, int role=Qt::DisplayRole) const override
CheckedBehavior m_defaultChecked
QModelIndex index(int row, int column, QModelIndex const &parent=QModelIndex()) const override
std::unique_ptr< BookshelfModel::Item > m_rootItem
void groupingOrderChanged(BtBookshelfTreeModel::Grouping newGrouping)
void removeModule(CSwordModuleInfo &module)
QModelIndex parent(QModelIndex const &index) const override
std::shared_ptr< QAbstractItemModel > m_sourceModel
BookshelfModel::Item & getItem(QModelIndex const &index) const
QModelIndex getIndex(BookshelfModel::Item const &item)
void setCheckable(bool checkable)
void setCheckedModules(BtConstModuleSet const &modules)
void setGroupingOrder(BtBookshelfTreeModel::Grouping const &groupingOrder, bool emitSignal=true)
BtBookshelfTreeModel(QObject *parent=nullptr)
CSwordModuleInfo * module(QModelIndex const &index) const
bool setData(QModelIndex const &index, QVariant const &value, int role) override
QList< CSwordModuleInfo * > modules() const
int columnCount(QModelIndex const &parent=QModelIndex()) const override
~BtBookshelfTreeModel() override
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
void resetParentCheckStates(QModelIndex parentIndex, bool inReset)
QModelIndex getGroup(CSwordModuleInfo const &module, QModelIndex const parentIndex, bool &beginInsert)
Qt::ItemFlags flags(QModelIndex const &index) const override
Grouping const & groupingOrder() const
bool hasChildren(QModelIndex const &parent=QModelIndex()) const override
std::shared_ptr< QAbstractItemModel > sourceModel() const noexcept
void setSourceModel(std::shared_ptr< QAbstractItemModel > sourceModel)
int rowCount(QModelIndex const &parent=QModelIndex()) const override
void moduleDataChanged(QModelIndex const &topLeft, QModelIndex const &bottomRight)
void addModule(CSwordModuleInfo &module, bool checked, bool inReset)
void moduleRemoved(QModelIndex const &parent, int start, int end)
void moduleInserted(QModelIndex const &parent, int start, int end)
void moduleChecked(CSwordModuleInfo *module, bool checked)
void setValue(QString const &key, T const &value)
Sets a value for a key.
QVariant qVariantValue(QString const &key, QVariant const &defaultValue=QVariant()) const
Returns the settings value for the given global key as a QVariant.
bool contains(CSwordModuleInfo const *const m) const
Definition btmoduleset.h:30
#define UNLIKELY(c)
Gives the compiler a hint that the given conditional is likely to evaluate to false.
Definition macros.h:44