3#include "resources/config.h"
13#include <QStringBuilder>
15#if defined(QT_UTILITIES_GUI_QTWIDGETS)
16#include <QApplication>
17#elif defined(QT_UTILITIES_GUI_QTQUICK)
18#include <QGuiApplication>
20#include <QCoreApplication>
28#include <c++utilities/conversion/stringbuilder.h>
36inline void initResources()
38 Q_INIT_RESOURCE(qtutilsicons);
41inline void cleanupResources()
43 Q_CLEANUP_RESOURCE(qtutilsicons);
77namespace TranslationFiles {
82static QList<QTranslator *> translators;
95static QString relativeBase()
97 static const auto relativeBase = [] {
98 auto appDir = QCoreApplication::applicationDirPath();
99 if (appDir.isEmpty()) {
100 appDir = QStringLiteral(
".");
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);
155 || qtTranslator->load(fileName,
157#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
158 QLibraryInfo::location(QLibraryInfo::TranslationsPath)
160 QLibraryInfo::path(QLibraryInfo::TranslationsPath)
164 || qtTranslator->load(fileName, path = relBase + QStringLiteral(
"/../share/qt/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;
175 if (localeName.startsWith(QLatin1String(
"en"))) {
179#if defined(Q_OS_ANDROID)
180 qDebug() <<
"Unable to load translation file for Qt repository: " << repoName << localeName;
182 cerr <<
"Unable to load translation file for Qt repository \"" << repoName.toLocal8Bit().data() <<
"\" and locale "
183 << localeName.toLocal8Bit().data() <<
"." << endl;
214 const auto defaultLocale(QLocale().name());
215 if (defaultLocale != QLatin1String(
"en_US")) {
221static void logTranslationEvent(
222 const char *event,
const QString &configName,
const QString &applicationName,
const QString &localeName,
const QString &path = QString())
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;
229 if (!path.isEmpty()) {
230 qDebug() <<
"path: " << path;
233 cerr <<
event <<
" translation file for \"" << applicationName.toLocal8Bit().data() <<
"\"";
234 if (!configName.isEmpty()) {
235 cerr <<
" (config \"" << configName.toLocal8Bit().data() <<
"\")";
237 cerr <<
" and locale \"" << localeName.toLocal8Bit().data() <<
'\"';
238 if (!path.isEmpty()) {
239 cerr <<
" from \"" << path.toLocal8Bit().data() <<
'\"';
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();
274 if (
auto path = QString();
279 || appTranslator->load(fileName, path = relBase)
282 || appTranslator->load(fileName, path = relBase % QStringLiteral(
"/../") % applicationName)
283 || appTranslator->load(fileName, path = relBase % QStringLiteral(
"/../../") % applicationName)
286 || appTranslator->load(fileName, path = relBase % QStringLiteral(
"/translations"))
288 || appTranslator->load(fileName, path = relBase % QStringLiteral(
"/../share/") % directoryName % QStringLiteral(
"/translations"))
289 || appTranslator->load(fileName, path = QStringLiteral(APP_INSTALL_PREFIX
"/share/") % directoryName % QStringLiteral(
"/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);
298 delete appTranslator;
299 if (localeName.startsWith(QLatin1String(
"en"))) {
303 logTranslationEvent(
"Unable to load", configName, applicationName, localeName);
315 for (
const QString &applicationName : applicationNames) {
329 for (
const QString &applicationName : applicationNames) {
339 for (
auto *
const translator : translators) {
340 QCoreApplication::removeTranslator(translator);
352namespace ApplicationInstances {
354#if defined(QT_UTILITIES_GUI_QTWIDGETS)
360 return qobject_cast<QApplication *>(QCoreApplication::instance()) !=
nullptr;
364#if defined(QT_UTILITIES_GUI_QTWIDGETS) || defined(QT_UTILITIES_GUI_QTQUICK)
370 return qobject_cast<QGuiApplication *>(QCoreApplication::instance()) !=
nullptr;
379 return qobject_cast<QCoreApplication *>(QCoreApplication::instance()) !=
nullptr;
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");
403#ifdef QT_FEATURE_fontdialog
404 if (!qEnvironmentVariableIsSet(
"FONTCONFIG_PATH") && QDir(QStringLiteral(
"/etc/fonts")).exists()) {
405 qputenv(
"FONTCONFIG_PATH",
"/etc/fonts");
410#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
411 if (!QCoreApplication::instance()) {
412 QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling,
true);
414 QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps,
true);
430std::unique_ptr<QSettings>
getSettings(
const QString &organization,
const QString &application)
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);
441 settings = std::make_unique<QSettings>(QSettings::IniFormat, QSettings::UserScope, organization, application);
443 if (organization != QCoreApplication::organizationName() || application != QCoreApplication::applicationName()) {
445 = QSettings(QSettings::IniFormat, QSettings::UserScope, QCoreApplication::organizationName(), QCoreApplication::applicationName())
447 QFile::rename(oldConfig, settings->fileName()) || QFile::remove(oldConfig);
460 if (!qEnvironmentVariableIntValue(
"QT_DEBUG_SETTINGS")) {
464 if (error.isEmpty()) {
465 std::cerr <<
"Loaded/synced settings from " << settings.fileName().toStdString() <<
'\n';
467 std::cerr <<
"Unable to load settings: " << error.toStdString() <<
'\n';
477 if (!qEnvironmentVariableIntValue(
"QT_DEBUG_SETTINGS")) {
481 if (error.isEmpty()) {
482 std::cerr <<
"Saved/synced settings to " << settings.fileName().toStdString() <<
'\n';
484 std::cerr <<
"Unable to save settings: " << error.toStdString() <<
'\n';
493 auto errorMessage = QString();
494 switch (settings.status()) {
495 case QSettings::NoError:
497 case QSettings::AccessError:
498 errorMessage = QCoreApplication::translate(
"QtUtilities",
"unable to access file");
500 case QSettings::FormatError:
501 errorMessage = QCoreApplication::translate(
"QtUtilities",
"file has invalid format");
504 errorMessage = QCoreApplication::translate(
"QtUtilities",
"unknown error");
506 return QCoreApplication::translate(
"QtUtilities",
"Unable to sync settings from \"%1\": %2").arg(settings.fileName(), errorMessage);
519 qputenv(
"QSG_RHI_DISABLE_DISK_CACHE",
"1");
QT_UTILITIES_EXPORT bool hasCoreApp()
Returns whether a QCoreApplication has been instantiated yet.
Functions for using the resources provided by this library.
QT_UTILITIES_EXPORT void init()
Initiates the resources used and provided by this library.
QT_UTILITIES_EXPORT void cleanup()
Frees the resources used and provided by this library.
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.
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.