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"