1 /*********
2 *
3 * In the name of the Father, and of the Son, and of the Holy Spirit.
4 *
5 * This file is part of BibleTime's source code, http://www.bibletime.info/.
6 *
7 * Copyright 1999-2016 by the BibleTime developers.
8 * The BibleTime source code is licensed under the GNU General Public License
9 * version 2.0.
10 *
11 **********/
12 
13 #include "btwindowinterface.h"
14 
15 #include <QDebug>
16 #include <QFile>
17 #include <QObject>
18 #include <QQmlContext>
19 #include <QQmlEngine>
20 #include <QQuickItem>
21 #include <QStringList>
22 #include <swkey.h>
23 #include "backend/config/btconfig.h"
24 #include "backend/drivers/cswordbiblemoduleinfo.h"
25 #include "backend/drivers/cswordbookmoduleinfo.h"
26 #include "backend/drivers/cswordlexiconmoduleinfo.h"
27 #include "backend/drivers/cswordmoduleinfo.h"
28 #include "backend/keys/cswordkey.h"
29 #include "backend/keys/cswordtreekey.h"
30 #include "backend/managers/cswordbackend.h"
31 #include "backend/models/btmoduletextmodel.h"
32 #include "backend/rendering/cdisplayrendering.h"
33 #include "backend/rendering/centrydisplay.h"
34 #include "mobile/btmmain.h"
35 #include "mobile/keychooser/bookkeychooser.h"
36 #include "mobile/keychooser/keynamechooser.h"
37 #include "mobile/keychooser/versechooser.h"
38 #include "mobile/ui/modulechooser.h"
39 #include "mobile/ui/viewmanager.h"
40 #include "util/btconnect.h"
41 
42 
43 namespace btm {
44 
BtWindowInterface(QObject * parent)45 BtWindowInterface::BtWindowInterface(QObject* parent)
46     : QObject(parent),
47       m_key(nullptr),
48       m_textModel(new RoleItemModel()),
49       m_moduleTextModel(new BtModuleTextModel(this)),
50       m_bookKeyChooser(nullptr),
51       m_keyNameChooser(nullptr),
52       m_verseKeyChooser(nullptr),
53       m_historyIndex(-1) {
54 
55     ViewManager* viewManager = getViewManager();
56     if (viewManager == nullptr)
57         return;
58     QtQuick2ApplicationViewer* viewer = viewManager->getViewer();
59     m_verseKeyChooser = new VerseChooser(viewer, this);
60 
61     BT_CONNECT(m_verseKeyChooser, SIGNAL(referenceChanged()),
62                this,              SLOT(referenceChosen()));
63 
64     m_bookKeyChooser = new BookKeyChooser(viewer, this);
65     BT_CONNECT(m_bookKeyChooser, SIGNAL(referenceChanged()),
66                this,             SLOT(referenceChosen()));
67 
68     m_keyNameChooser = new KeyNameChooser(viewer, this);
69     BT_CONNECT(m_keyNameChooser, SIGNAL(referenceChanged(int)),
70                this,             SLOT(referenceChosen(int)));
71 
72     BT_CONNECT(CSwordBackend::instance(),
73                SIGNAL(sigSwordSetupChanged(CSwordBackend::SetupChangedReason)),
74                this,
75                SLOT(reloadModules(CSwordBackend::SetupChangedReason)));
76 }
77 
reloadModules(CSwordBackend::SetupChangedReason)78 void BtWindowInterface::reloadModules(CSwordBackend::SetupChangedReason /* reason */ ) {
79     //first make sure all used Sword modules are still present
80 
81     if (CSwordBackend::instance()->findModuleByName(m_moduleName)) {
82         QString moduleName = m_moduleName;
83         m_moduleName = "";
84         setModuleName(moduleName);
85         ;
86     } else {
87         // close window ?
88     }
89 }
90 
updateModel()91 void BtWindowInterface::updateModel() {
92     QString moduleName= getModuleName();
93     QStringList moduleList = QStringList() << moduleName;
94     QList<const CSwordModuleInfo*> modules =
95             CSwordBackend::instance()->getConstPointerList(moduleList);
96 }
97 
moduleIsBook(const CSwordModuleInfo * module)98 static bool moduleIsBook(const CSwordModuleInfo* module) {
99     CSwordModuleInfo::Category category = module->category();
100     if (category == CSwordModuleInfo::Books)
101         return true;
102     return false;
103 }
104 
moduleIsLexicon(const CSwordModuleInfo * module)105 static bool moduleIsLexicon(const CSwordModuleInfo* module) {
106     CSwordModuleInfo::Category category = module->category();
107     if (category == CSwordModuleInfo::Lexicons ||
108         category == CSwordModuleInfo::DailyDevotional)
109         return true;
110     return false;
111 }
112 
moduleIsBibleOrCommentary(const CSwordModuleInfo * module)113 static bool moduleIsBibleOrCommentary(const CSwordModuleInfo* module) {
114     CSwordModuleInfo::Category category = module->category();
115     if (category == CSwordModuleInfo::Bibles ||
116             category == CSwordModuleInfo::Cult ||
117             category == CSwordModuleInfo::Commentaries)
118         return true;
119     return false;
120 }
121 
getCurrentModelIndex() const122 int BtWindowInterface::getCurrentModelIndex() const {
123     if (m_key == nullptr)
124         return 0;
125     if (moduleIsBibleOrCommentary(module())) {
126         CSwordVerseKey* verseKey = dynamic_cast<CSwordVerseKey*>(m_key);
127         int index = m_moduleTextModel->verseKeyToIndex(*verseKey);
128         return index;
129     }
130     else if (moduleIsBook(module())) {
131         const CSwordBookModuleInfo *m = qobject_cast<const CSwordBookModuleInfo*>(module());
132         CSwordTreeKey key(m->tree(), m);
133         QString keyName = m_key->key();
134         key.setKey(keyName);
135         CSwordTreeKey p(key);
136         p.root();
137         if(p != key)
138             return key.getIndex()/4;
139     }
140     else if (moduleIsLexicon(module())){        const CSwordLexiconModuleInfo *li = qobject_cast<const CSwordLexiconModuleInfo*>(m_key->module());
141         int index = li->entries().indexOf(m_key->key());
142         return index;
143     }
144     return 0;
145 }
146 
getModuleLanguage() const147 QString BtWindowInterface::getModuleLanguage() const {
148     QString language;
149     if (m_key)
150         language = m_key->module()->language()->englishName();
151     return language;
152 }
153 
getModuleName() const154 QString BtWindowInterface::getModuleName() const {
155     QString moduleName;
156     if (m_key)
157         moduleName = m_key->module()->name();
158     return moduleName;
159 }
160 
setReference(const QString & key)161 void BtWindowInterface::setReference(const QString& key) {
162     if (m_key && m_key->key() == key)
163         return;
164     if (m_key) {
165         CSwordVerseKey* verseKey = dynamic_cast<CSwordVerseKey*>(m_key);
166         if (verseKey)
167             verseKey->setIntros(true);
168         m_key->setKey(key);
169         referenceChanged();
170     }
171 }
172 
moduleNameChanged(const QString & moduleName)173 void BtWindowInterface::moduleNameChanged(const QString& moduleName)
174 {
175     setModuleName(moduleName);
176     setHistoryPoint();
177 }
178 
setModuleToBeginning()179 void BtWindowInterface::setModuleToBeginning() {
180     if (moduleIsBibleOrCommentary(m_key->module())) {
181          CSwordVerseKey* verseKey = dynamic_cast<CSwordVerseKey*>(m_key);
182          verseKey->setPosition(sword::TOP);
183          emit referenceChange();
184     }
185 }
186 
setModuleName(const QString & moduleName)187 void BtWindowInterface::setModuleName(const QString& moduleName) {
188     if (m_key && m_moduleName == moduleName)
189         return;
190     if (moduleName.isEmpty())
191         return;
192     m_moduleName = moduleName;
193     CSwordModuleInfo* m = CSwordBackend::instance()->findModuleByName(moduleName);
194     if (!m_key) {
195         m_key = CSwordKey::createInstance(m);
196     }
197     else {
198         if (moduleIsBibleOrCommentary(m) &&
199                 moduleIsBibleOrCommentary(m_key->module())) {
200             m_key->setModule(m);
201         }
202         else if (moduleIsBook(m) &&
203                  moduleIsBook(m_key->module())) {
204             m_key->setModule(m);
205         }
206 
207         else {
208             delete m_key;
209             m_key = CSwordKey::createInstance(m);
210         }
211 
212     }
213 
214     CSwordTreeKey* treeKey = dynamic_cast<CSwordTreeKey*>(m_key);
215     if (treeKey)
216         treeKey->firstChild();
217 
218     QStringList moduleNames;
219     moduleNames.append(moduleName);
220     m_moduleTextModel->setModules(moduleNames);
221 
222     emit moduleChanged();
223     emit referenceChange();
224     updateModel();
225 }
226 
getReference() const227 QString BtWindowInterface::getReference() const {
228     QString reference;
229     if (m_key)
230         reference = m_key->key();
231     return reference;
232 }
233 
changeModule()234 void BtWindowInterface::changeModule() {
235     QtQuick2ApplicationViewer* viewer = getViewManager()->getViewer();
236     ModuleChooser* dlg = new ModuleChooser(viewer, this);
237     dlg->open();
238 }
239 
updateCurrentModelIndex()240 void BtWindowInterface::updateCurrentModelIndex() {
241     emit currentModelIndexChanged();
242 }
243 
updateTextFonts()244 void BtWindowInterface::updateTextFonts() {
245     emit textChanged();
246 }
247 
parseKey(CSwordTreeKey * currentKey,QStringList * keyPath,QStringList * children)248 static void parseKey(CSwordTreeKey* currentKey, QStringList* keyPath, QStringList* children)
249 {
250     if (currentKey == nullptr)
251         return;
252 
253     CSwordTreeKey localKey(*currentKey);
254 
255     QString oldKey = localKey.key(); //string backup of key
256 
257     if (oldKey.isEmpty()) { //don't set keys equal to "/", always use a key which may have content
258         localKey.firstChild();
259         oldKey = localKey.key();
260     }
261 
262     QStringList siblings; //split up key
263     if (!oldKey.isEmpty()) {
264         siblings = oldKey.split('/', QString::SkipEmptyParts);
265     }
266 
267     int depth = 0;
268     int index = 0;
269     localKey.root(); //start iteration at root node
270 
271     while ( localKey.firstChild() && (depth < siblings.count()) ) {
272         QString key = localKey.key();
273         index = (depth == 0) ? -1 : 0;
274 
275         bool found = false;
276         do { //look for matching sibling
277             ++index;
278             found = (localKey.getLocalNameUnicode() == siblings[depth]);
279         }
280         while (!found && localKey.nextSibling());
281 
282         if (found)
283             key = localKey.key(); //found: change key to this level
284         else
285             localKey.setKey(key); //not found: restore old key
286 
287         *keyPath << key;
288 
289         //last iteration: get child entries
290         if (depth == siblings.count() - 1 && localKey.hasChildren()) {
291             localKey.firstChild();
292             ++depth;
293             do {
294                 *children << localKey.getLocalNameUnicode();
295             }
296             while (localKey.nextSibling());
297         }
298         depth++;
299     }
300 }
301 
getEnglishKey(CSwordKey * m_key)302 static QString getEnglishKey(CSwordKey* m_key) {
303     sword::VerseKey * vk = dynamic_cast<sword::VerseKey*>(m_key);
304     QString oldLang;
305     if (vk) {
306         // Save keys in english only:
307         const QString oldLang = QString::fromLatin1(vk->getLocale());
308         vk->setLocale("en");
309         QString englishKey = m_key->key();
310         vk->setLocale(oldLang.toLatin1());
311         return englishKey;
312     } else {
313         return m_key->key();
314     }
315 }
316 
saveWindowStateToConfig(int windowIndex)317 void BtWindowInterface::saveWindowStateToConfig(int windowIndex) {
318     const QString windowKey = QString::number(windowIndex);
319     const QString windowGroup = "window/" + windowKey + '/';
320 
321     BtConfig & conf = btConfig();
322     conf.beginGroup(windowGroup);
323     conf.setSessionValue("key", getEnglishKey(m_key));
324     QStringList modules;
325     QString moduleName = getModuleName();
326     modules.append(moduleName);
327     conf.setSessionValue("modules", modules);
328     conf.endGroup();
329 }
330 
changeReference()331 void BtWindowInterface::changeReference() {
332     CSwordVerseKey* verseKey = dynamic_cast<CSwordVerseKey*>(m_key);
333     if (verseKey != nullptr) {
334         m_verseKeyChooser->open(verseKey);
335     }
336 
337     CSwordTreeKey* treeKey = dynamic_cast<CSwordTreeKey*>(m_key);
338     if (treeKey != nullptr) {
339         QStringList keyPath;
340         QStringList children;
341         parseKey(treeKey, &keyPath, &children);
342         m_bookKeyChooser->open();
343     }
344     CSwordLDKey* lexiconKey = dynamic_cast<CSwordLDKey*>(m_key);
345     if (lexiconKey != nullptr) {
346         m_keyNameChooser->open(m_moduleTextModel);
347     }
348 }
349 
referenceChanged()350 void BtWindowInterface::referenceChanged() {
351     emit referenceChange();
352 }
353 
referenceChosen()354 void BtWindowInterface::referenceChosen() {
355     emit referenceChange();
356     updateModel();
357     emit currentModelIndexChanged();
358     setHistoryPoint();
359 }
360 
referenceChosen(int index)361 void BtWindowInterface::referenceChosen(int index) {
362     updateKeyText(index);
363     QString keyName = m_moduleTextModel->indexToKeyName(index);
364     m_key->setKey(keyName);
365     setReference(keyName);
366     emit currentModelIndexChanged();
367     setHistoryPoint();
368 }
369 
module() const370 const CSwordModuleInfo* BtWindowInterface::module() const {
371     const CSwordModuleInfo* module = m_key->module();
372     return module;
373 }
374 
getKey() const375 CSwordKey* BtWindowInterface::getKey() const {
376     return m_key;
377 }
378 
getFontName() const379 QString BtWindowInterface::getFontName() const {
380     const CSwordModuleInfo* m = module();
381     if (m == nullptr)
382         return QString();
383     const CLanguageMgr::Language* lang = m->language();
384     if (lang == nullptr)
385         return QString();
386     BtConfig::FontSettingsPair fontPair = btConfig().getFontForLanguage(*lang);
387     if (fontPair.first) {
388         QFont font = fontPair.second;
389         QString fontName = font.family();
390         return fontName;
391     }
392     QFont font = getDefaultFont();
393     QString fontName = font.family();
394     return fontName;
395 }
396 
getFontSize() const397 int BtWindowInterface::getFontSize() const {
398     const CLanguageMgr::Language* lang = module()->language();
399     BtConfig::FontSettingsPair fontPair = btConfig().getFontForLanguage(*lang);
400     if (fontPair.first) {
401         QFont font = fontPair.second;
402         int fontPointSize = font.pointSize();
403         return fontPointSize;
404     }
405     int fontPointSize = btConfig().value<int>("ui/textFontSize",22);
406     return fontPointSize;
407 }
408 
setFontSize(int size)409 void BtWindowInterface::setFontSize(int size) {
410     const CLanguageMgr::Language* lang = module()->language();
411     BtConfig::FontSettingsPair fontPair = btConfig().getFontForLanguage(*lang);
412     fontPair.second.setPointSize(size);
413     btConfig().setFontForLanguage(*lang, fontPair);
414     emit textChanged();
415 }
416 
getTextModel()417 QVariant BtWindowInterface::getTextModel() {
418     QVariant var;
419     var.setValue(m_moduleTextModel);
420     return var;
421 }
422 
updateKeyText(int index)423 void BtWindowInterface::updateKeyText(int index) {
424     QString keyName = m_moduleTextModel->indexToKeyName(index);
425     setReference(keyName);
426 }
427 
getHighlightWords() const428 QString BtWindowInterface::getHighlightWords() const {
429     return m_highlightWords;
430 }
431 
setHighlightWords(const QString & words)432 void BtWindowInterface::setHighlightWords(const QString& words) {
433     m_highlightWords = words;
434     m_moduleTextModel->setHighlightWords(words);
435 }
436 
setHistoryPoint()437 void BtWindowInterface::setHistoryPoint() {
438     History history;
439     while (  m_history.count()>0  &&  (m_historyIndex<m_history.count()-1)  )
440         m_history.pop_back();
441     history.moduleName = getModuleName();
442     history.reference = getReference();
443     m_history.append(history);
444     m_historyIndex = m_history.count() - 1;
445     emit historyChanged();
446 }
447 
getHistoryForwardVisible() const448 bool BtWindowInterface::getHistoryForwardVisible() const {
449     return m_historyIndex < (m_history.count() - 1);
450 }
451 
getHistoryBackwardVisible() const452 bool BtWindowInterface::getHistoryBackwardVisible() const {
453     return (m_historyIndex > 0) && (m_history.count() > 1);
454 }
455 
moveHistoryBackward()456 void BtWindowInterface::moveHistoryBackward() {
457     if ( ! getHistoryBackwardVisible())
458         return;
459     m_historyIndex--;
460     History history = m_history.at(m_historyIndex);
461     setModuleName(history.moduleName);
462     setReference(history.reference);
463     emit currentModelIndexChanged();
464     emit historyChanged();
465 }
466 
moveHistoryForward()467 void BtWindowInterface::moveHistoryForward() {
468     if ( ! getHistoryForwardVisible())
469         return;
470     m_historyIndex++;
471     History history = m_history.at(m_historyIndex);
472     setModuleName(history.moduleName);
473     setReference(history.reference);
474     emit currentModelIndexChanged();
475     emit historyChanged();
476 }
477 
478 } // end namespace
479