← Back to team overview

gephi.team team mailing list archive

[Merge] lp:~eduramiba/gephi/linkfluence-features into lp:gephi

 

Eduardo Ramos has proposed merging lp:~eduramiba/gephi/linkfluence-features into lp:gephi.

Requested reviews:
  Gephi Team (gephi.team)

For more details, see:
https://code.launchpad.net/~eduramiba/gephi/linkfluence-features/+merge/48162
-- 
https://code.launchpad.net/~eduramiba/gephi/linkfluence-features/+merge/48162
Your team Gephi Team is requested to review the proposed merge of lp:~eduramiba/gephi/linkfluence-features into lp:gephi.
=== modified file 'DataLaboratoryAPI/nbproject/genfiles.properties'
--- DataLaboratoryAPI/nbproject/genfiles.properties	2010-09-16 21:39:28 +0000
+++ DataLaboratoryAPI/nbproject/genfiles.properties	2011-02-01 14:23:14 +0000
@@ -1,8 +1,8 @@
-build.xml.data.CRC32=08e66a2b
+build.xml.data.CRC32=cde22f7d
 build.xml.script.CRC32=f4b00cc3
 build.xml.stylesheet.CRC32=a56c6a5b@1.42.2
 # This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
 # Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
-nbproject/build-impl.xml.data.CRC32=08e66a2b
+nbproject/build-impl.xml.data.CRC32=cde22f7d
 nbproject/build-impl.xml.script.CRC32=eea9549d
 nbproject/build-impl.xml.stylesheet.CRC32=238281d1@1.42.2

=== modified file 'DataLaboratoryAPI/nbproject/project.xml'
--- DataLaboratoryAPI/nbproject/project.xml	2010-09-16 21:39:28 +0000
+++ DataLaboratoryAPI/nbproject/project.xml	2011-02-01 14:23:14 +0000
@@ -47,6 +47,14 @@
                     </run-dependency>
                 </dependency>
                 <dependency>
+                    <code-name-base>org.openide.dialogs</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>7.15.1</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
                     <code-name-base>org.openide.util</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>

=== modified file 'DataLaboratoryAPI/src/org/gephi/datalab/api/AttributeColumnsController.java'
--- DataLaboratoryAPI/src/org/gephi/datalab/api/AttributeColumnsController.java	2010-10-12 12:56:30 +0000
+++ DataLaboratoryAPI/src/org/gephi/datalab/api/AttributeColumnsController.java	2011-02-01 14:23:14 +0000
@@ -31,6 +31,7 @@
 import org.gephi.graph.api.Attributes;
 import org.gephi.graph.api.Edge;
 import org.gephi.graph.api.Node;
