BibleTime
bibletimeapp.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 "bibletimeapp.h"
14 
15 #ifdef Q_OS_WIN
16 #include <array>
17 #endif
18 #include <cstdio>
19 #include <cstdlib>
20 #include <memory>
21 #include <QByteArray>
22 #include <QColor>
23 #include <QDebug>
24 #ifdef Q_OS_WIN
25 #include <QDir>
26 #endif
27 #include <QFile>
28 #include <QIODevice>
29 #include <QMessageBox>
30 #include <QPalette>
31 #include <QString>
32 #include <Qt>
33 #include <QtGlobal>
34 #if defined(Q_OS_WIN) || defined(Q_OS_MAC)
35 #include <QTextStream>
36 #endif
37 #ifdef Q_OS_WIN
38 #include <windows.h>
39 #endif
40 #include "../backend/config/btconfig.h"
41 #include "../backend/managers/cswordbackend.h"
42 #include "../backend/managers/cdisplaytemplatemgr.h"
43 #include "../util/btassert.h"
44 #include "../util/bticons.h"
45 #if defined(Q_OS_WIN) || defined(Q_OS_MAC)
46 #include "../util/directory.h"
47 #endif
48 #include "messagedialog.h"
50 
51 // Sword includes:
52 #pragma GCC diagnostic push
53 #pragma GCC diagnostic ignored "-Wextra-semi"
54 #ifdef Q_OS_MAC
55  #include "localemgr.h"
56 #endif
57 #include <swlog.h>
58 #pragma GCC diagnostic pop
59 
60 
61 class QMessageLogContext;
62 
63 namespace {
64 
65 std::unique_ptr<QFile> debugStream;
66 
67 void myMessageOutput(QtMsgType type,
68  QMessageLogContext const &,
69  QString const & message)
70 {
71  QByteArray msg = message.toLatin1();
72  #define MY_OUTPUT_LITERAL(literal) \
73  debugStream->write(literal, sizeof(literal) - 1u)
74  switch (type) {
75  case QtDebugMsg:
76  if (btApp->debugMode()) { // Only show messages if they are enabled!
77  MY_OUTPUT_LITERAL("(BibleTime " BT_VERSION ") Debug: ");
78  debugStream->write(msg);
79  debugStream->putChar('\n');
80  debugStream->flush();
81  }
82  break;
83  case QtInfoMsg:
84  MY_OUTPUT_LITERAL("(BibleTime " BT_VERSION ") INFO: ");
85  debugStream->write(msg);
86  debugStream->putChar('\n');
87  debugStream->flush();
88  break;
89  case QtWarningMsg:
90  MY_OUTPUT_LITERAL("(BibleTime " BT_VERSION ") WARNING: ");
91  debugStream->write(msg);
92  debugStream->putChar('\n');
93  debugStream->flush();
94  break;
95  case QtCriticalMsg:
96  MY_OUTPUT_LITERAL("(BibleTime " BT_VERSION ") CRITICAL: ");
97  debugStream->write(msg);
98  MY_OUTPUT_LITERAL("\nPlease report this bug at "
99  "https://github.com/bibletime/bibletime/issues\n");
100  debugStream->flush();
101  break;
102  case QtFatalMsg:
103  MY_OUTPUT_LITERAL("(BibleTime " BT_VERSION ") FATAL: ");
104  debugStream->write(msg);
105  MY_OUTPUT_LITERAL("\nPlease report this bug at "
106  "https://github.com/bibletime/bibletime/issues\n");
107 
108  // Dump core on purpose (see qInstallMsgHandler documentation):
109  debugStream->close();
110  std::abort();
111  }
112  #undef MY_OUTPUT_LITERAL
113 }
114 
115 } // anonymous namespace
116 
117 BibleTimeApp::BibleTimeApp(int &argc, char **argv)
118  : QApplication(argc, argv)
119  , m_init(false)
120  , m_debugMode(qgetenv("BIBLETIME_DEBUG") == QByteArrayLiteral("1"))
121  , m_icons(nullptr)
122 {
123  setApplicationName(QStringLiteral("bibletime"));
124  setApplicationVersion(BT_VERSION);
125 
126  // Support for retina displays
127  #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
128  this->setAttribute(Qt::AA_UseHighDpiPixmaps);
129  #endif
130 
131  #ifdef Q_OS_WIN
132  // On Windows, add a path for Qt plugins to be loaded from:
133  addLibraryPath(applicationDirPath() + QStringLiteral("/plugins"));
134 
135  // Must set HOME var on Windows:
136  // getenv and qgetenv don't work right on Windows with unicode characters
137  std::array<wchar_t, 4096u> homeDir;
138  GetEnvironmentVariable(TEXT("APPDATA"), homeDir.data(), homeDir.size());
139  SetEnvironmentVariable(TEXT("HOME"), homeDir.data());
140  #endif
141 
142  // Setup message handler:
143  #ifdef Q_OS_WIN
144  // Use the default Qt message handler if --debug is not specified
145  // This works with Visual Studio debugger Output Window
146  if (m_debugMode) {
147  debugStream =
148  std::make_unique<QFile>(
149  QDir::home().filePath(
150  QStringLiteral("/BibleTime Debug.txt")));
151  debugStream->open(QIODevice::WriteOnly | QIODevice::Text);
152  qInstallMessageHandler(myMessageOutput);
153  }
154  #else
155  debugStream = std::make_unique<QFile>();
156  debugStream->open(stderr, QIODevice::WriteOnly | QIODevice::Text);
157  qInstallMessageHandler(myMessageOutput);
158  #endif
159 }
160 
162  // Prevent writing to the log file before the directory cache is init:
163  if (!m_init || BtConfig::m_instance == nullptr)
164  return;
165 
166  //we can set this safely now because we close now (hopyfully without crash)
167  btConfig().setValue(QStringLiteral("state/crashedLastTime"), false);
168  btConfig().setValue(QStringLiteral("state/crashedTwoTimes"), false);
169 
171  m_backend.reset();
172  delete m_icons;
173 
175 }
176 
178  BT_ASSERT(m_init);
179 
181  if (r == BtConfig::INIT_OK)
182  return true;
184  /// \todo Migrate from btConfigOldApi to BTCONFIG_API_VERSION
185  qWarning() << "BibleTime configuration migration is not yet implemented!!!";
187  nullptr,
188  tr("Warning!"),
189  tr("Migration to the new configuration system is not yet "
190  "implemented. Proceeding might result in <b>loss of data"
191  "</b>. Please backup your configuration files before "
192  "you continue!<br/><br/>Do you want to continue? Press "
193  "\"No\" to quit BibleTime immediately."),
194  QMessageBox::Yes | QMessageBox::No,
195  QMessageBox::No) == QMessageBox::No)
196  return false;
197  } else {
200  nullptr,
201  tr("Error loading configuration!"),
202  tr("Failed to load BibleTime's configuration, because it "
203  "appears that the configuration file corresponds to a "
204  "newer version of BibleTime. This is likely caused by "
205  "BibleTime being downgraded. Loading the new "
206  "configuration file may result in <b>loss of data</b>."
207  "<br/><br/>Do you still want to try to load the new "
208  "configuration file? Press \"No\" to quit BibleTime "
209  "immediately."),
210  QMessageBox::Yes | QMessageBox::No,
211  QMessageBox::No) == QMessageBox::No)
212  return false;
213  }
215  return true;
216 }
217 
219  enum LightDarkMode {
220  systemDefault = 0,
221  light = 1,
222  dark = 2
223  };
224 
225  auto const lightDarkMode =
226  btConfig().value<int>(QStringLiteral("GUI/lightDarkMode"), 0);
227  if (lightDarkMode == LightDarkMode::systemDefault)
228  return;
229  QPalette p;
230  if (lightDarkMode == LightDarkMode::dark) {
231  p.setColor(QPalette::WindowText,QColor(0xfc, 0xfc, 0xfc));
232  p.setColor(QPalette::Button,QColor(0x31, 0x36, 0x3b));
233  p.setColor(QPalette::Light,QColor(0x18, 0x1b, 0x1d));
234  p.setColor(QPalette::Midlight,QColor(0x25, 0x29, 0x2c));
235  p.setColor(QPalette::Dark,QColor(0x62, 0x6c, 0x76));
236  p.setColor(QPalette::Mid,QColor(0x41, 0x48, 0x4e));
237  p.setColor(QPalette::Text,QColor(0xfc, 0xfc, 0xfc));
238  p.setColor(QPalette::BrightText,QColor(0xff, 0xff, 0xff));
239  p.setColor(QPalette::ButtonText,QColor(0xfc, 0xfc, 0xfc));
240  p.setColor(QPalette::Base,QColor(0x1b, 0x1e, 0x20));
241  p.setColor(QPalette::Window,QColor(0x2a, 0x2e, 0x32));
242  p.setColor(QPalette::Shadow,QColor(0x76, 0x76, 0x76));
243  p.setColor(QPalette::Highlight,QColor(0x3d, 0xae, 0xe9));
244  p.setColor(QPalette::HighlightedText,QColor(0xfc, 0xfc, 0xfc));
245  p.setColor(QPalette::Link,QColor(0x1d, 0x99, 0xf3));
246  p.setColor(QPalette::LinkVisited,QColor(0x9b, 0x59, 0xb6));
247  } else {
248  p.setColor(QPalette::WindowText,QColor(0x23, 0x26, 0x29));
249  p.setColor(QPalette::Button,QColor(0xf7, 0xf7, 0xf7));
250  p.setColor(QPalette::Light,QColor(0x0, 0x0, 0x0));
251  p.setColor(QPalette::Midlight,QColor(0x0, 0x0, 0x0));
252  p.setColor(QPalette::Dark,QColor(0x7b, 0x7b, 0x7b));
253  p.setColor(QPalette::Mid,QColor(0xa5, 0xa5, 0xa5));
254  p.setColor(QPalette::Text,QColor(0x23, 0x26, 0x29));
255  p.setColor(QPalette::BrightText,QColor(0xff, 0xff, 0xff));
256  p.setColor(QPalette::ButtonText,QColor(0x23, 0x26, 0x29));
257  p.setColor(QPalette::Base,QColor(0xff, 0xff, 0xff));
258  p.setColor(QPalette::Window,QColor(0xef, 0xf0, 0xf1));
259  p.setColor(QPalette::Shadow,QColor(0x76, 0x76, 0x76));
260  p.setColor(QPalette::Highlight,QColor(0x3d, 0xae, 0xe9));
261  p.setColor(QPalette::HighlightedText,QColor(0xff, 0xff, 0xff));
262  p.setColor(QPalette::Link,QColor(0x29, 0x80, 0xb9));
263  p.setColor(QPalette::LinkVisited,QColor(0x9b, 0x59, 0xb6));
264  }
265  setPalette(p);
266 }
267 
269  BT_ASSERT(m_init);
270 
271  QString errorMessage;
272  new CDisplayTemplateMgr(errorMessage);
273  if (errorMessage.isNull())
274  return true;
275  message::showCritical(nullptr, tr("Fatal error!"), errorMessage);
276  return false;
277 }
278 
280 
282  // On Windows the sword.conf must be created before the initialization of sword
283  // It will contain the LocalePath which is used for sword locales
284  // It also contains a DataPath to the %ProgramData%\Sword directory
285  // If this is not done here, the sword locales.d won't be found
286 
287  #if defined(Q_OS_WIN) || defined(Q_OS_MAC)
288  QFile file(util::directory::getUserHomeSwordDir().filePath(
289  QStringLiteral("sword.conf")));
290  if (file.exists() || !file.open(QIODevice::WriteOnly | QIODevice::Text))
291  return;
292  QTextStream out(&file);
293  out << "\n";
294  out << "[Install]\n";
295  #if defined(Q_OS_WIN)
296  out << "DataPath=" << QDir::toNativeSeparators(util::directory::getSharedSwordDir().absolutePath()) << "\n";
297  out << "LocalePath=" << QDir::toNativeSeparators(util::directory::getApplicationSwordDir().absolutePath()) << "\n";
298  #elif defined(Q_OS_MAC)
299  out << "DataPath=" << QDir::toNativeSeparators(util::directory::getUserHomeSwordDir().absolutePath()) << "\n";
300  #endif
301  out << "\n";
302  #endif
303 
304  sword::SWLog::getSystemLog()->setLogLevel(btApp->debugMode()
305  ? sword::SWLog::LOG_DEBUG
306  : sword::SWLog::LOG_ERROR);
307 
308 #ifdef Q_OS_MAC
309  // set a LocaleMgr with a fixed path to the locales.d of the DMG image on MacOS
310  qDebug() << "Using sword locales dir: " << util::directory::getSwordLocalesDir().absolutePath().toUtf8();
311  sword::LocaleMgr::setSystemLocaleMgr(new sword::LocaleMgr(util::directory::getSwordLocalesDir().absolutePath().toUtf8()));
312 #endif
313 
314  /*
315  Set book names language if not set. This is a hack. We do this call here,
316  because we need to keep the setting displayed in BtLanguageSettingsPage in
317  sync with the language of the book names displayed, so that both would
318  always use the same setting.
319  */
320  CDisplaySettingsPage::resetLanguage(); /// \todo refactor this hack
321 
322  m_backend.emplace();
323 }
#define MY_OUTPUT_LITERAL(literal)
#define btApp
Definition: bibletimeapp.h:59
#define BT_ASSERT(...)
Definition: btassert.h:17
BtConfig & btConfig()
This is a shortchand for BtConfig::getInstance().
Definition: btconfig.h:305
bool initDisplayTemplateManager()
std::optional< CSwordBackend > m_backend
Definition: bibletimeapp.h:55
BibleTimeApp(int &argc, char **argv)
void initLightDarkPalette()
void initBackends()
BtIcons * m_icons
Definition: bibletimeapp.h:54
bool initBtConfig()
~BibleTimeApp() override
bool m_debugMode
Definition: bibletimeapp.h:53
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
static InitState initBtConfig()
Definition: btconfig.cpp:141
static void destroyInstance()
Definition: btconfig.cpp:216
static void forceMigrate()
Definition: btconfig.cpp:161
static BtConfig * m_instance
singleton instance
Definition: btconfig.h:285
@ INIT_NEED_UNIMPLEMENTED_FORWARD_MIGRATE
Definition: btconfig.h:56
@ INIT_NEED_UNIMPLEMENTED_BACKWARD_MIGRATE
Definition: btconfig.h:54
@ INIT_OK
Definition: btconfig.h:55
static CDisplayTemplateMgr * instance()
QStringList r(content.left(bodyIndex))
void myMessageOutput(QtMsgType type, QMessageLogContext const &, QString const &message)
std::unique_ptr< QFile > debugStream
QMessageBox::StandardButton showCritical(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton)
QMessageBox::StandardButton showWarning(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton)
const QDir & getUserHomeSwordDir()
Definition: directory.cpp:342