Qt Utilities 6.18.1
Common Qt related C++ classes and routines used by my applications such as dialogs, widgets and models
Loading...
Searching...
No Matches
resources.cpp
Go to the documentation of this file.
1#include "./resources.h"
2
3#include "resources/config.h"
4
5#include <QDir>
6#include <QFile>
7#include <QFont>
8#include <QIcon>
9#include <QLibraryInfo>
10#include <QLocale>
11#include <QSettings>
12#include <QString>
13#include <QStringBuilder>
14#include <QTranslator>
15#if defined(QT_UTILITIES_GUI_QTWIDGETS)
16#include <QApplication>
17#elif defined(QT_UTILITIES_GUI_QTQUICK)
18#include <QGuiApplication>
19#else
20#include <QCoreApplication>
21#endif
22
23#ifdef Q_OS_ANDROID
24#include <QStandardPaths>
25#endif
26
27#include <iostream>
28
29using namespace std;
30
32inline void initResources()
33{
34 Q_INIT_RESOURCE(qtutilsicons);
35}
36
37inline void cleanupResources()
38{
39 Q_CLEANUP_RESOURCE(qtutilsicons);
40}
42
43namespace QtUtilities {
44
49namespace QtUtilitiesResources {
50
55void init()
56{
57 initResources();
58}
59
64void cleanup()
65{
66 cleanupResources();
67}
68} // namespace QtUtilitiesResources
69
73namespace TranslationFiles {
74
78static QList<QTranslator *> translators;
79
85{
86 static QString path;
87 return path;
88}
89
91static QString relativeBase()
92{
93 static const auto relativeBase = [] {
94 auto appDir = QCoreApplication::applicationDirPath();
95 if (appDir.isEmpty()) {
96 appDir = QStringLiteral(".");
97 }
98 return appDir;
99 }();
100 return relativeBase;
101}
103
118void loadQtTranslationFile(std::initializer_list<QString> repositoryNames)
119{
120 loadQtTranslationFile(repositoryNames, QLocale().name());
121}
122
138void loadQtTranslationFile(initializer_list<QString> repositoryNames, const QString &localeName)
139{
140 const auto debugTranslations = qEnvironmentVariableIntValue("QT_DEBUG_TRANSLATIONS");
141 const auto relBase = relativeBase();
142 for (const auto &repoName : repositoryNames) {
143 auto *const qtTranslator = new QTranslator(QCoreApplication::instance());
144 const auto fileName = QString(repoName % QChar('_') % localeName);
145
146 QString path;
147 if ((!additionalTranslationFilePath().isEmpty() && qtTranslator->load(fileName, path = additionalTranslationFilePath()))
148 || qtTranslator->load(fileName,
149 path =
150#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
151 QLibraryInfo::location(QLibraryInfo::TranslationsPath)
152#else
153 QLibraryInfo::path(QLibraryInfo::TranslationsPath)
154#endif
155 )
156 || qtTranslator->load(fileName, path = relBase + QStringLiteral("/../share/qt/translations"))
157 || qtTranslator->load(fileName, path = QStringLiteral(":/translations"))) {
158 QCoreApplication::installTranslator(qtTranslator);
159 translators.append(qtTranslator);
160 if (debugTranslations) {
161 cerr << "Loading translation file for Qt repository \"" << repoName.toLocal8Bit().data() << "\" and the locale \""
162 << localeName.toLocal8Bit().data() << "\" from \"" << path.toLocal8Bit().data() << "\"." << endl;
163 }
164 } else {
165 delete qtTranslator;
166 if (localeName.startsWith(QLatin1String("en"))) {
167 // the translation file is probably just empty (English is built-in and usually only used for plural forms)
168 continue;
169 }
170 cerr << "Unable to load translation file for Qt repository \"" << repoName.toLocal8Bit().data() << "\" and locale "
171 << localeName.toLocal8Bit().data() << "." << endl;
172 }
173 }
174}
175
195void loadApplicationTranslationFile(const QString &configName, const QString &applicationName)
196{
197 // load English translation files as fallback
198 loadApplicationTranslationFile(configName, applicationName, QStringLiteral("en_US"));
199 // load translation files for current locale
200 const auto defaultLocale(QLocale().name());
201 if (defaultLocale != QLatin1String("en_US")) {
202 loadApplicationTranslationFile(configName, applicationName, defaultLocale);
203 }
204}
205
207static void logTranslationEvent(
208 const char *event, const QString &configName, const QString &applicationName, const QString &localeName, const QString &path = QString())
209{
210 cerr << event << " translation file for \"" << applicationName.toLocal8Bit().data() << "\"";
211 if (!configName.isEmpty()) {
212 cerr << " (config \"" << configName.toLocal8Bit().data() << "\")";
213 }
214 cerr << " and locale \"" << localeName.toLocal8Bit().data() << '\"';
215 if (!path.isEmpty()) {
216 cerr << " from \"" << path.toLocal8Bit().data() << '\"';
217 }
218 cerr << '.' << endl;
219}
221
242void loadApplicationTranslationFile(const QString &configName, const QString &applicationName, const QString &localeName)
243{
244 auto *const appTranslator = new QTranslator(QCoreApplication::instance());
245 const auto fileName = QString(applicationName % QChar('_') % localeName);
246 const auto directoryName = configName.isEmpty() ? applicationName : QString(applicationName % QChar('-') % configName);
247 const auto relBase = relativeBase();
248
249 if (auto path = QString(); (!additionalTranslationFilePath().isEmpty() && appTranslator->load(fileName, path = additionalTranslationFilePath()))
250 || appTranslator->load(fileName, path = relBase) || appTranslator->load(fileName, path = relBase % QStringLiteral("/../") % directoryName)
251 || appTranslator->load(fileName, path = relBase % QStringLiteral("/../../") % directoryName)
252 || appTranslator->load(fileName, path = relBase % QStringLiteral("/translations"))
253 || appTranslator->load(fileName, path = relBase % QStringLiteral("/../share/") % directoryName % QStringLiteral("/translations"))
254 || appTranslator->load(fileName, path = QStringLiteral(APP_INSTALL_PREFIX "/share/") % directoryName % QStringLiteral("/translations"))
255 || appTranslator->load(fileName, path = QStringLiteral(":/translations"))) {
256 QCoreApplication::installTranslator(appTranslator);
257 translators.append(appTranslator);
258 if (qEnvironmentVariableIntValue("QT_DEBUG_TRANSLATIONS")) {
259 logTranslationEvent("Loading", configName, applicationName, localeName, path);
260 }
261 } else {
262 delete appTranslator;
263 if (localeName.startsWith(QLatin1String("en"))) {
264 // the translation file is probably just empty (English is built-in and usually only used for plural forms)
265 return;
266 }
267 logTranslationEvent("Unable to load", configName, applicationName, localeName);
268 }
269}
270
276void loadApplicationTranslationFile(const QString &configName, const std::initializer_list<QString> &applicationNames)
277{
278 for (const QString &applicationName : applicationNames) {
279 loadApplicationTranslationFile(configName, applicationName);
280 }
281}
282
289void loadApplicationTranslationFile(const QString &configName, const std::initializer_list<QString> &applicationNames, const QString &localeName)
290{
291 for (const QString &applicationName : applicationNames) {
292 loadApplicationTranslationFile(configName, applicationName, localeName);
293 }
294}
295
300{
301 for (auto *const translator : translators) {
302 QCoreApplication::removeTranslator(translator);
303 }
304 translators.clear();
305}
306
307} // namespace TranslationFiles
308
314namespace ApplicationInstances {
315
316#if defined(QT_UTILITIES_GUI_QTWIDGETS)
320bool hasWidgetsApp()
321{
322 return qobject_cast<QApplication *>(QCoreApplication::instance()) != nullptr;
323}
324#endif
325
326#if defined(QT_UTILITIES_GUI_QTWIDGETS) || defined(QT_UTILITIES_GUI_QTQUICK)
330bool hasGuiApp()
331{
332 return qobject_cast<QGuiApplication *>(QCoreApplication::instance()) != nullptr;
333}
334#endif
335
340{
341 return qobject_cast<QCoreApplication *>(QCoreApplication::instance()) != nullptr;
342}
343} // namespace ApplicationInstances
344
352{
353 // enable dark window frame on Windows if the configured color palette is dark
354 // - supported as of Qt 6.4; no longer required as of Qt 6.5
355 // - see https://bugreports.qt.io/browse/QTBUG-72028?focusedCommentId=677819#comment-677819
356 // and https://www.qt.io/blog/dark-mode-on-windows-11-with-qt-6.5
357#if defined(Q_OS_WINDOWS) && (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) && (QT_VERSION < QT_VERSION_CHECK(6, 5, 0))
358 if (const auto qtVersion = QLibraryInfo::version();
359 qtVersion >= QVersionNumber(6, 4, 0) && qtVersion < QVersionNumber(6, 5, 0) && !qEnvironmentVariableIsSet("QT_QPA_PLATFORM")) {
360 qputenv("QT_QPA_PLATFORM", "windows:darkmode=1");
361 }
362#endif
363
364 // ensure FONTCONFIG_PATH is set (mainly required for static GNU/Linux builds)
365#ifdef QT_FEATURE_fontdialog
366 if (!qEnvironmentVariableIsSet("FONTCONFIG_PATH") && QDir(QStringLiteral("/etc/fonts")).exists()) {
367 qputenv("FONTCONFIG_PATH", "/etc/fonts");
368 }
369#endif
370
371 // enable settings for High-DPI scaling
372#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
373 if (!QCoreApplication::instance()) {
374 QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true);
375 }
376 QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true);
377#endif
378}
379
392std::unique_ptr<QSettings> getSettings(const QString &organization, const QString &application)
393{
394 auto settings = std::unique_ptr<QSettings>();
395 const auto portableFileName
396 = application.isEmpty() ? organization + QStringLiteral(".ini") : organization % QChar('/') % application % QStringLiteral(".ini");
397 if (const auto portableFileWorkingDir = QFile(portableFileName); portableFileWorkingDir.exists()) {
398 settings = std::make_unique<QSettings>(portableFileWorkingDir.fileName(), QSettings::IniFormat);
399 } else if (const auto portableFileNextToApp = QFile(QCoreApplication::applicationDirPath() % QChar('/') % portableFileName);
400 portableFileNextToApp.exists()) {
401 settings = std::make_unique<QSettings>(portableFileNextToApp.fileName(), QSettings::IniFormat);
402 } else {
403 settings = std::make_unique<QSettings>(QSettings::IniFormat, QSettings::UserScope, organization, application);
404 // move config created by older versions to new location
405 if (organization != QCoreApplication::organizationName() || application != QCoreApplication::applicationName()) {
406 const auto oldConfig
407 = QSettings(QSettings::IniFormat, QSettings::UserScope, QCoreApplication::organizationName(), QCoreApplication::applicationName())
408 .fileName();
409 QFile::rename(oldConfig, settings->fileName()) || QFile::remove(oldConfig);
410 }
411 }
412 loadSettingsWithLogging(*settings);
413 return settings;
414}
415
419void loadSettingsWithLogging(QSettings &settings)
420{
421 settings.sync();
422 if (!qEnvironmentVariableIntValue("QT_DEBUG_SETTINGS")) {
423 return;
424 }
425 const auto error = errorMessageForSettings(settings);
426 if (error.isEmpty()) {
427 std::cerr << "Loaded/synced settings from " << settings.fileName().toStdString() << '\n';
428 } else {
429 std::cerr << "Unable to load settings: " << error.toStdString() << '\n';
430 }
431}
432
436void saveSettingsWithLogging(QSettings &settings)
437{
438 settings.sync();
439 if (!qEnvironmentVariableIntValue("QT_DEBUG_SETTINGS")) {
440 return;
441 }
442 const auto error = errorMessageForSettings(settings);
443 if (error.isEmpty()) {
444 std::cerr << "Saved/synced settings to " << settings.fileName().toStdString() << '\n';
445 } else {
446 std::cerr << "Unable to save settings: " << error.toStdString() << '\n';
447 }
448}
449
453QString errorMessageForSettings(const QSettings &settings)
454{
455 auto errorMessage = QString();
456 switch (settings.status()) {
457 case QSettings::NoError:
458 return QString();
459 case QSettings::AccessError:
460 errorMessage = QCoreApplication::translate("QtUtilities", "unable to access file");
461 break;
462 case QSettings::FormatError:
463 errorMessage = QCoreApplication::translate("QtUtilities", "file has invalid format");
464 break;
465 default:
466 errorMessage = QCoreApplication::translate("QtUtilities", "unknown error");
467 }
468 return QCoreApplication::translate("QtUtilities", "Unable to sync settings from \"%1\": %2").arg(settings.fileName(), errorMessage);
469}
470
475{
476#ifdef Q_OS_ANDROID
477 // delete OpenGL pipeline cache under Android as it seems to break loading the app in certain cases
478 const auto cachePaths = QStandardPaths::standardLocations(QStandardPaths::CacheLocation);
479 for (const auto &cachePath : cachePaths) {
480 const auto cacheDir = QDir(cachePath);
481 const auto subdirs = cacheDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
482 for (const auto &subdir : subdirs) {
483 if (subdir.startsWith(QLatin1String("qtpipelinecache"))) {
484 QFile::remove(cachePath % QChar('/') % subdir % QStringLiteral("/qqpc_opengl"));
485 }
486 }
487 }
488#endif
489}
490
491} // namespace QtUtilities
QT_UTILITIES_EXPORT bool hasCoreApp()
Returns whether a QCoreApplication has been instantiated yet.
Functions for using the resources provided by this library.
Definition resources.h:53
QT_UTILITIES_EXPORT void init()
Initiates the resources used and provided by this library.
Definition resources.cpp:55
QT_UTILITIES_EXPORT void cleanup()
Frees the resources used and provided by this library.
Definition resources.cpp:64
QT_UTILITIES_EXPORT void loadQtTranslationFile(std::initializer_list< QString > repositoryNames)
Loads and installs the appropriate Qt translation file for the current locale.
QT_UTILITIES_EXPORT void clearTranslationFiles()
Clears all translation files previously loaded via the load-functions in this namespace.
QT_UTILITIES_EXPORT void loadApplicationTranslationFile(const QString &configName, const QString &applicationName)
Loads and installs the appropriate application translation file for the current locale.
QT_UTILITIES_EXPORT QString & additionalTranslationFilePath()
Allows to set an additional search path for translation files.
Definition resources.cpp:84
QT_UTILITIES_EXPORT void loadSettingsWithLogging(QSettings &settings)
Loads settings and logs a corresponding message if the env variable QT_DEBUG_SETTINGS is set.
QT_UTILITIES_EXPORT std::unique_ptr< QSettings > getSettings(const QString &organization, const QString &application=QString())
Returns the settings object for the specified organization and application.
QT_UTILITIES_EXPORT void deletePipelineCacheIfNeeded()
Deletes the Qt Quick pipeline cache on platforms where this is needed to workaround issues with the c...
QT_UTILITIES_EXPORT void setupCommonQtApplicationAttributes()
Sets Qt application attributes which are commonly used within my Qt applications.
QT_UTILITIES_EXPORT void saveSettingsWithLogging(QSettings &settings)
Saves settings and logs a corresponding message if the env variable QT_DEBUG_SETTINGS is set.
QT_UTILITIES_EXPORT QString errorMessageForSettings(const QSettings &settings)
Returns an error message for the specified settings or an empty string if there's no error.