changeset 8870:c26fb37899ca

Introduced groups for modules. Modules marked with the same group-id, will be put together in the ui. Also using now the localization info from the server instead of localizing the modules again on the client side.
author gernotbelger
date Wed, 07 Feb 2018 11:59:13 +0100 (2018-02-07)
parents 20b85ea3b524
children 78cd6572778d
files artifacts/doc/conf/modules.xml artifacts/src/main/java/org/dive4elements/river/artifacts/context/RiverContextFactory.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/Module.java artifacts/src/main/java/org/dive4elements/river/artifacts/services/ModuleService.java artifacts/src/main/resources/messages.properties artifacts/src/main/resources/messages_de.properties artifacts/src/main/resources/messages_de_DE.properties artifacts/src/main/resources/messages_en.properties gwt-client/src/main/java/org/dive4elements/river/client/client/ui/MapSelection.java gwt-client/src/main/java/org/dive4elements/river/client/client/ui/ModuleSelection.java gwt-client/src/main/java/org/dive4elements/river/client/client/ui/ParameterList.java gwt-client/src/main/java/org/dive4elements/river/client/server/ModuleServiceImpl.java gwt-client/src/main/java/org/dive4elements/river/client/shared/model/DefaultModule.java gwt-client/src/main/java/org/dive4elements/river/client/shared/model/Module.java
diffstat 14 files changed, 312 insertions(+), 178 deletions(-) [+]
line wrap: on
line diff
--- a/artifacts/doc/conf/modules.xml	Wed Feb 07 11:52:04 2018 +0100
+++ b/artifacts/doc/conf/modules.xml	Wed Feb 07 11:59:13 2018 +0100
@@ -3,19 +3,19 @@
   <module name="winfo" selected="true">
     <river name="Beispielfluss"/>
   </module>
-  <module name="sinfo">
-    <river name="Beispielfluss"/>
-  </module>
-  <module name="minfo">
+  <module name="sinfo" group="module.group.ingwerds">
     <river name="Beispielfluss"/>
   </module>
-  <module name="new_map">
+  <module name="minfo" group="module.group.ingwerds">
     <river name="Beispielfluss"/>
   </module>
-  <module name="new_chart">
+  <module name="new_map" group="module.group.bottom">
     <river name="Beispielfluss"/>
   </module>
-  <module name="fixanalysis">
+  <module name="new_chart" group="module.group.bottom">
+    <river name="Beispielfluss"/>
+  </module>
+  <module name="fixanalysis" group="module.group.bottom">
     <river name="Beispielfluss"/>
   </module>
 </modules>
\ No newline at end of file
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/context/RiverContextFactory.java	Wed Feb 07 11:52:04 2018 +0100
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/context/RiverContextFactory.java	Wed Feb 07 11:59:13 2018 +0100
@@ -617,30 +617,34 @@
         NodeList modulenodes = (NodeList) XMLUtils.xpath(
             cfg, XPATH_MODULES, XPathConstants.NODESET);
 
