[rkward-cvs] SF.net SVN: rkward-code:[4504] trunk/rkward/rkward

tfry at users.sf.net tfry at users.sf.net
Fri Jan 25 16:37:32 UTC 2013


Revision: 4504
          http://sourceforge.net/p/rkward/code/4504
Author:   tfry
Date:     2013-01-25 16:37:31 +0000 (Fri, 25 Jan 2013)
Log Message:
-----------
Better keep track of errors when loading plugin maps, and report broken / quirky maps to the user.
Changed pluginmap selection widget to allow checking / unchecking pluginmaps.
This will be extended to show more info (about data and dependencies).

Modified Paths:
--------------
    trunk/rkward/rkward/main.cpp
    trunk/rkward/rkward/plugin/rkcomponentmap.cpp
    trunk/rkward/rkward/plugin/rkcomponentmap.h
    trunk/rkward/rkward/plugin/rkcomponentmeta.cpp
    trunk/rkward/rkward/rkward.cpp
    trunk/rkward/rkward/rkward.h
    trunk/rkward/rkward/settings/rksettingsmodulegeneral.cpp
    trunk/rkward/rkward/settings/rksettingsmodulegeneral.h
    trunk/rkward/rkward/settings/rksettingsmoduleplugins.cpp
    trunk/rkward/rkward/settings/rksettingsmoduleplugins.h

Modified: trunk/rkward/rkward/main.cpp
===================================================================
--- trunk/rkward/rkward/main.cpp	2013-01-25 16:32:55 UTC (rev 4503)
+++ trunk/rkward/rkward/main.cpp	2013-01-25 16:37:31 UTC (rev 4504)
@@ -124,7 +124,7 @@
 	options.add ("debugger <command>", ki18n ("Debugger (enclose any debugger arguments in single quotes ('') together with the command)"), "");
 	options.add ("+[File]", ki18n ("R workspace file to open"), 0);
 
-	KAboutData aboutData("rkward", QByteArray (), ki18n ("RKWard"), RKWARD_VERSION, ki18n ("Frontend to the R statistics language"), KAboutData::License_GPL, ki18n ("(c) 2002, 2004 - 2012"), KLocalizedString (), "http://rkward.sf.net", "rkward-devel at lists.sourceforge.net");
+	KAboutData aboutData("rkward", QByteArray (), ki18n ("RKWard"), RKWARD_VERSION, ki18n ("Frontend to the R statistics language"), KAboutData::License_GPL, ki18n ("(c) 2002, 2004 - 2013"), KLocalizedString (), "http://rkward.sf.net", "rkward-devel at lists.sourceforge.net");
 	aboutData.addAuthor (ki18n ("%1").subs ("Thomas Friedrichsmeier"), ki18n ("Project leader / main developer"));
 	aboutData.addAuthor (ki18n ("%1").subs ("Pierre Ecochard"), ki18n ("C++ developer between 2004 and 2007"));
 	aboutData.addAuthor (ki18n ("%1").subs ("Prasenjit Kapat"), ki18n ("Many plugins, suggestions, plot history feature"));

Modified: trunk/rkward/rkward/plugin/rkcomponentmap.cpp
===================================================================
--- trunk/rkward/rkward/plugin/rkcomponentmap.cpp	2013-01-25 16:32:55 UTC (rev 4503)
+++ trunk/rkward/rkward/plugin/rkcomponentmap.cpp	2013-01-25 16:37:31 UTC (rev 4504)
@@ -2,7 +2,7 @@
                           rkcomponentmap.cpp  -  description
                              -------------------
     begin                : Thu May 12 2005
-    copyright            : (C) 2005, 2006, 2007, 2009, 2010 by Thomas Friedrichsmeier
+    copyright            : (C) 2005-2013 by Thomas Friedrichsmeier
     email                : tfry at users.sourceforge.net
  ***************************************************************************/
 
@@ -21,6 +21,7 @@
 #include <qdir.h>
 #include <QTime>
 #include <QObjectCleanupHandler>
+#include <QSet>
 
 #include <klocale.h>
 #include <kactioncollection.h>
@@ -316,19 +317,26 @@
 	return true;
 }
 
