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 }