-        int num = modulenodes != null ? modulenodes.getLength() : 0;
-        ArrayList<Module> modules = new ArrayList<Module>(num);
+        final int num = modulenodes != null ? modulenodes.getLength() : 0;
+
+        final List<Module> modules = new ArrayList<>(num);
 
         for (int i = 0; i < num; i++) {
-            Element e = (Element) modulenodes.item(i);
-            String modulename = e.getAttribute("name");
-            String attrselected = e.getAttribute("selected");
-            boolean selected = attrselected == null ? false :
-                attrselected.equalsIgnoreCase("true");
+            final Element e = (Element) modulenodes.item(i);
+            final String modulename = e.getAttribute("name");
+            final String attrselected = e.getAttribute("selected");
+            final boolean selected = Boolean.parseBoolean(attrselected);
+            final String group = e.getAttribute("group");
+            
             log.debug("Loaded module " + modulename);
-            NodeList children = e.getChildNodes();
-            List<String> rivers = new ArrayList<String>(children.getLength());
+            
+            final NodeList children = e.getChildNodes();
+            final List<String> rivers = new ArrayList<>(children.getLength());
             for (int j = 0; j < children.getLength(); j++) {
                 if (children.item(j).getNodeType() != Node.ELEMENT_NODE) {
                     continue;
                 }
-                Element ce = (Element)children.item(j);
+                
+                final Element ce = (Element)children.item(j);
                 if (ce.hasAttribute("uuid")) {
                     rivers.add(ce.getAttribute("uuid"));
                 }
                 else if (ce.hasAttribute("name")) {
-                    List<River> allRivers = RiverFactory.getRivers();
-                    String name = ce.getAttribute("name");
-                    for (River r: allRivers) {
+                    final List<River> allRivers = RiverFactory.getRivers();
+                    final String name = ce.getAttribute("name");
+                    for (final River r: allRivers) {
                         if (name.equals(r.getName())) {
                             rivers.add(r.getModelUuid());
                             break;
@@ -648,7 +652,7 @@
                     }
                 }
             }
-            modules.add(new Module(modulename, selected, rivers));
+            modules.add(new Module(modulename, selected, group, rivers));
         }
         context.put(RiverContext.MODULES, modules);
     }
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/Module.java	Wed Feb 07 11:52:04 2018 +0100
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/Module.java	Wed Feb 07 11:59:13 2018 +0100
@@ -15,12 +15,21 @@
  */
 public class Module {
 
-    private String name;
-    List<String> rivers;
-    private boolean selected;
+    private final String name;
 
-    public Module(String name, boolean selected, List<String> rivers) {
+    /** 
+     * Defines an (optional) group name for a module. Modules with the same group name are considered to be in the same group
+     * This can be uised to group modules in the front-end  
+     */
+    private final String group;
+
+    private final boolean selected;
+
+    private final List<String> rivers;
+
+    public Module(final String name, final boolean selected, final String group, final List<String> rivers) {
         this.name = name;
+        this.group = group;
         this.rivers = rivers;
         this.selected = selected;
     }
@@ -32,6 +41,10 @@
     public boolean isSelected() {
         return this.selected;
     }
+    
+    public String getGroup() {
+        return this.group;
+    }
 
     public List<String> getRivers() {
         return this.rivers;
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/services/ModuleService.java	Wed Feb 07 11:52:04 2018 +0100
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/services/ModuleService.java	Wed Feb 07 11:59:13 2018 +0100
@@ -30,6 +30,7 @@
 
     private static Logger log = Logger.getLogger(ModuleService.class);
 
+    @Override
     protected Document doProcess(
         Document      data,
         GlobalContext globalContext,
@@ -37,26 +38,25 @@
     ) {
         log.debug("ModuleService.process");
 
-        Document result = XMLUtils.newDocument();
+        final Document result = XMLUtils.newDocument();
 
-        XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
+        final XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
             result,
             ArtifactNamespaceContext.NAMESPACE_URI,
             ArtifactNamespaceContext.NAMESPACE_PREFIX);
 
-        Element em = ec.create("modules");
-        List<Module> modules = (List<Module>)globalContext.get(
-            RiverContext.MODULES);
+        final Element em = ec.create("modules");
+        final List<Module> modules = (List<Module>)globalContext.get( RiverContext.MODULES);
 
-        for (Module module : modules) {
-            Element m = ec.create("module");
+        for (final Module module : modules) {
+            final Element m = ec.create("module");
             ec.addAttr(m, "name", module.getName(), true);
-            String localname = Resources.getMsg(callMeta,
-                    MODULE + "." + module.getName(), module.getName());
+
+            final String localname = Resources.getMsg(callMeta, MODULE + "." + module.getName(), module.getName());
             ec.addAttr(m, "localname", localname, true);
 
-            for (String river : module.getRivers()) {
-                Element r = ec.create("river");
+            for (final String river : module.getRivers()) {
+                final Element r = ec.create("river");
                 r.setAttribute("uuid", river);
                 m.appendChild(r);
             }
@@ -64,6 +64,13 @@
                 ec.addAttr(m, "selected", "true", true);
             }
 
+            final String group = module.getGroup();
+            if( group != null ) {
+                final String groupLabel = Resources.getMsg(callMeta, group);
+                ec.addAttr(m, "groupId", group, true);
+                ec.addAttr(m, "groupLabel", groupLabel, true);
+            }
+
             em.appendChild(m);
         }
 
--- a/artifacts/src/main/resources/messages.properties	Wed Feb 07 11:52:04 2018 +0100
+++ b/artifacts/src/main/resources/messages.properties	Wed Feb 07 11:59:13 2018 +0100
@@ -762,6 +762,10 @@
 help.state.fix.vollmer.qs=${help.url}/OnlineHilfe/Fixierungsanalyse#help.state.fix.vollmer.qs
 help.state.fix.vollmer.compute=${help.url}/OnlineHilfe/Fixierungsanalyse#help.state.fix.vollmer.compute
 
+module.group.ingwerds = INGwerDs
+# empty label, so it does not get a group-frame in the ui 
+module.group.bottom = 
+
 state.sinfo.river = River
 state.sinfo.calculation_mode=Calculation Mode
 
--- a/artifacts/src/main/resources/messages_de.properties	Wed Feb 07 11:52:04 2018 +0100
+++ b/artifacts/src/main/resources/messages_de.properties	Wed Feb 07 11:59:13 2018 +0100
@@ -768,6 +768,10 @@
 help.state.fix.vollmer.qs=${help.url}/OnlineHilfe/Fixierungsanalyse#help.state.fix.vollmer.qs
 help.state.fix.vollmer.compute=${help.url}/OnlineHilfe/Fixierungsanalyse#help.state.fix.vollmer.compute
 
+module.group.ingwerds = INGwerDs
+# empty label, so it does not get a group-frame in the ui 
+module.group.bottom = 
+
 state.sinfo.river = Gew\u00e4sser
 state.sinfo.calculation_mode=Berechnungsart
 
--- a/artifacts/src/main/resources/messages_de_DE.properties	Wed Feb 07 11:52:04 2018 +0100
+++ b/artifacts/src/main/resources/messages_de_DE.properties	Wed Feb 07 11:59:13 2018 +0100
@@ -764,6 +764,10 @@
 help.state.fix.vollmer.qs=${help.url}/OnlineHilfe/Fixierungsanalyse#help.state.fix.vollmer.qs
 help.state.fix.vollmer.compute=${help.url}/OnlineHilfe/Fixierungsanalyse#help.state.fix.vollmer.compute
 
+module.group.ingwerds = INGwerDs
+# empty label, so it does not get a group-frame in the ui 
+module.group.bottom = 
+
 state.sinfo.river = Gew\u00e4sser
 state.sinfo.calculation_mode=Berechnungsart
 
--- a/artifacts/src/main/resources/messages_en.properties	Wed Feb 07 11:52:04 2018 +0100
+++ b/artifacts/src/main/resources/messages_en.properties	Wed Feb 07 11:59:13 2018 +0100
@@ -763,6 +763,10 @@
 help.state.fix.vollmer.qs=${help.url}/OnlineHilfe/Fixierungsanalyse#help.state.fix.vollmer.qs
 help.state.fix.vollmer.compute=${help.url}/OnlineHilfe/Fixierungsanalyse#help.state.fix.vollmer.compute
 
+module.group.ingwerds = INGwerDs
+# empty label, so it does not get a group-frame in the ui 
+module.group.bottom = 
+
 state.sinfo.river = River
 state.sinfo.calculation_mode=Calculation Mode
 
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/MapSelection.java	Wed Feb 07 11:52:04 2018 +0100
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/MapSelection.java	Wed Feb 07 11:59:13 2018 +0100
@@ -29,7 +29,7 @@
 
     private static final long serialVersionUID = 1261822454641198692L;
 
-    protected ModuleSelection moduleSelection;
+    private ModuleSelection moduleSelection;
 
     public MapSelection() {
     }
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/ModuleSelection.java	Wed Feb 07 11:52:04 2018 +0100
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/ModuleSelection.java	Wed Feb 07 11:59:13 2018 +0100
@@ -8,19 +8,13 @@
 
 package org.dive4elements.river.client.client.ui;
 
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.user.client.rpc.AsyncCallback;
-
-import com.smartgwt.client.types.VerticalAlignment;
-import com.smartgwt.client.util.SC;
-import com.smartgwt.client.widgets.Canvas;
-import com.smartgwt.client.widgets.Label;
-import com.smartgwt.client.widgets.form.DynamicForm;
-import com.smartgwt.client.widgets.form.fields.RadioGroupItem;
-import com.smartgwt.client.widgets.form.fields.events.ChangeEvent;
-import com.smartgwt.client.widgets.form.fields.events.ChangeHandler;
-import com.smartgwt.client.widgets.layout.HLayout;
-import com.smartgwt.client.widgets.layout.VLayout;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
 
 import org.dive4elements.river.client.client.Config;
 import org.dive4elements.river.client.client.FLYSConstants;
@@ -32,10 +26,21 @@
 import org.dive4elements.river.client.shared.model.DefaultData;
 import org.dive4elements.river.client.shared.model.DefaultDataItem;
 import org.dive4elements.river.client.shared.model.Module;
+import org.dive4elements.river.client.shared.model.ModuleGroup;
 
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.smartgwt.client.types.VerticalAlignment;
+import com.smartgwt.client.util.SC;
+import com.smartgwt.client.widgets.Canvas;
+import com.smartgwt.client.widgets.Label;
+import com.smartgwt.client.widgets.form.DynamicForm;
+import com.smartgwt.client.widgets.form.fields.RadioGroupItem;
+import com.smartgwt.client.widgets.form.fields.events.ChangeEvent;
+import com.smartgwt.client.widgets.form.fields.events.ChangeHandler;
+import com.smartgwt.client.widgets.layout.HLayout;
+import com.smartgwt.client.widgets.layout.LayoutSpacer;
+import com.smartgwt.client.widgets.layout.VLayout;
 
 /**
  * The ModuleSelection combines the river selection and the module selection in
@@ -50,32 +55,34 @@
     private static final long serialVersionUID = -5634831815175543328L;
 
     /** The message class that provides i18n strings.*/
-    protected FLYSConstants MESSAGES = GWT.create(FLYSConstants.class);
-
-    /** The module checkboxes.*/
-    protected static RadioGroupItem radio;
-
-    /** */
-    protected Module[] modules;
+    private FLYSConstants MESSAGES = GWT.create(FLYSConstants.class);
 
     /** The ModuleService used to retrieve the available modules of a user.*/
-    protected ModuleServiceAsync moduleService = GWT.create(
-        ModuleService.class);
-
-    private Map<String, List<String> > modulesRiverMap;
-    protected Map<String, HLayout> rivers;
+    private ModuleServiceAsync moduleService = GWT.create(ModuleService.class);
 
-    /**
-     * The default constructor.
-     */
+    private Map<String, List<String> > modulesRiverMap = new LinkedHashMap<String, List<String> >();
+
+    private Map<String, Module> modulesByName = new HashMap<String, Module>();
+
+    private Map<ModuleGroup, List<Module>> modulesByGroup = new LinkedHashMap<ModuleGroup, List<Module>>();
+
+    private Map<ModuleGroup, RadioGroupItem> groupToRadios = new LinkedHashMap<ModuleGroup, RadioGroupItem>();
+
+    private Map<ModuleGroup, Canvas> groupToCanvas = new LinkedHashMap<ModuleGroup, Canvas>();
+
+    private Map<String, HLayout> rivers = null;
+
+    private VLayout radioPanel;
+
+    /* TODO: seems that it needs to be static for the callback, is this really necessary? 
+     * TODO: what happens if we have several 'new project' windows open in parallel?
+     * */
+    private static Module selectedModule = null;
+
     public ModuleSelection() {
-        rivers = null;
-        modulesRiverMap = new LinkedHashMap<String, List<String> >();
-
         readModules();
     }
 
-
     /**
      * This method returns a widget that renders the checkboxes for each module
      * and the MapSelection that lets the user choose the river.
@@ -114,59 +121,54 @@
             @Override
             public void onSuccess(Module[] newmodules) {
                 GWT.log("Retrieved " + newmodules.length + " modules.");
-                modules = newmodules;
-                setModules();
+                setModules(newmodules);
+                updateRadioPanels();
             }
         });
     }
 
+    // TODO: bad. Too much knowledge spread over the application.
+    // TODO: instead, e.g. use a controller that knows both (ModuleSelection and LinkSelection) and let him do this kind of things 
     private void checkRivers(String selected) {
-        if (selected == null) {
-            selected = getSelectedModule();
-        }
-        if (rivers != null
-            && !rivers.isEmpty()
-            && modules != null
-            && selected != null
-        ) {
-            List<String> allowedRivers = modulesRiverMap.get(selected);
-            if ( allowedRivers == null ) {
-                GWT.log("No configured rivers for module: " + selected);
-            }
-            for (Map.Entry<String, HLayout> s: rivers.entrySet()) {
-                if ( allowedRivers == null ) {
-                    s.getValue().hide();
-                    continue;
-                }
-                if (!allowedRivers.contains(s.getKey())) {
-                    s.getValue().hide();
-                } else {
-                    s.getValue().show();
-                }
+        if (rivers == null || rivers.isEmpty() || /*modules == null || */ selected == null ) 
+            return;
+        
+        final List<String> allowedRivers = modulesRiverMap.get(selected);
+        if ( allowedRivers == null ) 
+            GWT.log("No configured rivers for module: " + selected);
+
+        for (final Map.Entry<String, HLayout> s: rivers.entrySet()) {
+            if (allowedRivers == null || !allowedRivers.contains(s.getKey())) {
+                s.getValue().hide();
+            } else {
+                s.getValue().show();
             }
         }
     }
-
-    private void setModules() {
-        LinkedHashMap<String, String> values =
-            new LinkedHashMap<String, String>();
+    
+    protected final void setModules(Module[] newmodules) {
+        modulesRiverMap.clear();
+        
+        if( newmodules == null )
+            return;
+                
+        for(final Module module : newmodules) {
+            final String name = module.getName();
+            
+            /* remember rivers per module */
+            modulesRiverMap.put(name, module.getRivers());
 
-        if (this.modules!= null) {
-            for(Module module : this.modules) {
-                values.put(module.getName(), module.getLocalizedName());
-                if (module.isSelected()) {
-                    GWT.log("Module " + module.getName() + " is selected.");
-                    if (radio != null) {
-                        radio.setDefaultValue(module.getName());
-                    }
-                }
-                modulesRiverMap.put(module.getName(), module.getRivers());
-            }
+            /* hash by name */
+            modulesByName.put(name, module);
+            
+            /* hash by group  */
+            final ModuleGroup group = module.getGroup();
+            if( !modulesByGroup.containsKey( group ) )
+                modulesByGroup.put(group, new LinkedList<Module>());
+            
+            final List<Module> modulesGroup = modulesByGroup.get(group);
+            modulesGroup.add(module);
         }
-        if (radio != null) {
-            radio.setValueMap(values);
-        }
-        checkRivers(null);
     }
 
     /**
@@ -175,36 +177,119 @@
      * @return a widget with checkboxes.
      */
     protected Canvas createWidget() {
-        HLayout layout = new HLayout();
-
-        Label      label = new Label(MESSAGES.module_selection());
-        DynamicForm form = new DynamicForm();
+        
+        final HLayout layout = new HLayout();
+        
+        radioPanel = new VLayout();
 
-        radio = new RadioGroupItem("plugin");
-        radio.addChangeHandler(new ChangeHandler() {
-            @Override
-            public void onChange(ChangeEvent event) {
-                checkRivers((String)event.getValue());
-            }
-        });
-
+        final Label label = new Label(MESSAGES.module_selection());
         label.setWidth(50);
         label.setHeight(25);
 
-
-        radio.setShowTitle(false);
-        radio.setVertical(true);
-
-        setModules();
-
-        form.setFields(radio);
-
         layout.addMember(label);
-        layout.addMember(form);
-
+        layout.addMember(radioPanel);
+        
+        updateRadioPanels();
+        
         return layout;
     }
 
+    protected final void updateRadioPanels() {
+        
+        /* first clear existing panels, if any exist */
+        final Collection<Canvas> oldforms = groupToCanvas.values();
+        for (Canvas oldCanvas : oldforms) {
+            radioPanel.removeMember(oldCanvas);
+            oldCanvas.destroy();
+        }
+
+        groupToCanvas.clear();
+        groupToRadios.clear();
+        selectedModule = null;
+        
+        if( radioPanel == null )
+            return;
+        
+        /* now create radio items per group */
+        int count = 0;
+        for (final Entry<ModuleGroup, List<Module>> groupEntry : modulesByGroup.entrySet()) {
+            
+            final  ModuleGroup group = groupEntry.getKey();
+            final List<Module> groupModule = groupEntry.getValue();
+            
+            final HLayout groupPanel = new HLayout();
+
+            if( group.showGroupFrame() )
+            {
+                final LayoutSpacer spacer = new LayoutSpacer();
+                spacer.setWidth(25);
+                groupPanel.addMember(spacer);
+
+                final Label label = new Label(group.toString());
+                label.setWidth(25);
+                label.setHeight(25);
+                groupPanel.addMember(label);
+            }
+            
+            final DynamicForm groupForm = new DynamicForm();
+
+            final RadioGroupItem moduleRadio = new RadioGroupItem("modulegroup" + count++);
+            moduleRadio.setShowTitle(false);
+            moduleRadio.setVertical(true);
+
+            moduleRadio.addChangeHandler(new ChangeHandler() {
+                @Override
+                public void onChange(ChangeEvent event) {
+                    final String selectedModuleName = (String) event.getValue();
+                    handleModuleSelected(selectedModuleName);
+                }
+            });
+            
+            groupForm.setItems(moduleRadio);
+            groupPanel.addMember(groupForm);
+
+            final LinkedHashMap<String, String> values = new LinkedHashMap<String, String>();
+            for(final Module module : groupModule) {
+                values.put(module.getName(), module.getLocalizedName());
+                if (module.isSelected()) {
+                    GWT.log("Module " + module.getName() + " is selected.");
+                    selectedModule = module;
+                    moduleRadio.setValue(module.getName());
+                }
+            }
+            
+            this.groupToRadios.put( group, moduleRadio );
+            this.groupToCanvas.put( group, groupPanel );
+
+            moduleRadio.setValueMap(values);
+            
+            
+            radioPanel.addMember(groupPanel);
+        }
+        
+        checkRivers(getSelectedModule());
+    }
+
+    protected void handleModuleSelected(final String selectedModuleName) {
+  
+        /* remember selected module for later */
+        selectedModule = modulesByName.get(selectedModuleName);
+        
+        /* because radios might in different radio-groups, we need to de-select them manually */
+        final ModuleGroup group = selectedModule.getGroup();
+        
+        for (final Entry<ModuleGroup, RadioGroupItem> entry : groupToRadios.entrySet()) {
+            final ModuleGroup radioGroup = entry.getKey();
+
+            if( !group.equals(radioGroup))
+            {
+                final RadioGroupItem groupRadio = entry.getValue();
+                groupRadio.setValue((String)null);
+            }
+        }
+        
+        checkRivers(selectedModuleName);
+    }
 
     /**
      * This method prepares the data of two widgets - the module selection and
@@ -217,13 +302,11 @@
     @Override
     protected Data[] getData() {
 
-        String module = radio.getValueAsString();
+        final Module module = selectedModule;
 
-        DataItem[] items = new DefaultDataItem[1];
-        items[0]         = new DefaultDataItem(module, module, module);
+        final DataItem[] items = new DefaultDataItem[] { new DefaultDataItem(module.getLocalizedName(), module.getLocalizedName(), module.getName()) };
 
-        Data       data  = new DefaultData("module", null, null, items);
-
+        final Data data  = new DefaultData("module", null, null, items);
         return new Data[] {data};
     }
 
@@ -236,10 +319,10 @@
     }-*/;
 
     private static String getSelectedModule() {
-        if (radio == null) {
+        if (selectedModule == null) {
             return null;
         }
-        return radio.getValueAsString();
+        return selectedModule.getName();
     }
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/ParameterList.java	Wed Feb 07 11:52:04 2018 +0100
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/ParameterList.java	Wed Feb 07 11:59:13 2018 +0100
@@ -379,8 +379,7 @@
                     unlockUI();
                     return;
                 }
-
-                String newTitle = MSG.getString(module);
+                final String newTitle = moduleItems[0].getLabel();
                 setTitle(newTitle);
 
                 Config config       = Config.getInstance();
--- a/gwt-client/src/main/java/org/dive4elements/river/client/server/ModuleServiceImpl.java	Wed Feb 07 11:52:04 2018 +0100
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/server/ModuleServiceImpl.java	Wed Feb 07 11:59:13 2018 +0100
@@ -42,29 +42,29 @@
         "error_no_module_found";
 
     @Override
-    public Module[] list(String locale) throws ServerException {
-        User user = this.getUser();
+    public Module[] list(final String locale) throws ServerException {
+        final User user = this.getUser();
 
         log.info("ModuleService.list");
 
-        String url = getServletContext().getInitParameter("server-url");
+        final String url = getServletContext().getInitParameter("server-url");
 
         // create dummy xml
-        Document doc = XMLUtils.newDocument();
+        final Document doc = XMLUtils.newDocument();
 
-        XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
+        final XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
             doc,
             ArtifactNamespaceContext.NAMESPACE_URI,
             ArtifactNamespaceContext.NAMESPACE_PREFIX);
 
-        Element dummy = ec.create("modules");
+        final Element dummy = ec.create("modules");
         doc.appendChild(dummy);
 
-        HttpClient client = new HttpClientImpl(url, locale);
+        final HttpClient client = new HttpClientImpl(url, locale);
         try {
-            Document result = client.callService(url, "modules", doc);
+            final Document result = client.callService(url, "modules", doc);
 
-            NodeList list = (NodeList) XMLUtils.xpath(
+            final NodeList list = (NodeList) XMLUtils.xpath(
                 result,
                 XPATH_MODULES,
                 XPathConstants.NODESET,
@@ -76,29 +76,27 @@
                 throw new ServerException(ERROR_NO_MODULES_FOUND);
             }
 
-            int num = list.getLength();
+            final int num = list.getLength();
 
-            List<Module> modules = new ArrayList<Module>(list.getLength());
+            final List<Module> modules = new ArrayList<Module>(list.getLength());
             for(int i =0; i < num; i++) {
-                Element em = (Element)list.item(i);
-                String name = em.getAttributeNS(
-                        ArtifactNamespaceContext.NAMESPACE_URI, "name");
-                String localname = em.getAttributeNS(
-                        ArtifactNamespaceContext.NAMESPACE_URI, "localname");
-                String strselected = em.getAttributeNS(
-                        ArtifactNamespaceContext.NAMESPACE_URI, "selected");
-                boolean selected = strselected == null ? false :
-                        strselected.equalsIgnoreCase("true");
-                NodeList rivers = em.getChildNodes();
-                List<String> riverUuids = new ArrayList<String>();
+                final Element em = (Element)list.item(i);
+                final String name = em.getAttributeNS(ArtifactNamespaceContext.NAMESPACE_URI, "name");
+                final String localname = em.getAttributeNS(ArtifactNamespaceContext.NAMESPACE_URI, "localname");
+                final String strselected = em.getAttributeNS(ArtifactNamespaceContext.NAMESPACE_URI, "selected");
+                boolean selected = Boolean.parseBoolean(strselected); 
+                final String group = em.getAttributeNS(ArtifactNamespaceContext.NAMESPACE_URI, "groupId");
+                final String groupLabel = em.getAttributeNS(ArtifactNamespaceContext.NAMESPACE_URI, "groupLabel");
+
+                final NodeList rivers = em.getChildNodes();
+                final List<String> riverUuids = new ArrayList<String>(rivers.getLength());
                 for (int j = 0; j < rivers.getLength(); j++) {
-                    Element re = (Element)rivers.item(j);
+                    final Element re = (Element)rivers.item(j);
                     riverUuids.add(re.getAttribute("uuid"));
                 }
                 log.debug("Found module " + name + " " + localname);
                 if (user == null || user.canUseFeature("module:" + name)) {
-                    modules.add(new DefaultModule(
-                            name, localname, selected, riverUuids));
+                    modules.add(new DefaultModule(name, localname, selected, group, groupLabel, riverUuids));
                 }
             }
             return modules.toArray(new Module[modules.size()]);
--- a/gwt-client/src/main/java/org/dive4elements/river/client/shared/model/DefaultModule.java	Wed Feb 07 11:52:04 2018 +0100
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/shared/model/DefaultModule.java	Wed Feb 07 11:59:13 2018 +0100
@@ -14,26 +14,31 @@
 
     private String name;
     private String localname;
-    private boolean selected = false;
+    private ModuleGroup group;
+    private boolean selected;
     private List<String> rivers;
 
     public DefaultModule() {
     }
 
     public DefaultModule(
-        String name,
-        String localname,
-        boolean selected,
-        List<String> rivers) {
+        final String name,
+        final String localname,
+        final boolean selected,
+        final String groupId,
+        final String groupLabel,
+        final List<String> rivers) {
         this.name = name;
         this.localname = localname;
         this.selected = selected;
+        this.group = new ModuleGroup(groupId, groupLabel);
         this.rivers = rivers;
     }
 
     /**
      * Returns the name of the module
      */
+    @Override
     public String getName() {
         return this.name;
     }
@@ -41,6 +46,7 @@
     /**
      * Returns the localized name of the module
      */
+    @Override
     public String getLocalizedName() {
         return this.localname;
     }
@@ -48,15 +54,21 @@
     /**
      * Returns true if the module should be selected
      */
+    @Override
     public boolean isSelected() {
         return this.selected;
     }
+    
+    @Override
+    public ModuleGroup getGroup() {
+        return this.group;
+    }
 
     /**
      * @return the rivers
      */
+    @Override
     public List<String> getRivers() {
         return this.rivers;
     }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 tw=80 :
+}
\ No newline at end of file
--- a/gwt-client/src/main/java/org/dive4elements/river/client/shared/model/Module.java	Wed Feb 07 11:52:04 2018 +0100
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/shared/model/Module.java	Wed Feb 07 11:59:13 2018 +0100
@@ -18,6 +18,8 @@
     String getLocalizedName();
 
     boolean isSelected();
+    
+    ModuleGroup getGroup();
 
     List<String> getRivers();
 }

http://dive4elements.wald.intevation.org