File indexing completed on 2024-04-28 15:35:52

0001 // SPDX-FileCopyrightText: 2023 Carl Schwan <carl@carlschwan.eu>
0002 // SPDX-License-Identifier: LGPL-2.1-only or LGPL-3.0-only or LicenseRef-KDE-Accepted-LGPL
0003 
0004 #include "tableofcontentmodel.h"
0005 #include <QJsonArray>
0006 #include <QJsonDocument>
0007 #include <QJsonObject>
0008 
0009 TreeItem::TreeItem(const QString &title, const QString &href, const QString &id, const QJsonArray &childs, TreeItem *parent)
0010     : m_parentItem(parent)
0011     , m_title(title)
0012     , m_href(href)
0013     , m_id(id)
0014 {
0015     for (const auto &jsonEntry : childs) {
0016         const auto value = jsonEntry.toObject();
0017         auto child = std::make_unique<TreeItem>(value[QStringLiteral("label")].toString(),
0018                                                 value[QStringLiteral("href")].toString(),
0019                                                 value[QStringLiteral("id")].toString(),
0020                                                 value[QStringLiteral("subitems")].toArray(),
0021                                                 this);
0022         appendChild(std::move(child));
0023     }
0024 }
0025 
0026 void TreeItem::clear()
0027 {
0028     m_childItems.clear();
0029 }
0030 
0031 void TreeItem::appendChild(std::unique_ptr<TreeItem> &&item)
0032 {
0033     m_childItems.push_back(std::move(item));
0034 }
0035 
0036 TreeItem *TreeItem::child(int row)
0037 {
0038     if (row < 0 || row >= m_childItems.size()) {
0039         return nullptr;
0040     }
0041     return m_childItems.at(row).get();
0042 }
0043 
0044 int TreeItem::childCount() const
0045 {
0046     return m_childItems.size();
0047 }
0048 
0049 int TreeItem::row() const
0050 {
0051     if (m_parentItem) {
0052         const auto it = std::find_if(m_parentItem->m_childItems.cbegin(), m_parentItem->m_childItems.cend(), [this](const std::unique_ptr<TreeItem> &treeItem) {
0053             return treeItem.get() == const_cast<TreeItem *>(this);
0054         });
0055 
0056         if (it != m_parentItem->m_childItems.cend()) {
0057             return std::distance(m_parentItem->m_childItems.cbegin(), it);
0058         }
0059         Q_ASSERT(false); // should not happen
0060         return -1;
0061     }
0062 
0063     return 0;
0064 }
0065 
0066 QVariant TreeItem::data(int role) const
0067 {
0068     switch (role) {
0069     case Qt::DisplayRole:
0070     case TableOfContentModel::TitleRole:
0071         return m_title;
0072     case TableOfContentModel::HrefRole:
0073         return m_href;
0074     case TableOfContentModel::IdRole:
0075         return m_id;
0076     default:
0077         return {};
0078     }
0079 };
0080 
0081 TreeItem *TreeItem::parentItem()
0082 {
0083     return m_parentItem;
0084 }
0085 
0086 TableOfContentModel::TableOfContentModel(QObject *parent)
0087     : QAbstractItemModel(parent)
0088     , m_rootItem(std::make_unique<TreeItem>(QString{}, QString{}, QString{}, QJsonArray{}))
0089 {
0090 }
0091 
0092 QHash<int, QByteArray> TableOfContentModel::roleNames() const
0093 {
0094     return {
0095         {TitleRole, "title"},
0096         {HrefRole, "href"},
0097         {IdRole, "id"},
0098     };
0099 }
0100 
0101 QModelIndex TableOfContentModel::index(int row, int column, const QModelIndex &parent) const
0102 {
0103     if (!hasIndex(row, column, parent)) {
0104         return {};
0105     }
0106 
0107     TreeItem *parentItem;
0108 
0109     if (!parent.isValid()) {
0110         parentItem = m_rootItem.get();
0111     } else {
0112         parentItem = static_cast<TreeItem *>(parent.internalPointer());
0113     }
0114 
0115     auto childItem = parentItem->child(row);
0116     if (childItem) {
0117         return createIndex(row, column, childItem);
0118     }
0119     return {};
0120 }
0121 
0122 QModelIndex TableOfContentModel::parent(const QModelIndex &index) const
0123 {
0124     if (!index.isValid()) {
0125         return {};
0126     }
0127 
0128     auto childItem = static_cast<TreeItem *>(index.internalPointer());
0129     auto parentItem = childItem->parentItem();
0130 
0131     if (parentItem == m_rootItem.get()) {
0132         return {};
0133     }
0134 
0135     return createIndex(parentItem->row(), 0, parentItem);
0136 }
0137 
0138 int TableOfContentModel::rowCount(const QModelIndex &parent) const
0139 {
0140     TreeItem *parentItem;
0141     if (parent.column() > 0) {
0142         return 0;
0143     }
0144     if (!parent.isValid()) {
0145         parentItem = m_rootItem.get();
0146     } else {
0147         parentItem = static_cast<TreeItem *>(parent.internalPointer());
0148     }
0149 
0150     return parentItem->childCount();
0151 }
0152 
0153 int TableOfContentModel::columnCount(const QModelIndex &parent) const
0154 {
0155     Q_UNUSED(parent)
0156     return 1;
0157 }
0158 
0159 QVariant TableOfContentModel::data(const QModelIndex &index, int role) const
0160 {
0161     Q_ASSERT(checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid));
0162 
0163     auto item = static_cast<TreeItem *>(index.internalPointer());
0164 
0165     return item->data(role);
0166 }
0167 
0168 void TableOfContentModel::importFromJson(const QByteArray &json)
0169 {
0170     auto doc = QJsonDocument::fromJson(json);
0171     Q_ASSERT(doc.isArray());
0172 
0173     const auto jsonEntries = doc.array();
0174 
0175     beginResetModel();
0176     m_rootItem->clear();
0177     for (const auto &jsonEntry : jsonEntries) {
0178         const auto value = jsonEntry.toObject();
0179         m_rootItem->appendChild(std::make_unique<TreeItem>(value[QStringLiteral("label")].toString(),
0180                                                            value[QStringLiteral("href")].toString(),
0181                                                            value[QStringLiteral("id")].toString(),
0182                                                            value[QStringLiteral("subitems")].toArray(),
0183                                                            m_rootItem.get()));
0184     }
0185     endResetModel();
0186 }
0187 
0188 #include "moc_tableofcontentmodel.cpp"