[rkward-cvs] [rkward] /: Change the implementation of plugin menu building.

Thomas Friedrichsmeier thomas.friedrichsmeier at ruhr-uni-bochum.de
Fri Nov 28 13:05:38 UTC 2014


Git commit ee810caa39b4e93507959f0176e3087966a43b12 by Thomas Friedrichsmeier.
Committed on 28/11/2014 at 13:02.
Pushed by tfry into branch 'master'.

Change the implementation of plugin menu building.
- Keep a representation of the menu hierarchy.
- Allow grouping plugins within a menu. Sort plugins within each group, alphabetically.
- Drop the former "index" attribute controlling oder of menu entries.

M  +2    -0    ChangeLog
M  +0    -7    rkward/plugin/rkcomponentcontext.cpp
M  +0    -2    rkward/plugin/rkcomponentcontext.h
M  +138  -56   rkward/plugin/rkcomponentmap.cpp
M  +21   -20   rkward/plugin/rkcomponentmap.h
M  +16   -13   rkward/plugins/analysis.pluginmap
M  +5    -4    rkward/plugins/data.pluginmap
M  +89   -88   rkward/plugins/distributions.pluginmap
M  +1    -0    rkward/plugins/embedded.pluginmap
M  +4    -3    rkward/plugins/import_export.pluginmap
M  +10   -15   rkward/plugins/irt.pluginmap
A  +23   -0    rkward/plugins/menu.pluginmap
M  +2    -1    rkward/plugins/plots.pluginmap
M  +19   -15   rkward/plugins/under_development.pluginmap
M  +2    -2    rkward/plugins/x11device.pluginmap
M  +1    -0    rkward/rkward.cpp

http://commits.kde.org/rkward/ee810caa39b4e93507959f0176e3087966a43b12

diff --git a/ChangeLog b/ChangeLog
index 7a05c59..bdf1735 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,5 @@
+- More robust control over placement of plugins within a menu
+  - TODO: document, discuss
 - Fix several issues of excessive printing of digits in plugins' output
 - Restructure layout of CSV-import dialog
 - Allow to open (any number of) R script files and rkward://-urls from the command line
diff --git a/rkward/plugin/rkcomponentcontext.cpp b/rkward/plugin/rkcomponentcontext.cpp
index 7e4b768..254b179 100644
--- a/rkward/plugin/rkcomponentcontext.cpp
+++ b/rkward/plugin/rkcomponentcontext.cpp
@@ -35,13 +35,6 @@ RKContextMap::~RKContextMap () {
 	RK_TRACE (PLUGIN);
 }
 
