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 #include <iostream>
24 
25 using namespace std;
26 
27 ///! \cond
initResources()28 inline void initResources()
29 {
30     Q_INIT_RESOURCE(qtutilsicons);
31 }
32 
cleanupResources()33 inline void cleanupResources()
34 {
35     Q_CLEANUP_RESOURCE(qtutilsicons);
36 }
37 ///! \endcond
38 
39 namespace QtUtilities {
40 
41 /*!
42  * \brief Functions for using the resources provided by this library.
43  * \deprecated Replaced by ENABLE_QT_RESOURCES_OF_STATIC_DEPENDENCIES macro.
44  */
45 namespace QtUtilitiesResources {
46 
47 /*!
48  * \brief Initiates the resources used and provided by this library.
49  * \deprecated Replaced by ENABLE_QT_RESOURCES_OF_STATIC_DEPENDENCIES macro.
50  */
init()51 void init()
52 {
53     initResources();
54 }
55 
56 /*!
57  * \brief Frees the resources used and provided by this library.
58  * \deprecated Replaced by ENABLE_QT_RESOURCES_OF_STATIC_DEPENDENCIES macro.
59  */
cleanup()60 void cleanup()
61 {
62     cleanupResources();
63 }
64 } // namespace QtUtilitiesResources
65 
66 /*!
67  * \brief Convenience functions to load translations for Qt and the application.
68  */
69 namespace TranslationFiles {
70 
71 /*!
72  * \brief Allows to set an additional search path for translation files.
73  * \remarks This path is considered *before* the default directories.
74  */
additionalTranslationFilePath()75 QString &additionalTranslationFilePath()
76 {
77     static QString path;
78     return path;
79 }
80 
81 /*!
82  * \brief Loads and installs the appropriate Qt translation file for the current
83  * locale.
84  * \param repositoryNames Specifies the names of the Qt repositories to load
85  * translations for (eg. qtbase, qtscript, ...).
86  * \remarks
87  *  - Translation files have to be placed in one of the following locations:
88  *    * QLibraryInfo::location(QLibraryInfo::TranslationsPath) (used in UNIX)
89  *    * ../share/qt/translations (used in Windows)
90  *  - Translation files can also be built-in using by setting the CMake variable
91  *    BUILTIN_TRANSLATIONS.
92  *    In this case it is also necessary to load the translations using this
93  *    function.
94  */
loadQtTranslationFile(std::initializer_list<QString> repositoryNames)95 void loadQtTranslationFile(std::initializer_list<QString> repositoryNames)
96 {
97     loadQtTranslationFile(repositoryNames, QLocale().name());
98 }
99 
100 /*!
101  * \brief Loads and installs the appropriate Qt translation file for the
102  * specified locale.
103  * \param repositoryNames Specifies the names of the Qt repositories to load
104  * translations for (eg. qtbase, qtscript, ...).
105  * \param localeName Specifies the name of the locale.
106  * \remarks
107  *  - Translation files have to be placed in one of the following locations:
108  *    * QLibraryInfo::location(QLibraryInfo::TranslationsPath) (used in UNIX)
109  *    * ../share/qt/translations (used in Windows)
110  *  - Translation files can also be built-in using by setting the CMake variable
111  *    BUILTIN_TRANSLATIONS.
112  *    In this case it is also necessary to load the translations using this
113  *    function.
114  */
loadQtTranslationFile(initializer_list<QString> repositoryNames,const QString & localeName)115 void loadQtTranslationFile(initializer_list<QString> repositoryNames, const QString &localeName)
116 {
117     const auto debugTranslations = qEnvironmentVariableIsSet("QT_DEBUG_TRANSLATIONS");
118     for (const auto &repoName : repositoryNames) {
119         auto *const qtTranslator = new QTranslator(QCoreApplication::instance());
120         const auto fileName = QString(repoName % QChar('_') % localeName);
121 
122         QString path;
123         if ((!additionalTranslationFilePath().isEmpty() && qtTranslator->load(fileName, path = additionalTranslationFilePath()))
124             || qtTranslator->load(fileName,
125                 path =
126 #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
127                     QLibraryInfo::location(QLibraryInfo::TranslationsPath)
128 #else
129                     QLibraryInfo::path(QLibraryInfo::TranslationsPath)
130 #endif
131                     )
132             || qtTranslator->load(fileName, path = QStringLiteral("../share/qt/translations"))
133             || qtTranslator->load(fileName, path = QStringLiteral(":/translations"))) {
134             QCoreApplication::installTranslator(qtTranslator);
135             if (debugTranslations) {
136                 cerr << "Loading translation file for Qt repository \"" << repoName.toLocal8Bit().data() << "\" and the locale \""
137                      << localeName.toLocal8Bit().data() << "\" from \"" << path.toLocal8Bit().data() << "\"." << endl;
138             }
139         } else {
140             delete qtTranslator;
141             if (localeName.startsWith(QLatin1String("en"))) {
142                 // the translation file is probably just empty (English is built-in and usually only used for plural forms)
143                 continue;
144             }
145             cerr << "Unable to load translation file for Qt repository \"" << repoName.toLocal8Bit().data() << "\" and locale "
146                  << localeName.toLocal8Bit().data() << "." << endl;
147         }
148     }
149 }
150 
151 /*!
152  * \brief Loads and installs the appropriate application translation file for
153  * the current locale.
154  * \param applicationName Specifies the name of the application.
155  * \remarks
156  *  - Translation files have to be placed in one of the following locations:
157  *    * ./
158  *    * ../$application
159  *    * ../../$application
160  *    * ./translations
161  *    * ../share/$application/translations
162  *    * $install_prefix/share/$application/translations
163  *  - Translation files must be named using the following scheme:
164  *    * $application_$language.qm
165  *  - Translation files can also be built-in using by setting the CMake variable
166  *    BUILTIN_TRANSLATIONS.
167  *    In this case it is also necessary to load the translations using this
168  *    function.
169  */
loadApplicationTranslationFile(const QString & configName,const QString & applicationName)170 void loadApplicationTranslationFile(const QString &configName, const QString &applicationName)
171 {
172     // load English translation files as fallback
173     loadApplicationTranslationFile(configName, applicationName, QStringLiteral("en_US"));
174     // load translation files for current locale
175     const auto defaultLocale(QLocale().name());
176     if (defaultLocale != QLatin1String("en_US")) {
177         loadApplicationTranslationFile(configName, applicationName, defaultLocale);
178     }
179 }
180 
181 /// \cond
logTranslationEvent(const char * event,const QString & configName,const QString & applicationName,const QString & localeName,const QString & path=QString ())182 void logTranslationEvent(
183     const char *event, const QString &configName, const QString &applicationName, const QString &localeName, const QString &path = QString())
184 {
185     cerr << event << " translation file for \"" << applicationName.toLocal8Bit().data() << "\"";
186     if (!configName.isEmpty()) {
187         cerr << " (config \"" << configName.toLocal8Bit().data() << "\")";
188     }
189     cerr << " and locale \"" << localeName.toLocal8Bit().data() << '\"';
190     if (!path.isEmpty()) {
191         cerr << " from \"" << path.toLocal8Bit().data() << '\"';
192     }
193     cerr << '.' << endl;
194 }
195 /// \endcond
196 
197 /*!
198  * \brief Loads and installs the appropriate application translation file for
199  * the specified locale.
200  * \param applicationName Specifies the name of the application.
201  * \param localeName Specifies the name of the locale.
202  * \remarks
203  *  - Translation files have to be placed in one of the following locations:
204  *    * ./
205  *    * ../$application
206  *    * ../../$application
207  *    * ./translations
208  *    * ../share/$application/translations
209  *    * $install_prefix/share/$application/translations
210  *  - Translation files must be named using the following scheme:
211  *    * $application_$language.qm
212  *  - Translation files can also be built-in using by setting the CMake variable
213  *    BUILTIN_TRANSLATIONS.
214  *    In this case it is also necessary to load the translations using this
215  *    function.
216  */
loadApplicationTranslationFile(const QString & configName,const QString & applicationName,const QString & localeName)217 void loadApplicationTranslationFile(const QString &configName, const QString &applicationName, const QString &localeName)
218 {
219     auto *const appTranslator = new QTranslator(QCoreApplication::instance());
220     const auto fileName = QString(applicationName % QChar('_') % localeName);
221     const auto directoryName = configName.isEmpty() ? applicationName : QString(applicationName % QChar('-') % configName);
222 
223     QString path;
224     if ((!additionalTranslationFilePath().isEmpty() && appTranslator->load(fileName, path = additionalTranslationFilePath()))
225         || appTranslator->load(fileName, path = QStringLiteral(".")) || appTranslator->load(fileName, path = QStringLiteral("../") % directoryName)
226         || appTranslator->load(fileName, path = QStringLiteral("../") % directoryName)
227         || appTranslator->load(fileName, path = QStringLiteral("../../") % directoryName)
228         || appTranslator->load(fileName, path = QStringLiteral("./translations"))
229         || appTranslator->load(fileName, path = QStringLiteral("../share/") % directoryName % QStringLiteral("/translations"))
230         || appTranslator->load(fileName, path = QStringLiteral(APP_INSTALL_PREFIX "/share/") % directoryName % QStringLiteral("/translations"))
231         || appTranslator->load(fileName, path = QStringLiteral(":/translations"))) {
232         QCoreApplication::installTranslator(appTranslator);
233         if (qEnvironmentVariableIsSet("QT_DEBUG_TRANSLATIONS")) {
234             logTranslationEvent("Loading", configName, applicationName, localeName, path);
235         }
236     } else {
237         delete appTranslator;
238         if (localeName.startsWith(QLatin1String("en"))) {
239             // the translation file is probably just empty (English is built-in and usually only used for plural forms)
240             return;
241         }
242         logTranslationEvent("Unable to load", configName, applicationName, localeName);
243     }
244 }
245 
246 /*!
247  * \brief Loads and installs the appropriate application translation file for
248  * the current locale.
249  * \param applicationNames Specifies the names of the applications.
250  */
loadApplicationTranslationFile(const QString & configName,const std::initializer_list<QString> & applicationNames)251 void loadApplicationTranslationFile(const QString &configName, const std::initializer_list<QString> &applicationNames)
252 {
253     for (const QString &applicationName : applicationNames) {
254         loadApplicationTranslationFile(configName, applicationName);
255     }
256 }
257 
258 /*!
259  * \brief Loads and installs the appropriate application translation file for
260  * the specified locale.
261  * \param applicationNames Specifies the names of the applications.
262  * \param localeName Specifies the name of the locale.
263  */
loadApplicationTranslationFile(const QString & configName,const std::initializer_list<QString> & applicationNames,const QString & localeName)264 void loadApplicationTranslationFile(const QString &configName, const std::initializer_list<QString> &applicationNames, const QString &localeName)
265 {
266     for (const QString &applicationName : applicationNames) {
267         loadApplicationTranslationFile(configName, applicationName, localeName);
268     }
269 }
270 } // namespace TranslationFiles
271 
272 /*!
273  * \brief Convenience functions to check whether a
274  * QCoreApplication/QGuiApplication/QApplication singleton has been instantiated
275  * yet.
276  */
277 namespace ApplicationInstances {
278 
279 #if defined(QT_UTILITIES_GUI_QTWIDGETS)
280 /*!
281  * \brief Returns whether a QApplication has been instantiated yet.
282  */
hasWidgetsApp()283 bool hasWidgetsApp()
284 {
285     return qobject_cast<QApplication *>(QCoreApplication::instance()) != nullptr;
286 }
287 #endif
288 
289 #if defined(QT_UTILITIES_GUI_QTWIDGETS) || defined(QT_UTILITIES_GUI_QTQUICK)
290 /*!
291  * \brief Returns whether a QGuiApplication has been instantiated yet.
292  */
hasGuiApp()293 bool hasGuiApp()
294 {
295     return qobject_cast<QGuiApplication *>(QCoreApplication::instance()) != nullptr;
296 }
297 #endif
298 
299 /*!
300  * \brief Returns whether a QCoreApplication has been instantiated yet.
301  */
hasCoreApp()302 bool hasCoreApp()
303 {
304     return qobject_cast<QCoreApplication *>(QCoreApplication::instance()) != nullptr;
305 }
306 } // namespace ApplicationInstances
307 
308 /*!
309  * \brief Sets Qt application attributes which are commonly used within my Qt applications.
310  * \remarks
311  * - So far this enables High-DPI support.
312  * - The exact attributes are unspecified and might change to whatever makes sense in the future.
313  */
setupCommonQtApplicationAttributes()314 void setupCommonQtApplicationAttributes()
315 {
316 #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
317     QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true);
318     QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true);
319 #endif
320 }
321 
322 // namespace ApplicationInstances
323 
324 } // namespace QtUtilities
325