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