-int RKComponentMap::addPluginMap (const QString& plugin_map_file) {
+void RKPluginMapParseResult::addAndPrintError (int level, const QString message) {
+	detailed_problems.append (message);
+	RK_DEBUG (PLUGIN, level, qPrintable (message));
+}
+
+RKPluginMapParseResult RKComponentMap::addPluginMap (const QString& plugin_map_file) {
 	RK_TRACE (PLUGIN);
 
 	return getMap()->addPluginMapLocal (plugin_map_file);
 }
 
-int RKComponentMap::addPluginMapLocal (const QString& plugin_map_file) {
+RKPluginMapParseResult RKComponentMap::addPluginMapLocal (const QString& plugin_map_file) {
 	RK_TRACE (PLUGIN);
 
+	RKPluginMapParseResult ret;
+
 	QString plugin_map_file_abs = QFileInfo (plugin_map_file).absoluteFilePath ();
 	if (pluginmapfiles.contains (plugin_map_file_abs)) {
 		RK_DEBUG (PLUGIN, DL_INFO, "Plugin map file '%s' already loaded", plugin_map_file.toLatin1().data ());
-		return 0;
+		return ret;
 	}
 
 	XMLHelper* xml = XMLHelper::getStaticHelper ();
@@ -336,7 +344,10 @@
 	XMLChildList list;
 
 	QDomElement document_element = xml->openXMLFile (plugin_map_file_abs, DL_ERROR);
-	if (xml->highestError () >= DL_ERROR) return (0);
+	if (xml->highestError () >= DL_ERROR) {
+		ret.addAndPrintError (DL_ERROR, i18n ("Could not open plugin map file %1. (Is not readble, or failed to parse)", plugin_map_file_abs));
+		return ret;
+	}
 
 	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) + "::";
@@ -348,16 +359,13 @@
 	QDomElement dependencies = xml->getChildElement (document_element, "dependencies", DL_INFO);
 	if (!dependencies.isNull ()) {
 		if (!RKComponentDependency::isRKWardVersionCompatible (dependencies)) {
-			RK_DEBUG (PLUGIN, DL_INFO, "Skipping plugin map file '%s': Not compatible with this version of RKWard", qPrintable (plugin_map_file_abs));
-			return 0;
+			ret.addAndPrintError (DL_WARNING, i18n ("Skipping plugin map file '%1': Not compatible with this version of RKWard", plugin_map_file_abs));
+			return ret;
 		}
 		pluginmap_file_desc->dependencies = RKComponentDependency::parseDependencies (dependencies);
 	}
-	QDomElement about = xml->getChildElement (document_element, "about", DL_INFO);
-	if (!about.isNull ()) pluginmap_file_desc->about = new RKComponentAboutData (about);
 
 	// step 1: include required files
-	int counter = 0;
 	QStringList includelist;
 	list = xml->getChildElements (document_element, "require", DL_INFO);
 	for (XMLChildList::const_iterator it=list.constBegin (); it != list.constEnd (); ++it) {
@@ -365,16 +373,20 @@
 		if (QFileInfo (file).isReadable ()) {
 			includelist.append (file);
 		} else {
-			RK_DEBUG (PLUGIN, DL_ERROR, "Specified required file '%s' does not exist or is not readable. Ignoring.", file.toLatin1 ().data ());
+			ret.addAndPrintError (DL_ERROR, i18n ("Specified required file '%1' does not exist or is not readable. Ignoring.", file));
 		}
 	}
 	for (QStringList::const_iterator it = includelist.constBegin (); it != includelist.constEnd (); ++it) {
-		counter += addPluginMapLocal (*it);
+		ret.add (addPluginMapLocal (*it));
 	}
 
 	// step 2: create (list of) components
 	element = xml->getChildElement (document_element, "components", DL_INFO);
 	list = xml->getChildElements (element, "component", DL_INFO);
+	// Plugins that depend on a specific version of RKWard can be specified in several alternative version.
+	// It is not an error, unless *none* of the specified alternatives can be loaded.
+	QSet<QString> local_components;
+	QSet<QString> depfailed_local_components;
 
 	for (XMLChildList::const_iterator it=list.begin (); it != list.end (); ++it) {
 		QString id = cnamespace + xml->getStringAttribute((*it), "id", QString::null, DL_WARNING);
@@ -383,7 +395,8 @@
 		QDomElement cdependencies = xml->getChildElement (*it, "dependencies", DL_INFO);
 		if (!cdependencies.isNull ()) {
 			if (!RKComponentDependency::isRKWardVersionCompatible (cdependencies)) {
-				RK_DEBUG (PLUGIN, DL_INFO, "Skipping component '%s': Not compatible with this version of RKWard", qPrintable (id));
+				RK_DEBUG (PLUGIN, DL_INFO, "Skipping component '%1': Not compatible with this version of RKWard", qPrintable (id));
+				depfailed_local_components.insert (id);
 				continue;
 			}
 		}
@@ -393,9 +406,9 @@
 		QString label = xml->getStringAttribute ((*it), "label", i18n ("(no label)"), DL_WARNING);
 
 		if (components.contains (id)) {
-			RK_DEBUG (PLUGIN, DL_WARNING, "RKComponentMap already contains a component with id \"%s\". Ignoring second entry.", id.toLatin1 ().data ());
+			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_ERROR, "Specified file '%s' for component id \"%s\" does not exist or is not readable. Ignoring.", filename.toLatin1 ().data (), id.toLatin1 ().data ());
+			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
 			RKComponentHandle *handle = new RKComponentHandle (pluginmap_file_desc, filename, label, (RKComponentType) type);
@@ -405,12 +418,18 @@
 			}
 			if (!cdependencies.isNull ()) handle->addDependencies (RKComponentDependency::parseDependencies (cdependencies));
 			components.insert (id, handle);
+			local_components.insert (id);
 		}
 	}
 
+	foreach (const QString &id, depfailed_local_components) {
+		if (local_components.contains (id)) continue;
+		ret.addAndPrintError (DL_ERROR, i18n ("Component '%1' is not available in a version compatible with this version of RKWard", id));
+	}
+
 	// step 3: create / insert into menus
 	QDomElement xmlgui_menubar_element = xml->getChildElement (gui_xml.documentElement (), "MenuBar", DL_ERROR);
-	counter += createMenus (xmlgui_menubar_element, xml->getChildElement (document_element, "hierarchy", DL_INFO), cnamespace);
+	ret.valid_plugins += createMenus (xmlgui_menubar_element, xml->getChildElement (document_element, "hierarchy", DL_INFO), cnamespace);
 
 	// step 4: create and register contexts
 	list = xml->getChildElements (document_element, "context", DL_INFO);
@@ -422,12 +441,12 @@
 			context = new RKContextMap (id);
 			contexts.insert (id, context);
 		}
-		counter += context->create (*it, cnamespace);
+		ret.valid_plugins += context->create (*it, cnamespace);
 	}
 
-	setXMLGUIBuildDocument (gui_xml);
+	setXMLGUIBuildDocument (gui_xml);		// TODO: Should be called only once, not for each pluginmap!
 	actionCollection ()->readSettings ();
-	return counter;
+	return ret;
 }
 
 void RKComponentMap::addedEntry (const QString &id, RKComponentHandle *handle) {

Modified: trunk/rkward/rkward/plugin/rkcomponentmap.h
===================================================================
--- trunk/rkward/rkward/plugin/rkcomponentmap.h	2013-01-25 16:32:55 UTC (rev 4503)
+++ trunk/rkward/rkward/plugin/rkcomponentmap.h	2013-01-25 16:37:31 UTC (rev 4504)
@@ -2,7 +2,7 @@
                           rkcomponentmap.h  -  description
                              -------------------
     begin                : Thu May 12 2005
-    copyright            : (C) 2005, 2006, 2007, 2009, 2010 by Thomas Friedrichsmeier
+    copyright            : (C) 2005-2013 by Thomas Friedrichsmeier
     email                : tfry at users.sourceforge.net
  ***************************************************************************/
 
@@ -25,7 +25,7 @@
 /** very simple helper class to keep track of .pluginmap files */
 class RKPluginMapFile {
 public:
-	RKPluginMapFile (const QString &basedir) : about (0) { RKPluginMapFile::basedir = basedir; };
+	RKPluginMapFile (const QString &basedir) { RKPluginMapFile::basedir = basedir; };
 	~RKPluginMapFile () {};
 
 	QString getBaseDir () { return basedir; };
@@ -34,7 +34,6 @@
 private:
 friend class RKComponentMap;
 	QString basedir;
-	RKComponentAboutData *about;
 	QList<RKComponentDependency> dependencies;
 };
 
@@ -147,6 +146,18 @@
 
 class RKContextMap;
 
+class RKPluginMapParseResult {
+public:
+	RKPluginMapParseResult () : valid_plugins (0) {};
+	void add (const RKPluginMapParseResult &other) {
+		detailed_problems.append (other.detailed_problems);
+		valid_plugins += other.valid_plugins;
+	};
+	void addAndPrintError (int level, const QString message);
+	QStringList detailed_problems;
+	int valid_plugins;
+};
+
 /** This class (only a single instance should ever be needed) keeps a list of named components, which can be made accessible via the menu-structure
 or included in other plugins. What this class does is rather simple: It basically maps a two piece name (namespace, component name) to a short description of the component (RKComponentHandle). The most important part of that description is the filename where a more elaborate definition of
 the component can be retrieved.
@@ -162,8 +173,8 @@
 	~RKComponentMap ();
 
 /** adds all Plugins / components in a .pluginmap-file. Also takes care of creating the menu-items, etc.
- at returns number of plugins (i.e. stand-alone components/menu-entries) added successfully */
-	static int addPluginMap (const QString& plugin_map_file);
+ at returns status info of number of plugins (i.e. stand-alone components/menu-entries) added successfully / failed */
+	static RKPluginMapParseResult addPluginMap (const QString& plugin_map_file);
 
 /** clears out (and deletes) all components / plugins */
 	static void clearAll ();
@@ -187,6 +198,7 @@
 	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); };
 private:
 /** typedef for easy reference to iterator */
 	typedef QMap<QString, RKComponentHandle*> ComponentMap;