-int RKContextMap::create (const QDomElement &context_element, XMLHelper &xml, const QString &component_namespace) {
-	RK_TRACE (PLUGIN);
-
-	QDomElement element = xml.getChildElement (gui_xml.documentElement (), "MenuBar", DL_ERROR);
-	return (createMenus (element, xml, context_element, component_namespace));
-}
-
 RKContextHandler *RKContextMap::makeContextHandler (QObject *parent, bool create_actions) {
 	RK_TRACE (PLUGIN);
 
diff --git a/rkward/plugin/rkcomponentcontext.h b/rkward/plugin/rkcomponentcontext.h
index 33f7052..fc841fd 100644
--- a/rkward/plugin/rkcomponentcontext.h
+++ b/rkward/plugin/rkcomponentcontext.h
@@ -38,8 +38,6 @@ public:
 	RKContextMap (const QString &id);
 /** destructor */
 	~RKContextMap ();
-/** A menu entries to the context map from a .pluginmap file */
-	int create (const QDomElement &context_element, XMLHelper &xml, const QString &component_namespace);
 /** Create a context handler for this context. */
 	RKContextHandler *makeContextHandler (QObject *parent, bool create_actions=true);
 	QStringList components () { return component_ids; };
diff --git a/rkward/plugin/rkcomponentmap.cpp b/rkward/plugin/rkcomponentmap.cpp
index b6e4eda..0cde1a4 100644
--- a/rkward/plugin/rkcomponentmap.cpp
+++ b/rkward/plugin/rkcomponentmap.cpp
@@ -60,80 +60,154 @@ RKComponentGUIXML::~RKComponentGUIXML () {
 void RKComponentGUIXML::clearGUIDescription () {
 	RK_TRACE (PLUGIN);
 
-	gui_xml.setContent (QString ("<!DOCTYPE kpartgui>\n<kpartgui name=\"rkwardcomponents\" version=\"0.3.4\">\n<MenuBar>\n\n</MenuBar>\n</kpartgui>"));
+	gui_xml.setContent (QString ("<!DOCTYPE kpartgui>\n<kpartgui name=\"rkwardcomponents\" version=\"063\">\n<MenuBar>\n\n</MenuBar>\n</kpartgui>"));
+	toplevel_menu.subentries.clear ();
+}
+
+void RKComponentGUIXML::menuItemsToXml (const QList<RKComponentGUIXML::MenuEntry> &entries, QDomElement &xml) {
+	// ok, we could really do simply text-pasting, instead of using QDom in this function, but I'm afraid of not getting all character escapes right.
+	for (int i = 0; i < entries.size (); ++i) {
+		const RKComponentGUIXML::MenuEntry &entry = entries[i];
+		if (entry.type == RKComponentGUIXML::MenuEntry::Group) {
+			if (entry.label == "-") xml.appendChild (gui_xml.createElement ("Separator"));
+			menuItemsToXml (entry.subentries, xml);
+			if (entry.label == "-") xml.appendChild (gui_xml.createElement ("Separator"));
+		} else if (entry.type == RKComponentGUIXML::MenuEntry::Menu) {
+			if (entry.subentries.isEmpty ()) {
+				continue;	// just skip over empty menus
+			}
+			QDomElement submenu = gui_xml.createElement ("Menu");
+			submenu.setAttribute ("name", entry.id);
+			QDomElement text = gui_xml.createElement ("text");
+			text.appendChild (gui_xml.createTextNode (entry.label));
+			submenu.appendChild (text);
+			menuItemsToXml (entry.subentries, submenu);
+			xml.appendChild (submenu);
+		} else {
+			QDomElement action = gui_xml.createElement ("Action");
+			action.setAttribute ("name", entry.id);
+			xml.appendChild (action);
+		}
+	}
 }
 
-int RKComponentGUIXML::createMenus (QDomElement& parent, XMLHelper &xml, const QDomElement& hierarchy_description, const QString& cnamespace) {
+int RKComponentGUIXML::createMenus (XMLHelper &xml, const QDomElement& hierarchy_description, const QString& cnamespace) {
 	RK_TRACE (PLUGIN);
 
-	XMLChildList list = xml.getChildElements (hierarchy_description, "menu", DL_INFO);
-	int counter = 0;
-	for (XMLChildList::const_iterator it=list.begin (); it != list.end (); ++it) {
-		counter += addSubMenu (parent, xml, (*it), cnamespace);
-	}
-	return counter;
+	return (addEntries (&toplevel_menu, xml, hierarchy_description, cnamespace));
 }
 
-QDomElement RKComponentGUIXML::findOrCreateElement (QDomElement& parent, XMLHelper &xml, const QString& tagname, const QString& name, const QString& label, int index) {
+void RKComponentGUIXML::finalize () {
 	RK_TRACE (PLUGIN);
 
-	XMLChildList list = xml.getChildElements (parent, QString::null, DL_INFO);		// we need to look at all children, so we get the order right
-	QDomElement insert_after_element;
-	for (XMLChildList::const_iterator it=list.begin (); it != list.end (); ++it) {
-		if ((tagname == (*it).tagName ()) && (name == xml.getStringAttribute ((*it), "name", "", DL_ERROR))) {
-			return (*it);
-		} else {
-			if (index >= 0) {
-				if (index > xml.getIntAttribute ((*it), "index", -1, DL_INFO)) {
-					insert_after_element = *it;
+	QDomElement xmlgui_menubar_element = gui_xml.documentElement ().firstChildElement ("MenuBar");
+	menuItemsToXml (toplevel_menu.subentries, xmlgui_menubar_element);
+}
+
+void insertEntry (RKComponentGUIXML::MenuEntry *parent, const RKComponentGUIXML::MenuEntry &entry) {
+	RK_ASSERT (parent && (parent->type == RKComponentGUIXML::MenuEntry::Menu));
+
+	// try to find group
+	QList<RKComponentGUIXML::MenuEntry> *list = &(parent->subentries);
+	for (int i = 0; i < list->size (); ++i) {
+		RKComponentGUIXML::MenuEntry &g = (*list)[i];
+		if (g.id == entry.group) {
+			if (entry.type == RKComponentGUIXML::MenuEntry::Group) {
+				list->insert (++i, entry);
+			} else {
+				QList<RKComponentGUIXML::MenuEntry> *group_list = &(g.subentries);
+				for (int j = 0; j < group_list->size (); ++j) {
+					if (QString::localeAwareCompare (group_list->at (j).label, entry.label) > 0) {
+						group_list->insert (j, entry);
+						return;
+					}
 				}
+				group_list->append (entry);
 			}
+			return;
 		}
 	}
 
-	// element not found. Create a new one instead
-	QDomElement ret = gui_xml.createElement (tagname);
-	ret.setAttribute ("name", name);
-	ret.setAttribute ("index", index);
-	if (!label.isEmpty ()) {
-		QDomElement text = gui_xml.createElement ("text");
-		text.appendChild (gui_xml.createTextNode (label));
-		ret.appendChild (text);
+	// group not found: Declare it, implicitly, and try again.
+	RKComponentGUIXML::MenuEntry new_group;
+	new_group.type = RKComponentGUIXML::MenuEntry::Group;
+	new_group.id = entry.group;
+	if (entry.group == QLatin1String ("top")) {
+		list->insert (0, new_group);
+	} else if (entry.group == QLatin1String ("bottom")) {
+		list->append (new_group);
+	} else {
+		if (list->isEmpty () || list->last ().group != QLatin1String ("bottom")) {
+			list->append (new_group);
+		} else {
+			list->insert (list->size () - 1, new_group);
+		}
 	}
-	parent.insertAfter (ret, insert_after_element);	// if index_after_element.isNull, this add the new element as the last child of parent!
+	insertEntry (parent, entry);
+}
 
-	return ret;
+RKComponentGUIXML::MenuEntry *findMenu (RKComponentGUIXML::MenuEntry *parent, const QString id) {
+	RK_ASSERT (parent && parent->type == RKComponentGUIXML::MenuEntry::Menu);
+	QList<RKComponentGUIXML::MenuEntry> *list = &(parent->subentries);
+	for (int i = 0; i < list->size (); ++i) {
+		QList<RKComponentGUIXML::MenuEntry> *group_list = &((*list)[i].subentries);
+		for (int j = 0; j < group_list->size (); ++j) {
+			RKComponentGUIXML::MenuEntry *g = &((*group_list)[j]);
+			if (g->id == id) return g;
+		}
+	}
+	return 0;
 }
 
-int RKComponentGUIXML::addSubMenu (QDomElement& parent, XMLHelper &xml, const QDomElement& description, const QString& cnamespace) {
+int RKComponentGUIXML::addEntries (RKComponentGUIXML::MenuEntry *menu, XMLHelper &xml, const QDomElement description, const QString& cnamespace) {
 	RK_TRACE (PLUGIN);
 
-	int counter = 0;
-
-	// 1: check whether menu already exists, and create new menu otherwise
-	QDomElement menu_element = findOrCreateElement (parent, xml, "Menu", xml.getStringAttribute (description, "id", "none", DL_ERROR), xml.i18nStringAttribute (description, "label", i18n ("(no label)"), DL_WARNING), xml.getIntAttribute (description, "index", -1, DL_INFO));
-
-	// 2: recurse into submenus (of element to add!)
-	XMLChildList list = xml.getChildElements (description, "menu", DL_INFO);
-	for (XMLChildList::const_iterator it=list.begin (); it != list.end (); ++it) {
-		counter += addSubMenu (menu_element, xml, (*it), cnamespace);
-	}
-
-	// 3: add entries
-	list = xml.getChildElements (description, "entry", DL_INFO);
-	for (XMLChildList::const_iterator it=list.begin (); it != list.end (); ++it) {
-		QString id = cnamespace + xml.getStringAttribute ((*it), "component", "#invalid#", DL_ERROR);
+	int leaves = 0;
+	XMLChildList list = xml.getChildElements (description, QString (), DL_INFO);
+	for (int i = 0; i < list.size (); ++i) {
+		const QString add_to = xml.getStringAttribute (list[i], "group", QString (), DL_INFO);
+		if (list[i].tagName () == "menu") {
+			QString sub_id = xml.getStringAttribute (list[i], "id", "none", DL_ERROR);
+			MenuEntry *found = findMenu (menu, sub_id);
+			if (found) {
+				leaves += addEntries (found, xml, list[i], cnamespace);
+			} else {
+				MenuEntry sub;
+				sub.id = sub_id;
+				sub.label = xml.i18nStringAttribute (list[i], "label", i18n ("(no label)"), DL_WARNING);
+				sub.group = add_to;
+				sub.type = MenuEntry::Menu;
+				leaves += addEntries (&sub, xml, list[i], cnamespace);
+				insertEntry (menu, sub);
+			}
+		} else if (list[i].tagName () == "entry") {
+			QString id = cnamespace + xml.getStringAttribute (list[i], "component", "#invalid#", DL_ERROR);
 
-		RKComponentHandle* handle = RKComponentMap::getComponentHandle (id);
-		if ((!handle) || (!handle->isPlugin ())) {
-			RK_DEBUG (PLUGIN, DL_ERROR, "No such component found while creating menu-entries or component is not a standalone plugin: \"%s\". No entry created.", id.toLatin1 ().data ());
+			RKComponentHandle* handle = RKComponentMap::getComponentHandle (id);
+			if ((!handle) || (!handle->isPlugin ())) {
+				RK_DEBUG (PLUGIN, DL_ERROR, "No such component found while creating menu-entries or component is not a standalone plugin: \"%s\". No entry created.", id.toLatin1 ().data ());
+			} else {
+				MenuEntry plug;
+				plug.id = id;
+				plug.label = handle->getLabel ();
+				plug.group = add_to;
+				plug.type = MenuEntry::Entry;
+				insertEntry (menu, plug);
+				addedEntry (id, handle);
+				++leaves;
+			}
+		} else if (list[i].tagName () == "group") {
+			MenuEntry group;
+			group.id = xml.getStringAttribute (list[i], "id", "none", DL_ERROR);
+			group.group = add_to;
+			if (xml.getBoolAttribute (list[i], "separated", false, DL_INFO)) group.label = "-";
+			group.type = MenuEntry::Group;
+			insertEntry (menu, group);
 		} else {
-			findOrCreateElement (menu_element, xml, "Action", id, QString::null, xml.getIntAttribute ((*it), "index", -1, DL_INFO));
-			addedEntry (id, handle);
-			counter++;
+			RK_ASSERT (false);
 		}
 	}
-	return counter;
+	return leaves;
 }
 
 /////////////////////////// END RKComponentXMLGUIClient /////////////////////////////////
@@ -334,6 +408,17 @@ RKPluginMapParseResult RKComponentMap::addPluginMap (const QString& plugin_map_f
 	return getMap()->addPluginMapLocal (plugin_map_file);
 }
 
+void RKComponentMap::finalizeAll () {
+	RK_TRACE (PLUGIN);
+
+	finalize ();
+	setXMLGUIBuildDocument (gui_xml);
+	actionCollection ()->readSettings ();
+	foreach (RKContextMap *ctx, getMap()->contexts) {
+		ctx->finalize ();
+	}
+}
+
 RKPluginMapParseResult RKComponentMap::addPluginMapLocal (const QString& plugin_map_file) {
 	RK_TRACE (PLUGIN);
 
@@ -490,8 +575,7 @@ RKPluginMapParseResult RKComponentMap::addPluginMapLocal (const QString& plugin_
 	}
 
 	// step 3: create / insert into menus
-	QDomElement xmlgui_menubar_element = xml.getChildElement (gui_xml.documentElement (), "MenuBar", DL_ERROR);
-	ret.valid_plugins += createMenus (xmlgui_menubar_element, xml, xml.getChildElement (document_element, "hierarchy", DL_INFO), cnamespace);
+	ret.valid_plugins += createMenus (xml, xml.getChildElement (document_element, "hierarchy", DL_INFO), cnamespace);
 
 	// step 4: create and register contexts
 	list = xml.getChildElements (document_element, "context", DL_INFO);
@@ -503,11 +587,9 @@ RKPluginMapParseResult RKComponentMap::addPluginMapLocal (const QString& plugin_
 			context = new RKContextMap (id);
 			contexts.insert (id, context);
 		}
-		ret.valid_plugins += context->create (*it, xml, cnamespace);
+		ret.valid_plugins += context->createMenus (xml, *it, cnamespace);
 	}
 
-	setXMLGUIBuildDocument (gui_xml);		// TODO: Should be called only once, not for each pluginmap!
-	actionCollection ()->readSettings ();
 	return ret;
 }
 
diff --git a/rkward/plugin/rkcomponentmap.h b/rkward/plugin/rkcomponentmap.h
index d5e90d0..64abc80 100644
--- a/rkward/plugin/rkcomponentmap.h
+++ b/rkward/plugin/rkcomponentmap.h
@@ -118,6 +118,23 @@ This class represents the common functionality between RKComponentMap and RKCont
 @author Thomas Friedrichsmeier
 */
 class RKComponentGUIXML {
+public:
+	struct MenuEntry {
+		QString label;
+		QString id;
+		QString group;
+		enum {
+			Entry,
+			Menu,
+			Group
+		} type;
+		QList<MenuEntry> subentries;
+	} toplevel_menu;
+/** build XMLGUI menus
+ at param hierarchy_description the QDomElement containing the description for the new menu hierarchy
+ at returns number of plugins/menu-entries added successfully */
+	int createMenus (XMLHelper &xml, const QDomElement& hierarchy_description, const QString& cnamespace);
+	void finalize ();
 protected:
 	RKComponentGUIXML ();
 	virtual ~RKComponentGUIXML ();
@@ -125,31 +142,14 @@ protected:
 /** reset the xml file */
 	void clearGUIDescription ();
 
-/** build XMLGUI menus
- at param parent the parent menu (or tag) (in the KXMLGUI)
- at param hierarchy_description the QDomElement containing the description for the new menu hierarchy
- at returns number of plugins/menu-entries added successfully */
-	int createMenus (QDomElement& parent, XMLHelper &xml, const QDomElement& hierarchy_description, const QString& cnamespace);
-
-/** recurse into a lower menu-level 
- at param parent the parent menu (in the KXMLGUI)
- at param description the QDomElement containing the description for the new submenu
- at returns number of plugins/menu-entries added successfully */
-	int addSubMenu (QDomElement& parent, XMLHelper &xml, const QDomElement& description, const QString& cnamespace);
-
-/** helper function: Find a specified element, and return it. If the element could not be found, it is created instead. The first three parameters are used as search parameters (all have to match). The additional two parameters only take effect, if a new element is created.
- at param parent the QDomElement whose children to search through
- at param tagname the tagname to look for
- at param name value of the "name"-attribute to look for
- at param label the label to assign to the new element (if no existing match could be found)
- at param index the index position where to insert the new element in the list of children (if no existing match could be found). -1 means insert at the end of the list. */
-	QDomElement findOrCreateElement (QDomElement& parent, XMLHelper &xml, const QString& tagname, const QString& name, const QString& label, int index);
-
 /** an entry was added to the menu(s) somewhere. Reimplement, if you want to e.g. create a KAction for this */
 	virtual void addedEntry (const QString & /* id */, RKComponentHandle * /* handle */) {};
 
 /** The generated XML GUI description in KDEs ui.rc format */
 	QDomDocument gui_xml;
+private:
+	int addEntries (RKComponentGUIXML::MenuEntry *menu, XMLHelper &xml, const QDomElement description, const QString& cnamespace);
+	void menuItemsToXml (const QList<RKComponentGUIXML::MenuEntry> &entries, QDomElement &xml);
 };
 
 
@@ -184,6 +184,7 @@ public:
 /** adds all Plugins / components in a .pluginmap-file. Also takes care of creating the menu-items, etc.
 @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);
+	void finalizeAll ();
 
 /** clears out (and deletes) all components / plugins */
 	static void clearAll ();
diff --git a/rkward/plugins/analysis.pluginmap b/rkward/plugins/analysis.pluginmap
index 2b62a9a..cc73d33 100644
--- a/rkward/plugins/analysis.pluginmap
+++ b/rkward/plugins/analysis.pluginmap
@@ -2,6 +2,7 @@
 
 <document base_prefix="" namespace="rkward" id="analysis" po_id="analysis">
 	<include file="pluginmap_meta.inc"/>
+	<require file="menu.pluginmap"/>
 
 	<components>
 		<component type="standard" id="basic_statistics" file="uni1.2/description.xml" label="Basic Statistics" />
@@ -48,39 +49,41 @@
 	</components>
 
 	<hierarchy>
-		<menu id="analysis" label="Analysis" index="4">
-			<entry component="basic_statistics" index="1"/>
-			<menu id="correlation" label="Correlation" index="1">
+		<menu id="analysis" label="Analysis">
+			<group id="descriptives" group="top"/>
+			<entry component="basic_statistics" group="descriptives"/>
+			<entry component="descriptive" group="descriptives"/>
+
+			<menu id="correlation" label="Correlation">
 				<entry component="corr_matrix" />
 				<entry component="cor_graph" />
 			</menu>
-			<menu id="crosstabs" label="Crosstabs" index="2">			
+			<menu id="crosstabs" label="Crosstabs">
 				<entry component="crosstab" />
 				<entry component="crosstab_multi" />
 			</menu>
-			<entry component="descriptive" index="3"/>
-			<menu id="means" label="Means" index="4">
-				<menu id="ttests" label="t-Tests" index="0">
+			<menu id="means" label="Means">
+				<menu id="ttests" label="t-Tests">
 					<entry component="t_test" />
 				</menu>
 			</menu>
-			<menu id="moments" label="Moments" i18n_context="Statistics" index="6">
+			<menu id="moments" label="Moments" i18n_context="Statistics">
 				<entry component="anscombe_test"/>
 				<entry component="bonett_test"/>
 				<entry component="agostino_test"/>
 				<entry component="moment"/>
 				<entry component="skewness_kurtosis"/>
 			</menu>
-			<menu id="outliers" label="Outlier Tests" index="7">
+			<menu id="outliers" label="Outlier Tests">
 				<entry component="chisq_out_test"/>
 				<entry component="dixon_test"/>
 				<entry component="grubbs_test"/>
 				<entry component="outlier"/>
 			</menu>
-			<menu id="regression" label="Regression" index="8">
+			<menu id="regression" label="Regression">
 				<entry component="linear_regression"/>
 			</menu>
-			<menu id="variances" label="Variances / Scale" index="10">
+			<menu id="variances" label="Variances / Scale">
 				<menu id="variances_parametric" label="Parametric tests">
 					<entry component="bartlett_test"/>
 					<entry component="F_test"/>
@@ -93,13 +96,13 @@
 					<entry component="mood_test"/>
 				</menu>
 			</menu>
-			<menu id="time_series" label="Time Series" index="10">
+			<menu id="time_series" label="Time Series">
 				<entry component="Box_test"/>
 				<entry component="hp_filter"/>
 				<entry component="kpss_test"/>
 				<entry component="PP_test"/>
 			</menu>
-			<menu id="wilcoxon_test" label="Wilcoxon Tests" index="11">
+			<menu id="wilcoxon_test" label="Wilcoxon Tests">
 				<entry component="wilcoxon_tests" />
 			</menu>
 		</menu>
diff --git a/rkward/plugins/data.pluginmap b/rkward/plugins/data.pluginmap
index 2a96215..ccd00b1 100644
--- a/rkward/plugins/data.pluginmap
+++ b/rkward/plugins/data.pluginmap
@@ -2,6 +2,7 @@
 
 <document base_prefix="" namespace="rkward" id="data">
 	<include file="pluginmap_meta.inc"/>
+	<require file="menu.pluginmap"/>
 
 	<components>
 		<component type="standard" id="generate_random" file="data/generate_random.xml" label="Generate random data" />
@@ -11,11 +12,11 @@
 	</components>
 
 	<hierarchy>
-		<menu id="data" label="Data" index="3">
-			<entry component="generate_random" />
+		<menu id="data" label="Data">
+			<entry component="generate_random"/>
 			<entry component="recode_categorical"/>
-			<entry component="sort_data" />
-			<entry component="subset_dataframe" />
+			<entry component="sort_data"/>
+			<entry component="subset_dataframe"/>
 		</menu>
 	</hierarchy>
 </document>
\ No newline at end of file
diff --git a/rkward/plugins/distributions.pluginmap b/rkward/plugins/distributions.pluginmap
index 2b546d8..56f7877 100644
--- a/rkward/plugins/distributions.pluginmap
+++ b/rkward/plugins/distributions.pluginmap
@@ -2,6 +2,7 @@
 
 <document base_prefix="distributions/" namespace="rkward" id="distributions">
 	<include file="pluginmap_meta.inc"/>
+	<require file="../menu.pluginmap"/>
 
 	<components>
 		<!-- tests -->
@@ -117,134 +118,134 @@
 	</components>
 
 	<hierarchy>
-		<menu id="distributions" label="Distributions" index="7">
-			<menu id="distribution_analysis" label="Distribution Analysis" index="0">
-				<entry component="ad_test" label="Anderson-Darling Normality Test" index="0"/>
-				<entry component="cvm_test" label="Cramer-von Mises Normality Test" index="1"/>
-				<entry component="lillie_test" label="Lilliefors (Kolmogorov-Smirnov) Normality Test" index="2"/>
-				<entry component="pearson_test" label="Pearson chi-square Normality Test" index="3"/>
-				<entry component="sf_test" label="Shapiro-Francia Normality Test" index="4"/>
-				<entry component="shapiro_test" label="Shapiro-Wilk Normality Test" index="5"/>
+		<menu id="distributions" label="Distributions">
+			<menu id="distribution_analysis" label="Distribution Analysis">
+				<entry component="ad_test" label="Anderson-Darling Normality Test"/>
+				<entry component="cvm_test" label="Cramer-von Mises Normality Test"/>
+				<entry component="lillie_test" label="Lilliefors (Kolmogorov-Smirnov) Normality Test"/>
+				<entry component="pearson_test" label="Pearson chi-square Normality Test"/>
+				<entry component="sf_test" label="Shapiro-Francia Normality Test"/>
+				<entry component="shapiro_test" label="Shapiro-Wilk Normality Test"/>
 				<entry component="jb_test" label="Jarque-Bera Normality Test"/>
 			</menu>
-			<menu id="univariate_continuous_distributions" label="Univariate continuous distributions" index="1">
-				<menu id="beta_distribution" label="Beta" index="0">
-					<entry component="beta_quantiles" label="Beta quantiles" index="1"/>
-					<entry component="beta_probabilities" label="Beta probabilities" index="0"/>
-					<entry component="plot_beta_clt" label="Beta CLT" index="2"/>
+			<menu id="univariate_continuous_distributions" label="Univariate continuous distributions">
+				<menu id="beta_distribution" label="Beta">
+					<entry component="beta_quantiles" label="Beta quantiles"/>
+					<entry component="beta_probabilities" label="Beta probabilities"/>
+					<entry component="plot_beta_clt" label="Beta CLT"/>
 					<entry component="plot_beta_distribution" label="Plot Beta distribution"/>
 				</menu>
-				<menu id="cauchy_distribution" label="Cauchy" index="1">
-					<entry component="cauchy_quantiles" label="Cauchy quantiles" index="1"/>
-					<entry component="cauchy_probabilities" label="Cauchy probabilities" index="0"/>
+				<menu id="cauchy_distribution" label="Cauchy">
+					<entry component="cauchy_quantiles" label="Cauchy quantiles"/>
+					<entry component="cauchy_probabilities" label="Cauchy probabilities"/>
 					<entry component="plot_cauchy_distribution" label="Plot Cauchy distribution"/>
 				</menu>
-				<menu id="chi_squared_distribution" label="Chi-squared" index="2">
-					<entry component="chi_squared_quantiles" label="Chi-squared quantiles" index="1"/>
-					<entry component="chi_squared_probabilities" label="Chi-squared probabilities" index="0"/>
-					<entry component="plot_chi_squared_clt" label="Chi-squared CLT" index="2"/>
+				<menu id="chi_squared_distribution" label="Chi-squared">
+					<entry component="chi_squared_quantiles" label="Chi-squared quantiles"/>
+					<entry component="chi_squared_probabilities" label="Chi-squared probabilities"/>
+					<entry component="plot_chi_squared_clt" label="Chi-squared CLT"/>
 					<entry component="plot_chi_squared_distribution" label="Plot Chi-squared distribution"/>
 				</menu>
-				<menu id="exponential_distribution" label="Exponential" index="3">
-					<entry component="exponential_quantiles" label="Exponential quantiles" index="1"/>
-					<entry component="exponential_probabilities" label="Exponential probabilities" index="0"/>
-					<entry component="plot_exponential_clt" label="Exponential CLT" index="2"/>
+				<menu id="exponential_distribution" label="Exponential">
+					<entry component="exponential_quantiles" label="Exponential quantiles"/>
+					<entry component="exponential_probabilities" label="Exponential probabilities"/>
+					<entry component="plot_exponential_clt" label="Exponential CLT"/>
 					<entry component="plot_exponential_distribution" label="Plot Exponential distribution"/>
 				</menu>
-				<menu id="f_distribution" label="F" index="4">
-					<entry component="f_quantiles" label="F quantiles" index="1"/>
-					<entry component="f_probabilities" label="F probabilities" index="0"/>
-					<entry component="plot_f_clt" label="F CLT" index="2"/>
+				<menu id="f_distribution" label="F">
+					<entry component="f_quantiles" label="F quantiles"/>
+					<entry component="f_probabilities" label="F probabilities"/>
+					<entry component="plot_f_clt" label="F CLT"/>
 					<entry component="plot_f_distribution" label="Plot F distribution"/>
 				</menu>
-				<menu id="gamma_distribution" label="Gamma" index="5">
-					<entry component="gamma_quantiles" label="Gamma quantiles" index="1"/>
-					<entry component="gamma_probabilities" label="Gamma probabilities" index="0"/>
-					<entry component="plot_gamma_clt" label="Gamma CLT" index="2"/>
+				<menu id="gamma_distribution" label="Gamma">
+					<entry component="gamma_quantiles" label="Gamma quantiles"/>
+					<entry component="gamma_probabilities" label="Gamma probabilities"/>
+					<entry component="plot_gamma_clt" label="Gamma CLT"/>
 					<entry component="plot_gamma_distribution" label="Plot Gamma distribution"/>
 				</menu>
-				<menu id="gumbel_distribution" label="Gumbel" index="6">
-					<entry component="gumbel_quantiles" label="Gumbel quantiles" index="1"/>
-					<entry component="gumbel_probabilities" label="Gumbel probabilities" index="0"/>
+				<menu id="gumbel_distribution" label="Gumbel">
+					<entry component="gumbel_quantiles" label="Gumbel quantiles"/>
+					<entry component="gumbel_probabilities" label="Gumbel probabilities"/>
 				</menu>
-				<menu id="logistic_distribution" label="Logistic" index="7">
-					<entry component="logistic_quantiles" label="Logistic quantiles" index="1"/>
-					<entry component="logistic_probabilities" label="Logistic probabilities" index="0"/>
-					<entry component="plot_logistic_clt" label="Logistic CLT" index="2"/>
+				<menu id="logistic_distribution" label="Logistic">
+					<entry component="logistic_quantiles" label="Logistic quantiles"/>
+					<entry component="logistic_probabilities" label="Logistic probabilities"/>
+					<entry component="plot_logistic_clt" label="Logistic CLT"/>
 					<entry component="plot_logistic_distribution" label="Plot Logistic distribution"/>
 				</menu>
-				<menu id="log_normal_distribution" label="Log Normal" index="8">
-					<entry component="log_normal_quantiles" label="Log Normal quantiles" index="1"/>
-					<entry component="log_normal_probabilities" label="Log Normal probabilities" index="0"/>
-					<entry component="plot_log_normal_clt" label="Log Normal CLT" index="2"/>
+				<menu id="log_normal_distribution" label="Log Normal">
+					<entry component="log_normal_quantiles" label="Log Normal quantiles"/>
+					<entry component="log_normal_probabilities" label="Log Normal probabilities"/>
+					<entry component="plot_log_normal_clt" label="Log Normal CLT"/>
 					<entry component="plot_lognormal_distribution" label="Plot Lognormal distribution"/>
 				</menu>
-				<menu id="normal_distribution" label="Normal" index="9">
-					<entry component="normal_quantiles" label="Normal quantiles" index="1"/>
-					<entry component="normal_probabilities" label="Normal probabilities" index="0"/>
-					<entry component="plot_normal_clt" label="Normal CLT" index="2"/>
+				<menu id="normal_distribution" label="Normal">
+					<entry component="normal_quantiles" label="Normal quantiles"/>
+					<entry component="normal_probabilities" label="Normal probabilities"/>
+					<entry component="plot_normal_clt" label="Normal CLT"/>
 					<entry component="plot_normal_distribution" label="Plot Normal distribution"/>
 				</menu>
-				<menu id="tukey_distribution" label="Tukey" index="11">
-					<entry component="tukey_probabilities" label="Tukey probabilities" index="0"/>
-					<entry component="tukey_quantiles" label="Tukey quantiles" index="1"/>
-					<entry component="plot_tukey_distribution" label="Plot Studentized Range (Tukey) distribution" index="2"/>
+				<menu id="tukey_distribution" label="Tukey">
+					<entry component="tukey_probabilities" label="Tukey probabilities"/>
+					<entry component="tukey_quantiles" label="Tukey quantiles"/>
+					<entry component="plot_tukey_distribution" label="Plot Studentized Range (Tukey) distribution"/>
 				</menu>
-				<menu id="t_distribution" label="t" index="10">
-					<entry component="t_quantiles" label="t quantiles" index="1"/>
-					<entry component="t_probabilities" label="t probabilities" index="0"/>
-					<entry component="plot_t_clt" label="t CLT" index="2"/>
+				<menu id="t_distribution" label="t">
+					<entry component="t_quantiles" label="t quantiles"/>
+					<entry component="t_probabilities" label="t probabilities"/>
+					<entry component="plot_t_clt" label="t CLT"/>
 					<entry component="plot_t_distribution" label="Plot t distribution"/>
 				</menu>
-				<menu id="uniform_distribution" label="Uniform" index="12">
-					<entry component="uniform_quantiles" label="Uniform quantiles" index="1"/>
-					<entry component="uniform_probabilities" label="Uniform probabilities" index="0"/>
-					<entry component="plot_uniform_clt" label="Uniform CLT" index="2"/>
+				<menu id="uniform_distribution" label="Uniform">
+					<entry component="uniform_quantiles" label="Uniform quantiles"/>
+					<entry component="uniform_probabilities" label="Uniform probabilities"/>
+					<entry component="plot_uniform_clt" label="Uniform CLT"/>
 					<entry component="plot_uniform_distribution" label="Plot Uniform distribution"/>
 				</menu>
-				<menu id="weibull_distribution" label="Weibull" index="13">
-					<entry component="weibull_quantiles" label="Weibull quantiles" index="1"/>
-					<entry component="weibull_probabilities" label="Weibull probabilities" index="0"/>
-					<entry component="plot_weibull_clt" label="Weibull CLT" index="2"/>
+				<menu id="weibull_distribution" label="Weibull">
+					<entry component="weibull_quantiles" label="Weibull quantiles"/>
+					<entry component="weibull_probabilities" label="Weibull probabilities"/>
+					<entry component="plot_weibull_clt" label="Weibull CLT"/>
 					<entry component="plot_weibull_distribution" label="Plot Weibull distribution"/>
 				</menu>
 			</menu>
 
-			<menu id="univariate_discrete_distributions" label="Univariate discrete distributions" index="2">
-				<menu id="binomial_distribution" label="Binomial" index="0">
-					<entry component="binomial_quantiles" label="Binomial quantiles" index="1"/>
-					<entry component="binomial_tail_probabilities" label="Binomial tail probabilities" index="0"/>
-					<entry component="plot_binomial_clt" label="Binomial CLT" index="2"/>
+			<menu id="univariate_discrete_distributions" label="Univariate discrete distributions">
+				<menu id="binomial_distribution" label="Binomial">
+					<entry component="binomial_quantiles" label="Binomial quantiles"/>
+					<entry component="binomial_tail_probabilities" label="Binomial tail probabilities"/>
+					<entry component="plot_binomial_clt" label="Binomial CLT"/>
 					<entry component="plot_binomial_distribution" label="Plot binomial distribution"/>
 				</menu>
-				<menu id="geometric_distribution" label="Geometric" index="1">
-					<entry component="geom_probabilities" label="Geometric probabilities" index="0"/>
-					<entry component="geom_quantiles" label="Geometric quantiles" index="1"/>
-					<entry component="plot_geometric_clt" label="Geometric CLT" index="2"/>
+				<menu id="geometric_distribution" label="Geometric">
+					<entry component="geom_probabilities" label="Geometric probabilities"/>
+					<entry component="geom_quantiles" label="Geometric quantiles"/>
+					<entry component="plot_geometric_clt" label="Geometric CLT"/>
 					<entry component="plot_geometric_distribution" label="Plot Geometric distribution"/>
 				</menu>
-				<menu id="hypergeometric_distribution" label="Hypergeometric" index="2">
-					<entry component="hypergeometric_probabilities" label="Hypergeometric probabilities" index="0"/>
-					<entry component="hypergeometric_quantiles" label="Hypergeometric quantiles" index="1"/>
-					<entry component="plot_hypergeometric_clt" label="Hypergeometric CLT" index="2"/>
+				<menu id="hypergeometric_distribution" label="Hypergeometric">
+					<entry component="hypergeometric_probabilities" label="Hypergeometric probabilities"/>
+					<entry component="hypergeometric_quantiles" label="Hypergeometric quantiles"/>
+					<entry component="plot_hypergeometric_clt" label="Hypergeometric CLT"/>
 					<entry component="plot_hypergeometric_distribution" label="Plot Hypergeometric distribution"/>
 				</menu>
-				<menu id="negative_binomial_distribution" label="Negative Binomial" index="3">
-					<entry component="negative_binomial_probabilities" label="Negative Binomial probabilities" index="0"/>
-					<entry component="negative_binomial_quantiles" label="Hypergeometric quantiles" index="1"/>
-					<entry component="plot_negbinomial_clt" label="Negative Binomial CLT" index="2"/>
+				<menu id="negative_binomial_distribution" label="Negative Binomial">
+					<entry component="negative_binomial_probabilities" label="Negative Binomial probabilities"/>
+					<entry component="negative_binomial_quantiles" label="Hypergeometric quantiles"/>
+					<entry component="plot_negbinomial_clt" label="Negative Binomial CLT"/>
 					<entry component="plot_negbinomial_distribution" label="Plot Negative Binomial distribution"/>
 				</menu>
-				<menu id="poisson_distribution" label="Poisson" index="4">
-					<entry component="poisson_probabilities" label="Poisson probabilities" index="0"/>
-					<entry component="poisson_quantiles" label="Poisson quantiles" index="1"/>
-					<entry component="plot_poisson_clt" label="Poisson CLT" index="2"/>
+				<menu id="poisson_distribution" label="Poisson">
+					<entry component="poisson_probabilities" label="Poisson probabilities"/>
+					<entry component="poisson_quantiles" label="Poisson quantiles"/>
+					<entry component="plot_poisson_clt" label="Poisson CLT"/>
 					<entry component="plot_poisson_distribution" label="Plot Poisson distribution"/>
 				</menu>
-				<menu id="wilcoxon_distribution" label="Wilcoxon Rank Sum" index="14">
-					<entry component="wilcoxon_quantiles" label="Wilcoxon Rank Sum quantiles" index="1"/>
-					<entry component="wilcoxon_probabilities" label="Wilcoxon Rank Sum probabilities" index="0"/>
-					<entry component="plot_wilcoxon_clt" label="Wilcoxon CLT" index="2"/>
+				<menu id="wilcoxon_distribution" label="Wilcoxon Rank Sum">
+					<entry component="wilcoxon_quantiles" label="Wilcoxon Rank Sum quantiles"/>
+					<entry component="wilcoxon_probabilities" label="Wilcoxon Rank Sum probabilities"/>
+					<entry component="plot_wilcoxon_clt" label="Wilcoxon CLT"/>
 					<entry component="plot_wilcoxon_distribution" label="Plot Wilcoxon Rank Sum distribution"/>
 				</menu>
 			</menu>
diff --git a/rkward/plugins/embedded.pluginmap b/rkward/plugins/embedded.pluginmap
index cc48200..f0d0cdb 100644
--- a/rkward/plugins/embedded.pluginmap
+++ b/rkward/plugins/embedded.pluginmap
@@ -2,6 +2,7 @@
 
 <document base_prefix="" namespace="rkward" id="embedded">
 	<include file="pluginmap_meta.inc"/>
+	<require file="menu.pluginmap"/>
 
 	<components>
 		<!-- These are meant to be embedded, and are not shown in the menu -->
diff --git a/rkward/plugins/import_export.pluginmap b/rkward/plugins/import_export.pluginmap
index 1513174..034a38d 100644
--- a/rkward/plugins/import_export.pluginmap
+++ b/rkward/plugins/import_export.pluginmap
@@ -2,6 +2,7 @@
 
 <document base_prefix="00saveload/" namespace="rkward" id="import_export">
 	<include file="pluginmap_meta.inc"/>
+	<require file="../menu.pluginmap"/>
 
 	<components>
 		<component type="standard" id="load_r_object" file="import/load_data.xml" label="Load R data file" />
@@ -29,7 +30,7 @@
 
 	<hierarchy>
 		<menu id="file" label="File">
-			<menu id="import" label="Import" index="4">
+			<menu id="import" label="Import">
 				<menu id="import_format" label="Import format">
 					<entry component="import_spss"/>
 					<entry component="import_stata"/>
@@ -38,14 +39,14 @@
 				<entry component="load_r_object" />
 				<entry component="load_source" />
 			</menu>
-			<menu id="export" label="Export" index="5">
+			<menu id="export" label="Export">
 				<entry component="save_r" />
 				<entry component="save_variables" />
 				<entry component="save_table" />
 				<entry component="save_skeleton" />
 			</menu>
 		</menu>
-		<menu id="workspace" label="Workspace" index="5">
+		<menu id="workspace" label="Workspace">
 			<entry component="setworkdir" />
 		</menu>
 	</hierarchy>
diff --git a/rkward/plugins/irt.pluginmap b/rkward/plugins/irt.pluginmap
index ebf144d..f69a852 100644
--- a/rkward/plugins/irt.pluginmap
+++ b/rkward/plugins/irt.pluginmap
@@ -2,6 +2,7 @@
 
 <document base_prefix="" namespace="rkward" id="irt">
 	<include file="pluginmap_meta.inc"/>
+	<require file="menu.pluginmap"/>
 
 	<components>
 		<component type="standard" id="par_est_rasch" file="analysis/irt/dichotomous/par_est_rasch.xml" label="Rasch model fit" />
@@ -35,21 +36,15 @@
 	</components>
 
 	<hierarchy>
-		<menu id="file" label="File">
-			<menu id="import" label="Import" index="4">
-			</menu>
-			<menu id="export" label="Export" index="5">
-			</menu>
-		</menu>
-		<menu id="analysis" label="Analysis" index="4">
-			<menu id="menu_irt" label="Item Response Theory" index="4">
-      				<menu id="menu_dichot" label="Dichotomous data" index="1">
+		<menu id="analysis" label="Analysis">
+			<menu id="menu_irt" label="Item Response Theory">
+      				<menu id="menu_dichot" label="Dichotomous data">
         				<entry component="par_est_rasch" />
         				<entry component="par_est_2pl" />
         				<entry component="par_est_3pl" />
         				<entry component="par_est_lltm" />
       				</menu>
-      				<menu id="menu_poly" label="Polytomous data" index="2">
+      				<menu id="menu_poly" label="Polytomous data">
         				<entry component="par_est_grm" />
         				<entry component="par_est_rsm" />
         				<entry component="par_est_pcm" />
@@ -57,7 +52,7 @@
         				<entry component="par_est_lrsm" />
         				<entry component="par_est_lpcm" />
       				</menu>
-      				<menu id="menu_tests" label="Tests" index="3">
+      				<menu id="menu_tests" label="Tests">
         				<entry component="ltm_gof_rasch" />
         				<entry component="ltm_unidimensional" />
         				<entry component="ltm_item_fit" />
@@ -68,14 +63,14 @@
       				<entry component="ltm_cronbach_alpha" />
     			</menu>
 		</menu>
-		<menu id="plots" label="Plots" index="5">
-    			<menu id="menu_plot_irt" label="Item Response Theory" index="8">
-      				<menu id="menu_plot_dichot" label="Dichotomous data" index="1">
+		<menu id="plots" label="Plots">
+    			<menu id="menu_plot_irt" label="Item Response Theory">
+      				<menu id="menu_plot_dichot" label="Dichotomous data">
         				<entry component="plot_rasch" />
         				<entry component="plot_ltm" />
         				<entry component="plot_tpm" />
       				</menu>
-      				<menu id="menu_plot_poly" label="Polytomous data" index="2">
+      				<menu id="menu_plot_poly" label="Polytomous data">
         				<entry component="plot_grm" />
         				<entry component="plot_rsm" />
         				<entry component="plot_pcm" />
diff --git a/rkward/plugins/menu.pluginmap b/rkward/plugins/menu.pluginmap
new file mode 100644
index 0000000..ff33602
--- /dev/null
+++ b/rkward/plugins/menu.pluginmap
@@ -0,0 +1,23 @@
+<!DOCTYPE rkpluginmap>
+<!-- The point of this pluginmap is to pre-define the layout (and labels) of some menus -->
+<document base_prefix="" namespace="rkward" id="rkward_menu_hierarchy">
+	<include file="pluginmap_meta.inc"/>
+	<hierarchy>
+		<!-- Define a group before and after each top-level menu to allow inserting top-level menus at defined positions -->
+		<group id="before_data"/>
+		<!-- Define a group for each menu to suppress alphabetical sorting of the top-level menus -->
+		<menu id="data" label="Data" group="data"/>
+		<group id="after_data"/>
+		<group id="before_analysis"/>
+		<menu id="analysis" label="Analysis" group="analysis"/>
+		<group id="after_analysis"/>
+		<group id="before_plots"/>
+		<menu id="plots" label="Plots" group="plots"/>
+		<group id="after_plots"/>
+		<group id="before_distributions"/>
+		<menu id="distributions" label="Distributions" group="distributions"/>
+		<group id="after_distributions"/>
+		<menu id="empty" label="empty"/>
+	</hierarchy>
+</document>
+ 
diff --git a/rkward/plugins/plots.pluginmap b/rkward/plugins/plots.pluginmap
index 93eaea4..1a3f4ea 100644
--- a/rkward/plugins/plots.pluginmap
+++ b/rkward/plugins/plots.pluginmap
@@ -2,6 +2,7 @@
 
 <document base_prefix="plots/" namespace="rkward" id="plots">
 	<include file="pluginmap_meta.inc"/>
+	<require file="../menu.pluginmap"/>
 
 	<components>
 		<component type="standard" id="barplot" file="barplot.xml" label="Barplot" />
@@ -20,7 +21,7 @@
 	</components>
 
 	<hierarchy>
-		<menu id="plots" label="Plots" index="5">
+		<menu id="plots" label="Plots">
 			<entry component="barplot" />
 			<entry component="box_plot" />
 			<entry component="density_plot" />
diff --git a/rkward/plugins/under_development.pluginmap b/rkward/plugins/under_development.pluginmap
index 7bfec2d..c970b91 100644
--- a/rkward/plugins/under_development.pluginmap
+++ b/rkward/plugins/under_development.pluginmap
@@ -2,6 +2,7 @@
 
 <document base_prefix="" namespace="rkward">
 	<include file="pluginmap_meta.inc"/>
+	<require file="menu.pluginmap"/>
 
 	<components>
 		<component type="standard" id="simple_anova" file="simple_anova/description.xml" label="Simple Anova" />
@@ -23,30 +24,33 @@
 
 	<hierarchy>
 		<menu id="file" label="File">
-			<menu id="import" label="Import" index="4">
+			<menu id="import" label="Import">
 				<menu id="import_format" label="Import format">
 					<entry component="import_xls"/>
 				</menu>
 			</menu>
-			<menu id="export" label="Export" index="5">
+			<menu id="export" label="Export">
 			</menu>
 		</menu>
-		<menu id="data" label="Data" index="3">
-			<entry component="generate_random"/>
-			<entry component="sort_data"/>
-			<entry component="sort_data2"/>
+		<menu id="data" label="Data">
+			<group id="testing" group="bottom" separated="true"/>
+			<entry component="generate_random" group="testing"/>
+			<entry component="sort_data" group="testing"/>
+			<entry component="sort_data2" group="testing"/>
 		</menu>
-		<menu id="analysis" label="Analysis" index="4">
-			<entry component="simple_anova" index="9"/>
-			<entry component="qtscript_test1" index="1"/>
-			<entry component="optionset_test" index="1"/>
-			<entry component="matrix_test1" index="1"/>
-			<entry component="valueselect_test1" index="1"/>
+		<menu id="analysis" label="Analysis">
+			<group id="testing" group="bottom" separated="true"/>
+			<entry component="simple_anova" group="testing"/>
+			<entry component="qtscript_test1" group="testing"/>
+			<entry component="optionset_test" group="testing"/>
+			<entry component="matrix_test1" group="testing"/>
+			<entry component="valueselect_test1" group="testing"/>
 		</menu>
-		<menu id="plots" label="Plots" index="5">
-			<entry component="sieve_plot" />
+		<menu id="plots" label="Plots">
+			<group id="testing" group="bottom" separated="true"/>
+			<entry component="sieve_plot" group="testing"/>
 		</menu>
-		<menu id="distributions" label="Distributions" index="7">
+		<menu id="distributions" label="Distributions">
 		</menu>
 	</hierarchy>
 
diff --git a/rkward/plugins/x11device.pluginmap b/rkward/plugins/x11device.pluginmap
index 857dac6..d856441 100644
--- a/rkward/plugins/x11device.pluginmap
+++ b/rkward/plugins/x11device.pluginmap
@@ -8,10 +8,10 @@
 	</components>
 
 	<context id="x11">
-		<menu id="device" label="Device" index="1">
+		<menu id="device" label="Device">
 			<entry component="export_x11_device" />
 		</menu>
-		<menu id="edit" label="Edit" index="3">
+		<menu id="edit" label="Edit">
 			<entry component="x11grid" />
 		</menu>
 	</context>
diff --git a/rkward/rkward.cpp b/rkward/rkward.cpp
index d8e2b39..e73cb08 100644
--- a/rkward/rkward.cpp
+++ b/rkward/rkward.cpp
@@ -307,6 +307,7 @@ void RKWardMainWindow::initPlugins (const QStringList &automatically_added) {
 		}
 	}
 
+	RKComponentMap::getMap ()->finalizeAll ();
 	factory ()->addClient (RKComponentMap::getMap ());
 
 	if (!automatically_added.isEmpty ()) {





More information about the rkward-tracker mailing list