1 /*
2  * Copyright 2018  Wolthera van Hövell tot Westerflier <griffinvalley@gmail.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) version 3, or any
8  * later version accepted by the membership of KDE e.V. (or its
9  * successor approved by the membership of KDE e.V.), which shall
10  * act as a proxy defined in Section 6 of version 3 of the license.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
19  *
20  */
21 
22 #include "AcbfReferences.h"
23 #include <QTimer>
24 #include <QXmlStreamReader>
25 
26 #include <acbf_debug.h>
27 
28 using namespace AdvancedComicBookFormat;
29 
30 class References::Private {
31 public:
Private(References * qq)32     Private(References* qq)
33         : q(qq)
34     {}
35     References* q;
36     QMultiHash<QString, Reference*> referencesById;
37     QObjectList references;
38 
addReference(Reference * reference,bool emitListChangeSignal=true)39     void addReference(Reference* reference, bool emitListChangeSignal = true) {
40         referencesById.insert(reference->id(), reference);
41         references << reference;
42         QObject::connect(reference, &Reference::languageChanged, q, &References::referencesChanged);
43         QObject::connect(reference, &Reference::paragraphsChanged, q, &References::referencesChanged);
44         QObject::connect(reference, &Reference::idChanged, q, [this, reference](){
45             QMutableHashIterator<QString, Reference*> iterator(referencesById);
46             while(iterator.findNext(reference)) {
47                 iterator.remove();
48             }
49             referencesById.insert(reference->id(), reference);
50             Q_EMIT q->referencesChanged();
51         });
52         QObject::connect(reference, &QObject::destroyed, q, [this, reference](){
53             referencesById.remove(referencesById.key(reference));
54             references.removeAll(reference);
55             Q_EMIT q->referencesChanged();
56         });
57         Q_EMIT q->referenceAdded(reference);
58         if (emitListChangeSignal) {
59             Q_EMIT q->referencesChanged();
60         }
61     }
62 };
63 
References(Document * parent)64 References::References(Document* parent)
65     : QObject(parent)
66     , d(new Private(this))
67 {
68     static const int typeId = qRegisterMetaType<References*>("References*");
69     Q_UNUSED(typeId);
70 }
71 
72 References::~References() = default;
73 
toXml(QXmlStreamWriter * writer)74 void References::toXml(QXmlStreamWriter* writer) {
75     writer->writeStartElement(QStringLiteral("references"));
76 
77     for(QObject* reference : d->references) {
78         qobject_cast<Reference*>(reference)->toXml(writer);
79     }
80     writer->writeEndElement();
81 }
82 
fromXml(QXmlStreamReader * xmlReader)83 bool References::fromXml(QXmlStreamReader *xmlReader)
84 {
85     qDeleteAll(d->references);
86     while(xmlReader->readNextStartElement())
87     {
88         if(xmlReader->name() == QStringLiteral("reference"))
89         {
90             Reference* newReference = new Reference(this);
91             if(!newReference->fromXml(xmlReader)) {
92                 return false;
93             }
94             d->addReference(newReference, false);
95         }
96         else
97         {
98             qCWarning(ACBF_LOG) << Q_FUNC_INFO << "currently unsupported subsection:" << xmlReader->name();
99             xmlReader->skipCurrentElement();
100         }
101     }
102 
103     if (xmlReader->hasError()) {
104         qCWarning(ACBF_LOG) << Q_FUNC_INFO << "Failed to read ACBF XML document at token" << xmlReader->name() << "(" << xmlReader->lineNumber() << ":" << xmlReader->columnNumber() << ") The reported error was:" << xmlReader->errorString();
105     }
106 
107     qCDebug(ACBF_LOG) << Q_FUNC_INFO << "Created reference section with" << d->references.count() << "references";
108     Q_EMIT referencesChanged();
109 
110     return !xmlReader->hasError();
111 }
112 
reference(const QString & id) const113 Reference* References::reference(const QString& id) const
114 {
115     return d->referencesById.value(id);
116 }
117 
addReference(const QString & id,const QStringList & paragraphs,const QString & language)118 Reference* References::addReference(const QString& id, const QStringList& paragraphs, const QString& language)
119 {
120     Reference* ref = new Reference(this);
121     ref->setId(id);
122     ref->setParagraphs(paragraphs);
123     ref->setLanguage(language);
124     d->addReference(ref);
125     return ref;
126 }
127 
referenceIds() const128 QStringList References::referenceIds() const
129 {
130     return d->referencesById.keys();
131 }
132 
references() const133 QObjectList References::references() const
134 {
135     return d->references;
136 }
137 
referenceIndex(Reference * reference) const138 int References::referenceIndex(Reference* reference) const
139 {
140     return d->references.indexOf(reference);
141 }
142 
swapReferences(QObject * swapThis,QObject * withThis)143 void References::swapReferences(QObject* swapThis, QObject* withThis)
144 {
145     int first = d->references.indexOf(swapThis);
146     int second = d->references.indexOf(withThis);
147     swapReferencesByIndex(first, second);
148 }
149 
swapReferencesByIndex(int swapThis,int withThis)150 void References::swapReferencesByIndex(int swapThis, int withThis)
151 {
152     if (swapThis > -1 && swapThis < d->references.count() && withThis > -1 && withThis < d->references.count()) {
153         d->references.swapItemsAt(swapThis, withThis);
154         InternalReferenceObject* first = qobject_cast<InternalReferenceObject*>(d->references[swapThis]);
155         InternalReferenceObject* second = qobject_cast<InternalReferenceObject*>(d->references[withThis]);
156         Q_EMIT first->propertyDataChanged();
157         Q_EMIT second->propertyDataChanged();
158         Q_EMIT referencesChanged();
159     } else {
160         qCWarning(ACBF_LOG) << "There was an attempt to swap two references, and at least one of them was outside the bounds of the current list of references:" << swapThis << withThis;
161     }
162 }
163