@@ -196,7 +208,7 @@
 	RKComponentHandle* getComponentHandleLocal (const QString &id);
 	QString getComponentIdLocal (RKComponentHandle* component);
 	RKContextMap *getContextLocal (const QString &id);
-	int addPluginMapLocal (const QString& plugin_map_file);
+	RKPluginMapParseResult addPluginMapLocal (const QString& plugin_map_file);
 
 	void clearLocal ();
 

Modified: trunk/rkward/rkward/plugin/rkcomponentmeta.cpp
===================================================================
--- trunk/rkward/rkward/plugin/rkcomponentmeta.cpp	2013-01-25 16:32:55 UTC (rev 4503)
+++ trunk/rkward/rkward/plugin/rkcomponentmeta.cpp	2013-01-25 16:37:31 UTC (rev 4504)
@@ -113,8 +113,10 @@
 	dep.type = RKComponentDependency::RKWardVersion;
 	dep.min_version = 0;
 	dep.max_version = 0xFFFFFFFF;
-	if (e.hasAttribute (rkward_min_version_tag)) dep.min_version = RKSessionVars::parseVersionString (e.attribute (rkward_min_version_tag), 0);
-	if (e.hasAttribute (rkward_max_version_tag)) dep.max_version = RKSessionVars::parseVersionString (e.attribute (rkward_max_version_tag), 0);
+	// Although we ignore it, here, RKWard dependencies may come with a non-numeric suffix
+	QString suffix_dummy;
+	if (e.hasAttribute (rkward_min_version_tag)) dep.min_version = RKSessionVars::parseVersionString (e.attribute (rkward_min_version_tag), &suffix_dummy);
+	if (e.hasAttribute (rkward_max_version_tag)) dep.max_version = RKSessionVars::parseVersionString (e.attribute (rkward_max_version_tag), &suffix_dummy);
 	if ((dep.min_version > 0) || (dep.max_version < 0xFFFFFFFF)) ret.append (dep);
 
 	return ret;

Modified: trunk/rkward/rkward/rkward.cpp
===================================================================
--- trunk/rkward/rkward/rkward.cpp	2013-01-25 16:32:55 UTC (rev 4503)
+++ trunk/rkward/rkward/rkward.cpp	2013-01-25 16:37:31 UTC (rev 4504)
@@ -2,7 +2,7 @@
                           rkward.cpp  -  description
                              -------------------
     begin                : Tue Oct 29 20:06:08 CET 2002
-    copyright            : (C) 2002, 2005, 2006, 2007, 2008, 2009, 2011 by Thomas Friedrichsmeier 
+    copyright            : (C) 2002-2013 by Thomas Friedrichsmeier 
     email                : tfry at users.sourceforge.net
  ***************************************************************************/
 
@@ -266,25 +266,54 @@
 	setCaption (QString ());	// our version of setCaption takes care of creating a correct caption, so we do not need to provide it here
 }
 
