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-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 "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"
38 #include "btbookshelfdockwidget.h"
39 #include "btmessageinputdialog.h"
40 #include "cmdiarea.h"
41 #include "display/btfindwidget.h"
48 #include "messagedialog.h"
50 
51 
52 namespace {
53 
54 template <typename T>
55 auto 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 
62 QString const splashes[] = {
63  QStringLiteral("startuplogo.png"),
64  QStringLiteral("startuplogo_christmas.png"),
65  QStringLiteral("startuplogo_easter.jpg"),
66 };
67 
68 auto const splashHtml =
69  QStringLiteral(
70  "<div style='background:transparent;color:white;font-weight:bold'>"
71  "%1</div>");
72 
73 } // anonymous namespace
74 
76 
77 BibleTime::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();
127  initConnections();
128 
129  setWindowTitle(QStringLiteral("BibleTime " BT_VERSION));
130  setWindowIcon(CResMgr::mainWindow::icon());
131  retranslateUi();
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. */
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, m_mdi);
155  break;
157  displayWindow = new CCommentaryReadWindow(modules, m_mdi);
158  break;
160  displayWindow = new CLexiconReadWindow(modules, m_mdi);
161  break;
163  displayWindow = new CBookReadWindow(modules, m_mdi);
164  break;
165  default:
166  qFatal("unknown module type");
167  std::terminate();
168  }
169  m_mdi->addSubWindow(displayWindow);
170  displayWindow->show();
171  displayWindow->lookupKey(key);
172 
173  /* We have to process pending events here, otherwise displayWindow is not
174  fully painted. */
175  qApp->processEvents();
176  qApp->restoreOverrideCursor();
177  return displayWindow;
178 }
179 
180 
181 /** \brief Creates a new presenter in the MDI area according to the type of the
182  module. */
184  CSwordModuleInfo * const module,
185  QString const & key)
186 { return createReadDisplayWindow(QList<CSwordModuleInfo*>() << module, key); }
187 
188 bool BibleTime::moduleUnlock(CSwordModuleInfo * module, QWidget * const parent){
189  BT_ASSERT(module);
190 
191  /// \todo Write a proper unlocking dialog with integrated error messages.
192  BtMessageInputDialog unlockKeyInputDialog(
193  tr("Unlock Work"),
194  tr("Enter the unlock key for %1.").arg(module->name()),
197  module->getUnlockInfo(),
198  parent);
199 
200  while (unlockKeyInputDialog.exec() == QDialog::Accepted) {
201  module->unlock(unlockKeyInputDialog.getUserInput());
202 
203  /// \todo refactor this module reload
204  /* There is currently a deficiency in SWORD 1.8.1 in that
205  backend->setCipherKey() does not work correctly for modules from
206  which data was already fetched. Therefore we have to reload the
207  modules. */
208  {
209  auto const moduleName(module->name());
210  auto & backend = CSwordBackend::instance();
211  backend.reloadModules();
212  module = backend.findModuleByName(moduleName);
213  BT_ASSERT(module);
214  }
215 
216  // Return true if the module was succesfully unlocked:
217  if (!module->isLocked())
218  return true;
219 
221  parent,
222  tr("Warning: Invalid unlock key!"),
223  tr("The unlock key you provided did not properly unlock "
224  "this module. Please try again."));
225  }
226  return false;
227 }
228 
230  moduleUnlock(module, this);
231 }
232 
234  BTAboutModuleDialog *dialog = new BTAboutModuleDialog(module, this);
235  dialog->setAttribute(Qt::WA_DeleteOnClose); // Destroy dialog when closed
236  dialog->show();
237  dialog->raise();
238 }
239 
240 /** Refreshes all presenters.*/
242  for (auto const * const subWindow : m_mdi->subWindowList())
243  if (CDisplayWindow * const window =
244  dynamic_cast<CDisplayWindow*>(subWindow->widget()))
245  window->reload();
246 }
247 
248 void BibleTime::processCommandline(bool const ignoreSession,
249  QString const & bibleKey)
250 {
251  if (btConfig().value<bool>(QStringLiteral("state/crashedTwoTimes"), false))
252  return;
253 
254  // Restore workspace if not not ignoring session data:
255  if (!ignoreSession)
256  reloadProfile();
257 
258  if (btConfig().value<bool>(QStringLiteral("state/crashedLastTime"), false))
259  return;
260 
261  if (!bibleKey.isNull()) {
262  auto * const bible =
264  QStringLiteral("standardBible"));
265  if (bibleKey == QStringLiteral("random")) {
266  CSwordVerseKey vk(nullptr);
267  auto const newIndex = randInt<decltype(vk.index())>(0, 31100);
268  vk.positionToTop();
269  vk.setIndex(newIndex);
270  createReadDisplayWindow(bible, vk.key());
271  } else {
272  createReadDisplayWindow(bible, bibleKey);
273  }
274 
275  /*
276  We are sure only one window is open - it should be displayed
277  fullscreen in the working area:
278  */
280  }
281 
282  if (btConfig().value<bool>(QStringLiteral("state/crashedLastTime"), false)){
283  btConfig().setValue(QStringLiteral("state/crashedTwoTimes"), true);
284  } else {
285  btConfig().setValue(QStringLiteral("state/crashedLastTime"), true);
286  }
287  btConfig().sync();
288 
289  // temporary for testing
290  Q_EMIT colorThemeChanged();
291 }
292 
293 bool BibleTime::event(QEvent* event) {
294  if (event->type() == QEvent::PaletteChange) {
295  Q_EMIT colorThemeChanged();
296  // allow to continue to update other parts of Qt widgets
297  } else if (event->type() == QEvent::KeyPress) {
298  if (static_cast<QKeyEvent *>(event)->modifiers() > 0)
299  return false;
300  if (autoScrollAnyKey())
301  return true;
302  }
303  return QMainWindow::event(event);
304 }
305 
307  if (auto * const activeSubWindow = m_mdi->activeSubWindow())
308  if (auto * const displayWindow =
309  dynamic_cast<CDisplayWindow *>(activeSubWindow->widget()))
310  return displayWindow->firstModule();
311  return nullptr;
312 }
313 
315  if (auto * const activeSubWindow = m_mdi->activeSubWindow())
316  if (auto * const displayWindow =
317  dynamic_cast<CDisplayWindow *>(activeSubWindow->widget()))
318  return displayWindow->displayWidget();
319  return nullptr;
320 }
321 
323  if (auto * const display = getCurrentDisplay())
324  display->setDisplayFocus();
325 }
326 
328  QString const & searchText)
329 {
330  if (!m_searchDialog)
332  m_searchDialog->reset(std::move(modules), searchText);
333 }
334 
#define BT_ASSERT(...)
Definition: btassert.h:17
BtConfig & btConfig()
This is a shortchand for BtConfig::getInstance().
Definition: btconfig.h:305
QList< CSwordModuleInfo const * > BtConstModuleList
Definition: btmodulelist.h:21
void initBackends()
QPointer< QWidget > m_debugWindow
Definition: bibletime.h:455
void slotModuleUnlock(CSwordModuleInfo *module)
Definition: bibletime.cpp:229
BtBookshelfDockWidget * m_bookshelfDock
Definition: bibletime.h:369
void initMenubar()
static bool moduleUnlock(CSwordModuleInfo *module, QWidget *parent=nullptr)
Definition: bibletime.cpp:188
void retranslateUi()
void moduleAbout(CSwordModuleInfo *module)
Definition: bibletime.cpp:233
void reloadProfile()
void openFindWidget()
Definition: bibletime.cpp:335
CMDIArea * m_mdi
Definition: bibletime.h:447
void initView()
bool event(QEvent *event) override
Definition: bibletime.cpp:293
void initToolbars()
void openSearchDialog(BtConstModuleList modules, QString const &searchText={})
Definition: bibletime.cpp:327
void saveProfile()
static BibleTime * m_instance
Definition: bibletime.h:366
void colorThemeChanged()
void setDisplayFocus()
Definition: bibletime.cpp:322
void initConnections()
void initActions()
BtFindWidget * m_findWidget
Definition: bibletime.h:448
QPointer< Search::CSearchDialog > m_searchDialog
Definition: bibletime.h:452
CSwordModuleInfo const * getCurrentModule()
Definition: bibletime.cpp:306
BtModelViewReadDisplay * getCurrentDisplay()
Definition: bibletime.cpp:314
~BibleTime() override
Definition: bibletime.cpp:134
void processCommandline(bool ignoreSession, QString const &bibleKey)
Definition: bibletime.cpp:248
CDisplayWindow * createReadDisplayWindow(QList< CSwordModuleInfo * > modules, QString const &key)
Creates a new presenter in the MDI area according to the type of the module.
Definition: bibletime.cpp:145
void refreshDisplayWindows() const
Definition: bibletime.cpp:241
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.
Definition: btconfigcore.h:73
CSwordModuleInfo * getDefaultSwordModuleByType(const QString &moduleType)
Returns default sword module info class for a given module type.
Definition: btconfig.cpp:503
void showAndSelect()
The BtMessageInputDialog class provides a editable field for user input. Optionally it displays a lar...
QString getUserInput() const
getUserInput
The base class for all display windows of BibleTime.
void lookupKey(QString const &key)
The class used to display lexicons.
void myTileVertical()
Definition: cmdiarea.cpp:163
QMdiSubWindow * addSubWindow(QWidget *widget, Qt::WindowFlags windowFlags=Qt::WindowFlags())
Definition: cmdiarea.cpp:96
static CSwordBackend & instance() noexcept
Definition: cswordbackend.h:98
QString config(const CSwordModuleInfo::ConfigEntry entry) const
bool unlock(const QString &unlockKey)
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)
std::unique_ptr< CSwordBackend > backend(sword::InstallSource const &is)
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)
const QDir & getPicsDir()
Definition: directory.cpp:279