File indexing completed on 2024-06-09 04:32:02

0001 /*
0002  * Copyright (C) 2020 Dan Leinir Turthra Jensen <admin@leinir.dk>
0003  *
0004  * This library is free software; you can redistribute it and/or
0005  * modify it under the terms of the GNU Lesser General Public
0006  * License as published by the Free Software Foundation; either
0007  * version 2.1 of the License, or (at your option) version 3, or any
0008  * later version accepted by the membership of KDE e.V. (or its
0009  * successor approved by the membership of KDE e.V.), which shall
0010  * act as a proxy defined in Section 6 of version 3 of the license.
0011  *
0012  * This library is distributed in the hope that it will be useful,
0013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015  * Lesser General Public License for more details.
0016  *
0017  * You should have received a copy of the GNU Lesser General Public
0018  * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
0019  *
0020  */
0021 
0022 #include "AcbfIdentifiedObjectModel.h"
0023 #include "AcbfBody.h"
0024 #include "AcbfDocument.h"
0025 #include "AcbfInternalReferenceObject.h"
0026 #include "AcbfData.h"
0027 #include "AcbfFrame.h"
0028 #include "AcbfJump.h"
0029 #include "AcbfReferences.h"
0030 #include "AcbfTextarea.h"
0031 
0032 using namespace AdvancedComicBookFormat;
0033 
0034 class IdentifiedObjectModel::Private {
0035 public:
0036     Private(IdentifiedObjectModel* qq)
0037         : q(qq)
0038     {}
0039     IdentifiedObjectModel* q{nullptr};
0040     Document* document{nullptr};
0041     QList<InternalReferenceObject*> identifiedObjects;
0042 
0043     void addAndConnectChild(InternalReferenceObject* child) {
0044         if (child) {
0045             int idx = identifiedObjects.count();
0046             q->beginInsertRows(QModelIndex(), idx, idx);
0047             identifiedObjects.append(child);
0048             q->endInsertRows();
0049             QObject::connect(child, &QObject::destroyed, q, [this, child](){
0050                 int idx = identifiedObjects.indexOf(child);
0051                 q->beginRemoveRows(QModelIndex(), idx, idx);
0052                 identifiedObjects.removeOne(child);
0053                 q->endRemoveRows();
0054                 child->disconnect(q);
0055             });
0056             QObject::connect(child, &InternalReferenceObject::propertyDataChanged, q, [this, child]() {
0057                 QModelIndex idx = q->index(identifiedObjects.indexOf(child));
0058                 q->dataChanged(idx, idx);
0059             });
0060 
0061             // Some special handling for pages, because pages are special and potentially contain things, including some that can also have reference objects
0062             Page* page = qobject_cast<Page*>(child);
0063             if (page) {
0064                 connect(page, &Page::jumpAdded, q, [this](QObject* child) { addAndConnectChild(qobject_cast<InternalReferenceObject*>(child)); });
0065                 connect(page, &Page::jumpsChanged, q,  [this]() { q->dataChanged(q->index(0), q->index(identifiedObjects.count())); });
0066                 for (QObject* obj: page->jumps()) {
0067                     addAndConnectChild(qobject_cast<InternalReferenceObject*>(obj));
0068                 }
0069                 connect(page, &Page::frameAdded, q, [this](QObject* child) { addAndConnectChild(qobject_cast<InternalReferenceObject*>(child)); });
0070                 connect(page, &Page::framePointStringsChanged, q, [this]() { q->dataChanged(q->index(0), q->index(identifiedObjects.count())); });
0071                 for (Frame* frame : page->frames()) {
0072                     addAndConnectChild(frame);
0073                 }
0074                 connect(page, &Page::textLayerAdded, q, [this](QObject* child) { connectTextLayer(qobject_cast<Textlayer*>(child)); });
0075                 connect(page, &Page::textLayerLanguagesChanged, q, [this](){ q->dataChanged(q->index(0), q->index(identifiedObjects.count())); });
0076                 for (Textlayer* textlayer : page->textLayersForAllLanguages()) {
0077                     connectTextLayer(textlayer);
0078                 }
0079             }
0080         }
0081     }
0082     void connectTextLayer(Textlayer* textlayer) {
0083         connect(textlayer, &Textlayer::textareaAdded, q, [this](QObject* child) { addAndConnectChild(qobject_cast<InternalReferenceObject*>(child)); });
0084         connect(textlayer, &Textlayer::textareasChanged, q, [this](){ q->dataChanged(q->index(0), q->index(identifiedObjects.count())); });
0085         for (QObject* obj : textlayer->textareas()) {
0086             Textarea* textarea = qobject_cast<Textarea*>(obj);
0087             addAndConnectChild(textarea);
0088         }
0089     }
0090 };
0091 
0092 IdentifiedObjectModel::IdentifiedObjectModel(QObject* parent)
0093     : QAbstractListModel(parent)
0094     , d(new Private(this))
0095 {
0096 }
0097 
0098 IdentifiedObjectModel::~IdentifiedObjectModel() = default;
0099 
0100 QHash<int, QByteArray> IdentifiedObjectModel::roleNames() const
0101 {
0102     static const QHash<int, QByteArray> roleNames{
0103         {IdRole, "id"},
0104         {OriginalIndexRole, "originalIndex"},
0105         {TypeRole, "type"},
0106         {ObjectRole, "object"}
0107     };
0108     return roleNames;
0109 }
0110 
0111 QVariant IdentifiedObjectModel::data(const QModelIndex& index, int role) const
0112 {
0113     QVariant data;
0114     if (checkIndex(index) && d->document) {
0115         InternalReferenceObject* object = d->identifiedObjects.value(index.row());
0116         if (object) {
0117             switch(role) {
0118                 case IdRole:
0119                     data.setValue(object->property("id"));
0120                     break;
0121                 case TypeRole:
0122                     if (qobject_cast<Reference*>(object)) {
0123                         data.setValue<int>(ReferenceType);
0124                     } else if (qobject_cast<Binary*>(object)) {
0125                         data.setValue<int>(BinaryType);
0126                     } else if (qobject_cast<Textarea*>(object)) {
0127                         data.setValue<int>(TextareaType);
0128                     } else if (qobject_cast<Frame*>(object)) {
0129                         data.setValue<int>(FrameType);
0130                     } else if (qobject_cast<Page*>(object)) {
0131                         data.setValue<int>(PageType);
0132                     } else if (qobject_cast<Jump*>(object)) {
0133                         data.setValue<int>(JumpType);
0134                     } else {
0135                         data.setValue<int>(UnknownType);
0136                     }
0137                     break;
0138                 case OriginalIndexRole:
0139                     data.setValue<int>(object->localIndex());
0140                     break;
0141                 case ObjectRole:
0142                     data.setValue<QObject*>(object);
0143                     break;
0144                 default:
0145                     break;
0146             };
0147         }
0148     }
0149     return data;
0150 }
0151 
0152 int IdentifiedObjectModel::rowCount(const QModelIndex& parent) const
0153 {
0154     if(parent.isValid()) {
0155         return 0;
0156     }
0157     return d->identifiedObjects.count();
0158 }
0159 
0160 QObject * IdentifiedObjectModel::document() const
0161 {
0162     return d->document;
0163 }
0164 
0165 void IdentifiedObjectModel::setDocument(QObject* document)
0166 {
0167     if (d->document != document) {
0168         beginResetModel();
0169         for (QObject* obj : d->identifiedObjects) {
0170             obj->disconnect(this);
0171         }
0172         d->identifiedObjects.clear();
0173         d->document = qobject_cast<Document*>(document);
0174         if (d->document) {
0175             std::function<void(const QObject* parent)> findAllIdentifiedObjects;
0176             findAllIdentifiedObjects = [&findAllIdentifiedObjects, this](const QObject *parent) {
0177                 for (QObject *child : parent->children()) {
0178                     InternalReferenceObject* refObj = qobject_cast<InternalReferenceObject*>(child);
0179                     if (refObj) {
0180                         d->addAndConnectChild(refObj);
0181                     }
0182                     findAllIdentifiedObjects(child);
0183                 }
0184             };
0185             findAllIdentifiedObjects(d->document);
0186             connect(d->document->data(), &Data::binaryAdded, this, [this](QObject* child){ d->addAndConnectChild(qobject_cast<InternalReferenceObject*>(child)); });
0187             connect(d->document->data(), &Data::binariesChanged, this, [this](){ dataChanged(index(0), index(d->identifiedObjects.count())); });
0188             connect(d->document->references(), &References::referenceAdded, this, [this](QObject* child){ d->addAndConnectChild(qobject_cast<InternalReferenceObject*>(child)); });
0189             connect(d->document->references(), &References::referencesChanged, this, [this](){ dataChanged(index(0), index(d->identifiedObjects.count())); });
0190             connect(d->document->body(), &Body::pageCountChanged, this, [this](){ dataChanged(index(0), index(d->identifiedObjects.count())); });
0191             connect(d->document->body(), &Body::pageAdded, this, [this](QObject* child) { d->addAndConnectChild(qobject_cast<InternalReferenceObject*>(child)); });
0192         }
0193         endResetModel();
0194         Q_EMIT documentChanged();
0195     }
0196 }
0197 
0198 QObject * IdentifiedObjectModel::objectById(const QString& id)
0199 {
0200     QObject* identified{nullptr};
0201     static const char* idProp{"id"};
0202     for (InternalReferenceObject* object : d->identifiedObjects) {
0203         if ((object->supportedReferenceType() & InternalReferenceObject::ReferenceTarget) == InternalReferenceObject::ReferenceTarget) {
0204             if (object->property(idProp).toString() == id) {
0205                 identified = object;
0206                 break;
0207             }
0208         }
0209     }
0210     return identified;
0211 }