File indexing completed on 2024-05-19 15:10:51
0001 /* 0002 SPDX-FileCopyrightText: 2007 Carlo Segato <brandon.ml@gmail.com> 0003 SPDX-FileCopyrightText: 2008 Montel Laurent <montel@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.1-or-later 0006 */ 0007 0008 #include "kemoticons.h" 0009 #include "kemoticonsglobal_p.h" 0010 #include "kemoticonsprovider.h" 0011 0012 #include <QFile> 0013 #include <QDir> 0014 #include <QMimeDatabase> 0015 #include <QStandardPaths> 0016 #include "kemoticons_core_debug.h" 0017 #include <QFileSystemWatcher> 0018 0019 #include <KPluginLoader> 0020 #include <KPluginMetaData> 0021 #include <KConfigGroup> 0022 #include <KSharedConfig> 0023 #include <KTar> 0024 #include <kzip.h> 0025 0026 Q_GLOBAL_STATIC(KEmoticonsGlobal, s_global) 0027 0028 class KEmoticonsPrivate 0029 { 0030 public: 0031 KEmoticonsPrivate(KEmoticons *parent); 0032 ~KEmoticonsPrivate(); 0033 void loadServiceList(); 0034 KEmoticonsProvider *loadProvider(const KPluginMetaData &plugin); 0035 KEmoticonsProvider *loadProvider(const KService::Ptr &service); 0036 KEmoticonsTheme loadTheme(const QString &name); 0037 0038 QList<KService::Ptr> m_oldStylePlugins; 0039 QVector<KPluginMetaData> m_plugins; 0040 QHash<QString, KEmoticonsTheme> m_themes; 0041 QFileSystemWatcher m_fileWatcher; 0042 KEmoticons *q; 0043 QSize m_preferredSize; 0044 0045 //private slots 0046 void changeTheme(const QString &path); 0047 }; 0048 0049 KEmoticonsPrivate::KEmoticonsPrivate(KEmoticons *parent) 0050 : q(parent) 0051 { 0052 } 0053 0054 KEmoticonsPrivate::~KEmoticonsPrivate() 0055 { 0056 } 0057 0058 static bool priorityLessThan(const KService::Ptr &s1, const KService::Ptr &s2) 0059 { 0060 return (s1->property(QStringLiteral("X-KDE-Priority")).toInt() > s2->property(QStringLiteral("X-KDE-Priority")).toInt()); 0061 } 0062 0063 void KEmoticonsPrivate::loadServiceList() 0064 { 0065 const QString constraint(QStringLiteral("(exist Library)")); 0066 m_oldStylePlugins = KServiceTypeTrader::self()->query(QStringLiteral("KEmoticons"), constraint); 0067 std::sort(m_oldStylePlugins.begin(), m_oldStylePlugins.end(), priorityLessThan); 0068 0069 m_plugins = KPluginLoader::findPlugins(QStringLiteral("kf5/emoticonsthemes")); 0070 std::sort(m_plugins.begin(), m_plugins.end(), [](const KPluginMetaData &s1, const KPluginMetaData &s2) { 0071 return s1.rawData().value(QStringLiteral("X-KDE-Priority")).toInt() > s2.rawData().value(QStringLiteral("X-KDE-Priority")).toInt(); 0072 }); 0073 } 0074 0075 KEmoticonsProvider *KEmoticonsPrivate::loadProvider(const KPluginMetaData &plugin) 0076 { 0077 KPluginFactory *factory = qobject_cast<KPluginFactory *>(plugin.instantiate()); 0078 if (!factory) { 0079 qCWarning(KEMOTICONS_CORE) << "Invalid plugin factory for" << plugin.fileName(); 0080 return nullptr; 0081 } 0082 KEmoticonsProvider *provider = factory->create<KEmoticonsProvider>(nullptr); 0083 return provider; 0084 } 0085 0086 KEmoticonsProvider *KEmoticonsPrivate::loadProvider(const KService::Ptr &service) 0087 { 0088 KPluginFactory *factory = KPluginLoader(service->library()).factory(); 0089 if (!factory) { 0090 qCWarning(KEMOTICONS_CORE) << "Invalid plugin factory for" << service->library(); 0091 return nullptr; 0092 } 0093 KEmoticonsProvider *provider = factory->create<KEmoticonsProvider>(nullptr); 0094 return provider; 0095 } 0096 0097 void KEmoticonsPrivate::changeTheme(const QString &path) 0098 { 0099 const QFileInfo info(path); 0100 const QString name = info.dir().dirName(); 0101 0102 if (m_themes.contains(name)) { 0103 loadTheme(name); 0104 } 0105 } 0106 0107 KEmoticonsTheme KEmoticonsPrivate::loadTheme(const QString &name) 0108 { 0109 const auto registerProvider = [this](const QString &name, const QString &path, KEmoticonsProvider *provider) { 0110 if (m_preferredSize.isValid()) { 0111 provider->setPreferredEmoticonSize(m_preferredSize); 0112 } 0113 KEmoticonsTheme theme(provider); 0114 provider->loadTheme(path); 0115 m_themes.insert(name, theme); 0116 m_fileWatcher.addPath(path); 0117 return theme; 0118 }; 0119 0120 for (const KPluginMetaData &plugin : std::as_const(m_plugins)) { 0121 const QString fName = plugin.rawData().value(QStringLiteral("X-KDE-EmoticonsFileName")).toString(); 0122 const QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("emoticons/") + name + QLatin1Char('/') + fName); 0123 0124 if (QFile::exists(path)) { 0125 KEmoticonsProvider *provider = loadProvider(plugin); 0126 if (provider) { 0127 return registerProvider(name, path, provider); 0128 } 0129 } 0130 } 0131 // KF6: remove support for old plugins 0132 for (const KService::Ptr &service : std::as_const(m_oldStylePlugins)) { 0133 const QString fName = service->property(QStringLiteral("X-KDE-EmoticonsFileName")).toString(); 0134 const QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("emoticons/") + name + QLatin1Char('/') + fName); 0135 0136 if (QFile::exists(path)) { 0137 KEmoticonsProvider *provider = loadProvider(service); 0138 if (provider) { 0139 return registerProvider(name, path, provider); 0140 } 0141 } 0142 } 0143 return KEmoticonsTheme(); 0144 } 0145 0146 KEmoticons::KEmoticons() 0147 : d(new KEmoticonsPrivate(this)) 0148 { 0149 d->loadServiceList(); 0150 connect(&d->m_fileWatcher, &QFileSystemWatcher::fileChanged, this, [this](const QString &str) {d->changeTheme(str);}); 0151 } 0152 0153 KEmoticons::~KEmoticons() 0154 { 0155 } 0156 0157 KEmoticonsTheme KEmoticons::theme() const 0158 { 0159 return theme(currentThemeName()); 0160 } 0161 0162 KEmoticonsTheme KEmoticons::theme(const QString &name) const 0163 { 0164 KEmoticonsTheme theme = d->m_themes.value(name); 0165 if (!theme.isNull()) { 0166 return theme; 0167 } 0168 0169 return d->loadTheme(name); 0170 } 0171 0172 QString KEmoticons::currentThemeName() 0173 { 0174 return s_global()->m_themeName; 0175 } 0176 0177 QStringList KEmoticons::themeList() 0178 { 0179 const QStringList themeDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("emoticons"), QStandardPaths::LocateDirectory); 0180 QStringList ls; 0181 ls.reserve(themeDirs.count()); 0182 0183 for (int i = 0; i < themeDirs.count(); ++i) { 0184 QDir themeQDir(themeDirs[i]); 0185 themeQDir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot); 0186 themeQDir.setSorting(QDir::Name); 0187 ls << themeQDir.entryList(); 0188 } 0189 return ls; 0190 } 0191 0192 void KEmoticons::setTheme(const KEmoticonsTheme &theme) 0193 { 0194 setTheme(theme.themeName()); 0195 } 0196 0197 void KEmoticons::setTheme(const QString &theme) 0198 { 0199 s_global()->setThemeName(theme); 0200 } 0201 0202 KEmoticonsTheme::ParseMode KEmoticons::parseMode() 0203 { 0204 return s_global()->m_parseMode; 0205 } 0206 0207 void KEmoticons::setParseMode(KEmoticonsTheme::ParseMode mode) 0208 { 0209 s_global()->setParseMode(mode); 0210 } 0211 0212 KEmoticonsTheme KEmoticons::newTheme(const QString &name, const KService::Ptr &service) 0213 { 0214 KEmoticonsProvider *provider = d->loadProvider(service); 0215 if (provider) { 0216 KEmoticonsTheme theme(provider); 0217 theme.setThemeName(name); 0218 0219 provider->newTheme(); 0220 0221 return theme; 0222 } 0223 return KEmoticonsTheme(); 0224 } 0225 0226 QStringList KEmoticons::installTheme(const QString &archiveName) 0227 { 0228 QStringList foundThemes; 0229 const KArchiveEntry *currentEntry = nullptr; 0230 const KArchiveDirectory *currentDir = nullptr; 0231 KArchive *archive = nullptr; 0232 0233 const QString localThemesDir(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/emoticons")); 0234 0235 if (localThemesDir.isEmpty()) { 0236 qCCritical(KEMOTICONS_CORE) << "Could not find a suitable place in which to install the emoticon theme"; 0237 return QStringList(); 0238 } 0239 0240 QMimeDatabase db; 0241 const QString currentBundleMimeType = db.mimeTypeForFile(archiveName).name(); 0242 0243 if (currentBundleMimeType == QLatin1String("application/zip") || 0244 currentBundleMimeType == QLatin1String("application/x-zip") || 0245 currentBundleMimeType == QLatin1String("application/x-zip-compressed")) { 0246 archive = new KZip(archiveName); 0247 } else if (currentBundleMimeType == QLatin1String("application/x-compressed-tar") || 0248 currentBundleMimeType == QLatin1String("application/x-bzip-compressed-tar") || 0249 currentBundleMimeType == QLatin1String("application/x-lzma-compressed-tar") || 0250 currentBundleMimeType == QLatin1String("application/x-xz-compressed-tar") || 0251 currentBundleMimeType == QLatin1String("application/x-gzip") || 0252 currentBundleMimeType == QLatin1String("application/x-bzip") || 0253 currentBundleMimeType == QLatin1String("application/x-lzma") || 0254 currentBundleMimeType == QLatin1String("application/x-xz")) { 0255 archive = new KTar(archiveName); 0256 } else if (archiveName.endsWith(QLatin1String("jisp")) || archiveName.endsWith(QLatin1String("zip"))) { 0257 archive = new KZip(archiveName); 0258 } else { 0259 archive = new KTar(archiveName); 0260 } 0261 0262 if (!archive || !archive->open(QIODevice::ReadOnly)) { 0263 qCCritical(KEMOTICONS_CORE) << "Could not open" << archiveName << "for unpacking"; 0264 delete archive; 0265 return QStringList(); 0266 } 0267 0268 const KArchiveDirectory *rootDir = archive->directory(); 0269 0270 // iterate all the dirs looking for an emoticons.xml file 0271 const QStringList entries = rootDir->entries(); 0272 for (QStringList::ConstIterator it = entries.begin(); it != entries.end(); ++it) { 0273 currentEntry = const_cast<KArchiveEntry *>(rootDir->entry(*it)); 0274 0275 if (currentEntry->isDirectory()) { 0276 currentDir = dynamic_cast<const KArchiveDirectory *>(currentEntry); 0277 0278 for (const KPluginMetaData &plugin : std::as_const(d->m_plugins)) { 0279 const QString fName = plugin.rawData().value(QStringLiteral("X-KDE-EmoticonsFileName")).toString(); 0280 if (currentDir && currentDir->entry(fName) != nullptr) { 0281 foundThemes.append(currentDir->name()); 0282 } 0283 } 0284 for (const KService::Ptr &service : std::as_const(d->m_oldStylePlugins)) { 0285 const QString fName = service->property(QStringLiteral("X-KDE-EmoticonsFileName")).toString(); 0286 if (currentDir && currentDir->entry(fName) != nullptr) { 0287 foundThemes.append(currentDir->name()); 0288 } 0289 } 0290 } 0291 } 0292 0293 if (foundThemes.isEmpty()) { 0294 qCCritical(KEMOTICONS_CORE) << "The file" << archiveName << "is not a valid emoticon theme archive"; 0295 archive->close(); 0296 delete archive; 0297 return QStringList(); 0298 } 0299 0300 for (int themeIndex = 0; themeIndex < foundThemes.size(); ++themeIndex) { 0301 const QString &theme = foundThemes[themeIndex]; 0302 0303 currentEntry = const_cast<KArchiveEntry *>(rootDir->entry(theme)); 0304 if (currentEntry == nullptr) { 0305 // qCDebug(KEMOTICONS_CORE) << "couldn't get next archive entry"; 0306 continue; 0307 } 0308 0309 if (currentEntry->isDirectory()) { 0310 currentDir = dynamic_cast<const KArchiveDirectory *>(currentEntry); 0311 0312 if (currentDir == nullptr) { 0313 // qCDebug(KEMOTICONS_CORE) << "couldn't cast archive entry to KArchiveDirectory"; 0314 continue; 0315 } 0316 0317 currentDir->copyTo(localThemesDir + theme); 0318 } 0319 } 0320 0321 archive->close(); 0322 delete archive; 0323 0324 return foundThemes; 0325 } 0326 0327 void KEmoticons::setPreferredEmoticonSize(const QSize &size) 0328 { 0329 d->m_preferredSize = size; 0330 } 0331 0332 QSize KEmoticons::preferredEmoticonSize() const 0333 { 0334 return d->m_preferredSize; 0335 } 0336 0337 #include "moc_kemoticons.cpp"