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#include <type_traits>
29#include "../backend/config/btconfig.h"
30#include "../backend/drivers/cswordmoduleinfo.h"
31#include "../backend/keys/cswordversekey.h"
32#include "../backend/managers/cswordbackend.h"
33#include "../util/btassert.h"
34#include "../util/cresmgr.h"
35#include "../util/directory.h"
36#include "bibletimeapp.h"
37#include "btaboutmoduledialog.h"
40#include "cmdiarea.h"
48#include "messagedialog.h"
50
51
52namespace {
53
54template <typename T>
55auto randInt(T min, T max)
56 -> std::enable_if_t<std::is_integral_v<std::decay_t<T>>, T>
57{
58 static std::mt19937 rng((std::random_device()()));
59 return std::uniform_int_distribution<std::decay_t<T>>(min, max)(rng);
60}
61
62QString const splashes[] = {
63 QStringLiteral("startuplogo.png"),
64 QStringLiteral("startuplogo_christmas.png"),
65 QStringLiteral("startuplogo_easter.jpg"),
66};
67
68auto const splashHtml =
69 QStringLiteral(
70 "<div style='background:transparent;color:white;font-weight:bold'>"
71 "%1</div>");
72
73} // anonymous namespace
74
76
77BibleTime::BibleTime(BibleTimeApp & app, QWidget *parent, Qt::WindowFlags flags)
78 : QMainWindow(parent, flags)
79{
80 namespace DU = util::directory;
81
83 m_instance = this;
84
85 QSplashScreen * splash = nullptr;
86 constexpr static auto const splashTextAlignment =
87 Qt::AlignHCenter | Qt::AlignTop;
88
89 if (btConfig().value<bool>(QStringLiteral("GUI/showSplashScreen"), true)) {
90 auto const splashNumber =
91 randInt<std::size_t>(0u,
92 std::extent_v<decltype(splashes)> - 1u);
93 QPixmap pm;
94 if (pm.load(DU::getPicsDir().filePath(splashes[splashNumber]))) {
95 splash = new QSplashScreen(pm);
96 splash->showMessage(
97 splashHtml.arg(tr("Initializing the SWORD engine...")),
98 splashTextAlignment);
99 splash->show();
100 qApp->processEvents();
101 } else {
102 qWarning("Can't load startuplogo! Check your installation.");
103 }
104 }
105 app.initBackends();
106
107 if (splash) {
108 splash->showMessage(
109 splashHtml.arg(
110 tr("Creating BibleTime's user interface...")),
111 splashTextAlignment);
112 qApp->processEvents();
113 }
114 initView();
115
116 if (splash) {
117 splash->showMessage(
118 splashHtml.arg(tr("Initializing menu- and toolbars...")),
119 splashTextAlignment);
120 qApp->processEvents();
121 splash->setAttribute(Qt::WA_DeleteOnClose);
122 splash->finish(this);
123 }
124 initActions();
125 initMenubar();
126 initToolbars();
128
129 setWindowTitle(QStringLiteral("BibleTime " BT_VERSION));
130 setWindowIcon(CResMgr::mainWindow::icon());
132}
133
135 // delete m_dcopInterface;
136 // The backend is deleted by the BibleTimeApp instance
137
138 delete m_debugWindow;
140 saveProfile();
141}
142
143/** \brief Creates a new presenter in the MDI area according to the type of the
144 module. */
146 QList<CSwordModuleInfo *> modules,
147 QString const & key)
148{
149 qApp->setOverrideCursor(QCursor(Qt::WaitCursor));
150
151 CDisplayWindow * displayWindow;
152 switch (modules.first()->type()) {
154 displayWindow = new CBibleReadWindow(modules, key, m_mdi);
155 break;
157 displayWindow = new CCommentaryReadWindow(modules, key, m_mdi);
158 break;
160 displayWindow = new CLexiconReadWindow(modules, key, m_mdi);
161 break;
163 displayWindow = new CBookReadWindow(modules, key, m_mdi);
164 break;
165 default:
166 qFatal("unknown module type");
167 std::terminate();
168 }
169 m_mdi->addDisplayWindow(displayWindow);
170 displayWindow->show();
171
172 /* We have to process pending events here, otherwise displayWindow is not
173 fully painted. */
174 qApp->processEvents();
175 qApp->restoreOverrideCursor();
176 return displayWindow;
177}
178
179
180/** \brief Creates a new presenter in the MDI area according to the type of the
181 module. */
183 CSwordModuleInfo * const module,
184 QString const & key)
185{ return createReadDisplayWindow(QList<CSwordModuleInfo*>() << module, key); }
186
187bool BibleTime::moduleUnlock(CSwordModuleInfo * module, QWidget * const parent){
188 BT_ASSERT(module);
189
190 /// \todo Write a proper unlocking dialog with integrated error messages.
191 BtMessageInputDialog unlockKeyInputDialog(
192 tr("Unlock Work"),
193 tr("Enter the unlock key for %1.").arg(module->name()),
196 module->getUnlockInfo(),
197 parent);
198
199 while (unlockKeyInputDialog.exec() == QDialog::Accepted) {
200 module->unlock(unlockKeyInputDialog.getUserInput());
201
202 /// \todo refactor this module reload
203 /* There is currently a deficiency in SWORD 1.8.1 in that
204 backend->setCipherKey() does not work correctly for modules from
205 which data was already fetched. Therefore we have to reload the
206 modules. */
207 {
208 auto const moduleName(module->name());
209 auto & backend = CSwordBackend::instance();
210 backend.reloadModules();
211 module = backend.findModuleByName(moduleName);
212 BT_ASSERT(module);
213 }
214
215 // Return true if the module was succesfully unlocked:
216 if (!module->isLocked())
217 return true;
218
220 parent,
221 tr("Warning: Invalid unlock key!"),
222 tr("The unlock key you provided did not properly unlock "
223 "this module. Please try again."));
224 }
225 return false;
226}
227
229 moduleUnlock(module, this);
230}
231
233 BTAboutModuleDialog *dialog = new BTAboutModuleDialog(module, this);
234 dialog->setAttribute(Qt::WA_DeleteOnClose); // Destroy dialog when closed
235 dialog->show();
236 dialog->raise();
237}
238
239/** Refreshes all presenters.*/
241 for (auto const * const subWindow : m_mdi->subWindowList())
242 if (CDisplayWindow * const window =
243 dynamic_cast<CDisplayWindow*>(subWindow->widget()))
244 window->reload();
245}
246
247void BibleTime::processCommandline(bool const ignoreSession,
248 QString const & bibleKey)
249{
250 if (btConfig().value<bool>(QStringLiteral("state/crashedTwoTimes"), false))
251 return;
252
253 // Restore workspace if not not ignoring session data:
254 if (!ignoreSession)
256
257 if (btConfig().value<bool>(QStringLiteral("state/crashedLastTime"), false))
258 return;
259
260 if (!bibleKey.isNull()) {
261 auto * const bible =
263 QStringLiteral("standardBible"));
264 if (bibleKey == QStringLiteral("random")) {
265 CSwordVerseKey vk(nullptr);
266 auto const newIndex = randInt<decltype(vk.index())>(0, 31100);
267 vk.positionToTop();
268 vk.setIndex(newIndex);
269 createReadDisplayWindow(bible, vk.key());
270 } else {
271 createReadDisplayWindow(bible, bibleKey);
272 }
273
274 /*
275 We are sure only one window is open - it should be displayed
276 fullscreen in the working area:
277 */
279 }
280
281 if (btConfig().value<bool>(QStringLiteral("state/crashedLastTime"), false)){
282 btConfig().setValue(QStringLiteral("state/crashedTwoTimes"), true);
283 } else {
284 btConfig().setValue(QStringLiteral("state/crashedLastTime"), true);
285 }
286 btConfig().sync();
287
288 // temporary for testing
289 Q_EMIT colorThemeChanged();
290}
291
292bool BibleTime::event(QEvent* event) {
293 if (event->type() == QEvent::PaletteChange) {
294 Q_EMIT colorThemeChanged();
295 // allow to continue to update other parts of Qt widgets
296 } else if (event->type() == QEvent::KeyPress) {
297 if (static_cast<QKeyEvent *>(event)->modifiers() > 0)
298 return false;
299 if (autoScrollAnyKey())
300 return true;
301 }
302 return QMainWindow::event(event);
303}
304
306 if (auto * const activeSubWindow = m_mdi->activeSubWindow())
307 if (auto * const displayWindow =
308 dynamic_cast<CDisplayWindow *>(activeSubWindow->widget()))
309 return displayWindow->firstModule();
310 return nullptr;
311}
312
314 if (auto * const activeSubWindow = m_mdi->activeSubWindow())
315 if (auto * const displayWindow =
316 dynamic_cast<CDisplayWindow *>(activeSubWindow->widget()))
317 return displayWindow->displayWidget();
318 return nullptr;
319}
320
322 if (auto * const display = getCurrentDisplay())
323 display->setDisplayFocus();
324}
325
327 QString const & searchText)
328{
329 if (!m_searchDialog)
331 m_searchDialog->reset(std::move(modules), searchText);
332}
333
#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:478
void slotModuleUnlock(CSwordModuleInfo *module)
BtBookshelfDockWidget * m_bookshelfDock
Definition bibletime.h:442
static bool moduleUnlock(CSwordModuleInfo *module, QWidget *parent=nullptr)
void retranslateUi()
void moduleAbout(CSwordModuleInfo *module)
void openFindWidget()
CMDIArea * m_mdi
Definition bibletime.h:470
bool event(QEvent *event) override
void initToolbars()
void openSearchDialog(BtConstModuleList modules, QString const &searchText={})
static BibleTime * m_instance
Definition bibletime.h:439
void colorThemeChanged()
void setDisplayFocus()
void initConnections()
BtFindWidget * m_findWidget
Definition bibletime.h:471
QPointer< Search::CSearchDialog > m_searchDialog
Definition bibletime.h:475
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:77
void sync()
Synchronizes the configuration to disk.
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:494
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:55
QMessageBox::StandardButton showWarning(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton)