BibleTime
bibletime.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 "bibletime.h"
14
15#include <cmath>
16#include <cstdlib>
17#include <exception>
18#include <random>
19#include <QAction>
20#include <QApplication>
21#include <QCloseEvent>
22#include <QDebug>
23#include <QInputDialog>
24#include <QLabel>
25#include <QMdiSubWindow>
26#include <QSplashScreen>
27#include <QSplitter>
28#ifdef BUILD_TEXT_TO_SPEECH
29#include <QTextToSpeech>
30#endif
31#include <type_traits>
32#include "../backend/config/btconfig.h"
33#include "../backend/drivers/cswordmoduleinfo.h"
34#include "../backend/keys/cswordversekey.h"
35#include "../backend/managers/cswordbackend.h"
36#include "../util/btassert.h"
37#include "../util/cresmgr.h"
38#include "../util/directory.h"
39#include "bibletimeapp.h"
40#include "btaboutmoduledialog.h"
43#include "cmdiarea.h"
51#include "messagedialog.h"
53
54
55namespace {
56
57template <typename T>
58auto randInt(T min, T max)
59 -> std::enable_if_t<std::is_integral_v<std::decay_t<T>>, T>
60{
61 static std::mt19937 rng((std::random_device()()));
62 return std::uniform_int_distribution<std::decay_t<T>>(min, max)(rng);
63}
64
65QString const splashes[] = {
66 QStringLiteral("startuplogo.png"),
67 QStringLiteral("startuplogo_christmas.png"),
68 QStringLiteral("startuplogo_easter.jpg"),
69};
70
71auto const splashHtml =
72 QStringLiteral(
73 "<div style='background:transparent;color:white;font-weight:bold'>"
74 "%1</div>");
75
76} // anonymous namespace
77
79
80BibleTime::BibleTime(BibleTimeApp & app, QWidget *parent, Qt::WindowFlags flags)
81 : QMainWindow(parent, flags)
82{
83 namespace DU = util::directory;
84
86 m_instance = this;
87
88 QSplashScreen * splash = nullptr;
89 constexpr static auto const splashTextAlignment =
90 Qt::AlignHCenter | Qt::AlignTop;
91
92 if (btConfig().value<bool>(QStringLiteral("GUI/showSplashScreen"), true)) {
93 auto const splashNumber =
94 randInt<std::size_t>(0u,
95 std::extent_v<decltype(splashes)> - 1u);
96 QPixmap pm;
97 if (pm.load(DU::getPicsDir().filePath(splashes[splashNumber]))) {
98 splash = new QSplashScreen(pm);
99 splash->showMessage(
100 splashHtml.arg(tr("Initializing the SWORD engine...")),
101 splashTextAlignment);
102 splash->show();
103 qApp->processEvents();
104 } else {
105 qWarning("Can't load startuplogo! Check your installation.");
106 }
107 }
108 app.initBackends();
109
110 if (splash) {
111 splash->showMessage(
112 splashHtml.arg(
113 tr("Creating BibleTime's user interface...")),
114 splashTextAlignment);
115 qApp->processEvents();
116 }
117 initView();
118
119 if (splash) {
120 splash->showMessage(
121 splashHtml.arg(tr("Initializing menu- and toolbars...")),
122 splashTextAlignment);
123 qApp->processEvents();
124 splash->setAttribute(Qt::WA_DeleteOnClose);
125 splash->finish(this);
126 }
127 initActions();
128 initMenubar();
129 initToolbars();
131
132 setWindowTitle(QStringLiteral("BibleTime " BT_VERSION));
133 setWindowIcon(CResMgr::mainWindow::icon());
135}
136
138 // delete m_dcopInterface;
139 // The backend is deleted by the BibleTimeApp instance
140
141 delete m_debugWindow;
143 saveProfile();
144}
145
146/** \brief Creates a new presenter in the MDI area according to the type of the
147 module. */
149 QList<CSwordModuleInfo *> modules,
150 QString const & key)
151{
152 qApp->setOverrideCursor(QCursor(Qt::WaitCursor));
153
154 CDisplayWindow * displayWindow;
155 switch (modules.first()->type()) {
157 displayWindow = new CBibleReadWindow(modules, key, m_mdi);
158 break;
160 displayWindow = new CCommentaryReadWindow(modules, key, m_mdi);
161 break;
163 displayWindow = new CLexiconReadWindow(modules, key, m_mdi);
164 break;
166 displayWindow = new CBookReadWindow(modules, key, m_mdi);
167 break;
168 default:
169 qFatal("unknown module type");
170 std::terminate();
171 }
172 m_mdi->addDisplayWindow(displayWindow);
173 displayWindow->show();
174
175 /* We have to process pending events here, otherwise displayWindow is not
176 fully painted. */
177 qApp->processEvents();
178 qApp->restoreOverrideCursor();
179 return displayWindow;
180}
181
182
183/** \brief Creates a new presenter in the MDI area according to the type of the
184 module. */
186 CSwordModuleInfo * const module,
187 QString const & key)
188{ return createReadDisplayWindow(QList<CSwordModuleInfo*>() << module, key); }
189
190bool BibleTime::moduleUnlock(CSwordModuleInfo * module, QWidget * const parent){
191 BT_ASSERT(module);
192
193 /// \todo Write a proper unlocking dialog with integrated error messages.
194 BtMessageInputDialog unlockKeyInputDialog(
195 tr("Unlock Work"),
196 tr("Enter the unlock key for %1.").arg(module->name()),
199 module->getUnlockInfo(),
200 parent);
201
202 while (unlockKeyInputDialog.exec() == QDialog::Accepted) {
203 module->unlock(unlockKeyInputDialog.getUserInput());
204
205 /// \todo refactor this module reload
206 /* There is currently a deficiency in SWORD 1.8.1 in that
207 backend->setCipherKey() does not work correctly for modules from
208 which data was already fetched. Therefore we have to reload the
209 modules. */
210 {
211 auto const moduleName(module->name());
212 auto & backend = CSwordBackend::instance();
213 backend.reloadModules();
214 module = backend.findModuleByName(moduleName);
215 BT_ASSERT(module);
216 }
217
218 // Return true if the module was succesfully unlocked:
219 if (!module->isLocked())
220 return true;
221
223 parent,
224 tr("Warning: Invalid unlock key!"),
225 tr("The unlock key you provided did not properly unlock "
226 "this module. Please try again."));
227 }
228 return false;
229}
230
232 moduleUnlock(module, this);
233}
234
236 BTAboutModuleDialog *dialog = new BTAboutModuleDialog(module, this);
237 dialog->setAttribute(Qt::WA_DeleteOnClose); // Destroy dialog when closed
238 dialog->show();
239 dialog->raise();
240}
241
242/** Refreshes all presenters.*/
244 for (auto const * const subWindow : m_mdi->subWindowList())
245 if (CDisplayWindow * const window =
246 dynamic_cast<CDisplayWindow*>(subWindow->widget()))
247 window->reload();
248}
249
250void BibleTime::processCommandline(bool const ignoreSession,
251 QString const & bibleKey)
252{
253 if (btConfig().value<bool>(QStringLiteral("state/crashedTwoTimes"), false))
254 return;
255
256 // Restore workspace if not not ignoring session data:
257 if (!ignoreSession)
259
260 if (btConfig().value<bool>(QStringLiteral("state/crashedLastTime"), false))
261 return;
262
263 if (!bibleKey.isNull()) {
264 auto * const bible =
266 QStringLiteral("standardBible"));
267 if (bibleKey == QStringLiteral("random")) {
268 CSwordVerseKey vk(nullptr);
269 auto const newIndex = randInt<decltype(vk.index())>(0, 31100);
270 vk.positionToTop();
271 vk.setIndex(newIndex);
272 createReadDisplayWindow(bible, vk.key());
273 } else {
274 createReadDisplayWindow(bible, bibleKey);
275 }
276
277 /*
278 We are sure only one window is open - it should be displayed
279 fullscreen in the working area:
280 */
282 }
283
284 if (btConfig().value<bool>(QStringLiteral("state/crashedLastTime"), false)){
285 btConfig().setValue(QStringLiteral("state/crashedTwoTimes"), true);
286 } else {
287 btConfig().setValue(QStringLiteral("state/crashedLastTime"), true);
288 }
289 btConfig().sync();
290
291 // temporary for testing
292 Q_EMIT colorThemeChanged();
293}
294
295bool BibleTime::event(QEvent* event) {
296 if (event->type() == QEvent::PaletteChange) {
297 Q_EMIT colorThemeChanged();
298 // allow to continue to update other parts of Qt widgets
299 } else if (event->type() == QEvent::KeyPress) {
300 if (static_cast<QKeyEvent *>(event)->modifiers() > 0)
301 return false;
302 if (autoScrollAnyKey())
303 return true;
304 }
305 return QMainWindow::event(event);
306}
307
309 if (auto * const activeSubWindow = m_mdi->activeSubWindow())
310 if (auto * const displayWindow =
311 dynamic_cast<CDisplayWindow *>(activeSubWindow->widget()))
312 return displayWindow->firstModule();
313 return nullptr;
314}
315
317 if (auto * const activeSubWindow = m_mdi->activeSubWindow())
318 if (auto * const displayWindow =
319 dynamic_cast<CDisplayWindow *>(activeSubWindow->widget()))
320 return displayWindow->displayWidget();
321 return nullptr;
322}
323
325 if (auto * const display = getCurrentDisplay())
326 display->setDisplayFocus();
327}
328
330 QString const & searchText)
331{
332 if (!m_searchDialog)
334 m_searchDialog->reset(std::move(modules), searchText);
335}
336
338
339#ifdef BUILD_TEXT_TO_SPEECH
340void BibleTime::speakText(QString const & text) {
341 if (!m_textToSpeech)
342 m_textToSpeech = createTextToSpeechInstance();
343
344 m_textToSpeech->say(text);
345}
346
347std::unique_ptr<QTextToSpeech> BibleTime::createTextToSpeechInstance() {
348 std::unique_ptr<QTextToSpeech> tts;
349
350 // restore settings from config
351 auto const configuredEngine = btConfig().value<QString>(QStringLiteral("GUI/ttsEngine"));
352 if (QTextToSpeech::availableEngines().contains(configuredEngine)) {
353 tts = std::make_unique<QTextToSpeech>(configuredEngine);
354 } else {
355 tts = std::make_unique<QTextToSpeech>();
356 }
357
358 auto const configuredLocale = btConfig().value<QLocale>(QStringLiteral("GUI/ttsLocale"));
359 if (tts->availableLocales().contains(configuredLocale))
360 tts->setLocale(configuredLocale);
361
362 auto const configuredVoice = btConfig().value<QString>(QStringLiteral("GUI/ttsVoice"));
363 for (auto const & voice : tts->availableVoices()) {
364 if (voice.name() == configuredVoice) {
365 tts->setVoice(voice);
366 break;
367 }
368 }
369
370 return tts;
371}
372#endif
#define BT_ASSERT(...)
Definition btassert.h:17
BtConfig & btConfig()
This is a shortchand for BtConfig::getInstance().
Definition btconfig.h:305
QList< CSwordModuleInfo const * > BtConstModuleList
QPointer< QWidget > m_debugWindow
Definition bibletime.h:496
void slotModuleUnlock(CSwordModuleInfo *module)
BtBookshelfDockWidget * m_bookshelfDock
Definition bibletime.h:460
static bool moduleUnlock(CSwordModuleInfo *module, QWidget *parent=nullptr)
void retranslateUi()
void moduleAbout(CSwordModuleInfo *module)
void openFindWidget()
CMDIArea * m_mdi
Definition bibletime.h:488
bool event(QEvent *event) override
void initToolbars()
void openSearchDialog(BtConstModuleList modules, QString const &searchText={})
static BibleTime * m_instance
Definition bibletime.h:457
void colorThemeChanged()
void setDisplayFocus()
void initConnections()
BtFindWidget * m_findWidget
Definition bibletime.h:489
QPointer< Search::CSearchDialog > m_searchDialog
Definition bibletime.h:493
CSwordModuleInfo const * getCurrentModule()
BtModelViewReadDisplay * getCurrentDisplay()
~BibleTime() override
void processCommandline(bool ignoreSession, QString const &bibleKey)
CDisplayWindow * createReadDisplayWindow(QList< CSwordModuleInfo * > modules, QString const &key)
Creates a new presenter in the MDI area according to the type of the module.
void refreshDisplayWindows() const
bool autoScrollAnyKey()
BibleTime(BibleTimeApp &app, QWidget *parent=nullptr, Qt::WindowFlags flags=Qt::WindowFlags())
Definition bibletime.cpp:80
void sync()
Synchronizes the configuration to disk.
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.
CSwordModuleInfo * getDefaultSwordModuleByType(const QString &moduleType)
Returns default sword module info class for a given module type.
Definition btconfig.cpp:493
void showAndSelect()
The BtMessageInputDialog class provides a editable field for user input. Optionally it displays a lar...
The base class for all display windows of BibleTime.
The class used to display lexicons.
void myTileVertical()
Definition cmdiarea.cpp:154
QMdiSubWindow * addDisplayWindow(CDisplayWindow *displayWindow)
Definition cmdiarea.cpp:85
static CSwordBackend & instance() noexcept
QString config(const CSwordModuleInfo::ConfigEntry entry) const
QString const & name() const
CSwordKey implementation for Sword's VerseKey.
void setIndex(long v)
QString key() const final override
long index() const
#define T(f)
auto randInt(T min, T max) -> std::enable_if_t< std::is_integral_v< std::decay_t< T > >, T >
Definition bibletime.cpp:58
QMessageBox::StandardButton showWarning(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton)