1 /*********
2 *
3 * This file is part of BibleTime's source code, http://www.bibletime.info/.
4 *
5 * Copyright 1999-2016 by the BibleTime developers.
6 * The BibleTime source code is licensed under the GNU General Public License version 2.0.
7 *
8 **********/
9
10 #include "ctextrendering.h"
11
12 #include <memory>
13 #include <QRegExp>
14 #include <QtAlgorithms>
15 #include "../../util/btassert.h"
16 #include "../drivers/cswordmoduleinfo.h"
17 #include "../keys/cswordkey.h"
18 #include "../keys/cswordversekey.h"
19 #include "../managers/cdisplaytemplatemgr.h"
20 #include "../managers/referencemanager.h"
21
22 // Sword includes:
23 #include <swkey.h>
24
25
26 using namespace Rendering;
27
KeyTreeItem(const QString & key,const CSwordModuleInfo * module,const Settings & settings)28 CTextRendering::KeyTreeItem::KeyTreeItem(const QString &key,
29 const CSwordModuleInfo *module,
30 const Settings &settings)
31 : m_settings(settings),
32 m_moduleList(),
33 m_key( key ),
34 m_childList(),
35 m_stopKey( QString::null ),
36 m_alternativeContent( QString::null ) {
37 m_moduleList.append( const_cast<CSwordModuleInfo*>(module) ); //BAD CODE
38 }
39
KeyTreeItem(const QString & content,const Settings & settings)40 CTextRendering::KeyTreeItem::KeyTreeItem(const QString &content,
41 const Settings &settings)
42 : m_settings( settings ),
43 m_moduleList(),
44 m_key( QString::null ),
45 m_childList(),
46 m_stopKey( QString::null ),
47 m_alternativeContent( content ) {
48 }
49
KeyTreeItem(const QString & key,const BtConstModuleList & mods,const Settings & settings)50 CTextRendering::KeyTreeItem::KeyTreeItem(const QString &key,
51 const BtConstModuleList &mods,
52 const Settings &settings)
53 : m_settings( settings ),
54 m_moduleList( mods ),
55 m_key( key ),
56 m_childList(),
57 m_stopKey( QString::null ),
58 m_alternativeContent( QString::null ) {
59 }
60
KeyTreeItem()61 CTextRendering::KeyTreeItem::KeyTreeItem()
62 : m_settings(),
63 m_moduleList(),
64 m_key(QString::null),
65 m_childList(),
66 m_stopKey(QString::null),
67 m_alternativeContent(QString::null) {
68 }
69
KeyTreeItem(const KeyTreeItem & i)70 CTextRendering::KeyTreeItem::KeyTreeItem(const KeyTreeItem& i)
71 : m_settings( i.m_settings ),
72 m_moduleList( i.m_moduleList ),
73 m_key( i.m_key ),
74 m_childList(),
75 m_stopKey( i.m_stopKey ),
76 m_alternativeContent( i.m_alternativeContent )
77 {
78 const KeyTree &tree = *i.childList();
79 Q_FOREACH (const KeyTreeItem * const item, tree) {
80 m_childList.append(new KeyTreeItem((*item))); //deep copy
81 }
82
83 }
84
KeyTreeItem(const QString & startKey,const QString & stopKey,const CSwordModuleInfo * module,const Settings & settings)85 CTextRendering::KeyTreeItem::KeyTreeItem(const QString &startKey,
86 const QString &stopKey,
87 const CSwordModuleInfo *module,
88 const Settings &settings)
89 : m_settings( settings ),
90 m_moduleList(),
91 m_key( startKey ),
92 m_childList(),
93 m_stopKey( stopKey ),
94 m_alternativeContent( QString::null ) {
95 BT_ASSERT(module);
96 m_moduleList.append(module);
97
98 //use the start and stop key to ceate our child items
99
100 if (module->type() == CSwordModuleInfo::Bible) {
101 CSwordVerseKey start(module);
102 start.setKey(startKey);
103
104 CSwordVerseKey stop(module);
105 stop.setKey(stopKey);
106
107 if (!m_key.isEmpty() && !m_stopKey.isEmpty()) { //we have a range of keys
108 bool ok = true;
109
110 while (ok && ((start < stop) || (start == stop)) ) { //range
111 m_childList.append(
112 new KeyTreeItem(start.key(), module, KeyTreeItem::Settings(false, settings.keyRenderingFace))
113 );
114
115
116 ok = start.next(CSwordVerseKey::UseVerse);
117 }
118 }
119 else if (m_key.isEmpty()) {
120 m_childList.append( new KeyTreeItem(startKey, module, KeyTreeItem::Settings(false, settings.keyRenderingFace)) );
121 }
122 }
123 else if ((module->type() == CSwordModuleInfo::Lexicon) || (module->type() == CSwordModuleInfo::Commentary) ) {
124 m_childList.append( new KeyTreeItem(startKey, module, KeyTreeItem::Settings(false, KeyTreeItem::Settings::NoKey)) );
125 }
126 else if (module->type() == CSwordModuleInfo::GenericBook) {
127 m_childList.append( new KeyTreeItem(startKey, module, KeyTreeItem::Settings(false, KeyTreeItem::Settings::NoKey)) );
128 }
129
130 //make it into "<simple|range> (modulename)"
131
132 if (startKey == stopKey) {
133 m_alternativeContent = startKey;
134 }
135 else {
136 sword::VerseKey vk(startKey.toUtf8().constData(), stopKey.toUtf8().constData());
137
138 if (vk.getLowerBound().getBook() != vk.getUpperBound().getBook()) {
139 m_alternativeContent = QString::fromUtf8(vk.getRangeText());
140 }
141 else if (vk.getLowerBound().getChapter() != vk.getUpperBound().getChapter()) {
142 m_alternativeContent = QString("%1 - %2:%3")
143 .arg(QString::fromUtf8(vk.getLowerBound().getText()))
144 .arg(vk.getUpperBound().getChapter())
145 .arg(vk.getUpperBound().getVerse());
146 }
147 else { //only verses differ (same book, same chapter)
148 m_alternativeContent = QString("%1 - %2")
149 .arg(QString::fromUtf8(vk.getLowerBound().getText()))
150 .arg(vk.getUpperBound().getVerse());
151 }
152 }
153
154 m_alternativeContent.append(" (").append(module->name()).append(")");
155 m_alternativeContent.prepend("<div class=\"rangeheading\" dir=\"ltr\">").append("</div>"); //insert the right tags
156 }
157
collectModules(const KeyTree & tree) const158 BtConstModuleList CTextRendering::collectModules(const KeyTree &tree) const {
159 //collect all modules which are available and used by child items
160 BtConstModuleList modules;
161
162 Q_FOREACH (const KeyTreeItem * const c, tree) {
163 BT_ASSERT(c);
164 Q_FOREACH (const CSwordModuleInfo * const mod, c->modules()) {
165 if (!modules.contains(mod))
166 modules.append(mod);
167 }
168 }
169 return modules;
170 }
171
renderKeyTree(const KeyTree & tree)172 const QString CTextRendering::renderKeyTree(const KeyTree &tree) {
173 initRendering();
174
175 const BtConstModuleList modules = collectModules(tree);
176 QString t;
177
178 //optimization for entries with the same key
179
180 if (modules.count() == 1) { //this optimizes the rendering, only one key created for all items
181 std::unique_ptr<CSwordKey> key(
182 CSwordKey::createInstance(modules.first()));
183 Q_FOREACH (const KeyTreeItem * const c, tree) {
184 key->setKey(c->key());
185 t.append(renderEntry(*c, key.get()));
186 }
187 }
188 else {
189 Q_FOREACH (const KeyTreeItem * const c, tree) {
190 t.append( renderEntry( *c ) );
191 }
192 }
193
194 return finishText(t, tree);
195 }
196
renderKeyRange(const QString & start,const QString & stop,const BtConstModuleList & modules,const QString & highlightKey,const KeyTreeItem::Settings & keySettings)197 const QString CTextRendering::renderKeyRange(
198 const QString &start,
199 const QString &stop,
200 const BtConstModuleList &modules,
201 const QString &highlightKey,
202 const KeyTreeItem::Settings &keySettings)
203 {
204
205 const CSwordModuleInfo *module = modules.first();
206 //qWarning( "renderKeyRange start %s stop %s \n", start.latin1(), stop.latin1() );
207
208 std::unique_ptr<CSwordKey> lowerBound( CSwordKey::createInstance(module) );
209 lowerBound->setKey(start);
210
211 std::unique_ptr<CSwordKey> upperBound( CSwordKey::createInstance(module) );
212 upperBound->setKey(stop);
213
214 sword::SWKey* sw_start = dynamic_cast<sword::SWKey*>(lowerBound.get());
215 sword::SWKey* sw_stop = dynamic_cast<sword::SWKey*>(upperBound.get());
216
217 BT_ASSERT((*sw_start == *sw_stop) || (*sw_start < *sw_stop));
218
219 if (*sw_start == *sw_stop) { //same key, render single key
220 return renderSingleKey(lowerBound->key(), modules);
221 }
222 else if (*sw_start < *sw_stop) { // Render range
223 KeyTree tree;
224 KeyTreeItem::Settings settings = keySettings;
225
226 CSwordVerseKey* vk_start = dynamic_cast<CSwordVerseKey*>(lowerBound.get());
227 BT_ASSERT(vk_start);
228
229 CSwordVerseKey* vk_stop = dynamic_cast<CSwordVerseKey*>(upperBound.get());
230 BT_ASSERT(vk_stop);
231
232 while ((*vk_start < *vk_stop) || (*vk_start == *vk_stop)) {
233
234 //make sure the key given by highlightKey gets marked as current key
235 settings.highlight = (!highlightKey.isEmpty() ? (vk_start->key() == highlightKey) : false);
236
237 /**
238 \todo We need to take care of linked verses if we render one or
239 (esp) more modules. If the verses 2,3,4,5 are linked to 1,
240 it should be displayed as one entry with the caption 1-5.
241 */
242
243 if (vk_start->getChapter() == 0) { // range was 0:0-1:x, render 0:0 first and jump to 1:0
244 vk_start->setVerse(0);
245 tree.append( new KeyTreeItem(vk_start->key(), modules, settings) );
246 vk_start->setChapter(1);
247 vk_start->setVerse(0);
248 }
249 tree.append( new KeyTreeItem(vk_start->key(), modules, settings) );
250 if (!vk_start->next(CSwordVerseKey::UseVerse)) {
251 /// \todo Notify the user about this failure.
252 break;
253 }
254 }
255 return renderKeyTree(tree);
256 }
257
258 return QString::null;
259 }
260
renderSingleKey(const QString & key,const BtConstModuleList & modules,const KeyTreeItem::Settings & settings)261 const QString CTextRendering::renderSingleKey(
262 const QString &key,
263 const BtConstModuleList &modules,
264 const KeyTreeItem::Settings &settings)
265 {
266 KeyTree tree;
267 tree.append( new KeyTreeItem(key, modules, settings) );
268
269 return renderKeyTree(tree);
270 }
271