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