[rkward-cvs] [rkward] /: Allow to override plugins (based on the specified version), e.g. for updating an "official" plugin by way of an external pluginmap.
Thomas Friedrichsmeier
thomas.friedrichsmeier at ruhr-uni-bochum.de
Fri Dec 19 20:33:24 UTC 2014
Git commit f68a324c88b1e7e2bc9c0a7060b997110dc78758 by Thomas Friedrichsmeier.
Committed on 19/12/2014 at 20:25.
Pushed by tfry into branch 'master'.
Allow to override plugins (based on the specified version), e.g. for updating an "official" plugin by way of an external pluginmap.
- More than one plugin can have the same id
- The plugin to use will be picked after all pluginmaps are loaded.
M +1 -0 ChangeLog
M +80 -22 rkward/plugin/rkcomponentmap.cpp
M +18 -11 rkward/plugin/rkcomponentmap.h
M +1 -0 rkward/plugin/rkcomponentmeta.cpp
M +2 -0 rkward/plugin/rkcomponentmeta.h
M +4 -9 rkward/windows/rkhtmlwindow.cpp
http://commits.kde.org/rkward/f68a324c88b1e7e2bc9c0a7060b997110dc78758
diff --git a/ChangeLog b/ChangeLog
index 282d6c7..ec078bd 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,4 @@
+- Allow to override plugins from a different pluginmap (the plugin with the highest specified version is used)
- Fixed: Error when using fix() or trace(...edit=TRUE) with default settings on some systems
- When the RKWard installation has moved on disk, try to adjust stored .pluginmaps paths, accordingly
- TODO: Actually test this on Windows
diff --git a/rkward/plugin/rkcomponentmap.cpp b/rkward/plugin/rkcomponentmap.cpp
index fd70859..da2b025 100644
--- a/rkward/plugin/rkcomponentmap.cpp
+++ b/rkward/plugin/rkcomponentmap.cpp
@@ -35,8 +35,9 @@
#include "../rkglobals.h"
#include "../rkward.h"
#include "../settings/rksettingsmoduleplugins.h"
+#include "../rbackend/rksessionvars.h"
-QString RKPluginMapFile::makeFileName (const QString &filename) {
+QString RKPluginMapFile::makeFileName (const QString &filename) const {
return QDir::cleanPath (QDir (basedir).filePath (filename));
}
@@ -46,6 +47,15 @@ QString RKPluginMapFile::parseId (const QDomElement& e, XMLHelper &xml) {
return (xml.getStringAttribute (e, "namespace", "rkward", DL_WARNING) + "::" + xml.getStringAttribute (e, "id", QString (), DL_INFO));
}
+RKComponentAboutData RKPluginMapFile::getAboutData () {
+ RK_TRACE (PLUGIN);
+
+ if (about) return *about;
+ XMLHelper xml (filename);
+ QDomElement element = xml.openXMLFile (DL_ERROR);
+ about = new RKComponentAboutData (xml.getChildElement (element, "about", DL_INFO), xml); // might be empty, but not null
+ return *about;
+}
RKComponentGUIXML::RKComponentGUIXML () {
RK_TRACE (PLUGIN);
@@ -248,8 +258,8 @@ void RKComponentMap::clearAll () {
}
components.clear ();
- for (PluginMapFileMap::const_iterator it = pluginmapfiles.constBegin (); it != pluginmapfiles.constEnd (); ++it) {
- delete (it.value ());
+ for (int i = 0; i < pluginmapfiles.size (); ++i) {
+ delete (pluginmapfiles[i]);
}
pluginmapfiles.clear ();
component_attributes.clear ();
@@ -290,8 +300,29 @@ RKComponentHandle* RKComponentMap::getComponentHandle (const QString &id) {
RKComponentHandle* RKComponentMap::getComponentHandleLocal (const QString &id) {
RK_TRACE (PLUGIN);
- if (components.contains (id)) return (components[id]);
- return 0;
+ QList<RKComponentHandle*> candidates = components.values (id);
+ if (candidates.isEmpty ()) return 0;
+ if (candidates.length () == 1) return candidates.first ();
+
+ RK_DEBUG (PLUGIN, DL_INFO, "Looking for latest version of component %s, among %d candidates", qPrintable (id), candidates.size ());
+ RKComponentHandle* candidate = candidates.first ();
+ QString sufa;
+ quint32 vera = RKSessionVars::parseVersionString (candidate->getAboutData ().version, &sufa);
+ for (int i = 1; i < candidates.size (); ++i) {
+ QString sufb;
+ quint32 verb = RKSessionVars::parseVersionString (candidates[i]->getAboutData ().version, &sufb);
+ if ((verb > vera) || ((verb == vera) && (sufb > sufa))) {
+ candidate = candidates[i];
+ vera = verb;
+ sufa = sufb;
+ }
+ }
+ // purge inferior components to avoid future version lookups
+ RK_DEBUG (PLUGIN, DL_INFO, "Latest version is '%s'. Forgetting about the others.", qPrintable (candidate->getFilename ()));
+ components.remove (id);
+ components.insert (id, candidate);
+
+ return candidate;
}
//static
@@ -304,14 +335,18 @@ QString RKComponentMap::getComponentId (RKComponentHandle* by_component) {
QString RKComponentMap::getComponentIdLocal (RKComponentHandle* component) {
RK_TRACE (PLUGIN);
- for (ComponentMap::iterator it = components.begin (); it != components.end (); ++it) {
- if (it.value () == component) {
- return it.key ();
- }
- }
+ QString component_found = components.key (component, QString ());
+ RK_ASSERT (!component_found.isNull ());
+ return (component_found);
+}
- RK_ASSERT (false);
- return (QString ());
+bool RKComponentMap::isPluginMapLoaded (const QString& abs_filename) const {
+ RK_TRACE (PLUGIN);
+
+ for (int i = 0; i < pluginmapfiles.size (); ++i) {
+ if (pluginmapfiles[i]->filename == abs_filename) return true;
+ }
+ return false;
}
//static
@@ -414,7 +449,7 @@ RKPluginMapParseResult RKComponentMap::addPluginMap (const QString& plugin_map_f
RKPluginMapParseResult ret;
QString plugin_map_file_abs = QFileInfo (plugin_map_file).absoluteFilePath ();
- if (pluginmapfiles.contains (plugin_map_file_abs)) {
+ if (isPluginMapLoaded (plugin_map_file_abs)) {
RK_DEBUG (PLUGIN, DL_INFO, "Plugin map file '%s' already loaded", plugin_map_file.toLatin1().data ());
return ret;
}
@@ -432,9 +467,9 @@ RKPluginMapParseResult RKComponentMap::addPluginMap (const QString& plugin_map_f
QString prefix = QFileInfo (plugin_map_file_abs).absolutePath() + '/' + xml.getStringAttribute (document_element, "base_prefix", QString::null, DL_INFO);
QString cnamespace = xml.getStringAttribute (document_element, "namespace", "rkward", DL_INFO) + "::";
- RKPluginMapFile *pluginmap_file_desc = new RKPluginMapFile (prefix, xml.messageCatalog ());
+ RKPluginMapFile* pluginmap_file_desc = new RKPluginMapFile (QFileInfo (plugin_map_file).absoluteFilePath (), prefix, xml.messageCatalog ());
pluginmap_file_desc->id = RKPluginMapFile::parseId (document_element, xml);
- pluginmapfiles.insert (QFileInfo (plugin_map_file).absoluteFilePath (), pluginmap_file_desc);
+ pluginmapfiles.append (pluginmap_file_desc);
// step 0: check dependencies, parse about, and initialize
QDomElement dependencies = xml.getChildElement (document_element, "dependencies", DL_INFO);
@@ -461,9 +496,9 @@ RKPluginMapParseResult RKComponentMap::addPluginMap (const QString& plugin_map_f
QString map_id = xml.getStringAttribute (*it, "map", QString (), DL_ERROR);
// Try to locate the map among the already loaded files, first
QString file;
- for (PluginMapFileMap::const_iterator pmit = pluginmapfiles.constBegin (); pmit != pluginmapfiles.constEnd (); ++pmit) {
- if (pmit.value ()->id == map_id) {
- file = pmit.key ();
+ for (int i = 0; i < pluginmapfiles.size (); ++i) {
+ if (pluginmapfiles[i]->id == map_id) {
+ file = pluginmapfiles[i]->filename;
break;
}
}
@@ -506,9 +541,13 @@ RKPluginMapParseResult RKComponentMap::addPluginMap (const QString& plugin_map_f
int type = xml.getMultiChoiceAttribute ((*it), "type", "standard", 0, DL_WARNING);
QString label = xml.i18nStringAttribute ((*it), "label", i18n ("(no label)"), DL_WARNING);
+ if (local_components.contains (id)) {
+ ret.addAndPrintError (DL_WARNING, i18n ("Duplicate declaration of component id \"%1\" within pluginmap file \"%2\".", id, plugin_map_file_abs));
+ }
if (components.contains (id)) {
- ret.addAndPrintError (DL_WARNING, i18n ("RKComponentMap already contains a component with id \"%1\". Ignoring second entry.", id));
- } else if (!QFileInfo (pluginmap_file_desc->makeFileName (filename)).isReadable ()) {
+ RK_DEBUG (PLUGIN, DL_INFO, "RKComponentMap already contains a component with id \"%1\". Highest version will be picked at runtime.", qPrintable (id));
+ }
+ if (!QFileInfo (pluginmap_file_desc->makeFileName (filename)).isReadable ()) {
ret.addAndPrintError (DL_ERROR, i18n ("Specified file '%1' for component id \"%2\" does not exist or is not readable. Ignoring.", filename, id));
} else {
// create and initialize component handle
@@ -647,10 +686,29 @@ QList <RKComponentDependency> RKComponentHandle::getDependencies () {
return (ret + (*it));
}
-QString RKComponentHandle::getPluginmapFilename () {
+QString RKComponentHandle::getPluginmapFilename () const {
+ RK_TRACE (PLUGIN);
+
+ if (!plugin_map) {
+ RK_ASSERT (plugin_map);
+ return QString ();
+ }
+ return plugin_map->getFileName ();
+}
+
+RKComponentAboutData RKComponentHandle::getAboutData () {
RK_TRACE (PLUGIN);
- return RKComponentMap::getMap ()->pluginmapfiles.key (plugin_map);
+ // NOTE: In order to determine the message catalog to use, we have to open the pluginmap file...
+ XMLHelper pluginmap_xml (getPluginmapFilename ());
+ QDomElement pluginmap_doc = pluginmap_xml.openXMLFile (DL_ERROR);
+
+ XMLHelper component_xml (getFilename (), pluginmap_xml.messageCatalog ());
+ QDomElement component_doc = component_xml.openXMLFile (DL_ERROR);
+ QDomElement about = component_xml.getChildElement (component_doc, "about", DL_INFO);
+ if (!about.isNull ()) return RKComponentAboutData (about, component_xml);
+
+ return (plugin_map->getAboutData ());
}
///########################### END RKComponentHandle ###############################
diff --git a/rkward/plugin/rkcomponentmap.h b/rkward/plugin/rkcomponentmap.h
index ab2054d..108ae75 100644
--- a/rkward/plugin/rkcomponentmap.h
+++ b/rkward/plugin/rkcomponentmap.h
@@ -27,20 +27,25 @@ class XMLHelper;
/** very simple helper class to keep track of .pluginmap files */
class RKPluginMapFile {
public:
- RKPluginMapFile (const QString &basedir, const RKMessageCatalog *_catalog) { RKPluginMapFile::basedir = basedir; catalog = _catalog; };
- ~RKPluginMapFile () {};
+ RKPluginMapFile (const QString &filename, const QString &basedir, const RKMessageCatalog *_catalog) { RKPluginMapFile::filename = filename; RKPluginMapFile::basedir = basedir; catalog = _catalog; about = 0; };
+ ~RKPluginMapFile () { delete about; };
- QString getBaseDir () { return basedir; };
- QString makeFileName (const QString &filename);
- QList<RKComponentDependency> getDependencies () { return dependencies; };
+ QString getBaseDir () const { return basedir; };
+ QString getFileName () const { return filename; };
+ QString makeFileName (const QString &filename) const;
+ QList<RKComponentDependency> getDependencies () const { return dependencies; };
static QString parseId (const QDomElement &e, XMLHelper &xml);
const RKMessageCatalog *messageCatalog () const { return catalog; };
+ // Get the about data for this pluginmap.
+ RKComponentAboutData getAboutData ();
private:
friend class RKComponentMap;
QString basedir;
+ QString filename;
QString id;
QList<RKComponentDependency> dependencies;
const RKMessageCatalog *catalog;
+ RKComponentAboutData *about;
};
/** enum of different types of RKComponent */
@@ -72,7 +77,7 @@ public:
QString getLabel () { return label; };
RKComponentType getType () { return type; };
bool isPlugin ();
- QString getPluginmapFilename ();
+ QString getPluginmapFilename () const;
RKStandardComponent *invoke (RKComponent *parent_component, QWidget *parent_widget);
@@ -88,6 +93,9 @@ public:
/** Returns whether this component is accessible from the menu, somewhere (else it might be in a context) */
bool isAccessible () const { return is_accessible; };
const RKMessageCatalog *messageCatalog () const { return plugin_map->messageCatalog (); };
+/** Returns the about data for this component (likely that of the containing pluginmap). Note that in contrast to RKPluginMapFile::getAboutData (), the parsed
+ * about data is not cached, as access to this data should be infrequent. */
+ RKComponentAboutData getAboutData ();
protected:
/** The plugin map where this component was declared */
RKPluginMapFile *plugin_map;
@@ -99,7 +107,7 @@ private:
bool is_accessible;
};
-#include <qmap.h>
+#include <QMultiMap>
#include <QDomDocument>
#include <kxmlguiclient.h>
@@ -207,13 +215,13 @@ public:
static bool invokeComponent (const QString &component_id, const QStringList &serialized_settings, ComponentInvocationMode submit_mode = ManualSubmit, QString *message=0, RCommandChain *in_chain = 0);
/** @returns a list of all currently registered component ids */
QStringList allComponentIds () { return components.keys(); };
- bool isPluginMapLoaded (const QString& abs_filename) { return pluginmapfiles.contains (abs_filename); };
+ bool isPluginMapLoaded (const QString& abs_filename) const;
public slots:
/** Slot called, when a menu-item for a component is selected. Responsible for creating the GUI. */
void activateComponent ();
private:
/** typedef for easy reference to iterator */
- typedef QMap<QString, RKComponentHandle*> ComponentMap;
+ typedef QMultiMap<QString, RKComponentHandle*> ComponentMap;
/** the actual map of components */
ComponentMap components;
@@ -224,8 +232,7 @@ private:
typedef QMap<QString, RKContextMap*> RKComponentContextMap;
RKComponentContextMap contexts;
- typedef QMap<QString, RKPluginMapFile*> PluginMapFileMap;
- PluginMapFileMap pluginmapfiles;
+ QList<RKPluginMapFile*> pluginmapfiles;
static RKComponentMap *component_map;
friend class RKComponentHandle;
diff --git a/rkward/plugin/rkcomponentmeta.cpp b/rkward/plugin/rkcomponentmeta.cpp
index a39cb15..90b0f46 100644
--- a/rkward/plugin/rkcomponentmeta.cpp
+++ b/rkward/plugin/rkcomponentmeta.cpp
@@ -36,6 +36,7 @@ RKComponentAboutData::RKComponentAboutData (const QDomElement& e, XMLHelper &xml
RK_TRACE (PLUGIN);
if (e.isNull ()) return;
+ valid = true;
name = xml.i18nStringAttribute (e, "name", QString (), DL_INFO);
version = xml.getStringAttribute (e, "version", QString (), DL_INFO);
releasedate = xml.getStringAttribute (e, "releasedate", QString (), DL_INFO);
diff --git a/rkward/plugin/rkcomponentmeta.h b/rkward/plugin/rkcomponentmeta.h
index fc13adc..991560d 100644
--- a/rkward/plugin/rkcomponentmeta.h
+++ b/rkward/plugin/rkcomponentmeta.h
@@ -52,6 +52,7 @@ struct RKComponentAuthor {
class RKComponentAboutData {
public:
+ RKComponentAboutData () { valid = false; };
RKComponentAboutData (const QDomElement &e, XMLHelper &xml);
~RKComponentAboutData ();
QString toHtml () const;
@@ -67,6 +68,7 @@ public:
QList<RKComponentAuthor> authors;
QString translator_names;
QString translator_emails;
+ bool valid;
};
#endif
diff --git a/rkward/windows/rkhtmlwindow.cpp b/rkward/windows/rkhtmlwindow.cpp
index c85d3c1..cea48e0 100644
--- a/rkward/windows/rkhtmlwindow.cpp
+++ b/rkward/windows/rkhtmlwindow.cpp
@@ -645,18 +645,13 @@ bool RKHTMLWindow::renderRKHelp (const KUrl &url) {
}
// "about" section
+ RKComponentAboutData about;
if (for_component) {
- element = component_xml.getChildElement (component_doc_element, "about", DL_INFO);
- if (element.isNull ()) {
- XMLHelper pluginmap_helper (chandle->getPluginmapFilename (), chandle->messageCatalog ());
- element = pluginmap_helper.openXMLFile (DL_ERROR);
- element = pluginmap_helper.getChildElement (element, "about", DL_INFO);
- }
+ about = chandle->getAboutData ();
} else {
- element = help_xml.getChildElement (help_doc_element, "about", DL_INFO);
+ about = RKComponentAboutData (help_xml.getChildElement (help_doc_element, "about", DL_INFO), help_xml);
}
- if (!element.isNull ()) {
- RKComponentAboutData about (element, for_component ? component_xml : help_xml);
+ if (about.valid) {
writeHTML (startSection ("about", i18n ("About"), QString (), &anchors, &anchornames));
writeHTML (about.toHtml ());
}
More information about the rkward-tracker
mailing list