diff --git a/src/configwidget.cpp b/src/configwidget.cpp index 9469638..66868c1 100644 --- a/src/configwidget.cpp +++ b/src/configwidget.cpp @@ -1,248 +1,248 @@ /** =========================================================== * @file * * This file is a part of KDE project * * * @date 2012-08-06 * @brief Plugins config widget. * * @author Copyright (C) 2004-2018 by Gilles Caulier * caulier dot gilles at gmail dot com * @author Copyright (C) 2012 by Victor Dodon * dodonvictor at gmail dot com * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "configwidget.h" // Qt include #include #include -// KDE includes +// KF includes -#include -#include +#include +#include namespace KIPI { class PluginCheckBox : public QTreeWidgetItem { public: PluginCheckBox(PluginLoader::Info* const info, QTreeWidget* const parent) : QTreeWidgetItem(parent), m_info(info) { setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicator); setDisabled(false); // Name + Icon + Selector setText(0, m_info->name()); setIcon(0, m_info->icon()); setCheckState(0, m_info->shouldLoad() ? Qt::Checked : Qt::Unchecked); // Categories QStringList list = m_info->pluginCategories(); list.removeDuplicates(); list.sort(); setText(1, list.join(QString::fromLatin1(", "))); // Description setText(2, m_info->comment()); // Author setText(3, m_info->author().section(QString::fromLatin1(","), 0, 0)); }; ~PluginCheckBox() override { }; bool contains(const QString& txt, Qt::CaseSensitivity cs) const { return (text(0).contains(txt, cs) || text(1).contains(txt, cs) || text(2).contains(txt, cs) || text(3).contains(txt, cs)); }; public: PluginLoader::Info* m_info; }; // --------------------------------------------------------------------- class ConfigWidget::Private { public: Private() { }; QString filter; QList boxes; }; ConfigWidget::ConfigWidget(QWidget* const parent) : QTreeWidget(parent), d(new Private) { setRootIsDecorated(false); setSelectionMode(QAbstractItemView::SingleSelection); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); setAllColumnsShowFocus(true); setSortingEnabled(true); setColumnCount(4); header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); header()->setSectionResizeMode(1, QHeaderView::ResizeToContents); header()->setSectionResizeMode(2, QHeaderView::Stretch); header()->setSectionResizeMode(3, QHeaderView::Interactive); header()->setSortIndicatorShown(true); setAutoFillBackground(false); viewport()->setAutoFillBackground(false); PluginLoader* const loader = PluginLoader::instance(); if (loader) { foreach(PluginLoader::Info* const info, loader->pluginList()) { if (info) { d->boxes.append(new PluginCheckBox(info, this)); } } } // Sort items by plugin names. sortItems(0, Qt::AscendingOrder); } ConfigWidget::~ConfigWidget() { delete d; } void ConfigWidget::apply() { if (PluginLoader::instance()) { KSharedConfigPtr config = KSharedConfig::openConfig(); KConfigGroup group = config->group(QString::fromLatin1("KIPI/EnabledPlugin")); foreach (PluginCheckBox* const item, d->boxes) { bool orig = item->m_info->shouldLoad(); bool load = (item->checkState(0) == Qt::Checked); if (orig != load) { group.writeEntry(item->m_info->uname(), load); item->m_info->setShouldLoad(load); // See Bug #289779 - Plugins are not really freed / unplugged when disabled in the kipi setup dialog, always call reload() // to reload plugins properly when the replug() signal is send. item->m_info->reload(); } } config->sync(); emit PluginLoader::instance()->replug(); } } void ConfigWidget::selectAll() { foreach (PluginCheckBox* const item, d->boxes) { item->setCheckState(0, Qt::Checked); } } void ConfigWidget::clearAll() { foreach (PluginCheckBox* const item, d->boxes) { item->setCheckState(0, Qt::Unchecked); } } int ConfigWidget::count() const { return d->boxes.count(); } int ConfigWidget::actived() const { int actived = 0; foreach (PluginCheckBox* const item, d->boxes) { if (item->checkState(0) == Qt::Checked) actived++; } return actived; } int ConfigWidget::visible() const { int visible = 0; foreach (PluginCheckBox* const item, d->boxes) { if (!item->isHidden()) visible++; } return visible; } void ConfigWidget::setFilter(const QString& filter, Qt::CaseSensitivity cs) { d->filter = filter; bool query = false; foreach (PluginCheckBox* const item, d->boxes) { if (item->contains(filter, cs)) { query = true; item->setHidden(false); } else { item->setHidden(true); } } emit signalSearchResult(query); } QString ConfigWidget::filter() const { return d->filter; } } // namespace KIPI diff --git a/src/plugin.cpp b/src/plugin.cpp index 5fce194..beb89fb 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -1,498 +1,498 @@ /** =========================================================== * @file * * This file is a part of KDE project * * * @date 2004-02-01 * @brief plugin interface * * @author Copyright (C) 2004-2018 by Gilles Caulier * caulier dot gilles at gmail dot com * @author Copyright (C) 2012 by Victor Dodon * dodonvictor at gmail dot com * @author Copyright (C) 2004-2005 by Renchi Raju * renchi dot raju at gmail dot com * @author Copyright (C) 2004-2005 by Jesper K. Pedersen * blackie at kde dot org * @author Copyright (C) 2004-2005 by Aurelien Gateau * aurelien dot gateau at free dot fr * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "plugin.h" // Qt includes #include #include #include #include #include #include -// KDE includes +// KF includes -#include +#include // Local includes #include "libkipi_version.h" #include "libkipi_debug.h" #include "interface.h" #include "pluginloader.h" namespace KIPI { class Q_DECL_HIDDEN Plugin::Private { public: Private() : uiBaseName(QString()) { defaultWidget = nullptr; defaultCategory = InvalidCategory; } ActionCategoryMap actionsCat; QWidget* defaultWidget; QString uiBaseName; Category defaultCategory; public: class XMLParser { public: static QDomElement makeElement(QDomDocument& domDoc, const QDomElement& from); static void buildPaths(const QDomElement& original, const QDomNodeList& localNodes, QHashPath& paths); static int findByNameAttr(const QDomNodeList& list, const QDomElement& node); static void removeDisabledActions(QDomElement& elem); private: XMLParser(); static void buildPaths(const QDomElement& original, const QDomNodeList& localNodes, QHashPath& paths, QDomElemList& stack); }; }; QDomElement Plugin::Private::XMLParser::makeElement(QDomDocument& domDoc, const QDomElement& from) { if (domDoc.isNull() || from.isNull()) return QDomElement(); QDomElement elem = domDoc.createElement(from.tagName()); QDomNamedNodeMap attributes = from.attributes(); for (int i = 0; i < attributes.size(); ++i) { QDomAttr attr = attributes.item(i).toAttr(); if (attr.name() != QString::fromLatin1("alreadyVisited")) elem.setAttributeNode(attr); } return elem; } void Plugin::Private::XMLParser::buildPaths(const QDomElement& original, const QDomNodeList& localNodes, QHashPath& paths) { /* * For each child element of "local", we will construct the path from the * "original" element to first appearance of the respective child in the * subtree. */ QDomElemList stack; buildPaths(original, localNodes, paths, stack); } int Plugin::Private::XMLParser::findByNameAttr(const QDomNodeList& list, const QDomElement& node) { const QString nodeName = node.toElement().attribute(QString::fromLatin1("name")); const QString nodeTag = node.toElement().tagName(); for (int i = 0; i < list.size(); ++i) { QDomElement e = list.at(i).toElement(); if (e.tagName() == nodeTag && e.attribute(QString::fromLatin1("name")) == nodeName) return i; } return -1; } void Plugin::Private::XMLParser::removeDisabledActions(QDomElement& elem) { QDomNodeList actionList = elem.elementsByTagName(QString::fromLatin1("Action")); QStringList disabledActions = PluginLoader::instance()->disabledPluginActions(); QDomElemList disabledElements; for(int i = 0; i < actionList.size(); ++i) { QDomElement el = actionList.item(i).toElement(); if (el.isNull()) continue; if (disabledActions.contains(el.attribute(QString::fromLatin1("name")))) { disabledElements << el; } } foreach(QDomElement element, disabledElements) { //qCDebug(LIBKIPI_LOG) << "Plugin action '" << element.attribute("name") << "' is disabled."; QDomElement parent = element.parentNode().toElement(); parent.removeChild(element); } } void Plugin::Private::XMLParser::buildPaths(const QDomElement& original, const QDomNodeList& localNodes, QHashPath& paths, QDomElemList& stack) { stack.push_back(original.cloneNode(true).toElement()); int idx; if ((idx = findByNameAttr(localNodes, original)) != -1) { paths[localNodes.item(idx).toElement().attribute(QString::fromLatin1("name"))] = stack; } if (!original.hasChildNodes()) { stack.pop_back(); return; } for (QDomNode n = original.firstChild(); !n.isNull(); n = n.nextSibling()) { QDomElement e = n.toElement(); if (e.tagName() == QString::fromLatin1("Menu") && e.hasChildNodes()) { buildPaths(e, localNodes, paths, stack); } } stack.pop_back(); } // -------------------------------------------------------------------------------------------------------------- Plugin::Plugin(QObject* const parent, const char* name) : QObject(parent), d(new Private) { setObjectName(QString::fromLatin1(name)); } Plugin::~Plugin() { clearActions(); delete d; } QList Plugin::actions(QWidget* const widget) const { QWidget* const w = !widget ? d->defaultWidget : widget; if (!d->actionsCat.contains(w)) { qCWarning(LIBKIPI_LOG) << "Error in plugin. It needs to call Plugin::setup(QWidget*) " << "as the very first line when overriding the setup method."; } return d->actionsCat[w].keys(); } void Plugin::addAction(const QString& name, QAction* const action) { if (!action || name.isEmpty()) return; if (!PluginLoader::instance()->disabledPluginActions().contains(name)) { actionCollection()->addAction(name, action); addAction(action); } else { //qCDebug(LIBKIPI_LOG) << "Action '" << name << "' is disabled."; } } void Plugin::addAction(QAction* const action) { addAction(action, d->defaultCategory); } void Plugin::addAction(const QString& name, QAction* const action, Category cat) { if (!action || name.isEmpty()) return; if (!PluginLoader::instance()->disabledPluginActions().contains(name)) { actionCollection()->addAction(name, action); addAction(action, cat); } else { //qCDebug(LIBKIPI_LOG) << "Action '" << name << "' is disabled."; } } void Plugin::addAction(QAction* const action, Category cat) { if (cat == InvalidCategory) { qCWarning(LIBKIPI_LOG) << "Error in plugin. Action '" << action->objectName() << "has " "invalid category. You must set default plugin category or " "to use a valid Category"; } d->actionsCat[d->defaultWidget].insert(action, cat); } void Plugin::setup(QWidget* const widget) { clearActions(); d->defaultWidget = widget; d->actionsCat.insert(widget, QMap()); } Category Plugin::category(QAction* const action) const { QMap::const_iterator it = d->actionsCat[d->defaultWidget].constFind(action); if (it != d->actionsCat[d->defaultWidget].constEnd()) { return it.value(); } else { if (d->defaultCategory == InvalidCategory) { qCWarning(LIBKIPI_LOG) << "Error in plugin. Invalid category. " "You must set default plugin category."; } return d->defaultCategory; } } Interface* Plugin::interface() const { return (dynamic_cast(parent())); } void Plugin::setUiBaseName(const char* name) { if (name && *name) d->uiBaseName = QString::fromLatin1(name); } QString Plugin::uiBaseName() const { return d->uiBaseName; } void Plugin::mergeXMLFile(KXMLGUIClient *const host) { if (!host) { qCCritical(LIBKIPI_LOG) << "Host KXMLGUIClient is null!"; return; } if (d->uiBaseName.isEmpty()) { qCCritical(LIBKIPI_LOG) << "UI file basename is not set! You must first call setUiBaseName."; return; } const QString componentName = QApplication::applicationName(); const QString defaultUI = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QString::fromLatin1("kxmlgui5/kipi/") + d->uiBaseName); const QString localUIdir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QString::fromLatin1("/kxmlgui5/") + componentName; const QString localUI = localUIdir + QString::fromLatin1("/") + d->uiBaseName; qCDebug(LIBKIPI_LOG) << "UI file :" << defaultUI; QFile defaultUIFile(defaultUI); QDomDocument defaultDomDoc; if (!defaultUIFile.open(QFile::ReadOnly) || !defaultDomDoc.setContent(&defaultUIFile)) { qCCritical(LIBKIPI_LOG) << "Could not open default ui file " << defaultUI << " for ui basename " << d->uiBaseName; return; } defaultUIFile.close(); const QDomDocument hostDoc = host->domDocument(); if (hostDoc.isNull() || defaultDomDoc.isNull()) { qCCritical(LIBKIPI_LOG) << "Cannot merge the XML files, at least one is null!"; return; } QDomElement hostGuiElem = hostDoc.firstChildElement(QString::fromLatin1("kpartgui")); QDomElement hostMenuBarElem = hostGuiElem.firstChildElement(QString::fromLatin1("MenuBar")); QDomDocument newPluginDoc(defaultDomDoc.doctype()); QDomElement defGuiElem = defaultDomDoc.firstChildElement(QString::fromLatin1("gui")); Private::XMLParser::removeDisabledActions(defGuiElem); QDomElement newGuiElem = Private::XMLParser::makeElement(newPluginDoc, defGuiElem); QDomElement defMenuBarElem = defGuiElem.firstChildElement(QString::fromLatin1("MenuBar")); QDomElement newMenuBarElem = Private::XMLParser::makeElement(newPluginDoc, defMenuBarElem); QDomElement defToolBarElem = defGuiElem.firstChildElement(QString::fromLatin1("ToolBar")); QDomElement defActionPropElem = defGuiElem.firstChildElement(QString::fromLatin1("ActionProperties")); QHashPath paths; Private::XMLParser::buildPaths(hostMenuBarElem, defMenuBarElem.childNodes(), paths); for (QDomNode n = defMenuBarElem.firstChild(); !n.isNull(); n = n.nextSibling()) { QDomElemList path = paths[n.toElement().attribute(QString::fromLatin1("name"))]; QDomElement current = newMenuBarElem; QDomElement origCurr = defMenuBarElem; if (path.empty()) { newMenuBarElem.appendChild(n.cloneNode()); } else { for (int i = 1; i < path.size() - 1; ++i) { int idx = Private::XMLParser::findByNameAttr(current.childNodes(), path[i]); origCurr = path[i]; if (idx == -1) { if (!path[i].isNull()) { QDomElement newChild = Private::XMLParser::makeElement(newPluginDoc, path[i]); QDomElement textElem = origCurr.firstChildElement(QString::fromLatin1("text")); if (!textElem.isNull()) { newChild.appendChild(textElem.cloneNode()); } current.appendChild(newChild); current = newChild; } } else { current = current.childNodes().item(idx).toElement(); } } } if (!current.isNull()) current.appendChild(n.cloneNode()); } newGuiElem.appendChild(newMenuBarElem); QFile localUIFile(localUI); QDomDocument localDomDoc; // be safe rather than sorry // create the appname folder in kxmlgui5 QDir localUIDir(localUIdir); if (!localUIDir.exists()) QDir().mkpath(localUIdir); if (!localUIFile.exists() || !localUIFile.open(QFile::ReadOnly) || !localDomDoc.setContent(&localUIFile)) { newGuiElem.appendChild(defToolBarElem.cloneNode()); newGuiElem.appendChild(defActionPropElem.cloneNode()); } else { QDomElement localGuiElem = localDomDoc.firstChildElement(QString::fromLatin1("gui")); Private::XMLParser::removeDisabledActions(localGuiElem); QDomElement localToolBarElem = localGuiElem.firstChildElement(QString::fromLatin1("ToolBar")); QDomElement localActionPropElem = localGuiElem.firstChildElement(QString::fromLatin1("ActionProperties")); newGuiElem.appendChild(localToolBarElem.cloneNode()); newGuiElem.appendChild(localActionPropElem.cloneNode()); } localUIFile.close(); QFile writeFile(localUI); if (!writeFile.open(QFile::WriteOnly | QFile::Truncate)) { qCCritical(LIBKIPI_LOG) << "Could not open " << localUI << " for writing!"; return; } newPluginDoc.appendChild(newGuiElem); writeFile.write(newPluginDoc.toString().toUtf8()); writeFile.close(); setXMLFile(d->uiBaseName); } void Plugin::clearActions() { QList actions = actionCollection()->actions(); foreach (QAction* const action, actions) { actionCollection()->removeAction(action); } } void Plugin::setupXML() { mergeXMLFile(dynamic_cast(interface()->parent())); } void Plugin::rebuild() { QString file = xmlFile(); if (!file.isEmpty()) { setXMLGUIBuildDocument(QDomDocument()); setXMLFile(file, false); } } void Plugin::setDefaultCategory(Category cat) { d->defaultCategory = cat; } Category Plugin::defaultCategory() const { return d->defaultCategory; } } // namespace KIPI diff --git a/src/plugin.h b/src/plugin.h index 955414c..367d988 100644 --- a/src/plugin.h +++ b/src/plugin.h @@ -1,219 +1,219 @@ /** =========================================================== * @file * * This file is a part of KDE project * * * @date 2004-02-01 * @brief plugin interface * * @author Copyright (C) 2004-2018 by Gilles Caulier * caulier dot gilles at gmail dot com * @author Copyright (C) 2012 by Victor Dodon * dodonvictor at gmail dot com * @author Copyright (C) 2004-2005 by Renchi Raju * renchi dot raju at gmail dot com * @author Copyright (C) 2004-2005 by Jesper K. Pedersen * blackie at kde dot org * @author Copyright (C) 2004-2005 by Aurelien Gateau * aurelien dot gateau at free dot fr * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef KIPI_PLUGIN_H #define KIPI_PLUGIN_H // Qt includes #include #include #include #include #include #include -// KDE includes +// KF includes -#include +#include // Local includes #include "libkipi_export.h" class QAction; namespace KIPI { class Interface; enum Category { InvalidCategory = -1, ImagesPlugin = 0, ToolsPlugin, ImportPlugin, ExportPlugin, BatchPlugin, CollectionsPlugin }; /** * @short Base class for the KIPI plugins * */ class LIBKIPI_EXPORT Plugin : public QObject, public KXMLGUIClient { Q_OBJECT typedef QList QDomElemList; typedef QHash QHashPath; typedef QMap > ActionCategoryMap; public: /** * Constructs a plugin * * @param parent the parent of this object * @param name the name of the plugin */ Plugin(QObject* const parent, const char* name); /** * Standard destructor * * All the actions in the actionCollection are deleted before the plugin is * deleted */ ~Plugin() override; /** * Returns the plugin actions associated with the widget passed as argument, or with * the default widget, if widget is null or not provided. The actions are in * the same order as added to the plugin. */ QList actions(QWidget* const widget = nullptr) const; /** * Returns the KIPI::Interface */ Interface* interface() const; /** * Virtual method that must be overridden by the non abstract descendants and * must be called before any actions are added. * * @param widget The widget which holds the plugin. It will be set as the default widget. */ virtual void setup(QWidget* const widget) = 0; /** * Returns the category of the specified plugin action, or InvalidCategory * if the action is not recognised */ Category category(QAction* const action) const; /** * Force the plugin to reread and to reload its xml file */ void rebuild(); protected: /** * Register an action to the plugin instance and add it to the action collection. * * The action is added only if the action name is not in the disabled actions * list of the PluginLoader singleton class. * * @param name the name by which the action will be added to the action collection * @param action the action to add * * @note It just calls addAction with the default category, so the default * category must be set using setDefaultCategory before you use this method */ void addAction(const QString& name, QAction* const action); /** * Register action to the plugin instance and add it to the action collection * * The action is added only if the action name is not in the disabled actions * list of the PluginLoader singleton class. * * @param name the name by which the action will be added to the action collection * @param action the action to add * @param cat the category of the action */ void addAction(const QString& name, QAction* const action, Category cat); /** * Sets the default category of the plugin actions * * \sa defaultCategory() */ void setDefaultCategory(Category cat); /** * Returns the default category of the plugin actions * * \sa setDefaultCategory() */ Category defaultCategory() const; /** * Sets the name of the xml file associated with this KXMLGUIClient. You must * provide only the filename without slashes. * * The default xml file must be installed in the ${DATA_INSTALL_DIR}/kipi, * modifications are stored in the local config dir of the KGlobal::mainComponent * * \sa uiBaseName() */ void setUiBaseName(const char* name); /** * Return the base name of the xml file associated with this KXMLGUIClient * * \sa setUiBaseName() */ QString uiBaseName() const; /** * Adapt the xml file of the plugin with the one of the KXmlGuiWindow main window. * It's recommended to call it on every creation of the plugin. * * @note the xml file of the plugin must be set using setUiBaseName() */ void setupXML(); private: /** For internal uses only */ void addAction(QAction* const action); void addAction(QAction* const action, Category cat); void mergeXMLFile(KXMLGUIClient* const host); void clearActions(); private: class Private; Private* const d; }; } // namespace KIPI #endif // KIPI_PLUGIN_H diff --git a/src/pluginloader.cpp b/src/pluginloader.cpp index d8631fd..4eb26af 100644 --- a/src/pluginloader.cpp +++ b/src/pluginloader.cpp @@ -1,405 +1,405 @@ /** =========================================================== * @file * * This file is a part of KDE project * * * @date 2004-02-01 * @brief plugin loader * * @author Copyright (C) 2004-2018 by Gilles Caulier * caulier dot gilles at gmail dot com * @author Copyright (C) 2004-2005 by Renchi Raju * renchi dot raju at gmail dot com * @author Copyright (C) 2009 by Andi Clemens * andi dot clemens at googlemail dot com * @author Copyright (C) 2009 by Aleix Pol Gonzalez * aleixpol at kde dot org * @author Copyright (C) 2012-2013 by Victor Dodon * dodonvictor at gmail dot com * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "pluginloader.h" // Qt includes #include #include #include #include #include -// KDE includes - -#include -#include -#include -#include -#include -#include -#include +// KF includes + +#include +#include +#include +#include +#include +#include +#include // Local includes #include "plugin.h" #include "interface.h" #include "configwidget.h" #include "libkipi_version.h" #include "libkipi_config.h" #include "libkipi_debug.h" namespace KIPI { class Q_DECL_HIDDEN PluginLoader::Info::Private { public: Private() { shouldLoad = false; plugin = nullptr; parent = nullptr; } bool shouldLoad; KService::Ptr service; Plugin* plugin; KXmlGuiWindow* parent; }; PluginLoader::Info::Info(KXmlGuiWindow* const parent, const KService::Ptr& service, bool shouldLoad) : d(new Private) { d->service = service; d->shouldLoad = shouldLoad; d->parent = parent; } PluginLoader::Info::~Info() { if (d->parent && d->plugin) { d->parent->guiFactory()->removeClient(d->plugin); foreach(KToolBar* const toolbar, d->parent->toolBars()) { toolbar->removeXMLGUIClient(d->plugin); } } delete d->plugin; delete d; } KService::Ptr PluginLoader::Info::service() const { return d->service; } QString PluginLoader::Info::name() const { return d->service->name(); } QString PluginLoader::Info::uname() const { return d->service->untranslatedGenericName(); } QString PluginLoader::Info::author() const { return d->service->property(QString::fromLatin1("author"), QVariant::String).toString(); } QString PluginLoader::Info::comment() const { return d->service->comment(); } QString PluginLoader::Info::library() const { return d->service->library(); } QIcon PluginLoader::Info::icon() const { if (d->service->icon().isEmpty() && d->plugin) { if (!d->plugin->actions().isEmpty() && d->plugin->actions().first()) { return d->plugin->actions().first()->icon(); } else { return QIcon(); } } else { return QIcon::fromTheme(d->service->icon()); } } Plugin* PluginLoader::Info::plugin() const { if (!d->plugin && shouldLoad()) { QString error; d->plugin = d->service->createInstance(PluginLoader::instance()->interface(), QVariantList(), &error); if (d->plugin) { qCDebug(LIBKIPI_LOG) << "Loaded plugin " << d->plugin->objectName(); emit (PluginLoader::instance()->plug(const_cast(this))); } else { qCWarning(LIBKIPI_LOG) << "Cannot create instance for plugin " << name() << " (" << library() << ")" << " with error: " << error; } } return d->plugin; } QStringList PluginLoader::Info::pluginCategories() const { return d->service->property(QString::fromLatin1("X-KIPI-PluginCategories")).toStringList(); } void PluginLoader::Info::reload() { if (d->parent) { d->parent->guiFactory()->removeClient(d->plugin); foreach(KToolBar* const toolbar, d->parent->toolBars()) { toolbar->removeXMLGUIClient(d->plugin); } } delete d->plugin; d->plugin = nullptr; } bool PluginLoader::Info::shouldLoad() const { return d->shouldLoad; } void PluginLoader::Info::setShouldLoad(bool value) { d->shouldLoad = value; } //--------------------------------------------------------------------- static PluginLoader* s_instance = nullptr; static bool s_loaded = false; class Q_DECL_HIDDEN PluginLoader::Private { public: Private() { interface = nullptr; parent = nullptr; }; QStringList ignoredPlugins; QStringList disabledActions; KXmlGuiWindow* parent; PluginLoader::PluginList pluginList; Interface* interface; }; PluginLoader::PluginLoader() : d(new Private) { Q_ASSERT((s_instance == nullptr) && (!s_loaded)); s_instance = this; } PluginLoader::PluginLoader(KXmlGuiWindow* const parent) : d(new Private) { Q_ASSERT((s_instance == nullptr) && (!s_loaded)); s_instance = this; if (!parent) { qWarning(LIBKIPI_LOG) << "KDE XML application instance is null..."; } d->parent = parent; } void PluginLoader::setInterface(Interface* const interface) { d->interface = interface; setParent(interface); } void PluginLoader::setIgnoredPluginsList(const QStringList& ignores) { d->ignoredPlugins = ignores; } void PluginLoader::setDisabledPluginActions(const QStringList& disabledActions) { d->disabledActions = disabledActions; } QStringList PluginLoader::disabledPluginActions() const { return d->disabledActions; } void PluginLoader::init() { Q_ASSERT((s_instance != nullptr) && (!s_loaded)); if (!d->interface) { qWarning(LIBKIPI_LOG) << "KIPI host interface instance is null. No plugin will be loaded..."; return; } s_loaded = true; const KService::List offers = KServiceTypeTrader::self()->query(QString::fromLatin1("KIPI/Plugin")); KSharedConfigPtr config = KSharedConfig::openConfig(); KConfigGroup group = config->group(QString::fromLatin1("KIPI/EnabledPlugin")); for (KService::List::ConstIterator iter = offers.begin(); iter != offers.end(); ++iter) { KService::Ptr service = *iter; QString name = service->name(); QString uname = service->untranslatedGenericName(); QString library = service->library(); QStringList reqFeatures = service->property(QString::fromLatin1("X-KIPI-ReqFeatures")).toStringList(); int binVersion = service->property(QString::fromLatin1("X-KIPI-BinaryVersion")).toInt(); if (library.isEmpty() || uname.isEmpty()) { qCWarning(LIBKIPI_LOG) << "Plugin had an empty name or library file - this should not happen."; continue; } if (d->ignoredPlugins.contains(uname)) { qCDebug(LIBKIPI_LOG) << "Plugin " << name << " (generic name: " << uname << ") is in the ignore list from host application"; continue; } if (binVersion != kipi_binary_version) { qCDebug(LIBKIPI_LOG) << "Plugin " << name << "has a SO version (" << binVersion << ") which is different than libkipi ABI version (" << kipi_binary_version << "). " << "Refusing to load."; continue; } bool appHasAllReqFeatures = true; for (QStringList::const_iterator featureIt = reqFeatures.constBegin(); featureIt != reqFeatures.constEnd(); ++featureIt) { if (!d->interface->hasFeature(*featureIt)) { qCDebug(LIBKIPI_LOG) << "Plugin " << name << " was not loaded because the host application is missing\n" << "the feature " << *featureIt; appHasAllReqFeatures = false; break; } } bool load = group.readEntry(uname, true); if (!appHasAllReqFeatures) { continue; } Info* const info = new Info(d->parent, service, load); d->pluginList.append(info); } } PluginLoader::~PluginLoader() { delete d; } void PluginLoader::loadPlugins() { emit replug(); // added for convenience, now they can be loaded on demand } const PluginLoader::PluginList& PluginLoader::pluginList() { return d->pluginList; } PluginLoader* PluginLoader::instance() { if (!s_instance) { qCDebug(LIBKIPI_LOG) << "Instance is null..."; } return s_instance; } Interface* PluginLoader::interface() const { return d->interface; } ConfigWidget* PluginLoader::configWidget(QWidget* const parent) const { return new ConfigWidget(parent); } QString PluginLoader::kipiPluginsVersion() const { QString ver; QString path = QStandardPaths::locate(QStandardPaths::ApplicationsLocation, QString::fromLatin1("kipiplugins.desktop")); KDesktopFile desk(path); QMap map = desk.entryMap(QString::fromLatin1("X-KipiPlugins Entry")); if (!map.isEmpty()) { QString val = map[QString::fromLatin1("Version")]; if (!val.isEmpty()) ver = val; } return ver; } } // namespace KIPI diff --git a/src/pluginloader.h b/src/pluginloader.h index dfeafa4..175f98e 100644 --- a/src/pluginloader.h +++ b/src/pluginloader.h @@ -1,323 +1,323 @@ /** =========================================================== * @file * * This file is a part of KDE project * * * @date 2004-02-01 * @brief plugin loader * * @author Copyright (C) 2004-2018 by Gilles Caulier * caulier dot gilles at gmail dot com * @author Copyright (C) 2004-2005 by Renchi Raju * renchi dot raju at gmail dot com * @author Copyright (C) 2009 by Andi Clemens * andi dot clemens at googlemail dot com * @author Copyright (C) 2009 by Aleix Pol Gonzalez * aleixpol at kde dot org * @author Copyright (C) 2012 by Victor Dodon * dodonvictor at gmail dot com * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef KIPI_PLUGINLOADER_H #define KIPI_PLUGINLOADER_H // Qt includes #include -// KDE includes +// KF includes -#include -#include +#include +#include // Local includes #include "libkipi_export.h" namespace KIPI { class Plugin; class Interface; class ConfigWidget; /** \author Gilles Caulier \par Maintainer: Victor Dodon \class PluginLoader This is the class that will help host applications to load plugins. The host application must create an instance of the plugin loader, and call the method loadPlugins() to get the plugins loaded. To ensure that plugins are correctly removed from menus and toolbars when loaded and unloaded after constructions, the application must connect to either the signals plug() / unplug() or the signal replug(). These signals are emitted when a plugin is to be inserted into the menus. If your application is using KDE XMLGUI, the easiest(nicest) way to get the plugins inserted into the menus is by adding an item in your application XML ui.rc file looking like this: \code &Image &Tools Main Toolbar \endcode Then loading plugins into menus could be done with code similar to this implementation: \code class MyKipiApplication : public KXmlGuiWindow { Q_OBJECT public: MyKipiApplication(); private Q_SLOTS: void slotKipiPluginPlug(); private: KIPI::Interface* m_iface; KIPI::PluginLoader* m_loader; }; // ------------------------------------------------------------------------------- MyKipiApplication::MyKipiApplication() : KXmlGuiWindow(0) { m_iface = new KIPI::Interface(this, "MyKipiApplication_KIPI_interface"); m_loader = new KIPI::PluginLoader(this); m_loader->setInterface(m_iface); m_loader->init(); connect(m_loader, SIGNAL(replug()), this, SLOT(slotKipiPluginPlug())); m_loader->loadPlugins(); } void MyKipiApplication::slotKipiPluginPlug() { QList kipiImageActions, kipiExportActions, kipiToolsActions; PluginLoader::PluginList list = m_loader->pluginList(); // We need to remove loaded plugins from the gui factory for (PluginLoader::PluginList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it ) { Plugin* plugin = (*it)->plugin(); if ( !plugin || !(*it)->shouldLoad() ) continue; guiFactory()->removeClient(plugin); } for (PluginLoader::PluginList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it ) { Plugin* plugin = (*it)->plugin(); if ( !plugin || !(*it)->shouldLoad() ) continue; plugin->setup(this); } // We add plugins to the factory for (PluginLoader::PluginList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it ) { Plugin* plugin = (*it)->plugin(); if ( !plugin || !(*it)->shouldLoad() ) continue; guiFactory()->addClient(plugin); } } \endcode For a implementation sample used to manage Kipi-plugins in host application, look the code of the kxmlkipicmd test application in the "test" folder from libkipi. To configure which plugins should be loaded, simply call PluginLoader::configWidget(), and insert the widget into your normal configuration dialog. */ class LIBKIPI_EXPORT PluginLoader : public QObject { Q_OBJECT public: class LIBKIPI_EXPORT Info { public: Info(KXmlGuiWindow* const parent, const KService::Ptr& service, bool shouldLoad); ~Info(); QString name() const; QString uname() const; QString author() const; QString comment() const; QIcon icon() const; QString library() const; KService::Ptr service() const; Plugin* plugin() const; QStringList pluginCategories() const; void reload(); bool shouldLoad() const; void setShouldLoad(bool); private: class Private; Private* const d; }; public: typedef QList PluginList; public: /** * Use this constructor if your application does not use KDE XML GUI technology */ PluginLoader(); /** * Standard constructor. You must pass the instance of KDE XML GUI application as argument. * @param parent the pointer to the KXmlGuiWindow of your application */ PluginLoader(KXmlGuiWindow* const parent); /** * Standard destructor */ ~PluginLoader() override; /** * Set KIPI interface instance from host application. */ void setInterface(Interface* const interface); /** * Return KIPI host interface instance. */ Interface* interface() const; /** * Set Plugins ignore list, with name of obsoletes plugins to not load through init(). */ void setIgnoredPluginsList(const QStringList& ignores); /** * Set disabled plugin actions that will not be plugged into the gui, */ void setDisabledPluginActions(const QStringList& disabledActions); /** * Return the list of disabled plugin actions */ QStringList disabledPluginActions() const; /** * Init plugin loader. Call this method to parse relevant plugins installed on your system. * Before to call this method, you must setup KIPI interface instance. * Optionally, setup list of plugins to ignore, the constraint list, and * the disabled plugin actions */ void init(); /** * Call this method to load relevant plugins installed on your system to your KIPI host application * NOTE: plugins can be loaded through Info item. */ void loadPlugins(); /** * Returns the list of loaded plugins */ const PluginList& pluginList(); /** * Return the kipi-plugins version installed on your computer if it's found through kipiplugins.desktop file. */ QString kipiPluginsVersion() const; /** * Return the config widget with list of plugins to manage. */ ConfigWidget* configWidget(QWidget* const parent) const; /** * Returns plugin loader instance. */ static PluginLoader* instance(); Q_SIGNALS: void plug(KIPI::PluginLoader::Info*); void unplug(KIPI::PluginLoader::Info*); // NOTE: plugin can be plugged through Info item. void replug(); private: class Private; Private* const d; private: friend class ConfigWidget; }; } // namespace KIPI #endif /* KIPI_PLUGINLOADER_H */ diff --git a/tests/common/kipiinterface.cpp b/tests/common/kipiinterface.cpp index db82157..99b73ab 100644 --- a/tests/common/kipiinterface.cpp +++ b/tests/common/kipiinterface.cpp @@ -1,532 +1,532 @@ /** =========================================================== * @file * * This file is a part of KDE project * * * @date 2009-11-21 * @brief kipi host test application * * @author Copyright (C) 2009-2010 by Michael G. Hansen * mike at mghansen dot de * @author Copyright (C) 2011-2018 by Gilles Caulier * caulier dot gilles at gmail dot com * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "kipiinterface.h" // Qt includes #include #include #include #include // Libkipi includes #include "libkipi_version.h" #include "imagecollection.h" -// KDE includes +// KF includes #ifdef HAVE_KEXIV2 # include #endif // Local includes #include "kipiimageinfoshared.h" #include "kipiimagecollectionselector.h" #include "kipiuploadwidget.h" #include "kipiimagecollectionshared.h" #include "kipiwriteimage.h" namespace KXMLKipiCmd { KipiInterface::KipiInterface(QObject* const parent, const QString& name) : Interface(parent, name), m_selectedImages(), m_selectedAlbums(), m_albums() { } KipiInterface::~KipiInterface() { } ImageCollection KipiInterface::currentAlbum() { qDebug() << "Called by plugins"; QUrl currentAlbumUrl; if (!m_selectedAlbums.isEmpty()) { currentAlbumUrl = m_selectedAlbums.at(0); } return (ImageCollection(new KipiImageCollectionShared(currentAlbumUrl))); } ImageCollection KipiInterface::currentSelection() { qDebug() << "Called by plugins"; return (ImageCollection(new KipiImageCollectionShared(m_selectedImages))); } QList KipiInterface::allAlbums() { QList listAllAlbums; for (QList::const_iterator it = m_albums.constBegin(); it!=m_albums.constEnd(); ++it) { listAllAlbums.append(ImageCollection(new KipiImageCollectionShared(*it))); } // make sure albums which have been specified as selectedalbums are also in the allAlbums list: for (QList::const_iterator it = m_selectedAlbums.constBegin(); it!=m_selectedAlbums.constEnd(); ++it) { if (!m_albums.contains(*it)) { listAllAlbums.append(ImageCollection(new KipiImageCollectionShared(*it))); } } return listAllAlbums; } ImageInfo KipiInterface::info(const QUrl& url) { qDebug() << QString::fromLatin1( "Plugin wants information about image \"%1\"").arg( url.url() ); return (ImageInfo(new KipiImageInfoShared(this, url))); } bool KipiInterface::addImage(const QUrl& url, QString& errmsg) { Q_UNUSED(errmsg); qDebug() << QString::fromLatin1( "Plugin added an image: \"%1\"").arg( url.url() ); return true; } void KipiInterface::delImage(const QUrl& url) { qDebug() << QString::fromLatin1( "Plugin deleted an image: \"%1\"").arg( url.url() ); } void KipiInterface::refreshImages(const QList& urls) { qDebug() << QString::fromLatin1( "Plugin asks to refresh %1 images:").arg( urls.size() ); for (QList::ConstIterator it = urls.constBegin(); it!=urls.constEnd(); ++it) { qDebug() << QString::fromLatin1(" ") + (*it).url(); } } int KipiInterface::features() const { qDebug() << "Called by plugins"; return ImagesHasTime #ifdef HAVE_KEXIV2 | HostSupportsMetadataProcessing #endif ; } ImageCollectionSelector* KipiInterface::imageCollectionSelector(QWidget* parent) { qDebug() << "Called by plugins"; return (new KipiImageCollectionSelector(this, parent)); } UploadWidget* KipiInterface::uploadWidget(QWidget* parent) { qDebug() << "Called by plugins"; return (new KipiUploadWidget(this, parent)); } void KipiInterface::addSelectedImages(const QList& images) { m_selectedImages.append(images); } void KipiInterface::addSelectedImage(const QUrl& image) { m_selectedImages.append(image); } void KipiInterface::addAlbums(const QList& albums) { for (QList::const_iterator it = albums.constBegin(); it!=albums.constEnd(); ++it) { addAlbum(*it); } } void KipiInterface::addAlbum(const QUrl& album) { m_albums.append(album); // TODO: recurse through sub-directories? } void KipiInterface::addSelectedAlbums(const QList& albums) { for (QList::const_iterator it = albums.constBegin(); it!=albums.constEnd(); ++it) { addSelectedAlbum(*it); } } void KipiInterface::addSelectedAlbum(const QUrl& album) { m_selectedAlbums.append(album); // TODO: recurse through sub-directories? } QVariant KipiInterface::hostSetting(const QString& settingName) { Q_UNUSED(settingName); return QVariant(); } void KipiInterface::thumbnails(const QList& list, int) { foreach(const QUrl& url, list) { QIcon icon(url.url()); emit gotThumbnail(url, icon.pixmap(256)); } } bool KipiInterface::saveImage(const QUrl& url, const QString& format, const QByteArray& data, uint width, uint height, bool sixteenBit, bool hasAlpha, bool* cancel) { KIPIWriteImage writer; writer.setImageData(data, width, height, sixteenBit, hasAlpha); writer.setCancel(cancel); if (format.toUpper() == QLatin1String("JPG") || format.toUpper() == QLatin1String("JPEG")) { return writer.write2JPEG(url.toLocalFile()); } if (format.toUpper() == QLatin1String("TIF") || format.toUpper() == QLatin1String("TIFF")) { return writer.write2TIFF(url.toLocalFile()); } if (format.toUpper() == QLatin1String("PNG")) { return writer.write2PNG(url.toLocalFile()); } if (format.toUpper() == QLatin1String("PPM")) { return writer.write2PPM(url.toLocalFile()); } return false; } // --------------------------------------------------------------------------------------- #ifdef HAVE_KEXIV2 class KipiMetadataProcessor : public KIPI::MetadataProcessor { public: KipiMetadataProcessor() {}; ~KipiMetadataProcessor() override {}; bool load(const QUrl& url) override { return m_meta.load(url.toLocalFile()); } bool save(const QUrl& url, bool writeToFileOnly) override { if (writeToFileOnly) m_meta.setMetadataWritingMode((int) KExiv2Iface::KExiv2::WRITETOIMAGEONLY); return m_meta.save(url.toLocalFile()); } bool applyChanges() override { return m_meta.applyChanges(); } QSize getPixelSize() override { return m_meta.getPixelSize(); } bool setImageProgramId(const QString& program, const QString& version) override { return m_meta.setImageProgramId(program, version); } QSize getImageDimensions() override { return m_meta.getImageDimensions(); } bool setImageDimensions(const QSize& size) override { return m_meta.setImageDimensions(size); } int getImageOrientation() override { return m_meta.getImageOrientation(); } bool setImageOrientation(int orientation) override { return m_meta.setImageOrientation((KExiv2Iface::KExiv2::ImageOrientation)orientation); } bool rotateExifQImage(QImage& img, int orientation) override { return m_meta.rotateExifQImage(img, (KExiv2Iface::KExiv2::ImageOrientation)orientation); } QDateTime getImageDateTime() override { return m_meta.getImageDateTime(); } bool setImageDateTime(const QDateTime& dt) override { return m_meta.setImageDateTime(dt); } bool getImagePreview(QImage& img) override { return m_meta.getImagePreview(img); } bool setImagePreview(const QImage& img) override { return m_meta.setImagePreview(img); } bool hasExif() override { return m_meta.hasExif(); } bool hasIptc() override { return m_meta.hasIptc(); } bool hasXmp() override { return m_meta.hasXmp(); } QByteArray getExif() override { return m_meta.getExifEncoded(); } QByteArray getIptc() override { return m_meta.getIptc(); } QByteArray getXmp() override { return m_meta.getXmp(); } bool setExif(const QByteArray& data) override { return m_meta.setExif(data); } bool setIptc(const QByteArray& data) override { return m_meta.setIptc(data); } bool setXmp(const QByteArray& data) override { return m_meta.setXmp(data); } bool registerXmpNameSpace(const QString& uri, const QString& prefix) override { return m_meta.registerXmpNameSpace(uri, prefix); } bool supportXmp() override { return m_meta.supportXmp(); } bool canWriteXmp(const QUrl& url) override { return m_meta.canWriteXmp(url.toLocalFile()); } bool removeExifTags(const QStringList& tagFilters) override { KExiv2Iface::KExiv2::MetaDataMap m = m_meta.getExifTagsDataList(tagFilters); if (m.isEmpty()) return false; for (KExiv2Iface::KExiv2::MetaDataMap::iterator it = m.begin(); it != m.end(); ++it) { m_meta.removeExifTag(it.key().toLatin1().constData()); } return true; } bool removeIptcTags(const QStringList& tagFilters) override { KExiv2Iface::KExiv2::MetaDataMap m = m_meta.getIptcTagsDataList(tagFilters); if (m.isEmpty()) return false; for (KExiv2Iface::KExiv2::MetaDataMap::iterator it = m.begin(); it != m.end(); ++it) { m_meta.removeIptcTag(it.key().toLatin1().constData()); } return true; } bool removeXmpTags(const QStringList& tagFilters) override { KExiv2Iface::KExiv2::MetaDataMap m = m_meta.getXmpTagsDataList(tagFilters); if (m.isEmpty()) return false; for (KExiv2Iface::KExiv2::MetaDataMap::iterator it = m.begin(); it != m.end(); ++it) { m_meta.removeXmpTag(it.key().toLatin1().constData()); } return true; } bool getGPSInfo(double& alt, double& lat, double& lon) override { return m_meta.getGPSInfo(alt, lat, lon); } bool setGPSInfo(const double alt, const double lat, const double lon) override { return m_meta.setGPSInfo(alt, lat, lon); } bool removeGPSInfo() override { return m_meta.removeGPSInfo(); } QString getExifTagString(const QString& tag) override { return m_meta.getExifTagString(tag.toLatin1().constData()); } bool setExifTagString(const QString& tag, const QString& val) override { return m_meta.setExifTagString(tag.toLatin1().constData(), val); } bool getExifTagRational(const QString& tag, long int& num, long int& den) override { return m_meta.getExifTagRational(tag.toLatin1().constData(), num, den); } bool setExifTagRational(const QString& tag, long int num, long int den) override { return m_meta.setExifTagRational(tag.toLatin1().constData(), num, den); } QString getXmpTagString(const QString& tag) override { return m_meta.getXmpTagString(tag.toLatin1().constData()); } bool setXmpTagString(const QString& tag, const QString& val) override { return m_meta.setXmpTagString(tag.toLatin1().constData(), val); } QStringList getXmpKeywords() override { return m_meta.getXmpKeywords(); } bool setXmpKeywords(const QStringList& keywords) override { return m_meta.setXmpKeywords(keywords); } QVariant getXmpTagVariant(const QString& tag) override { return m_meta.getXmpTagVariant(tag.toLatin1().constData()); } private: KExiv2Iface::KExiv2 m_meta; }; MetadataProcessor* KipiInterface::createMetadataProcessor() const { return (new KipiMetadataProcessor); } #else // HAVE_KEXIV2 MetadataProcessor* KipiInterface::createMetadataProcessor() const { qDebug() << "This interface was not compiled with libkexiv2 to support Metadata processing"; return 0; } #endif // HAVE_KEXIV2 // --------------------------------------------------------------------------------------- FileReadWriteLock* KipiInterface::createReadWriteLock(const QUrl&) const { return nullptr; // TODO } } // namespace KXMLKipiCmd diff --git a/tests/kxmlkipicmd/kipisetup.cpp b/tests/kxmlkipicmd/kipisetup.cpp index 0ee96ec..9ff2552 100644 --- a/tests/kxmlkipicmd/kipisetup.cpp +++ b/tests/kxmlkipicmd/kipisetup.cpp @@ -1,266 +1,266 @@ /** =========================================================== * @file * * This file is a part of KDE project * * * @date 2012-06-28 * @brief kipi host test application using KDE XML-GUI technology * * @author Copyright (C) 2012 by Victor Dodon * dodonvictor at gmail dot com * @author Copyright (C) 2012-2018 by Gilles Caulier * caulier dot gilles at gmail dot com * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "kipisetup.h" // Qt includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include -// KDE includes +// KF includes -#include -#include -#include +#include +#include +#include // Libkipi includes #include "pluginloader.h" #include "configwidget.h" // Local includes #include "kipitestpluginloader.h" using namespace KIPI; namespace KXMLKipiCmd { const QString SetupXML::CONFIG_GROUP_NAME = QString::fromLatin1("UI Settings"); class KipiSetup::Private { public: Private() : pluginFilter(nullptr), buttons(nullptr), tabView(nullptr), pluginsList(nullptr), xmlPage(nullptr) { } QLineEdit* pluginFilter; QDialogButtonBox* buttons; QTabWidget* tabView; ConfigWidget* pluginsList; SetupXML* xmlPage; }; KipiSetup::KipiSetup(QWidget* const parent) : QDialog(parent), d(new Private) { setWindowTitle(QLatin1String("Configure")); setModal(true); setMinimumSize(600, 400); const int spacing = QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); d->buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); d->tabView = new QTabWidget(this); QScrollArea* const sv = new QScrollArea(this); QWidget* const panel = new QWidget(sv->viewport()); QGridLayout* const grid = new QGridLayout(panel); d->pluginFilter = new QLineEdit(panel); d->pluginFilter->setClearButtonEnabled(true); d->pluginFilter->setPlaceholderText(QLatin1String("Plugins list filter.")); d->pluginsList = PluginLoader::instance()->configWidget(d->tabView); d->pluginsList->setToolTip(QLatin1String("Configure plugins")); QStringList labels; labels.append(QLatin1String("Name")); labels.append(QLatin1String("Categories")); labels.append(QLatin1String("Description")); labels.append(QLatin1String("Author")); d->pluginsList->setHeaderLabels(labels); grid->addWidget(d->pluginFilter, 0, 0, 1, 1); grid->addWidget(d->pluginsList, 1, 0, 1, 1); grid->setRowStretch(1, 10); grid->setContentsMargins(spacing, spacing, spacing, spacing); grid->setSpacing(spacing); sv->setWidget(panel); sv->setWidgetResizable(true); d->tabView->insertTab(KipiPluginsPage, sv, QIcon(QLatin1String(":/icons/kipi-icon.svg")), QLatin1String("Kipi Plugins")); d->xmlPage = new SetupXML(d->tabView); d->xmlPage->setToolTip(QLatin1String("Configure the UI file for the KXMLKipiCmd application")); d->tabView->insertTab(XmlFilesPage, d->xmlPage, QIcon::fromTheme(QString::fromLatin1("application-xml")), QLatin1String("UI layouts")); QVBoxLayout* const vbx = new QVBoxLayout(this); vbx->addWidget(d->tabView); vbx->addWidget(d->buttons); setLayout(vbx); KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group = config->group("Setup Dialog"); KWindowConfig::restoreWindowSize(windowHandle(), group); d->tabView->setCurrentIndex(group.readEntry("Setup Page", 0)); connect(d->pluginFilter, &QLineEdit::textChanged, this, &KipiSetup::slotFilterChanged); connect(d->buttons->button(QDialogButtonBox::Ok), &QPushButton::clicked, this, &KipiSetup::slotOkClicked); connect(d->buttons->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &KipiSetup::close); } KipiSetup::~KipiSetup() { KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group = config->group(QString::fromLatin1("Setup Dialog")); KWindowConfig::saveWindowSize(windowHandle(), group); group.writeEntry("Setup Page", d->tabView->currentIndex()); config->sync(); delete d; } void KipiSetup::slotFilterChanged(const QString& filter) { d->pluginsList->setFilter(filter, Qt::CaseInsensitive); } bool KipiSetup::runSetupDialog(QWidget* const parent) { QPointer setup = new KipiSetup(parent); bool success = (setup->exec() == QDialog::Accepted); delete setup; return success; } void KipiSetup::slotOkClicked() { qDebug() << "Ok clicked"; QApplication::setOverrideCursor(Qt::WaitCursor); d->pluginsList->apply(); d->xmlPage->apply(); QApplication::restoreOverrideCursor(); accept(); } // ------------------------------------------------------------------- class SetupXML::Private { public: Private() { xmlFilesCob = nullptr; } QString uiFilesPath; QList uiFilesList; QComboBox* xmlFilesCob; }; SetupXML::SetupXML(QWidget* const parent) : QScrollArea(parent), d(new Private) { QWidget* const panel = new QWidget(viewport()); setWidget(panel); setWidgetResizable(true); d->uiFilesPath = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QApplication::applicationName(), QStandardPaths::LocateDirectory).last(); qDebug() << d->uiFilesPath; QDir dir(d->uiFilesPath); QString filter = QString::fromLatin1("*ui.rc"); d->uiFilesList = dir.entryList(QStringList(filter), QDir::Files | QDir::NoSymLinks); d->xmlFilesCob = new QComboBox(panel); d->xmlFilesCob->setEditable(false); foreach(QString uiFile, d->uiFilesList) { d->xmlFilesCob->addItem(uiFile, uiFile); } QVBoxLayout* const mainLayout = new QVBoxLayout; mainLayout->setAlignment(Qt::AlignTop); mainLayout->addWidget(d->xmlFilesCob); panel->setLayout(mainLayout); KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group = config->group(CONFIG_GROUP_NAME); QString uiFile = group.readEntry("UiFile", "kxmlkipicmd_defaultui.rc"); d->xmlFilesCob->setCurrentIndex(d->uiFilesList.indexOf(uiFile)); setWidgetResizable(true); } SetupXML::~SetupXML() { delete d; } void SetupXML::apply() { QString uiFile = d->xmlFilesCob->itemData(d->xmlFilesCob->currentIndex()).toString(); KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group = config->group(CONFIG_GROUP_NAME); QString oldUiFile = group.readEntry("UiFile", "kxmlkipicmd_defaultui.rc"); if (uiFile != oldUiFile) { group.writeEntry("UiFile", uiFile); } group.sync(); } } // namespace KXMLKipiCmd diff --git a/tests/kxmlkipicmd/kipitestmainwindow.cpp b/tests/kxmlkipicmd/kipitestmainwindow.cpp index e2e5321..eb38c66 100644 --- a/tests/kxmlkipicmd/kipitestmainwindow.cpp +++ b/tests/kxmlkipicmd/kipitestmainwindow.cpp @@ -1,192 +1,192 @@ /** =========================================================== * @file * * This file is a part of KDE project * * * @date 2012-06-20 * @brief kipi host test application * * @author Copyright (C) 2012 by Victor Dodon * dodonvictor at gmail dot com * @author Copyright (C) 2012-2018 by Gilles Caulier * caulier dot gilles at gmail dot com * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "kipitestmainwindow.h" // Qt includes #include #include #include -// KDE includes - -#include -#include -#include -#include -#include -#include -#include -#include +// KF includes + +#include +#include +#include +#include +#include +#include +#include +#include // LibKIPI includes #include "pluginloader.h" #include "libkipi_version.h" // Local includes #include "kipitestpluginloader.h" #include "kipiinterface.h" #include "kipisetup.h" namespace KXMLKipiCmd { KipiTestMainWindow* KipiTestMainWindow::m_instance = nullptr; class KipiTestMainWindow::Private { public: Private() : config(nullptr), quitAction(nullptr), showMenuBarAction(nullptr), kipiInterface(nullptr), uiFile(QString()) { } KSharedConfig::Ptr config; QAction* quitAction; KToggleAction* showMenuBarAction; KipiInterface* kipiInterface; QString uiFile; }; KipiTestMainWindow::KipiTestMainWindow(const QList& selectedImages, const QList& selectedAlbums, const QList& allAlbums) : KXmlGuiWindow(nullptr), d(new Private()) { m_instance = this; d->config = KSharedConfig::openConfig(); d->kipiInterface = new KipiInterface(this, QLatin1String("kxmlkipicmd_KIPI_interface")); KConfigGroup uiGroup = d->config->group(QLatin1String("UI Settings")); QString uiFile = uiGroup.readEntry(QString::fromLatin1("UiFile"), QString::fromLatin1("kxmlkipicmd_defaultui.rc")); d->uiFile = uiFile; setObjectName(QString::fromLatin1("kxmlkipicmd")); setMinimumSize(QSize(800, 600)); KConfigGroup mainWindowGroup = d->config->group(QLatin1String("MainWindow Dialog")); KWindowConfig::restoreWindowSize(windowHandle(), mainWindowGroup); if (!selectedImages.empty()) { d->kipiInterface->addSelectedImages(selectedImages); } if (!selectedAlbums.empty()) { d->kipiInterface->addSelectedAlbums(selectedAlbums); } if (!allAlbums.empty()) { d->kipiInterface->addAlbums(allAlbums); } setupActions(); // Provides a menu entry that allows showing/hiding the toolbar(s) setStandardToolBarMenuEnabled(true); loadPlugins(); } KipiTestMainWindow::~KipiTestMainWindow() { KConfigGroup group = d->config->group(QLatin1String("MainWindow Dialog")); KWindowConfig::saveWindowSize(windowHandle(), group); group.sync(); delete d; } KipiTestMainWindow* KipiTestMainWindow::instance() { return m_instance; } void KipiTestMainWindow::setupActions() { d->showMenuBarAction = KStandardAction::showMenubar(this, SLOT(slotShowMenuBar()), actionCollection()); d->quitAction = KStandardAction::quit(this, SLOT(close()), actionCollection()); actionCollection()->addAction(QString::fromLatin1("app_exit"), d->quitAction); KStandardAction::keyBindings(this, SLOT(slotEditKeys()), actionCollection()); KStandardAction::configureToolbars(this, SLOT(slotConfToolbars()), actionCollection()); KStandardAction::preferences(this, SLOT(slotSetup()), actionCollection()); createGUI(d->uiFile); } void KipiTestMainWindow::loadPlugins() { new KipiTestPluginLoader(this, d->kipiInterface); } void KipiTestMainWindow::slotShowMenuBar() { menuBar()->setVisible(d->showMenuBarAction->isChecked()); } void KipiTestMainWindow::slotEditKeys() { KShortcutsDialog dialog(KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsAllowed, this); dialog.addCollection(actionCollection(), QLatin1String("General")); dialog.addCollection(KipiTestPluginLoader::instance()->pluginsActionCollection(), QLatin1String("KIPI-Plugins")); dialog.configure(); } void KipiTestMainWindow::slotConfToolbars() { KConfigGroup grp = d->config->group(QLatin1String("General Settings")); saveMainWindowSettings(grp); KEditToolBar dlg(factory(), this); connect(&dlg, &KEditToolBar::newToolBarConfig, this, &KipiTestMainWindow::slotNewToolbarConfig); dlg.exec(); } void KipiTestMainWindow::slotNewToolbarConfig() { applyMainWindowSettings(d->config->group(QLatin1String("General Settings"))); } void KipiTestMainWindow::slotSetup() { KipiSetup::runSetupDialog(this); } } // namespace KXMLKipiCmd diff --git a/tests/kxmlkipicmd/kipitestmainwindow.h b/tests/kxmlkipicmd/kipitestmainwindow.h index 73cc8ef..9a178c1 100644 --- a/tests/kxmlkipicmd/kipitestmainwindow.h +++ b/tests/kxmlkipicmd/kipitestmainwindow.h @@ -1,77 +1,77 @@ /** =========================================================== * @file * * This file is a part of KDE project * * * @date 2012-06-20 * @brief kipi host test application using KDE XML-GUI technology * * @author Copyright (C) 2012 by Victor Dodon * dodonvictor at gmail dot com * @author Copyright (C) 2012-2018 by Gilles Caulier * caulier dot gilles at gmail dot com * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef KIPITESTMAINWINDOW_H #define KIPITESTMAINWINDOW_H // Qt includes #include -// KDE includes +// KF includes -#include +#include namespace KXMLKipiCmd { class KipiTestMainWindow : public KXmlGuiWindow { Q_OBJECT public: KipiTestMainWindow(const QList& selectedImages, const QList& selectedAlbums, const QList& allAlbums); ~KipiTestMainWindow() override; static KipiTestMainWindow* instance(); private: void setupActions(); void loadPlugins(); private Q_SLOTS: void slotShowMenuBar(); void slotEditKeys(); void slotConfToolbars(); void slotNewToolbarConfig(); void slotSetup(); private: class Private; Private* const d; static KipiTestMainWindow* m_instance; }; } // namespace KXMLKipiCmd #endif // KIPITESTMAINWINDOW_H diff --git a/tests/kxmlkipicmd/kipitestpluginloader.cpp b/tests/kxmlkipicmd/kipitestpluginloader.cpp index c71a300..c79352c 100644 --- a/tests/kxmlkipicmd/kipitestpluginloader.cpp +++ b/tests/kxmlkipicmd/kipitestpluginloader.cpp @@ -1,320 +1,320 @@ /** =========================================================== * @file * * This file is a part of KDE project * * * @date 2012-06-20 * @brief kipi host test application using KDE XML-GUI technology * * @author Copyright (C) 2012 by Victor Dodon * dodonvictor at gmail dot com * @author Copyright (C) 2012-2018 by Gilles Caulier * caulier dot gilles at gmail dot com * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "kipitestpluginloader.h" // Qt includes #include #include #include -// KDE includes +// KF includes -#include -#include -#include +#include +#include +#include // Local includes #include "kipiinterface.h" #include "kipitestmainwindow.h" namespace KXMLKipiCmd { class KipiTestPluginLoader::Private { public: Private() { app = nullptr; kipipluginsActionCollection = nullptr; kipiPluginLoader = nullptr; kipiInterface = nullptr; } PluginLoader* kipiPluginLoader; KipiInterface* kipiInterface; KXmlGuiWindow* app; KActionCollection* kipipluginsActionCollection; QMap kipiCategoryMap; }; // -- Static values ------------------------------- KipiTestPluginLoader* KipiTestPluginLoader::m_instance = nullptr; // ----------------------------------------------- KipiTestPluginLoader::KipiTestPluginLoader(KXmlGuiWindow* const parent, KipiInterface *const interface) : QObject(parent), d(new Private) { m_instance = this; d->kipiInterface = interface; d->app = parent; loadPlugins(); } KipiTestPluginLoader::~KipiTestPluginLoader() { delete d; m_instance = nullptr; } KipiTestPluginLoader* KipiTestPluginLoader::instance() { return m_instance; } KActionCollection* KipiTestPluginLoader::pluginsActionCollection() const { return d->kipipluginsActionCollection; } QList KipiTestPluginLoader::pluginList() { return d->kipiPluginLoader->pluginList(); } QList KipiTestPluginLoader::kipiActionsByCategory(Category cat) const { KActionCategory* const category = d->kipiCategoryMap[cat]; if (category) { return category->actions(); } return QList(); } void KipiTestPluginLoader::loadPlugins() { d->kipipluginsActionCollection = new KActionCollection(d->app, QLatin1String("kipitest")); QStringList ignores; // List of obsoletes plugins to not load // Comment the following two lines if you want to load the HelloWorld and // the KXMLHelloWorld plugins for testing purposes //ignores.append("HelloWorld"); //ignores.append("KXMLHelloWorld"); ignores.append(QString::fromLatin1("KameraKlient")); // These plugins have been renamed with 0.2.0-rc1 ignores.append(QString::fromLatin1("Facebook Exporter")); ignores.append(QString::fromLatin1("SmugMug Exporter")); ignores.append(QString::fromLatin1("SlideShow")); ignores.append(QString::fromLatin1("PrintWizard")); ignores.append(QString::fromLatin1("SimpleViewer")); ignores.append(QString::fromLatin1("KioExport")); d->kipiPluginLoader = new PluginLoader(d->app); d->kipiPluginLoader->setInterface(d->kipiInterface); d->kipiPluginLoader->setIgnoredPluginsList(ignores); d->kipiPluginLoader->init(); connect(d->kipiPluginLoader, &PluginLoader::replug, this, &KipiTestPluginLoader::slotKipiPluginsPlug); d->kipiPluginLoader->loadPlugins(); } void KipiTestPluginLoader::slotKipiPluginsPlug() { d->kipiCategoryMap.clear(); d->kipipluginsActionCollection->clear(); PluginLoader::PluginList list = d->kipiPluginLoader->pluginList(); int cpt = 0; for (PluginLoader::PluginList::ConstIterator it = list.constBegin() ; it != list.constEnd() ; ++it) { Plugin* const plugin = (*it)->plugin(); if (!plugin || !(*it)->shouldLoad()) { continue; } d->app->guiFactory()->removeClient(plugin); } QStringList disabledActions = d->kipiPluginLoader->disabledPluginActions(); for (PluginLoader::PluginList::ConstIterator it = list.constBegin() ; it != list.constEnd() ; ++it) { Plugin* const plugin = (*it)->plugin(); if (!plugin || !(*it)->shouldLoad()) { continue; } ++cpt; plugin->setup(d->app); plugin->rebuild(); foreach(QAction* const action, plugin->actions()) { QString actionName(action->objectName()); Category cat = plugin->category(action); if (cat == InvalidCategory) { qWarning() << "Plugin action '" << actionName << "' has invalid category!"; continue; } if (!disabledActions.contains(actionName)) { KActionCategory* category = d->kipiCategoryMap[cat]; if (!category) { category = new KActionCategory(categoryName(cat), d->kipipluginsActionCollection); d->kipiCategoryMap.insert(cat, category); } category->addAction(actionName, qobject_cast(action)); } else { qDebug() << "Plugin '" << actionName << "' is disabled."; } } } for (PluginLoader::PluginList::ConstIterator it = list.constBegin() ; it != list.constEnd() ; ++it) { Plugin* const plugin = (*it)->plugin(); if (!plugin || !(*it)->shouldLoad()) { continue; } d->app->guiFactory()->addClient(plugin); } // load KIPI actions settings d->kipipluginsActionCollection->readSettings(); } void KipiTestPluginLoader::checkEmptyCategory(Category cat) { KActionCategory* const category = d->kipiCategoryMap[cat]; if (!category) { QString actionName = QString::fromLatin1("emptyCategory") + categoryShortName(cat); QAction* const action = d->app->actionCollection()->addAction(actionName); action->setEnabled(false); d->kipiCategoryMap.insert(cat, new KActionCategory(categoryName(cat), d->kipipluginsActionCollection)); } } QString KipiTestPluginLoader::categoryName(Category cat) const { QString res; switch (cat) { case ExportPlugin: res = QLatin1String("Export Tools"); break; case ImportPlugin: res = QLatin1String("Import Tools"); break; case ImagesPlugin: res = QLatin1String("Images Tools"); break; case ToolsPlugin: res = QLatin1String("Miscellaneous Tools"); break; case BatchPlugin: res = QLatin1String("Batch Tools"); break; case CollectionsPlugin: res = QLatin1String("Albums Tools"); break; default: res = QLatin1String("Unknown Tools"); break; } return res; } QString KipiTestPluginLoader::categoryShortName(Category cat) const { QString res; switch (cat) { case ExportPlugin: res = QLatin1String("Export"); break; case ImportPlugin: res = QLatin1String("Import"); break; case ToolsPlugin: res = QLatin1String("Tools"); break; case BatchPlugin: res = QLatin1String("Batch"); break; case CollectionsPlugin: res = QLatin1String("Collections"); break; default: res = QLatin1String("Unknown"); break; } return res; } } // namespace KXMLKipiCmd diff --git a/tests/plugins/kxmlhelloworld/plugin_kxmlhelloworld.cpp b/tests/plugins/kxmlhelloworld/plugin_kxmlhelloworld.cpp index c1a3e4a..20d6c71 100644 --- a/tests/plugins/kxmlhelloworld/plugin_kxmlhelloworld.cpp +++ b/tests/plugins/kxmlhelloworld/plugin_kxmlhelloworld.cpp @@ -1,333 +1,333 @@ /* ============================================================ * * This file is a part of KDE project * * Date : 2012-02-16 * Description : an Hello World plugin using KDE XML-GUI technology. * * Copyright (C) 2012-2018 by Gilles Caulier * Copyright (C) 2012 by Victor Dodon * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ /** Take a care about includes order, to prevent compilation problem. * 1/ class header file. * 2/ C ansi if really necessary. * 3/ C++ (always preferred than C ansi. * 4/ Extra libraries such as openCV for ex. * 4/ Qt. * 5/ KDE. * 6/ Local files. * 7/ At end of file .moc file of class. * * Also, use C++ classes include header styles with Qt5, * but do not use it with KF5 headers (use C ANSI style instead). */ // include here header file. See also loc file to plug at end of implementation. #include "plugin_kxmlhelloworld.h" // Qt includes #include #include #include #include #include #include #include #include #include #include #include -// KDE includes +// KF includes -#include +#include /// This is all libkipi headers included in this tool. #include "imagecollection.h" #include "imagecollectionselector.h" #include "interface.h" // Special header used to include kipi-plugins debug space when plugin is includes in Kipi-plugins project. //#include "kipiplugins_debug.h" /** Put here all Kipi-plugins common includes used to create a new tool. * Look into kipi-plugins/common/libkipiplugins/ API documentation for details. */ /// You must wrap all your plugin code to a dedicated namespace namespace KIPIKXMLHelloWorldPlugin { /** Under Kipi-plugins, you can use KPToolDialog class from kipi-plugins to display plugin dialogs. It offers some facilities to set data and rules about plugins, especially to wrap properly tool with KDE bugzilla. We use KPAboutData container for that. */ /** Using private container everywhere is clear to speed up compilation and reduce source code dependencies through header files. * See this url for details : http://techbase.kde.org/Policies/Binary_Compatibility_Issues_With_C%2B%2B#Using_a_d-Pointer */ class Plugin_KXMLHelloWorld::Private { public: Private() { /// It's always clean to init pointers to zero. If crash appear, /// debugger will show a null pointer instead a non initialized one. actionImages = nullptr; actionTools = nullptr; actionExport = nullptr; actionImport = nullptr; } /** These plugin actions will plugged into menu KIPI host application. */ QAction* actionImages; QAction* actionTools; QAction* actionExport; QAction* actionImport; }; /** Macro from KDE KParts to create the factory for this plugin. * The first argument is the name of the plugin library * and the second is the generic factory templated from * the class for your plugin. */ K_PLUGIN_FACTORY(KXMLHelloWorldFactory, registerPlugin();) /** The plugin constructor. Note that plugin name passed as string in 3rd argument of KIPI::Plugin parent class * is the same than Name value from .desktop file. */ Plugin_KXMLHelloWorld::Plugin_KXMLHelloWorld(QObject* const parent, const QVariantList&) : Plugin(parent, "KXMLHelloWorld"), /// Private container is allocated here. d(new Private) { // plugin do not use libkipi debug space. There is a debug space dedicate to kipi-plugins. See kipiplugins_debug.h header. //qCDebug(KIPIPLUGINS_LOG) << "Plugin_KXMLHelloWorld plugin loaded"; /** This is needed to setup the plugin gui and to merge with the kipi host * application gui. * The name of the UI file must be: nameofpluginui.rc, where "nameofplugin" * is the name given to the plugin factory, usually: kipiplugin_ . * UI file of the plugin must be installed in kipi data dir. */ setUiBaseName("kipiplugin_kxmlhelloworldui.rc"); /** We need to call setupXML so the XML file and the GUI of the plugin to * be merged with those of the KIPI host app */ setupXML(); } Plugin_KXMLHelloWorld::~Plugin_KXMLHelloWorld() { /// Don't forget to clear d private container allocation in destructor to prevent memory leak. delete d; } void Plugin_KXMLHelloWorld::setup(QWidget* const widget) { /** Each plugin must overload Plugin::setup method. * We pass the widget which host plugin in KIPI host application */ Plugin::setup(widget); /** This is the interface instance to plugin host application. Note that you can get it everywhere in your plugin using * instance of KIPI::PluginLoader singleton which provide a method for that. * Since libkipi 2.0.0, KIPI host interface is also available from KIPI::Plugin::interface(). */ if (!interface()) return; /** We will enable plugin actions only if the KIPI interface is not null */ setupActions(); } void Plugin_KXMLHelloWorld::setupActions() { /** We define plugin action which will be plug in KIPI host application. * Note that if you set keyboard shortcut to an action you must take a care * about already existing one from other tool to prevent conflict. * Don't forget to define an unique string name to your action, to be able to disable it * in KIPI host application if necessary. You must check of course name already used in * others tool before to prevent redundancy. */ /** We need to call setDefaultCategory in case the plugin loader cannot * recognize the category of an action */ setDefaultCategory(ExportPlugin); /** An action dedicated to be plugged in Image menu. */ d->actionImages = new QAction(this); d->actionImages->setText(QLatin1String("KXML Hello World Image...")); d->actionImages->setIcon(QIcon::fromTheme(QString::fromLatin1("script-error"))); d->actionImages->setShortcut(QKeySequence(Qt::ALT + Qt::SHIFT + Qt::CTRL + Qt::Key_F1)); /** Connect plugin action signal to dedicated slot. */ connect(d->actionImages, SIGNAL(triggered(bool)), this, SLOT(slotActivateActionImages())); /** We need to register actions in plugin instance */ addAction(QString::fromLatin1("kxmlhelloworld-actionImage"), d->actionImages, ImagesPlugin); /** This will get items selection from KIPI host application. */ ImageCollection selection = interface()->currentSelection(); d->actionImages->setEnabled(selection.isValid() && !selection.images().isEmpty()); /** Another action dedicated to be plugged in Tool menu. */ d->actionTools = new QAction(this); d->actionTools->setText(QLatin1String("KXML Hello World Tools...")); d->actionTools->setIcon(QIcon::fromTheme(QString::fromLatin1("script-error"))); d->actionTools->setShortcut(QKeySequence(Qt::ALT+Qt::SHIFT+Qt::CTRL+Qt::Key_F2)); connect(d->actionTools, SIGNAL(triggered(bool)), this, SLOT(slotActivateActionTools())); addAction(QString::fromLatin1("kxmlhelloworld-actionTools"), d->actionTools, ToolsPlugin); /** We will get current selected album in the host tree view */ ImageCollection currAlbum = interface()->currentAlbum(); bool enable = currAlbum.isValid() && !currAlbum.images().isEmpty(); d->actionTools->setEnabled(enable); /** Another action dedicated to be plugged in host Export menu. */ d->actionExport = new QAction(this); d->actionExport->setText(QLatin1String("KXML Hello World Export...")); d->actionExport->setIcon(QIcon::fromTheme(QString::fromLatin1("script-error"))); d->actionExport->setShortcut(QKeySequence(Qt::ALT+Qt::SHIFT+Qt::CTRL+Qt::Key_F3)); connect(d->actionExport, SIGNAL(triggered(bool)), this, SLOT(slotActivateActionExport())); addAction(QString::fromLatin1("kxmlhelloworld-actionExport"), d->actionExport, ExportPlugin); /** Another action dedicated to be plugged in host Import menu. */ d->actionImport = new QAction(this); d->actionImport->setText(QLatin1String("KXML Hello World Import...")); d->actionImport->setIcon(QIcon::fromTheme(QString::fromLatin1("script-error"))); d->actionImport->setShortcut(QKeySequence(Qt::ALT+Qt::SHIFT+Qt::CTRL+Qt::Key_F4)); connect(d->actionImport, SIGNAL(triggered(bool)), this, SLOT(slotActivateActionImport())); addAction(QString::fromLatin1("kxmlhelloworld-actionImport"), d->actionImport, ImportPlugin); /** If selection change in KIPI host application, this signal will be fired, and plugin action enabled accordingly. */ connect(interface(), SIGNAL(selectionChanged(bool)), d->actionImages, SLOT(setEnabled(bool))); connect(interface(), SIGNAL(currentAlbumChanged(bool)), d->actionTools, SLOT(setEnabled(bool))); } void Plugin_KXMLHelloWorld::slotActivateActionImages() { /** When actionImages is actived, we display list of items selected in a message box. * This example show a simple dialog with current items selected in KIPI host application. * You can branch here your dedicated dialog to process items as you want. */ ImageCollection images = interface()->currentSelection(); if (images.isValid() && !images.images().isEmpty()) { QStringList names; foreach (const QUrl& url, images.images()) names << url.fileName(); QMessageBox::information(nullptr, QLatin1String("This is the list of selected items"), names.join(QString::fromLatin1("\n"))); } } void Plugin_KXMLHelloWorld::slotActivateActionTools() { /** When actionTools is actived, we display a dedicated widget to select albums from kipi host application * for post processing purpose. When selection is done, we display it in a message box. */ QPointer dlg = new QDialog(nullptr); ImageCollectionSelector* const selector = interface()->imageCollectionSelector(dlg); QDialogButtonBox* const buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, dlg); QVBoxLayout* const vbox = new QVBoxLayout(dlg); vbox->addWidget(selector); vbox->addWidget(buttons); dlg->setLayout(vbox); connect(buttons->button(QDialogButtonBox::Ok), &QPushButton::clicked, dlg.data(), &QDialog::accept); connect(buttons->button(QDialogButtonBox::Cancel), &QPushButton::clicked, dlg.data(), &QDialog::reject); dlg->exec(); QList list = selector->selectedImageCollections(); if (!list.isEmpty()) { QStringList names; foreach (const ImageCollection& col, list) names << col.name(); QMessageBox::information(nullptr, QLatin1String("This is the list of selected albums"), names.join(QString::fromLatin1("\n"))); } delete dlg; } void Plugin_KXMLHelloWorld::slotActivateActionExport() { /** When actionExport is actived, we can display a dedicated widget from libkipiplugins which will show * and permit to manage current items selection from kipi host application for batch post-processing purpose. */ QMessageBox::information(nullptr, QLatin1String("Information"), QLatin1String("Plugin_KXMLHelloWorld::slotActivateActionExport() activated")); } void Plugin_KXMLHelloWorld::slotActivateActionImport() { ImageCollection images = interface()->currentSelection(); if (images.isValid() && !images.images().isEmpty()) { /** When actionImport is actived, we can display a dedicated widget from libkipiplugins which will preview * the first selected item of current selection from kipi host application. */ } QMessageBox::information(nullptr, QLatin1String("Information"), QLatin1String("Plugin_KXMLHelloWorld::slotActivateActionImport() activated")); } } // namespace KIPIHelloWorldPlugin // Moc file must be included at end because it's a plugin (it's a specific and special case where we need to do it). #include "plugin_kxmlhelloworld.moc"