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