Qt Utilities 6.21.3
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 <QDebug>
25#endif
26
27#ifdef Q_OS_ANDROID
28#include <c++utilities/conversion/stringbuilder.h>
29#endif
30
31#include <iostream>
32
33using namespace std;
34
36inline void initResources()
37{
38 Q_INIT_RESOURCE(qtutilsicons);
39}
40
41inline void cleanupResources()
42{
43 Q_CLEANUP_RESOURCE(qtutilsicons);
44}
46
47namespace QtUtilities {
48
53namespace QtUtilitiesResources {
54
59void init()
60{
61 initResources();
62}
63
68void cleanup()
69{
70 cleanupResources();
71}
72} // namespace QtUtilitiesResources
73
77namespace TranslationFiles {
78
82static QList<QTranslator *> translators;
83
89{
90 static QString path;
91 return path;
92}
93
95static QString relativeBase()
96{
97 static const auto relativeBase = [] {
98 auto appDir = QCoreApplication::applicationDirPath();
99 if (appDir.isEmpty()) {
100 appDir = QStringLiteral(".");
101 }
102 return appDir;
103 }();
104 return relativeBase;
105}
107
122void loadQtTranslationFile(std::initializer_list<QString> repositoryNames)
123{
124 loadQtTranslationFile(repositoryNames, QLocale().name());
125}
126
142void loadQtTranslationFile(initializer_list<QString> repositoryNames, const QString &localeName)
143{
144 const auto debugTranslations = qEnvironmentVariableIntValue("QT_DEBUG_TRANSLATIONS");
145 const auto relBase = relativeBase();
146 for (const auto &repoName : repositoryNames) {
147 auto *const qtTranslator = new QTranslator(QCoreApplication::instance());
148 const auto fileName = QString(repoName % QChar('_') % localeName);
149
150 QString path;
151 if (
152 // allow putting translations into configurable directory
153 (!additionalTranslationFilePath().isEmpty() && qtTranslator->load(fileName, path = additionalTranslationFilePath()))
154 // allow putting translations next to the Qt libraries
155 || qtTranslator->load(fileName,
156 path =
157#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
158 QLibraryInfo::location(QLibraryInfo::TranslationsPath)
159#else
160 QLibraryInfo::path(QLibraryInfo::TranslationsPath)
161#endif
162 )
163 // load translations from the "share" directory to support installations following the usual UNIX file system hierarchy
164 || qtTranslator->load(fileName, path = relBase + QStringLiteral("/../share/qt/translations"))
165 // load built-in translations
166 || qtTranslator->load(fileName, path = QStringLiteral(":/translations"))) {
167 QCoreApplication::installTranslator(qtTranslator);
168 translators.append(qtTranslator);
169 if (debugTranslations) {
170 cerr << "Loading translation file for Qt repository \"" << repoName.toLocal8Bit().data() << "\" and the locale \""
171 << localeName.toLocal8Bit().data() << "\" from \"" << path.toLocal8Bit().data() << "\"." << endl;
172 }
173 } else {
174 delete qtTranslator;
175 if (localeName.startsWith(QLatin1String("en"))) {
176 // the translation file is probably just empty (English is built-in and usually only used for plural forms)
177 continue;
178 }
179#if defined(Q_OS_ANDROID)
180 qDebug() << "Unable to load translation file for Qt repository: " << repoName << localeName;
181#else
182 cerr << "Unable to load translation file for Qt repository \"" << repoName.toLocal8Bit().data() << "\" and locale "
183 << localeName.toLocal8Bit().data() << "." << endl;
184#endif
185 }
186 }
187}
188
209void loadApplicationTranslationFile(const QString &configName, const QString &applicationName)
210{
211 // load English translation files as fallback
212 loadApplicationTranslationFile(configName, applicationName, QStringLiteral("en_US"));
213 // load translation files for current locale
214 const auto defaultLocale(QLocale().name());
215 if (defaultLocale != QLatin1String("en_US")) {
216 loadApplicationTranslationFile(configName, applicationName, defaultLocale);
217 }
218}
219
221static void logTranslationEvent(
222 const char *event, const QString &configName, const QString &applicationName, const QString &localeName, const QString &path = QString())
223{
224#if defined(Q_OS_ANDROID)
225 qDebug() << CppUtilities::argsToString(event, " translation file for: ").data() << applicationName << localeName;
226 if (!configName.isEmpty()) {
227 qDebug() << "config: " << configName;
228 }
229 if (!path.isEmpty()) {
230 qDebug() << "path: " << path;
231 }
232#else
233 cerr << event << " translation file for \"" << applicationName.toLocal8Bit().data() << "\"";
234 if (!configName.isEmpty()) {
235 cerr << " (config \"" << configName.toLocal8Bit().data() << "\")";
236 }
237 cerr << " and locale \"" << localeName.toLocal8Bit().data() << '\"';
238 if (!path.isEmpty()) {
239 cerr << " from \"" << path.toLocal8Bit().data() << '\"';
240 }
241 cerr << '.' << endl;
242#endif
243}
245
267void loadApplicationTranslationFile(const QString &configName, const QString &applicationName, const QString &localeName)
268{
269 auto *const appTranslator = new QTranslator(QCoreApplication::instance());
270 const auto fileName = QString(applicationName % QChar('_') % localeName);
271 const auto directoryName = configName.isEmpty() ? applicationName : QString(applicationName % QChar('-') % configName);
272 const auto relBase = relativeBase();
273
274 if (auto path = QString();
275 // allow putting translations into configurable directory
276 (!additionalTranslationFilePath().isEmpty() && appTranslator->load(fileName, path = additionalTranslationFilePath()))
277 // allow putting translations next to the executable (useful during development and for easily supplying additional
278 // translations, e.g. under Windows)
279 || appTranslator->load(fileName, path = relBase)
280 // load translations from sibling directories (useful during development to load translations of e.g. qtutilities
281 // from its build directory)
282 || appTranslator->load(fileName, path = relBase % QStringLiteral("/../") % applicationName)
283 || appTranslator->load(fileName, path = relBase % QStringLiteral("/../../") % applicationName)
284 // allow putting translations next to the executable into "translations" subdirectory (useful for easily supplying
285 // additional translations, e.g. under Windows)
286 || appTranslator->load(fileName, path = relBase % QStringLiteral("/translations"))
287 // load translations from the "share" directory to support installations following the usual UNIX file system hierarchy
288 || appTranslator->load(fileName, path = relBase % QStringLiteral("/../share/") % directoryName % QStringLiteral("/translations"))
289 || appTranslator->load(fileName, path = QStringLiteral(APP_INSTALL_PREFIX "/share/") % directoryName % QStringLiteral("/translations"))
290 // load built-in translations
291 || appTranslator->load(fileName, path = QStringLiteral(":/translations"))) {
292 QCoreApplication::installTranslator(appTranslator);
293 translators.append(appTranslator);
294 if (qEnvironmentVariableIntValue("QT_DEBUG_TRANSLATIONS")) {
295 logTranslationEvent("Loading", configName, applicationName, localeName, path);
296 }
297 } else {
298 delete appTranslator;
299 if (localeName.startsWith(QLatin1String("en"))) {
300 // the translation file is probably just empty (English is built-in and usually only used for plural forms)
301 return;
302 }
303 logTranslationEvent("Unable to load", configName, applicationName, localeName);
304 }
305}
306
313void loadApplicationTranslationFile(const QString &configName, const std::initializer_list<QString> &applicationNames)
314{
315 for (const QString &applicationName : applicationNames) {
316 loadApplicationTranslationFile(configName, applicationName);
317 }
318}
319
327void loadApplicationTranslationFile(const QString &configName, const std::initializer_list<QString> &applicationNames, const QString &localeName)
328{
329 for (const QString &applicationName : applicationNames) {
330 loadApplicationTranslationFile(configName, applicationName, localeName);
331 }
332}
333
338{
339 for (auto *const translator : translators) {
340 QCoreApplication::removeTranslator(translator);
341 }
342 translators.clear();
343}
344
345} // namespace TranslationFiles
346
352namespace ApplicationInstances {
353
354#if defined(QT_UTILITIES_GUI_QTWIDGETS)
358bool hasWidgetsApp()
359{
360 return qobject_cast<QApplication *>(QCoreApplication::instance()) != nullptr;
361}
362#endif
363
364#if defined(QT_UTILITIES_GUI_QTWIDGETS) || defined(QT_UTILITIES_GUI_QTQUICK)
368bool hasGuiApp()
369{
370 return qobject_cast<QGuiApplication *>(QCoreApplication::instance()) != nullptr;
371}
372#endif
373
378{
379 return qobject_cast<QCoreApplication *>(QCoreApplication::instance()) != nullptr;
380}
381} // namespace ApplicationInstances
382
390{
391 // enable dark window frame on Windows if the configured color palette is dark
392 // - supported as of Qt 6.4; no longer required as of Qt 6.5
393 // - see https://bugreports.qt.io/browse/QTBUG-72028?focusedCommentId=677819#comment-677819
394 // and https://www.qt.io/blog/dark-mode-on-windows-11-with-qt-6.5
395#if defined(Q_OS_WINDOWS) && (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) && (QT_VERSION < QT_VERSION_CHECK(6, 5, 0))
396 if (const auto qtVersion = QLibraryInfo::version();
397 qtVersion >= QVersionNumber(6, 4, 0) && qtVersion < QVersionNumber(6, 5, 0) && !qEnvironmentVariableIsSet("QT_QPA_PLATFORM")) {
398 qputenv("QT_QPA_PLATFORM", "windows:darkmode=1");
399 }
400#endif
401
402 // ensure FONTCONFIG_PATH is set (mainly required for static GNU/Linux builds)
403#ifdef QT_FEATURE_fontdialog
404 if (!qEnvironmentVariableIsSet("FONTCONFIG_PATH") && QDir(QStringLiteral("/etc/fonts")).exists()) {
405 qputenv("FONTCONFIG_PATH", "/etc/fonts");
406 }
407#endif
408
409 // enable settings for High-DPI scaling
410#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
411 if (!QCoreApplication::instance()) {
412 QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true);
413 }
414 QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true);
415#endif
416}
417
430std::unique_ptr<QSettings> getSettings(const QString &organization, const QString &application)
431{
432 auto settings = std::unique_ptr<QSettings>();
433 const auto portableFileName
434 = application.isEmpty() ? organization + QStringLiteral(".ini") : organization % QChar('/') % application % QStringLiteral(".ini");
435 if (const auto portableFileWorkingDir = QFile(portableFileName); portableFileWorkingDir.exists()) {
436 settings = std::make_unique<QSettings>(portableFileWorkingDir.fileName(), QSettings::IniFormat);
437 } else if (const auto portableFileNextToApp = QFile(QCoreApplication::applicationDirPath() % QChar('/') % portableFileName);
438 portableFileNextToApp.exists()) {
439 settings = std::make_unique<QSettings>(portableFileNextToApp.fileName(), QSettings::IniFormat);
440 } else {
441 settings = std::make_unique<QSettings>(QSettings::IniFormat, QSettings::UserScope, organization, application);
442 // move config created by older versions to new location
443 if (organization != QCoreApplication::organizationName() || application != QCoreApplication::applicationName()) {
444 const auto oldConfig
445 = QSettings(QSettings::IniFormat, QSettings::UserScope, QCoreApplication::organizationName(), QCoreApplication::applicationName())
446 .fileName();
447 QFile::rename(oldConfig, settings->fileName()) || QFile::remove(oldConfig);
448 }
449 }
450 loadSettingsWithLogging(*settings);
451 return settings;
452}
453
457void loadSettingsWithLogging(QSettings &settings)
458{
459 settings.sync();
460 if (!qEnvironmentVariableIntValue("QT_DEBUG_SETTINGS")) {
461 return;
462 }
463 const auto error = errorMessageForSettings(settings);
464 if (error.isEmpty()) {
465 std::cerr << "Loaded/synced settings from " << settings.fileName().toStdString() << '\n';
466 } else {
467 std::cerr << "Unable to load settings: " << error.toStdString() << '\n';
468 }
469}
470
474void saveSettingsWithLogging(QSettings &settings)
475{
476 settings.sync();
477 if (!qEnvironmentVariableIntValue("QT_DEBUG_SETTINGS")) {
478 return;
479 }
480 const auto error = errorMessageForSettings(settings);
481 if (error.isEmpty()) {
482 std::cerr << "Saved/synced settings to " << settings.fileName().toStdString() << '\n';
483 } else {
484 std::cerr << "Unable to save settings: " << error.toStdString() << '\n';
485 }
486}
487
491QString errorMessageForSettings(const QSettings &settings)
492{
493 auto errorMessage = QString();
494 switch (settings.status()) {
495 case QSettings::NoError:
496 return QString();
497 case QSettings::AccessError:
498 errorMessage = QCoreApplication::translate("QtUtilities", "unable to access file");
499 break;
500 case QSettings::FormatError:
501 errorMessage = QCoreApplication::translate("QtUtilities", "file has invalid format");
502 break;
503 default:
504 errorMessage = QCoreApplication::translate("QtUtilities", "unknown error");
505 }
506 return QCoreApplication::translate("QtUtilities", "Unable to sync settings from \"%1\": %2").arg(settings.fileName(), errorMessage);
507}
508
516{
517#ifdef Q_OS_ANDROID
518 // disable OpenGL pipeline cache under Android as it seems to break loading the app in certain cases
519 qputenv("QSG_RHI_DISABLE_DISK_CACHE", "1");
520#endif
521}
522
523} // 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:59
QT_UTILITIES_EXPORT void cleanup()
Frees the resources used and provided by this library.
Definition resources.cpp:68
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:88
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()
Disables the Qt Quick pipeline cache on platforms where this is needed to workaround issues with the ...
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.