]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/actions/AssignSymbolGroup.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.modeling.ui / src / org / simantics / modeling / ui / actions / AssignSymbolGroup.java
index 18317084c2b2b977eef102abe5c86d2980159c32..aada046df3ce04cbccac48b553f8cb8948a074fa 100644 (file)
-/*******************************************************************************\r
- * Copyright (c) 2012 Association for Decentralized Information Management\r
- * in Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- *     VTT Technical Research Centre of Finland - initial API and implementation\r
- *******************************************************************************/\r
-package org.simantics.modeling.ui.actions;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Arrays;\r
-import java.util.Collection;\r
-import java.util.Collections;\r
-import java.util.HashSet;\r
-import java.util.List;\r
-import java.util.Set;\r
-import java.util.concurrent.atomic.AtomicReference;\r
-\r
-import org.eclipse.jface.dialogs.Dialog;\r
-import org.eclipse.jface.dialogs.IInputValidator;\r
-import org.eclipse.jface.dialogs.InputDialog;\r
-import org.eclipse.jface.dialogs.MessageDialog;\r
-import org.eclipse.jface.viewers.ICheckStateProvider;\r
-import org.eclipse.jface.viewers.IStructuredContentProvider;\r
-import org.eclipse.jface.viewers.LabelProvider;\r
-import org.eclipse.jface.viewers.Viewer;\r
-import org.eclipse.swt.widgets.Shell;\r
-import org.eclipse.ui.PlatformUI;\r
-import org.simantics.Simantics;\r
-import org.simantics.db.ReadGraph;\r
-import org.simantics.db.Resource;\r
-import org.simantics.db.WriteGraph;\r
-import org.simantics.db.common.request.PossibleIndexRoot;\r
-import org.simantics.db.common.request.UniqueRead;\r
-import org.simantics.db.common.request.WriteRequest;\r
-import org.simantics.db.common.utils.NameUtils;\r
-import org.simantics.db.exception.DatabaseException;\r
-import org.simantics.db.layer0.adapter.ActionFactory;\r
-import org.simantics.db.layer0.adapter.ActionFactory2;\r
-import org.simantics.db.request.Read;\r
-import org.simantics.diagram.stubs.DiagramResource;\r
-import org.simantics.modeling.AssignSymbolGroupRequest;\r
-import org.simantics.modeling.GetSymbolGroups;\r
-import org.simantics.modeling.NewSymbolGroupRequest;\r
-import org.simantics.utils.strings.AlphanumComparator;\r
-import org.simantics.utils.ui.ErrorLogger;\r
-import org.simantics.utils.ui.dialogs.ShowMessage;\r
-\r
-/**\r
- * @author Hannu Niemistö\r
- * @author Tuukka Lehtonen <tuukka.lehtonen@semantum.fi>\r
- */\r
-public class AssignSymbolGroup implements ActionFactory, ActionFactory2 {\r
-\r
-    @Override\r
-    public Runnable create(Collection<?> targets) {\r
-        final ArrayList<Resource> resources = new ArrayList<Resource>();\r
-        for (Object target : targets) {\r
-            if (!(target instanceof Resource))\r
-                return null;\r
-            resources.add((Resource) target);\r
-        }\r
-        return new Runnable() {\r
-            @Override\r
-            public void run() {\r
-                assignGroups(resources);\r
-                \r
-            }\r
-        };\r
-    }\r
-\r
-    @Override\r
-    public Runnable create(Object target) {\r
-        if(!(target instanceof Resource))\r
-            return null;\r
-        final Resource symbol = (Resource)target;\r
-        return new Runnable() {\r
-            @Override\r
-            public void run() {\r
-                assignGroups(Collections.singletonList(symbol));\r
-            }\r
-        };\r
-    }\r
-\r
-    private static final SymbolGroup[] NO_SYMBOL_GROUPS = new SymbolGroup[0];\r
-\r
-    static enum Tristate {\r
-        NONE, SOME, ALL;\r
-\r
-        public static Tristate add(Tristate current, boolean next) {\r
-            if (current == null)\r
-                return next ? ALL : NONE;\r
-            switch (current) {\r
-            case ALL: return next ? ALL : SOME; \r
-            case SOME: return next ? SOME : SOME;\r
-            case NONE: return next ? SOME : NONE;\r
-            default: return NONE;\r
-            }\r
-        }\r
-    }\r
-\r
-    private static class SymbolGroup implements Comparable<SymbolGroup> {\r
-        Resource resource;\r
-        String name;\r
-        Tristate originallySelected;\r
-        Tristate selected;\r
-\r
-        public SymbolGroup(Resource resource, String name, Tristate originallySelected, Tristate selected) {\r
-            super();\r
-            this.resource = resource;\r
-            this.name = name;\r
-            this.originallySelected = originallySelected;\r
-            this.selected = selected;\r
-        }\r
-\r
-        @Override\r
-        public int compareTo(SymbolGroup o) {\r
-            return AlphanumComparator.CASE_INSENSITIVE_COMPARATOR.compare(name, o.name);\r
-        }\r
-\r
-        @Override\r
-        public String toString() {\r
-            return getClass().getSimpleName() + "[name=" + name\r
-                    + ", originally selected=" + originallySelected\r
-                    + ", selected=" + selected + "]";\r
-        }\r
-    }\r
-\r
-    private static class ContentProviderImpl implements IStructuredContentProvider {    \r
-        @Override\r
-        public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {\r
-        }\r
-\r
-        @Override\r
-        public void dispose() {\r
-        }\r
-\r
-        @Override\r
-        public Object[] getElements(Object inputElement) {\r
-            return (Object[])inputElement;\r
-        }\r
-    };\r
-\r
-    private static class LabelProviderImpl extends LabelProvider {\r
-        @Override\r
-        public String getText(Object element) {\r
-            return ((SymbolGroup)element).name;\r
-        }\r
-    }\r
-\r
-    private static class CheckStateProviderImpl implements ICheckStateProvider {\r
-        @Override\r
-        public boolean isChecked(Object element) {\r
-            return ((SymbolGroup) element).selected != Tristate.NONE;\r
-        }\r
-        @Override\r
-        public boolean isGrayed(Object element) {\r
-            return ((SymbolGroup) element).selected == Tristate.SOME;\r
-        }\r
-    }\r
-\r
-    private static Resource getCommonModel(final Collection<Resource> symbols) {\r
-        try {\r
-            return Simantics.sync(new UniqueRead<Resource>() {\r
-                @Override\r
-                public Resource perform(ReadGraph graph) throws DatabaseException {\r
-                    return getPossibleIndexRoot(graph, symbols);\r
-                }\r
-            });\r
-        } catch (DatabaseException e) {\r
-            ErrorLogger.defaultLogError(e);\r
-            return null;\r
-        }\r
-    }\r
-\r
-    private static Resource getPossibleIndexRoot(ReadGraph g, Collection<Resource> symbols) throws DatabaseException {\r
-        Resource model = null;\r
-        for (Resource symbol : symbols) {\r
-            Resource m = getIndexRootOf(g, symbol);\r
-            if (m == null)\r
-                return null;\r
-            if (model == null)\r
-                model = m;\r
-            else if (!model.equals(m))\r
-                return null;\r
-        }\r
-        return model;\r
-    }\r
-\r
-    private static Resource getIndexRootOf(ReadGraph g, Resource symbol) throws DatabaseException {\r
-        return g.syncRequest(new PossibleIndexRoot(symbol));\r
-    }\r
-\r
-    private static SymbolGroup[] getSymbolGroups(final Collection<Resource> symbols) {\r
-        try {\r
-            return Simantics.getSession().syncRequest(new Read<SymbolGroup[]>() {\r
-                @Override\r
-                public SymbolGroup[] perform(ReadGraph g) throws DatabaseException {\r
-                    return getSymbolGroups(g, symbols);\r
-                }\r
-            });\r
-        } catch(DatabaseException e) {\r
-            e.printStackTrace();\r
-            return NO_SYMBOL_GROUPS;\r
-        }\r
-    }\r
-\r
-    private static SymbolGroup[] getSymbolGroups(ReadGraph g, Collection<Resource> symbols) throws DatabaseException {\r
-        Resource model = getPossibleIndexRoot(g, symbols);\r
-        if (model == null)\r
-            return NO_SYMBOL_GROUPS;\r
-        // All symbols have same model.\r
-        // Resolve the symbol group selection states now.\r
-        ArrayList<SymbolGroup> result = new ArrayList<SymbolGroup>();\r
-        DiagramResource DIA = DiagramResource.getInstance(g);\r
-        for (Resource library : GetSymbolGroups.getSymbolGroups(g, model)) {\r
-            Tristate selected = getLibrarySelectionState(g, library, symbols, DIA);\r
-            selected = selected != null ? selected : Tristate.NONE;\r
-            result.add( new SymbolGroup(\r
-                    library,\r
-                    NameUtils.getSafeLabel(g, library),\r
-                    selected,\r
-                    selected) );\r
-        }\r
-        //System.out.println("result: " + EString.implode(result));\r
-        Collections.sort(result);\r
-        //System.out.println("sorted result: " + EString.implode(result));\r
-        return result.toArray(new SymbolGroup[result.size()]);\r
-    }\r
-\r
-    protected static Tristate getLibrarySelectionState(ReadGraph graph, Resource library,\r
-            Collection<Resource> symbols, DiagramResource DIA) throws DatabaseException {\r
-        Tristate selected = null;\r
-        for (Resource symbol : symbols) {\r
-            selected = Tristate.add(selected, graph.hasStatement(library, DIA.HasSymbol, symbol));\r
-        }\r
-        return selected != null ? selected : Tristate.NONE;\r
-    }\r
-\r
-    private static SymbolGroup[] selectedElements(SymbolGroup[] symbolGroups) {\r
-        int count = 0;\r
-        for(SymbolGroup g : symbolGroups)\r
-            if(g.selected != Tristate.NONE)\r
-                ++count;\r
-        SymbolGroup[] result = new SymbolGroup[count];\r
-        count = 0;\r
-        for(SymbolGroup g : symbolGroups)\r
-            if(g.selected != Tristate.NONE)\r
-                result[count++] = g;\r
-        return result;\r
-    }\r
-\r
-    public void assignGroups(final Collection<Resource> symbols) {\r
-        if (symbols.isEmpty())\r
-            return;\r
-\r
-        final Resource model = getCommonModel(symbols);\r
-        if (model == null) {\r
-            ShowMessage.showInformation("Same Model Required", "All the selected symbols must be from within the same model.");\r
-            return;\r
-        }\r
-\r
-        final AtomicReference<SymbolGroup[]> groups =\r
-                new AtomicReference<SymbolGroup[]>( getSymbolGroups(symbols) );\r
-\r
-        StringBuilder message = new StringBuilder();\r
-        message.append("Select symbol groups the selected ");\r
-        if (symbols.size() > 1)\r
-            message.append(symbols.size()).append(" symbols are shown in.");\r
-        else\r
-            message.append("symbol is shown in.");\r
-\r
-        AssignSymbolGroupsDialog dialog = new AssignSymbolGroupsDialog(\r
-                PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),\r
-                groups.get(),\r
-                new ContentProviderImpl(), \r
-                new LabelProviderImpl(), \r
-                new CheckStateProviderImpl(),\r
-                message.toString()) {\r
-\r
-            @Override\r
-            protected void checkStateChanged(Object[] elements, boolean checked) {\r
-                for (Object _g : elements) {\r
-                    SymbolGroup g = (SymbolGroup) _g;\r
-                    g.selected = checked ? Tristate.ALL : Tristate.NONE;\r
-                    // Refresh checked states through provider.\r
-                    listViewer.refresh();\r
-                }\r
-            }\r
-\r
-            @Override\r
-            protected void newAction() {\r
-                SymbolGroup newGroup = newSymbolGroup(getShell(), model, (SymbolGroup[])inputElement);\r
-                if (newGroup != null) {\r
-                    // Select the new library by default.\r
-                    newGroup.selected = Tristate.ALL;\r
-\r
-                    SymbolGroup[] newGroups = (SymbolGroup[]) inputElement;\r
-                    newGroups = Arrays.copyOf(newGroups, newGroups.length+1);\r
-                    newGroups[newGroups.length-1] = newGroup;\r
-                    Arrays.sort(newGroups);\r
-                    listViewer.setInput(newGroups);\r
-                    inputElement = newGroups;\r
-                    groups.set(newGroups);\r
-                }\r
-            }\r
-\r
-            @Override\r
-            protected void deleteAction(Object[] array) {\r
-                SymbolGroup[] groupsToRemove = Arrays.copyOf(array, array.length, SymbolGroup[].class);\r
-                if (removeSymbolGroups(getShell(), groupsToRemove)) {\r
-                    listViewer.remove(groupsToRemove);\r
-                    Set<SymbolGroup> removedGroups = new HashSet<SymbolGroup>();\r
-                    for (SymbolGroup removed : groupsToRemove)\r
-                        removedGroups.add(removed);\r
-                    List<SymbolGroup> newGroups = new ArrayList<SymbolGroup>(groups.get().length);\r
-                    for (SymbolGroup old : groups.get()) {\r
-                        if (!removedGroups.contains(old))\r
-                            newGroups.add(old);\r
-                    }\r
-                    groups.set( newGroups.toArray(NO_SYMBOL_GROUPS) );\r
-                }\r
-            }\r
-        };\r
-        dialog.setTitle("Symbol Group Assignments");\r
-        dialog.setInitialSelections(selectedElements(groups.get()));\r
-        if (dialog.open() == Dialog.OK) {\r
-            final ArrayList<SymbolGroup> added = new ArrayList<SymbolGroup>();\r
-            final ArrayList<SymbolGroup> removed = new ArrayList<SymbolGroup>();\r
-            for (SymbolGroup g : groups.get()) {\r
-                if (g.selected != g.originallySelected && g.selected == Tristate.ALL)\r
-                    added.add(g);\r
-                if (g.selected != g.originallySelected && g.selected == Tristate.NONE)\r
-                    removed.add(g);\r
-            }\r
-            if (!added.isEmpty() || !removed.isEmpty()) {\r
-                ArrayList<Resource> addedSymbolGroups = new ArrayList<Resource>();\r
-                ArrayList<Resource> removedSymbolGroups = new ArrayList<Resource>();\r
-                for (SymbolGroup group : added)\r
-                    addedSymbolGroups.add(group.resource);\r
-                for (SymbolGroup group : removed)\r
-                    removedSymbolGroups.add(group.resource);\r
-                Simantics.getSession().asyncRequest(new AssignSymbolGroupRequest(addedSymbolGroups, removedSymbolGroups, symbols));\r
-            }\r
-        }\r
-    }\r
-\r
-    private static SymbolGroup newSymbolGroup(Shell shell, Resource model, final SymbolGroup[] oldGroups) {\r
-        InputDialog dialog = new InputDialog(shell,\r
-                "New Symbol Group",\r
-                "Write the name of the new symbol group.",\r
-                "NewSymbolGroup",\r
-                new IInputValidator() {\r
-                    @Override\r
-                    public String isValid(String newText) {\r
-                        newText = newText.trim();\r
-                        if (newText.isEmpty())\r
-                            return "The name must be non-empty.";\r
-                        for (SymbolGroup g : oldGroups)\r
-                            if (newText.equals(g.name))\r
-                                return "A symbol group with that name already exists.";\r
-                        return null;\r
-                    }\r
-                }\r
-        );\r
-        if (dialog.open() == Dialog.OK) {\r
-            String name = dialog.getValue();\r
-            try {\r
-                NewSymbolGroupRequest request = new NewSymbolGroupRequest(name, model);\r
-                Resource symbolGroup = Simantics.getSession().syncRequest(request);\r
-                if (symbolGroup == null)\r
-                    return null;\r
-                return new SymbolGroup(symbolGroup, name, Tristate.NONE, Tristate.NONE);\r
-            } catch (DatabaseException e) {\r
-                ErrorLogger.defaultLogError(e);\r
-                return null;\r
-            }\r
-        }\r
-        return null;\r
-    }\r
-\r
-    private boolean removeSymbolGroups(Shell shell, final SymbolGroup[] groups) {\r
-        if (groups.length == 0)\r
-            return false;\r
-        String message;\r
-        if (groups.length == 1)\r
-            message = "Are you sure you want to remove symbol group '" + groups[0].name + "' ?";\r
-        else\r
-            message = "Are you sure you want to remove " + groups.length + " symbol groups?";\r
-        MessageDialog dialog = \r
-            new MessageDialog(shell, "Confirm removal", null, message, MessageDialog.QUESTION, new String[] { "OK", "Cancel" }, 0);\r
-        if (dialog.open() == Dialog.OK) {\r
-            Simantics.getSession().asyncRequest(new WriteRequest() {\r
-                @Override\r
-                public void perform(WriteGraph graph) throws DatabaseException {\r
-                    for (SymbolGroup group : groups)\r
-                        graph.deny(group.resource);\r
-                }\r
-            });\r
-            return true;\r
-        }\r
-        else\r
-            return false;\r
-    }\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2012 Association for Decentralized Information Management
+ * in Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics.modeling.ui.actions;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IInputValidator;
+import org.eclipse.jface.dialogs.InputDialog;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.viewers.ICheckStateProvider;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.PlatformUI;
+import org.simantics.Simantics;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.common.request.PossibleIndexRoot;
+import org.simantics.db.common.request.UniqueRead;
+import org.simantics.db.common.request.WriteRequest;
+import org.simantics.db.common.utils.NameUtils;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.adapter.ActionFactory;
+import org.simantics.db.layer0.adapter.ActionFactory2;
+import org.simantics.db.request.Read;
+import org.simantics.diagram.stubs.DiagramResource;
+import org.simantics.modeling.AssignSymbolGroupRequest;
+import org.simantics.modeling.GetSymbolGroups;
+import org.simantics.modeling.NewSymbolGroupRequest;
+import org.simantics.utils.strings.AlphanumComparator;
+import org.simantics.utils.ui.ErrorLogger;
+import org.simantics.utils.ui.dialogs.ShowMessage;
+
+/**
+ * @author Hannu Niemist&ouml;
+ * @author Tuukka Lehtonen <tuukka.lehtonen@semantum.fi>
+ */
+public class AssignSymbolGroup implements ActionFactory, ActionFactory2 {
+
+    @Override
+    public Runnable create(Collection<?> targets) {
+        final ArrayList<Resource> resources = new ArrayList<Resource>();
+        for (Object target : targets) {
+            if (!(target instanceof Resource))
+                return null;
+            resources.add((Resource) target);
+        }
+        return new Runnable() {
+            @Override
+            public void run() {
+                assignGroups(resources);
+                
+            }
+        };
+    }
+
+    @Override
+    public Runnable create(Object target) {
+        if(!(target instanceof Resource))
+            return null;
+        final Resource symbol = (Resource)target;
+        return new Runnable() {
+            @Override
+            public void run() {
+                assignGroups(Collections.singletonList(symbol));
+            }
+        };
+    }
+
+    private static final SymbolGroup[] NO_SYMBOL_GROUPS = new SymbolGroup[0];
+
+    static enum Tristate {
+        NONE, SOME, ALL;
+
+        public static Tristate add(Tristate current, boolean next) {
+            if (current == null)
+                return next ? ALL : NONE;
+            switch (current) {
+            case ALL: return next ? ALL : SOME; 
+            case SOME: return next ? SOME : SOME;
+            case NONE: return next ? SOME : NONE;
+            default: return NONE;
+            }
+        }
+    }
+
+    private static class SymbolGroup implements Comparable<SymbolGroup> {
+        Resource resource;
+        String name;
+        Tristate originallySelected;
+        Tristate selected;
+
+        public SymbolGroup(Resource resource, String name, Tristate originallySelected, Tristate selected) {
+            super();
+            this.resource = resource;
+            this.name = name;
+            this.originallySelected = originallySelected;
+            this.selected = selected;
+        }
+
+        @Override
+        public int compareTo(SymbolGroup o) {
+            return AlphanumComparator.CASE_INSENSITIVE_COMPARATOR.compare(name, o.name);
+        }
+
+        @Override
+        public String toString() {
+            return getClass().getSimpleName() + "[name=" + name
+                    + ", originally selected=" + originallySelected
+                    + ", selected=" + selected + "]";
+        }
+    }
+
+    private static class ContentProviderImpl implements IStructuredContentProvider {    
+        @Override
+        public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+        }
+
+        @Override
+        public void dispose() {
+        }
+
+        @Override
+        public Object[] getElements(Object inputElement) {
+            return (Object[])inputElement;
+        }
+    };
+
+    private static class LabelProviderImpl extends LabelProvider {
+        @Override
+        public String getText(Object element) {
+            return ((SymbolGroup)element).name;
+        }
+    }
+
+    private static class CheckStateProviderImpl implements ICheckStateProvider {
+        @Override
+        public boolean isChecked(Object element) {
+            return ((SymbolGroup) element).selected != Tristate.NONE;
+        }
+        @Override
+        public boolean isGrayed(Object element) {
+            return ((SymbolGroup) element).selected == Tristate.SOME;
+        }
+    }
+
+    private static Resource getCommonModel(final Collection<Resource> symbols) {
+        try {
+            return Simantics.sync(new UniqueRead<Resource>() {
+                @Override
+                public Resource perform(ReadGraph graph) throws DatabaseException {
+                    return getPossibleIndexRoot(graph, symbols);
+                }
+            });
+        } catch (DatabaseException e) {
+            ErrorLogger.defaultLogError(e);
+            return null;
+        }
+    }
+
+    private static Resource getPossibleIndexRoot(ReadGraph g, Collection<Resource> symbols) throws DatabaseException {
+        Resource model = null;
+        for (Resource symbol : symbols) {
+            Resource m = getIndexRootOf(g, symbol);
+            if (m == null)
+                return null;
+            if (model == null)
+                model = m;
+            else if (!model.equals(m))
+                return null;
+        }
+        return model;
+    }
+
+    private static Resource getIndexRootOf(ReadGraph g, Resource symbol) throws DatabaseException {
+        return g.syncRequest(new PossibleIndexRoot(symbol));
+    }
+
+    private static SymbolGroup[] getSymbolGroups(final Collection<Resource> symbols) {
+        try {
+            return Simantics.getSession().syncRequest(new Read<SymbolGroup[]>() {
+                @Override
+                public SymbolGroup[] perform(ReadGraph g) throws DatabaseException {
+                    return getSymbolGroups(g, symbols);
+                }
+            });
+        } catch(DatabaseException e) {
+            e.printStackTrace();
+            return NO_SYMBOL_GROUPS;
+        }
+    }
+
+    private static SymbolGroup[] getSymbolGroups(ReadGraph g, Collection<Resource> symbols) throws DatabaseException {
+        Resource model = getPossibleIndexRoot(g, symbols);
+        if (model == null)
+            return NO_SYMBOL_GROUPS;
+        // All symbols have same model.
+        // Resolve the symbol group selection states now.
+        ArrayList<SymbolGroup> result = new ArrayList<SymbolGroup>();
+        DiagramResource DIA = DiagramResource.getInstance(g);
+        for (Resource library : GetSymbolGroups.getSymbolGroups(g, model)) {
+            Tristate selected = getLibrarySelectionState(g, library, symbols, DIA);
+            selected = selected != null ? selected : Tristate.NONE;
+            result.add( new SymbolGroup(
+                    library,
+                    NameUtils.getSafeLabel(g, library),
+                    selected,
+                    selected) );
+        }
+        //System.out.println("result: " + EString.implode(result));
+        Collections.sort(result);
+        //System.out.println("sorted result: " + EString.implode(result));
+        return result.toArray(new SymbolGroup[result.size()]);
+    }
+
+    protected static Tristate getLibrarySelectionState(ReadGraph graph, Resource library,
+            Collection<Resource> symbols, DiagramResource DIA) throws DatabaseException {
+        Tristate selected = null;
+        for (Resource symbol : symbols) {
+            selected = Tristate.add(selected, graph.hasStatement(library, DIA.HasSymbol, symbol));
+        }
+        return selected != null ? selected : Tristate.NONE;
+    }
+
+    private static SymbolGroup[] selectedElements(SymbolGroup[] symbolGroups) {
+        int count = 0;
+        for(SymbolGroup g : symbolGroups)
+            if(g.selected != Tristate.NONE)
+                ++count;
+        SymbolGroup[] result = new SymbolGroup[count];
+        count = 0;
+        for(SymbolGroup g : symbolGroups)
+            if(g.selected != Tristate.NONE)
+                result[count++] = g;
+        return result;
+    }
+
+    public void assignGroups(final Collection<Resource> symbols) {
+        if (symbols.isEmpty())
+            return;
+
+        final Resource model = getCommonModel(symbols);
+        if (model == null) {
+            ShowMessage.showInformation("Same Model Required", "All the selected symbols must be from within the same model.");
+            return;
+        }
+
+        final AtomicReference<SymbolGroup[]> groups =
+                new AtomicReference<SymbolGroup[]>( getSymbolGroups(symbols) );
+
+        StringBuilder message = new StringBuilder();
+        message.append("Select symbol groups the selected ");
+        if (symbols.size() > 1)
+            message.append(symbols.size()).append(" symbols are shown in.");
+        else
+            message.append("symbol is shown in.");
+
+        AssignSymbolGroupsDialog dialog = new AssignSymbolGroupsDialog(
+                PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
+                groups.get(),
+                new ContentProviderImpl(), 
+                new LabelProviderImpl(), 
+                new CheckStateProviderImpl(),
+                message.toString()) {
+
+            @Override
+            protected void checkStateChanged(Object[] elements, boolean checked) {
+                for (Object _g : elements) {
+                    SymbolGroup g = (SymbolGroup) _g;
+                    g.selected = checked ? Tristate.ALL : Tristate.NONE;
+                    // Refresh checked states through provider.
+                    listViewer.refresh();
+                }
+            }
+
+            @Override
+            protected void newAction() {
+                SymbolGroup newGroup = newSymbolGroup(getShell(), model, (SymbolGroup[])inputElement);
+                if (newGroup != null) {
+                    // Select the new library by default.
+                    newGroup.selected = Tristate.ALL;
+
+                    SymbolGroup[] newGroups = (SymbolGroup[]) inputElement;
+                    newGroups = Arrays.copyOf(newGroups, newGroups.length+1);
+                    newGroups[newGroups.length-1] = newGroup;
+                    Arrays.sort(newGroups);
+                    listViewer.setInput(newGroups);
+                    inputElement = newGroups;
+                    groups.set(newGroups);
+                }
+            }
+
+            @Override
+            protected void deleteAction(Object[] array) {
+                SymbolGroup[] groupsToRemove = Arrays.copyOf(array, array.length, SymbolGroup[].class);
+                if (removeSymbolGroups(getShell(), groupsToRemove)) {
+                    listViewer.remove(groupsToRemove);
+                    Set<SymbolGroup> removedGroups = new HashSet<SymbolGroup>();
+                    for (SymbolGroup removed : groupsToRemove)
+                        removedGroups.add(removed);
+                    List<SymbolGroup> newGroups = new ArrayList<SymbolGroup>(groups.get().length);
+                    for (SymbolGroup old : groups.get()) {
+                        if (!removedGroups.contains(old))
+                            newGroups.add(old);
+                    }
+                    groups.set( newGroups.toArray(NO_SYMBOL_GROUPS) );
+                }
+            }
+        };
+        dialog.setTitle("Symbol Group Assignments");
+        dialog.setInitialSelections(selectedElements(groups.get()));
+        if (dialog.open() == Dialog.OK) {
+            final ArrayList<SymbolGroup> added = new ArrayList<SymbolGroup>();
+            final ArrayList<SymbolGroup> removed = new ArrayList<SymbolGroup>();
+            for (SymbolGroup g : groups.get()) {
+                if (g.selected != g.originallySelected && g.selected == Tristate.ALL)
+                    added.add(g);
+                if (g.selected != g.originallySelected && g.selected == Tristate.NONE)
+                    removed.add(g);
+            }
+            if (!added.isEmpty() || !removed.isEmpty()) {
+                ArrayList<Resource> addedSymbolGroups = new ArrayList<Resource>();
+                ArrayList<Resource> removedSymbolGroups = new ArrayList<Resource>();
+                for (SymbolGroup group : added)
+                    addedSymbolGroups.add(group.resource);
+                for (SymbolGroup group : removed)
+                    removedSymbolGroups.add(group.resource);
+                Simantics.getSession().asyncRequest(new AssignSymbolGroupRequest(addedSymbolGroups, removedSymbolGroups, symbols));
+            }
+        }
+    }
+
+    private static SymbolGroup newSymbolGroup(Shell shell, Resource model, final SymbolGroup[] oldGroups) {
+        InputDialog dialog = new InputDialog(shell,
+                "New Symbol Group",
+                "Write the name of the new symbol group.",
+                "NewSymbolGroup",
+                new IInputValidator() {
+                    @Override
+                    public String isValid(String newText) {
+                        newText = newText.trim();
+                        if (newText.isEmpty())
+                            return "The name must be non-empty.";
+                        for (SymbolGroup g : oldGroups)
+                            if (newText.equals(g.name))
+                                return "A symbol group with that name already exists.";
+                        return null;
+                    }
+                }
+        );
+        if (dialog.open() == Dialog.OK) {
+            String name = dialog.getValue();
+            try {
+                NewSymbolGroupRequest request = new NewSymbolGroupRequest(name, model);
+                Resource symbolGroup = Simantics.getSession().syncRequest(request);
+                if (symbolGroup == null)
+                    return null;
+                return new SymbolGroup(symbolGroup, name, Tristate.NONE, Tristate.NONE);
+            } catch (DatabaseException e) {
+                ErrorLogger.defaultLogError(e);
+                return null;
+            }
+        }
+        return null;
+    }
+
+    private boolean removeSymbolGroups(Shell shell, final SymbolGroup[] groups) {
+        if (groups.length == 0)
+            return false;
+        String message;
+        if (groups.length == 1)
+            message = "Are you sure you want to remove symbol group '" + groups[0].name + "' ?";
+        else
+            message = "Are you sure you want to remove " + groups.length + " symbol groups?";
+        MessageDialog dialog = 
+            new MessageDialog(shell, "Confirm removal", null, message, MessageDialog.QUESTION, new String[] { "OK", "Cancel" }, 0);
+        if (dialog.open() == Dialog.OK) {
+            Simantics.getSession().asyncRequest(new WriteRequest() {
+                @Override
+                public void perform(WriteGraph graph) throws DatabaseException {
+                    for (SymbolGroup group : groups)
+                        graph.deny(group.resource);
+                }
+            });
+            return true;
+        }
+        else
+            return false;
+    }
+
+}