File indexing completed on 2024-05-12 05:21:08
0001 /* 0002 This file is part of the KDE Kontact Plugin Interface Library. 0003 0004 SPDX-FileCopyrightText: 2001 Matthias Hoelzer-Kluepfel <mhk@kde.org> 0005 SPDX-FileCopyrightText: 2002-2003 Daniel Molkentin <molkentin@kde.org> 0006 0007 SPDX-License-Identifier: LGPL-2.0-or-later 0008 */ 0009 0010 #include "plugin.h" 0011 #include "core.h" 0012 #include "kontactinterface_debug.h" 0013 #include "processes.h" 0014 0015 #include <KAboutData> 0016 #include <KIO/CommandLauncherJob> 0017 #include <KXMLGUIFactory> 0018 0019 #include <QDBusConnection> 0020 #include <QDir> 0021 #include <QDomDocument> 0022 #include <QFileInfo> 0023 0024 #include <QCoreApplication> 0025 #include <QStandardPaths> 0026 0027 using namespace KontactInterface; 0028 0029 /** 0030 PluginPrivate class that helps to provide binary compatibility between releases. 0031 @internal 0032 */ 0033 //@cond PRIVATE 0034 class Q_DECL_HIDDEN Plugin::PluginPrivate 0035 { 0036 public: 0037 void partDestroyed(); 0038 void setXmlFiles(); 0039 void removeInvisibleToolbarActions(Plugin *plugin); 0040 0041 Core *core = nullptr; 0042 QList<QAction *> newActions; 0043 QList<QAction *> syncActions; 0044 QString identifier; 0045 QString title; 0046 QString icon; 0047 QString executableName; 0048 QString serviceName; 0049 QByteArray partLibraryName; 0050 QByteArray pluginName; 0051 KParts::Part *part = nullptr; 0052 bool hasPart = true; 0053 bool disabled = false; 0054 }; 0055 //@endcond 0056 Plugin::Plugin(Core *core, QObject *parent, const KPluginMetaData &, const char *appName, const char *pluginName) 0057 : KXMLGUIClient(core) 0058 , QObject(parent) 0059 , d(new PluginPrivate) 0060 { 0061 setObjectName(QLatin1StringView(appName)); 0062 core->factory()->addClient(this); 0063 0064 d->pluginName = pluginName ? pluginName : appName; 0065 d->core = core; 0066 } 0067 0068 Plugin::~Plugin() 0069 { 0070 delete d->part; 0071 } 0072 0073 void Plugin::setIdentifier(const QString &identifier) 0074 { 0075 d->identifier = identifier; 0076 } 0077 0078 QString Plugin::identifier() const 0079 { 0080 return d->identifier; 0081 } 0082 0083 void Plugin::setTitle(const QString &title) 0084 { 0085 d->title = title; 0086 } 0087 0088 QString Plugin::title() const 0089 { 0090 return d->title; 0091 } 0092 0093 void Plugin::setIcon(const QString &icon) 0094 { 0095 d->icon = icon; 0096 } 0097 0098 QString Plugin::icon() const 0099 { 0100 return d->icon; 0101 } 0102 0103 void Plugin::setExecutableName(const QString &bin) 0104 { 0105 d->executableName = bin; 0106 } 0107 0108 QString Plugin::executableName() const 0109 { 0110 return d->executableName; 0111 } 0112 0113 void Plugin::setPartLibraryName(const QByteArray &libName) 0114 { 0115 d->partLibraryName = libName; 0116 } 0117 0118 bool Plugin::isRunningStandalone() const 0119 { 0120 return false; 0121 } 0122 0123 KParts::Part *Plugin::loadPart() 0124 { 0125 return core()->createPart(d->partLibraryName.constData()); 0126 } 0127 0128 const KAboutData Plugin::aboutData() 0129 { 0130 return KAboutData(); 0131 } 0132 0133 KParts::Part *Plugin::part() 0134 { 0135 if (!d->part) { 0136 d->part = createPart(); 0137 if (d->part) { 0138 connect(d->part, &KParts::Part::destroyed, this, [this]() { 0139 d->partDestroyed(); 0140 }); 0141 d->removeInvisibleToolbarActions(this); 0142 core()->partLoaded(this, d->part); 0143 } 0144 } 0145 return d->part; 0146 } 0147 0148 QString Plugin::registerClient() 0149 { 0150 if (d->serviceName.isEmpty()) { 0151 d->serviceName = QLatin1StringView("org.kde.") + QLatin1StringView(objectName().toLatin1()); 0152 #ifdef Q_OS_WIN 0153 const QString pid = QString::number(QCoreApplication::applicationPid()); 0154 d->serviceName.append(QLatin1StringView(".unique-") + pid); 0155 #endif 0156 QDBusConnection::sessionBus().registerService(d->serviceName); 0157 } 0158 return d->serviceName; 0159 } 0160 0161 int Plugin::weight() const 0162 { 0163 return 0; 0164 } 0165 0166 void Plugin::insertNewAction(QAction *action) 0167 { 0168 d->newActions.append(action); 0169 } 0170 0171 void Plugin::insertSyncAction(QAction *action) 0172 { 0173 d->syncActions.append(action); 0174 } 0175 0176 QList<QAction *> Plugin::newActions() const 0177 { 0178 return d->newActions; 0179 } 0180 0181 QList<QAction *> Plugin::syncActions() const 0182 { 0183 return d->syncActions; 0184 } 0185 0186 QStringList Plugin::invisibleToolbarActions() const 0187 { 0188 return {}; 0189 } 0190 0191 bool Plugin::canDecodeMimeData(const QMimeData *data) const 0192 { 0193 Q_UNUSED(data) 0194 return false; 0195 } 0196 0197 void Plugin::processDropEvent(QDropEvent *) 0198 { 0199 } 0200 0201 void Plugin::readProperties(const KConfigGroup &) 0202 { 0203 } 0204 0205 void Plugin::saveProperties(KConfigGroup &) 0206 { 0207 } 0208 0209 Core *Plugin::core() const 0210 { 0211 return d->core; 0212 } 0213 0214 void Plugin::aboutToSelect() 0215 { 0216 // Because the 3 korganizer plugins share the same part, we need to switch 0217 // that part's XML files every time we are about to show its GUI... 0218 d->setXmlFiles(); 0219 0220 select(); 0221 } 0222 0223 void Plugin::select() 0224 { 0225 } 0226 0227 void Plugin::configUpdated() 0228 { 0229 } 0230 0231 //@cond PRIVATE 0232 void Plugin::PluginPrivate::partDestroyed() 0233 { 0234 part = nullptr; 0235 } 0236 0237 void Plugin::PluginPrivate::removeInvisibleToolbarActions(Plugin *plugin) 0238 { 0239 if (pluginName.isEmpty()) { 0240 return; 0241 } 0242 0243 // Hide unwanted toolbar action by modifying the XML before createGUI, rather 0244 // than doing it by calling removeAction on the toolbar after createGUI. Both 0245 // solutions work visually, but only modifying the XML ensures that the 0246 // actions don't appear in "edit toolbars". #207296 0247 const QStringList hideActions = plugin->invisibleToolbarActions(); 0248 // qCDebug(KONTACTINTERFACE_LOG) << "Hiding actions" << hideActions << "from" << pluginName << part; 0249 const QDomDocument doc = part->domDocument(); 0250 const QDomElement docElem = doc.documentElement(); 0251 // 1. Iterate over containers 0252 for (QDomElement containerElem = docElem.firstChildElement(); !containerElem.isNull(); containerElem = containerElem.nextSiblingElement()) { 0253 if (QString::compare(containerElem.tagName(), QLatin1StringView("ToolBar"), Qt::CaseInsensitive) == 0) { 0254 // 2. Iterate over actions in toolbars 0255 QDomElement actionElem = containerElem.firstChildElement(); 0256 while (!actionElem.isNull()) { 0257 QDomElement nextActionElem = actionElem.nextSiblingElement(); 0258 if (QString::compare(actionElem.tagName(), QLatin1StringView("Action"), Qt::CaseInsensitive) == 0) { 0259 // qCDebug(KONTACTINTERFACE_LOG) << "Looking at action" << actionElem.attribute("name"); 0260 if (hideActions.contains(actionElem.attribute(QStringLiteral("name")))) { 0261 // qCDebug(KONTACTINTERFACE_LOG) << "REMOVING"; 0262 containerElem.removeChild(actionElem); 0263 } 0264 } 0265 actionElem = nextActionElem; 0266 } 0267 } 0268 } 0269 0270 // Possible optimization: we could do all the above and the writing below 0271 // only when (newAppFile does not exist) or (version of domDocument > version of newAppFile) (*) 0272 // This requires parsing newAppFile when it exists, though, and better use 0273 // the fast kdeui code for that rather than a full QDomDocument. 0274 // (*) or when invisibleToolbarActions() changes :) 0275 0276 const QString newAppFile = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1StringView("/kontact/default-") 0277 + QLatin1StringView(pluginName) + QLatin1StringView(".rc"); 0278 const QFileInfo fileInfo(newAppFile); 0279 QDir().mkpath(fileInfo.absolutePath()); 0280 0281 QFile file(newAppFile); 0282 if (!file.open(QFile::WriteOnly)) { 0283 qCWarning(KONTACTINTERFACE_LOG) << "error writing to" << newAppFile; 0284 return; 0285 } 0286 file.write(doc.toString().toUtf8()); 0287 file.flush(); 0288 0289 setXmlFiles(); 0290 } 0291 0292 void Plugin::PluginPrivate::setXmlFiles() 0293 { 0294 if (pluginName.isEmpty()) { 0295 return; 0296 } 0297 const QString newAppFile = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1StringView("/kontact/default-") 0298 + QLatin1StringView(pluginName) + QLatin1StringView(".rc"); 0299 const QString localFile = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1StringView("/kontact/local-") 0300 + QLatin1StringView(pluginName) + QLatin1StringView(".rc"); 0301 if (!localFile.isEmpty() && !newAppFile.isEmpty()) { 0302 if (part->xmlFile() != newAppFile || part->localXMLFile() != localFile) { 0303 part->replaceXMLFile(newAppFile, localFile); 0304 } 0305 } 0306 } 0307 //@endcond 0308 0309 void Plugin::slotConfigUpdated() 0310 { 0311 configUpdated(); 0312 } 0313 0314 void Plugin::bringToForeground() 0315 { 0316 if (d->executableName.isEmpty()) { 0317 return; 0318 } 0319 #ifdef Q_OS_WIN 0320 activateWindowForProcess(d->executableName); 0321 #else 0322 auto job = new KIO::CommandLauncherJob(d->executableName); 0323 job->start(); 0324 #endif 0325 } 0326 0327 Summary *Plugin::createSummaryWidget(QWidget *parent) 0328 { 0329 Q_UNUSED(parent) 0330 return nullptr; 0331 } 0332 0333 bool Plugin::showInSideBar() const 0334 { 0335 return d->hasPart; 0336 } 0337 0338 void Plugin::setShowInSideBar(bool hasPart) 0339 { 0340 d->hasPart = hasPart; 0341 } 0342 0343 bool Plugin::queryClose() const 0344 { 0345 return true; 0346 } 0347 0348 void Plugin::setDisabled(bool disabled) 0349 { 0350 d->disabled = disabled; 0351 } 0352 0353 bool Plugin::disabled() const 0354 { 0355 return d->disabled; 0356 } 0357 0358 void Plugin::shortcutChanged() 0359 { 0360 } 0361 0362 void Plugin::virtual_hook(int, void *) 0363 { 0364 // BASE::virtual_hook( id, data ); 0365 } 0366 0367 #include "moc_plugin.cpp"