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