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