-void RKWardMainWindow::initPlugins () {
+void RKWardMainWindow::initPlugins (const QStringList &automatically_added) {
 	RK_TRACE (APP);
 	slotSetStatusBarText(i18n("Setting up plugins..."));
-	
+
+	QStringList all_maps = RKSettingsModulePlugins::pluginMaps ();
+	if (all_maps.isEmpty()) {
+		KMessageBox::information (0, i18n ("Plugins are needed: you may manage these through \"Settings->Configure RKWard\".\n"), i18n ("No active plugin maps"));
+		return;
+	}
+
 	factory ()->removeClient (RKComponentMap::getMap ());
 	RKComponentMap::clearAll ();
 
-	QStringList list = RKSettingsModulePlugins::pluginMaps ();
-	int counter = 0;
-	for (QStringList::const_iterator it = RKSettingsModulePlugins::pluginMaps ().begin (); it != RKSettingsModulePlugins::pluginMaps ().end (); ++it) {
-		counter += RKComponentMap::addPluginMap ((*it));
+	QStringList completely_broken_maps;
+	QStringList completely_broken_maps_details;
+	QStringList somewhat_broken_maps;
+	QStringList somewhat_broken_maps_details;
+	for (int i = 0; i < all_maps.size (); ++i) {
+		const QString &map = all_maps[i];
+		RKPluginMapParseResult result = RKComponentMap::addPluginMap (map);
+		if (!result.valid_plugins) {
+			RKSettingsModulePlugins::markPluginMapAsBroken (map);
+			completely_broken_maps.append (map);
+			completely_broken_maps_details.append (result.detailed_problems);
+		} else if (!result.detailed_problems.isEmpty ()) {
+			if (RKSettingsModulePlugins::markPluginMapAsQuirky (map)) {
+				somewhat_broken_maps.append (map);
+				somewhat_broken_maps_details.append (result.detailed_problems);
+			}
+		}
 	}
 
-	if (counter < 1) {
-		KMessageBox::information (0, i18n ("Plugins are needed: you may manage these through \"Settings->Configure RKWard\".\n"), i18n ("No (valid) plugins found"));
+	factory ()->addClient (RKComponentMap::getMap ());
+
+	if (!automatically_added.isEmpty ()) {
+		// NOTE: When plugins are added from R, these must be fully initialized *before* showing any dialog, which is modal, i.e. has an event loop. Otherwise, subsequent calls e.g. to rk.call.plugin() could sneak in front of this.
+		// This is the reason for handling notification about automatically_added plugins, here.
+		KMessageBox::informationList (RKWardMainWindow::getMain (), i18n ("New RKWard plugin packs (listed below) have been found, and have been activated, automatically. To de-activate selected plugin packs, use Settings->Configure RKWard->Plugins."), automatically_added, i18n ("New plugins found"), "new_plugins_found");
 	}
+	if (!completely_broken_maps.isEmpty ()) {
+		QString maplist = "<ul><li>" + completely_broken_maps.join ("</li>\n<li>") + "</li></ul>";
+		KMessageBox::detailedError (0, QString ("<p>%1</p><p>%2</p>").arg (i18n ("The following RKWard pluginmap files could not be loaded, and have been disabled. This could be because they are broken, not compatible with this version of RKWard, or not meant for direct loading (see the 'Details' for more information). They have been disabled.")).arg (maplist), completely_broken_maps_details.join ("\n"), i18n ("Failed to load some plugin maps"));
+	}
+	if (!somewhat_broken_maps.isEmpty ()) {
+		QString maplist = "<ul><li>" + somewhat_broken_maps.join ("</li>\n<li>") + "</li></ul>";
+		KMessageBox::detailedError (0, QString ("<p>%1</p><p>%2</p><p>%3</p>").arg (i18n ("Some errors were encountered while loading the following RKWard pluginmap files. This could be because individual plugins are broken or not compatible with this version of RKWard (see the 'Details' for more information). Other plugins were loaded, successfully, however.")).arg (maplist).arg (i18n ("Note: You will not be warned about these pluginmap files again, until you upgrade RKWard, or remove and re-add them in Settings->Configure RKWard->Plugins.")), somewhat_broken_maps_details.join ("\n"), i18n ("Failed to load some plugin maps"));
+	}
 
-	factory ()->addClient (RKComponentMap::getMap ());
-
 	slotSetStatusReady ();
 }
 

Modified: trunk/rkward/rkward/rkward.h
===================================================================
--- trunk/rkward/rkward/rkward.h	2013-01-25 16:32:55 UTC (rev 4503)
+++ trunk/rkward/rkward/rkward.h	2013-01-25 16:37:31 UTC (rev 4504)
@@ -1,19 +1,19 @@
 /***************************************************************************
-			rkward.h  -  description
-			-------------------
-begin                : Tue Oct 29 20:06:08 CET 2002 
-copyright            : (C) 2002, 2005, 2006, 2007, 2008, 2009, 2010, 2011 by Thomas Friedrichsmeier 
-email                : tfry at users.sourceforge.net
-***************************************************************************/
+                          rkward.h  -  description
+                             -------------------
+    begin                : Tue Oct 29 20:06:08 CET 2002
+    copyright            : (C) 2002-2013 by Thomas Friedrichsmeier 
+    email                : tfry at users.sourceforge.net
+ ***************************************************************************/
 
 /***************************************************************************
-*                                                                         *
-*   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 of the License, or     *
-*   (at your option) any later version.                                   *
-*                                                                         *
-***************************************************************************/
+ *                                                                         *
+ *   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 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
 
 #ifndef RKWARD_H
 #define RKWARD_H
@@ -179,7 +179,7 @@
 	friend class RKSettings;
 
 	/** Finds plugins and inserts them into the menu-structure */
-	void initPlugins ();
+	void initPlugins (const QStringList &automatically_added = QStringList ());
 
 	RKWardStartupOptions *startup_options;
 

Modified: trunk/rkward/rkward/settings/rksettingsmodulegeneral.cpp
===================================================================
--- trunk/rkward/rkward/settings/rksettingsmodulegeneral.cpp	2013-01-25 16:32:55 UTC (rev 4503)
+++ trunk/rkward/rkward/settings/rksettingsmodulegeneral.cpp	2013-01-25 16:37:31 UTC (rev 4504)
@@ -2,7 +2,7 @@
                           rksettingsmodulegeneral  -  description
                              -------------------
     begin                : Fri Jul 30 2004
-    copyright            : (C) 2004, 2007, 2008, 2011, 2012 by Thomas Friedrichsmeier
+    copyright            : (C) 2004-2013 by Thomas Friedrichsmeier
     email                : tfry at users.sourceforge.net
  ***************************************************************************/
 
@@ -35,6 +35,7 @@
 #include "../misc/rkspinbox.h"
 #include "../misc/rkcommonfunctions.h"
 #include "../rkglobals.h"
+#include "../version.h"
 #include "../debug.h"
 
 // static members
@@ -50,6 +51,7 @@
 bool RKSettingsModuleGeneral::config_exists;
 RKSettingsModuleGeneral::InitialDirectory RKSettingsModuleGeneral::initial_dir;
 QString RKSettingsModuleGeneral::initial_dir_specification;
+bool RKSettingsModuleGeneral::rkward_version_changed;
 
 RKSettingsModuleGeneral::RKSettingsModuleGeneral (RKSettings *gui, QWidget *parent) : RKSettingsModule (gui, parent) {
 	RK_TRACE (SETTINGS);
@@ -230,6 +232,7 @@
 
 	cg = config->group ("Internal");
 	cg.writeEntry ("config file version", (int) RKWardConfig_Latest);
+	cg.writeEntry ("previous runtime version", QString (RKWARD_VERSION));
 }
 
 void RKSettingsModuleGeneral::loadSettings (KConfig *config) {
@@ -259,6 +262,7 @@
 
 	cg = config->group ("Internal");
 	stored_config_version = (RKWardConfigVersion) cg.readEntry ("config file version", (int) RKWardConfig_Pre0_5_7);
+	rkward_version_changed = (cg.readEntry ("previous runtime version", QString ()) != RKWARD_VERSION);
 }
 
 QString RKSettingsModuleGeneral::getSavedWorkplace (KConfig *config) {

Modified: trunk/rkward/rkward/settings/rksettingsmodulegeneral.h
===================================================================
--- trunk/rkward/rkward/settings/rksettingsmodulegeneral.h	2013-01-25 16:32:55 UTC (rev 4503)
+++ trunk/rkward/rkward/settings/rksettingsmodulegeneral.h	2013-01-25 16:37:31 UTC (rev 4504)
@@ -2,7 +2,7 @@
                           rksettingsmodulegeneral  -  description
                              -------------------
     begin                : Fri Jul 30 2004
-    copyright            : (C) 2004, 2007, 2008, 2011, 2012 by Thomas Friedrichsmeier
+    copyright            : (C) 2004-2013 by Thomas Friedrichsmeier
     email                : tfry at users.sourceforge.net
  ***************************************************************************/
 
@@ -83,6 +83,7 @@
 	enum RKWardConfigVersion {
 		RKWardConfig_Pre0_5_7,
 		RKWardConfig_0_5_7,
+		RKWardConfig_0_6_1,
 		RKWardConfig_Next,		/**< add new configuration versions above / before this entry */
 		RKWardConfig_Latest = RKWardConfig_Next - 1
 	};
@@ -92,6 +93,8 @@
 	static RKWardConfigVersion storedConfigVersion () { return stored_config_version; };
 	/** Did a config file already exist? */
 	static bool anyExistingConfig () { return config_exists; };
+	/** Returns true, if the runtime version of RKWard has changed since the previous session. */
+	static bool rkwardVersionChanged () { return rkward_version_changed; };
 public slots:
 	void settingChanged ();
 private:
@@ -118,6 +121,7 @@
 	static bool config_exists;
 	static InitialDirectory initial_dir;
 	static QString initial_dir_specification;
+	static bool rkward_version_changed;
 };
 
 #endif

Modified: trunk/rkward/rkward/settings/rksettingsmoduleplugins.cpp
===================================================================
--- trunk/rkward/rkward/settings/rksettingsmoduleplugins.cpp	2013-01-25 16:32:55 UTC (rev 4503)
+++ trunk/rkward/rkward/settings/rksettingsmoduleplugins.cpp	2013-01-25 16:37:31 UTC (rev 4504)
@@ -2,7 +2,7 @@
                           rksettingsmoduleplugins  -  description
                              -------------------
     begin                : Wed Jul 28 2004
-    copyright            : (C) 2004, 2006, 2007, 2010, 2012 by Thomas Friedrichsmeier
+    copyright            : (C) 2004-2013 by Thomas Friedrichsmeier
     email                : tfry at users.sourceforge.net
  ***************************************************************************/
 
@@ -40,13 +40,13 @@
 #include "../misc/multistringselector.h"
 #include "../misc/rkcommonfunctions.h"
 #include "../misc/rkspinbox.h"
+#include "../plugin/rkcomponentmap.h"
 #include "rksettingsmodulegeneral.h"
 
 #include "../debug.h"
 
 // static members
-QStringList RKSettingsModulePlugins::plugin_maps;
-QStringList RKSettingsModulePlugins::known_plugin_maps;
+QList<RKSettingsModulePlugins::PluginMapStoredInfo> RKSettingsModulePlugins::known_plugin_maps;
 RKSettingsModulePlugins::PluginPrefs RKSettingsModulePlugins::interface_pref;
 bool RKSettingsModulePlugins::show_code;
 int RKSettingsModulePlugins::code_size;
@@ -107,13 +107,14 @@
 	main_vbox->addSpacing (2*RKGlobals::spacingHint ());
 
 
-	map_choser = new MultiStringSelector (i18n ("Select .pluginmap file(s)"), this);
-	map_choser->setValues (plugin_maps);
-	connect (map_choser, SIGNAL (getNewStrings (QStringList*)), this, SLOT (browseRequest (QStringList*)));
+	map_choser = new RKMultiStringSelectorV2 (i18n ("Select .pluginmap file(s)"), this);
+	map_model = new RKSettingsModulePluginsModel (this);
+	map_model->init (known_plugin_maps);
+	map_choser->setModel (map_model, 1);
+	connect (map_choser, SIGNAL (insertNewStrings(int)), map_model, SLOT (insertNewStrings(int)));
+	connect (map_choser, SIGNAL (swapRows(int,int)), map_model, SLOT (swapRows(int,int)));
 	connect (map_choser, SIGNAL (listChanged ()), this, SLOT (settingChanged ()));
 	main_vbox->addWidget (map_choser);
-
-	main_vbox->addStretch ();
 }
 
 RKSettingsModulePlugins::~RKSettingsModulePlugins() {
@@ -125,12 +126,6 @@
 	change ();
 }
 
-void RKSettingsModulePlugins::browseRequest (QStringList* strings) {
-	RK_TRACE (SETTINGS);
-
-	(*strings) = KFileDialog::getOpenFileNames (RKCommonFunctions::getRKWardDataDir (), "*.pluginmap", this, i18n ("Select .pluginmap-file"));
-}
-
 QString RKSettingsModulePlugins::caption () {
 	RK_TRACE (SETTINGS);
 	return (i18n ("Plugins"));
@@ -144,13 +139,16 @@
 void RKSettingsModulePlugins::applyChanges () {
 	RK_TRACE (SETTINGS);
 
-	plugin_maps = map_choser->getValues ();
+	known_plugin_maps = map_model->pluginMaps ();
 	interface_pref = static_cast<PluginPrefs> (button_group->checkedId ());
 	show_code = show_code_box->isChecked ();
 	code_size = code_size_box->intValue ();
 
 	fixPluginMapLists ();
 	RKWardMainWindow::getMain ()->initPlugins();
+	map_choser->setModel (0);	// we don't want any extra change notification for this
+	map_model->init (known_plugin_maps);
+	map_choser->setModel (map_model, 1);
 }
 
 void RKSettingsModulePlugins::save (KConfig *config) {
@@ -162,8 +160,17 @@
 	RK_TRACE (SETTINGS);
 
 	KConfigGroup cg = config->group ("Plugin Settings");
-	cg.writeEntry ("Plugin Maps", plugin_maps);
-	cg.writeEntry ("All known plugin maps", known_plugin_maps);
+	cg.deleteGroup ("Known Plugin maps");	// always start from scratch to remove cruft from pluginmaps
+	KConfigGroup pmg = cg.group ("Known Plugin maps");
+	for (int i = 0; i < known_plugin_maps.size (); ++i) {
+		const PluginMapStoredInfo &inf = known_plugin_maps[i];
+		KConfigGroup ppmg = pmg.group (inf.filename);
+		ppmg.writeEntry ("Active", inf.active);
+		ppmg.writeEntry ("Broken", inf.broken_in_this_version);
+		ppmg.writeEntry ("Quirky", inf.quirky_in_this_version);
+		ppmg.writeEntry ("timestamp", inf.last_modified);
+	}
+
 	cg.writeEntry ("Interface Preferences", static_cast<int> (interface_pref));
 	cg.writeEntry ("Code display default", show_code);
 	cg.writeEntry ("Code display size", code_size);
@@ -173,13 +180,30 @@
 	RK_TRACE (SETTINGS);
 
 	KConfigGroup cg = config->group ("Plugin Settings");
-	plugin_maps = cg.readEntry ("Plugin Maps", QStringList ());
-	known_plugin_maps = cg.readEntry ("All known plugin maps", QStringList ());
+	if (RKSettingsModuleGeneral::storedConfigVersion () < RKSettingsModuleGeneral::RKWardConfig_0_6_1) {
+		QStringList plugin_maps = cg.readEntry ("Plugin Maps", QStringList ());
+		QStringList kplugin_maps = cg.readEntry ("All known plugin maps", QStringList ());
+		for (int i = 0; i < kplugin_maps.size (); ++i) {
+			PluginMapStoredInfo inf (kplugin_maps[i]);
+			inf.active = plugin_maps.contains (kplugin_maps[i]);
+			// state info will be properly initialized in fixPluginMapLists()
+			known_plugin_maps.append (inf);
+		}
+	} else {
+		KConfigGroup pmg = cg.group ("Known Plugin maps");
+		QStringList kplugin_maps = pmg.groupList ();
+		for (int i = 0; i < kplugin_maps.size (); ++i) {
+			KConfigGroup ppmg = pmg.group (kplugin_maps[i]);
+			PluginMapStoredInfo inf (kplugin_maps[i]);
+			inf.active = ppmg.readEntry ("Active", false);
+			// Pluginmaps which are broken with one version of RKWard may be alright with other versions. So reset flags, if version has changed.
+			inf.broken_in_this_version = ppmg.readEntry ("Broken", false) && !RKSettingsModuleGeneral::rkwardVersionChanged ();
+			inf.quirky_in_this_version = ppmg.readEntry ("Quirky", false) && !RKSettingsModuleGeneral::rkwardVersionChanged ();
+			inf.last_modified = ppmg.readEntry ("timestamp", QDateTime ());
+			known_plugin_maps.append (inf);
+		}
+	}
 	fixPluginMapLists ();	// removes any maps which don't exist any more
-	if (plugin_maps.isEmpty ()) {
-		plugin_maps.append (RKCommonFunctions::getRKWardDataDir () + "/all.pluginmap");
-		fixPluginMapLists ();	// in case the default one is missing, somehow, too
-	}
 
 	interface_pref = static_cast<PluginPrefs> (cg.readEntry ("Interface Preferences", static_cast<int> (PreferRecommended)));
 	show_code = cg.readEntry ("Code display default", false);
@@ -189,6 +213,53 @@
 	}
 }
 
+int findKnownPluginMap (const QString& filename, const RKSettingsModulePlugins::PluginMapList& haystack) {
+	RK_TRACE (SETTINGS);
+
+	int i;
+	for (i = haystack.size () - 1; i >= 0; --i) {
+		if (haystack[i].filename == filename) return i;
+	}
+	return i;
+}
+
+bool RKSettingsModulePlugins::markPluginMapAsBroken (const QString& map) {
+	RK_TRACE (SETTINGS);
+
+	int index = findKnownPluginMap (map, known_plugin_maps);
+	if (index < 0) {
+		RK_ASSERT (index >= 0);
+		return false;
+	}
+	bool ret = !known_plugin_maps[index].broken_in_this_version;
+	known_plugin_maps[index].broken_in_this_version = true;
+	known_plugin_maps[index].active = false;
+	return ret;
+}
+
+bool RKSettingsModulePlugins::markPluginMapAsQuirky (const QString& map) {
+	RK_TRACE (SETTINGS);
+
+	int index = findKnownPluginMap (map, known_plugin_maps);
+	if (index < 0) {
+		RK_ASSERT (index >= 0);
+		return false;
+	}
+	bool ret = !known_plugin_maps[index].quirky_in_this_version;
+	known_plugin_maps[index].quirky_in_this_version = true;
+	return ret;
+}
+
+QStringList RKSettingsModulePlugins::pluginMaps () {
+	RK_TRACE (SETTINGS);
+
+	QStringList ret;
+	for (int i = known_plugin_maps.size () - 1; i >= 0; --i) {
+		if (known_plugin_maps[i].active) ret.append (known_plugin_maps[i].filename);
+	}
+	return ret;
+}
+
 // static
 void RKSettingsModulePlugins::registerPluginMaps (const QStringList &maps, bool force_add, bool force_reload) {
 	RK_TRACE (SETTINGS);
@@ -196,42 +267,62 @@
 	QStringList added;
 	foreach (const QString &map, maps) {
 		if (map.isEmpty ()) continue;
-		if (known_plugin_maps.contains (map)) {
+		int index = findKnownPluginMap (map, known_plugin_maps);
+		if (index >= 0) {
 			if (!force_add) continue;
-		} else {
-			known_plugin_maps.append (map);
+		} else {	// not found
+			PluginMapStoredInfo inf (map);
+			known_plugin_maps.append (inf);
+			index = known_plugin_maps.size () - 1;
 		}
 
-		if (plugin_maps.contains (map)) continue;
-		plugin_maps.append (map);
-		added.append (map);
+		if (!known_plugin_maps[index].active) {
+			known_plugin_maps[index].active = true;
+			added.append (map);
+		}
 	}
 
 	if (force_reload || (!added.isEmpty ())) {
-		// NOTE: Do this *before* showing the dialog, which is modal, i.e. has an event loop. Otherwise, subsequent calls e.g. to rk.call.plugin() could sneak in front of this
-		RKWardMainWindow::getMain ()->initPlugins();
+		RKWardMainWindow::getMain ()->initPlugins (added);
 	}
-
-	if (!added.isEmpty ()) {
-		KMessageBox::informationList (RKWardMainWindow::getMain (), i18n ("New RKWard plugin packs (listed below) have been found, and have been activated, automatically. To de-activate selected plugin packs, use Settings->Configure RKWard->Plugins."), added, i18n ("New plugins found"), "new_plugins_found");
-	}
 }
 
 void RKSettingsModulePlugins::fixPluginMapLists () {
 	RK_TRACE (SETTINGS);
 
-	for (int i = 0; i < plugin_maps.size (); ++i) {
-		QFileInfo info (plugin_maps[i]);
+	QFileInfo default_pluginmap (RKCommonFunctions::getRKWardDataDir () + "/all.pluginmap");
+	int default_pluginmap_index = -1;
+	bool any_active_pluginmap = false;
+
+	for (int i = 0; i < known_plugin_maps.size (); ++i) {
+		PluginMapStoredInfo &inf = known_plugin_maps[i];
+		QFileInfo info (inf.filename);
 		if (!info.isReadable ()) {
-			known_plugin_maps.removeAll (plugin_maps[i]);
-			plugin_maps.removeAt (i);
+			known_plugin_maps.removeAt (i);
 			--i;
+		} else {
+			if (inf.active) any_active_pluginmap = true;
+			if ((default_pluginmap_index < 0) && (info == default_pluginmap)) default_pluginmap_index = i;
+
+			if (info.lastModified () != inf.last_modified) {
+				inf.broken_in_this_version = false;
+				inf.quirky_in_this_version = false;
+				inf.last_modified = info.lastModified ();
+			}
 		}
 	}
 
-	foreach (const QString &map, plugin_maps) {
-		if (!known_plugin_maps.contains (map)) known_plugin_maps.append (map);
+	// make sure the default plugin map is in the list (unless it is non-readable)
+	if ((default_pluginmap_index < 0) && (default_pluginmap.isReadable ())) {
+		PluginMapStoredInfo inf (default_pluginmap.absoluteFilePath ());
+		known_plugin_maps.append (inf);
+		default_pluginmap_index = known_plugin_maps.size () - 1;
 	}
+
+	// if no other pluginmap is active, activate the default map
+	if (!any_active_pluginmap && (default_pluginmap_index >= 0)) {
+		known_plugin_maps[default_pluginmap_index].active = true;
+	}
 }
 
 void RKSettingsModulePlugins::installPluginPack (const QString &archive_file) {
@@ -259,7 +350,7 @@
 		archive = new KTar (archive_file);
 	}
 	if (!archive->open (QIODevice::ReadOnly)) {
-#warning TODO: show error message
+		// TODO: show error message
 		RK_ASSERT (false);
 		return;
 	}
@@ -267,9 +358,7 @@
 	delete (archive);
 
 	QStringList installed_maps = findPluginMapsRecursive (basename);
-	foreach (const QString map, installed_maps) {
-		if (!plugin_maps.contains (map)) plugin_maps.append (map);
-	}
+	registerPluginMaps (installed_maps, false, false);
 }
 
 void RKSettingsModulePlugins::uninstallPluginPack (const QString &archive_file) {
@@ -326,4 +415,158 @@
 	return ret;
 }
 
+RKSettingsModulePluginsModel::RKSettingsModulePluginsModel (RKSettingsModulePlugins* parent) : QAbstractTableModel (parent) {
+	RK_TRACE (SETTINGS);
+}
+
+RKSettingsModulePluginsModel::~RKSettingsModulePluginsModel() {
+	RK_TRACE (SETTINGS);
+}
+
+void RKSettingsModulePluginsModel::init (const RKSettingsModulePlugins::PluginMapList& known_plugin_maps) {
+	RK_TRACE (SETTINGS);
+	plugin_maps = known_plugin_maps;
+	emit (reset ());
+}
+
+int RKSettingsModulePluginsModel::rowCount (const QModelIndex& parent) const {
+	//RK_TRACE (SETTINGS);
+	if (parent.isValid ()) return 0;
+	return plugin_maps.size ();
+}
+
+#define COLUMN_CHECKED 0
+#define COLUMN_FILENAME 1
+#define COLUMN_STATUS 2
+#define COLUMN_COUNT 3
+
+int RKSettingsModulePluginsModel::columnCount (const QModelIndex& parent) const {
+	// RK_TRACE (SETTINGS);
+	if (parent.isValid ()) return 0;
+	return COLUMN_COUNT;
+}
+
+QVariant RKSettingsModulePluginsModel::data (const QModelIndex& index, int role) const {
+	// RK_TRACE (SETTINGS);
+	if (!index.isValid ()) return QVariant ();
+	int col = index.column ();
+
+	const RKSettingsModulePlugins::PluginMapStoredInfo &inf = plugin_maps[index.row ()];
+
+	if (role == Qt::BackgroundRole) {
+		if (inf.broken_in_this_version) return Qt::red;
+		if (inf.quirky_in_this_version) return Qt::yellow;
+	}
+
+	if (col == COLUMN_CHECKED) {
+		if (role == Qt::CheckStateRole) {
+			return (inf.active ? Qt::Checked : Qt::Unchecked);
+		}
+	} else if (col == COLUMN_FILENAME) {
+		if (role == Qt::DisplayRole) return inf.filename;
+	} else if (col == COLUMN_STATUS) {
+		if (role == Qt::DisplayRole) {
+			if (inf.broken_in_this_version) return i18n ("Broken");
+			QString status;
+			if (RKComponentMap::getMap ()->isPluginMapLoaded (inf.filename)) status = i18n ("Loaded");
+			if (inf.quirky_in_this_version) {
+				if (!status.isEmpty ()) status.append (", ");
+				status.append (i18n ("Quirky"));
+			}
+			return status;
+		}
+	}
+
+	return QVariant ();
+}
+
+Qt::ItemFlags RKSettingsModulePluginsModel::flags (const QModelIndex& index) const {
+	// RK_TRACE (SETTINGS);
+	Qt::ItemFlags flags = QAbstractItemModel::flags (index);
+	if (index.isValid () && (index.column () == COLUMN_CHECKED)) flags |= Qt::ItemIsUserCheckable | Qt::ItemIsEditable;
+	return flags;
+}
+
+QVariant RKSettingsModulePluginsModel::headerData (int section, Qt::Orientation orientation, int role) const {
+	// RK_TRACE (SETTINGS);
+	if ((role == Qt::DisplayRole) && (orientation == Qt::Horizontal)) {
+		if (section == COLUMN_CHECKED) return i18n ("Active");
+		if (section == COLUMN_FILENAME) return i18n ("Filename");
+		if (section == COLUMN_STATUS) return i18n ("Status");
+		RK_ASSERT (false);
+	}
+	return QVariant ();
+}
+
+bool RKSettingsModulePluginsModel::setData (const QModelIndex& index, const QVariant& value, int role) {
+	RK_TRACE (SETTINGS);
+
+	if (role == Qt::CheckStateRole) {
+		if (index.isValid () && (index.column () == COLUMN_CHECKED)) {
+			plugin_maps[index.row ()].active = value.toBool ();
+			emit (dataChanged (index, index));
+			return true;
+		}
+	}
+
+	return QAbstractItemModel::setData (index, value, role);
+}
+
+void RKSettingsModulePluginsModel::insertNewStrings (int above_row) {
+	RK_TRACE (SETTINGS);
+
+	QStringList files = KFileDialog::getOpenFileNames (RKCommonFunctions::getRKWardDataDir (), "*.pluginmap", static_cast<QWidget*> (QObject::parent ()), i18n ("Select .pluginmap-file"));
+
+	// already known files are activated, but not added
+	for (int i = files.size () -1; i >= 0; --i) {
+		int pos = findKnownPluginMap (files[i], plugin_maps);
+		if (pos >= 0) {
+			if (!plugin_maps[pos].active) {
+				plugin_maps[pos].active = true;
+				emit (dataChanged (index (pos, 0), index (pos, COLUMN_COUNT - 1)));
+			}
+			files.removeAt (i);
+		} 
+	}
+
+	beginInsertRows (QModelIndex (), above_row, files.size ());
+	for (int i = files.size () - 1; i >= 0; --i) {
+		RKSettingsModulePlugins::PluginMapStoredInfo inf (files[i]);
+		inf.active = true;
+		plugin_maps.insert (above_row, inf);
+	}
+	endInsertRows ();
+}
+
+void RKSettingsModulePluginsModel::swapRows (int rowa, int rowb) {
+	RK_TRACE (SETTINGS);
+
+	RK_ASSERT ((rowa >= 0) && (rowa < rowCount ()) && (rowb >= 0) && (rowb < rowCount ()));
+	RKSettingsModulePlugins::PluginMapStoredInfo inf = plugin_maps[rowa];
+	plugin_maps[rowa] = plugin_maps[rowb];
+	plugin_maps[rowb] = inf;
+}
+
+bool RKSettingsModulePluginsModel::removeRows (int row, int count, const QModelIndex& parent) {
+	RK_TRACE (SETTINGS);
+	RK_ASSERT (!parent.isValid ());
+
+	if ((row < 0) || (count < 1) || (row + count > rowCount () - 1)) return false;
+	for (int i = row + count - 1; i >= row; --i) {
+		plugin_maps.removeAt (i);
+	}
+	return true;
+}
+
+const RKSettingsModulePluginsModel::PluginMapMetaInfo& RKSettingsModulePluginsModel::getPluginMapMetaInfo (const QString& pluginmapfile) {
+	RK_TRACE (SETTINGS);
+	if (!plugin_map_dynamic_info.contains (pluginmapfile)) {
+		// TODO
+		PluginMapMetaInfo inf;
+		plugin_map_dynamic_info.insert (pluginmapfile, inf);
+	}
+
+	return (plugin_map_dynamic_info[pluginmapfile]);
+}
+
 #include "rksettingsmoduleplugins.moc"

Modified: trunk/rkward/rkward/settings/rksettingsmoduleplugins.h
===================================================================
--- trunk/rkward/rkward/settings/rksettingsmoduleplugins.h	2013-01-25 16:32:55 UTC (rev 4503)
+++ trunk/rkward/rkward/settings/rksettingsmoduleplugins.h	2013-01-25 16:37:31 UTC (rev 4504)
@@ -2,7 +2,7 @@
                           rksettingsmoduleplugins  -  description
                              -------------------
     begin                : Wed Jul 28 2004
-    copyright            : (C) 2004, 2006, 2007, 2010 by Thomas Friedrichsmeier
+    copyright            : (C) 2004-2013 by Thomas Friedrichsmeier
     email                : tfry at users.sourceforge.net
  ***************************************************************************/
 
@@ -18,10 +18,14 @@
 #define RKSETTINGSMODULEPLUGINS_H
 
 #include "rksettingsmodule.h"
+#include "../plugin/rkcomponentmeta.h"
 
 #include <qstringlist.h>
+#include <QDateTime>
+#include <QAbstractTableModel>
 
-class MultiStringSelector;
+class RKMultiStringSelectorV2;
+class RKSettingsModulePluginsModel;
 class QButtonGroup;
 class QCheckBox;
 class RKSpinBox;
@@ -32,10 +36,9 @@
 class RKSettingsModulePlugins : public RKSettingsModule {
 	Q_OBJECT
 public:
-    RKSettingsModulePlugins (RKSettings *gui, QWidget *parent);
+	RKSettingsModulePlugins (RKSettings *gui, QWidget *parent);
+	~RKSettingsModulePlugins ();
 
-    ~RKSettingsModulePlugins ();
-
 	bool hasChanges ();
 	void applyChanges ();
 	void save (KConfig *config);
@@ -46,8 +49,9 @@
 	static void loadSettings (KConfig *config);
 	
 	QString caption ();
-	
-	static QStringList &pluginMaps () { return plugin_maps; };
+
+	/** @returns a list of active plugin maps */
+	static QStringList pluginMaps ();
 	static PluginPrefs getInterfacePreference () { return interface_pref; };
 	static bool showCodeByDefault () { return show_code; };
 	static int defaultCodeHeight () { return code_size; };
@@ -57,19 +61,32 @@
 	 * @param force_reload If true, plugin maps are always reloaded, even if no maps were added
 	 */
 	static void registerPluginMaps (const QStringList &maps, bool force_add, bool force_reload);
+	/** marks given map as broken (in this version), and deactivates it. @Returns false is the map was already known to be broken, true otherwise. */
+	static bool markPluginMapAsBroken (const QString &map);
+	/** marks given map as quirky (in this version). @Returns false is the map was already known to be quirky, true otherwise. */
+	static bool markPluginMapAsQuirky (const QString &map);
+	/** Helper struct used by RKSettingsModulePlugins to keep track of plugin map files. */
+	struct PluginMapStoredInfo {
+		PluginMapStoredInfo (const QString &_filename) : filename (_filename), active (false), broken_in_this_version (false), quirky_in_this_version (false) {};
+		QString filename;
+		bool active;
+		bool broken_in_this_version;
+		bool quirky_in_this_version;
+		QDateTime last_modified;
+	};
+	typedef QList<PluginMapStoredInfo> PluginMapList;
 public slots:
 	void settingChanged ();
-	void browseRequest (QStringList* strings);
 private:
-	MultiStringSelector *map_choser;
+ 	RKMultiStringSelectorV2 *map_choser;
+	RKSettingsModulePluginsModel *map_model;
 	QButtonGroup *button_group;
 	QCheckBox *show_code_box;
 	RKSpinBox *code_size_box;
 
-	/** active plugin maps */
-	static QStringList plugin_maps;
 	/** plugin maps which are not necessarily active, but have been encountered, before. @see plugin_maps */
-	static QStringList known_plugin_maps;
+	static PluginMapList known_plugin_maps;
+
 	static PluginPrefs interface_pref;
 	static bool show_code;
 	static int code_size;
@@ -82,4 +99,35 @@
 	static void fixPluginMapLists ();
 };
 
+class RKSettingsModulePluginsModel : public QAbstractTableModel {
+	Q_OBJECT
+public:
+	RKSettingsModulePluginsModel (RKSettingsModulePlugins* parent);
+	virtual ~RKSettingsModulePluginsModel ();
+/** (re-)initialize the model */
+	void init (const RKSettingsModulePlugins::PluginMapList &known_plugin_maps);
+	RKSettingsModulePlugins::PluginMapList pluginMaps () { return plugin_maps; };
+public slots:
+	void swapRows (int rowa, int rowb);
+	void insertNewStrings (int above_row);
+private:
+	RKSettingsModulePlugins::PluginMapList plugin_maps;
+	struct PluginMapMetaInfo {
+		RKComponentAboutData *about;
+		QList<RKComponentDependency> dependencies;
+		bool loaded;
+	};
+	QHash<QString, PluginMapMetaInfo> plugin_map_dynamic_info;
+	const PluginMapMetaInfo &getPluginMapMetaInfo (const QString &pluginmapfile);
+
+	// reimplemented model functions
+	int rowCount (const QModelIndex &parent = QModelIndex()) const;
+	int columnCount (const QModelIndex &parent = QModelIndex()) const;
+	QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const;
+	QVariant headerData (int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
+	bool setData (const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
+    bool removeRows (int row, int count, const QModelIndex& parent = QModelIndex ());
+	Qt::ItemFlags flags (const QModelIndex &index) const;
+};
+
 #endif





More information about the rkward-tracker mailing list