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