BibleTime
btconfig.cpp
Go to the documentation of this file.
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, https://bibletime.info/
6*
7* Copyright 1999-2026 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 "btconfig.h"
14
15#include <cstddef>
16#include <limits>
17#include <QByteArray>
18#include <QDir>
19#include <QFile>
20#include <QKeySequence>
21#include <QLocale>
22#include <QSettings>
23#include <QVariant>
24#include <memory>
25#include <utility>
26#include "../../util/btassert.h"
27#include "../../util/directory.h"
28#include "../btglobal.h"
29#include "../drivers/cswordmoduleinfo.h"
30#include "../language.h"
31#include "../managers/cswordbackend.h"
32
33
34// Sword includes:
35#ifdef __GNUC__
36#pragma GCC diagnostic push
37#pragma GCC diagnostic ignored "-Wextra-semi"
38#pragma GCC diagnostic ignored "-Wsuggest-override"
39#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
40#endif
41#ifdef __clang__
42#pragma clang diagnostic push
43#pragma clang diagnostic ignored "-Wsuggest-destructor-override"
44#endif
45#include <swkey.h>
46#include <swmodule.h>
47#include <listkey.h>
48#include <versekey.h> // For search scope configuration
49#ifdef __clang__
50#pragma clang diagnostic pop
51#endif
52#ifdef __GNUC__
53#pragma GCC diagnostic pop
54#endif
55
56
57#define BTCONFIG_API_VERSION 1
58namespace {
59auto const BTCONFIG_API_VERSION_KEY = QStringLiteral("btconfig_api_version");
60auto const GROUP_SESSIONS = QStringLiteral("sessions");
61auto const GROUP_SESSIONS_PREFIX = QStringLiteral("sessions/");
62auto const KEY_CURRENT_SESSION = QStringLiteral("sessions/currentSession");
63auto const KEY_SESSION_NAME = QStringLiteral("sessions/%1/name");
64} // anonymous namespace
65
66/*
67 * set the instance variable initially to 0, so it can be safely checked
68 * whether the variable has been initialized yet.
69 */
71
73
74
75BtConfig::BtConfig(const QString & settingsFile)
77 std::make_shared<QSettings>(settingsFile, QSettings::IniFormat))
78{
79 BT_ASSERT(!m_instance && "BtConfig already initialized!");
80 m_instance = this;
81
82 if (m_defaultSearchScopes.isEmpty()) {
83 m_defaultSearchScopes.insert(QT_TR_NOOP("Old testament"),
84 QStringLiteral("Gen - Mal"));
85 m_defaultSearchScopes.insert(QT_TR_NOOP("Moses/Pentateuch/Torah"),
86 QStringLiteral("Gen - Deut"));
87 m_defaultSearchScopes.insert(QT_TR_NOOP("History"),
88 QStringLiteral("Jos - Est"));
89 m_defaultSearchScopes.insert(QT_TR_NOOP("Prophets"),
90 QStringLiteral("Isa - Mal"));
91 m_defaultSearchScopes.insert(QT_TR_NOOP("New testament"),
92 QStringLiteral("Mat - Rev"));
93 m_defaultSearchScopes.insert(QT_TR_NOOP("Gospels"),
94 QStringLiteral("Mat - Joh"));
95 m_defaultSearchScopes.insert(QT_TR_NOOP("Letters/Epistles"),
96 QStringLiteral("Rom - Jude"));
97 m_defaultSearchScopes.insert(QT_TR_NOOP("Paul's Epistles"),
98 QStringLiteral("Rom - Phile"));
99 }
100
101#ifdef Q_OS_WIN
102 const double minPointSize = 14.0;
103 double pointSize = m_defaultFont.pointSizeF();
104 if (pointSize < minPointSize)
105 m_defaultFont.setPointSizeF(minPointSize);
106#endif
107
108 // Read all session keys and names:
109 auto sessionsConf = group(GROUP_SESSIONS);
110 for (auto && sessionKey : sessionsConf.childGroups()) {
111 // Skip empty//keys just in case:
112 if (sessionKey.isEmpty())
113 continue;
114
115 auto sessionName =
116 sessionsConf.value<QString>(
117 sessionKey + QStringLiteral("/name"));
118 if (!sessionName.isEmpty())
119 m_sessionNames.insert(std::move(sessionKey),
120 std::move(sessionName));
121 }
122
123 // Get current session key:
124 m_currentSessionKey = value<QString>(KEY_CURRENT_SESSION);
125
126 /*
127 If no session with the current session key exists, default to the first
128 session found. If no sessions were found, create a default session.
129 */
130 if (m_currentSessionKey.isEmpty()
132 {
133 if (m_sessionNames.isEmpty()) {
134 m_currentSessionKey = QString::number(0, 36);
135 setValue(KEY_CURRENT_SESSION, m_currentSessionKey);
136 setValue(KEY_SESSION_NAME.arg(m_currentSessionKey),
137 tr("Default Session"));
138 } else {
139 m_currentSessionKey = m_sessionNames.keys().first();
140 }
141 }
142}
143
146
147 const QString confFileName = util::directory::getUserBaseDir().absolutePath()
148 + QStringLiteral("/bibletimerc");
149 bool confExisted = QFile::exists(confFileName);
150 m_instance = new BtConfig(confFileName);
151 if (!confExisted) {
152 m_instance->setValue<int>(BTCONFIG_API_VERSION_KEY, BTCONFIG_API_VERSION);
153 return INIT_OK;
154 }
155
156 int btConfigOldApi = m_instance->value<int>(BTCONFIG_API_VERSION_KEY, 0);
157 if (btConfigOldApi == BTCONFIG_API_VERSION)
158 return INIT_OK;
159 return (btConfigOldApi < BTCONFIG_API_VERSION)
162}
163
165{ m_instance->setValue<int>(BTCONFIG_API_VERSION_KEY, BTCONFIG_API_VERSION); }
166
168 BT_ASSERT(m_instance && "BtConfig not yet initialized!");
169 return *m_instance;
170}
171
172void BtConfig::setCurrentSession(QString const & key) {
173 BT_ASSERT(!key.isEmpty());
174 BT_ASSERT(m_sessionNames.contains(key));
176
177 setValue(KEY_CURRENT_SESSION, key);
178}
179
180QString BtConfig::addSession(QString const & name) {
181 BT_ASSERT(!name.isEmpty());
182
183 // Generate a new session key:
184 QString key = QString::number(0u, 36);
185 if (m_sessionNames.contains(key)) {
186 QString keyPrefix;
187 std::size_t i = 1u;
188 for (;;) {
189 key = QString::number(i, 36);
190 if (!m_sessionNames.contains(keyPrefix + key))
191 break;
192 if (i == std::numeric_limits<std::size_t>::max()) {
193 i = 0u;
194 keyPrefix.append('_');
195 } else {
196 i++;
197 }
198 }
199 }
200 BT_ASSERT(!m_sessionNames.contains(key));
201 m_sessionNames.insert(key, name);
202
203 setValue(KEY_SESSION_NAME.arg(key), name);
204 return key;
205}
206
207
208void BtConfig::deleteSession(QString const & key) {
209 BT_ASSERT(m_sessionNames.contains(key));
211 m_sessionNames.remove(key);
212
213 remove(GROUP_SESSIONS_PREFIX + key);
214}
215
217{ return group(GROUP_SESSIONS_PREFIX + m_currentSessionKey); }
218
220 delete m_instance;
221 m_instance = nullptr;
222}
223
224void BtConfig::setModuleEncryptionKey(const QString & name,
225 const QString & key)
226{
227 BT_ASSERT(!name.isEmpty());
228 setValue(QStringLiteral("Module keys/") + name, key);
229}
230
231QString BtConfig::getModuleEncryptionKey(const QString & name) {
232 BT_ASSERT(!name.isEmpty());
233 return value<QString>(QStringLiteral("Module keys/") + name, QString());
234}
235
236BtConfig::ShortcutsMap BtConfig::getShortcuts(QString const & shortcutGroup) {
237 ShortcutsMap allShortcuts;
238 auto shortcutsConf = group(shortcutGroup);
239 for (QString const & key : shortcutsConf.childKeys()) {
240 auto const variant = shortcutsConf.qVariantValue(key);
241
242 QList<QKeySequence> shortcuts;
243 auto const typeId = variant.typeId();
244 if (typeId == QMetaType::QVariantList) { // For BibleTime before 2.9
245 for (QVariant const & shortcut : variant.toList())
246 shortcuts.append(shortcut.toString());
247 } else if (typeId == QMetaType::QStringList
248 || typeId == QMetaType::QString)
249 { // a StringList with one element is recognized as a QVariant::String
250 for (QString const & shortcut : variant.toStringList())
251 shortcuts.append(shortcut);
252 } else { // it's something we don't know, skip it
253 continue;
254 }
255
256 allShortcuts.insert(key, shortcuts);
257 }
258 return allShortcuts;
259}
260
261void BtConfig::setShortcuts(QString const & shortcutGroup,
262 ShortcutsMap const & shortcuts)
263{
264 auto shortcutsConf = group(shortcutGroup);
265 for (auto it = shortcuts.begin(); it != shortcuts.end(); ++it) {
266 // Write beautiful string lists (since 2.9):
267 /// \note saving QKeySequences directly doesn't appear to work!
268 QStringList varList;
269 for (QKeySequence const & shortcut : it.value())
270 varList.append(shortcut.toString());
271
272 if (!varList.empty())
273 shortcutsConf.setValue(it.key(), varList);
274 }
275}
276
278 FilterOptions os;
279 auto const subConf = group.group(QStringLiteral("presentation"));
280 os.footnotes = subConf.value<bool>(QStringLiteral("footnotes"), true);
281 os.strongNumbers = subConf.value<bool>(QStringLiteral("strongNumbers"), true);
282 os.headings = subConf.value<bool>(QStringLiteral("headings"), true);
283 os.morphTags = subConf.value<bool>(QStringLiteral("morphTags"), true);
284 os.lemmas = subConf.value<bool>(QStringLiteral("lemmas"), true);
285 os.redLetterWords = subConf.value<bool>(QStringLiteral("redLetterWords"), true);
286 os.hebrewPoints = subConf.value<bool>(QStringLiteral("hebrewPoints"), true);
287 os.hebrewCantillation = subConf.value<bool>(QStringLiteral("hebrewCantillation"), true);
288 os.greekAccents = subConf.value<bool>(QStringLiteral("greekAccents"), true);
289 os.textualVariants = subConf.value<bool>(QStringLiteral("textualVariants"), false);
290 os.scriptureReferences = subConf.value<bool>(QStringLiteral("scriptureReferences"), true);
291 os.morphSegmentation = subConf.value<bool>(QStringLiteral("morphSegmentation"), true);
292 return os;
293}
294
296 BtConfigCore & group)
297{
298 auto subConf = group.group(QStringLiteral("presentation"));
299 subConf.setValue(QStringLiteral("footnotes"), static_cast<bool>(os.footnotes));
300 subConf.setValue(QStringLiteral("strongNumbers"), static_cast<bool>(os.strongNumbers));
301 subConf.setValue(QStringLiteral("headings"), static_cast<bool>(os.headings));
302 subConf.setValue(QStringLiteral("morphTags"), static_cast<bool>(os.morphTags));
303 subConf.setValue(QStringLiteral("lemmas"), static_cast<bool>(os.lemmas));
304 subConf.setValue(QStringLiteral("redLetterWords"), static_cast<bool>(os.redLetterWords));
305 subConf.setValue(QStringLiteral("hebrewPoints"), static_cast<bool>(os.hebrewPoints));
306 subConf.setValue(QStringLiteral("hebrewCantillation"), static_cast<bool>(os.hebrewCantillation));
307 subConf.setValue(QStringLiteral("greekAccents"), static_cast<bool>(os.greekAccents));
308 subConf.setValue(QStringLiteral("textualVariants"), static_cast<bool>(os.textualVariants));
309 subConf.setValue(QStringLiteral("scriptureReferences"), static_cast<bool>(os.scriptureReferences));
310 subConf.setValue(QStringLiteral("morphSegmentation"), static_cast<bool>(os.morphSegmentation));
311}
312
316 auto const subConf = group.group(QStringLiteral("presentation"));
317 os.lineBreaks = subConf.value<bool>(QStringLiteral("lineBreaks"), false);
318 os.verseNumbers = subConf.value<bool>(QStringLiteral("verseNumbers"), true);
319 return os;
320}
321
323 BtConfigCore & group)
324{
325 auto subConf = group.group(QStringLiteral("presentation"));
326 subConf.setValue(QStringLiteral("lineBreaks"),
327 static_cast<bool>(os.lineBreaks));
328 subConf.setValue(QStringLiteral("verseNumbers"),
329 static_cast<bool>(os.verseNumbers));
330}
331
333 FontSettingsPair const & fontSettings)
334{
335 auto fontAsString = fontSettings.second.toString();
336
337 const QString & englishName = language.englishName();
338 BT_ASSERT(!englishName.isEmpty());
339
340 // write the language to the settings
341 setValue(QStringLiteral("fonts/") + englishName, fontAsString);
342 setValue(QStringLiteral("font standard settings/") + englishName,
343 fontSettings.first);
344
345 auto const & abbrev = language.abbrev();
346 BT_ASSERT(!abbrev.isEmpty());
347
348 // (over-)write the language to the settings using abbreviation:
349 setValue(QStringLiteral("fonts/") + abbrev, std::move(fontAsString));
350 setValue(QStringLiteral("font standard settings/") + abbrev,
351 fontSettings.first);
352
353 // Update cache:
354 m_fontCache[&language] = fontSettings;
355}
356
359 // Check the cache first:
360 auto it(m_fontCache.find(&language));
361 if (it != m_fontCache.end())
362 return *it;
363
364 // Retrieve the font from the settings
365 FontSettingsPair fontSettings;
366
367 const QString & englishName = language.englishName();
368 BT_ASSERT(!englishName.isEmpty());
369 auto const & abbrev = language.abbrev();
370 BT_ASSERT(!abbrev.isEmpty());
371
372 if (auto const v =
373 qVariantValue(QStringLiteral("font standard settings/") + abbrev);
374 v.canConvert<bool>())
375 {
376 fontSettings.first = v.value<bool>();
377 } else {
378 fontSettings.first =
379 value<bool>(
380 QStringLiteral("font standard settings/") + englishName,
381 false);
382 }
383
384 QFont font;
385 if (fontSettings.first) {
386 auto const v = qVariantValue(QStringLiteral("fonts/") + abbrev);
387 auto fontName =
388 v.canConvert<QString>()
389 ? v.value<QString>()
390 : value<QString>(QStringLiteral("fonts/") + englishName,
391 getDefaultFont().toString());
392 if (!font.fromString(std::move(fontName))) {
393 /// \todo
394 }
395 } else {
396 font = getDefaultFont();
397 }
398 fontSettings.second = font;
399
400 // Cache the value:
401 m_fontCache.insert(&language, fontSettings);
402
403 return fontSettings;
404}
405
407 auto const storedMap =
408 value<BtConfig::StringMap>(
409 QStringLiteral("properties/searchScopes"),
411 StringMap map;
412
413 // Apply translation for default search scope names:
414 for (auto it = storedMap.cbegin(); it != storedMap.cend(); ++it) {
415 if (m_defaultSearchScopes.contains(it.key())) {
416 map.insert(tr(it.key().toUtf8()), it.value());
417 } else {
418 map.insert(it.key(), it.value());
419 }
420 }
421
422 // Convert map to current locale:
423 static auto const separator = QStringLiteral("; ");
424 for (auto & data : map) {
425 sword::ListKey list = parseVerseListWithModules(data, scopeModules);
426 data.clear();
427 for (int i = 0; i < list.getCount(); i++) {
428 data.append(QString::fromUtf8(list.getElement(i)->getRangeText()));
429 data.append(separator);
430 }
431 }
432 return map;
433}
434
435void BtConfig::setSearchScopesWithCurrentLocale(const QStringList& scopeModules, StringMap searchScopes) {
436 /**
437 * We want to make sure that the search scopes are saved with english
438 * key names so loading them will always work with each locale set.
439 */
440 auto iter(searchScopes.begin());
441 while (iter != searchScopes.end()) {
442 QString &data = iter.value();
443 bool parsingWorked = true;
444 sword::ListKey list = parseVerseListWithModules(data, scopeModules);
445 data.clear();
446 for (int i = 0; i < list.getCount(); i++) {
447 sword::VerseKey * verse(dynamic_cast<sword::VerseKey *>(list.getElement(i)));
448
449 if (verse != nullptr) {
450 verse->setLocale("en");
451 data.append(QString::fromUtf8(verse->getRangeText()));
452 data.append(';');
453 } else {
454 parsingWorked = false;
455 break;
456 }
457 }
458
459 if (parsingWorked)
460 iter++;
461 else
462 iter = searchScopes.erase(iter);
463 }
464 setValue(QStringLiteral("properties/searchScopes"), searchScopes);
465}
466
468 static auto const key = QStringLiteral("GUI/booknameLanguage");
469 auto r = value<QString>(key, QLocale().name());
470
471 // Maintain backwards compatibility with BibleTime versions older than 3.1:
472 bool const updateConfig = r.contains('_');
473 r.replace('_', '-'); // BCP 47
474 if (updateConfig)
475 setValue(key, r);
476
477 return r;
478}
479
480sword::ListKey BtConfig::parseVerseListWithModules(const QString& data, const QStringList& scopeModules) {
481 for (auto const & moduleName : scopeModules) {
482 auto module = CSwordBackend::instance().findModuleByName(moduleName);
483 if (module == nullptr)
484 continue;
485 sword::VerseKey vk = module->swordModule().getKey();
486 sword::ListKey list(vk.parseVerseList(data.toUtf8(), "Genesis 1:1", true));
487 if (list.getCount() > 0)
488 return list;
489 }
490 return sword::ListKey();
491}
492
494 remove(QStringLiteral("properties/searchScopes"));
495}
496
498 auto const moduleName =
499 value<QString>(QStringLiteral("settings/defaults/") + moduleType);
500 if (moduleName.isEmpty())
501 return nullptr;
502
503 return CSwordBackend::instance().findModuleByName(moduleName);
504}
505
506void BtConfig::setDefaultSwordModuleByType(const QString &moduleType,
507 const CSwordModuleInfo * const module)
508{
509 setValue(QStringLiteral("settings/defaults/") + moduleType,
510 module != nullptr ? module->name() : QString());
511}
512
513/**
514 \todo -CDisplayWindow gets a construct method that reads from config and constructs and
515 returns the respective child window (check whether module is installed...)
516 -CDisplayWindows get a new variable "id" or something, which is a unique identifier.
517 The path in the configuration will use this id as name. (who gives out the IDs?)
518 -values are updated as they are changed, just like the rest of bibletime
519 -QMdiArea::subWindowActivated signal will trigger reading the window order and saving
520 it to the config.
521 Action Plan:
522 1. get current code to work with old session system
523 2. move complete code over to BtConfig
524 3. remove CBTConfig
525 4. implement BtConfig infrastructure for saving window configuration
526 - function to add a window
527 - function to remove a window
528 - specify how to save ordering
529 5. change CDisplayWindows to write all state changes to the configuration
530 6. implement BtConfig::readSession and callers
531 7. make session handling code work with QSetting paths instead of properties
532 8. add gui for new session handling
533 9. remove old gui for session handling
534*/
#define BT_ASSERT(...)
Definition btassert.h:17
#define BTCONFIG_API_VERSION
Definition btconfig.cpp:57
friend class BtConfig
void remove(QString const &key)
removes a key (and its children) from the current group.
BtConfigCore group(Prefix &&prefix) const &
T value(QString const &key, T const &defaultValue=T()) const
Returns the settings value for the given global key.
void setValue(QString const &key, T const &value)
Sets a value for a key.
QVariant qVariantValue(QString const &key, QVariant const &defaultValue=QVariant()) const
Returns the settings value for the given global key as a QVariant.
QMap< QString, QString > StringMap
Definition btconfig.h:48
QString getModuleEncryptionKey(const QString &name)
Function to get a module decryption key.
Definition btconfig.cpp:231
static void storeFilterOptionsToGroup(FilterOptions const &options, BtConfigCore &group)
Saves the current filter options.
Definition btconfig.cpp:295
BtConfigCore session() const
Definition btconfig.cpp:216
static FilterOptions loadFilterOptionsFromGroup(BtConfigCore const &group)
Definition btconfig.cpp:277
QFont const & getDefaultFont() const
Definition btconfig.h:192
void setFontForLanguage(Language const &language, FontSettingsPair const &fontSettings)
Set font for a language.
Definition btconfig.cpp:332
QPair< bool, QFont > FontSettingsPair
Definition btconfig.h:47
static DisplayOptions loadDisplayOptionsFromGroup(BtConfigCore const &group)
Definition btconfig.cpp:314
QHash< QString, QString > m_sessionNames
Definition btconfig.h:292
void setDefaultSwordModuleByType(const QString &moduleType, const CSwordModuleInfo *const module)
Sets the default sword module for a module type.
Definition btconfig.cpp:506
void setSearchScopesWithCurrentLocale(const QStringList &scopeModules, StringMap searchScopes)
Definition btconfig.cpp:435
QString m_currentSessionKey
Definition btconfig.h:293
static InitState initBtConfig()
Definition btconfig.cpp:144
static void destroyInstance()
Definition btconfig.cpp:219
QString addSession(const QString &name)
Creates a new session with the given name.
Definition btconfig.cpp:180
void deleteSearchScopesWithCurrentLocale()
Definition btconfig.cpp:493
static void forceMigrate()
Definition btconfig.cpp:164
static sword::ListKey parseVerseListWithModules(const QString &data, const QStringList &scopeModules)
Definition btconfig.cpp:480
void setModuleEncryptionKey(const QString &name, const QString &key)
Function to set a module decryption key.
Definition btconfig.cpp:224
QHash< Language const *, FontSettingsPair > m_fontCache
a cache for the fonts saved in the configuration file for speed
Definition btconfig.h:288
void setShortcuts(QString const &shortcutGroup, ShortcutsMap const &shortcuts)
Sets the shortcuts for the given group.
Definition btconfig.cpp:261
static BtConfig * m_instance
singleton instance
Definition btconfig.h:285
QFont m_defaultFont
default font used when no special one is set
Definition btconfig.h:287
FontSettingsPair getFontForLanguage(Language const &language)
Get font for a language.
Definition btconfig.cpp:358
@ INIT_NEED_UNIMPLEMENTED_FORWARD_MIGRATE
Definition btconfig.h:56
@ INIT_NEED_UNIMPLEMENTED_BACKWARD_MIGRATE
Definition btconfig.h:54
@ INIT_OK
Definition btconfig.h:55
void setCurrentSession(const QString &key)
Notifies the configuration system that the session settings should be read from and saved to the give...
Definition btconfig.cpp:172
void deleteSession(const QString &key)
Deletes the session with the given key.
Definition btconfig.cpp:208
QString booknameLanguage()
Definition btconfig.cpp:467
CSwordModuleInfo * getDefaultSwordModuleByType(const QString &moduleType)
Returns default sword module info class for a given module type.
Definition btconfig.cpp:497
ShortcutsMap getShortcuts(QString const &shortcutGroup)
Gets the shortcuts for the given group.
Definition btconfig.cpp:236
static void storeDisplayOptionsToGroup(DisplayOptions const &options, BtConfigCore &group)
Saves the current display options.
Definition btconfig.cpp:322
static StringMap m_defaultSearchScopes
Definition btconfig.h:290
static BtConfig & getInstance()
Definition btconfig.cpp:167
StringMap getSearchScopesForCurrentLocale(const QStringList &scopeModules)
Definition btconfig.cpp:406
CSwordModuleInfo * findModuleByName(const QString &name) const
Searches for a module with the given name.
static CSwordBackend & instance() noexcept
QString const & name() const
QString const & abbrev() const
Definition language.h:34
QString const & englishName() const noexcept
Definition language.h:43
QStringList r(content.left(bodyIndex))
const QDir & getUserBaseDir()
int morphSegmentation
Definition btglobal.h:37
int hebrewCantillation
Definition btglobal.h:32
int textualVariants
Definition btglobal.h:34
int hebrewPoints
Definition btglobal.h:31
int scriptureReferences
Definition btglobal.h:36
int greekAccents
Definition btglobal.h:33
int redLetterWords
Definition btglobal.h:35
int strongNumbers
Definition btglobal.h:27