File indexing completed on 2024-04-28 04:41:59

0001 /* This file is part of the KDE project
0002    Copyright (C) 2015-2019 Jarosław Staniek <staniek@kde.org>
0003    Copyright (C) 2016 Adam Pigg <adam@piggz.co.uk>
0004 
0005    This library is free software; you can redistribute it and/or
0006    modify it under the terms of the GNU Library General Public
0007    License as published by the Free Software Foundation; either
0008    version 2.1 of the License, or (at your option) any later version.
0009 
0010    This library is distributed in the hope that it will be useful,
0011    but WITHOUT ANY WARRANTY; without even the implied warranty of
0012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013    Library General Public License for more details.
0014 
0015    You should have received a copy of the GNU Library General Public License
0016    along with this library; see the file COPYING.LIB.  If not, write to
0017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018    Boston, MA 02110-1301, USA.
0019 */
0020 
0021 #include "KReportUtils_p.h"
0022 #include "config-kreport.h"
0023 
0024 #include <KConfigGroup>
0025 #include <KSharedConfig>
0026 #include <KMessageBox>
0027 
0028 #include <QApplication>
0029 #include <QDebug>
0030 #include <QDir>
0031 #include <QFileInfo>
0032 #include <QGlobalStatic>
0033 #include <QPrinter>
0034 #include <QRegularExpression>
0035 #include <QResource>
0036 #include <QStandardPaths>
0037 
0038 #ifdef Q_WS_X11
0039 #include <QX11Info>
0040 #else
0041 #include <QDesktopWidget>
0042 #endif
0043 
0044 #ifdef Q_OS_WIN
0045 #define KPATH_SEPARATOR ';'
0046 #else
0047 #define KPATH_SEPARATOR ':'
0048 #endif
0049 
0050 class KReportDpiSingleton
0051 {
0052 public:
0053     KReportDpiSingleton();
0054 
0055     int m_dpiX;
0056     int m_dpiY;
0057 };
0058 
0059 KReportDpiSingleton::KReportDpiSingleton()
0060 {
0061     // Another way to get the DPI of the display would be QPaintDeviceMetrics,
0062     // but we have no widget here (and moving this to KoView wouldn't allow
0063     // using this from the document easily).
0064 #ifdef Q_WS_X11
0065     m_dpiX = QX11Info::appDpiX();
0066     m_dpiY = QX11Info::appDpiY();
0067 #else
0068     QDesktopWidget *w = QApplication::desktop();
0069     if (w) {
0070         m_dpiX = w->logicalDpiX();
0071         m_dpiY = w->logicalDpiY();
0072     } else {
0073         m_dpiX = 96;
0074         m_dpiY = 96;
0075     }
0076 #endif
0077 }
0078 
0079 Q_GLOBAL_STATIC(KReportDpiSingleton, s_instance)
0080 
0081 
0082 
0083 namespace KReportPrivate
0084 {
0085 
0086 bool fileReadable(const QString &path)
0087 {
0088     return !path.isEmpty() && QFileInfo(path).isReadable();
0089 }
0090 
0091 QStringList correctStandardLocations(const QString &privateName,
0092                                      QStandardPaths::StandardLocation location,
0093                                      const QString &extraLocation)
0094  {
0095     QStringList result;
0096     if (!privateName.isEmpty()) {
0097         QRegularExpression re(QLatin1Char('/') + QCoreApplication::applicationName() + QLatin1Char('$'));
0098         QStringList standardLocations(QStandardPaths::standardLocations(location));
0099         if (!extraLocation.isEmpty()) {
0100             standardLocations.append(extraLocation);
0101         }
0102         for(const QString &dir : standardLocations) {
0103             if (dir.indexOf(re) != -1) {
0104                 QString realDir(dir);
0105                 realDir.replace(re, QLatin1Char('/') + privateName);
0106                 result.append(realDir);
0107             }
0108         }
0109     }
0110     return result;
0111 }
0112 
0113 QString locateFile(const QString &privateName,
0114                    const QString& path, QStandardPaths::StandardLocation location,
0115                    const QString &extraLocation)
0116 {
0117     // let QStandardPaths handle this, it will look for app local stuff
0118     QString fullPath = QFileInfo(
0119         QStandardPaths::locate(location, path)).canonicalFilePath();
0120     if (fileReadable(fullPath)) {
0121         return fullPath;
0122     }
0123 
0124     // Try extra location
0125     fullPath = QFileInfo(extraLocation + QLatin1Char('/') + path).canonicalFilePath();
0126     if (fileReadable(fullPath)) {
0127         return fullPath;
0128     }
0129     // Try in PATH subdirs, useful for running apps from the build dir, without installing
0130     for(const QByteArray &pathDir : qgetenv("PATH").split(KPATH_SEPARATOR)) {
0131         const QString dataDirFromPath = QFileInfo(QFile::decodeName(pathDir) + QStringLiteral("/data/")
0132                                                   + path).canonicalFilePath();
0133         if (fileReadable(dataDirFromPath)) {
0134             return dataDirFromPath;
0135         }
0136     }
0137 
0138     const QStringList correctedStandardLocations(correctStandardLocations(privateName, location, extraLocation));
0139     for(const QString &dir : correctedStandardLocations) {
0140         fullPath = QFileInfo(dir + QLatin1Char('/') + path).canonicalFilePath();
0141         if (fileReadable(fullPath)) {
0142             return fullPath;
0143         }
0144     }
0145     return QString();
0146 }
0147 
0148 bool registerIconsResource(const QString &privateName, const QString& path,
0149                              QStandardPaths::StandardLocation location,
0150                              const QString &resourceRoot, const QString &extraLocation,
0151                              QString *errorMessage, QString *detailedErrorMessage)
0152 {
0153     const QString fullPath = locateFile(privateName, path, location, extraLocation);
0154     if (fullPath.isEmpty() || !QFileInfo(fullPath).isReadable()
0155         || !QResource::registerResource(fullPath, resourceRoot))
0156     {
0157         QStringList triedLocations(QStandardPaths::standardLocations(location));
0158         if (!extraLocation.isEmpty()) {
0159             triedLocations.append(extraLocation);
0160         }
0161         triedLocations.append(correctStandardLocations(privateName, location, extraLocation));
0162         const QString triedLocationsString = QLocale().createSeparatedList(triedLocations);
0163 #ifdef QT_ONLY
0164         *errorMessage = QString("Could not open icon resource file %1.").arg(path);
0165         *detailedErrorMessage = QString("Tried to find in %1.").arg(triedLocationsString);
0166 #else
0167         //! @todo 3.1 Re-add translation
0168         *errorMessage = /*QObject::tr*/ QString::fromLatin1(
0169             "Could not open icon resource file \"%1\". "
0170             "Application will not start. "
0171             "Please check if it is properly installed.")
0172             .arg(QFileInfo(path).fileName());
0173         //! @todo 3.1 Re-add translation
0174         *detailedErrorMessage = QString::fromLatin1("Tried to find in %1.").arg(triedLocationsString);
0175 #endif
0176         return false;
0177     }
0178     *errorMessage = QString();
0179     *detailedErrorMessage = QString();
0180     return true;
0181 }
0182 
0183 bool registerGlobalIconsResource(const QString &themeName,
0184                                  QString *errorMessage,
0185                                  QString *detailedErrorMessage)
0186 {
0187     QString extraLocation;
0188 #ifdef CMAKE_INSTALL_FULL_ICONDIR
0189     extraLocation = QDir::fromNativeSeparators(QFile::decodeName(CMAKE_INSTALL_FULL_ICONDIR));
0190     if (extraLocation.endsWith("/icons")) {
0191         extraLocation.chop(QLatin1String("/icons").size());
0192     }
0193 #elif defined(Q_OS_WIN)
0194     extraLocation = QCoreApplication::applicationDirPath() + QStringLiteral("/data");
0195 #endif
0196     return registerIconsResource(QString(), QString::fromLatin1("icons/%1/%1-icons.rcc").arg(themeName),
0197                             QStandardPaths::GenericDataLocation,
0198                             QStringLiteral("/icons/") + themeName,
0199                             extraLocation, errorMessage,
0200                             detailedErrorMessage);
0201 }
0202 
0203 bool registerGlobalIconsResource(const QString &themeName)
0204 {
0205     QString errorMessage;
0206     QString detailedErrorMessage;
0207     if (!registerGlobalIconsResource(themeName, &errorMessage, &detailedErrorMessage)) {
0208         if (detailedErrorMessage.isEmpty()) {
0209             KMessageBox::error(nullptr, errorMessage);
0210         } else {
0211             KMessageBox::detailedError(nullptr, errorMessage, detailedErrorMessage);
0212         }
0213         qWarning() << qPrintable(errorMessage);
0214         return false;
0215     }
0216     return true;
0217 }
0218 
0219 bool registerGlobalIconsResource()
0220 {
0221     return registerGlobalIconsResource(supportedIconTheme);
0222 }
0223 
0224 bool setupPrivateIconsResource(const QString &privateName, const QString& path,
0225                                const QString &themeName,
0226                                QString *errorMessage, QString *detailedErrorMessage,
0227                                const QString &prefix)
0228 {
0229     // Register application's resource first to have priority over the theme.
0230     // Some icons may exists in both resources.
0231     if (!registerIconsResource(privateName, path,
0232                           QStandardPaths::AppDataLocation,
0233                           QString(), QString(), errorMessage, detailedErrorMessage))
0234     {
0235         return false;
0236     }
0237     bool changeTheme = false;
0238 #ifdef QT_GUI_LIB
0239     QIcon::setThemeSearchPaths(QStringList() << prefix << QIcon::themeSearchPaths());
0240     changeTheme = 0 != QIcon::themeName().compare(themeName, Qt::CaseInsensitive);
0241     if (changeTheme) {
0242         QIcon::setThemeName(themeName);
0243     }
0244 #endif
0245 
0246     KConfigGroup cg(KSharedConfig::openConfig(), "Icons");
0247     changeTheme = changeTheme || 0 != cg.readEntry("Theme", QString()).compare(themeName, Qt::CaseInsensitive);
0248     // tell KIconLoader an co. about the theme
0249     if (changeTheme) {
0250         cg.writeEntry("Theme", themeName);
0251         cg.sync();
0252     }
0253     return true;
0254 }
0255 
0256 bool setupPrivateIconsResourceWithMessage(const QString &privateName, const QString& path,
0257                                           const QString &themeName,
0258                                           QString *errorMessage, QString *detailedErrorMessage,
0259                                           const QString &prefix)
0260 {
0261     if (!setupPrivateIconsResource(privateName, path, themeName,
0262                                    errorMessage, detailedErrorMessage, prefix))
0263     {
0264         if (detailedErrorMessage->isEmpty()) {
0265             KMessageBox::error(nullptr, *errorMessage);
0266         } else {
0267             KMessageBox::detailedError(nullptr, *errorMessage, *detailedErrorMessage);
0268         }
0269         return false;
0270     }
0271     return true;
0272 }
0273 
0274 bool setupPrivateIconsResourceWithMessage(const QString &privateName, const QString& path,
0275                                           QString *errorMessage, QString *detailedErrorMessage,
0276                                           const QString &prefix)
0277 {
0278     return setupPrivateIconsResourceWithMessage(privateName, path, supportedIconTheme,
0279                                                 errorMessage, detailedErrorMessage, prefix);
0280 }
0281 
0282 bool setupPrivateIconsResourceWithMessage(const QString &privateName, const QString& path,
0283                                           QtMsgType messageType,
0284                                           const QString &prefix)
0285 {
0286     QString errorMessage;
0287     QString detailedErrorMessage;
0288     if (!setupPrivateIconsResourceWithMessage(privateName, path,
0289                                               &errorMessage, &detailedErrorMessage, prefix)) {
0290         if (messageType == QtFatalMsg) {
0291             qFatal("%s %s", qPrintable(errorMessage), qPrintable(detailedErrorMessage));
0292         } else {
0293             qWarning() << qPrintable(errorMessage) << qPrintable(detailedErrorMessage);
0294         }
0295         return false;
0296     }
0297     return true;
0298 }
0299 
0300 bool setupGlobalIconTheme()
0301 {
0302     if (0 != QIcon::themeName().compare(supportedIconTheme, Qt::CaseInsensitive)) {
0303         const QString message = QString::fromLatin1(
0304             "\"%1\"  supports only \"%2\" icon theme but current system theme is \"%3\". "
0305             "Application's icon theme will be changed to \"%2\". "
0306             "Please consider adding support for other themes to %4.")
0307             .arg(QLatin1String(KREPORT_BASE_NAME)).arg(supportedIconTheme).arg(QIcon::themeName())
0308             .arg(QCoreApplication::applicationName());
0309         qDebug() << qPrintable(message);
0310         if (!registerGlobalIconsResource()) {
0311             // don't fail, just warn
0312             const QString message = QString::fromLatin1(
0313                 "Failed to set icon theme to \"%1\". Icons in the application will be inconsistent. "
0314                 "Please install .rcc file(s) for the system theme.")
0315                 .arg(supportedIconTheme);
0316             qDebug() << qPrintable(message);
0317             return false;
0318         }
0319     }
0320     return true;
0321 }
0322 
0323 int dpiX()
0324 {
0325     return s_instance->m_dpiX;
0326 }
0327 
0328 int dpiY()
0329 {
0330     return s_instance->m_dpiY;
0331 }
0332 
0333 Q_GLOBAL_STATIC_WITH_ARGS(QPrinter, s_printerInstance, (QPrinter::HighResolution))
0334 
0335 QPrinter* highResolutionPrinter()
0336 {
0337     return s_printerInstance;
0338 }
0339 
0340 PageLayout::PageLayout() : QPageLayout(QPageSize(QPageSize::A4), QPageLayout::Portrait, QMarginsF(0,0,0,0))
0341 {
0342 }
0343 
0344 PageLayout::PageLayout(const QPageLayout& pageLayout) : QPageLayout(pageLayout)
0345 {
0346 }
0347 
0348 KReportPrivate::PageLayout & PageLayout::operator=(const QPageLayout& other)
0349 {
0350     QPageLayout::operator=(other);
0351     return *this;
0352 }
0353 
0354 }