+import org.openide.util.Lookup;
 
 /**
  * <p>This interface defines part of the Data Laboratory API basic actions.</p>
@@ -109,6 +110,26 @@
     void fillColumnWithValue(AttributeTable table, AttributeColumn column, String value);
 
     /**
+     * <p>Fills the data values of a given column of the indicated nodes with a value as a String,
+     * parsing it for the <code>AttributeType</code> of the column. If it is not possible to parse,
+     * the value will be set to null.</p>
+     * @param nodes Nodes to fill
+     * @param column Column to fill
+     * @param value String representation of the value for the column for each node
+     */
+    void fillNodesColumnWithValue(Node[] nodes, AttributeColumn column, String value);
+
+    /**
+     * <p>Fills the data values of a given column of the indicated edges with a value as a String,
+     * parsing it for the <code>AttributeType</code> of the column. If it is not possible to parse,
+     * the value will be set to null.</p>
+     * @param edges Edges to fill
+     * @param column Column to fill
+     * @param value String representation of the value for the column for each edge
+     */
+    void fillEdgesColumnWithValue(Edge[] edges, AttributeColumn column, String value);
+
+    /**
      * <p>Clears all rows data for a given column of a table (nodes table or edges table)</p>
      * @param table Table to clear column data
      * @param column Column to clear data

=== modified file 'DataLaboratoryAPI/src/org/gephi/datalab/api/Bundle.properties'
--- DataLaboratoryAPI/src/org/gephi/datalab/api/Bundle.properties	2010-09-07 09:00:13 +0000
+++ DataLaboratoryAPI/src/org/gephi/datalab/api/Bundle.properties	2011-02-01 14:23:14 +0000
@@ -3,3 +3,5 @@
     API/SPI for interacting with the Data Laboratory and extending it.
 OpenIDE-Module-Name=Data Laboratory API
 OpenIDE-Module-Short-Description=API/SPI for interacting with the Data Laboratory and extending it
+DataLaboratoryHelper.ui.okButton.text=Ok
+SettingsPanel.title={0}

=== modified file 'DataLaboratoryAPI/src/org/gephi/datalab/api/Bundle_es.properties'
--- DataLaboratoryAPI/src/org/gephi/datalab/api/Bundle_es.properties	2010-12-30 12:17:31 +0000
+++ DataLaboratoryAPI/src/org/gephi/datalab/api/Bundle_es.properties	2011-02-01 14:23:14 +0000
@@ -8,3 +8,6 @@
 OpenIDE-Module-Long-Description=API/SPI para interactuar con el Laboratorio de Datos y extenderlo.
 
 OpenIDE-Module-Short-Description=API/SPI para interactuar con el Laboratorio de Datos y extenderlo.
+
+DataLaboratoryHelper.ui.okButton.text=Ok
+SettingsPanel.title={0}
\ No newline at end of file

=== modified file 'DataLaboratoryAPI/src/org/gephi/datalab/api/Bundle_fr.properties'
--- DataLaboratoryAPI/src/org/gephi/datalab/api/Bundle_fr.properties	2010-12-30 12:17:31 +0000
+++ DataLaboratoryAPI/src/org/gephi/datalab/api/Bundle_fr.properties	2011-02-01 14:23:14 +0000
@@ -8,3 +8,6 @@
 OpenIDE-Module-Long-Description=API/SPI pour interagir avec le Data Laboratory et l'\u00e9tendre
 
 OpenIDE-Module-Short-Description=API/SPI pour interagir avec le Data Laboratory et l'\u00e9tendre
+
+DataLaboratoryHelper.ui.okButton.text=Ok
+SettingsPanel.title={0}
\ No newline at end of file

=== modified file 'DataLaboratoryAPI/src/org/gephi/datalab/api/Bundle_ru.properties'
--- DataLaboratoryAPI/src/org/gephi/datalab/api/Bundle_ru.properties	2010-12-30 12:17:31 +0000
+++ DataLaboratoryAPI/src/org/gephi/datalab/api/Bundle_ru.properties	2011-02-01 14:23:14 +0000
@@ -8,3 +8,4 @@
 OpenIDE-Module-Long-Description=API/SPI \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u041b\u0430\u0431\u043e\u0440\u0430\u0442\u043e\u0440\u0438\u0435\u0439 \u0414\u0430\u043d\u043d\u044b\u0445
 
 OpenIDE-Module-Short-Description=API/SPI \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u041b\u0430\u0431\u043e\u0440\u0430\u0442\u043e\u0440\u0438\u0435\u0439 \u0414\u0430\u043d\u043d\u044b\u0445
+SettingsPanel.title={0}
\ No newline at end of file

=== added file 'DataLaboratoryAPI/src/org/gephi/datalab/api/DataLaboratoryHelper.java'
--- DataLaboratoryAPI/src/org/gephi/datalab/api/DataLaboratoryHelper.java	1970-01-01 00:00:00 +0000
+++ DataLaboratoryAPI/src/org/gephi/datalab/api/DataLaboratoryHelper.java	2011-02-01 14:23:14 +0000
@@ -0,0 +1,409 @@
+/*
+Copyright 2008-2010 Gephi
+Authors : Eduardo Ramos <eduramiba@xxxxxxxxx>
+Website : http://www.gephi.org
+
+This file is part of Gephi.
+
+Gephi is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+Gephi is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with Gephi.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.gephi.datalab.api;
+
+import java.awt.Dialog;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+import javax.swing.SwingUtilities;
+import org.gephi.data.attributes.api.AttributeColumn;
+import org.gephi.data.attributes.api.AttributeTable;
+import org.gephi.datalab.spi.DialogControls;
+import org.gephi.datalab.spi.Manipulator;
+import org.gephi.datalab.spi.ManipulatorUI;
+import org.gephi.datalab.spi.columns.AttributeColumnsManipulator;
+import org.gephi.datalab.spi.columns.AttributeColumnsManipulatorUI;
+import org.gephi.datalab.spi.columns.merge.AttributeColumnsMergeStrategy;
+import org.gephi.datalab.spi.columns.merge.AttributeColumnsMergeStrategyBuilder;
+import org.gephi.datalab.spi.values.AttributeValueManipulator;
+import org.gephi.datalab.spi.values.AttributeValueManipulatorBuilder;
+import org.gephi.datalab.spi.edges.EdgesManipulator;
+import org.gephi.datalab.spi.edges.EdgesManipulatorBuilder;
+import org.gephi.datalab.spi.general.GeneralActionsManipulator;
+import org.gephi.datalab.spi.general.PluginGeneralActionsManipulator;
+import org.gephi.datalab.spi.nodes.NodesManipulator;
+import org.gephi.datalab.spi.nodes.NodesManipulatorBuilder;
+import org.openide.DialogDescriptor;
+import org.openide.DialogDisplayer;
+import org.openide.util.Lookup;
+import org.openide.util.NbBundle;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ * Helper class for simplifying the use of Data Laboratory API and SPI.
+ */
+@ServiceProvider(service=DataLaboratoryHelper.class)
+public class DataLaboratoryHelper{
+
+    /**
+     * <p>Prepares an array with one new instance of every NodesManipulator
+     * that has a builder registered and returns it.</p>
+     * <p>It also returns the manipulators ordered first by type and then by position.</p>
+     * @return Array of all NodesManipulator implementations
+     */
+    public NodesManipulator[] getNodesManipulators() {
+        ArrayList<NodesManipulator> nodesManipulators = new ArrayList<NodesManipulator>();
+        for (NodesManipulatorBuilder nm : Lookup.getDefault().lookupAll(NodesManipulatorBuilder.class)) {
+            nodesManipulators.add(nm.getNodesManipulator());
+        }
+        sortManipulators(nodesManipulators);
+        return nodesManipulators.toArray(new NodesManipulator[0]);
+    }
+
+    /**
+     * <p>Prepares an array with one new instance of every EdgesManipulator
+     * that has a builder registered and returns it.</p>
+     * <p>It also returns the manipulators ordered first by type and then by position.</p>
+     * @return Array of all EdgesManipulator implementations
+     */
+    public EdgesManipulator[] getEdgesManipulators() {
+        ArrayList<EdgesManipulator> edgesManipulators = new ArrayList<EdgesManipulator>();
+        for (EdgesManipulatorBuilder em : Lookup.getDefault().lookupAll(EdgesManipulatorBuilder.class)) {
+            edgesManipulators.add(em.getEdgesManipulator());
+        }
+        sortManipulators(edgesManipulators);
+        return edgesManipulators.toArray(new EdgesManipulator[0]);
+    }
+
+    /**
+     * <p>Prepares an array with one instance of every GeneralActionsManipulator that is registered.</p>
+     * <p>It also returns the manipulators ordered first by type and then by position.</p>
+     * @return Array of all GeneralActionsManipulator implementations
+     */
+    public GeneralActionsManipulator[] getGeneralActionsManipulators() {
+        ArrayList<GeneralActionsManipulator> generalActionsManipulators = new ArrayList<GeneralActionsManipulator>();
+        generalActionsManipulators.addAll(Lookup.getDefault().lookupAll(GeneralActionsManipulator.class));
+        sortManipulators(generalActionsManipulators);
+        return generalActionsManipulators.toArray(new GeneralActionsManipulator[0]);
+    }
+
+    /**
+     * <p>Prepares an array with one instance of every PluginGeneralActionsManipulator that is registered.</p>
+     * <p>It also returns the manipulators ordered first by type and then by position.</p>
+     * @return Array of all PluginGeneralActionsManipulator implementations
+     */
+    public PluginGeneralActionsManipulator[] getPluginGeneralActionsManipulators() {
+        ArrayList<PluginGeneralActionsManipulator> pluginGeneralActionsManipulators = new ArrayList<PluginGeneralActionsManipulator>();
+        pluginGeneralActionsManipulators.addAll(Lookup.getDefault().lookupAll(PluginGeneralActionsManipulator.class));
+        sortManipulators(pluginGeneralActionsManipulators);
+        return pluginGeneralActionsManipulators.toArray(new PluginGeneralActionsManipulator[0]);
+    }
+
+    /**
+     * <p>Prepares an array that has one instance of every AttributeColumnsManipulator implementation
+     * that has a builder registered and returns it.</p>
+     * <p>It also returns the manipulators ordered first by type and then by position.</p>
+     * @return Array of all AttributeColumnsManipulator implementations
+     */
+    public AttributeColumnsManipulator[] getAttributeColumnsManipulators() {
+        ArrayList<AttributeColumnsManipulator> attributeColumnsManipulators = new ArrayList<AttributeColumnsManipulator>();
+        attributeColumnsManipulators.addAll(Lookup.getDefault().lookupAll(AttributeColumnsManipulator.class));
+        sortAttributeColumnsManipulators(attributeColumnsManipulators);
+        return attributeColumnsManipulators.toArray(new AttributeColumnsManipulator[0]);
+    }
+
+    /**
+     * <p>Prepares an array with one new instance of every AttributeValueManipulator
+     * that has a builder registered and returns it.</p>
+     * <p>It also returns the manipulators ordered first by type and then by position.</p>
+     * @return Array of all AttributeValueManipulator implementations
+     */
+    public AttributeValueManipulator[] getAttributeValueManipulators() {
+        ArrayList<AttributeValueManipulator> attributeValueManipulators = new ArrayList<AttributeValueManipulator>();
+        for (AttributeValueManipulatorBuilder am : Lookup.getDefault().lookupAll(AttributeValueManipulatorBuilder.class)) {
+            attributeValueManipulators.add(am.getAttributeValueManipulator());
+        }
+        sortManipulators(attributeValueManipulators);
+        return attributeValueManipulators.toArray(new AttributeValueManipulator[0]);
+    }
+
+    /**
+     * <p>Prepares an array that has one new instance of every AttributeColumnsMergeStrategy implementation that is registered.</p>
+     * <p>It also returns the manipulators ordered first by type and then by position.</p>
+     * @return Array of all AttributeColumnsMergeStrategy implementations
+     */
+    public AttributeColumnsMergeStrategy[] getAttributeColumnsMergeStrategies() {
+        ArrayList<AttributeColumnsMergeStrategy> strategies = new ArrayList<AttributeColumnsMergeStrategy>();
+        for (AttributeColumnsMergeStrategyBuilder cs : Lookup.getDefault().lookupAll(AttributeColumnsMergeStrategyBuilder.class)) {
+            strategies.add(cs.getAttributeColumnsMergeStrategy());
+        }
+        sortManipulators(strategies);
+        return strategies.toArray(new AttributeColumnsMergeStrategy[0]);
+    }
+
+    private void sortManipulators(ArrayList<? extends Manipulator> m) {
+        Collections.sort(m, new Comparator<Manipulator>() {
+
+            public int compare(Manipulator o1, Manipulator o2) {
+                //Order by type, position.
+                if (o1.getType() == o2.getType()) {
+                    return o1.getPosition() - o2.getPosition();
+                } else {
+                    return o1.getType() - o2.getType();
+                }
+            }
+        });
+    }
+
+    private void sortAttributeColumnsManipulators(ArrayList<? extends AttributeColumnsManipulator> m) {
+        Collections.sort(m, new Comparator<AttributeColumnsManipulator>() {
+
+            public int compare(AttributeColumnsManipulator o1, AttributeColumnsManipulator o2) {
+                //Order by type, position.
+                if (o1.getType() == o2.getType()) {
+                    return o1.getPosition() - o2.getPosition();
+                } else {
+                    return o1.getType() - o2.getType();
+                }
+            }
+        });
+    }
+
+    /**
+     * Prepares the dialog UI of a manipulator if it has one and executes the manipulator in a separate
+     * Thread when the dialog is accepted or directly if there is no UI.
+     * @param m Manipulator to execute
+     */
+    public void executeManipulator(final Manipulator m) {
+        if (m.canExecute()) {
+            SwingUtilities.invokeLater(new Runnable() {
+
+                public void run() {
+
+                    final ManipulatorUI ui = m.getUI();
+                    //Show a dialog for the manipulator UI if it provides one. If not, execute the manipulator directly:
+                    if (ui != null) {
+                        final JButton okButton = new JButton(NbBundle.getMessage(DataLaboratoryHelper.class, "DataLaboratoryHelper.ui.okButton.text"));
+                        DialogControls dialogControls = new DialogControlsImpl(okButton);
+                        ui.setup(m, dialogControls);
+                        JPanel settingsPanel = ui.getSettingsPanel();
+                        DialogDescriptor dd = new DialogDescriptor(settingsPanel, NbBundle.getMessage(DataLaboratoryHelper.class, "SettingsPanel.title", ui.getDisplayName()), ui.isModal(), new ActionListener() {
+
+                            public void actionPerformed(ActionEvent e) {
+                                if (e.getSource().equals(okButton)) {
+                                    ui.unSetup();
+                                    executeManipulatorInOtherThread(m);
+                                } else {
+                                    ui.unSetup();
+                                }
+                            }
+                        });
+                        dd.setOptions(new Object[]{okButton, DialogDescriptor.CANCEL_OPTION});
+                        dd.setClosingOptions(null);//All options close
+                        Dialog dialog = DialogDisplayer.getDefault().createDialog(dd);
+                        dialog.addWindowListener(new WindowAdapter() {
+
+                            @Override
+                            public void windowClosing(WindowEvent e) {
+                                ui.unSetup();
+                            }
+                        });
+                        dialog.setVisible(true);
+                    } else {
+                        executeManipulatorInOtherThread(m);
+                    }
+                }
+            });
+        }
+    }
+
+    private void executeManipulatorInOtherThread(final Manipulator m) {
+        new Thread() {
+
+            @Override
+            public void run() {
+                m.execute();
+            }
+        }.start();
+    }
+
+    /**
+     * Prepares the dialog UI of a AttributeColumnsManipulator if it has one and executes the manipulator in a separate
+     * Thread when the dialog is accepted or directly if there is no UI.
+     * @param m AttributeColumnsManipulator
+     * @param table Table of the column
+     * @param column Column to manipulate
+     */
+    public void executeAttributeColumnsManipulator(final AttributeColumnsManipulator m, final AttributeTable table, final AttributeColumn column) {
+        if (m.canManipulateColumn(table, column)) {
+            SwingUtilities.invokeLater(new Runnable() {
+
+                public void run() {
+                    final AttributeColumnsManipulatorUI ui = m.getUI(table, column);
+                    //Show a dialog for the manipulator UI if it provides one. If not, execute the manipulator directly:
+                    if (ui != null) {
+                        final JButton okButton = new JButton(NbBundle.getMessage(DataLaboratoryHelper.class, "DataLaboratoryHelper.ui.okButton.text"));
+                        DialogControls dialogControls = new DialogControlsImpl(okButton);
+                        ui.setup(m, table, column, dialogControls);
+                        JPanel settingsPanel = ui.getSettingsPanel();
+                        DialogDescriptor dd = new DialogDescriptor(settingsPanel, NbBundle.getMessage(DataLaboratoryHelper.class, "SettingsPanel.title", ui.getDisplayName()), ui.isModal(), new ActionListener() {
+
+                            public void actionPerformed(ActionEvent e) {
+                                if (e.getSource().equals(okButton)) {
+                                    ui.unSetup();
+                                    executeAttributeColumnsManipulatorInOtherThread(m, table, column);
+                                } else {
+                                    ui.unSetup();
+                                }
+                            }
+                        });
+                        dd.setOptions(new Object[]{okButton, DialogDescriptor.CANCEL_OPTION});
+                        dd.setClosingOptions(null);//All options close
+                        Dialog dialog = DialogDisplayer.getDefault().createDialog(dd);
+                        dialog.addWindowListener(new WindowAdapter() {
+
+                            @Override
+                            public void windowClosing(WindowEvent e) {
+                                ui.unSetup();
+                            }
+                        });
+                        dialog.setVisible(true);
+                    } else {
+                        executeAttributeColumnsManipulatorInOtherThread(m, table, column);
+                    }
+                }
+            });
+        }
+    }
+
+    private void executeAttributeColumnsManipulatorInOtherThread(final AttributeColumnsManipulator m, final AttributeTable table, final AttributeColumn column) {
+        new Thread() {
+
+            @Override
+            public void run() {
+                m.execute(table, column);
+            }
+        }.start();
+    }
+
+    /**
+     * Returns the AttributeColumnsMergeStrategy with that class name or null if it does not exist
+     */
+    public NodesManipulator getNodesManipulatorByName(String name){
+        for (NodesManipulatorBuilder nm : Lookup.getDefault().lookupAll(NodesManipulatorBuilder.class)) {
+            if(nm.getNodesManipulator().getClass().getSimpleName().equals(name)){
+                return nm.getNodesManipulator();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the AttributeColumnsMergeStrategy with that class name or null if it does not exist
+     */
+    public EdgesManipulator getEdgesManipulatorByName(String name){
+        for (EdgesManipulatorBuilder nm : Lookup.getDefault().lookupAll(EdgesManipulatorBuilder.class)) {
+            if(nm.getEdgesManipulator().getClass().getSimpleName().equals(name)){
+                return nm.getEdgesManipulator();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the AttributeColumnsMergeStrategy with that class name or null if it does not exist
+     */
+    public GeneralActionsManipulator getGeneralActionsManipulatorByName(String name) {
+        for (GeneralActionsManipulator m : Lookup.getDefault().lookupAll(GeneralActionsManipulator.class)) {
+            if(m.getClass().getSimpleName().equals(name)){
+                return m;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the AttributeColumnsMergeStrategy with that class name or null if it does not exist
+     */
+    public PluginGeneralActionsManipulator getPluginGeneralActionsManipulatorByName(String name) {
+        for (PluginGeneralActionsManipulator m : Lookup.getDefault().lookupAll(PluginGeneralActionsManipulator.class)) {
+            if(m.getClass().getSimpleName().equals(name)){
+                return m;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the AttributeColumnsMergeStrategy with that class name or null if it does not exist
+     */
+    public AttributeColumnsManipulator getAttributeColumnsManipulatorByName(String name) {
+        for (AttributeColumnsManipulator m : Lookup.getDefault().lookupAll(AttributeColumnsManipulator.class)) {
+            if(m.getClass().getSimpleName().equals(name)){
+                return m;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the AttributeColumnsMergeStrategy with that class name or null if it does not exist
+     */
+    public AttributeValueManipulator getAttributeValueManipulatorByName(String name) {
+        for (AttributeValueManipulatorBuilder am : Lookup.getDefault().lookupAll(AttributeValueManipulatorBuilder.class)) {
+            if(am.getAttributeValueManipulator().getClass().getSimpleName().equals(name)){
+                return am.getAttributeValueManipulator();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the AttributeColumnsMergeStrategy with that class name or null if it does not exist
+     */
+    public AttributeColumnsMergeStrategy getAttributeColumnsMergeStrategieByName(String name) {
+        for (AttributeColumnsMergeStrategyBuilder cs : Lookup.getDefault().lookupAll(AttributeColumnsMergeStrategyBuilder.class)) {
+            if(cs.getAttributeColumnsMergeStrategy().getClass().getSimpleName().equals(name)){
+                return cs.getAttributeColumnsMergeStrategy();
+            }
+        }
+        return null;
+    }
+
+    class DialogControlsImpl implements DialogControls {
+
+        JComponent okButton;
+
+        public DialogControlsImpl(JComponent okButton) {
+            this.okButton = okButton;
+        }
+
+        public void setOkButtonEnabled(boolean enabled) {
+            okButton.setEnabled(enabled);
+        }
+
+        public boolean isOkButtonEnabled(){
+            return okButton.isEnabled();
+        }
+    }
+
+    public static DataLaboratoryHelper getDefault(){
+        return Lookup.getDefault().lookup(DataLaboratoryHelper.class);
+    }
+}

=== modified file 'DataLaboratoryAPI/src/org/gephi/datalab/impl/AttributeColumnsControllerImpl.java'
--- DataLaboratoryAPI/src/org/gephi/datalab/impl/AttributeColumnsControllerImpl.java	2010-10-16 16:36:58 +0000
+++ DataLaboratoryAPI/src/org/gephi/datalab/impl/AttributeColumnsControllerImpl.java	2011-02-01 14:23:14 +0000
@@ -142,6 +142,22 @@
         }
     }
 
+    public void fillNodesColumnWithValue(Node[] nodes, AttributeColumn column, String value){
+        if (canChangeColumnData(column)) {
+            for (Node node: nodes) {
+                setAttributeValue(value, node.getNodeData().getAttributes(), column);
+            }
+        }
+    }
+
+    public void fillEdgesColumnWithValue(Edge[] edges, AttributeColumn column, String value){
+        if (canChangeColumnData(column)) {
+            for (Edge edge: edges) {
+                setAttributeValue(value, edge.getEdgeData().getAttributes(), column);
+            }
+        }
+    }
+
     public void clearColumnData(AttributeTable table, AttributeColumn column) {
         if (canClearColumnData(column)) {
             final int columnIndex = column.getIndex();

=== modified file 'DataLaboratoryAPI/src/org/gephi/datalab/impl/GraphElementsControllerImpl.java'
--- DataLaboratoryAPI/src/org/gephi/datalab/impl/GraphElementsControllerImpl.java	2010-10-11 12:12:06 +0000
+++ DataLaboratoryAPI/src/org/gephi/datalab/impl/GraphElementsControllerImpl.java	2011-02-01 14:23:14 +0000
@@ -35,6 +35,8 @@
 import org.gephi.graph.api.Node;
 import org.gephi.graph.api.NodeData;
 import org.gephi.graph.api.UndirectedGraph;
+import org.openide.DialogDisplayer;
+import org.openide.NotifyDescriptor;
 import org.openide.util.Lookup;
 import org.openide.util.NbBundle;
 import org.openide.util.lookup.ServiceProvider;
@@ -47,8 +49,9 @@
  */
 @ServiceProvider(service = GraphElementsController.class)
 public class GraphElementsControllerImpl implements GraphElementsController {
-    private static final float DEFAULT_NODE_SIZE=10f;
-    private static final float DEFAULT_EDGE_WEIGHT=1f;
+
+    private static final float DEFAULT_NODE_SIZE = 10f;
+    private static final float DEFAULT_EDGE_WEIGHT = 1f;
 
     public Node createNode(String label) {
         Node newNode = buildNode(label);
@@ -176,10 +179,39 @@
 
     public boolean groupNodes(Node[] nodes) {
         if (canGroupNodes(nodes)) {
-            HierarchicalGraph hg = getHierarchicalGraph();
-            Node group = hg.groupNodes(nodes);
-            //Set the group node label to the same used int visualization module:
-            group.getNodeData().setLabel(NbBundle.getMessage(GraphElementsControllerImpl.class, "Group.nodeCount.label", getNodeChildrenCount(hg, group)));
+            HierarchicalGraph graph = getHierarchicalGraph();
+            try {
+                float centroidX = 0;
+                float centroidY = 0;
+                int len = 0;
+                float sizes = 0;
+                float r = 0;
+                float g = 0;
+                float b = 0;
+                Node group = graph.groupNodes(nodes);
+                group.getNodeData().setLabel(NbBundle.getMessage(GraphElementsControllerImpl.class, "Group.nodeCount.label", nodes.length));
+                group.getNodeData().setSize(10f);
+                for (Node child : nodes) {
+                    centroidX += child.getNodeData().x();
+                    centroidY += child.getNodeData().y();
+                    len++;
+                    sizes += child.getNodeData().getSize() / 10f;
+                    r += child.getNodeData().r();
+                    g += child.getNodeData().g();
+                    b += child.getNodeData().b();
+                }
+                centroidX /= len;
+                centroidY /= len;
+                group.getNodeData().setSize(sizes);
+                group.getNodeData().setColor(r / len, g / len, b / len);
+                group.getNodeData().setX(centroidX);
+                group.getNodeData().setY(centroidY);
+            } catch (Exception e) {
+                graph.readUnlockAll();
+                NotifyDescriptor.Message nd = new NotifyDescriptor.Message(e.getMessage());
+                DialogDisplayer.getDefault().notifyLater(nd);
+                return false;
+            }
             return true;
         } else {
             return false;

=== modified file 'DataLaboratoryAPI/src/org/gephi/datalab/spi/Manipulator.java'
--- DataLaboratoryAPI/src/org/gephi/datalab/spi/Manipulator.java	2010-09-07 09:00:13 +0000
+++ DataLaboratoryAPI/src/org/gephi/datalab/spi/Manipulator.java	2011-02-01 14:23:14 +0000
@@ -31,7 +31,7 @@
  * <ul>
  *  <li>Execute an action</li>
  *  <li>Provide a name, description, type and order of appearance (position in group of its type)</li>
- *  <li>Indicate wether they have to be executable or not</li>
+ *  <li>Indicate wether they have to be executable (enabled in the context menu) or not</li>
  *  <li>Provide and UI or not</li>
  *  <li>Provide and icon or not</li>
  * </ul>
@@ -64,9 +64,9 @@
     String getDescription();
 
     /**
-     * Indicates if this Manipulator has to be shown.
+     * Indicates if this Manipulator has to be executable.
      * Implementations should evaluate the current data and conditions.
-     * @return True if it has to be shown, false otherwise
+     * @return True if it has to be executable, false otherwise
      */
     boolean canExecute();
 

=== added file 'DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/GeneralColumnAndValueChooser.java'
--- DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/GeneralColumnAndValueChooser.java	1970-01-01 00:00:00 +0000
+++ DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/GeneralColumnAndValueChooser.java	2011-02-01 14:23:14 +0000
@@ -0,0 +1,65 @@
+/*
+Copyright 2008-2010 Gephi
+Authors : Eduardo Ramos <eduramiba@xxxxxxxxx>
+Website : http://www.gephi.org
+
+This file is part of Gephi.
+
+Gephi is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+Gephi is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with Gephi.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.gephi.datalab.plugin.manipulators;
+
+import org.gephi.data.attributes.api.AttributeColumn;
+import org.gephi.data.attributes.api.AttributeTable;
+import org.gephi.datalab.plugin.manipulators.nodes.TagNodes;
+
+/**
+ * Interface in common for choosing a column to manipulate from a list and a String value.
+ * Used to be able to mass-tag a column of nodes/edges.
+ * @author Eduardo Ramos <eduramiba@xxxxxxxxx>
+ * @see FillNodesColumnWithValue
+ */
+public interface GeneralColumnAndValueChooser{
+
+    /**
+     * Provide columns to show in the UI to select one.
+     * Normally provide all table columns that can be manipulated.
+     * @return Columns to show in the GeneralColumnAndValueChooserUI
+     */
+    AttributeColumn[] getColumns();
+
+    /**
+     * Provide table for auto-completion of column values
+     * @return
+     */
+    AttributeTable getTable();
+
+    /**
+     * The GeneralColumnAndValueChooserUI will use this method to set the column to finally manipulate, after the GeneralColumnAndValueChooserUI is closed.
+     * @param columnsToClearData Column to manipulate
+     */
+    void setColumn(AttributeColumn column);
+
+    /**
+     * The GeneralColumnAndValueChooserUI will use this method to set the String value to finally use, after the GeneralColumnAndValueChooserUI is closed.
+     * @param columnsToClearData Column to manipulate
+     */
+    void setValue(String value);
+
+    /**
+     * Provide title for the GeneralColumnAndValueChooserUI.
+     * @return Title name
+     */
+    String getName();
+}

=== modified file 'DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/GeneralColumnsChooser.java'
--- DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/GeneralColumnsChooser.java	2010-12-28 14:28:46 +0000
+++ DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/GeneralColumnsChooser.java	2011-02-01 14:23:14 +0000
@@ -40,7 +40,7 @@
 
     /**
      * The GeneralChooseColumnsUI will use this method to set the columns to finally manipulate, after the GeneralChooseColumnsUI is closed.
-     * @param columnsToClearData Columns to clear
+     * @param columnsToClearData Columns to manipulate
      */
     void setColumns(AttributeColumn[] columnsToClearData);
 

=== modified file 'DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/edges/Bundle.properties'
--- DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/edges/Bundle.properties	2010-12-29 15:36:47 +0000
+++ DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/edges/Bundle.properties	2011-02-01 14:23:14 +0000
@@ -12,6 +12,8 @@
 DeleteEdgesWithNodes.name.single=Delete edge with nodes...
 DeleteEdgesWithNodes.name.multiple=Delete all edges with nodes...
 
+TagEdges.name.single=Tag edge...
+TagEdges.name.multiple=Tag edges...
 ClearEdgesData.name.single=Clear...
 ClearEdgesData.name.multiple=Clear all...
 ClearEdgesData.description=Clear data of the selected edges

=== modified file 'DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/edges/ClearEdgesData.java'
--- DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/edges/ClearEdgesData.java	2010-09-07 09:00:13 +0000
+++ DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/edges/ClearEdgesData.java	2011-02-01 14:23:14 +0000
@@ -89,7 +89,7 @@
     }
 
     public int getPosition() {
-        return 0;
+        return 100;
     }
 
     public Icon getIcon() {

=== modified file 'DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/edges/CopyEdgeDataToOtherEdges.java'
--- DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/edges/CopyEdgeDataToOtherEdges.java	2010-12-28 14:28:46 +0000
+++ DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/edges/CopyEdgeDataToOtherEdges.java	2011-02-01 14:23:14 +0000
@@ -87,7 +87,7 @@
     }
 
     public int getPosition() {
-        return 100;
+        return 200;
     }
 
     public Icon getIcon() {

=== added file 'DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/edges/TagEdges.java'
--- DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/edges/TagEdges.java	1970-01-01 00:00:00 +0000
+++ DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/edges/TagEdges.java	2011-02-01 14:23:14 +0000
@@ -0,0 +1,119 @@
+/*
+Copyright 2008-2010 Gephi
+Authors : Eduardo Ramos <eduramiba@xxxxxxxxx>
+Website : http://www.gephi.org
+
+This file is part of Gephi.
+
+Gephi is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+Gephi is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with Gephi.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.gephi.datalab.plugin.manipulators.edges;
+
+import java.util.ArrayList;
+import javax.swing.Icon;
+import org.gephi.data.attributes.api.AttributeColumn;
+import org.gephi.data.attributes.api.AttributeController;
+import org.gephi.data.attributes.api.AttributeTable;
+import org.gephi.datalab.api.AttributeColumnsController;
+import org.gephi.datalab.api.DataTablesController;
+import org.gephi.datalab.plugin.manipulators.GeneralColumnAndValueChooser;
+import org.gephi.datalab.plugin.manipulators.ui.GeneralColumnAndValueChooserUI;
+import org.gephi.datalab.spi.ManipulatorUI;
+import org.gephi.datalab.spi.edges.EdgesManipulator;
+import org.gephi.graph.api.Edge;
+import org.openide.util.ImageUtilities;
+import org.openide.util.Lookup;
+import org.openide.util.NbBundle;
+
+/**
+ * Edges manipulator that fills the given column of multiple edges with a value.
+ * @author Eduardo Ramos <eduramiba@xxxxxxxxx>
+ */
+public class TagEdges implements EdgesManipulator, GeneralColumnAndValueChooser {
+
+    private Edge[] edges;
+    private AttributeColumn column;
+    private AttributeTable table;
+    private AttributeColumn[] availableColumns;
+    private String value;
+
+    public void setup(Edge[] edges, Edge clickedEdge) {
+        this.edges = edges;
+        table = Lookup.getDefault().lookup(AttributeController.class).getModel().getEdgeTable();
+        AttributeColumnsController ac = Lookup.getDefault().lookup(AttributeColumnsController.class);
+        ArrayList<AttributeColumn> availableColumnsList = new ArrayList<AttributeColumn>();
+        for (AttributeColumn c : table.getColumns()) {
+            if (ac.canChangeColumnData(c)) {
+                availableColumnsList.add(c);
+            }
+        }
+        availableColumns = availableColumnsList.toArray(new AttributeColumn[0]);
+    }
+
+    public void execute() {
+        if (column != null) {
+            AttributeColumnsController ac = Lookup.getDefault().lookup(AttributeColumnsController.class);
+            ac.fillEdgesColumnWithValue(edges, column, value);
+            Lookup.getDefault().lookup(DataTablesController.class).refreshCurrentTable();
+        }
+    }
+
+    public String getName() {
+        if (edges.length > 1) {
+            return NbBundle.getMessage(TagEdges.class, "TagEdges.name.multiple");
+        } else {
+            return NbBundle.getMessage(TagEdges.class, "TagEdges.name.single");
+        }
+    }
+
+    public String getDescription() {
+        return "";
+    }
+
+    public boolean canExecute() {
+        return edges.length > 0;
+    }
+
+    public ManipulatorUI getUI() {
+        return new GeneralColumnAndValueChooserUI();
+    }
+
+    public int getType() {
+        return 200;
+    }
+
+    public int getPosition() {
+        return 0;
+    }
+
+    public Icon getIcon() {
+        return ImageUtilities.loadImageIcon("org/gephi/datalab/plugin/manipulators/resources/tag-label.png", true);
+    }
+
+    public AttributeColumn[] getColumns() {
+        return availableColumns;
+    }
+
+    public void setColumn(AttributeColumn column) {
+        this.column = column;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    public AttributeTable getTable() {
+        return table;
+    }
+}

=== added file 'DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/edges/TagEdgesBuilder.java'
--- DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/edges/TagEdgesBuilder.java	1970-01-01 00:00:00 +0000
+++ DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/edges/TagEdgesBuilder.java	2011-02-01 14:23:14 +0000
@@ -0,0 +1,37 @@
+/*
+Copyright 2008-2010 Gephi
+Authors : Eduardo Ramos <eduramiba@xxxxxxxxx>
+Website : http://www.gephi.org
+
+This file is part of Gephi.
+
+Gephi is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+Gephi is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with Gephi.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.gephi.datalab.plugin.manipulators.edges;
+
+import org.gephi.datalab.spi.edges.EdgesManipulator;
+import org.gephi.datalab.spi.edges.EdgesManipulatorBuilder;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ * Builder for TagEdges edges manipulator.
+ * @author Eduardo Ramos <eduramiba@xxxxxxxxx>
+ */
+@ServiceProvider(service=EdgesManipulatorBuilder.class)
+public class TagEdgesBuilder implements EdgesManipulatorBuilder{
+
+    public EdgesManipulator getEdgesManipulator() {
+        return new TagEdges();
+    }
+}

=== modified file 'DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/nodes/Bundle.properties'
--- DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/nodes/Bundle.properties	2010-12-29 15:36:47 +0000
+++ DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/nodes/Bundle.properties	2011-02-01 14:23:14 +0000
@@ -10,6 +10,8 @@
 DeleteNodes.name.multiple=Delete all
 DeleteNodes.confirmation.message=Confirm nodes deletion?
 
+TagNodes.name.multiple=Tag nodes...
+TagNodes.name.single=Tag node...
 ClearNodesData.name.single=Clear...
 ClearNodesData.name.multiple=Clear all...
 ClearNodesData.description=Clear data of the selected nodes

=== modified file 'DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/nodes/ClearNodesData.java'
--- DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/nodes/ClearNodesData.java	2010-09-07 09:00:13 +0000
+++ DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/nodes/ClearNodesData.java	2011-02-01 14:23:14 +0000
@@ -89,7 +89,7 @@
     }
 
     public int getPosition() {
-        return 0;
+        return 100;
     }
 
     public Icon getIcon() {

=== modified file 'DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/nodes/CopyNodeDataToOtherNodes.java'
--- DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/nodes/CopyNodeDataToOtherNodes.java	2010-12-28 14:28:46 +0000
+++ DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/nodes/CopyNodeDataToOtherNodes.java	2011-02-01 14:23:14 +0000
@@ -87,7 +87,7 @@
     }
 
     public int getPosition() {
-        return 100;
+        return 200;
     }
 
     public Icon getIcon() {

=== added file 'DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/nodes/TagNodes.java'
--- DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/nodes/TagNodes.java	1970-01-01 00:00:00 +0000
+++ DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/nodes/TagNodes.java	2011-02-01 14:23:14 +0000
@@ -0,0 +1,119 @@
+/*
+Copyright 2008-2010 Gephi
+Authors : Eduardo Ramos <eduramiba@xxxxxxxxx>
+Website : http://www.gephi.org
+
+This file is part of Gephi.
+
+Gephi is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+Gephi is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with Gephi.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.gephi.datalab.plugin.manipulators.nodes;
+
+import java.util.ArrayList;
+import javax.swing.Icon;
+import org.gephi.data.attributes.api.AttributeColumn;
+import org.gephi.data.attributes.api.AttributeController;
+import org.gephi.data.attributes.api.AttributeTable;
+import org.gephi.datalab.api.AttributeColumnsController;
+import org.gephi.datalab.api.DataTablesController;
+import org.gephi.datalab.plugin.manipulators.GeneralColumnAndValueChooser;
+import org.gephi.datalab.plugin.manipulators.ui.GeneralColumnAndValueChooserUI;
+import org.gephi.datalab.spi.ManipulatorUI;
+import org.gephi.datalab.spi.nodes.NodesManipulator;
+import org.gephi.graph.api.Node;
+import org.openide.util.ImageUtilities;
+import org.openide.util.Lookup;
+import org.openide.util.NbBundle;
+
+/**
+ * Nodes manipulator that fills the given column of multiple nodes with a value.
+ * @author Eduardo Ramos <eduramiba@xxxxxxxxx>
+ */
+public class TagNodes implements NodesManipulator, GeneralColumnAndValueChooser {
+
+    private Node[] nodes;
+    private AttributeColumn column;
+    private AttributeTable table;
+    private AttributeColumn[] availableColumns;
+    private String value;
+
+    public void setup(Node[] nodes, Node clickedNode) {
+        this.nodes = nodes;
+        table = Lookup.getDefault().lookup(AttributeController.class).getModel().getNodeTable();
+        AttributeColumnsController ac = Lookup.getDefault().lookup(AttributeColumnsController.class);
+        ArrayList<AttributeColumn> availableColumnsList = new ArrayList<AttributeColumn>();
+        for (AttributeColumn c : table.getColumns()) {
+            if (ac.canChangeColumnData(c)) {
+                availableColumnsList.add(c);
+            }
+        }
+        availableColumns = availableColumnsList.toArray(new AttributeColumn[0]);
+    }
+
+    public void execute() {
+        if (column != null) {
+            AttributeColumnsController ac = Lookup.getDefault().lookup(AttributeColumnsController.class);
+            ac.fillNodesColumnWithValue(nodes, column, value);
+            Lookup.getDefault().lookup(DataTablesController.class).refreshCurrentTable();
+        }
+    }
+
+    public String getName() {
+        if (nodes.length > 1) {
+            return NbBundle.getMessage(TagNodes.class, "TagNodes.name.multiple");
+        } else {
+            return NbBundle.getMessage(TagNodes.class, "TagNodes.name.single");
+        }
+    }
+
+    public String getDescription() {
+        return "";
+    }
+
+    public boolean canExecute() {
+        return nodes.length > 0;
+    }
+
+    public ManipulatorUI getUI() {
+        return new GeneralColumnAndValueChooserUI();
+    }
+
+    public int getType() {
+        return 200;
+    }
+
+    public int getPosition() {
+        return 0;
+    }
+
+    public Icon getIcon() {
+        return ImageUtilities.loadImageIcon("org/gephi/datalab/plugin/manipulators/resources/tag-label.png", true);
+    }
+
+    public AttributeColumn[] getColumns() {
+        return availableColumns;
+    }
+
+    public void setColumn(AttributeColumn column) {
+        this.column = column;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    public AttributeTable getTable() {
+        return table;
+    }
+}

=== added file 'DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/nodes/TagNodesBuilder.java'
--- DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/nodes/TagNodesBuilder.java	1970-01-01 00:00:00 +0000
+++ DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/nodes/TagNodesBuilder.java	2011-02-01 14:23:14 +0000
@@ -0,0 +1,38 @@
+/*
+Copyright 2008-2010 Gephi
+Authors : Eduardo Ramos <eduramiba@xxxxxxxxx>
+Website : http://www.gephi.org
+
+This file is part of Gephi.
+
+Gephi is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+Gephi is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with Gephi.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.gephi.datalab.plugin.manipulators.nodes;
+
+import org.gephi.datalab.spi.nodes.NodesManipulator;
+import org.gephi.datalab.spi.nodes.NodesManipulatorBuilder;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ * Builder for TagNodes nodes manipulator.
+ * @author Eduardo Ramos <eduramiba@xxxxxxxxx>
+ */
+@ServiceProvider(service=NodesManipulatorBuilder.class)
+public class TagNodesBuilder implements NodesManipulatorBuilder{
+
+    public NodesManipulator getNodesManipulator() {
+        return new TagNodes();
+    }
+
+}

=== added file 'DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/resources/tag-label.png'
Binary files DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/resources/tag-label.png	1970-01-01 00:00:00 +0000 and DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/resources/tag-label.png	2011-02-01 14:23:14 +0000 differ
=== modified file 'DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/ui/Bundle.properties'
--- DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/ui/Bundle.properties	2010-09-07 09:00:13 +0000
+++ DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/ui/Bundle.properties	2011-02-01 14:23:14 +0000
@@ -5,3 +5,6 @@
 GeneralNumberListStatisticsReportUI.useLinearRegression.text=Show linear regression
 GeneralNumberListStatisticsReportUI.configureHistogramButton.text=Configure Histogram
 GeneralNumberListStatisticsReportUI.divisionsLabel.text=Divisions:
+GeneralColumnAndValueChooserUI.columnLabel.text=Column:
+GeneralColumnAndValueChooserUI.valueLabel.text=Value:
+GeneralColumnAndValueChooserUI.autoComplete.text=Auto- complete

=== added file 'DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/ui/GeneralColumnAndValueChooserUI.form'
--- DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/ui/GeneralColumnAndValueChooserUI.form	1970-01-01 00:00:00 +0000
+++ DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/ui/GeneralColumnAndValueChooserUI.form	2011-02-01 14:23:14 +0000
@@ -0,0 +1,102 @@
+<?xml version="1.1" encoding="UTF-8" ?>
+
+<Form version="1.5" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
+  <AuxValues>
+    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+  </AuxValues>
+
+  <Layout>
+    <DimensionLayout dim="0">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Group type="102" alignment="0" attributes="0">
+              <EmptySpace min="-2" max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="1" max="-2" attributes="0">
+                  <Component id="valueLabel" alignment="0" max="32767" attributes="1"/>
+                  <Component id="columnLabel" alignment="0" pref="63" max="32767" attributes="1"/>
+              </Group>
+              <EmptySpace min="-2" max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="0" attributes="0">
+                  <Group type="102" attributes="0">
+                      <Component id="valueComboBox" pref="145" max="32767" attributes="0"/>
+                      <EmptySpace min="-2" max="-2" attributes="0"/>
+                      <Component id="autoComplete" min="-2" max="-2" attributes="0"/>
+                  </Group>
+                  <Component id="columnComboBox" pref="246" max="32767" attributes="0"/>
+              </Group>
+              <EmptySpace min="-2" max="-2" attributes="0"/>
+          </Group>
+      </Group>
+    </DimensionLayout>
+    <DimensionLayout dim="1">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Group type="102" alignment="0" attributes="0">
+              <EmptySpace max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="3" attributes="0">
+                  <Component id="columnLabel" alignment="3" min="-2" max="-2" attributes="0"/>
+                  <Component id="columnComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
+              </Group>
+              <EmptySpace type="unrelated" max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="3" attributes="0">
+                  <Component id="valueLabel" alignment="3" min="-2" max="-2" attributes="0"/>
+                  <Component id="valueComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
+                  <Component id="autoComplete" alignment="3" min="-2" max="-2" attributes="0"/>
+              </Group>
+              <EmptySpace max="32767" attributes="0"/>
+          </Group>
+      </Group>
+    </DimensionLayout>
+  </Layout>
+  <SubComponents>
+    <Component class="javax.swing.JComboBox" name="columnComboBox">
+      <Properties>
+        <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
+          <StringArray count="0"/>
+        </Property>
+      </Properties>
+      <Events>
+        <EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="columnComboBoxItemStateChanged"/>
+      </Events>
+    </Component>
+    <Component class="javax.swing.JLabel" name="columnLabel">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="org/gephi/datalab/plugin/manipulators/ui/Bundle.properties" key="GeneralColumnAndValueChooserUI.columnLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
+    <Component class="javax.swing.JLabel" name="valueLabel">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="org/gephi/datalab/plugin/manipulators/ui/Bundle.properties" key="GeneralColumnAndValueChooserUI.valueLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
+    <Component class="javax.swing.JComboBox" name="valueComboBox">
+      <Properties>
+        <Property name="editable" type="boolean" value="true"/>
+        <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
+          <StringArray count="0"/>
+        </Property>
+      </Properties>
+    </Component>
+    <Component class="javax.swing.JCheckBox" name="autoComplete">
+      <Properties>
+        <Property name="selected" type="boolean" value="true"/>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="org/gephi/datalab/plugin/manipulators/ui/Bundle.properties" key="GeneralColumnAndValueChooserUI.autoComplete.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+      <Events>
+        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="autoCompleteActionPerformed"/>
+      </Events>
+    </Component>
+  </SubComponents>
+</Form>

=== added file 'DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/ui/GeneralColumnAndValueChooserUI.java'
--- DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/ui/GeneralColumnAndValueChooserUI.java	1970-01-01 00:00:00 +0000
+++ DataLaboratoryPlugin/src/org/gephi/datalab/plugin/manipulators/ui/GeneralColumnAndValueChooserUI.java	2011-02-01 14:23:14 +0000
@@ -0,0 +1,218 @@
+/*
+Copyright 2008-2010 Gephi
+Authors : Eduardo Ramos <eduramiba@xxxxxxxxx>
+Website : http://www.gephi.org
+
+This file is part of Gephi.
+
+Gephi is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+Gephi is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with Gephi.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.gephi.datalab.plugin.manipulators.ui;
+
+import java.util.ArrayList;
+import javax.swing.JPanel;
+import org.gephi.data.attributes.api.AttributeColumn;
+import org.gephi.data.attributes.api.AttributeTable;
+import org.gephi.datalab.api.AttributeColumnsController;
+import org.gephi.datalab.plugin.manipulators.GeneralColumnAndValueChooser;
+import org.gephi.datalab.spi.DialogControls;
+import org.gephi.datalab.spi.Manipulator;
+import org.gephi.datalab.spi.ManipulatorUI;
+import org.gephi.graph.api.Attributes;
+import org.jdesktop.swingx.autocomplete.AutoCompleteDecorator;
+import org.openide.util.Lookup;
+import org.openide.util.NbPreferences;
+
+/**
+ * UI for GeneralColumnAndValueChooser
+ * @author Eduardo Ramos <eduramiba@xxxxxxxxx>
+ */
+public class GeneralColumnAndValueChooserUI extends javax.swing.JPanel implements ManipulatorUI {
+
+    private static final String AUTO_COMPLETE_SAVED_PREFERENCES = "GeneralColumnAndValueChooserUI_autoComplete";
+    private GeneralColumnAndValueChooser manipulator;
+    private AttributeColumn[] columns;
+    private AttributeTable table;
+    private String values[];
+    private static String lastChosenColumn=null;//To try to preserve last chosen column only while this program execution.
+
+    /** Creates new form GeneralColumnAndValueChooserUI */
+    public GeneralColumnAndValueChooserUI() {
+        initComponents();
+    }
+
+    public void setup(Manipulator m, DialogControls dialogControls) {
+        this.manipulator = (GeneralColumnAndValueChooser) m;
+        this.table = manipulator.getTable();
+        refreshColumns();
+        autoComplete.setSelected(NbPreferences.forModule(GeneralChooseColumnsAndRowUI.class).getBoolean(AUTO_COMPLETE_SAVED_PREFERENCES, false));
+        refreshAutoComplete();
+    }
+
+    public void unSetup() {
+        manipulator.setColumn(getChosenColumn());
+        manipulator.setValue(valueComboBox.getSelectedItem() != null ? valueComboBox.getSelectedItem().toString() : null);
+        NbPreferences.forModule(GeneralChooseColumnsAndRowUI.class).putBoolean(AUTO_COMPLETE_SAVED_PREFERENCES, autoComplete.isSelected());
+        lastChosenColumn=getChosenColumn().getTitle();
+    }
+
+    public String getDisplayName() {
+        return manipulator.getName();
+    }
+
+    public JPanel getSettingsPanel() {
+        return this;
+    }
+
+    public boolean isModal() {
+        return true;
+    }
+
+    public AttributeColumn getChosenColumn() {
+        if (columnComboBox.getSelectedIndex() != -1) {
+            return columns[columnComboBox.getSelectedIndex()];
+        } else {
+            return null;
+        }
+    }
+
+    private void refreshColumns() {
+        columns = manipulator.getColumns();
+        for (int i = 0; i < columns.length; i++) {
+            columnComboBox.addItem(columns[i].getTitle());
+        }
+        if(lastChosenColumn!=null&&!lastChosenColumn.isEmpty()){
+            columnComboBox.setSelectedItem(lastChosenColumn);
+        }
+    }
+    private int lastFetchedColumn = -1;
+
+    private void refreshAutoComplete() {
+        Object currentValue = valueComboBox.getSelectedItem();
+        if (autoComplete.isSelected()) {
+            AttributeColumn column = getChosenColumn();
+            if (column != null) {
+                if ((lastFetchedColumn != columnComboBox.getSelectedIndex()) || values == null) {
+                    ArrayList<String> valuesList = new ArrayList<String>();
+                    Object value;
+                    String str;
+                    for (Attributes row : Lookup.getDefault().lookup(AttributeColumnsController.class).getTableAttributeRows(table)) {
+                        value = row.getValue(column.getId());
+                        if (value != null) {
+                            str = value.toString();
+                            if (!valuesList.contains(str)) {
+                                valuesList.add(str);
+                            }
+                        }
+                    }
+                    
+                    values = valuesList.toArray(new String[0]);
+                    lastFetchedColumn = columnComboBox.getSelectedIndex();
+                }
+                valueComboBox.removeAllItems();
+                for (String item : values) {
+                    valueComboBox.addItem(item);
+                }
+                AutoCompleteDecorator.decorate(valueComboBox);
+            }
+        } else {
+            valueComboBox.removeAllItems();
+        }
+        valueComboBox.setSelectedItem(currentValue);
+    }
+
+    /** This method is called from within the constructor to
+     * initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is
+     * always regenerated by the Form Editor.
+     */
+    @SuppressWarnings("unchecked")
+    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
+    private void initComponents() {
+
+        columnComboBox = new javax.swing.JComboBox();
+        columnLabel = new javax.swing.JLabel();
+        valueLabel = new javax.swing.JLabel();
+        valueComboBox = new javax.swing.JComboBox();
+        autoComplete = new javax.swing.JCheckBox();
+
+        columnComboBox.addItemListener(new java.awt.event.ItemListener() {
+            public void itemStateChanged(java.awt.event.ItemEvent evt) {
+                columnComboBoxItemStateChanged(evt);
+            }
+        });
+
+        columnLabel.setText(org.openide.util.NbBundle.getMessage(GeneralColumnAndValueChooserUI.class, "GeneralColumnAndValueChooserUI.columnLabel.text")); // NOI18N
+
+        valueLabel.setText(org.openide.util.NbBundle.getMessage(GeneralColumnAndValueChooserUI.class, "GeneralColumnAndValueChooserUI.valueLabel.text")); // NOI18N
+
+        valueComboBox.setEditable(true);
+
+        autoComplete.setSelected(true);
+        autoComplete.setText(org.openide.util.NbBundle.getMessage(GeneralColumnAndValueChooserUI.class, "GeneralColumnAndValueChooserUI.autoComplete.text")); // NOI18N
+        autoComplete.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                autoCompleteActionPerformed(evt);
+            }
+        });
+
+        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+        this.setLayout(layout);
+        layout.setHorizontalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(layout.createSequentialGroup()
+                .addContainerGap()
+                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
+                    .addComponent(valueLabel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                    .addComponent(columnLabel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 63, Short.MAX_VALUE))
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                    .addGroup(layout.createSequentialGroup()
+                        .addComponent(valueComboBox, 0, 145, Short.MAX_VALUE)
+                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                        .addComponent(autoComplete))
+                    .addComponent(columnComboBox, 0, 246, Short.MAX_VALUE))
+                .addContainerGap())
+        );
+        layout.setVerticalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(layout.createSequentialGroup()
+                .addContainerGap()
+                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                    .addComponent(columnLabel)
+                    .addComponent(columnComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                    .addComponent(valueLabel)
+                    .addComponent(valueComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                    .addComponent(autoComplete))
+                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+        );
+    }// </editor-fold>//GEN-END:initComponents
+
+    private void autoCompleteActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_autoCompleteActionPerformed
+        refreshAutoComplete();
+    }//GEN-LAST:event_autoCompleteActionPerformed
+
+    private void columnComboBoxItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_columnComboBoxItemStateChanged
+        refreshAutoComplete();
+    }//GEN-LAST:event_columnComboBoxItemStateChanged
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JCheckBox autoComplete;
+    private javax.swing.JComboBox columnComboBox;
+    private javax.swing.JLabel columnLabel;
+    private javax.swing.JComboBox valueComboBox;
+    private javax.swing.JLabel valueLabel;
+    // End of variables declaration//GEN-END:variables
+}

=== modified file 'DesktopBranding/src/org/gephi/branding/desktop/multilingual/LanguageAction.java'
--- DesktopBranding/src/org/gephi/branding/desktop/multilingual/LanguageAction.java	2010-12-20 00:55:12 +0000
+++ DesktopBranding/src/org/gephi/branding/desktop/multilingual/LanguageAction.java	2011-02-01 14:23:14 +0000
@@ -45,9 +45,9 @@
 
     public enum Language {
 
-        EN_US("en", "English");
-        //FR_FR("fr", "Français"),
-        //ES_ES("es", "Español");
+        EN_US("en", "English"),
+        FR_FR("fr", "Français"),
+        ES_ES("es", "Español");
         private String locale;
         private String name;
 

=== modified file 'DesktopDataLaboratory/nbproject/genfiles.properties'
--- DesktopDataLaboratory/nbproject/genfiles.properties	2010-09-11 15:09:14 +0000
+++ DesktopDataLaboratory/nbproject/genfiles.properties	2011-02-01 14:23:14 +0000
@@ -1,8 +1,8 @@
-build.xml.data.CRC32=acfd7417
+build.xml.data.CRC32=93720211
 build.xml.script.CRC32=e39c7ed0
 build.xml.stylesheet.CRC32=a56c6a5b@1.42.2
 # This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
 # Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
-nbproject/build-impl.xml.data.CRC32=acfd7417
+nbproject/build-impl.xml.data.CRC32=93720211
 nbproject/build-impl.xml.script.CRC32=91e63bf0
 nbproject/build-impl.xml.stylesheet.CRC32=238281d1@1.42.2

=== modified file 'DesktopDataLaboratory/nbproject/project.xml'
--- DesktopDataLaboratory/nbproject/project.xml	2010-09-11 15:09:14 +0000
+++ DesktopDataLaboratory/nbproject/project.xml	2011-02-01 14:23:14 +0000
@@ -23,14 +23,6 @@
                     </run-dependency>
                 </dependency>
                 <dependency>
-                    <code-name-base>org.gephi.datalab.plugin</code-name-base>
-                    <build-prerequisite/>
-                    <compile-dependency/>
-                    <run-dependency>
-                        <specification-version>0.7.1</specification-version>
-                    </run-dependency>
-                </dependency>
-                <dependency>
                     <code-name-base>org.gephi.dynamic.api</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>

=== modified file 'DesktopDataLaboratory/src/org/gephi/desktop/datalab/DataTableTopComponent.java'
--- DesktopDataLaboratory/src/org/gephi/desktop/datalab/DataTableTopComponent.java	2010-12-28 14:28:46 +0000
+++ DesktopDataLaboratory/src/org/gephi/desktop/datalab/DataTableTopComponent.java	2011-02-01 14:23:14 +0000
@@ -54,6 +54,7 @@
 import org.gephi.data.attributes.api.AttributeListener;
 import org.gephi.data.attributes.api.AttributeModel;
 import org.gephi.data.attributes.api.AttributeTable;
+import org.gephi.datalab.api.DataLaboratoryHelper;
 import org.gephi.datalab.api.DataTablesController;
 import org.gephi.datalab.api.DataTablesEventListener;
 import org.gephi.datalab.spi.columns.AttributeColumnsManipulator;
@@ -74,7 +75,6 @@
 import org.gephi.desktop.datalab.general.actions.AddColumnUI;
 import org.gephi.desktop.datalab.general.actions.CSVExportUI;
 import org.gephi.desktop.datalab.general.actions.MergeColumnsUI;
-import org.gephi.desktop.datalab.utils.DataLaboratoryHelper;
 import org.gephi.ui.utils.DialogFileFilter;
 import org.gephi.ui.utils.UIUtils;
 import org.gephi.utils.TableCSVExporter;
@@ -298,7 +298,7 @@
     }
 
     private synchronized void refreshAll() {
-        if (Lookup.getDefault().lookup(ProjectController.class).getCurrentWorkspace()!=null) {//Some workspace is selected
+        if (Lookup.getDefault().lookup(ProjectController.class).getCurrentWorkspace() != null) {//Some workspace is selected
             refreshTable();
             refreshColumnManipulators();
             refreshGeneralActionsButtons();
@@ -741,7 +741,7 @@
             columns = edgeAvailableColumnsModel.getAvailableColumns();
         }
 
-        DataLaboratoryHelper dlh = new DataLaboratoryHelper();
+        DataLaboratoryHelper dlh = DataLaboratoryHelper.getDefault();
         AttributeColumnsManipulator[] manipulators = dlh.getAttributeColumnsManipulators();
 
         JCommandButtonStrip currentButtonGroup = new JCommandButtonStrip(JCommandButtonStrip.StripOrientation.HORIZONTAL);
@@ -802,7 +802,7 @@
                         button.addActionListener(new ActionListener() {
 
                             public void actionPerformed(ActionEvent e) {
-                                new DataLaboratoryHelper().executeAttributeColumnsManipulator(acm, table, column);
+                                DataLaboratoryHelper.getDefault().executeAttributeColumnsManipulator(acm, table, column);
                             }
                         });
                         popup.addMenuButton(button);
@@ -924,7 +924,7 @@
         //Figure out the index to place the buttons, in order to put them between separator 2 and the boxGlue.
         int index = controlToolbar.getComponentIndex(boxGlue);
 
-        final DataLaboratoryHelper dlh = new DataLaboratoryHelper();
+        final DataLaboratoryHelper dlh = DataLaboratoryHelper.getDefault();
         JButton button;
         for (final GeneralActionsManipulator m : dlh.getGeneralActionsManipulators()) {
             button = new JButton(m.getName(), m.getIcon());
@@ -996,7 +996,7 @@
             button.addActionListener(new ActionListener() {
 
                 public void actionPerformed(ActionEvent e) {
-                    new DataLaboratoryHelper().executeManipulator(m);
+                    DataLaboratoryHelper.getDefault().executeManipulator(m);
                 }
             });
         } else {
@@ -1053,8 +1053,11 @@
         KeyEvent evt = (KeyEvent) event;
 
         if (evt.getID() == KeyEvent.KEY_RELEASED && (evt.getModifiersEx() & KeyEvent.CTRL_DOWN_MASK) != 0 && evt.getKeyCode() == KeyEvent.VK_F) {
-            DataLaboratoryHelper dlh = new DataLaboratoryHelper();
-            dlh.executeManipulator(dlh.getSearchReplaceManipulator());
+            DataLaboratoryHelper dlh = DataLaboratoryHelper.getDefault();
+            GeneralActionsManipulator gam=dlh.getGeneralActionsManipulatorByName("SearchReplace");
+            if (gam!=null) {
+                dlh.executeManipulator(gam);
+            }
             evt.consume();
         }
     }

=== modified file 'DesktopDataLaboratory/src/org/gephi/desktop/datalab/EdgeDataTable.java'
--- DesktopDataLaboratory/src/org/gephi/desktop/datalab/EdgeDataTable.java	2010-10-14 21:06:13 +0000
+++ DesktopDataLaboratory/src/org/gephi/desktop/datalab/EdgeDataTable.java	2011-02-01 14:23:14 +0000
@@ -56,8 +56,8 @@
 import org.gephi.data.attributes.type.NumberList;
 import org.gephi.data.attributes.type.TimeInterval;
 import org.gephi.datalab.api.AttributeColumnsController;
+import org.gephi.datalab.api.DataLaboratoryHelper;
 import org.gephi.datalab.spi.edges.EdgesManipulator;
-import org.gephi.desktop.datalab.utils.DataLaboratoryHelper;
 import org.gephi.graph.api.Edge;
 import org.gephi.graph.api.HierarchicalGraph;
 import org.gephi.tools.api.EditWindowController;
@@ -182,10 +182,10 @@
             @Override
             public void keyReleased(KeyEvent e) {
                 if (e.getKeyCode() == KeyEvent.VK_DELETE) {
+                    DataLaboratoryHelper dlh = DataLaboratoryHelper.getDefault();
                     Edge[] selectedEdges = getEdgesFromSelectedRows();
                     if (selectedEdges.length > 0) {
-                        DataLaboratoryHelper dlh = new DataLaboratoryHelper();
-                        EdgesManipulator del = dlh.getDeleEdgesManipulator();
+                        EdgesManipulator del = dlh.getEdgesManipulatorByName("DeleteEdges");
                         if (del != null) {
                             del.setup(selectedEdges, null);
                             if (del.canExecute()) {
@@ -576,7 +576,7 @@
             JPopupMenu contextMenu = new JPopupMenu();
 
             //First add edges manipulators items:
-            DataLaboratoryHelper dlh = new DataLaboratoryHelper();
+            DataLaboratoryHelper dlh = DataLaboratoryHelper.getDefault();
             Integer lastManipulatorType = null;
             for (EdgesManipulator em : dlh.getEdgesManipulators()) {
                 em.setup(selectedEdges, clickedEdge);

=== modified file 'DesktopDataLaboratory/src/org/gephi/desktop/datalab/NodeDataTable.java'
--- DesktopDataLaboratory/src/org/gephi/desktop/datalab/NodeDataTable.java	2010-12-28 14:28:46 +0000
+++ DesktopDataLaboratory/src/org/gephi/desktop/datalab/NodeDataTable.java	2011-02-01 14:23:14 +0000
@@ -58,6 +58,7 @@
 import org.gephi.data.attributes.type.NumberList;
 import org.gephi.data.attributes.type.TimeInterval;
 import org.gephi.datalab.api.AttributeColumnsController;
+import org.gephi.datalab.api.DataLaboratoryHelper;
 import org.gephi.dynamic.api.DynamicModel.TimeFormat;
 import org.gephi.graph.api.HierarchicalGraph;
 import org.gephi.graph.api.ImmutableTreeNode;
@@ -71,7 +72,6 @@
 import org.openide.awt.MouseUtils;
 import org.openide.util.Lookup;
 import org.gephi.datalab.spi.nodes.NodesManipulator;
-import org.gephi.desktop.datalab.utils.DataLaboratoryHelper;
 import org.gephi.graph.api.Attributes;
 import org.gephi.tools.api.EditWindowController;
 import org.gephi.desktop.datalab.utils.PopupMenuUtils;
@@ -148,10 +148,10 @@
             @Override
             public void keyReleased(KeyEvent e) {
                 if (e.getKeyCode() == KeyEvent.VK_DELETE) {
+                    DataLaboratoryHelper dlh = DataLaboratoryHelper.getDefault();
                     Node[] selectedNodes = getNodesFromSelectedRows();
                     if (selectedNodes.length > 0) {
-                        DataLaboratoryHelper dlh = new DataLaboratoryHelper();
-                        NodesManipulator del = dlh.getDeleteNodesManipulator();
+                        NodesManipulator del = dlh.getNodesManipulatorByName("DeleteNodes");
                         if (del != null) {
                             del.setup(selectedNodes, null);
                             if (del.canExecute()) {
@@ -567,7 +567,7 @@
             JPopupMenu contextMenu = new JPopupMenu();
 
             //First add nodes manipulators items:
-            DataLaboratoryHelper dlh = new DataLaboratoryHelper();
+            DataLaboratoryHelper dlh = DataLaboratoryHelper.getDefault();
             Integer lastManipulatorType = null;
             for (NodesManipulator nm : dlh.getNodesManipulators()) {
                 nm.setup(selectedNodes, clickedNode);

=== modified file 'DesktopDataLaboratory/src/org/gephi/desktop/datalab/general/actions/MergeColumnsUI.java'
--- DesktopDataLaboratory/src/org/gephi/desktop/datalab/general/actions/MergeColumnsUI.java	2010-12-28 14:28:46 +0000
+++ DesktopDataLaboratory/src/org/gephi/desktop/datalab/general/actions/MergeColumnsUI.java	2011-02-01 14:23:14 +0000
@@ -32,8 +32,8 @@
 import org.gephi.data.attributes.api.AttributeColumn;
 import org.gephi.data.attributes.api.AttributeController;
 import org.gephi.data.attributes.api.AttributeTable;
+import org.gephi.datalab.api.DataLaboratoryHelper;
 import org.gephi.datalab.spi.columns.merge.AttributeColumnsMergeStrategy;
-import org.gephi.desktop.datalab.utils.DataLaboratoryHelper;
 import org.gephi.ui.components.richtooltip.RichTooltip;
 import org.netbeans.validation.api.Problems;
 import org.netbeans.validation.api.Validator;
@@ -153,7 +153,7 @@
         if (columnsToMerge.length < 1) {
             return;
         }
-        AttributeColumnsMergeStrategy[] strategies = new DataLaboratoryHelper().getAttributeColumnsMergeStrategies();
+        AttributeColumnsMergeStrategy[] strategies = DataLaboratoryHelper.getDefault().getAttributeColumnsMergeStrategies();
         ArrayList<AttributeColumnsMergeStrategy> availableStrategiesList = new ArrayList<AttributeColumnsMergeStrategy>();
         for (AttributeColumnsMergeStrategy strategy : strategies) {
             strategy.setup(table, columnsToMerge);
@@ -205,7 +205,7 @@
     public void execute() {
         int index = availableStrategiesComboBox.getSelectedIndex();
         if (index != -1) {
-            new DataLaboratoryHelper().executeManipulator(availableMergeStrategies[index]);
+            DataLaboratoryHelper.getDefault().executeManipulator(availableMergeStrategies[index]);
         }
     }
 

=== modified file 'DesktopDataLaboratory/src/org/gephi/desktop/datalab/utils/Bundle.properties'
--- DesktopDataLaboratory/src/org/gephi/desktop/datalab/utils/Bundle.properties	2010-12-28 12:29:32 +0000
+++ DesktopDataLaboratory/src/org/gephi/desktop/datalab/utils/Bundle.properties	2011-02-01 14:23:14 +0000
@@ -1,3 +1,1 @@
 Cell.Popup.subMenu.text=Cell
-SettingsPanel.title={0}
-DataLaboratoryHelper.ui.okButton.text=OK
\ No newline at end of file

=== modified file 'DesktopDataLaboratory/src/org/gephi/desktop/datalab/utils/Bundle_es.properties'
--- DesktopDataLaboratory/src/org/gephi/desktop/datalab/utils/Bundle_es.properties	2010-12-30 12:17:31 +0000
+++ DesktopDataLaboratory/src/org/gephi/desktop/datalab/utils/Bundle_es.properties	2011-02-01 14:23:14 +0000
@@ -6,7 +6,3 @@
 !=Project-Id-Version\: gephi\nReport-Msgid-Bugs-To\: FULL NAME <EMAIL@ADDRESS>\nPOT-Creation-Date\: 2010-04-07 13\:16+0200\nPO-Revision-Date\: 2010-12-26 20\:29+0000\nLast-Translator\: Eduardo Ramos <Unknown>\nLanguage-Team\: Spanish <es@xxxxxx>\nMIME-Version\: 1.0\nContent-Type\: text/plain; charset\=UTF-8\nContent-Transfer-Encoding\: 8bit\nX-Launchpad-Export-Date\: 2010-12-27 04\:43+0000\nX-Generator\: Launchpad (build Unknown)\n
 
 Cell.Popup.subMenu.text=Celda
-
-SettingsPanel.title={0} - Par\u00e1metros
-
-DataLaboratoryHelper.ui.okButton.text=Ok

=== modified file 'DesktopDataLaboratory/src/org/gephi/desktop/datalab/utils/Bundle_fr.properties'
--- DesktopDataLaboratory/src/org/gephi/desktop/datalab/utils/Bundle_fr.properties	2010-12-30 12:17:31 +0000
+++ DesktopDataLaboratory/src/org/gephi/desktop/datalab/utils/Bundle_fr.properties	2011-02-01 14:23:14 +0000
@@ -6,7 +6,3 @@
 !=Project-Id-Version\: gephi\nReport-Msgid-Bugs-To\: FULL NAME <EMAIL@ADDRESS>\nPOT-Creation-Date\: 2010-04-07 13\:16+0200\nPO-Revision-Date\: 2010-12-25 20\:14+0000\nLast-Translator\: S\u00e9bastien Heymann <Unknown>\nLanguage-Team\: French <fr@xxxxxx>\nMIME-Version\: 1.0\nContent-Type\: text/plain; charset\=UTF-8\nContent-Transfer-Encoding\: 8bit\nX-Launchpad-Export-Date\: 2010-12-26 04\:54+0000\nX-Generator\: Launchpad (build Unknown)\n
 
 Cell.Popup.subMenu.text=Cellule
-
-SettingsPanel.title={0} - Param\u00e8tres
-
-DataLaboratoryHelper.ui.okButton.text=Ok

=== removed file 'DesktopDataLaboratory/src/org/gephi/desktop/datalab/utils/DataLaboratoryHelper.java'
--- DesktopDataLaboratory/src/org/gephi/desktop/datalab/utils/DataLaboratoryHelper.java	2010-09-16 21:39:28 +0000
+++ DesktopDataLaboratory/src/org/gephi/desktop/datalab/utils/DataLaboratoryHelper.java	1970-01-01 00:00:00 +0000
@@ -1,350 +0,0 @@
-/*
-Copyright 2008-2010 Gephi
-Authors : Eduardo Ramos <eduramiba@xxxxxxxxx>
-Website : http://www.gephi.org
-
-This file is part of Gephi.
-
-Gephi is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
-
-Gephi is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with Gephi.  If not, see <http://www.gnu.org/licenses/>.
- */
-package org.gephi.desktop.datalab.utils;
-
-import java.awt.Dialog;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.WindowAdapter;
-import java.awt.event.WindowEvent;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import javax.swing.JButton;
-import javax.swing.JComponent;
-import javax.swing.JPanel;
-import javax.swing.SwingUtilities;
-import org.gephi.data.attributes.api.AttributeColumn;
-import org.gephi.data.attributes.api.AttributeTable;
-import org.gephi.datalab.plugin.manipulators.edges.DeleteEdges;
-import org.gephi.datalab.plugin.manipulators.general.SearchReplace;
-import org.gephi.datalab.plugin.manipulators.nodes.DeleteNodes;
-import org.gephi.datalab.spi.DialogControls;
-import org.gephi.datalab.spi.Manipulator;
-import org.gephi.datalab.spi.ManipulatorUI;
-import org.gephi.datalab.spi.columns.AttributeColumnsManipulator;
-import org.gephi.datalab.spi.columns.AttributeColumnsManipulatorUI;
-import org.gephi.datalab.spi.columns.merge.AttributeColumnsMergeStrategy;
-import org.gephi.datalab.spi.columns.merge.AttributeColumnsMergeStrategyBuilder;
-import org.gephi.datalab.spi.values.AttributeValueManipulator;
-import org.gephi.datalab.spi.values.AttributeValueManipulatorBuilder;
-import org.gephi.datalab.spi.edges.EdgesManipulator;
-import org.gephi.datalab.spi.edges.EdgesManipulatorBuilder;
-import org.gephi.datalab.spi.general.GeneralActionsManipulator;
-import org.gephi.datalab.spi.general.PluginGeneralActionsManipulator;
-import org.gephi.datalab.spi.nodes.NodesManipulator;
-import org.gephi.datalab.spi.nodes.NodesManipulatorBuilder;
-import org.openide.DialogDescriptor;
-import org.openide.DialogDisplayer;
-import org.openide.util.Lookup;
-import org.openide.util.NbBundle;
-
-/**
- * Helper class for simplifying the implementation of Data Laboratory UI.
- * @author Eduardo Ramos <eduramiba@xxxxxxxxx>
- */
-public class DataLaboratoryHelper{
-
-    /**
-     * <p>Prepares an array with one new instance of every NodesManipulator
-     * that has a builder registered and returns it.</p>
-     * <p>It also returns the manipulators ordered first by type and then by position.</p>
-     * @return Array of all NodesManipulator implementations
-     */
-    public NodesManipulator[] getNodesManipulators() {
-        ArrayList<NodesManipulator> nodesManipulators = new ArrayList<NodesManipulator>();
-        for (NodesManipulatorBuilder nm : Lookup.getDefault().lookupAll(NodesManipulatorBuilder.class)) {
-            nodesManipulators.add(nm.getNodesManipulator());
-        }
-        sortManipulators(nodesManipulators);
-        return nodesManipulators.toArray(new NodesManipulator[0]);
-    }
-
-    /**
-     * <p>Prepares an array with one new instance of every EdgesManipulator
-     * that has a builder registered and returns it.</p>
-     * <p>It also returns the manipulators ordered first by type and then by position.</p>
-     * @return Array of all EdgesManipulator implementations
-     */
-    public EdgesManipulator[] getEdgesManipulators() {
-        ArrayList<EdgesManipulator> edgesManipulators = new ArrayList<EdgesManipulator>();
-        for (EdgesManipulatorBuilder em : Lookup.getDefault().lookupAll(EdgesManipulatorBuilder.class)) {
-            edgesManipulators.add(em.getEdgesManipulator());
-        }
-        sortManipulators(edgesManipulators);
-        return edgesManipulators.toArray(new EdgesManipulator[0]);
-    }
-
-    /**
-     * <p>Prepares an array with one instance of every GeneralActionsManipulator that is registered.</p>
-     * <p>It also returns the manipulators ordered first by type and then by position.</p>
-     * @return Array of all GeneralActionsManipulator implementations
-     */
-    public GeneralActionsManipulator[] getGeneralActionsManipulators() {
-        ArrayList<GeneralActionsManipulator> generalActionsManipulators = new ArrayList<GeneralActionsManipulator>();
-        generalActionsManipulators.addAll(Lookup.getDefault().lookupAll(GeneralActionsManipulator.class));
-        sortManipulators(generalActionsManipulators);
-        return generalActionsManipulators.toArray(new GeneralActionsManipulator[0]);
-    }
-
-    /**
-     * <p>Prepares an array with one instance of every PluginGeneralActionsManipulator that is registered.</p>
-     * <p>It also returns the manipulators ordered first by type and then by position.</p>
-     * @return Array of all PluginGeneralActionsManipulator implementations
-     */
-    public PluginGeneralActionsManipulator[] getPluginGeneralActionsManipulators() {
-        ArrayList<PluginGeneralActionsManipulator> pluginGeneralActionsManipulators = new ArrayList<PluginGeneralActionsManipulator>();
-        pluginGeneralActionsManipulators.addAll(Lookup.getDefault().lookupAll(PluginGeneralActionsManipulator.class));
-        sortManipulators(pluginGeneralActionsManipulators);
-        return pluginGeneralActionsManipulators.toArray(new PluginGeneralActionsManipulator[0]);
-    }
-
-    /**
-     * <p>Prepares an array that has one instance of every AttributeColumnsManipulator implementation
-     * that has a builder registered and returns it.</p>
-     * <p>It also returns the manipulators ordered first by type and then by position.</p>
-     * @return Array of all AttributeColumnsManipulator implementations
-     */
-    public AttributeColumnsManipulator[] getAttributeColumnsManipulators() {
-        ArrayList<AttributeColumnsManipulator> attributeColumnsManipulators = new ArrayList<AttributeColumnsManipulator>();
-        attributeColumnsManipulators.addAll(Lookup.getDefault().lookupAll(AttributeColumnsManipulator.class));
-        sortAttributeColumnsManipulators(attributeColumnsManipulators);
-        return attributeColumnsManipulators.toArray(new AttributeColumnsManipulator[0]);
-    }
-
-    /**
-     * <p>Prepares an array with one new instance of every AttributeValueManipulator
-     * that has a builder registered and returns it.</p>
-     * <p>It also returns the manipulators ordered first by type and then by position.</p>
-     * @return Array of all AttributeValueManipulator implementations
-     */
-    public AttributeValueManipulator[] getAttributeValueManipulators() {
-        ArrayList<AttributeValueManipulator> attributeValueManipulators = new ArrayList<AttributeValueManipulator>();
-        for (AttributeValueManipulatorBuilder am : Lookup.getDefault().lookupAll(AttributeValueManipulatorBuilder.class)) {
-            attributeValueManipulators.add(am.getAttributeValueManipulator());
-        }
-        sortManipulators(attributeValueManipulators);
-        return attributeValueManipulators.toArray(new AttributeValueManipulator[0]);
-    }
-
-    /**
-     * <p>Prepares an array that has one new instance of every AttributeColumnsMergeStrategy implementation that is registered.</p>
-     * <p>It also returns the manipulators ordered first by type and then by position.</p>
-     * @return Array of all AttributeColumnsMergeStrategy implementations
-     */
-    public AttributeColumnsMergeStrategy[] getAttributeColumnsMergeStrategies() {
-        ArrayList<AttributeColumnsMergeStrategy> strategies = new ArrayList<AttributeColumnsMergeStrategy>();
-        for (AttributeColumnsMergeStrategyBuilder cs : Lookup.getDefault().lookupAll(AttributeColumnsMergeStrategyBuilder.class)) {
-            strategies.add(cs.getAttributeColumnsMergeStrategy());
-        }
-        sortManipulators(strategies);
-        return strategies.toArray(new AttributeColumnsMergeStrategy[0]);
-    }
-
-    private void sortManipulators(ArrayList<? extends Manipulator> m) {
-        Collections.sort(m, new Comparator<Manipulator>() {
-
-            public int compare(Manipulator o1, Manipulator o2) {
-                //Order by type, position.
-                if (o1.getType() == o2.getType()) {
-                    return o1.getPosition() - o2.getPosition();
-                } else {
-                    return o1.getType() - o2.getType();
-                }
-            }
-        });
-    }
-
-    private void sortAttributeColumnsManipulators(ArrayList<? extends AttributeColumnsManipulator> m) {
-        Collections.sort(m, new Comparator<AttributeColumnsManipulator>() {
-
-            public int compare(AttributeColumnsManipulator o1, AttributeColumnsManipulator o2) {
-                //Order by type, position.
-                if (o1.getType() == o2.getType()) {
-                    return o1.getPosition() - o2.getPosition();
-                } else {
-                    return o1.getType() - o2.getType();
-                }
-            }
-        });
-    }
-
-    /**
-     * Prepares the dialog UI of a manipulator if it has one and executes the manipulator in a separate
-     * Thread when the dialog is accepted or directly if there is no UI.
-     * @param m Manipulator to execute
-     */
-    public void executeManipulator(final Manipulator m) {
-        if (m.canExecute()) {
-            SwingUtilities.invokeLater(new Runnable() {
-
-                public void run() {
-
-                    final ManipulatorUI ui = m.getUI();
-                    //Show a dialog for the manipulator UI if it provides one. If not, execute the manipulator directly:
-                    if (ui != null) {
-                        final JButton okButton = new JButton(NbBundle.getMessage(DataLaboratoryHelper.class, "DataLaboratoryHelper.ui.okButton.text"));
-                        DialogControls dialogControls = new DialogControlsImpl(okButton);
-                        ui.setup(m, dialogControls);
-                        JPanel settingsPanel = ui.getSettingsPanel();
-                        DialogDescriptor dd = new DialogDescriptor(settingsPanel, NbBundle.getMessage(DataLaboratoryHelper.class, "SettingsPanel.title", ui.getDisplayName()), ui.isModal(), new ActionListener() {
-
-                            public void actionPerformed(ActionEvent e) {
-                                if (e.getSource().equals(okButton)) {
-                                    ui.unSetup();
-                                    executeManipulatorInOtherThread(m);
-                                } else {
-                                    ui.unSetup();
-                                }
-                            }
-                        });
-                        dd.setOptions(new Object[]{okButton, DialogDescriptor.CANCEL_OPTION});
-                        dd.setClosingOptions(null);//All options close
-                        Dialog dialog = DialogDisplayer.getDefault().createDialog(dd);
-                        dialog.addWindowListener(new WindowAdapter() {
-
-                            @Override
-                            public void windowClosing(WindowEvent e) {
-                                ui.unSetup();
-                            }
-                        });
-                        dialog.setVisible(true);
-                    } else {
-                        executeManipulatorInOtherThread(m);
-                    }
-                }
-            });
-        }
-    }
-
-    private void executeManipulatorInOtherThread(final Manipulator m) {
-        new Thread() {
-
-            @Override
-            public void run() {
-                m.execute();
-            }
-        }.start();
-    }
-
-    /**
-     * Prepares the dialog UI of a AttributeColumnsManipulator if it has one and executes the manipulator in a separate
-     * Thread when the dialog is accepted or directly if there is no UI.
-     * @param m AttributeColumnsManipulator
-     * @param table Table of the column
-     * @param column Column to manipulate
-     */
-    public void executeAttributeColumnsManipulator(final AttributeColumnsManipulator m, final AttributeTable table, final AttributeColumn column) {
-        if (m.canManipulateColumn(table, column)) {
-            SwingUtilities.invokeLater(new Runnable() {
-
-                public void run() {
-                    final AttributeColumnsManipulatorUI ui = m.getUI(table, column);
-                    //Show a dialog for the manipulator UI if it provides one. If not, execute the manipulator directly:
-                    if (ui != null) {
-                        final JButton okButton = new JButton(NbBundle.getMessage(DataLaboratoryHelper.class, "DataLaboratoryHelper.ui.okButton.text"));
-                        DialogControls dialogControls = new DialogControlsImpl(okButton);
-                        ui.setup(m, table, column, dialogControls);
-                        JPanel settingsPanel = ui.getSettingsPanel();
-                        DialogDescriptor dd = new DialogDescriptor(settingsPanel, NbBundle.getMessage(DataLaboratoryHelper.class, "SettingsPanel.title", ui.getDisplayName()), ui.isModal(), new ActionListener() {
-
-                            public void actionPerformed(ActionEvent e) {
-                                if (e.getSource().equals(okButton)) {
-                                    ui.unSetup();
-                                    executeAttributeColumnsManipulatorInOtherThread(m, table, column);
-                                } else {
-                                    ui.unSetup();
-                                }
-                            }
-                        });
-                        dd.setOptions(new Object[]{okButton, DialogDescriptor.CANCEL_OPTION});
-                        dd.setClosingOptions(null);//All options close
-                        Dialog dialog = DialogDisplayer.getDefault().createDialog(dd);
-                        dialog.addWindowListener(new WindowAdapter() {
-
-                            @Override
-                            public void windowClosing(WindowEvent e) {
-                                ui.unSetup();
-                            }
-                        });
-                        dialog.setVisible(true);
-                    } else {
-                        executeAttributeColumnsManipulatorInOtherThread(m, table, column);
-                    }
-                }
-            });
-        }
-    }
-
-    private void executeAttributeColumnsManipulatorInOtherThread(final AttributeColumnsManipulator m, final AttributeTable table, final AttributeColumn column) {
-        new Thread() {
-
-            @Override
-            public void run() {
-                m.execute(table, column);
-            }
-        }.start();
-    }
-
-    /**
-     * Special method for making public DeleteNodes manipulator so it can be specifically retrieved from Data Table UI.
-     * It is used for reacting to delete key.
-     * @return DeleteNodes new instance
-     */
-    public NodesManipulator getDeleteNodesManipulator() {
-        return new DeleteNodes();
-    }
-
-    /**
-     * Special method for making public DeleteEdges manipulator so it can be specifically retrieved from Data Table UI.
-     * It is used for reacting to delete key.
-     * @return DeleteEdges new instance
-     */
-    public EdgesManipulator getDeleEdgesManipulator() {
-        return new DeleteEdges();
-    }
-
-    /**
-     * Special method for making public SearchReplace manipulator so it can be specifically retrieved from Data Table UI.
-     * It is used for reacting to Ctrl+F keys combination.
-     * @return SearchReplace new instance
-     */
-    public GeneralActionsManipulator getSearchReplaceManipulator() {
-        return new SearchReplace();
-    }
-
-    class DialogControlsImpl implements DialogControls {
-
-        JComponent okButton;
-
-        public DialogControlsImpl(JComponent okButton) {
-            this.okButton = okButton;
-        }
-
-        public void setOkButtonEnabled(boolean enabled) {
-            okButton.setEnabled(enabled);
-        }
-
-        public boolean isOkButtonEnabled(){
-            return okButton.isEnabled();
-        }
-    }
-}

=== modified file 'DesktopDataLaboratory/src/org/gephi/desktop/datalab/utils/PopupMenuUtils.java'
--- DesktopDataLaboratory/src/org/gephi/desktop/datalab/utils/PopupMenuUtils.java	2010-09-07 12:43:59 +0000
+++ DesktopDataLaboratory/src/org/gephi/desktop/datalab/utils/PopupMenuUtils.java	2011-02-01 14:23:14 +0000
@@ -26,6 +26,7 @@
 import javax.swing.JMenuItem;
 import org.gephi.data.attributes.api.AttributeColumn;
 import org.gephi.data.attributes.api.AttributeRow;
+import org.gephi.datalab.api.DataLaboratoryHelper;
 import org.gephi.datalab.spi.Manipulator;
 import org.gephi.datalab.spi.values.AttributeValueManipulator;
 import org.openide.util.ImageUtilities;
@@ -48,7 +49,7 @@
             menuItem.addActionListener(new ActionListener() {
 
                 public void actionPerformed(ActionEvent e) {
-                    DataLaboratoryHelper dlh = new DataLaboratoryHelper();
+                    DataLaboratoryHelper dlh = DataLaboratoryHelper.getDefault();
                     dlh.executeManipulator(nm);
                 }
             });
@@ -59,7 +60,7 @@
     }
 
     public static JMenu createSubMenuFromRowColumn(AttributeRow row, AttributeColumn column) {
-        DataLaboratoryHelper dlh = new DataLaboratoryHelper();
+        DataLaboratoryHelper dlh = DataLaboratoryHelper.getDefault();
         JMenu subMenu = new JMenu(NbBundle.getMessage(PopupMenuUtils.class, "Cell.Popup.subMenu.text"));
         subMenu.setIcon(ImageUtilities.loadImageIcon("org/gephi/desktop/datalab/resources/table-select.png", true));
 

=== modified file 'VisualizationAPI/nbproject/genfiles.properties'
--- VisualizationAPI/nbproject/genfiles.properties	2010-10-07 08:53:53 +0000
+++ VisualizationAPI/nbproject/genfiles.properties	2011-02-01 14:23:14 +0000
@@ -1,8 +1,8 @@
-build.xml.data.CRC32=006815d5
+build.xml.data.CRC32=aa440a30
 build.xml.script.CRC32=84b64b02
 build.xml.stylesheet.CRC32=a56c6a5b@1.42.2
 # This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
 # Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
-nbproject/build-impl.xml.data.CRC32=006815d5
+nbproject/build-impl.xml.data.CRC32=aa440a30
 nbproject/build-impl.xml.script.CRC32=1628a581
 nbproject/build-impl.xml.stylesheet.CRC32=238281d1@1.42.2

=== modified file 'VisualizationAPI/nbproject/project.xml'
--- VisualizationAPI/nbproject/project.xml	2010-10-07 08:53:53 +0000
+++ VisualizationAPI/nbproject/project.xml	2011-02-01 14:23:14 +0000
@@ -26,6 +26,7 @@
             <public-packages>
                 <package>org.gephi.visualization.api</package>
                 <package>org.gephi.visualization.impl</package>
+                <package>org.gephi.visualization.spi</package>
             </public-packages>
         </data>
     </configuration>

=== added directory 'VisualizationAPI/src/org/gephi/visualization/spi'
=== added file 'VisualizationAPI/src/org/gephi/visualization/spi/GraphContextMenuItem.java'
--- VisualizationAPI/src/org/gephi/visualization/spi/GraphContextMenuItem.java	1970-01-01 00:00:00 +0000
+++ VisualizationAPI/src/org/gephi/visualization/spi/GraphContextMenuItem.java	2011-02-01 14:23:14 +0000
@@ -0,0 +1,125 @@
+
+/*
+Copyright 2008-2010 Gephi
+Authors : Eduardo Ramos <eduramiba@xxxxxxxxx>
+Website : http://www.gephi.org
+
+This file is part of Gephi.
+
+Gephi is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+Gephi is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with Gephi.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.gephi.visualization.spi;
+
+import javax.swing.Icon;
+import org.gephi.graph.api.HierarchicalGraph;
+import org.gephi.graph.api.Node;
+
+/**
+ * <p>Interface from providing graph context menu items as services.</p>
+ * <p>All context menu items are able to:</p>
+ * <ul>
+ *  <li>Execute an action</li>
+ *  <li>Provide a name, type and order of appearance (position in group of its type)</li>
+ * <li>Indicate wether they have to be available (appear in the context menu) or not</li>
+ *  <li>Indicate wether they have to be executable (enabled in the context menu) or not</li>
+ *  <li>Provide and icon or not</li>
+ * </ul>
+ * <p>Used for different manipulators such as NodesManipulator, EdgesManipulator and GeneralActionsManipulator.</p>
+ * <p>The only methods that are called before setting up an item with the data are <b>getSubItems, getType and getPosition.</b>
+ * This way, the other methods behaviour can depend on the data that has been setup before</p>
+ * <p><b>getSubItems will be called before and after setup. Take care when the nodes are null!</b></p>
+ *
+ * To provide a context menu item, a class has to implement this interface and have a <code>@ServiceProvider</code> annotation
+ * @author Eduardo Ramos <eduramiba@xxxxxxxxx>
+ */
+public interface GraphContextMenuItem {
+
+    /**
+     * Prepare nodes for this item. Note that nodes could contain 0 nodes.
+     * @param graph Hierarchical graph
+     * @param nodes All selected nodes
+     */
+    void setup(HierarchicalGraph graph, Node[] nodes);
+
+    /**
+     * Execute this item.
+     * It will operate with data like nodes and edges previously setup for the type of manipulator.
+     */
+    void execute();
+
+    /**
+     * <p>This is optional. Return sub items for this menu item if desired.</p>
+     * <p>If this item should contain more items, return a new instance of each sub item.
+     * If not return null and implement execute for this item.</p>
+     * <p>In order to declare mnemonic keys for subitem(s), the implementation of this item
+     * must return the subitem(s) with the mnemonic even when it has not been setup with any node.
+     * If you don't need a mnemonic, return null if the item is not setup.</p>
+     * @return
+     */
+    GraphContextMenuItem[] getSubItems();
+
+    /**
+     * <p>Return name to show for this item in the context menu.</p>
+     * <p>Implementations can provide different names depending on the data this
+     * item has (for example depending on the number of nodes).</p>
+     * @return Name to show at current time and conditions
+     */
+    String getName();
+
+    /**
+     * Description of the context menu item, to show as tooltip.
+     * @return Description or null
+     */
+    String getDescription();
+
+    /**
+     * Indicates if this item has to appear in the context menu at all
+     * @return True to show, false otherwise
+     */
+    boolean isAvailable();
+
+    /**
+     * Indicates if this item can be executed when it is available.
+     * Implementations should evaluate the current data and conditions.
+     * @return True if it has to be executable, false otherwise
+     */
+    boolean canExecute();
+
+    /**
+     * Type of item. This is used for separating the items
+     * in groups when shown, using popup separators. First types to show will be the lesser.
+     * @return Type of this manipulator
+     */
+    int getType();
+
+    /**
+     * Returns a position value that indicates the position
+     * of this Manipulator in its type group. Less means upper.
+     * @return This Manipulator position
+     */
+    int getPosition();
+
+    /**
+     * Optional. Allows to declare a mnemonic key for this item in the menu.
+     * There should not be 2 items with the same mnemonic at the same time.
+     * @return Integer from <code>KeyEvent</code> values or null
+     */
+    Integer getMnemonicKey();
+
+    /**
+     * Returns an icon for this item if necessary.
+     * @return Icon or null
+     */
+    Icon getIcon();
+}

=== modified file 'VisualizationModule/nbproject/genfiles.properties'
--- VisualizationModule/nbproject/genfiles.properties	2010-09-16 21:37:51 +0000
+++ VisualizationModule/nbproject/genfiles.properties	2011-02-01 14:23:14 +0000
@@ -1,8 +1,8 @@
-build.xml.data.CRC32=4d1f53aa
+build.xml.data.CRC32=4ad970b7
 build.xml.script.CRC32=587055f0
 build.xml.stylesheet.CRC32=a56c6a5b@1.42.2
 # This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
 # Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
-nbproject/build-impl.xml.data.CRC32=4d1f53aa
+nbproject/build-impl.xml.data.CRC32=4ad970b7
 nbproject/build-impl.xml.script.CRC32=413ca750
 nbproject/build-impl.xml.stylesheet.CRC32=238281d1@1.42.2

=== modified file 'VisualizationModule/nbproject/project.xml'
--- VisualizationModule/nbproject/project.xml	2010-09-16 21:37:51 +0000
+++ VisualizationModule/nbproject/project.xml	2011-02-01 14:23:14 +0000
@@ -15,6 +15,14 @@
                     </run-dependency>
                 </dependency>
                 <dependency>
+                    <code-name-base>org.gephi.datalab.api</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>0.7.1.4</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
                     <code-name-base>org.gephi.desktop.project</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>

=== added file 'VisualizationModule/src/org/gephi/visualization/api/resources/delete.png'
Binary files VisualizationModule/src/org/gephi/visualization/api/resources/delete.png	1970-01-01 00:00:00 +0000 and VisualizationModule/src/org/gephi/visualization/api/resources/delete.png	2011-02-01 14:23:14 +0000 differ
=== added file 'VisualizationModule/src/org/gephi/visualization/api/resources/free.png'
Binary files VisualizationModule/src/org/gephi/visualization/api/resources/free.png	1970-01-01 00:00:00 +0000 and VisualizationModule/src/org/gephi/visualization/api/resources/free.png	2011-02-01 14:23:14 +0000 differ
=== added file 'VisualizationModule/src/org/gephi/visualization/api/resources/globe-network.png'
Binary files VisualizationModule/src/org/gephi/visualization/api/resources/globe-network.png	1970-01-01 00:00:00 +0000 and VisualizationModule/src/org/gephi/visualization/api/resources/globe-network.png	2011-02-01 14:23:14 +0000 differ
=== added file 'VisualizationModule/src/org/gephi/visualization/api/resources/table-select.png'
Binary files VisualizationModule/src/org/gephi/visualization/api/resources/table-select.png	1970-01-01 00:00:00 +0000 and VisualizationModule/src/org/gephi/visualization/api/resources/table-select.png	2011-02-01 14:23:14 +0000 differ
=== removed file 'VisualizationModule/src/org/gephi/visualization/apiimpl/Bundle.properties'
--- VisualizationModule/src/org/gephi/visualization/apiimpl/Bundle.properties	2010-09-16 21:37:51 +0000
+++ VisualizationModule/src/org/gephi/visualization/apiimpl/Bundle.properties	1970-01-01 00:00:00 +0000
@@ -1,14 +0,0 @@
-GraphContextMenu_Group = Group
-GraphContextMenu_Ungroup = Ungroup
-GraphContextMenu_Expand = Expand
-GraphContextMenu_Contract = Contract
-GraphContextMenu_Settle = Settle
-GraphContextMenu_Free = Free
-GraphContextMenu_Delete = Delete
-GraphContextMenu_MoveToWorkspace = Move to...
-GraphContextMenu_MoveToWorkspace_NewWorkspace = New workspace
-GraphContextMenu_CopyToWorkspace = Copy to...
-GraphContextMenu_CopyToWorkspace_NewWorkspace = New workspace
-
-GraphContextMenu.Delete.message = Nodes will be deleted, do you want to proceed?
-GraphContextMenu.Delete.message.title = Delete nodes
\ No newline at end of file

=== removed file 'VisualizationModule/src/org/gephi/visualization/apiimpl/Bundle_es.properties'
--- VisualizationModule/src/org/gephi/visualization/apiimpl/Bundle_es.properties	2010-12-30 12:17:31 +0000
+++ VisualizationModule/src/org/gephi/visualization/apiimpl/Bundle_es.properties	1970-01-01 00:00:00 +0000
@@ -1,32 +0,0 @@
-# Spanish translation for gephi
-# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010
-# This file is distributed under the same license as the gephi package.
-# FIRST AUTHOR <EMAIL@ADDRESS>, 2010.
-#
-!=Project-Id-Version\: gephi\nReport-Msgid-Bugs-To\: FULL NAME <EMAIL@ADDRESS>\nPOT-Creation-Date\: 2010-04-07 13\:16+0200\nPO-Revision-Date\: 2010-12-26 00\:11+0000\nLast-Translator\: Eduardo Ramos <Unknown>\nLanguage-Team\: Spanish <es@xxxxxx>\nMIME-Version\: 1.0\nContent-Type\: text/plain; charset\=UTF-8\nContent-Transfer-Encoding\: 8bit\nX-Launchpad-Export-Date\: 2010-12-27 04\:43+0000\nX-Generator\: Launchpad (build Unknown)\n
-
-GraphContextMenu_Group=Agrupar
-
-GraphContextMenu_Ungroup=Desagrupar
-
-GraphContextMenu_Expand=Expandir
-
-GraphContextMenu_Contract=Contraer
-
-GraphContextMenu_Settle=Bloquear
-
-GraphContextMenu_Free=Desbloquear
-
-GraphContextMenu_Delete=Eliminar
-
-GraphContextMenu_MoveToWorkspace=Mover a...
-
-GraphContextMenu_MoveToWorkspace_NewWorkspace=Nuevo espacio de trabajo
-
-GraphContextMenu_CopyToWorkspace=Copiar a...
-
-GraphContextMenu_CopyToWorkspace_NewWorkspace=Nuevo espacio de trabajo
-
-GraphContextMenu.Delete.message=Los nodos ser\u00e1n eliminados, \u00bfContinuar?
-
-GraphContextMenu.Delete.message.title=Eliminar nodos

=== removed file 'VisualizationModule/src/org/gephi/visualization/apiimpl/Bundle_fr.properties'
--- VisualizationModule/src/org/gephi/visualization/apiimpl/Bundle_fr.properties	2010-12-30 12:17:31 +0000
+++ VisualizationModule/src/org/gephi/visualization/apiimpl/Bundle_fr.properties	1970-01-01 00:00:00 +0000
@@ -1,32 +0,0 @@
-# French translation for gephi
-# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010
-# This file is distributed under the same license as the gephi package.
-# FIRST AUTHOR <EMAIL@ADDRESS>, 2010.
-#
-!=Project-Id-Version\: gephi\nReport-Msgid-Bugs-To\: FULL NAME <EMAIL@ADDRESS>\nPOT-Creation-Date\: 2010-04-07 13\:16+0200\nPO-Revision-Date\: 2010-12-25 19\:49+0000\nLast-Translator\: S\u00e9bastien Heymann <Unknown>\nLanguage-Team\: French <fr@xxxxxx>\nMIME-Version\: 1.0\nContent-Type\: text/plain; charset\=UTF-8\nContent-Transfer-Encoding\: 8bit\nX-Launchpad-Export-Date\: 2010-12-26 04\:53+0000\nX-Generator\: Launchpad (build Unknown)\n
-
-GraphContextMenu_Group=Grouper
-
-GraphContextMenu_Ungroup=D\u00e9grouper
-
-GraphContextMenu_Expand=\u00c9tendre
-
-GraphContextMenu_Contract=Contracter
-
-GraphContextMenu_Settle=Fixer
-
-GraphContextMenu_Free=Lib\u00e9rer
-
-GraphContextMenu_Delete=Supprimer
-
-GraphContextMenu_MoveToWorkspace=D\u00e9placer vers...
-
-GraphContextMenu_MoveToWorkspace_NewWorkspace=Nouvel espace de travail
-
-GraphContextMenu_CopyToWorkspace=Copier vers...
-
-GraphContextMenu_CopyToWorkspace_NewWorkspace=Nouvel espace de travail
-
-GraphContextMenu.Delete.message=Les noeuds seront supprim\u00e9s. Voulez-vous continuer ?
-
-GraphContextMenu.Delete.message.title=Suppression des noeuds

=== modified file 'VisualizationModule/src/org/gephi/visualization/apiimpl/GraphContextMenu.java'
--- VisualizationModule/src/org/gephi/visualization/apiimpl/GraphContextMenu.java	2010-09-16 21:37:51 +0000
+++ VisualizationModule/src/org/gephi/visualization/apiimpl/GraphContextMenu.java	2011-02-01 14:23:14 +0000
@@ -22,22 +22,20 @@
 
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
-import javax.swing.AbstractAction;
-import javax.swing.Action;
+import java.awt.event.KeyEvent;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
 import javax.swing.JMenu;
 import javax.swing.JMenuItem;
 import javax.swing.JPopupMenu;
-import org.gephi.project.api.ProjectController;
-import org.gephi.project.api.Workspace;
-import org.gephi.project.api.WorkspaceInformation;
-import org.gephi.project.api.WorkspaceProvider;
+import javax.swing.KeyStroke;
+import org.gephi.graph.api.HierarchicalGraph;
+import org.gephi.graph.api.Node;
 import org.gephi.visualization.VizController;
 import org.gephi.visualization.bridge.DHNSEventBridge;
-import org.openide.DialogDisplayer;
-import org.openide.NotifyDescriptor;
-import org.openide.util.ImageUtilities;
+import org.gephi.visualization.spi.GraphContextMenuItem;
 import org.openide.util.Lookup;
-import org.openide.util.NbBundle;
 
 /**
  *
@@ -54,173 +52,111 @@
     }
 
     public JPopupMenu getMenu() {
-        //Group
-        GraphContextMenuAction groupAction = new GraphContextMenuImpl("GraphContextMenu_Group", "org/gephi/visualization/api/resources/group.png") {
-
-            @Override
-            public void actionPerformed(ActionEvent e) {
-                eventBridge.group();
-            }
-        };
-        groupAction.setEnabled(eventBridge.canGroup());
-
-        //Ungroup
-        GraphContextMenuAction ungroupAction = new GraphContextMenuImpl("GraphContextMenu_Ungroup", "org/gephi/visualization/api/resources/ungroup.png") {
-
-            @Override
-            public void actionPerformed(ActionEvent e) {
-                eventBridge.ungroup();
-            }
-        };
-        ungroupAction.setEnabled(eventBridge.canUngroup());
-
-        //Expand
-        GraphContextMenuAction expandAction = new GraphContextMenuImpl("GraphContextMenu_Expand", "org/gephi/visualization/api/resources/expand.png") {
-
-            @Override
-            public void actionPerformed(ActionEvent e) {
-                eventBridge.expand();
-            }
-        };
-        expandAction.setEnabled(eventBridge.canExpand());
-
-        //Contract
-        GraphContextMenuAction contractAction = new GraphContextMenuImpl("GraphContextMenu_Contract", "org/gephi/visualization/api/resources/contract.png") {
-
-            @Override
-            public void actionPerformed(ActionEvent e) {
-                eventBridge.contract();
-            }
-        };
-        contractAction.setEnabled(eventBridge.canContract());
-
-        //Settle
-        GraphContextMenuAction settleAction = new GraphContextMenuImpl("GraphContextMenu_Settle", "org/gephi/visualization/api/resources/settle.png") {
-
-            @Override
-            public void actionPerformed(ActionEvent e) {
-                eventBridge.settle();
-            }
-        };
-        settleAction.setEnabled(eventBridge.canSettle());
-
-        //Free
-        GraphContextMenuAction freeAction = new GraphContextMenuImpl("GraphContextMenu_Free") {
-
-            @Override
-            public void actionPerformed(ActionEvent e) {
-                eventBridge.free();
-            }
-        };
-        freeAction.setEnabled(eventBridge.canFree());
-
-        //Free
-        GraphContextMenuAction deleteAction = new GraphContextMenuImpl("GraphContextMenu_Delete") {
-
-            @Override
-            public void actionPerformed(ActionEvent e) {
-                NotifyDescriptor.Confirmation notifyDescriptor = new NotifyDescriptor.Confirmation(
-                        NbBundle.getMessage(GraphContextMenu.class, "GraphContextMenu.Delete.message"),
-                        NbBundle.getMessage(GraphContextMenu.class, "GraphContextMenu.Delete.message.title"), NotifyDescriptor.YES_NO_OPTION);
-                if (DialogDisplayer.getDefault().notify(notifyDescriptor).equals(NotifyDescriptor.YES_OPTION)) {
-                    eventBridge.delete();
-                }
-            }
-        };
-        deleteAction.setEnabled(eventBridge.canDelete());
-
-        //Move workspace
-        JMenu moveToWorkspaceMenu = new JMenu(NbBundle.getMessage(GraphContextMenu.class, "GraphContextMenu_MoveToWorkspace"));
-        boolean moveOrCopyEnabled = eventBridge.canMoveOrCopyWorkspace();
-        if (moveOrCopyEnabled) {
-            moveToWorkspaceMenu.add(new GraphContextMenuImpl("GraphContextMenu_MoveToWorkspace_NewWorkspace", "org/gephi/visualization/api/resources/new-wokspace.png") {
-
-                @Override
-                public void actionPerformed(ActionEvent e) {
-                    eventBridge.moveToNewWorkspace();
-                }
-            });
-            moveToWorkspaceMenu.addSeparator();
-            ProjectController projectController = Lookup.getDefault().lookup(ProjectController.class);
-            for (final Workspace w : projectController.getCurrentProject().getLookup().lookup(WorkspaceProvider.class).getWorkspaces()) {
-                JMenuItem item = new JMenuItem(w.getLookup().lookup(WorkspaceInformation.class).getName());
-                item.addActionListener(new ActionListener() {
-
-                    public void actionPerformed(ActionEvent e) {
-                        eventBridge.moveToWorkspace(w);
-                    }
-                });
-                moveToWorkspaceMenu.add(item);
-                item.setEnabled(w != projectController.getCurrentWorkspace());
-            }
-        }
-        moveToWorkspaceMenu.setEnabled(moveOrCopyEnabled);
-
-        //Copy workspace
-        JMenu copyToWorkspaceMenu = new JMenu(NbBundle.getMessage(GraphContextMenu.class, "GraphContextMenu_CopyToWorkspace"));
-        if (moveOrCopyEnabled) {
-            copyToWorkspaceMenu.add(new GraphContextMenuImpl("GraphContextMenu_CopyToWorkspace_NewWorkspace", "org/gephi/visualization/api/resources/new-wokspace.png") {
-
-                @Override
-                public void actionPerformed(ActionEvent e) {
-                    eventBridge.copyToNewWorkspace();
-                }
-            });
-            copyToWorkspaceMenu.addSeparator();
-            ProjectController projectController = Lookup.getDefault().lookup(ProjectController.class);
-            for (final Workspace w : projectController.getCurrentProject().getLookup().lookup(WorkspaceProvider.class).getWorkspaces()) {
-                JMenuItem item = new JMenuItem(w.getLookup().lookup(WorkspaceInformation.class).getName());
-                item.addActionListener(new ActionListener() {
-
-                    public void actionPerformed(ActionEvent e) {
-                        eventBridge.copyToWorkspace(w);
-                    }
-                });
-                copyToWorkspaceMenu.add(item);
-                item.setEnabled(w != projectController.getCurrentWorkspace());
-            }
-        }
-        copyToWorkspaceMenu.setEnabled(moveOrCopyEnabled);
-
-        //Popup
-        JPopupMenu popupMenu = new JPopupMenu();
-        popupMenu.add(groupAction);
-        popupMenu.add(ungroupAction);
-        popupMenu.addSeparator();
-        popupMenu.add(expandAction);
-        popupMenu.add(contractAction);
-        popupMenu.addSeparator();
-        popupMenu.add(deleteAction);
-        popupMenu.add(moveToWorkspaceMenu);
-        popupMenu.add(copyToWorkspaceMenu);
-        popupMenu.addSeparator();
-        popupMenu.add(settleAction);
-        popupMenu.add(freeAction);
-        return popupMenu;
-    }
-
-    public static interface GraphContextMenuAction extends Action {
-
-        public boolean isVisible();
-    }
-
-    private static class GraphContextMenuImpl extends AbstractAction implements GraphContextMenuAction {
-
-        public GraphContextMenuImpl(String key) {
-            putValue(Action.NAME, NbBundle.getMessage(GraphContextMenu.class, key));
-        }
-
-        public GraphContextMenuImpl(String key, String icon) {
-            putValue(Action.NAME, NbBundle.getMessage(GraphContextMenu.class, key));
-            putValue(Action.SMALL_ICON, ImageUtilities.loadImageIcon(icon, false));
-        }
-
-        public void actionPerformed(ActionEvent e) {
-            throw new UnsupportedOperationException("Not supported yet.");
-        }
-
-        public boolean isVisible() {
-            return true;
-        }
+        GraphContextMenuItem[] items = getGraphContextMenuItems();
+        final Node[] selectedNodes = eventBridge.getSelectedNodes();
+        final HierarchicalGraph graph = eventBridge.getGraph();
+        JPopupMenu contextMenu = new JPopupMenu();
+
+        //Add items ordered:
+        Integer lastItemType = null;
+        for (GraphContextMenuItem item : items) {
+            item.setup(graph, selectedNodes);
+            if (lastItemType == null) {
+                lastItemType = item.getType();
+            }
+            if (lastItemType != item.getType()) {
+                contextMenu.addSeparator();
+            }
+            lastItemType = item.getType();
+            if (item.isAvailable()) {
+                contextMenu.add(createMenuItemFromGraphContextMenuItem(item, graph, selectedNodes));
+            }
+        }
+
+        return contextMenu;
+    }
+
+    /**
+     * <p>Prepares an array with one new instance of every GraphContextMenuItem and returns it.</p>
+     * <p>It also returns the items ordered first by type and then by position.</p>
+     * @return Array of all GraphContextMenuItem implementations
+     */
+    public GraphContextMenuItem[] getGraphContextMenuItems() {
+        ArrayList<GraphContextMenuItem> items = new ArrayList<GraphContextMenuItem>();
+        items.addAll(Lookup.getDefault().lookupAll(GraphContextMenuItem.class));
+        sortItems(items);
+        return items.toArray(new GraphContextMenuItem[0]);
+    }
+
+    public JMenuItem createMenuItemFromGraphContextMenuItem(final GraphContextMenuItem item, final HierarchicalGraph graph, final Node[] nodes) {
+        GraphContextMenuItem[] subItems = item.getSubItems();
+        if (subItems != null && item.canExecute()) {
+            JMenu subMenu = new JMenu();
+            subMenu.setText(item.getName());
+            if (item.getDescription() != null && !item.getDescription().isEmpty()) {
+                subMenu.setToolTipText(item.getDescription());
+            }
+            subMenu.setIcon(item.getIcon());
+            Integer lastItemType = null;
+            for (GraphContextMenuItem subItem : subItems) {
+                subItem.setup(graph, nodes);
+                if (lastItemType == null) {
+                    lastItemType = subItem.getType();
+                }
+                if (lastItemType != subItem.getType()) {
+                    subMenu.addSeparator();
+                }
+                lastItemType = subItem.getType();
+                if (subItem.isAvailable()) {
+                    subMenu.add(createMenuItemFromGraphContextMenuItem(subItem, graph, nodes));
+                }
+            }
+            if(item.getMnemonicKey()!=null){
+                subMenu.setMnemonic(item.getMnemonicKey());//Mnemonic for opening a sub menu
+            }
+            return subMenu;
+        } else {
+            JMenuItem menuItem = new JMenuItem();
+            menuItem.setText(item.getName());
+            if (item.getDescription() != null && !item.getDescription().isEmpty()) {
+                menuItem.setToolTipText(item.getDescription());
+            }
+            menuItem.setIcon(item.getIcon());
+            if (item.canExecute()) {
+                menuItem.addActionListener(new ActionListener() {
+
+                    public void actionPerformed(ActionEvent e) {
+                        new Thread() {
+
+                            @Override
+                            public void run() {
+                                item.execute();
+                            }
+                        }.start();
+                    }
+                });
+            } else {
+                menuItem.setEnabled(false);
+            }
+            if(item.getMnemonicKey()!=null){
+                menuItem.setMnemonic(item.getMnemonicKey());//Mnemonic for executing the action
+                menuItem.setAccelerator(KeyStroke.getKeyStroke(item.getMnemonicKey(),KeyEvent.CTRL_DOWN_MASK));//And the same key mnemonic + ctrl for executing the action (and as a help display for the user!).
+            }
+            return menuItem;
+        }
+    }
+
+    private void sortItems(ArrayList<? extends GraphContextMenuItem> m) {
+        Collections.sort(m, new Comparator<GraphContextMenuItem>() {
+
+            public int compare(GraphContextMenuItem o1, GraphContextMenuItem o2) {
+                //Order by type, position.
+                if (o1.getType() == o2.getType()) {
+                    return o1.getPosition() - o2.getPosition();
+                } else {
+                    return o1.getType() - o2.getType();
+                }
+            }
+        });
     }
 }

=== added directory 'VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems'
=== added file 'VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/Bundle.properties'
--- VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/Bundle.properties	1970-01-01 00:00:00 +0000
+++ VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/Bundle.properties	2011-02-01 14:23:14 +0000
@@ -0,0 +1,21 @@
+GraphContextMenu_Group = Group
+GraphContextMenu_Ungroup = Ungroup
+GraphContextMenu_Expand = Expand
+GraphContextMenu_Contract = Contract
+GraphContextMenu_Settle = Settle
+GraphContextMenu_Free = Free
+GraphContextMenu_TagNodes = Tag nodes...
+GraphContextMenu_Delete = Delete
+GraphContextMenu_MoveToWorkspace = Move to...
+GraphContextMenu_MoveToWorkspace_NewWorkspace = New workspace
+GraphContextMenu_CopyToWorkspace = Copy to...
+GraphContextMenu_CopyToWorkspace_NewWorkspace = New workspace
+
+GraphContextMenu.Delete.message = Nodes will be deleted, do you want to proceed?
+GraphContextMenu.Delete.message.title = Delete nodes
+GraphContextMenu_SelectInDataLaboratory = Select in data laboratory
+GraphContextMenu_OpenURL = Open in web browser
+GraphContextMenu_OpenURL.description = Open URL in an attribute of the node
+GraphContextMenu_OpenURLSubItem = Choose column...
+GraphContextMenu_OpenURLSubItem.message = Column:
+GraphContextMenu_OpenURLLastItem = Last column opened ({0})
\ No newline at end of file

=== added file 'VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/Bundle_es.properties'
--- VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/Bundle_es.properties	1970-01-01 00:00:00 +0000
+++ VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/Bundle_es.properties	2011-02-01 14:23:14 +0000
@@ -0,0 +1,32 @@
+# Spanish translation for gephi
+# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010
+# This file is distributed under the same license as the gephi package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2010.
+#
+!=Project-Id-Version\: gephi\nReport-Msgid-Bugs-To\: FULL NAME <EMAIL@ADDRESS>\nPOT-Creation-Date\: 2010-04-07 13\:16+0200\nPO-Revision-Date\: 2010-12-26 00\:11+0000\nLast-Translator\: Eduardo Ramos <Unknown>\nLanguage-Team\: Spanish <es@xxxxxx>\nMIME-Version\: 1.0\nContent-Type\: text/plain; charset\=UTF-8\nContent-Transfer-Encoding\: 8bit\nX-Launchpad-Export-Date\: 2010-12-27 04\:43+0000\nX-Generator\: Launchpad (build Unknown)\n
+
+GraphContextMenu_Group=Agrupar
+
+GraphContextMenu_Ungroup=Desagrupar
+
+GraphContextMenu_Expand=Expandir
+
+GraphContextMenu_Contract=Contraer
+
+GraphContextMenu_Settle=Bloquear
+
+GraphContextMenu_Free=Desbloquear
+
+GraphContextMenu_Delete=Eliminar
+
+GraphContextMenu_MoveToWorkspace=Mover a...
+
+GraphContextMenu_MoveToWorkspace_NewWorkspace=Nuevo espacio de trabajo
+
+GraphContextMenu_CopyToWorkspace=Copiar a...
+
+GraphContextMenu_CopyToWorkspace_NewWorkspace=Nuevo espacio de trabajo
+
+GraphContextMenu.Delete.message=Los nodos ser\u00e1n eliminados, \u00bfContinuar?
+
+GraphContextMenu.Delete.message.title=Eliminar nodos

=== added file 'VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/Bundle_fr.properties'
--- VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/Bundle_fr.properties	1970-01-01 00:00:00 +0000
+++ VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/Bundle_fr.properties	2011-02-01 14:23:14 +0000
@@ -0,0 +1,32 @@
+# French translation for gephi
+# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010
+# This file is distributed under the same license as the gephi package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2010.
+#
+!=Project-Id-Version\: gephi\nReport-Msgid-Bugs-To\: FULL NAME <EMAIL@ADDRESS>\nPOT-Creation-Date\: 2010-04-07 13\:16+0200\nPO-Revision-Date\: 2010-12-25 19\:49+0000\nLast-Translator\: S\u00e9bastien Heymann <Unknown>\nLanguage-Team\: French <fr@xxxxxx>\nMIME-Version\: 1.0\nContent-Type\: text/plain; charset\=UTF-8\nContent-Transfer-Encoding\: 8bit\nX-Launchpad-Export-Date\: 2010-12-26 04\:53+0000\nX-Generator\: Launchpad (build Unknown)\n
+
+GraphContextMenu_Group=Grouper
+
+GraphContextMenu_Ungroup=D\u00e9grouper
+
+GraphContextMenu_Expand=\u00c9tendre
+
+GraphContextMenu_Contract=Contracter
+
+GraphContextMenu_Settle=Fixer
+
+GraphContextMenu_Free=Lib\u00e9rer
+
+GraphContextMenu_Delete=Supprimer
+
+GraphContextMenu_MoveToWorkspace=D\u00e9placer vers...
+
+GraphContextMenu_MoveToWorkspace_NewWorkspace=Nouvel espace de travail
+
+GraphContextMenu_CopyToWorkspace=Copier vers...
+
+GraphContextMenu_CopyToWorkspace_NewWorkspace=Nouvel espace de travail
+
+GraphContextMenu.Delete.message=Les noeuds seront supprim\u00e9s. Voulez-vous continuer ?
+
+GraphContextMenu.Delete.message.title=Suppression des noeuds

=== added file 'VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/Contract.java'
--- VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/Contract.java	1970-01-01 00:00:00 +0000
+++ VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/Contract.java	2011-02-01 14:23:14 +0000
@@ -0,0 +1,126 @@
+/*
+Copyright 2008-2010 Gephi
+Authors : Mathieu Bastian <mathieu.bastian@xxxxxxxxx>, Eduardo Ramos <eduramiba@xxxxxxxxx>
+Website : http://www.gephi.org
+
+This file is part of Gephi.
+
+Gephi is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+Gephi is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with Gephi.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.gephi.visualization.apiimpl.contextmenuitems;
+
+import java.awt.event.KeyEvent;
+import java.util.HashSet;
+import java.util.Set;
+import javax.swing.Icon;
+import org.gephi.graph.api.GroupData;
+import org.gephi.graph.api.HierarchicalGraph;
+import org.gephi.graph.api.Node;
+import org.gephi.graph.api.NodeData;
+import org.gephi.visualization.hull.ConvexHull;
+import org.gephi.visualization.opengl.compatibility.objects.ConvexHullModel;
+import org.gephi.visualization.spi.GraphContextMenuItem;
+import org.openide.util.ImageUtilities;
+import org.openide.util.NbBundle;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ *
+ */
+@ServiceProvider(service = GraphContextMenuItem.class)
+public class Contract implements GraphContextMenuItem {
+
+    private Node[] nodes;
+    private HierarchicalGraph graph;
+
+    public void setup(HierarchicalGraph graph, Node[] nodes) {
+        this.nodes = nodes;
+        this.graph = graph;
+    }
+
+    public void execute() {
+        try {
+            Set<Node> parents = new HashSet<Node>();
+            for (Node node : nodes) {
+                Node nodeParent = graph.getParent(node);
+                if (nodeParent != null) {
+                    parents.add(nodeParent);
+                }
+            }
+
+            for (Node parent : parents) {
+                GroupData gd = (GroupData) parent.getNodeData();
+                if (gd.getHullModel() != null) {
+                    ConvexHull hull = ((ConvexHullModel) gd.getHullModel()).getObj();
+                    contractPositioning(hull);
+                }
+                graph.retract(parent);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            graph.readUnlockAll();
+        }
+    }
+
+    private void contractPositioning(ConvexHull hull) {
+        NodeData metaNode = hull.getMetaNode().getNodeData();
+        metaNode.setX(hull.x());
+        metaNode.setY(hull.y());
+
+        ConvexHullModel model = (ConvexHullModel) hull.getModel();
+        model.setScale(0.9f);
+        model.setScaleQuantum(-0.1f);
+    }
+
+    public GraphContextMenuItem[] getSubItems() {
+        return null;
+    }
+
+    public String getName() {
+        return NbBundle.getMessage(Contract.class, "GraphContextMenu_Contract");
+    }
+
+    public String getDescription() {
+        return null;
+    }
+
+    public boolean isAvailable() {
+        return true;
+    }
+
+    public boolean canExecute() {
+        for (Node n : nodes) {
+            if (graph.getParent(n)!=null) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public int getType() {
+        return 100;
+    }
+
+    public int getPosition() {
+        return 100;
+    }
+
+    public Icon getIcon() {
+        return ImageUtilities.loadImageIcon("org/gephi/visualization/api/resources/contract.png", false);
+    }
+
+    public Integer getMnemonicKey() {
+        return KeyEvent.VK_C;
+    }
+}

=== added file 'VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/CopyOrMoveToWorkspace.java'
--- VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/CopyOrMoveToWorkspace.java	1970-01-01 00:00:00 +0000
+++ VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/CopyOrMoveToWorkspace.java	2011-02-01 14:23:14 +0000
@@ -0,0 +1,93 @@
+/*
+Copyright 2008-2010 Gephi
+Authors : Mathieu Bastian <mathieu.bastian@xxxxxxxxx>, Eduardo Ramos <eduramiba@xxxxxxxxx>
+Website : http://www.gephi.org
+
+This file is part of Gephi.
+
+Gephi is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+Gephi is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with Gephi.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.gephi.visualization.apiimpl.contextmenuitems;
+
+import java.util.ArrayList;
+import javax.swing.Icon;
+import org.gephi.graph.api.HierarchicalGraph;
+import org.gephi.graph.api.Node;
+import org.gephi.project.api.ProjectController;
+import org.gephi.project.api.Workspace;
+import org.gephi.project.api.WorkspaceProvider;
+import org.gephi.visualization.spi.GraphContextMenuItem;
+import org.openide.util.Lookup;
+
+/**
+ *
+ */
+public abstract class CopyOrMoveToWorkspace implements GraphContextMenuItem {
+
+    private Node[] nodes;
+
+    public void setup(HierarchicalGraph graph, Node[] nodes) {
+        this.nodes = nodes;
+    }
+
+    public void execute() {
+    }
+
+    public GraphContextMenuItem[] getSubItems() {
+        if (nodes != null) {
+            int i = 0;
+            ArrayList<GraphContextMenuItem> subItems = new ArrayList<GraphContextMenuItem>();
+            if (canExecute()) {
+                subItems.add(new CopyOrMoveToWorkspaceSubItem(null, true, 0, 0, isCopy()));//New workspace
+                ProjectController projectController = Lookup.getDefault().lookup(ProjectController.class);
+                for (final Workspace w : projectController.getCurrentProject().getLookup().lookup(WorkspaceProvider.class).getWorkspaces()) {
+                    GraphContextMenuItem item = new CopyOrMoveToWorkspaceSubItem(w, w != projectController.getCurrentWorkspace(), 1, i, isCopy());
+                    subItems.add(item);
+                    i++;
+                }
+                return subItems.toArray(new GraphContextMenuItem[0]);
+            } else {
+                return null;
+            }
+        } else {
+            return null;
+        }
+    }
+
+    public String getDescription() {
+        return null;
+    }
+
+    public boolean isAvailable() {
+        return true;
+    }
+
+    public boolean canExecute() {
+        return nodes.length > 0;
+    }
+
+    public int getType() {
+        return 200;
+    }
+
+    public Icon getIcon() {
+        return null;
+    }
+
+    public Integer getMnemonicKey() {
+        return null;
+    }
+
+    protected abstract boolean isCopy();
+}

=== added file 'VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/CopyOrMoveToWorkspaceSubItem.java'
--- VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/CopyOrMoveToWorkspaceSubItem.java	1970-01-01 00:00:00 +0000
+++ VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/CopyOrMoveToWorkspaceSubItem.java	2011-02-01 14:23:14 +0000
@@ -0,0 +1,149 @@
+/*
+Copyright 2008-2010 Gephi
+Authors : Mathieu Bastian <mathieu.bastian@xxxxxxxxx>, Eduardo Ramos <eduramiba@xxxxxxxxx>
+Website : http://www.gephi.org
+
+This file is part of Gephi.
+
+Gephi is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+Gephi is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with Gephi.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.gephi.visualization.apiimpl.contextmenuitems;
+
+import javax.swing.Icon;
+import org.gephi.data.attributes.api.AttributeController;
+import org.gephi.data.attributes.api.AttributeModel;
+import org.gephi.desktop.project.api.ProjectControllerUI;
+import org.gephi.graph.api.Graph;
+import org.gephi.graph.api.GraphController;
+import org.gephi.graph.api.GraphModel;
+import org.gephi.graph.api.HierarchicalGraph;
+import org.gephi.graph.api.Node;
+import org.gephi.project.api.ProjectController;
+import org.gephi.project.api.Workspace;
+import org.gephi.project.api.WorkspaceInformation;
+import org.gephi.visualization.spi.GraphContextMenuItem;
+import org.openide.util.Lookup;
+import org.openide.util.NbBundle;
+
+public class CopyOrMoveToWorkspaceSubItem implements GraphContextMenuItem {
+
+    private Workspace workspace;
+    private boolean canExecute;
+    private int type;
+    private int position;
+    private HierarchicalGraph graph;
+    private Node[] nodes;
+    private final boolean copy;
+
+    /**
+     * Constructor with copy or move settings
+     * @param workspace Workspace to copy or move, or null to use new workspace
+     * @param canExecute canExecute
+     * @param type type
+     * @param position position
+     * @param copy True to copy, false to move
+     */
+    public CopyOrMoveToWorkspaceSubItem(Workspace workspace, boolean canExecute, int type, int position, boolean copy) {
+        this.workspace = workspace;
+        this.canExecute = canExecute;
+        this.type = type;
+        this.position = position;
+        this.copy = copy;
+    }
+
+    public void setup(HierarchicalGraph graph, Node[] nodes) {
+        this.graph = graph;
+        this.nodes = nodes;
+    }
+
+    public void execute() {
+        if (workspace == null) {
+            workspace = Lookup.getDefault().lookup(ProjectControllerUI.class).newWorkspace();
+        }
+        if (copy) {
+            copyToWorkspace(workspace);
+        } else {
+            moveToWorkspace(workspace);
+        }
+    }
+
+    public GraphContextMenuItem[] getSubItems() {
+        return null;
+    }
+
+    public String getName() {
+        if (workspace != null) {
+            return workspace.getLookup().lookup(WorkspaceInformation.class).getName();
+        } else {
+            return NbBundle.getMessage(CopyOrMoveToWorkspaceSubItem.class, copy ? "GraphContextMenu_CopyToWorkspace_NewWorkspace" : "GraphContextMenu_MoveToWorkspace_NewWorkspace");
+        }
+    }
+
+    public String getDescription() {
+        return null;
+    }
+
+    public boolean isAvailable() {
+        return true;
+    }
+
+    public boolean canExecute() {
+        return canExecute;
+    }
+
+    public int getType() {
+        return type;
+    }
+
+    public int getPosition() {
+        return position;
+    }
+
+    public Icon getIcon() {
+        return null;
+    }
+
+    public void copyToWorkspace(Workspace workspace) {
+        GraphController graphController = Lookup.getDefault().lookup(GraphController.class);
+        AttributeController attributeController = Lookup.getDefault().lookup(AttributeController.class);
+        ProjectController projectController = Lookup.getDefault().lookup(ProjectController.class);
+
+        Workspace currentWorkspace = projectController.getCurrentWorkspace();
+        AttributeModel sourceAttributeModel = attributeController.getModel(currentWorkspace);
+        AttributeModel destAttributeModel = attributeController.getModel(workspace);
+        destAttributeModel.mergeModel(sourceAttributeModel);
+
+        GraphModel sourceModel = graphController.getModel(currentWorkspace);
+        GraphModel destModel = graphController.getModel(workspace);
+        Graph destGraph = destModel.getHierarchicalGraphVisible();
+        Graph sourceGraph = sourceModel.getHierarchicalGraphVisible();
+
+        destModel.pushNodes(sourceGraph, nodes);
+    }
+
+    public void moveToWorkspace(Workspace workspace) {
+        copyToWorkspace(workspace);
+        delete();
+    }
+
+    public void delete() {
+        for (Node n : nodes) {
+            graph.removeNode(n);
+        }
+    }
+
+    public Integer getMnemonicKey() {
+        return null;
+    }
+}

=== added file 'VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/CopyToWorkspace.java'
--- VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/CopyToWorkspace.java	1970-01-01 00:00:00 +0000
+++ VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/CopyToWorkspace.java	2011-02-01 14:23:14 +0000
@@ -0,0 +1,31 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package org.gephi.visualization.apiimpl.contextmenuitems;
+
+import org.gephi.visualization.spi.GraphContextMenuItem;
+import org.openide.util.NbBundle;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ *
+ * @author Eduardo
+ */
+@ServiceProvider(service = GraphContextMenuItem.class)
+public class CopyToWorkspace extends CopyOrMoveToWorkspace{
+
+    @Override
+    protected boolean isCopy() {
+        return true;
+    }
+
+    public String getName() {
+        return NbBundle.getMessage(CopyOrMoveToWorkspace.class, "GraphContextMenu_CopyToWorkspace");
+    }
+
+    public int getPosition() {
+        return 200;
+    }
+}

=== added file 'VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/Delete.java'
--- VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/Delete.java	1970-01-01 00:00:00 +0000
+++ VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/Delete.java	2011-02-01 14:23:14 +0000
@@ -0,0 +1,93 @@
+/*
+Copyright 2008-2010 Gephi
+Authors : Mathieu Bastian <mathieu.bastian@xxxxxxxxx>, Eduardo Ramos <eduramiba@xxxxxxxxx>
+Website : http://www.gephi.org
+
+This file is part of Gephi.
+
+Gephi is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+Gephi is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with Gephi.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.gephi.visualization.apiimpl.contextmenuitems;
+
+import java.awt.event.KeyEvent;
+import javax.swing.Icon;
+import org.gephi.datalab.api.GraphElementsController;
+import org.gephi.graph.api.HierarchicalGraph;
+import org.gephi.graph.api.Node;
+import org.gephi.visualization.spi.GraphContextMenuItem;
+import org.openide.DialogDisplayer;
+import org.openide.NotifyDescriptor;
+import org.openide.util.ImageUtilities;
+import org.openide.util.Lookup;
+import org.openide.util.NbBundle;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ *
+ */
+@ServiceProvider(service = GraphContextMenuItem.class)
+public class Delete implements GraphContextMenuItem {
+
+    private Node[] nodes;
+
+    public void setup(HierarchicalGraph graph, Node[] nodes) {
+        this.nodes = nodes;
+    }
+
+    public void execute() {
+        NotifyDescriptor.Confirmation notifyDescriptor = new NotifyDescriptor.Confirmation(
+                NbBundle.getMessage(Delete.class, "GraphContextMenu.Delete.message"),
+                NbBundle.getMessage(Delete.class, "GraphContextMenu.Delete.message.title"), NotifyDescriptor.YES_NO_OPTION);
+        if (DialogDisplayer.getDefault().notify(notifyDescriptor).equals(NotifyDescriptor.YES_OPTION)) {
+            GraphElementsController gec = Lookup.getDefault().lookup(GraphElementsController.class);
+            gec.deleteNodes(nodes);
+        }
+    }
+
+    public GraphContextMenuItem[] getSubItems() {
+        return null;
+    }
+
+    public String getName() {
+        return NbBundle.getMessage(Delete.class, "GraphContextMenu_Delete");
+    }
+
+    public String getDescription() {
+        return null;
+    }
+
+    public boolean isAvailable() {
+        return true;
+    }
+
+    public boolean canExecute() {
+        return nodes.length > 0;
+    }
+
+    public int getType() {
+        return 200;
+    }
+
+    public int getPosition() {
+        return 0;
+    }
+
+    public Icon getIcon() {
+        return ImageUtilities.loadImageIcon("org/gephi/visualization/api/resources/delete.png", false);
+    }
+
+    public Integer getMnemonicKey() {
+        return KeyEvent.VK_D;
+    }
+}

=== added file 'VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/Expand.java'
--- VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/Expand.java	1970-01-01 00:00:00 +0000
+++ VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/Expand.java	2011-02-01 14:23:14 +0000
@@ -0,0 +1,124 @@
+/*
+Copyright 2008-2010 Gephi
+Authors : Mathieu Bastian <mathieu.bastian@xxxxxxxxx>, Eduardo Ramos <eduramiba@xxxxxxxxx>
+Website : http://www.gephi.org
+
+This file is part of Gephi.
+
+Gephi is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+Gephi is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with Gephi.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.gephi.visualization.apiimpl.contextmenuitems;
+
+import java.awt.event.KeyEvent;
+import javax.swing.Icon;
+import org.gephi.graph.api.HierarchicalGraph;
+import org.gephi.graph.api.Node;
+import org.gephi.graph.api.NodeData;
+import org.gephi.visualization.spi.GraphContextMenuItem;
+import org.openide.util.ImageUtilities;
+import org.openide.util.NbBundle;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ *
+ */
+@ServiceProvider(service = GraphContextMenuItem.class)
+public class Expand implements GraphContextMenuItem {
+
+    private Node[] nodes;
+    private HierarchicalGraph graph;
+
+    public void setup(HierarchicalGraph graph, Node[] nodes) {
+        this.nodes = nodes;
+        this.graph=graph;
+    }
+
+    public void execute() {
+        try {
+            for (Node node : nodes) {
+                if (graph.getDescendantCount(node) > 0) {
+                    expandPositioning(node);
+                    graph.expand(node);
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            graph.readUnlockAll();
+        }
+    }
+
+    private void expandPositioning(Node node) {
+        NodeData nodeData = node.getNodeData();
+        float centroidX = 0;
+        float centroidY = 0;
+        int len = 0;
+        Node[] children = graph.getChildren(node).toArray();
+        for (Node child : children) {
+            centroidX += child.getNodeData().x();
+            centroidY += child.getNodeData().y();
+            len++;
+        }
+        centroidX /= len;
+        centroidY /= len;
+
+        float diffX = nodeData.x() - centroidX;
+        float diffY = nodeData.y() - centroidY;
+        for (Node child : children) {
+            NodeData nd = child.getNodeData();
+            nd.setX(nd.x() + diffX);
+            nd.setY(nd.y() + diffY);
+        }
+    }
+
+    public GraphContextMenuItem[] getSubItems() {
+        return null;
+    }
+
+    public String getName() {
+        return NbBundle.getMessage(Expand.class, "GraphContextMenu_Expand");
+    }
+
+    public String getDescription() {
+        return null;
+    }
+
+    public boolean isAvailable() {
+        return true;
+    }
+
+    public boolean canExecute() {
+        for (Node n : nodes) {
+            if (graph.getDescendantCount(n)>0) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public int getType() {
+        return 100;
+    }
+
+    public int getPosition() {
+        return 0;
+    }
+
+    public Icon getIcon() {
+        return ImageUtilities.loadImageIcon("org/gephi/visualization/api/resources/expand.png", false);
+    }
+
+    public Integer getMnemonicKey() {
+        return KeyEvent.VK_E;
+    }
+}

=== added file 'VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/Free.java'
--- VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/Free.java	1970-01-01 00:00:00 +0000
+++ VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/Free.java	2011-02-01 14:23:14 +0000
@@ -0,0 +1,91 @@
+/*
+Copyright 2008-2010 Gephi
+Authors : Mathieu Bastian <mathieu.bastian@xxxxxxxxx>, Eduardo Ramos <eduramiba@xxxxxxxxx>
+Website : http://www.gephi.org
+
+This file is part of Gephi.
+
+Gephi is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+Gephi is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with Gephi.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.gephi.visualization.apiimpl.contextmenuitems;
+
+import java.awt.event.KeyEvent;
+import javax.swing.Icon;
+import org.gephi.datalab.api.GraphElementsController;
+import org.gephi.graph.api.HierarchicalGraph;
+import org.gephi.graph.api.Node;
+import org.gephi.visualization.spi.GraphContextMenuItem;
+import org.openide.util.ImageUtilities;
+import org.openide.util.Lookup;
+import org.openide.util.NbBundle;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ *
+ */
+@ServiceProvider(service = GraphContextMenuItem.class)
+public class Free implements GraphContextMenuItem {
+
+    private Node[] nodes;
+
+    public void setup(HierarchicalGraph graph, Node[] nodes) {
+        this.nodes = nodes;
+    }
+
+    public void execute() {
+        GraphElementsController gec = Lookup.getDefault().lookup(GraphElementsController.class);
+        gec.setNodesFixed(nodes, false);
+    }
+
+    public GraphContextMenuItem[] getSubItems() {
+        return null;
+    }
+
+    public String getName() {
+        return NbBundle.getMessage(Free.class, "GraphContextMenu_Free");
+    }
+
+    public String getDescription() {
+        return null;
+    }
+
+    public boolean isAvailable() {
+        return true;
+    }
+
+    public boolean canExecute() {
+        for (Node n : nodes) {
+            if (Lookup.getDefault().lookup(GraphElementsController.class).isNodeFixed(n)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public int getType() {
+        return 300;
+    }
+
+    public int getPosition() {
+        return 100;
+    }
+
+    public Icon getIcon() {
+        return ImageUtilities.loadImageIcon("org/gephi/visualization/api/resources/free.png", false);
+    }
+
+    public Integer getMnemonicKey() {
+        return KeyEvent.VK_F;
+    }
+}

=== added file 'VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/Group.java'
--- VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/Group.java	1970-01-01 00:00:00 +0000
+++ VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/Group.java	2011-02-01 14:23:14 +0000
@@ -0,0 +1,85 @@
+/*
+Copyright 2008-2010 Gephi
+Authors : Mathieu Bastian <mathieu.bastian@xxxxxxxxx>, Eduardo Ramos <eduramiba@xxxxxxxxx>
+Website : http://www.gephi.org
+
+This file is part of Gephi.
+
+Gephi is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+Gephi is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with Gephi.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.gephi.visualization.apiimpl.contextmenuitems;
+
+import java.awt.event.KeyEvent;
+import javax.swing.Icon;
+import org.gephi.datalab.api.GraphElementsController;
+import org.gephi.graph.api.HierarchicalGraph;
+import org.gephi.graph.api.Node;
+import org.gephi.visualization.spi.GraphContextMenuItem;
+import org.openide.util.ImageUtilities;
+import org.openide.util.Lookup;
+import org.openide.util.NbBundle;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ *
+ */
+@ServiceProvider(service=GraphContextMenuItem.class)
+public class Group implements GraphContextMenuItem{
+    private Node[] nodes;
+
+    public void setup(HierarchicalGraph graph,Node[] nodes) {
+        this.nodes=nodes;
+    }
+
+    public void execute() {
+        GraphElementsController gec=Lookup.getDefault().lookup(GraphElementsController.class);
+        gec.groupNodes(nodes);
+    }
+
+    public GraphContextMenuItem[] getSubItems() {
+        return null;
+    }
+
+    public String getName() {
+        return NbBundle.getMessage(Group.class, "GraphContextMenu_Group");
+    }
+
+    public String getDescription() {
+        return null;
+    }
+
+    public boolean isAvailable() {
+        return true;
+    }
+
+    public boolean canExecute() {
+        return nodes.length>0;
+    }
+
+    public int getType() {
+        return 0;
+    }
+
+    public int getPosition() {
+        return 0;
+    }
+
+    public Icon getIcon() {
+        return ImageUtilities.loadImageIcon("org/gephi/visualization/api/resources/group.png", false);
+    }
+
+    public Integer getMnemonicKey() {
+        return KeyEvent.VK_G;
+    }
+}

=== added file 'VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/LastColumnOpenedURL.java'
--- VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/LastColumnOpenedURL.java	1970-01-01 00:00:00 +0000
+++ VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/LastColumnOpenedURL.java	2011-02-01 14:23:14 +0000
@@ -0,0 +1,33 @@
+/*
+Copyright 2008-2010 Gephi
+Authors : Mathieu Bastian <mathieu.bastian@xxxxxxxxx>, Eduardo Ramos <eduramiba@xxxxxxxxx>
+Website : http://www.gephi.org
+
+This file is part of Gephi.
+
+Gephi is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+Gephi is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with Gephi.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.gephi.visualization.apiimpl.contextmenuitems;
+
+/**
+ *
+ * @author Eduardo
+ */
+public class LastColumnOpenedURL {
+    String column;
+
+    public LastColumnOpenedURL(String column) {
+        this.column = column;
+    }
+}

=== added file 'VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/MoveToWorkspace.java'
--- VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/MoveToWorkspace.java	1970-01-01 00:00:00 +0000
+++ VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/MoveToWorkspace.java	2011-02-01 14:23:14 +0000
@@ -0,0 +1,31 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package org.gephi.visualization.apiimpl.contextmenuitems;
+
+import org.gephi.visualization.spi.GraphContextMenuItem;
+import org.openide.util.NbBundle;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ *
+ * @author Eduardo
+ */
+@ServiceProvider(service = GraphContextMenuItem.class)
+public class MoveToWorkspace extends CopyOrMoveToWorkspace{
+
+    @Override
+    protected boolean isCopy() {
+        return false;
+    }
+
+    public String getName() {
+        return NbBundle.getMessage(CopyOrMoveToWorkspace.class, "GraphContextMenu_MoveToWorkspace");
+    }
+
+    public int getPosition() {
+        return 100;
+    }
+}

=== added file 'VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/OpenURL.java'
--- VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/OpenURL.java	1970-01-01 00:00:00 +0000
+++ VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/OpenURL.java	2011-02-01 14:23:14 +0000
@@ -0,0 +1,93 @@
+/*
+Copyright 2008-2010 Gephi
+Authors : Mathieu Bastian <mathieu.bastian@xxxxxxxxx>, Eduardo Ramos <eduramiba@xxxxxxxxx>
+Website : http://www.gephi.org
+
+This file is part of Gephi.
+
+Gephi is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+Gephi is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with Gephi.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.gephi.visualization.apiimpl.contextmenuitems;
+
+import java.util.ArrayList;
+import javax.swing.Icon;
+import org.gephi.data.attributes.api.AttributeRow;
+import org.gephi.data.attributes.api.AttributeType;
+import org.gephi.graph.api.HierarchicalGraph;
+import org.gephi.graph.api.Node;
+import org.gephi.visualization.spi.GraphContextMenuItem;
+import org.openide.util.ImageUtilities;
+import org.openide.util.NbBundle;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ *
+ */
+@ServiceProvider(service = GraphContextMenuItem.class)
+public class OpenURL implements GraphContextMenuItem {
+
+    private Node node;
+
+    public void setup(HierarchicalGraph graph, Node[] nodes) {
+        if (nodes.length == 1) {
+            node = nodes[0];
+        }else{
+            node=null;
+        }
+    }
+
+    public void execute() {
+    }
+
+    public GraphContextMenuItem[] getSubItems() {
+        GraphContextMenuItem[] subItems=new GraphContextMenuItem[2];
+
+        //Always provide subitems so their shortcuts are available:
+        subItems[0]=new OpenURLLastItem();
+        subItems[1]=new OpenURLSubItem();
+        return subItems;
+    }
+
+    public String getName() {
+        return NbBundle.getMessage(OpenURL.class, "GraphContextMenu_OpenURL");
+    }
+
+    public String getDescription() {
+        return NbBundle.getMessage(OpenURL.class, "GraphContextMenu_OpenURL.description");
+    }
+
+    public boolean isAvailable() {
+        return node != null;
+    }
+
+    public boolean canExecute() {
+        return true;
+    }
+
+    public int getType() {
+        return 400;
+    }
+
+    public int getPosition() {
+        return 300;
+    }
+
+    public Icon getIcon() {
+        return ImageUtilities.loadImageIcon("org/gephi/visualization/api/resources/globe-network.png", false);
+    }
+
+    public Integer getMnemonicKey() {
+        return null;
+    }
+}

=== added file 'VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/OpenURLLastItem.java'
--- VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/OpenURLLastItem.java	1970-01-01 00:00:00 +0000
+++ VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/OpenURLLastItem.java	2011-02-01 14:23:14 +0000
@@ -0,0 +1,119 @@
+/*
+Copyright 2008-2010 Gephi
+Authors : Mathieu Bastian <mathieu.bastian@xxxxxxxxx>, Eduardo Ramos <eduramiba@xxxxxxxxx>
+Website : http://www.gephi.org
+
+This file is part of Gephi.
+
+Gephi is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+Gephi is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with Gephi.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.gephi.visualization.apiimpl.contextmenuitems;
+
+import java.awt.event.KeyEvent;
+import java.net.URL;
+import javax.swing.Icon;
+import org.gephi.data.attributes.api.AttributeController;
+import org.gephi.data.attributes.api.AttributeRow;
+import org.gephi.data.attributes.api.AttributeTable;
+import org.gephi.graph.api.HierarchicalGraph;
+import org.gephi.graph.api.Node;
+import org.gephi.project.api.ProjectController;
+import org.gephi.visualization.spi.GraphContextMenuItem;
+import org.openide.awt.HtmlBrowser.URLDisplayer;
+import org.openide.util.Lookup;
+import org.openide.util.NbBundle;
+
+/**
+ *
+ */
+public class OpenURLLastItem implements GraphContextMenuItem {
+
+    private Node node;
+    private String column, url;
+
+    public void setup(HierarchicalGraph graph, Node[] nodes) {
+        url = null;
+        column = null;
+        if (nodes.length == 1) {
+            node = nodes[0];
+            LastColumnOpenedURL lc = Lookup.getDefault().lookup(ProjectController.class).getCurrentWorkspace().getLookup().lookup(LastColumnOpenedURL.class);
+            if (lc != null) {
+                column = lc.column;
+                AttributeTable table = Lookup.getDefault().lookup(AttributeController.class).getModel().getNodeTable();
+                if (table.hasColumn(column)) {
+                    AttributeRow row = (AttributeRow) node.getNodeData().getAttributes();
+                    Object value;
+                    if ((value = row.getValue(column)) != null) {
+                        url = value.toString();
+
+                        if (!url.matches("(https?|ftp):(//?|\\\\?)?.*")) {
+                            //Does not look like an URL, try http:
+                            url = "http://"; + url;
+                        }
+                    }
+                }else{
+                    column=null;
+                     Lookup.getDefault().lookup(ProjectController.class).getCurrentWorkspace().remove(lc);
+                }
+            }
+        } else {
+            node = null;
+        }
+    }
+
+    public void execute() {
+        if (url != null) {
+            try {
+                URLDisplayer.getDefault().showURLExternal(new URL(url));
+            } catch (Exception ex) {
+            }
+        }
+    }
+
+    public GraphContextMenuItem[] getSubItems() {
+        return null;
+    }
+
+    public String getName() {
+        return NbBundle.getMessage(OpenURLLastItem.class, "GraphContextMenu_OpenURLLastItem", column != null ? column : "--");
+    }
+
+    public String getDescription() {
+        return null;
+    }
+
+    public boolean isAvailable() {
+        return true;
+    }
+
+    public boolean canExecute() {
+        return url != null;
+    }
+
+    public int getType() {
+        return 0;
+    }
+
+    public int getPosition() {
+        return 0;
+    }
+
+    public Icon getIcon() {
+        return null;
+    }
+
+    public Integer getMnemonicKey() {
+        return KeyEvent.VK_P;
+    }
+}

=== added file 'VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/OpenURLSubItem.java'
--- VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/OpenURLSubItem.java	1970-01-01 00:00:00 +0000
+++ VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/OpenURLSubItem.java	2011-02-01 14:23:14 +0000
@@ -0,0 +1,152 @@
+/*
+Copyright 2008-2010 Gephi
+Authors : Mathieu Bastian <mathieu.bastian@xxxxxxxxx>, Eduardo Ramos <eduramiba@xxxxxxxxx>
+Website : http://www.gephi.org
+
+This file is part of Gephi.
+
+Gephi is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+Gephi is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with Gephi.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.gephi.visualization.apiimpl.contextmenuitems;
+
+import java.awt.event.KeyEvent;
+import java.net.URL;
+import java.util.ArrayList;
+import javax.swing.Icon;
+import javax.swing.JOptionPane;
+import org.gephi.data.attributes.api.AttributeColumn;
+import org.gephi.data.attributes.api.AttributeRow;
+import org.gephi.data.attributes.api.AttributeType;
+import org.gephi.graph.api.HierarchicalGraph;
+import org.gephi.graph.api.Node;
+import org.gephi.project.api.ProjectController;
+import org.gephi.visualization.spi.GraphContextMenuItem;
+import org.openide.awt.HtmlBrowser.URLDisplayer;
+import org.openide.util.Lookup;
+import org.openide.util.NbBundle;
+
+/**
+ *
+ */
+public class OpenURLSubItem implements GraphContextMenuItem {
+
+    private Node node;
+    private String columnTitle, url;
+
+    public void setup(HierarchicalGraph graph, Node[] nodes) {
+        if (nodes.length == 1) {
+            node = nodes[0];
+        } else {
+            node = null;
+        }
+    }
+
+    public void execute() {
+        if (node != null) {
+            LastColumnOpenedURL lc = Lookup.getDefault().lookup(ProjectController.class).getCurrentWorkspace().getLookup().lookup(LastColumnOpenedURL.class);
+            ArrayList<ColumnURL> columnUrls = new ArrayList<ColumnURL>();
+            ColumnURL initialSelection = null;
+            Object value;
+            String str;
+            AttributeColumn column;
+            AttributeRow row = (AttributeRow) node.getNodeData().getAttributes();
+            for (int i = 0; i < row.countValues(); i++) {
+                column = row.getColumnAt(i);
+                if ((column.getType() == AttributeType.STRING || column.getType() == AttributeType.DYNAMIC_STRING) && (value = row.getValue(i)) != null) {
+                    str = value.toString();
+                    if (!str.matches("(https?|ftp):(//?|\\\\?)?.*")) {
+                        //Does not look like an URL, try http:
+                        str = "http://"; + str;
+                    }
+                    if (lc != null && lc.column.equals(column.getTitle())) {
+                        columnUrls.add(initialSelection=new ColumnURL(column.getTitle(), str));
+                    }else{
+                        columnUrls.add(new ColumnURL(column.getTitle(), str));
+                    }
+                }
+            }
+
+            ColumnURL selection = (ColumnURL) JOptionPane.showInputDialog(null, NbBundle.getMessage(OpenURLSubItem.class, "GraphContextMenu_OpenURLSubItem.message"), getName(), JOptionPane.QUESTION_MESSAGE, null, columnUrls.toArray(), initialSelection);
+            if (selection != null) {
+                columnTitle = selection.column;
+                url = selection.url;
+                showUrl();
+            }
+        }
+    }
+
+    private void showUrl() {
+        try {
+            URLDisplayer.getDefault().showURLExternal(new URL(url));
+        } catch (Exception ex) {
+        }
+        LastColumnOpenedURL lc = Lookup.getDefault().lookup(ProjectController.class).getCurrentWorkspace().getLookup().lookup(LastColumnOpenedURL.class);
+        if (lc == null) {
+            Lookup.getDefault().lookup(ProjectController.class).getCurrentWorkspace().add(new LastColumnOpenedURL(columnTitle));
+        } else {
+            lc.column = columnTitle;
+        }
+    }
+
+    public GraphContextMenuItem[] getSubItems() {
+        return null;
+    }
+
+    public String getName() {
+        return NbBundle.getMessage(OpenURLSubItem.class, "GraphContextMenu_OpenURLSubItem");
+    }
+
+    public String getDescription() {
+        return null;
+    }
+
+    public boolean isAvailable() {
+        return true;
+    }
+
+    public boolean canExecute() {
+        return true;
+    }
+
+    public int getType() {
+        return 100;
+    }
+
+    public int getPosition() {
+        return 0;
+    }
+
+    public Icon getIcon() {
+        return null;
+    }
+
+    public Integer getMnemonicKey() {
+        return KeyEvent.VK_N;
+    }
+
+    class ColumnURL {
+
+        String column, url;
+
+        public ColumnURL(String column, String url) {
+            this.column = column;
+            this.url = url;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("%s - %s", column, url);
+        }
+    }
+}

=== added file 'VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/SelectInDataLaboratory.java'
--- VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/SelectInDataLaboratory.java	1970-01-01 00:00:00 +0000
+++ VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/SelectInDataLaboratory.java	2011-02-01 14:23:14 +0000
@@ -0,0 +1,86 @@
+/*
+Copyright 2008-2010 Gephi
+Authors : Mathieu Bastian <mathieu.bastian@xxxxxxxxx>, Eduardo Ramos <eduramiba@xxxxxxxxx>
+Website : http://www.gephi.org
+
+This file is part of Gephi.
+
+Gephi is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+Gephi is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with Gephi.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.gephi.visualization.apiimpl.contextmenuitems;
+
+import java.awt.event.KeyEvent;
+import javax.swing.Icon;
+import org.gephi.datalab.api.DataTablesController;
+import org.gephi.graph.api.HierarchicalGraph;
+import org.gephi.graph.api.Node;
+import org.gephi.visualization.spi.GraphContextMenuItem;
+import org.openide.util.ImageUtilities;
+import org.openide.util.Lookup;
+import org.openide.util.NbBundle;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ *
+ */
+@ServiceProvider(service = GraphContextMenuItem.class)
+public class SelectInDataLaboratory implements GraphContextMenuItem {
+    private Node[] nodes;
+    private DataTablesController dtc;
+    public void setup(HierarchicalGraph graph, Node[] nodes) {
+        this.nodes = nodes;
+        dtc=Lookup.getDefault().lookup(DataTablesController.class);
+    }
+
+    public void execute() {
+        dtc.setNodeTableSelection(nodes);
+        dtc.selectNodesTable();
+    }
+
+    public GraphContextMenuItem[] getSubItems() {
+        return null;
+    }
+
+    public String getName() {
+        return NbBundle.getMessage(SelectInDataLaboratory.class, "GraphContextMenu_SelectInDataLaboratory");
+    }
+
+    public String getDescription() {
+        return null;
+    }
+
+    public boolean isAvailable() {
+        return dtc.isDataTablesReady();
+    }
+
+    public boolean canExecute() {
+        return nodes.length>=1;
+    }
+
+    public int getType() {
+        return 400;
+    }
+
+    public int getPosition() {
+        return 100;
+    }
+
+    public Icon getIcon() {
+        return ImageUtilities.loadImageIcon("org/gephi/visualization/api/resources/table-select.png", false);
+    }
+
+    public Integer getMnemonicKey() {
+        return KeyEvent.VK_L;
+    }
+}

=== added file 'VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/Settle.java'
--- VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/Settle.java	1970-01-01 00:00:00 +0000
+++ VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/Settle.java	2011-02-01 14:23:14 +0000
@@ -0,0 +1,91 @@
+/*
+Copyright 2008-2010 Gephi
+Authors : Mathieu Bastian <mathieu.bastian@xxxxxxxxx>, Eduardo Ramos <eduramiba@xxxxxxxxx>
+Website : http://www.gephi.org
+
+This file is part of Gephi.
+
+Gephi is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+Gephi is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with Gephi.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.gephi.visualization.apiimpl.contextmenuitems;
+
+import java.awt.event.KeyEvent;
+import javax.swing.Icon;
+import org.gephi.datalab.api.GraphElementsController;
+import org.gephi.graph.api.HierarchicalGraph;
+import org.gephi.graph.api.Node;
+import org.gephi.visualization.spi.GraphContextMenuItem;
+import org.openide.util.ImageUtilities;
+import org.openide.util.Lookup;
+import org.openide.util.NbBundle;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ *
+ */
+@ServiceProvider(service = GraphContextMenuItem.class)
+public class Settle implements GraphContextMenuItem {
+
+    private Node[] nodes;
+
+    public void setup(HierarchicalGraph graph, Node[] nodes) {
+        this.nodes = nodes;
+    }
+
+    public void execute() {
+        GraphElementsController gec = Lookup.getDefault().lookup(GraphElementsController.class);
+        gec.setNodesFixed(nodes, true);
+    }
+
+    public GraphContextMenuItem[] getSubItems() {
+        return null;
+    }
+
+    public String getName() {
+        return NbBundle.getMessage(Settle.class, "GraphContextMenu_Settle");
+    }
+
+    public String getDescription() {
+        return null;
+    }
+
+    public boolean isAvailable() {
+        return true;
+    }
+
+    public boolean canExecute() {
+        for (Node n : nodes) {
+            if (!Lookup.getDefault().lookup(GraphElementsController.class).isNodeFixed(n)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public int getType() {
+        return 300;
+    }
+
+    public int getPosition() {
+        return 0;
+    }
+
+    public Icon getIcon() {
+        return ImageUtilities.loadImageIcon("org/gephi/visualization/api/resources/settle.png", false);
+    }
+
+    public Integer getMnemonicKey() {
+        return KeyEvent.VK_B;
+    }
+}

=== added file 'VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/TagNodes.java'
--- VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/TagNodes.java	1970-01-01 00:00:00 +0000
+++ VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/TagNodes.java	2011-02-01 14:23:14 +0000
@@ -0,0 +1,86 @@
+/*
+Copyright 2008-2010 Gephi
+Authors : Mathieu Bastian <mathieu.bastian@xxxxxxxxx>, Eduardo Ramos <eduramiba@xxxxxxxxx>
+Website : http://www.gephi.org
+
+This file is part of Gephi.
+
+Gephi is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+Gephi is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with Gephi.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.gephi.visualization.apiimpl.contextmenuitems;
+
+import java.awt.event.KeyEvent;
+import javax.swing.Icon;
+import org.gephi.datalab.api.DataLaboratoryHelper;
+import org.gephi.datalab.spi.nodes.NodesManipulator;
+import org.gephi.graph.api.HierarchicalGraph;
+import org.gephi.graph.api.Node;
+import org.gephi.visualization.spi.GraphContextMenuItem;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ * Tag Nodes context menu action, uses existing same Data Laboratory manipulator when available.
+ */
+@ServiceProvider(service = GraphContextMenuItem.class)
+public class TagNodes implements GraphContextMenuItem {
+    private NodesManipulator tagNodes;
+
+    public TagNodes() {
+        tagNodes = DataLaboratoryHelper.getDefault().getNodesManipulatorByName("TagNodes");
+    }
+
+    public void setup(HierarchicalGraph graph, Node[] nodes) {
+        tagNodes.setup(nodes, null);
+    }
+
+    public void execute() {
+        DataLaboratoryHelper.getDefault().executeManipulator(tagNodes);
+    }
+
+    public GraphContextMenuItem[] getSubItems() {
+        return null;
+    }
+
+    public String getName() {
+         return tagNodes != null ? tagNodes.getName() : null;
+    }
+
+    public String getDescription() {
+        return null;
+    }
+
+    public boolean isAvailable() {
+        return tagNodes != null;//Do not show tag nodes action if the TagNodes nodes manipulator does not exist
+    }
+
+    public boolean canExecute() {
+        return tagNodes != null ? tagNodes.canExecute() : false;
+    }
+
+    public int getType() {
+        return 400;
+    }
+
+    public int getPosition() {
+        return 0;
+    }
+
+    public Icon getIcon() {
+        return tagNodes != null ? tagNodes.getIcon() : null;
+    }
+
+    public Integer getMnemonicKey() {
+        return KeyEvent.VK_T;
+    }
+}

=== added file 'VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/Ungroup.java'
--- VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/Ungroup.java	1970-01-01 00:00:00 +0000
+++ VisualizationModule/src/org/gephi/visualization/apiimpl/contextmenuitems/Ungroup.java	2011-02-01 14:23:14 +0000
@@ -0,0 +1,91 @@
+/*
+Copyright 2008-2010 Gephi
+Authors : Mathieu Bastian <mathieu.bastian@xxxxxxxxx>, Eduardo Ramos <eduramiba@xxxxxxxxx>
+Website : http://www.gephi.org
+
+This file is part of Gephi.
+
+Gephi is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+Gephi is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with Gephi.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package org.gephi.visualization.apiimpl.contextmenuitems;
+
+import java.awt.event.KeyEvent;
+import javax.swing.Icon;
+import org.gephi.datalab.api.GraphElementsController;
+import org.gephi.graph.api.HierarchicalGraph;
+import org.gephi.graph.api.Node;
+import org.gephi.visualization.spi.GraphContextMenuItem;
+import org.openide.util.ImageUtilities;
+import org.openide.util.Lookup;
+import org.openide.util.NbBundle;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ *
+ */
+@ServiceProvider(service = GraphContextMenuItem.class)
+public class Ungroup implements GraphContextMenuItem {
+
+    private Node[] nodes;
+
+    public void setup(HierarchicalGraph graph, Node[] nodes) {
+        this.nodes = nodes;
+    }
+
+    public void execute() {
+        GraphElementsController gec = Lookup.getDefault().lookup(GraphElementsController.class);
+        gec.ungroupNodes(nodes);
+    }
+
+    public GraphContextMenuItem[] getSubItems() {
+        return null;
+    }
+
+    public String getName() {
+        return NbBundle.getMessage(Ungroup.class, "GraphContextMenu_Ungroup");
+    }
+
+    public String getDescription() {
+        return null;
+    }
+
+    public boolean isAvailable() {
+        return true;
+    }
+
+    public boolean canExecute() {
+        for (Node n : nodes) {
+            if (Lookup.getDefault().lookup(GraphElementsController.class).canUngroupNode(n)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public int getType() {
+        return 0;
+    }
+
+    public int getPosition() {
+        return 100;
+    }
+
+    public Icon getIcon() {
+        return ImageUtilities.loadImageIcon("org/gephi/visualization/api/resources/ungroup.png", false);
+    }
+
+    public Integer getMnemonicKey() {
+        return KeyEvent.VK_U;
+    }
+}

=== removed file 'VisualizationModule/src/org/gephi/visualization/apiimpl/es.po'
--- VisualizationModule/src/org/gephi/visualization/apiimpl/es.po	2010-12-27 04:43:17 +0000
+++ VisualizationModule/src/org/gephi/visualization/apiimpl/es.po	1970-01-01 00:00:00 +0000
@@ -1,57 +0,0 @@
-# Spanish translation for gephi
-# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010
-# This file is distributed under the same license as the gephi package.
-# FIRST AUTHOR <EMAIL@ADDRESS>, 2010.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: gephi\n"
-"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
-"POT-Creation-Date: 2010-04-07 13:16+0200\n"
-"PO-Revision-Date: 2010-12-26 00:11+0000\n"
-"Last-Translator: Eduardo Ramos <Unknown>\n"
-"Language-Team: Spanish <es@xxxxxx>\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2010-12-27 04:43+0000\n"
-"X-Generator: Launchpad (build Unknown)\n"
-
-msgid "GraphContextMenu_Group"
-msgstr "Agrupar"
-
-msgid "GraphContextMenu_Ungroup"
-msgstr "Desagrupar"
-
-msgid "GraphContextMenu_Expand"
-msgstr "Expandir"
-
-msgid "GraphContextMenu_Contract"
-msgstr "Contraer"
-
-msgid "GraphContextMenu_Settle"
-msgstr "Bloquear"
-
-msgid "GraphContextMenu_Free"
-msgstr "Desbloquear"
-
-msgid "GraphContextMenu_Delete"
-msgstr "Eliminar"
-
-msgid "GraphContextMenu_MoveToWorkspace"
-msgstr "Mover a..."
-
-msgid "GraphContextMenu_MoveToWorkspace_NewWorkspace"
-msgstr "Nuevo espacio de trabajo"
-
-msgid "GraphContextMenu_CopyToWorkspace"
-msgstr "Copiar a..."
-
-msgid "GraphContextMenu_CopyToWorkspace_NewWorkspace"
-msgstr "Nuevo espacio de trabajo"
-
-msgid "GraphContextMenu.Delete.message"
-msgstr "Los nodos serán eliminados, ¿Continuar?"
-
-msgid "GraphContextMenu.Delete.message.title"
-msgstr "Eliminar nodos"

=== removed file 'VisualizationModule/src/org/gephi/visualization/apiimpl/fr.po'
--- VisualizationModule/src/org/gephi/visualization/apiimpl/fr.po	2010-12-26 04:54:12 +0000
+++ VisualizationModule/src/org/gephi/visualization/apiimpl/fr.po	1970-01-01 00:00:00 +0000
@@ -1,57 +0,0 @@
-# French translation for gephi
-# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010
-# This file is distributed under the same license as the gephi package.
-# FIRST AUTHOR <EMAIL@ADDRESS>, 2010.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: gephi\n"
-"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
-"POT-Creation-Date: 2010-04-07 13:16+0200\n"
-"PO-Revision-Date: 2010-12-25 19:49+0000\n"
-"Last-Translator: Sébastien Heymann <Unknown>\n"
-"Language-Team: French <fr@xxxxxx>\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2010-12-26 04:53+0000\n"
-"X-Generator: Launchpad (build Unknown)\n"
-
-msgid "GraphContextMenu_Group"
-msgstr "Grouper"
-
-msgid "GraphContextMenu_Ungroup"
-msgstr "Dégrouper"
-
-msgid "GraphContextMenu_Expand"
-msgstr "Étendre"
-
-msgid "GraphContextMenu_Contract"
-msgstr "Contracter"
-
-msgid "GraphContextMenu_Settle"
-msgstr "Fixer"
-
-msgid "GraphContextMenu_Free"
-msgstr "Libérer"
-
-msgid "GraphContextMenu_Delete"
-msgstr "Supprimer"
-
-msgid "GraphContextMenu_MoveToWorkspace"
-msgstr "Déplacer vers..."
-
-msgid "GraphContextMenu_MoveToWorkspace_NewWorkspace"
-msgstr "Nouvel espace de travail"
-
-msgid "GraphContextMenu_CopyToWorkspace"
-msgstr "Copier vers..."
-
-msgid "GraphContextMenu_CopyToWorkspace_NewWorkspace"
-msgstr "Nouvel espace de travail"
-
-msgid "GraphContextMenu.Delete.message"
-msgstr "Les noeuds seront supprimés. Voulez-vous continuer ?"
-
-msgid "GraphContextMenu.Delete.message.title"
-msgstr "Suppression des noeuds"

=== removed file 'VisualizationModule/src/org/gephi/visualization/apiimpl/org-gephi-visualization-api.pot'
--- VisualizationModule/src/org/gephi/visualization/apiimpl/org-gephi-visualization-api.pot	2010-04-29 20:47:13 +0000
+++ VisualizationModule/src/org/gephi/visualization/apiimpl/org-gephi-visualization-api.pot	1970-01-01 00:00:00 +0000
@@ -1,37 +0,0 @@
-# English (en) translation for Gephi.
-# Copyright (C) 2010 Gephi contributors.
-# This file is distributed under the same license as the Gephi package.
-# Gephi Team <gephi.team@xxxxxxxxxxxxxxxxxxx>, 2010.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Gephi 0.7\n"
-"Report-Msgid-Bugs-To: gephi.team@xxxxxxxxxxxxxxxxxxx\n"
-"POT-Creation-Date: 2010-04-07 13:16+0200\n"
-"PO-Revision-Date: 2010-04-07 13:16+0200\n"
-"Last-Translator: Mathieu Bastian <gephi.team@xxxxxxxxxxxxxxxxxxx>\n"
-"Language-Team: English <https://launchpad.net/~gephi.team>\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-
-msgid "GraphContextMenu_Group"
-msgstr "Group"
-
-msgid "GraphContextMenu_Ungroup"
-msgstr "Ungroup"
-
-msgid "GraphContextMenu_Expand"
-msgstr "Expand"
-
-msgid "GraphContextMenu_Contract"
-msgstr "Contract"
-
-msgid "GraphContextMenu_Settle"
-msgstr "Settle"
-
-msgid "GraphContextMenu_Free"
-msgstr "Free"
-
-msgid "GraphContextMenu_Delete"
-msgstr "Delete"

=== removed file 'VisualizationModule/src/org/gephi/visualization/apiimpl/org-gephi-visualization-apiimpl.pot'
--- VisualizationModule/src/org/gephi/visualization/apiimpl/org-gephi-visualization-apiimpl.pot	2010-09-17 09:19:14 +0000
+++ VisualizationModule/src/org/gephi/visualization/apiimpl/org-gephi-visualization-apiimpl.pot	1970-01-01 00:00:00 +0000
@@ -1,55 +0,0 @@
-# English (en) translation for Gephi.
-# Copyright (C) 2010 Gephi contributors.
-# This file is distributed under the same license as the Gephi package.
-# Gephi Team <gephi.team@xxxxxxxxxxxxxxxxxxx>, 2010.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Gephi 0.7\n"
-"Report-Msgid-Bugs-To: gephi.team@xxxxxxxxxxxxxxxxxxx\n"
-"POT-Creation-Date: 2010-04-07 13:16+0200\n"
-"PO-Revision-Date: 2010-04-07 13:16+0200\n"
-"Last-Translator: Mathieu Bastian <gephi.team@xxxxxxxxxxxxxxxxxxx>\n"
-"Language-Team: English <https://launchpad.net/~gephi.team>\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-
-msgid "GraphContextMenu_Group"
-msgstr "Group"
-
-msgid "GraphContextMenu_Ungroup"
-msgstr "Ungroup"
-
-msgid "GraphContextMenu_Expand"
-msgstr "Expand"
-
-msgid "GraphContextMenu_Contract"
-msgstr "Contract"
-
-msgid "GraphContextMenu_Settle"
-msgstr "Settle"
-
-msgid "GraphContextMenu_Free"
-msgstr "Free"
-
-msgid "GraphContextMenu_Delete"
-msgstr "Delete"
-
-msgid "GraphContextMenu_MoveToWorkspace"
-msgstr "Move to..."
-
-msgid "GraphContextMenu_MoveToWorkspace_NewWorkspace"
-msgstr "New workspace"
-
-msgid "GraphContextMenu_CopyToWorkspace"
-msgstr "Copy to..."
-
-msgid "GraphContextMenu_CopyToWorkspace_NewWorkspace"
-msgstr "New workspace"
-
-msgid "GraphContextMenu.Delete.message"
-msgstr "Nodes will be deleted, do you want to proceed?"
-
-msgid "GraphContextMenu.Delete.message.title"
-msgstr "Delete nodes"

=== modified file 'VisualizationModule/src/org/gephi/visualization/bridge/DHNSEventBridge.java'
--- VisualizationModule/src/org/gephi/visualization/bridge/DHNSEventBridge.java	2010-09-30 08:11:38 +0000
+++ VisualizationModule/src/org/gephi/visualization/bridge/DHNSEventBridge.java	2011-02-01 14:23:14 +0000
@@ -21,33 +21,21 @@
 package org.gephi.visualization.bridge;
 
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
-import org.gephi.data.attributes.api.AttributeController;
-import org.gephi.data.attributes.api.AttributeModel;
-import org.gephi.desktop.project.api.ProjectControllerUI;
-import org.gephi.graph.api.Graph;
+import org.gephi.datalab.api.DataLaboratoryHelper;
+import org.gephi.datalab.spi.nodes.NodesManipulator;
 import org.gephi.graph.api.GraphController;
 import org.gephi.graph.api.GraphModel;
-import org.gephi.graph.api.GroupData;
 import org.gephi.graph.api.HierarchicalGraph;
 import org.gephi.graph.api.Model;
 import org.gephi.graph.api.Node;
 import org.gephi.graph.api.NodeData;
-import org.gephi.project.api.ProjectController;
-import org.gephi.project.api.Workspace;
 import org.gephi.visualization.VizArchitecture;
 import org.gephi.visualization.VizController;
 import org.gephi.visualization.apiimpl.GraphIO;
 import org.gephi.visualization.apiimpl.ModelImpl;
 import org.gephi.visualization.api.objects.ModelClass;
-import org.gephi.visualization.hull.ConvexHull;
 import org.gephi.visualization.opengl.AbstractEngine;
-import org.gephi.visualization.opengl.compatibility.objects.ConvexHullModel;
-import org.openide.DialogDisplayer;
-import org.openide.NotifyDescriptor;
 import org.openide.util.Lookup;
 
 /**
@@ -74,380 +62,70 @@
     public void initEvents() {
     }
 
-    //GROUPING
-    public boolean canExpand() {
-        GraphModel graphModel = graphController.getModel();
-        if (graphModel == null) {
-            return false;
-        }
-        this.graph = graphController.getModel().getHierarchicalGraphVisible();
-        ModelImpl[] selectedNodeModels = engine.getSelectedObjects(AbstractEngine.CLASS_NODE);
-        for (ModelImpl metaModelImpl : selectedNodeModels) {
-            NodeData nodeData = (NodeData) metaModelImpl.getObj();
-            Node node = nodeData.getNode(graph.getView().getViewId());
-            if (node != null && graph.getDescendantCount(node) > 0) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    public boolean canContract() {
-        GraphModel graphModel = graphController.getModel();
-        if (graphModel == null) {
-            return false;
-        }
-        this.graph = graphController.getModel().getHierarchicalGraphVisible();
-        ModelImpl[] selectedNodeModels = engine.getSelectedObjects(AbstractEngine.CLASS_NODE);
-        for (ModelImpl metaModelImpl : selectedNodeModels) {
-            NodeData nodeData = (NodeData) metaModelImpl.getObj();
-            Node node = nodeData.getNode(graph.getView().getViewId());
-            if (node != null && graph.getParent(node) != null) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    public boolean canGroup() {
-        GraphModel graphModel = graphController.getModel();
-        if (graphModel == null) {
-            return false;
-        }
-        this.graph = graphController.getModel().getHierarchicalGraphVisible();
-        ModelImpl[] selectedNodeModels = engine.getSelectedObjects(AbstractEngine.CLASS_NODE);
-        int nodesReallyPresent = 0;
+    public HierarchicalGraph getGraph(){
+        return graph;
+    }
+
+    public Node[] getSelectedNodes(){
+        GraphModel graphModel = graphController.getModel();
+        if (graphModel == null) {
+            return new Node[0];
+        }
+        this.graph = graphModel.getHierarchicalGraphVisible();
+        ModelImpl[] selectedNodeModels = engine.getSelectedObjects(AbstractEngine.CLASS_NODE);
+        ArrayList<Node> nodes=new ArrayList<Node>();
         for (ModelImpl metaModelImpl : selectedNodeModels) {
             NodeData nodeData = (NodeData) metaModelImpl.getObj();
             Node node = nodeData.getNode(graph.getView().getViewId());
             if (node != null) {
-                nodesReallyPresent++;
-            }
-        }
-        return nodesReallyPresent >= 1;
-    }
-
-    public boolean canUngroup() {
-        GraphModel graphModel = graphController.getModel();
-        if (graphModel == null) {
-            return false;
-        }
-        ModelImpl[] selectedNodeModels = engine.getSelectedObjects(AbstractEngine.CLASS_NODE);
-        return selectedNodeModels.length >= 1;
-    }
-
-    public void expand() {
-        this.graph = graphController.getModel().getHierarchicalGraphVisible();
-        ModelImpl[] selectedNodeModels = engine.getSelectedObjects(AbstractEngine.CLASS_NODE);
-        final ModelImpl[] models = Arrays.copyOf(selectedNodeModels, selectedNodeModels.length);
-        new Thread(new Runnable() {
-
-            public void run() {
-                try {
-                    for (ModelImpl metaModelImpl : models) {
-                        Node node = ((NodeData) metaModelImpl.getObj()).getNode(graph.getView().getViewId());
-                        if (node != null && graph.getDescendantCount(node) > 0) {
-                            expandPositioning(node);
-                            graph.expand(node);
-                        }
-                    }
-                } catch (Exception e) {
-                    e.printStackTrace();
-                    graph.readUnlockAll();
-                }
-            }
-        }, "Expand nodes").start();
-    }
-
-    public void contract() {
-        this.graph = graphController.getModel().getHierarchicalGraphVisible();
-        ModelImpl[] selectedNodeModels = engine.getSelectedObjects(AbstractEngine.CLASS_NODE);
-        final ModelImpl[] models = Arrays.copyOf(selectedNodeModels, selectedNodeModels.length);
-        new Thread(new Runnable() {
-
-            public void run() {
-                try {
-                    Set<Node> parents = new HashSet<Node>();
-                    for (ModelImpl metaModelImpl : models) {
-                        NodeData nodeData = (NodeData) metaModelImpl.getObj();
-                        Node node = nodeData.getNode(graph.getView().getViewId());
-                        if (node != null) {
-                            Node nodeParent = graph.getParent(node);
-                            if (nodeParent != null) {
-                                parents.add(nodeParent);
-                            }
-                        }
-                    }
-
-                    for (Node parent : parents) {
-                        GroupData gd = (GroupData) parent.getNodeData();
-                        if (gd.getHullModel() != null) {
-                            ConvexHull hull = ((ConvexHullModel) gd.getHullModel()).getObj();
-                            contractPositioning(hull);
-                        }
-                        graph.retract(parent);
-                    }
-                } catch (Exception e) {
-                    e.printStackTrace();
-                    graph.readUnlockAll();
-                }
-            }
-        }, "Contract nodes").start();
-    }
-
-    public void group() {
-        this.graph = graphController.getModel().getHierarchicalGraphVisible();
-        ModelImpl[] selectedNodeModels = engine.getSelectedObjects(AbstractEngine.CLASS_NODE);
-        final List<Node> newGroup = new ArrayList<Node>();
+                nodes.add(node);
+            }
+        }
+        return nodes.toArray(new Node[0]);
+    }
+
+    //MASS TAGGING OF NODES
+    public boolean isTagNodesAvailable() {
+        return DataLaboratoryHelper.getDefault().getNodesManipulatorByName("TagNodes")!=null;//Do not show tag nodes action if the TagNodes nodes manipulator does not exist
+    }
+
+    public boolean canTagNodes() {
+        this.graph = graphController.getModel().getHierarchicalGraphVisible();
+        ModelImpl[] selectedNodeModels = engine.getSelectedObjects(AbstractEngine.CLASS_NODE);
         for (int i = 0; i < selectedNodeModels.length; i++) {
             Node node = ((NodeData) selectedNodeModels[i].getObj()).getNode(graph.getView().getViewId());
             if (node != null) {
-                newGroup.add(node);
-            }
-        }
-        new Thread(new Runnable() {
-
-            public void run() {
-                try {
-                    float centroidX = 0;
-                    float centroidY = 0;
-                    int len = 0;
-                    float sizes = 0;
-                    float r = 0;
-                    float g = 0;
-                    float b = 0;
-                    Node group = graph.groupNodes(newGroup.toArray(new Node[0]));
-                    group.getNodeData().setLabel("Group (" + newGroup.size() + " nodes)");
-                    group.getNodeData().setSize(10f);
-                    for (Node child : newGroup) {
-                        centroidX += child.getNodeData().x();
-                        centroidY += child.getNodeData().y();
-                        len++;
-                        sizes += child.getNodeData().getSize() / 10f;
-                        r += child.getNodeData().r();
-                        g += child.getNodeData().g();
-                        b += child.getNodeData().b();
-                    }
-                    centroidX /= len;
-                    centroidY /= len;
-                    group.getNodeData().setSize(sizes);
-                    group.getNodeData().setColor(r / len, g / len, b / len);
-                    group.getNodeData().setX(centroidX);
-                    group.getNodeData().setY(centroidY);
-                } catch (Exception e) {
-                    graph.readUnlockAll();
-                    NotifyDescriptor.Message nd = new NotifyDescriptor.Message(e.getMessage());
-                    DialogDisplayer.getDefault().notifyLater(nd);
-                }
-            }
-        }, "Group nodes").start();
-    }
-
-    public void ungroup() {
-        this.graph = graphController.getModel().getHierarchicalGraph();
-        ModelImpl[] selectedNodeModels = engine.getSelectedObjects(AbstractEngine.CLASS_NODE);
-        final ModelImpl[] models = Arrays.copyOf(selectedNodeModels, selectedNodeModels.length);
-        new Thread(new Runnable() {
-
-            public void run() {
-                try {
-                    Set<Node> parents = new HashSet<Node>();
-                    for (ModelImpl metaModelImpl : models) {
-                        NodeData nodeData = (NodeData) metaModelImpl.getObj();
-                        Node node = nodeData.getNode(graph.getView().getViewId());
-                        if (node != null && graph.getDescendantCount(node) > 0) {
-                            parents.add(node);
-                        } else if (node != null && graph.getParent(node) != null) {
-                            parents.add(graph.getParent(node));
-                        }
-                    }
-                    for (Node parent : parents) {
-                        graph.ungroupNodes(parent);
-                    }
-                } catch (Exception e) {
-                    graph.readUnlockAll();
-                    NotifyDescriptor.Message nd = new NotifyDescriptor.Message(e.getMessage());
-                    DialogDisplayer.getDefault().notifyLater(nd);
-                }
-            }
-        }, "Ungroup nodes").start();
-    }
-
-    private void expandPositioning(Node node) {
-        NodeData nodeData = node.getNodeData();
-        float centroidX = 0;
-        float centroidY = 0;
-        int len = 0;
-        Node[] children = graph.getChildren(node).toArray();
-        for (Node child : children) {
-            centroidX += child.getNodeData().x();
-            centroidY += child.getNodeData().y();
-            len++;
-        }
-        centroidX /= len;
-        centroidY /= len;
-
-        float diffX = nodeData.x() - centroidX;
-        float diffY = nodeData.y() - centroidY;
-        for (Node child : children) {
-            NodeData nd = child.getNodeData();
-            nd.setX(nd.x() + diffX);
-            nd.setY(nd.y() + diffY);
-        }
-    }
-
-    private void contractPositioning(ConvexHull hull) {
-        NodeData metaNode = hull.getMetaNode().getNodeData();
-        metaNode.setX(hull.x());
-        metaNode.setY(hull.y());
-
-        ConvexHullModel model = (ConvexHullModel) hull.getModel();
-        model.setScale(0.9f);
-        model.setScaleQuantum(-0.1f);
-    }
-
-    //SETTLE AND FREE
-    public boolean canSettle() {
-        GraphModel graphModel = graphController.getModel();
-        if (graphModel == null) {
-            return false;
-        }
-        this.graph = graphController.getModel().getHierarchicalGraphVisible();
-        ModelImpl[] selectedNodeModels = engine.getSelectedObjects(AbstractEngine.CLASS_NODE);
-        for (ModelImpl metaModelImpl : selectedNodeModels) {
-            NodeData nodeData = (NodeData) metaModelImpl.getObj();
-            if (!nodeData.isFixed()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    public boolean canFree() {
-        GraphModel graphModel = graphController.getModel();
-        if (graphModel == null) {
-            return false;
-        }
-        this.graph = graphController.getModel().getHierarchicalGraphVisible();
-        ModelImpl[] selectedNodeModels = engine.getSelectedObjects(AbstractEngine.CLASS_NODE);
-        for (ModelImpl metaModelImpl : selectedNodeModels) {
-            NodeData nodeData = (NodeData) metaModelImpl.getObj();
-            if (nodeData.isFixed()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    public void settle() {
-        this.graph = graphController.getModel().getHierarchicalGraphVisible();
-        ModelImpl[] selectedNodeModels = engine.getSelectedObjects(AbstractEngine.CLASS_NODE);
-        for (ModelImpl metaModelImpl : selectedNodeModels) {
-            NodeData nodeData = (NodeData) metaModelImpl.getObj();
-            nodeData.setFixed(true);
-        }
-    }
-
-    public void free() {
-        this.graph = graphController.getModel().getHierarchicalGraphVisible();
-        ModelImpl[] selectedNodeModels = engine.getSelectedObjects(AbstractEngine.CLASS_NODE);
-        for (ModelImpl metaModelImpl : selectedNodeModels) {
-            NodeData nodeData = (NodeData) metaModelImpl.getObj();
-            nodeData.setFixed(false);
-        }
-    }
-
-    //DELETE
-    public boolean canDelete() {
-        GraphModel graphModel = graphController.getModel();
-        if (graphModel == null) {
-            return false;
-        }
-        this.graph = graphController.getModel().getHierarchicalGraphVisible();
-        ModelImpl[] selectedNodeModels = engine.getSelectedObjects(AbstractEngine.CLASS_NODE);
-        int nodesReallyPresent = 0;
-        for (ModelImpl metaModelImpl : selectedNodeModels) {
-            NodeData nodeData = (NodeData) metaModelImpl.getObj();
-            Node node = nodeData.getNode(graph.getView().getViewId());
-            if (node != null) {
-                nodesReallyPresent++;
-            }
-        }
-        return nodesReallyPresent >= 1;
-    }
-
-    public void delete() {
-        this.graph = graphController.getModel().getHierarchicalGraph();
-        ModelImpl[] selectedNodeModels = engine.getSelectedObjects(AbstractEngine.CLASS_NODE);
-        for (ModelImpl model : selectedNodeModels) {
-            NodeData nodeData = (NodeData) model.getObj();
-            Node node = nodeData.getRootNode();
-            if (node != null) {
-                graph.removeNode(node);
-            }
-        }
-    }
-
-    //MOVE & COPY WORKSPACE
-    public boolean canMoveOrCopyWorkspace() {
-        GraphModel graphModel = graphController.getModel();
-        if (graphModel == null) {
-            return false;
-        }
-        ModelImpl[] selectedNodeModels = engine.getSelectedObjects(AbstractEngine.CLASS_NODE);
-        int nodesReallyPresent = 0;
-        for (ModelImpl metaModelImpl : selectedNodeModels) {
-            NodeData nodeData = (NodeData) metaModelImpl.getObj();
-            Node node = nodeData.getNode(graph.getView().getViewId());
-            if (node != null) {
-                nodesReallyPresent++;
-            }
-        }
-        return nodesReallyPresent >= 1;
-    }
-
-    public void moveToWorkspace(Workspace workspace) {
-        copyToWorkspace(workspace);
-        delete();
-    }
-
-    public void moveToNewWorkspace() {
-        Workspace workspace = Lookup.getDefault().lookup(ProjectControllerUI.class).newWorkspace();
-        moveToWorkspace(workspace);
-    }
-
-    public void copyToWorkspace(Workspace workspace) {
-        AttributeController attributeController = Lookup.getDefault().lookup(AttributeController.class);
-        ProjectController projectController = Lookup.getDefault().lookup(ProjectController.class);
-
-        Workspace currentWorkspace = projectController.getCurrentWorkspace();
-        AttributeModel sourceAttributeModel = attributeController.getModel(currentWorkspace);
-        AttributeModel destAttributeModel = attributeController.getModel(workspace);
-        destAttributeModel.mergeModel(sourceAttributeModel);
-
-        GraphModel sourceModel = graphController.getModel(currentWorkspace);
-        GraphModel destModel = graphController.getModel(workspace);
-        Graph destGraph = destModel.getHierarchicalGraphVisible();
-        Graph sourceGraph = sourceModel.getHierarchicalGraphVisible();
-
-        ModelImpl[] selectedNodeModels = engine.getSelectedObjects(AbstractEngine.CLASS_NODE);
-        List<Node> nodes = new ArrayList<Node>();
-        for (ModelImpl metaModelImpl : selectedNodeModels) {
-            NodeData nodeData = (NodeData) metaModelImpl.getObj();
-            Node node = nodeData.getNode(sourceGraph.getView().getViewId());
-            if (node != null && destGraph.getNode(node.getNodeData().getId()) == null) {
-                nodes.add(node);
-            }
-        }
-
-        destModel.pushNodes(sourceGraph, nodes.toArray(new Node[0]));
-    }
-
-    public void copyToNewWorkspace() {
-        Workspace workspace = Lookup.getDefault().lookup(ProjectControllerUI.class).newWorkspace();
-        copyToWorkspace(workspace);
-    }
-
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public void tagNodes() {
+        try {
+            DataLaboratoryHelper dlh = DataLaboratoryHelper.getDefault();
+            NodesManipulator tag = dlh.getNodesManipulatorByName("TagNodes");
+
+            this.graph = graphController.getModel().getHierarchicalGraphVisible();
+            ModelImpl[] selectedNodeModels = engine.getSelectedObjects(AbstractEngine.CLASS_NODE);
+            final List<Node> nodes = new ArrayList<Node>();
+            for (int i = 0; i < selectedNodeModels.length; i++) {
+                Node node = ((NodeData) selectedNodeModels[i].getObj()).getNode(graph.getView().getViewId());
+                if (node != null) {
+                    nodes.add(node);
+                }
+            }
+
+            if (tag != null) {
+                tag.setup(nodes.toArray(new Node[0]), null);
+                if (tag.canExecute()) {
+                    dlh.executeManipulator(tag);
+                }
+            }
+        } catch (Exception ex) {
+            //TagNodes manipulator not available but it should be, ignore event
+        }
+    }
     public void mouseClick(ModelClass objectClass, Model[] clickedObjects) {
     }
 }

=== modified file 'VisualizationModule/src/org/gephi/visualization/component/GraphTopComponent.java'
--- VisualizationModule/src/org/gephi/visualization/component/GraphTopComponent.java	2010-08-22 21:06:56 +0000
+++ VisualizationModule/src/org/gephi/visualization/component/GraphTopComponent.java	2011-02-01 14:23:14 +0000
@@ -17,13 +17,18 @@
 
 You should have received a copy of the GNU Affero General Public License
 along with Gephi.  If not, see <http://www.gnu.org/licenses/>.
-*/
+ */
 package org.gephi.visualization.component;
 
+import java.awt.AWTEvent;
 import java.awt.BorderLayout;
 import java.awt.Component;
 import java.awt.FlowLayout;
+import java.awt.event.AWTEventListener;
+import java.awt.event.KeyEvent;
 import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.logging.Logger;
 import javax.swing.JComponent;
 import javax.swing.JPanel;
@@ -38,12 +43,14 @@
 import org.gephi.visualization.swing.GraphDrawableImpl;
 import org.gephi.project.api.Workspace;
 import org.gephi.project.api.WorkspaceListener;
+import org.gephi.visualization.bridge.DHNSEventBridge;
+import org.gephi.visualization.spi.GraphContextMenuItem;
 import org.openide.util.Lookup;
 import org.openide.util.NbBundle;
 import org.openide.windows.TopComponent;
 import org.openide.windows.WindowManager;
 
-final class GraphTopComponent extends TopComponent {
+final class GraphTopComponent extends TopComponent implements AWTEventListener {
 
     private static GraphTopComponent instance;
     /** path to the icon used by the component and its open action */
@@ -51,6 +58,8 @@
     private static final String PREFERRED_ID = "GraphTopComponent";
     private AbstractEngine engine;
     private VizBarController vizBarController;
+    private final DHNSEventBridge eventBridge;
+    private Map<Integer, GraphContextMenuItem> keyActionMappings = new HashMap<Integer, GraphContextMenuItem>();
 
     private GraphTopComponent() {
         initComponents();
@@ -60,6 +69,7 @@
 //        setIcon(Utilities.loadImage(ICON_PATH, true));
 
         engine = VizController.getInstance().getEngine();
+        eventBridge = (DHNSEventBridge) VizController.getInstance().getEventBridge();
 
         //Init
         initCollapsePanel();
@@ -81,6 +91,7 @@
                 });
             }
         });
+        initKeyEventContextMenuActionMappings();
         //remove(waitingLabel);
         //add(drawable.getGraphComponent(), BorderLayout.CENTER);
     }
@@ -176,6 +187,50 @@
         addonsBar.setEnabled(hasWorkspace);
     }
 
+    private void initKeyEventContextMenuActionMappings() {
+        mapItems(Lookup.getDefault().lookupAll(GraphContextMenuItem.class).toArray(new GraphContextMenuItem[0]));
+    }
+
+    private void mapItems(GraphContextMenuItem[] items){
+        Integer key;
+        GraphContextMenuItem[] subItems;
+        for (GraphContextMenuItem item : items) {
+            key = item.getMnemonicKey();
+            if (key != null) {
+                if (!keyActionMappings.containsKey(key)) {
+                    keyActionMappings.put(key, item);
+                }
+            }
+            subItems=item.getSubItems();
+            if(subItems!=null){
+                mapItems(subItems);
+            }
+        }
+    }
+
+    /**
+     * For attending Ctrl+Key events in graph window to launch context menu actions
+     */
+    public void eventDispatched(AWTEvent event) {
+        KeyEvent evt = (KeyEvent) event;
+
+        if (evt.getID() == KeyEvent.KEY_RELEASED && (evt.getModifiersEx() & KeyEvent.CTRL_DOWN_MASK) == KeyEvent.CTRL_DOWN_MASK) {
+            final GraphContextMenuItem item = keyActionMappings.get(evt.getKeyCode());
+            if (item != null) {
+                item.setup(eventBridge.getGraph(), eventBridge.getSelectedNodes());
+                if (item.isAvailable() && item.canExecute()) {
+                    new Thread(new Runnable() {
+
+                        public void run() {
+                            item.execute();
+                        }
+                    }).start();
+                }
+                evt.consume();
+            }
+        }
+    }
+
     /** This method is called from within the constructor to
      * initialize the form.
      * WARNING: Do NOT modify this code. The content of this method is
@@ -253,6 +308,16 @@
     }
 
     @Override
+    protected void componentActivated() {
+        java.awt.Toolkit.getDefaultToolkit().addAWTEventListener(this, AWTEvent.KEY_EVENT_MASK);
+    }
+
+    @Override
+    protected void componentDeactivated() {
+        java.awt.Toolkit.getDefaultToolkit().removeAWTEventListener(this);
+    }
+
+    @Override
     public void componentClosed() {
         engine.stopDisplay();
     }


Follow ups