diff --git a/src/core/kexipartmanager.cpp b/src/core/kexipartmanager.cpp index af26d9d8b..bdd095181 100644 --- a/src/core/kexipartmanager.cpp +++ b/src/core/kexipartmanager.cpp @@ -1,300 +1,302 @@ /* This file is part of the KDE project Copyright (C) 2003 Lucijan Busch Copyright (C) 2003-2015 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kexipartmanager.h" #include "kexipart.h" #include "kexiinternalpart.h" #include "kexipartinfo.h" //! @todo KEXI3 #include "kexistaticpart.h" #include "KexiVersion.h" #include "KexiJsonTrader.h" #include #include #include #include #include #include #include #include #include #include using namespace KexiPart; typedef QHash KexiInternalPartDict; Q_GLOBAL_STATIC_WITH_ARGS(KexiJsonTrader, KexiPartTrader_instance, ("kexi")) class Manager::Private { public: explicit Private(Manager *manager_); ~Private(); Manager *manager; PartDict parts; KexiInternalPartDict internalParts; PartInfoList partlist; PartInfoDict partsByPluginId; bool lookupDone; bool lookupResult; }; Manager::Private::Private(Manager *manager_) : manager(manager_) , lookupDone(false) , lookupResult(false) { } Manager::Private::~Private() { qDeleteAll(partlist); partlist.clear(); } //--- Manager::Manager(QObject *parent) : QObject(parent), KDbResultable(), d(new Private(this)) { } Manager::~Manager() { delete d; } template PartClass* Manager::part(Info *info, QHash *partDict) { if (!info) { return 0; } clearResult(); KDbMessageGuard mg(this); if (!lookup()) { return 0; } if (!info->isValid()) { m_result = KDbResult(info->errorMessage()); return 0; } PartClass *p = partDict->value(info->pluginId()); if (p) { return p; } // actual loading KPluginFactory *factory = qobject_cast(info->instantiate()); if (!factory) { m_result = KDbResult(ERR_OBJECT_NOT_FOUND, xi18nc("@info", "Could not load Kexi plugin file \"%1\".", info->fileName())); info->setErrorMessage(m_result.message()); qWarning() << m_result.message(); return 0; } p = factory->create(this); if (!p) { m_result = KDbResult(ERR_CANNOT_LOAD_OBJECT, i18n("Could not open Kexi plugin \"%1\".").arg(info->fileName())); qWarning() << m_result.message(); return 0; } p->setInfo(info); p->setObjectName(QString("%1 plugin").arg(info->id())); partDict->insert(info->pluginId(), p); return p; } //! @return a string list @a list with removed whitespace from the beginning and end of each string. //! Empty strings are also removed. static QStringList cleanupStringList(const QStringList &list) { QStringList result; foreach(const QString &item, list) { QString cleanedItem = item.trimmed(); if (!cleanedItem.isEmpty()) { result.append(cleanedItem); } } return result; } bool Manager::lookup() { if (d->lookupDone) { return d->lookupResult; } d->lookupDone = true; d->lookupResult = false; d->partlist.clear(); d->partsByPluginId.clear(); d->parts.clear(); // load visual order of plugins KConfigGroup cg(KSharedConfig::openConfig()->group("Parts")); const QStringList orderedPluginIds = cleanupStringList( cg.readEntry("Order", "org.kexi-project.table," "org.kexi-project.query," "org.kexi-project.form," "org.kexi-project.report," "org.kexi-project.macro," "org.kexi-project.script").split(',')); QVector orderedInfos(orderedPluginIds.count()); QStringList serviceTypes; serviceTypes << "Kexi/Viewer" << "Kexi/Designer" << "Kexi/Editor" << "Kexi/ModalDialog"; - const QList offers = KexiPartTrader_instance->query(serviceTypes); - foreach(QPluginLoader *loader, offers) { + QList offers = KexiPartTrader_instance->query(serviceTypes); + foreach(const QPluginLoader *loader, offers) { QScopedPointer info(new Info(*loader)); if (info->id().isEmpty()) { qWarning() << "No plugin ID (X-KDE-PluginInfo-Name) specified for Kexi Part" << info->fileName() << "-- skipping!"; continue; } // check version if (info->majorVersion() != KEXI_PART_VERSION) { qWarning() << "Kexi plugin" << info->id() << "has version (X-KDE-PluginInfo-Version)" << info->majorVersion() << "but required version is" << KEXI_PART_VERSION << "-- skipping!"; continue; } // skip experimental types if ( (!Kexi::tempShowMacros() && info->id() == "org.kexi-project.macro") || (!Kexi::tempShowScripts() && info->id() == "org.kexi-project.script") ) { continue; } // skip duplicates if (d->partsByPluginId.contains(info->id())) { qWarning() << "More than one Kexi plugin with ID" << info->id() << info->fileName() << "-- skipping this one"; continue; } // find correct place for plugins visible in Navigator if (info->isVisibleInNavigator()) { const int index = orderedPluginIds.indexOf(info->id()); if (index != -1) { orderedInfos[index] = info.data(); } else { orderedInfos.append(info.data()); } // append later when we know order } else { // append now d->partlist.append(info.data()); } d->partsByPluginId.insert(info->pluginId(), info.data()); info.take(); } + qDeleteAll(offers); + offers.clear(); // fill the final list using computed order for (int i = 0; i < orderedInfos.size(); i++) { Info *info = orderedInfos[i]; if (!info) { continue; } //qDebug() << "adding Kexi part info" << info->pluginId(); d->partlist.insert(i, info); } // now the d->partlist is: [ordered plugins visible in Navigator] [other plugins in unspecified order] d->lookupResult = true; return true; } Part* Manager::part(Info *info) { KDbMessageGuard mg(this); Part *p = part(info, &d->parts); if (p) { emit partLoaded(p); } return p; } static QString realPluginId(const QString &pluginId) { if (pluginId.contains('.')) { return pluginId; } else { // not like "org.kexi-project.table" - construct return QString::fromLatin1("org.kexi-project.") + QString(pluginId).remove("kexi/"); } } Part* Manager::partForPluginId(const QString &pluginId) { Info* info = infoForPluginId(pluginId); return part(info); } Info* Manager::infoForPluginId(const QString &pluginId) { KDbMessageGuard mg(this); if (!lookup()) return 0; const QString realId = realPluginId(pluginId); Info *i = realId.isEmpty() ? 0 : d->partsByPluginId.value(realId); if (i) return i; m_result = KDbResult(xi18nc("@info", "No plugin for ID %1", realId)); return 0; } /*! @todo KEXI3 void Manager::insertStaticPart(StaticPart* part) { if (!part) return; KDbMessageGuard mg(this); if (!lookup()) return; d->partlist.append(part->info()); if (!part->info()->pluginId().isEmpty()) d->partsByPluginId.insert(part->info()->pluginId(), part->info()); d->parts.insert(part->info()->pluginId(), part); } */ KexiInternalPart* Manager::internalPartForPluginId(const QString& pluginId) { Info* info = infoForPluginId(pluginId); return info ? part(info, &d->internalParts) : 0; } PartInfoList* Manager::infoList() { KDbMessageGuard mg(this); if (!lookup()) { return 0; } return &d->partlist; } diff --git a/src/formeditor/widgetlibrary.cpp b/src/formeditor/widgetlibrary.cpp index 64c856caf..47a087d6a 100644 --- a/src/formeditor/widgetlibrary.cpp +++ b/src/formeditor/widgetlibrary.cpp @@ -1,768 +1,770 @@ /* This file is part of the KDE project Copyright (C) 2003 Lucijan Busch Copyright (C) 2004 Cedric Pasteur Copyright (C) 2004-2015 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "widgetlibrary.h" #include #include "WidgetInfo.h" #include "widgetfactory.h" #include "libactionwidget.h" #include "container.h" #include "form.h" #include "formIO.h" #include "FormWidgetInterface.h" #include "objecttree.h" #include "KexiJsonTrader.h" #include "KexiFormWidgetsPluginMetaData.h" #include #include #include #include #include #include namespace KFormDesigner { Q_GLOBAL_STATIC_WITH_ARGS(KexiJsonTrader, KexiFormWidgetsPluginTrader_instance, ("kexi/forms/widgets")) //! @internal class WidgetLibrary::Private { public: Private(WidgetLibrary *library, const QStringList& supportedFactoryGroups) : showAdvancedProperties(true) , q(library) , m_couldNotFindAnyFormWidgetPluginsErrorDisplayed(false) , m_supportedFactoryGroups(supportedFactoryGroups.toSet()) , m_lookupDone(false) , m_loadFactoriesDone(false) { q->setMessageHandler(&messageHandler); m_advancedProperties.insert("acceptDrops"); m_advancedProperties.insert("accessibleDescription"); m_advancedProperties.insert("accessibleName"); m_advancedProperties.insert("autoMask"); m_advancedProperties.insert("backgroundOrigin"); m_advancedProperties.insert("backgroundMode");//this is rather useless m_advancedProperties.insert("baseSize"); m_advancedProperties.insert("contextMenuEnabled"); m_advancedProperties.insert("contextMenuPolicy"); m_advancedProperties.insert("cursorPosition"); m_advancedProperties.insert("cursorMoveStyle"); m_advancedProperties.insert("dragEnabled"); m_advancedProperties.insert("enableSqueezedText"); m_advancedProperties.insert("layout");// too large risk to break things m_advancedProperties.insert("layoutDirection"); m_advancedProperties.insert("locale"); m_advancedProperties.insert("mouseTracking"); /*! @todo: reenable */ m_advancedProperties.insert("palette"); m_advancedProperties.insert("sizeAdjustPolicy"); //QAbstractScrollArea m_advancedProperties.insert("sizeIncrement"); m_advancedProperties.insert("sizePolicy"); m_advancedProperties.insert("statusTip"); m_advancedProperties.insert("toolTipDuration"); m_advancedProperties.insert("trapEnterKeyEvent"); m_advancedProperties.insert("windowModality"); m_advancedProperties.insert("autoExclusive"); // by providing this in propeditor m_advancedProperties.insert("minimumSize"); m_advancedProperties.insert("maximumSize"); m_advancedProperties.insert("clickMessage"); // for backward compatibility Kexi projects created with Qt < 4.7 m_advancedProperties.insert("showClearButton"); // for backward compatibility Kexi projects created with Qt 4 #ifndef KEXI_SHOW_UNFINISHED /*! @todo reenable */ m_advancedProperties.insert("accel"); m_advancedProperties.insert("icon"); m_advancedProperties.insert("paletteBackgroundPixmap"); m_advancedProperties.insert("pixmap"); m_advancedProperties.insert("shortcut"); // renamed from "accel" in Qt 4 m_advancedProperties.insert("windowIcon"); // renamed from "icon" in Qt 4 #endif } ~Private() { qDeleteAll(m_factories); m_factories.clear(); qDeleteAll(m_pluginsMetaData); m_pluginsMetaData.clear(); } QHash widgets() { KDbMessageGuard mg(q); (void)loadFactories(); return m_widgets; } QHash factories() { KDbMessageGuard mg(q); (void)loadFactories(); return m_factories; } bool isAdvancedProperty(const QByteArray &property) const { return m_advancedProperties.contains(property); } bool showAdvancedProperties; private: //! Performs a form widget plugins lookup. @retrun true on success. //! @todo This method generates a few warnings, maybe we want to optionally display them somewhere (via the message handler)? bool lookup() { //! @todo Allow refreshing if (m_lookupDone) { return m_lookupResult; } m_lookupDone = true; m_lookupResult = false; q->clearResult(); QStringList serviceTypes; serviceTypes << "Kexi/FormWidgets"; - const QList offers = KexiFormWidgetsPluginTrader_instance->query(serviceTypes); + QList offers = KexiFormWidgetsPluginTrader_instance->query(serviceTypes); foreach(const QPluginLoader *loader, offers) { QScopedPointer metaData(new KexiFormWidgetsPluginMetaData(*loader)); if (metaData->id().isEmpty()) { qWarning() << "No plugin ID (X-KDE-PluginInfo-Name) specified for Kexi Form Widgets plugin" << metaData->fileName() << "-- skipping!"; continue; } // check version if (metaData->majorVersion() != KFormDesigner::version()) { qWarning() << "Kexi Form Widgets plugin" << metaData->id() << "has version (X-KDE-PluginInfo-Version)" << metaData->majorVersion() << "but required version is" << KFormDesigner::version() << "-- skipping!"; continue; } // skip duplicates if (m_pluginsMetaData.contains(metaData->id())) { qWarning() << "More than one Kexi Form Widgets plugin with ID" << metaData->id() << metaData->fileName() << "-- skipping this one"; continue; } //qDebug() << "found factory:" << ptr->name(); if (!metaData->group().isEmpty() && !m_supportedFactoryGroups.contains(metaData->group())) { qDebug() << "Factory group" << metaData->group() << "for Form Widgets plugin" << metaData->id() << metaData->fileName() << "is not supported -- skipping!"; continue; } m_pluginsMetaData.insert(metaData->id(), metaData.data()); metaData.take(); } + qDeleteAll(offers); + offers.clear(); if (m_pluginsMetaData.isEmpty()) { q->m_result = KDbResult(i18n("Could not find any form widget plugins.")); m_couldNotFindAnyFormWidgetPluginsErrorDisplayed = true; return false; } m_lookupResult = true; return true; } //! Loads all factory plugins bool loadFactories() { if (m_loadFactoriesDone) { if (m_couldNotFindAnyFormWidgetPluginsErrorDisplayed) { q->clearResult(); // show the warning only once } return m_loadFactoriesResult; } m_loadFactoriesDone = true; m_loadFactoriesResult = false; if (!lookup()) { return false; } foreach (KexiFormWidgetsPluginMetaData *pluginMetaData, m_pluginsMetaData) { WidgetFactory *factory = loadFactory(pluginMetaData); if (!factory) { continue; } //collect information about classes to be hidden if (factory->hiddenClasses()) { foreach (const QByteArray &c, *factory->hiddenClasses()) { m_hiddenClasses.insert(c); } } } //now we have factories instantiated: load widgets QList loadLater; foreach (WidgetFactory *factory, m_factories) { //ONE LEVEL, FLAT INHERITANCE, but works! //if this factory inherits from something, load its witgets later //! @todo improve if (factory->inheritsFactories()) loadLater.append(factory); else loadFactoryWidgets(factory); } //load now the rest foreach (WidgetFactory* f, loadLater) { loadFactoryWidgets(f); } m_loadFactoriesResult = true; return true; } //! Loads of a single factory. @return true on success WidgetFactory *loadFactory(KexiFormWidgetsPluginMetaData *pluginMetaData) { KPluginFactory *factory = qobject_cast(pluginMetaData->instantiate()); if (!factory) { q->m_result = KDbResult(ERR_OBJECT_NOT_FOUND, xi18nc("@info", "Could not load Kexi Form Widgets plugin file \"%1\".", pluginMetaData->fileName())); q->setErrorMessage(pluginMetaData, q->result().message()); qWarning() << q->result().message(); return 0; } WidgetFactory *widgetFactory = factory->create(q); if (!widgetFactory) { q->m_result = KDbResult(ERR_CANNOT_LOAD_OBJECT, i18n("Could not open Kexi Form Widgets plugin \"%1\".") .arg(pluginMetaData->fileName())); qWarning() << q->m_result.message(); return 0; } widgetFactory->setLibrary(q); widgetFactory->setObjectName(pluginMetaData->id()); widgetFactory->setAdvancedPropertiesVisible(showAdvancedProperties); //inherit this flag from the library m_factories.insert(pluginMetaData->id().toLatin1(), widgetFactory); return widgetFactory; } //! Loads widgets for factory @a f void loadFactoryWidgets(WidgetFactory *f) { QHash widgetsForFactory(f->classes()); foreach (WidgetInfo *w, widgetsForFactory) { if (m_hiddenClasses.contains( w->className() )) continue; //this class is hidden // check if we want to inherit a widget from a different factory if (!w->parentFactoryName().isEmpty() && !w->inheritedClassName().isEmpty()) { WidgetFactory *parentFactory = m_factories.value(w->parentFactoryName().toLower()); if (!parentFactory) { qWarning() << "class" << w->className() << ": no such parent factory" << w->parentFactoryName(); continue; } WidgetInfo* inheritedClass = parentFactory->widgetInfoForClassName(w->inheritedClassName()); if (!inheritedClass) { qWarning() << "class" << w->inheritedClassName() << " - no such class to inherit in factory" << w->parentFactoryName(); continue; } //ok: inherit properties: w->setInheritedClass( inheritedClass ); if (w->iconName().isEmpty()) w->setIconName(inheritedClass->iconName()); //ok? foreach(const QByteArray& alternateName, inheritedClass->alternateClassNames()) { w->addAlternateClassName( alternateName, inheritedClass->isOverriddenClassName(alternateName)); } if (w->includeFileName().isEmpty()) w->setIncludeFileName(inheritedClass->includeFileName()); if (w->name().isEmpty()) w->setName(inheritedClass->name()); if (w->namePrefix().isEmpty()) w->setNamePrefix(inheritedClass->namePrefix()); if (w->description().isEmpty()) w->setDescription(inheritedClass->description()); } QList cnames( w->alternateClassNames() ); cnames.prepend(w->className()); foreach (const QByteArray &wname, cnames) { WidgetInfo *widgetForClass = widgetsForFactory.value(wname); if (!widgetForClass || (widgetForClass && !widgetForClass->isOverriddenClassName(wname))) { //insert a widgetinfo, if: //1) this class has no alternate class assigned yet, or //2) this class has alternate class assigned but without 'override' flag m_widgets.insert(wname, w); } } } } WidgetLibrary *q; KexiGUIMessageHandler messageHandler; //! A map which associates a class name with a Widget class QHash m_pluginsMetaData; //!< owner bool m_couldNotFindAnyFormWidgetPluginsErrorDisplayed; QSet m_supportedFactoryGroups; QHash m_factories; //!< owner QHash m_widgets; //!< owner QSet m_advancedProperties; QSet m_hiddenClasses; bool m_lookupDone; bool m_lookupResult; bool m_loadFactoriesDone; bool m_loadFactoriesResult; }; } using namespace KFormDesigner; //------------------------------------------- WidgetLibrary::WidgetLibrary(QObject *parent, const QStringList& supportedFactoryGroups) : QObject(parent) , KDbResultable() , d(new Private(this, supportedFactoryGroups)) { } WidgetLibrary::~WidgetLibrary() { delete d; } void WidgetLibrary::createWidgetActions(ActionGroup *group) { foreach (WidgetInfo *winfo, d->widgets()) { LibActionWidget *a = new LibActionWidget(group, winfo); connect(a, SIGNAL(toggled(QByteArray)), this, SIGNAL(widgetActionToggled(QByteArray))); } } void WidgetLibrary::addCustomWidgetActions(KActionCollection *col) { if (!col) return; foreach (WidgetFactory *factory, d->factories()) { factory->createCustomActions(col); } } QWidget* WidgetLibrary::createWidget(const QByteArray &classname, QWidget *parent, const char *name, Container *c, WidgetFactory::CreateWidgetOptions options) { WidgetInfo *wclass = d->widgets().value(classname); if (!wclass) return 0; QWidget *widget = wclass->factory()->createWidget(wclass->className(), parent, name, c, options); if (!widget) { //try to instantiate from inherited class if (wclass->inheritedClass()) widget = wclass->inheritedClass()->factory()->createWidget( wclass->className(), parent, name, c, options); if (!widget) return 0; } widget->setAcceptDrops(true); if (options & WidgetFactory::DesignViewMode) { FormWidgetInterface* fwiface = dynamic_cast(widget); if (fwiface) fwiface->setDesignMode(true); } emit widgetCreated(widget); return widget; } bool WidgetLibrary::createMenuActions(const QByteArray &c, QWidget *w, QMenu *menu, KFormDesigner::Container *container) { WidgetInfo *wclass = d->widgets().value(c); if (!wclass) return false; if (wclass->factory()->createMenuActions(c, w, menu, container)) { return true; } //try from inherited class if (wclass->inheritedClass()) { return wclass->inheritedClass()->factory()->createMenuActions( wclass->className(), w, menu, container); } return false; } bool WidgetLibrary::startInlineEditing(const QByteArray &classname, QWidget *w, Container *container) { WidgetInfo *wclass = d->widgets().value(classname); if (!wclass) return false; FormWidgetInterface* fwiface = dynamic_cast(w); { KFormDesigner::WidgetFactory::InlineEditorCreationArguments args(classname, w, container); if (wclass->factory()->startInlineEditing(args)) { args.container->form()->createInlineEditor(args); if (fwiface) fwiface->setEditingMode(true); return true; } } if (wclass->inheritedClass()) { //try from inherited class KFormDesigner::WidgetFactory::InlineEditorCreationArguments args(wclass->className(), w, container); if (wclass->inheritedClass()->factory()->startInlineEditing(args)) { args.container->form()->createInlineEditor(args); if (fwiface) fwiface->setEditingMode(true); return true; } } return false; } bool WidgetLibrary::previewWidget(const QByteArray &classname, QWidget *widget, Container *container) { WidgetInfo *wclass = d->widgets().value(classname); if (!wclass) return false; FormWidgetInterface* fwiface = dynamic_cast(widget); if (fwiface) fwiface->setDesignMode(false); if (wclass->factory()->previewWidget(classname, widget, container)) return true; //try from inherited class if (wclass->inheritedClass()) return wclass->inheritedClass()->factory()->previewWidget(wclass->className(), widget, container); return false; } bool WidgetLibrary::clearWidgetContent(const QByteArray &classname, QWidget *w) { WidgetInfo *wclass = d->widgets().value(classname); if (!wclass) return false; if (wclass->factory()->clearWidgetContent(classname, w)) return true; //try from inherited class if (wclass->inheritedClass()) return wclass->inheritedClass()->factory()->clearWidgetContent(wclass->className(), w); return false; } QString WidgetLibrary::displayName(const QByteArray &classname) { WidgetInfo *wi = d->widgets().value(classname); if (wi) return wi->name(); return classname; } QString WidgetLibrary::savingName(const QByteArray &classname) { QString s; WidgetInfo *wi = d->widgets().value(classname); if (wi && !wi->savingName().isEmpty()) return wi->savingName(); return classname; } QString WidgetLibrary::namePrefix(const QByteArray &classname) { WidgetInfo *wi = d->widgets().value(classname); if (wi) return wi->namePrefix(); return classname; } QString WidgetLibrary::textForWidgetName(const QByteArray &name, const QByteArray &className) { WidgetInfo *widget = d->widgets().value(className); if (!widget) return QString(); QString newName = name; newName.remove(widget->namePrefix()); newName = widget->name() + (newName.isEmpty() ? QString() : (QLatin1String(" ") + newName)); return newName; } QByteArray WidgetLibrary::classNameForAlternate(const QByteArray &classname) { if (d->widgets().value(classname)) return classname; WidgetInfo *wi = d->widgets().value(classname); if (wi) { return wi->className(); } // widget not supported return "CustomWidget"; } QString WidgetLibrary::includeFileName(const QByteArray &classname) { WidgetInfo *wi = d->widgets().value(classname); if (wi) return wi->includeFileName(); return QString(); } QString WidgetLibrary::iconName(const QByteArray &classname) { WidgetInfo *wi = d->widgets().value(classname); if (wi) return wi->iconName(); return koIconName("unknown_widget"); } bool WidgetLibrary::saveSpecialProperty(const QByteArray &classname, const QString &name, const QVariant &value, QWidget *w, QDomElement &parentNode, QDomDocument &parent) { WidgetInfo *wi = d->widgets().value(classname); if (!wi) return false; if (wi->factory()->saveSpecialProperty(classname, name, value, w, parentNode, parent)) return true; //try from inherited class if (wi->inheritedClass()) return wi->inheritedClass()->factory()->saveSpecialProperty(wi->className(), name, value, w, parentNode, parent); return false; } bool WidgetLibrary::readSpecialProperty(const QByteArray &classname, QDomElement &node, QWidget *w, ObjectTreeItem *item) { WidgetInfo *wi = d->widgets().value(classname); if (!wi) return false; if (wi->factory()->readSpecialProperty(classname, node, w, item)) return true; //try from inherited class if (wi->inheritedClass()) return wi->inheritedClass()->factory()->readSpecialProperty(wi->className(), node, w, item); return false; } void WidgetLibrary::setAdvancedPropertiesVisible(bool set) { d->showAdvancedProperties = set; } bool WidgetLibrary::advancedPropertiesVisible() const { return d->showAdvancedProperties; } bool WidgetLibrary::isPropertyVisible(const QByteArray &classname, QWidget *w, const QByteArray &property, bool multiple, bool isTopLevel) { if (isTopLevel) { // no focus policy for top-level form widget... if (!d->showAdvancedProperties && property == "focusPolicy") return false; } WidgetInfo *wi = d->widgets().value(classname); if (!wi) return false; if (!d->showAdvancedProperties && d->isAdvancedProperty(property)) { //this is advanced property, should we hide it? if (!wi->internalProperty("forceShowAdvancedProperty:" + property).toBool() && (!wi->inheritedClass() || !wi->inheritedClass()->internalProperty("forceShowAdvancedProperty:" + property).toBool())) { return false; //hide it } } if (!wi->factory()->isPropertyVisible(classname, w, property, multiple, isTopLevel)) return false; //try from inherited class if (wi->inheritedClass() && !wi->inheritedClass()->factory()->isPropertyVisible(wi->className(), w, property, multiple, isTopLevel)) return false; return true; } QList WidgetLibrary::autoSaveProperties(const QByteArray &classname) { WidgetInfo *wi = d->widgets().value(classname); if (!wi) return QList(); return wi->autoSaveProperties(); } WidgetInfo* WidgetLibrary::widgetInfoForClassName(const char* classname) { return d->widgets().value(classname); } WidgetFactory* WidgetLibrary::factoryForClassName(const char* classname) { WidgetInfo *wi = widgetInfoForClassName(classname); return wi ? wi->factory() : 0; } QString WidgetLibrary::propertyDescForName(WidgetInfo *winfo, const QByteArray& propertyName) { if (!winfo || !winfo->factory()) return QString(); QString desc(winfo->factory()->propertyDescription(propertyName)); if (!desc.isEmpty()) return desc; if (winfo->parentFactoryName().isEmpty()) return QString(); //try in parent factory, if exists WidgetFactory *parentFactory = d->factories().value(winfo->parentFactoryName()); if (!parentFactory) return QString(); return parentFactory->propertyDescription(propertyName); } QString WidgetLibrary::propertyDescForValue(WidgetInfo *winfo, const QByteArray& name) { if (!winfo->factory()) return QString(); QString desc(winfo->factory()->valueDescription(name)); if (!desc.isEmpty()) return desc; if (winfo->parentFactoryName().isEmpty()) return QString(); //try in parent factory, if exists WidgetFactory *parentFactory = d->factories().value(winfo->parentFactoryName()); if (!parentFactory) return QString(); return parentFactory->valueDescription(name); } void WidgetLibrary::setPropertyOptions(KPropertySet& set, const WidgetInfo& winfo, QWidget* w) { if (!winfo.factory()) return; winfo.factory()->setPropertyOptions(set, winfo, w); if (winfo.parentFactoryName().isEmpty()) return; WidgetFactory *parentFactory = d->factories().value(winfo.parentFactoryName()); if (!parentFactory) return; parentFactory->setPropertyOptions(set, winfo, w); } WidgetFactory* WidgetLibrary::factory(const char* factoryName) const { return d->factories().value(factoryName); } QVariant WidgetLibrary::internalProperty(const QByteArray& classname, const QByteArray& property) { WidgetInfo *wclass = d->widgets().value(classname); if (!wclass) return QString(); QVariant value(wclass->internalProperty(property)); if (value.isNull() && wclass->inheritedClass()) return wclass->inheritedClass()->internalProperty(property); return value; } WidgetFactory::CreateWidgetOption WidgetLibrary::showOrientationSelectionPopup( const QByteArray &classname, QWidget* parent, const QPoint& pos) { WidgetInfo *wclass = d->widgets().value(classname); if (!wclass) return WidgetFactory::AnyOrientation; //get custom icons and strings QIcon iconHorizontal, iconVertical; QString iconName(wclass->internalProperty("orientationSelectionPopup:horizontalIcon").toString()); if (iconName.isEmpty() && wclass->inheritedClass()) iconName = wclass->inheritedClass()->internalProperty("orientationSelectionPopup:horizontalIcon").toString(); if (!iconName.isEmpty()) iconHorizontal = QIcon::fromTheme(iconName); iconName = wclass->internalProperty("orientationSelectionPopup:verticalIcon").toString(); if (iconName.isEmpty() && wclass->inheritedClass()) iconName = wclass->inheritedClass()->internalProperty("orientationSelectionPopup:verticalIcon").toString(); if (!iconName.isEmpty()) iconVertical = QIcon::fromTheme(iconName); QString textHorizontal = wclass->internalProperty("orientationSelectionPopup:horizontalText").toString(); if (textHorizontal.isEmpty() && wclass->inheritedClass()) iconName = wclass->inheritedClass()->internalProperty("orientationSelectionPopup:horizontalText").toString(); if (textHorizontal.isEmpty()) //default textHorizontal = xi18nc("Insert Horizontal Widget", "Insert Horizontal"); QString textVertical = wclass->internalProperty("orientationSelectionPopup:verticalText").toString(); if (textVertical.isEmpty() && wclass->inheritedClass()) iconName = wclass->inheritedClass()->internalProperty("orientationSelectionPopup:verticalText").toString(); if (textVertical.isEmpty()) //default textVertical = xi18nc("Insert Vertical Widget", "Insert Vertical"); QMenu popup(parent); popup.setObjectName("orientationSelectionPopup"); popup.addSection(QIcon::fromTheme(wclass->iconName()), xi18n("Insert Widget: %1", wclass->name())); QAction* horizAction = popup.addAction(iconHorizontal, textHorizontal); QAction* vertAction = popup.addAction(iconVertical, textVertical); popup.addSeparator(); popup.addAction(koIcon("dialog-cancel"), xi18n("Cancel")); QAction *a = popup.exec(pos); if (a == horizAction) return WidgetFactory::HorizontalOrientation; else if (a == vertAction) return WidgetFactory::VerticalOrientation; return WidgetFactory::AnyOrientation; //means "cancelled" } bool WidgetLibrary::propertySetShouldBeReloadedAfterPropertyChange( const QByteArray& classname, QWidget *w, const QByteArray& property) { WidgetInfo *winfo = widgetInfoForClassName(classname); if (!winfo) return false; return winfo->factory()->propertySetShouldBeReloadedAfterPropertyChange(classname, w, property); } ObjectTreeItem* WidgetLibrary::selectableItem(ObjectTreeItem* item) { qDebug() << item->widget()->metaObject()->className(); WidgetInfo *wi = d->widgets().value(item->widget()->metaObject()->className()); if (!wi) return item; return wi->factory()->selectableItem(item); } void WidgetLibrary::setErrorMessage(KexiFormWidgetsPluginMetaData *pluginMetaData, const QString& errorMessage) { pluginMetaData->setErrorMessage(errorMessage); } diff --git a/src/kexiutils/KexiJsonTrader.cpp b/src/kexiutils/KexiJsonTrader.cpp index 49e5cb1f8..4778d95ad 100644 --- a/src/kexiutils/KexiJsonTrader.cpp +++ b/src/kexiutils/KexiJsonTrader.cpp @@ -1,155 +1,166 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis Copyright (C) 2007 David Faure Copyright (C) 2015 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KexiJsonTrader.h" #include "utils.h" #include #include #include #include #include #include #include #include class KexiJsonTrader::Private { public: Private() : pluginPathFound(false) { } QString subDir; bool pluginPathFound; QStringList pluginPaths; }; // --- KexiJsonTrader::KexiJsonTrader(const QString& subDir) : d(new Private) { Q_ASSERT(!subDir.isEmpty()); Q_ASSERT(!subDir.contains(' ')); d->subDir = subDir; } KexiJsonTrader::~KexiJsonTrader() { delete d; } //! @return true if at least one service type from @a serviceTypeNames exists in @a foundServiceTypes static bool supportsAtLeastServiceType(const QStringList &foundServiceTypes, const QStringList &serviceTypeNames) { foreach(const QString &serviceTypeName, serviceTypeNames) { if (foundServiceTypes.contains(serviceTypeName)) { return true; } } return false; } static QJsonObject metaDataObjectForPluginLoader(const QPluginLoader &pluginLoader) { return pluginLoader.metaData().value(QLatin1String("MetaData")).toObject(); } //static QJsonObject KexiJsonTrader::rootObjectForPluginLoader(const QPluginLoader &pluginLoader) { QJsonObject json = metaDataObjectForPluginLoader(pluginLoader); if (json.isEmpty()) { return QJsonObject(); } return json.value(QLatin1String("KPlugin")).toObject(); } +//! Checks loader @a loader +static bool checkLoader(QPluginLoader *loader, const QStringList &servicetypes, + const QString &mimetype) +{ + const QJsonObject pluginData = KexiJsonTrader::rootObjectForPluginLoader(*loader); + if (pluginData.isEmpty()) { + //qDebug() << dirIter.filePath() << "has no json!"; + return false; + } + const QJsonArray foundServiceTypesAray = pluginData.value(QLatin1String("ServiceTypes")).toArray(); + if (foundServiceTypesAray.isEmpty()) { + qWarning() << "No ServiceTypes defined for plugin" << loader->fileName() << "-- skipping!"; + return false; + } + QStringList foundServiceTypes = KexiUtils::convertTypesUsingMethod(foundServiceTypesAray.toVariantList()); + if (!supportsAtLeastServiceType(foundServiceTypes, servicetypes)) { + return false; + } + + if (!mimetype.isEmpty()) { + QJsonObject json = metaDataObjectForPluginLoader(*loader); + QStringList mimeTypes = json.value(QLatin1String("X-KDE-ExtraNativeMimeTypes")) + .toString().split(QLatin1Char(',')); + mimeTypes += json.value(QLatin1String("MimeType")).toString().split(QLatin1Char(';')); + mimeTypes += json.value(QLatin1String("X-KDE-NativeMimeType")).toString(); + if (! mimeTypes.contains(mimetype)) { + return false; + } + } + return true; +} + static QList findPlugins(const QString &path, const QStringList &servicetypes, const QString &mimetype) { QList list; QDirIterator dirIter(path, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks); while (dirIter.hasNext()) { dirIter.next(); if (dirIter.fileInfo().isFile()) { QPluginLoader *loader = new QPluginLoader(dirIter.filePath()); - const QJsonObject pluginData = KexiJsonTrader::rootObjectForPluginLoader(*loader); - if (pluginData.isEmpty()) { - //qDebug() << dirIter.filePath() << "has no json!"; - continue; - } - const QJsonArray foundServiceTypesAray = pluginData.value(QLatin1String("ServiceTypes")).toArray(); - if (foundServiceTypesAray.isEmpty()) { - qWarning() << "No ServiceTypes defined for plugin" << loader->fileName() << "-- skipping!"; - continue; - } - QStringList foundServiceTypes = KexiUtils::convertTypesUsingMethod(foundServiceTypesAray.toVariantList()); - if (!supportsAtLeastServiceType(foundServiceTypes, servicetypes)) { - continue; - } - - if (!mimetype.isEmpty()) { - QJsonObject json = metaDataObjectForPluginLoader(*loader); - QStringList mimeTypes = json.value(QLatin1String("X-KDE-ExtraNativeMimeTypes")) - .toString().split(QLatin1Char(',')); - mimeTypes += json.value(QLatin1String("MimeType")).toString().split(QLatin1Char(';')); - mimeTypes += json.value(QLatin1String("X-KDE-NativeMimeType")).toString(); - if (! mimeTypes.contains(mimetype)) { - continue; - } + if (checkLoader(loader, servicetypes, mimetype)) { + list.append(loader); + } else { + delete loader; } - list.append(loader); } } return list; } QList KexiJsonTrader::query(const QStringList &servicetypes, const QString &mimetype) { if (!d->pluginPathFound) { QStringList searchDirs; searchDirs += QCoreApplication::libraryPaths(); foreach(const QString &dir, searchDirs) { //qDebug() << dir; QString possiblePath = dir + QLatin1Char('/') + d->subDir; if (QDir(possiblePath).exists()) { d->pluginPaths += possiblePath; } } d->pluginPathFound = true; } QList list; foreach(const QString &path, d->pluginPaths) { list += findPlugins(path, servicetypes, mimetype); } return list; } QList KexiJsonTrader::query(const QString &servicetype, const QString &mimetype) { QStringList servicetypes; servicetypes << servicetype; return query(servicetypes, mimetype); } diff --git a/src/kexiutils/KexiJsonTrader.h b/src/kexiutils/KexiJsonTrader.h index a7d1718ac..892f6f20d 100644 --- a/src/kexiutils/KexiJsonTrader.h +++ b/src/kexiutils/KexiJsonTrader.h @@ -1,82 +1,84 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis Copyright (C) 2007 David Faure Copyright (C) 2015 Jarosław Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KEXI_JSONTRADER_H #define KEXI_JSONTRADER_H #include "kexiutils_export.h" #include #include class QPluginLoader; class QJsonObject; /** * Support class to fetch a list of relevant plugins */ class KEXIUTILS_EXPORT KexiJsonTrader { public: //! Creates instance of the trader. //! @a subDir is a name of subdirectory in which plugin files of given type are stored, e.g. "kexi". explicit KexiJsonTrader(const QString& subDir); ~KexiJsonTrader(); /** * The main function in the KexiJsonTrader class. * * It will return a list of QPluginLoader objects that match your * specifications. The only required parameter is the @a servicetypes. * The @a mimetype parameter is used to limit the possible choices * returned based on the constraints you give it. * * The keys used in the query (Type, ServiceType, Exec) are all * fields found in the .desktop files. * * @param servicetypes A list of service types like 'KMyApp/Plugin' or 'KFilePlugin'. * At least one has to be found in a plugin. * @param mimetype A mimetype constraint to limit the choices returned, QString() to * get all services of the given @p servicetypes. * * @return A list of QPluginLoader that satisfy the query * @see http://techbase.kde.org/Development/Tutorials/Services/Traders#The_KTrader_Query_Language + * + * @note Ownership of the QPluginLoader objects is transferred to the caller. */ QList query(const QStringList &servicetypes, const QString &mimetype = QString()); /** * @overload QList query(const QStringList &, const QString &); */ QList query(const QString &servicetype, const QString &mimetype = QString()); /** * @return root json object for @a pluginLoader */ static QJsonObject rootObjectForPluginLoader(const QPluginLoader &pluginLoader); private: Q_DISABLE_COPY(KexiJsonTrader) class Private; Private * const d; }; #endif