File indexing completed on 2024-05-19 04:27:43
0001 /* 0002 * SPDX-FileCopyrightText: 2015 Boudewijn Rempt <boud@valdyas.org> 0003 * 0004 * SPDX-License-Identifier: LGPL-2.1-or-later 0005 */ 0006 #include "KoResourcePaths.h" 0007 0008 #include <QGlobalStatic> 0009 #include <QString> 0010 #include <QStringList> 0011 #include <QMap> 0012 #include <QStandardPaths> 0013 #include <QDir> 0014 #include <QFileInfo> 0015 #include <QDebug> 0016 #include <QCoreApplication> 0017 #include <QMutex> 0018 #include "kis_debug.h" 0019 #include "ksharedconfig.h" 0020 #include "kconfiggroup.h" 0021 #include "KisResourceLocator.h" 0022 #include "KisWindowsPackageUtils.h" 0023 0024 Q_GLOBAL_STATIC(KoResourcePaths, s_instance) 0025 0026 QString KoResourcePaths::s_overrideAppDataLocation; 0027 0028 namespace { 0029 0030 static QString cleanup(const QString &path) 0031 { 0032 return QDir::cleanPath(path); 0033 } 0034 0035 0036 static QStringList cleanup(const QStringList &pathList) 0037 { 0038 QStringList cleanedPathList; 0039 0040 bool getRidOfAppDataLocation = KoResourcePaths::getAppDataLocation() != QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); 0041 const QString writableLocation = []() { 0042 QString location = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); 0043 // we have to ensure that the location has a trailing separator, because otherwise when we'll do startsWith 0044 // check it will skip paths that start have the same path but different directory name. E.g: 0045 // ~/.local/share/krita -> AppDataLocation 0046 // ~/.local/share/krita3 -> custom location, but this will be skipped in getRidOfAppDataLocation. 0047 if (location.back() == '/') { 0048 return location; 0049 } else { 0050 return QString(location + "/"); 0051 } 0052 }(); 0053 0054 Q_FOREACH(const QString &path, pathList) { 0055 QString cleanPath = cleanup(path); 0056 if (getRidOfAppDataLocation && cleanPath.startsWith(writableLocation)) { 0057 continue; 0058 } 0059 cleanedPathList << cleanPath; 0060 } 0061 return cleanedPathList; 0062 } 0063 0064 static QStringList cleanupDirs(const QStringList &pathList) 0065 { 0066 QStringList cleanedPathList; 0067 0068 bool getRidOfAppDataLocation = KoResourcePaths::getAppDataLocation() != QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); 0069 const QString writableLocation = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); 0070 0071 Q_FOREACH(const QString &path, pathList) { 0072 QString cleanPath = QDir::cleanPath(path) + '/'; 0073 if (getRidOfAppDataLocation && cleanPath.startsWith(writableLocation)) { 0074 continue; 0075 } 0076 cleanedPathList << cleanPath; 0077 } 0078 return cleanedPathList; 0079 } 0080 0081 void appendResources(QStringList *dst, const QStringList &src, bool eliminateDuplicates) 0082 { 0083 Q_FOREACH (const QString &resource, src) { 0084 QString realPath = QDir::cleanPath(resource); 0085 if (!eliminateDuplicates || !dst->contains(realPath)) { 0086 *dst << realPath; 0087 } 0088 } 0089 } 0090 0091 0092 #ifdef Q_OS_WIN 0093 static const Qt::CaseSensitivity cs = Qt::CaseInsensitive; 0094 #else 0095 static const Qt::CaseSensitivity cs = Qt::CaseSensitive; 0096 #endif 0097 0098 #ifdef Q_OS_MACOS 0099 #include <ApplicationServices/ApplicationServices.h> 0100 #include <CoreFoundation/CoreFoundation.h> 0101 #include <CoreServices/CoreServices.h> 0102 #endif 0103 0104 QString getInstallationPrefix() { 0105 #ifdef Q_OS_MACOS 0106 QString appPath = qApp->applicationDirPath(); 0107 0108 dbgResources << "1" << appPath; 0109 appPath.chop(QString("MacOS/").length()); 0110 dbgResources << "2" << appPath; 0111 0112 bool makeInstall = QDir(appPath + "/../../../share/kritaplugins").exists(); 0113 bool inBundle = QDir(appPath + "/Resources/kritaplugins").exists(); 0114 0115 QString bundlePath; 0116 0117 if (inBundle) { 0118 bundlePath = appPath + "/"; 0119 } 0120 else if (makeInstall) { 0121 appPath.chop(QString("Contents/").length()); 0122 bundlePath = appPath + "/../../"; 0123 } 0124 else { 0125 // This is needed as tests will not run outside of the 0126 // install directory without this 0127 // This needs krita to be installed. 0128 QString envInstallPath = qgetenv("KIS_TEST_PREFIX_PATH"); 0129 if (!envInstallPath.isEmpty() && ( 0130 QDir(envInstallPath + "/share/kritaplugins").exists() 0131 || QDir(envInstallPath + "/Resources/kritaplugins").exists() )) 0132 { 0133 bundlePath = envInstallPath; 0134 } 0135 else { 0136 qFatal("Cannot calculate the bundle path from the app path"); 0137 qInfo() << "If running tests set KIS_TEST_PREFIX_PATH to krita install prefix"; 0138 } 0139 } 0140 0141 return bundlePath; 0142 #elif defined(Q_OS_ANDROID) 0143 // qApp->applicationDirPath() isn't writable and android system won't allow 0144 // any files other than libraries 0145 // NOTE the subscript [1]. It points to the internal location. 0146 return QStandardPaths::standardLocations(QStandardPaths::AppDataLocation)[1] + "/"; 0147 #else 0148 return qApp->applicationDirPath() + "/../"; 0149 #endif 0150 } 0151 0152 } 0153 0154 class Q_DECL_HIDDEN KoResourcePaths::Private { 0155 public: 0156 QMap<QString, QStringList> absolutes; // For each resource type, the list of absolute paths, from most local (most priority) to most global 0157 QMap<QString, QStringList> relatives; // Same with relative paths 0158 0159 QMutex relativesMutex; 0160 QMutex absolutesMutex; 0161 0162 QStringList aliases(const QString &type) 0163 { 0164 QStringList r; 0165 QStringList a; 0166 relativesMutex.lock(); 0167 if (relatives.contains(type)) { 0168 r += relatives[type]; 0169 } 0170 relativesMutex.unlock(); 0171 absolutesMutex.lock(); 0172 if (absolutes.contains(type)) { 0173 a += absolutes[type]; 0174 } 0175 absolutesMutex.unlock(); 0176 0177 return r + a; 0178 } 0179 0180 QStandardPaths::StandardLocation mapTypeToQStandardPaths(const QString &type) 0181 { 0182 if (type == "appdata") { 0183 return QStandardPaths::AppDataLocation; 0184 } 0185 else if (type == "data") { 0186 return QStandardPaths::AppDataLocation; 0187 } 0188 else if (type == "cache") { 0189 return QStandardPaths::CacheLocation; 0190 } 0191 else if (type == "locale") { 0192 return QStandardPaths::AppDataLocation; 0193 } 0194 else if (type == "genericdata") { 0195 return QStandardPaths::GenericDataLocation; 0196 } 0197 else { 0198 return QStandardPaths::AppDataLocation; 0199 } 0200 } 0201 }; 0202 0203 KoResourcePaths::KoResourcePaths() 0204 : d(new Private) 0205 { 0206 } 0207 0208 KoResourcePaths::~KoResourcePaths() 0209 { 0210 } 0211 0212 QString KoResourcePaths::getApplicationRoot() 0213 { 0214 return getInstallationPrefix(); 0215 } 0216 0217 QString KoResourcePaths::getAppDataLocation() 0218 { 0219 if (!s_overrideAppDataLocation.isEmpty()) { 0220 return s_overrideAppDataLocation; 0221 } 0222 0223 QString path; 0224 0225 KConfigGroup cfg(KSharedConfig::openConfig(), ""); 0226 path = cfg.readEntry(KisResourceLocator::resourceLocationKey, QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)); 0227 0228 QFileInfo fi(path); 0229 0230 // Check whether an existing location is writable 0231 if (fi.exists() && !fi.isWritable()) { 0232 path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); 0233 } 0234 else if (!fi.exists()) { 0235 // Check whether a non-existing location can be created 0236 if (!QDir().mkpath(path)) { 0237 path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); 0238 } 0239 QDir().rmpath(path); 0240 } 0241 return path; 0242 0243 0244 } 0245 0246 void KoResourcePaths::getAllUserResourceFoldersLocationsForWindowsStore(QString &standardLocation, QString &privateLocation) 0247 { 0248 standardLocation = ""; 0249 privateLocation = ""; 0250 QString resourcePath = QDir(KisResourceLocator::instance()->resourceLocationBase()).absolutePath(); 0251 #ifndef Q_OS_WIN 0252 // not Windows, no problem 0253 standardLocation = resourcePath; 0254 return; 0255 #else 0256 if (!KisWindowsPackageUtils::isRunningInPackage()) { 0257 standardLocation = resourcePath; // Windows, but not Windows Store, so no problem 0258 return; 0259 } 0260 0261 // running inside Windows Store 0262 const QDir resourceDir(resourcePath); 0263 QDir appDataGeneralDir = QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)); 0264 appDataGeneralDir.cdUp(); 0265 const QString appDataGeneralDirPath = appDataGeneralDir.path(); 0266 if (resourceDir.absolutePath().contains(appDataGeneralDirPath, Qt::CaseInsensitive)) { 0267 // resource folder location is inside appdata, so it can cause issues 0268 // from inside of Krita, we can't determine whether it uses genuine %AppData% or the private Windows Store one 0269 // so, half of the time, a custom folder inside %AppData% wouldn't work 0270 // we can't fix that, we can only inform users about it or prevent them from choosing such folder 0271 // in any case, here we need to return both folders: inside normal appdata and the private one 0272 // (note that this case also handles the default resource folder called "krita" inside the appdata) 0273 0274 0275 const QString folderName = QFileInfo(resourcePath).fileName(); 0276 0277 const QString privateAppData = KisWindowsPackageUtils::getPackageRoamingAppDataLocation(); 0278 const QDir privateResourceDir(QDir::fromNativeSeparators(privateAppData) + '/' + folderName); 0279 0280 standardLocation = resourcePath; 0281 0282 if (privateResourceDir.exists()) { 0283 privateLocation = privateResourceDir.absolutePath(); 0284 } 0285 0286 return; 0287 0288 } else { 0289 standardLocation = resourcePath; // custom folder not inside AppData, so no problem (hopefully) 0290 return; 0291 } 0292 0293 #endif 0294 } 0295 0296 void KoResourcePaths::addAssetType(const QString &type, const char *basetype, 0297 const QString &relativeName, bool priority) 0298 { 0299 s_instance->addResourceTypeInternal(type, QString::fromLatin1(basetype), relativeName, priority); 0300 } 0301 0302 void KoResourcePaths::addAssetDir(const QString &type, const QString &dir, bool priority) 0303 { 0304 s_instance->addResourceDirInternal(type, dir, priority); 0305 } 0306 0307 QString KoResourcePaths::findAsset(const QString &type, const QString &fileName) 0308 { 0309 return cleanup(s_instance->findResourceInternal(type, fileName)); 0310 } 0311 0312 QStringList KoResourcePaths::findDirs(const QString &type) 0313 { 0314 return cleanupDirs(s_instance->findDirsInternal(type)); 0315 } 0316 0317 QStringList KoResourcePaths::findAllAssets(const QString &type, 0318 const QString &filter, 0319 SearchOptions options) 0320 { 0321 return cleanup(s_instance->findAllResourcesInternal(type, filter, options)); 0322 } 0323 0324 QStringList KoResourcePaths::assetDirs(const QString &type) 0325 { 0326 return cleanupDirs(s_instance->resourceDirsInternal(type)); 0327 } 0328 0329 QString KoResourcePaths::saveLocation(const QString &type, const QString &suffix, bool create) 0330 { 0331 return QDir::cleanPath(s_instance->saveLocationInternal(type, suffix, create)) + '/'; 0332 } 0333 0334 QString KoResourcePaths::locate(const QString &type, const QString &filename) 0335 { 0336 return cleanup(s_instance->locateInternal(type, filename)); 0337 } 0338 0339 QString KoResourcePaths::locateLocal(const QString &type, const QString &filename, bool createDir) 0340 { 0341 return cleanup(s_instance->locateLocalInternal(type, filename, createDir)); 0342 } 0343 0344 void KoResourcePaths::addResourceTypeInternal(const QString &type, const QString &basetype, 0345 const QString &relativename, 0346 bool priority) 0347 { 0348 Q_UNUSED(basetype); 0349 if (relativename.isEmpty()) return; 0350 0351 QString copy = relativename; 0352 0353 Q_ASSERT(basetype == "data"); 0354 0355 if (!copy.endsWith(QLatin1Char('/'))) { 0356 copy += QLatin1Char('/'); 0357 } 0358 0359 d->relativesMutex.lock(); 0360 QStringList &rels = d->relatives[type]; // find or insert 0361 0362 if (!rels.contains(copy, cs)) { 0363 if (priority) { 0364 rels.prepend(copy); 0365 } else { 0366 rels.append(copy); 0367 } 0368 } 0369 d->relativesMutex.unlock(); 0370 0371 dbgResources << "addResourceType: type" << type << "basetype" << basetype << "relativename" << relativename << "priority" << priority << d->relatives[type]; 0372 } 0373 0374 void KoResourcePaths::addResourceDirInternal(const QString &type, const QString &absdir, bool priority) 0375 { 0376 if (absdir.isEmpty() || type.isEmpty()) return; 0377 0378 // find or insert entry in the map 0379 QString copy = absdir; 0380 if (copy.at(copy.length() - 1) != QLatin1Char('/')) { 0381 copy += QLatin1Char('/'); 0382 } 0383 0384 d->absolutesMutex.lock(); 0385 QStringList &paths = d->absolutes[type]; 0386 if (!paths.contains(copy, cs)) { 0387 if (priority) { 0388 paths.prepend(copy); 0389 } else { 0390 paths.append(copy); 0391 } 0392 } 0393 d->absolutesMutex.unlock(); 0394 0395 dbgResources << "addResourceDir: type" << type << "absdir" << absdir << "priority" << priority << d->absolutes[type]; 0396 } 0397 0398 QString KoResourcePaths::findResourceInternal(const QString &type, const QString &fileName) 0399 { 0400 QStringList aliases = d->aliases(type); 0401 dbgResources<< "aliases" << aliases << getApplicationRoot(); 0402 QString resource = QStandardPaths::locate(QStandardPaths::AppDataLocation, fileName, QStandardPaths::LocateFile); 0403 0404 if (resource.isEmpty()) { 0405 Q_FOREACH (const QString &alias, aliases) { 0406 resource = QStandardPaths::locate(d->mapTypeToQStandardPaths(type), alias + '/' + fileName, QStandardPaths::LocateFile); 0407 if (QFile::exists(resource)) { 0408 break; 0409 } 0410 } 0411 } 0412 if (resource.isEmpty() || !QFile::exists(resource)) { 0413 QString approot = getApplicationRoot(); 0414 Q_FOREACH (const QString &alias, aliases) { 0415 resource = approot + "/share/" + alias + '/' + fileName; 0416 if (QFile::exists(resource)) { 0417 break; 0418 } 0419 } 0420 } 0421 if (resource.isEmpty() || !QFile::exists(resource)) { 0422 QString approot = getApplicationRoot(); 0423 Q_FOREACH (const QString &alias, aliases) { 0424 resource = approot + "/share/krita/" + alias + '/' + fileName; 0425 if (QFile::exists(resource)) { 0426 break; 0427 } 0428 } 0429 } 0430 0431 if (resource.isEmpty() || !QFile::exists(resource)) { 0432 QStringList extraResourceDirs = findExtraResourceDirs(); 0433 0434 if (!extraResourceDirs.isEmpty()) { 0435 Q_FOREACH(const QString &extraResourceDir, extraResourceDirs) { 0436 if (aliases.isEmpty()) { 0437 resource = extraResourceDir + '/' + fileName; 0438 dbgResources<< "\t4" << resource; 0439 if (QFile::exists(resource)) { 0440 break; 0441 } 0442 } 0443 else { 0444 Q_FOREACH (const QString &alias, aliases) { 0445 resource = extraResourceDir + '/' + alias + '/' + fileName; 0446 dbgResources<< "\t4" << resource; 0447 if (QFile::exists(resource)) { 0448 break; 0449 } 0450 } 0451 } 0452 } 0453 } 0454 } 0455 0456 dbgResources<< "findResource: type" << type << "filename" << fileName << "resource" << resource; 0457 Q_ASSERT(!resource.isEmpty()); 0458 return resource; 0459 } 0460 0461 0462 QStringList filesInDir(const QString &startdir, const QString & filter, bool recursive) 0463 { 0464 dbgResources << "filesInDir: startdir" << startdir << "filter" << filter << "recursive" << recursive; 0465 QStringList result; 0466 0467 // First the entries in this path 0468 QStringList nameFilters; 0469 nameFilters << filter; 0470 const QStringList fileNames = QDir(startdir).entryList(nameFilters, QDir::Files | QDir::CaseSensitive, QDir::Name); 0471 dbgResources << "\tFound:" << fileNames.size() << ":" << fileNames; 0472 Q_FOREACH (const QString &fileName, fileNames) { 0473 QString file = startdir + '/' + fileName; 0474 result << file; 0475 } 0476 0477 // And then everything underneath, if recursive is specified 0478 if (recursive) { 0479 const QStringList entries = QDir(startdir).entryList(QDir::Dirs | QDir::NoDotAndDotDot); 0480 Q_FOREACH (const QString &subdir, entries) { 0481 dbgResources << "\tGoing to look in subdir" << subdir << "of" << startdir; 0482 result << filesInDir(startdir + '/' + subdir, filter, recursive); 0483 } 0484 } 0485 return result; 0486 } 0487 0488 QStringList KoResourcePaths::findDirsInternal(const QString &type) 0489 { 0490 QStringList aliases = d->aliases(type); 0491 dbgResources << type << aliases << d->mapTypeToQStandardPaths(type); 0492 0493 QStringList dirs; 0494 QStringList standardDirs = 0495 QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), "", QStandardPaths::LocateDirectory); 0496 0497 appendResources(&dirs, standardDirs, true); 0498 0499 Q_FOREACH (const QString &alias, aliases) { 0500 QStringList aliasDirs = 0501 QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), alias + '/', QStandardPaths::LocateDirectory); 0502 appendResources(&dirs, aliasDirs, true); 0503 0504 #ifdef Q_OS_MACOS 0505 dbgResources << "MAC:" << getApplicationRoot(); 0506 QStringList bundlePaths; 0507 bundlePaths << getApplicationRoot() + "/share/krita/" + alias; 0508 bundlePaths << getApplicationRoot() + "/../share/krita/" + alias; 0509 dbgResources << "bundlePaths" << bundlePaths; 0510 appendResources(&dirs, bundlePaths, true); 0511 Q_ASSERT(!dirs.isEmpty()); 0512 #endif 0513 0514 QStringList fallbackPaths; 0515 fallbackPaths << getApplicationRoot() + "/share/" + alias; 0516 fallbackPaths << getApplicationRoot() + "/share/krita/" + alias; 0517 appendResources(&dirs, fallbackPaths, true); 0518 0519 } 0520 0521 QStringList saveLocationList; 0522 saveLocationList << saveLocation(type, QString(), true); 0523 appendResources(&dirs, saveLocationList, true); 0524 0525 dbgResources << "findDirs: type" << type << "resource" << dirs; 0526 return dirs; 0527 } 0528 0529 0530 QStringList KoResourcePaths::findAllResourcesInternal(const QString &type, 0531 const QString &_filter, 0532 SearchOptions options) const 0533 { 0534 dbgResources << "====================================================="; 0535 dbgResources << type << _filter << QStandardPaths::standardLocations(d->mapTypeToQStandardPaths(type)); 0536 0537 bool recursive = options & KoResourcePaths::Recursive; 0538 0539 dbgResources << "findAllResources: type" << type << "filter" << _filter << "recursive" << recursive; 0540 0541 QStringList aliases = d->aliases(type); 0542 QString filter = _filter; 0543 0544 // In cases where the filter is like "color-schemes/*.colors" instead of "*.kpp", used with unregistered resource types 0545 if (filter.indexOf('*') > 0) { 0546 aliases << filter.split('*').first(); 0547 filter = '*' + filter.split('*')[1]; 0548 dbgResources << "Split up alias" << aliases << "filter" << filter; 0549 } 0550 0551 QStringList resources; 0552 if (aliases.isEmpty()) { 0553 QStringList standardResources = 0554 QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), 0555 filter, QStandardPaths::LocateFile); 0556 dbgResources << "standardResources" << standardResources; 0557 appendResources(&resources, standardResources, true); 0558 dbgResources << "1" << resources; 0559 } 0560 0561 QStringList extraResourceDirs = findExtraResourceDirs(); 0562 0563 if (!extraResourceDirs.isEmpty()) { 0564 Q_FOREACH(const QString &extraResourceDir, extraResourceDirs) { 0565 if (aliases.isEmpty()) { 0566 appendResources(&resources, filesInDir(extraResourceDir + '/' + type, filter, recursive), true); 0567 } 0568 else { 0569 Q_FOREACH (const QString &alias, aliases) { 0570 appendResources(&resources, filesInDir(extraResourceDir + '/' + alias + '/', filter, recursive), true); 0571 } 0572 } 0573 } 0574 0575 } 0576 0577 dbgResources << "\tresources from qstandardpaths:" << resources.size(); 0578 0579 Q_FOREACH (const QString &alias, aliases) { 0580 dbgResources << "\t\talias:" << alias; 0581 QStringList dirs; 0582 0583 QFileInfo dirInfo(alias); 0584 if (dirInfo.exists() && dirInfo.isDir() && dirInfo.isAbsolute()) { 0585 dirs << alias; 0586 } else { 0587 dirs << QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), alias, QStandardPaths::LocateDirectory) 0588 << getInstallationPrefix() + "share/" + alias + "/" 0589 << getInstallationPrefix() + "share/krita/" + alias + "/"; 0590 } 0591 0592 Q_FOREACH (const QString &dir, dirs) { 0593 appendResources(&resources, 0594 filesInDir(dir, filter, recursive), 0595 true); 0596 } 0597 } 0598 0599 dbgResources << "\tresources also from aliases:" << resources.size(); 0600 0601 // if the original filter is "input/*", we only want share/input/* and share/krita/input/* here, but not 0602 // share/*. therefore, use _filter here instead of filter which was split into alias and "*". 0603 QFileInfo fi(_filter); 0604 0605 QStringList prefixResources; 0606 prefixResources << filesInDir(getInstallationPrefix() + "share/" + fi.path(), fi.fileName(), false); 0607 prefixResources << filesInDir(getInstallationPrefix() + "share/krita/" + fi.path(), fi.fileName(), false); 0608 appendResources(&resources, prefixResources, true); 0609 0610 dbgResources << "\tresources from installation:" << resources.size(); 0611 dbgResources << "====================================================="; 0612 0613 return resources; 0614 } 0615 0616 QStringList KoResourcePaths::resourceDirsInternal(const QString &type) 0617 { 0618 QStringList resourceDirs; 0619 QStringList aliases = d->aliases(type); 0620 0621 Q_FOREACH (const QString &alias, aliases) { 0622 QStringList aliasDirs; 0623 0624 aliasDirs << QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), alias, QStandardPaths::LocateDirectory); 0625 0626 aliasDirs << getInstallationPrefix() + "share/" + alias + "/" 0627 << QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), alias, QStandardPaths::LocateDirectory); 0628 aliasDirs << getInstallationPrefix() + "share/krita/" + alias + "/" 0629 << QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), alias, QStandardPaths::LocateDirectory); 0630 0631 appendResources(&resourceDirs, aliasDirs, true); 0632 } 0633 0634 dbgResources << "resourceDirs: type" << type << resourceDirs; 0635 0636 return resourceDirs; 0637 } 0638 0639 QString KoResourcePaths::saveLocationInternal(const QString &type, const QString &suffix, bool create) 0640 { 0641 QString path; 0642 0643 bool useStandardLocation = false; 0644 const QStringList aliases = d->aliases(type); 0645 const QStandardPaths::StandardLocation location = d->mapTypeToQStandardPaths(type); 0646 0647 if (location == QStandardPaths::AppDataLocation) { 0648 KConfigGroup cfg(KSharedConfig::openConfig(), ""); 0649 path = cfg.readEntry(KisResourceLocator::resourceLocationKey, ""); 0650 } 0651 0652 if (path.isEmpty()) { 0653 path = QStandardPaths::writableLocation(location); 0654 useStandardLocation = true; 0655 } 0656 0657 #ifndef Q_OS_ANDROID 0658 // on Android almost all config locations we save to are app specific, 0659 // and don't end with "krita". 0660 if (!path.endsWith("krita") && useStandardLocation) { 0661 path += "/krita"; 0662 } 0663 #endif 0664 0665 if (!aliases.isEmpty()) { 0666 path += '/' + aliases.first(); 0667 } else { 0668 0669 if (!suffix.isEmpty()) { 0670 path += "/" + suffix; 0671 } 0672 } 0673 0674 QDir d(path); 0675 if (!d.exists() && create) { 0676 d.mkpath(path); 0677 } 0678 dbgResources << "saveLocation: type" << type << "suffix" << suffix << "create" << create << "path" << path; 0679 0680 return path; 0681 } 0682 0683 QString KoResourcePaths::locateInternal(const QString &type, const QString &filename) 0684 { 0685 QStringList aliases = d->aliases(type); 0686 0687 QStringList locations; 0688 if (aliases.isEmpty()) { 0689 locations << QStandardPaths::locate(d->mapTypeToQStandardPaths(type), filename, QStandardPaths::LocateFile); 0690 } 0691 0692 Q_FOREACH (const QString &alias, aliases) { 0693 locations << QStandardPaths::locate(d->mapTypeToQStandardPaths(type), 0694 (alias.endsWith('/') ? alias : alias + '/') + filename, QStandardPaths::LocateFile); 0695 } 0696 dbgResources << "locate: type" << type << "filename" << filename << "locations" << locations; 0697 if (locations.size() > 0) { 0698 return locations.first(); 0699 } 0700 else { 0701 return ""; 0702 } 0703 } 0704 0705 QString KoResourcePaths::locateLocalInternal(const QString &type, const QString &filename, bool createDir) 0706 { 0707 QString path = saveLocationInternal(type, "", createDir); 0708 dbgResources << "locateLocal: type" << type << "filename" << filename << "CreateDir" << createDir << "path" << path; 0709 return path + '/' + filename; 0710 } 0711 0712 QStringList KoResourcePaths::findExtraResourceDirs() const 0713 { 0714 #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) 0715 QStringList extraResourceDirs = 0716 QString::fromUtf8(qgetenv("EXTRA_RESOURCE_DIRS")) 0717 .split(';', Qt::SkipEmptyParts); 0718 #else 0719 QStringList extraResourceDirs = 0720 QString::fromUtf8(qgetenv("EXTRA_RESOURCE_DIRS")) 0721 .split(';', QString::SkipEmptyParts); 0722 #endif 0723 0724 const KConfigGroup cfg(KSharedConfig::openConfig(), ""); 0725 const QString customPath = 0726 cfg.readEntry(KisResourceLocator::resourceLocationKey, ""); 0727 if (!customPath.isEmpty()) { 0728 extraResourceDirs << customPath; 0729 } 0730 0731 if (getAppDataLocation() != QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)) { 0732 extraResourceDirs << getAppDataLocation(); 0733 } 0734 0735 return extraResourceDirs; 0736 }