]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/componentTypeEditor/ConfigurationPropertiesSection.java
Externalize strings
[simantics/platform.git] / bundles / org.simantics.modeling.ui / src / org / simantics / modeling / ui / componentTypeEditor / ConfigurationPropertiesSection.java
index 1ad4cd5394c8b95146577bd6690c13402e5ea38c..34d26e4915e387ca0d04266be246ede57d1081cd 100644 (file)
-package org.simantics.modeling.ui.componentTypeEditor;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Collection;\r
-import java.util.Collections;\r
-import java.util.HashMap;\r
-import java.util.HashSet;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.Set;\r
-\r
-import org.eclipse.jface.dialogs.IDialogSettings;\r
-import org.eclipse.jface.layout.GridDataFactory;\r
-import org.eclipse.jface.layout.GridLayoutFactory;\r
-import org.eclipse.jface.layout.TableColumnLayout;\r
-import org.eclipse.jface.resource.ImageDescriptor;\r
-import org.eclipse.jface.viewers.ColumnWeightData;\r
-import org.eclipse.jface.window.Window;\r
-import org.eclipse.swt.SWT;\r
-import org.eclipse.swt.custom.TableEditor;\r
-import org.eclipse.swt.events.DisposeEvent;\r
-import org.eclipse.swt.events.DisposeListener;\r
-import org.eclipse.swt.events.MouseAdapter;\r
-import org.eclipse.swt.events.MouseEvent;\r
-import org.eclipse.swt.events.SelectionAdapter;\r
-import org.eclipse.swt.events.SelectionEvent;\r
-import org.eclipse.swt.graphics.Color;\r
-import org.eclipse.swt.graphics.Rectangle;\r
-import org.eclipse.swt.layout.FillLayout;\r
-import org.eclipse.swt.widgets.Button;\r
-import org.eclipse.swt.widgets.Composite;\r
-import org.eclipse.swt.widgets.Control;\r
-import org.eclipse.swt.widgets.Shell;\r
-import org.eclipse.swt.widgets.Table;\r
-import org.eclipse.swt.widgets.TableColumn;\r
-import org.eclipse.swt.widgets.TableItem;\r
-import org.eclipse.ui.PlatformUI;\r
-import org.eclipse.ui.forms.widgets.Form;\r
-import org.eclipse.ui.forms.widgets.FormToolkit;\r
-import org.eclipse.ui.forms.widgets.Section;\r
-import org.simantics.Logger;\r
-import org.simantics.Simantics;\r
-import org.simantics.databoard.Bindings;\r
-import org.simantics.db.ReadGraph;\r
-import org.simantics.db.Resource;\r
-import org.simantics.db.WriteGraph;\r
-import org.simantics.db.common.CommentMetadata;\r
-import org.simantics.db.common.request.ObjectsWithType;\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.CopyHandler2;\r
-import org.simantics.db.layer0.adapter.Instances;\r
-import org.simantics.db.layer0.util.Layer0Utils;\r
-import org.simantics.db.layer0.variable.Variable;\r
-import org.simantics.db.layer0.variable.Variables;\r
-import org.simantics.layer0.Layer0;\r
-import org.simantics.modeling.ui.Activator;\r
-import org.simantics.modeling.ui.componentTypeEditor.LiftPropertiesDialog.LiftedProperty;\r
-import org.simantics.modeling.userComponent.ComponentTypeCommands;\r
-import org.simantics.selectionview.SelectionViewResources;\r
-import org.simantics.structural.stubs.StructuralResource2;\r
-import org.simantics.utils.datastructures.Pair;\r
-\r
-public class ConfigurationPropertiesSection implements ComponentTypeViewerSection {\r
-\r
-    private static final String[] COLUMN_NAMES = \r
-            new String[] {"Name", "Type", "Default Value", "Unit", "Range", "Label", "Description"};\r
-    private static final int[] COLUMN_LENGTHS =\r
-            new int[] { 120, 100, 100, 50, 100, 100, 100 };\r
-    private static final int[] COLUMN_WEIGHTS =\r
-            new int[] { 0, 0, 0, 0, 0, 50, 100 };\r
-\r
-    /**\r
-     * Configuration property table column indexes that are to be considered\r
-     * immutable when the property relation is immutable. Note that relation\r
-     * immutability does not make the asserted default value immutable.\r
-     */\r
-    private static final int[] IMMUTABLE_COLUMNS_WITH_IMMUTABLE_RELATION =\r
-        { 0, 1, 3, 4, 5, 6 };\r
-    \r
-    ComponentTypeViewerData data;\r
-    Table table;\r
-    TableColumn[] columns;\r
-    TableEditor editor;\r
-    Button newProperty;\r
-    Button removeProperty;\r
-    Button liftProperties;\r
-\r
-    boolean hasTypes = false;\r
-    Button setTypes;\r
-    \r
-    Section section;\r
-    \r
-    public ConfigurationPropertiesSection(ComponentTypeViewerData data) {\r
-        this.data = data;\r
-        FormToolkit tk = data.tk;\r
-        Form form = data.form;\r
-        \r
-        section = tk.createSection(form.getBody(), Section.TITLE_BAR | Section.EXPANDED);\r
-        section.setLayout(new FillLayout());\r
-        section.setText("Configuration properties");\r
-\r
-        Composite sectionBody = tk.createComposite(section);\r
-        GridLayoutFactory.fillDefaults().numColumns(2).applyTo(sectionBody);\r
-        section.setClient(sectionBody);\r
-\r
-        Composite tableComposite = tk.createComposite(sectionBody);\r
-        GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).applyTo(tableComposite);\r
-        TableColumnLayout tcl = new TableColumnLayout();\r
-        tableComposite.setLayout(tcl);\r
-\r
-        table = tk.createTable(tableComposite, SWT.MULTI | SWT.FULL_SELECTION | SWT.BORDER);\r
-        table.setLinesVisible(true);\r
-        table.setHeaderVisible(true);\r
-\r
-        columns = new TableColumn[COLUMN_NAMES.length];\r
-        for(int i=0;i<COLUMN_NAMES.length;++i) {\r
-            TableColumn column = new TableColumn(table, SWT.NONE);\r
-            columns[i] = column;\r
-            tcl.setColumnData(column, new ColumnWeightData(COLUMN_WEIGHTS[i], COLUMN_LENGTHS[i], true));\r
-            column.setText(COLUMN_NAMES[i]);\r
-        }\r
-\r
-        // Table editor\r
-        editor = new TableEditor(table);\r
-        editor.grabHorizontal = true;\r
-        editor.grabVertical = true;\r
-        editor.horizontalAlignment = SWT.LEFT;\r
-        table.addMouseListener(new MouseAdapter() {\r
-            @Override\r
-            public void mouseDown(MouseEvent e) {\r
-                // Clean up any previous editor control\r
-                Control oldEditor = editor.getEditor();\r
-                if (oldEditor != null) oldEditor.dispose();\r
-\r
-                if (data.readOnly)\r
-                    return;\r
-\r
-                // Relative position\r
-                Rectangle tableBounds = table.getClientArea();\r
-                int rx = e.x - tableBounds.x;\r
-                int ry = e.y - tableBounds.y;\r
-\r
-                // Find cell\r
-                TableItem selectedItem = null;\r
-                int selectedColumn = -1;\r
-                Rectangle selectedItemBounds = null;\r
-                for(TableItem item : table.getItems()) {\r
-                    for(int column = 0;column < COLUMN_NAMES.length;++column) {\r
-                        Rectangle bounds = item.getBounds(column);\r
-                        if(bounds.contains(rx, ry)) {\r
-                            selectedItemBounds = bounds;\r
-                            selectedItem = item;\r
-                            selectedColumn = column;\r
-                            break;\r
-                        }\r
-                    }\r
-                }\r
-                if(selectedItem == null) {\r
-                    return;\r
-                }\r
-\r
-                // Table editor\r
-                final int column = selectedColumn; \r
-                final ComponentTypeViewerPropertyInfo propertyInfo = (ComponentTypeViewerPropertyInfo)selectedItem.getData();\r
-                final Resource resource = propertyInfo.resource;\r
-                switch (column) {\r
-                case 0:\r
-                    data.editName(table, editor, propertyInfo, selectedItem, column, ComponentTypeViewerData.PROPERTY_NAME_PATTERN);\r
-                    break;\r
-\r
-                case 1:\r
-                    data.editType(table, editor, propertyInfo, selectedItem, column, true);\r
-                    break;\r
-\r
-                case 2: {\r
-                    data.editValue(table, editor, propertyInfo, selectedItem, column, data.readOnly ? null : new StringWriter() {\r
-                        @Override\r
-                        public void perform(WriteGraph graph, String newValue) throws DatabaseException {\r
-                            graph.markUndoPoint();\r
-                            ComponentTypeCommands.setDefaultValue(graph, data.componentType, propertyInfo.resource, newValue);\r
-                        }\r
-                    }, null);\r
-                } break;\r
-\r
-                case 3:\r
-                    data.editUnit(table, editor, propertyInfo, selectedItem, column);\r
-                    break;\r
-\r
-                case 4:\r
-                    data.editRange(table, editor, propertyInfo, selectedItem, selectedItemBounds, column);\r
-                    break;\r
-\r
-                case 5:\r
-                    data.editValue(table, editor, propertyInfo, selectedItem, column, propertyInfo.immutable ? null : new StringWriter() {\r
-                        @Override\r
-                        public void perform(WriteGraph graph, String newValue) throws DatabaseException {\r
-                            graph.markUndoPoint();\r
-                            String value = newValue.isEmpty() ? null : newValue;\r
-                            ComponentTypeCommands.setLabel(graph, resource, value);\r
-                        }\r
-                    }, null);\r
-                    break;\r
-\r
-                case 6:\r
-                    data.editMultilineText(table, editor, propertyInfo, selectedItem, selectedItemBounds, column, new StringWriter() {\r
-                        @Override\r
-                        public void perform(WriteGraph graph, String newValue) throws DatabaseException {\r
-                            graph.markUndoPoint();\r
-                            String value = newValue.isEmpty() ? null : newValue;\r
-                            ComponentTypeCommands.setDescription(graph, resource, value);\r
-                        }\r
-                    });\r
-                    break;\r
-                }\r
-            }\r
-        });\r
-\r
-        // Buttons\r
-\r
-        Composite buttons = tk.createComposite(sectionBody);\r
-        GridDataFactory.fillDefaults().applyTo(buttons);\r
-        GridLayoutFactory.fillDefaults().applyTo(buttons);\r
-\r
-        newProperty = tk.createButton(buttons, "New property", SWT.PUSH);\r
-        GridDataFactory.fillDefaults().applyTo(newProperty);\r
-        removeProperty = tk.createButton(buttons, "Remove property", SWT.PUSH);\r
-        GridDataFactory.fillDefaults().applyTo(removeProperty);\r
-\r
-        liftProperties = tk.createButton(buttons, "Lift Properties", SWT.PUSH);\r
-        GridDataFactory.fillDefaults().applyTo(liftProperties);\r
-\r
-        hasTypes = !getTypes().isEmpty();\r
-        \r
-        if(hasTypes) {\r
-            setTypes = tk.createButton(buttons, "Assign Types", SWT.PUSH);\r
-            GridDataFactory.fillDefaults().applyTo(setTypes);\r
-        }\r
-\r
-        // Actions\r
-\r
-        table.addSelectionListener(new SelectionAdapter() {\r
-            @Override\r
-            public void widgetSelected(SelectionEvent e) {\r
-                TableItem[] sel = table.getSelection();\r
-                for (TableItem item : sel) {\r
-                    ComponentTypeViewerPropertyInfo pi = (ComponentTypeViewerPropertyInfo) item.getData();\r
-                    if (pi.immutable) {\r
-                        removeProperty.setEnabled(false);\r
-                        return;\r
-                    }\r
-                }\r
-                removeProperty.setEnabled(true);\r
-            }\r
-        });\r
-\r
-        newProperty.addSelectionListener(new SelectionAdapter() {\r
-            @Override\r
-            public void widgetSelected(SelectionEvent e) {\r
-                if(editor.getEditor() != null)\r
-                    editor.getEditor().dispose();\r
-                Simantics.getSession().async(new WriteRequest() {\r
-                    @Override\r
-                    public void perform(WriteGraph graph)\r
-                            throws DatabaseException {\r
-                        ComponentTypeCommands.createPropertyWithDefaults(graph, data.componentType);\r
-                    }\r
-                });\r
-            }\r
-        });\r
-\r
-        removeProperty.addSelectionListener(new SelectionAdapter() {\r
-            @Override\r
-            public void widgetSelected(SelectionEvent e) {\r
-                if(editor.getEditor() != null)\r
-                    editor.getEditor().dispose();\r
-                final List<Resource> propertiesToBeRemoved = \r
-                        new ArrayList<>();\r
-                for(TableItem item : table.getSelection()) {\r
-                    ComponentTypeViewerPropertyInfo info = (ComponentTypeViewerPropertyInfo) item.getData();\r
-                    if (!info.immutable)\r
-                        propertiesToBeRemoved.add(info.resource);\r
-                }\r
-                System.out.println("remove " + propertiesToBeRemoved.size() + " resources");\r
-                if(!propertiesToBeRemoved.isEmpty())\r
-                    Simantics.getSession().async(new WriteRequest() {\r
-                        @Override\r
-                        public void perform(WriteGraph graph)\r
-                                throws DatabaseException {\r
-                            graph.markUndoPoint();\r
-                            for(Resource property : propertiesToBeRemoved)\r
-                                ComponentTypeCommands.removeProperty(graph, data.componentType, property);\r
-                        }\r
-                    });\r
-            }\r
-        });\r
-        \r
-        liftProperties.addSelectionListener(new SelectionAdapter() {\r
-            @Override\r
-            public void widgetSelected(SelectionEvent e) {\r
-                \r
-                if(editor.getEditor() != null)\r
-                    editor.getEditor().dispose();\r
-\r
-                try {\r
-                \r
-                    Map<LiftedProperty, Pair<String, ImageDescriptor>> map = Simantics.sync(new UniqueRead<Map<LiftedProperty,Pair<String,ImageDescriptor>>>() {\r
-    \r
-                        @Override\r
-                        public Map<LiftedProperty, Pair<String, ImageDescriptor>> perform(ReadGraph graph) throws DatabaseException {\r
-                            \r
-                            Map<LiftedProperty, Pair<String,ImageDescriptor>> map = new HashMap<>(); \r
-                            \r
-                            Layer0 L0 = Layer0.getInstance(graph);\r
-                            StructuralResource2 STR = StructuralResource2.getInstance(graph);\r
-                            SelectionViewResources SEL = SelectionViewResources.getInstance(graph);\r
-    \r
-                            Resource composite = graph.getPossibleObject(data.componentType, STR.IsDefinedBy);\r
-                            if(composite == null) return map;\r
-\r
-\r
-                            Set<String> existing = new HashSet<>();\r
-                            for(Resource predicate : graph.getObjects(data.componentType, L0.DomainOf)) {\r
-                                if(graph.isSubrelationOf(predicate, L0.HasProperty)) {\r
-                                    existing.add(NameUtils.getSafeName(graph, predicate));\r
-                                }\r
-                            }\r
-                            \r
-                            for(Resource component : graph.syncRequest(new ObjectsWithType(composite, L0.ConsistsOf, STR.Component))) {\r
-    \r
-                                Resource type = graph.getPossibleType(component, STR.Component);\r
-                                if(type == null) continue;\r
-                                \r
-                                String componentName = NameUtils.getSafeName(graph, component);\r
-    \r
-                                for(Resource predicate : graph.getPredicates(component)) {\r
-                                    if(graph.isSubrelationOf(predicate, L0.HasProperty)) {\r
-    \r
-                                        // Do not list properties shown under other properties\r
-                                        if(graph.hasStatement(predicate, SEL.IsShownUnder)) continue;\r
-                                        \r
-                                        // Do not list properties that are not visible in selection view\r
-                                        if(!graph.hasStatement(predicate, SEL.HasStandardPropertyInfo)) continue;\r
-                                        \r
-                                        // Some properties are explicitly marked as non-liftable\r
-                                        Boolean canBeLifted = graph.getPossibleRelatedValue(predicate, SEL.canBeLifted, Bindings.BOOLEAN);\r
-                                        if(canBeLifted != null && !canBeLifted) continue;\r
-                                        \r
-                                        String predicateName = NameUtils.getSafeName(graph, predicate);\r
-                                        if(existing.contains(predicateName)) continue;\r
-                                        \r
-                                        String name = componentName + " " + predicateName;\r
-                                        map.put(new LiftedProperty(component, type, predicate), new Pair<String, ImageDescriptor>(name, null));\r
-                                        \r
-                                    }\r
-                                }\r
-                                \r
-                            }\r
-                            \r
-                            return map;\r
-                            \r
-                        }\r
-    \r
-                    });\r
-                    \r
-                    Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();\r
-                    LiftPropertiesDialog dialog = new LiftPropertiesDialog(shell, map, "Select properties to lift") {\r
-                        @Override\r
-                        protected IDialogSettings getBaseDialogSettings() {\r
-                            return Activator.getDefault().getDialogSettings();\r
-                        }\r
-                    };\r
-                    if (dialog.open() == Window.OK) {\r
-                        final Collection<LiftedProperty> _result = dialog.getResultT();\r
-                        final boolean mapProperties = dialog.getMapProperties();\r
-                        if (!_result.isEmpty()) {\r
-                            Simantics.getSession().async(new WriteRequest() {\r
-                                public Resource findAssertion(ReadGraph graph, Resource sourceType, Resource predicate) throws DatabaseException {\r
-                                    Collection<Resource> ass = graph.getAssertedObjects(sourceType, predicate);\r
-                                    if(ass.size() == 1) return ass.iterator().next();\r
-                                    return null;\r
-                                }\r
-                                \r
-                                public void processSubs(ReadGraph graph, Resource predicate, Resource component, Resource componentType, List<LiftedProperty> result, List<Resource> properties, List<Resource> assertions) throws DatabaseException {\r
-                                    SelectionViewResources SEL = SelectionViewResources.getInstance(graph);\r
-                                    for(Resource sub : graph.getObjects(predicate, SEL.UnderOf)) {\r
-                                        Resource ass = findAssertion(graph, componentType, sub);\r
-                                        if(ass == null) continue;\r
-                                        result.add(new LiftedProperty(component, componentType, sub));\r
-                                        properties.add(sub);\r
-                                        assertions.add(ass);\r
-                                        processSubs(graph, sub, component, componentType, result, properties, assertions);\r
-                                    }\r
-                                }\r
-                                \r
-                                @Override\r
-                                public void perform(WriteGraph graph) throws DatabaseException {\r
-                                    \r
-                                    Layer0 L0 = Layer0.getInstance(graph);\r
-                                    graph.markUndoPoint();\r
-                                    List<Resource> properties = new ArrayList<>();\r
-                                    List<Resource> assertions = new ArrayList<>();\r
-                                    \r
-                                    List<LiftedProperty> result = new ArrayList<>(); \r
-                                    for(LiftedProperty p : _result) {\r
-                                        Resource ass = findAssertion(graph, p.getComponentType(), p.getPredicate());\r
-                                        if(ass == null) continue;\r
-                                        result.add(p);\r
-                                        properties.add(p.getPredicate());\r
-                                        assertions.add(ass);\r
-                                        processSubs(graph, p.getPredicate(), p.getComponent(), p.getComponentType(), result, properties, assertions);\r
-                                    }\r
-                                    \r
-                                    CopyHandler2 ch = Layer0Utils.getPossibleCopyHandler(graph, properties);\r
-                                    Collection<Resource> copies = Layer0Utils.copyTo(graph, data.componentType, null, ch, null);\r
-                                    int index = 0;\r
-                                    for(Resource copy : copies) {\r
-                                        Resource ass = assertions.get(index);\r
-                                        LiftedProperty p = result.get(index);\r
-                                        Collection<Resource> copyAss = Layer0Utils.copyTo(graph, null, ass);\r
-                                        if(copyAss.size() == 1) {\r
-                                            graph.claim(data.componentType, L0.DomainOf, copy);\r
-                                            Layer0Utils.assert_(graph, data.componentType, copy, copyAss.iterator().next());\r
-                                            CommentMetadata cm = graph.getMetadata(CommentMetadata.class);\r
-                                            graph.addMetadata(cm.add("Lifted property " + NameUtils.getSafeName(graph, copy) + " into "+ NameUtils.getSafeName(graph, data.componentType)));\r
-                                        }\r
-                                        if(mapProperties) {\r
-                                            Variable v = Variables.getVariable(graph, p.getComponent());\r
-                                            Variable property = v.getProperty(graph, p.getPredicate());\r
-                                            Variable displayValue = property.getProperty(graph, Variables.DISPLAY_VALUE);\r
-                                            displayValue.setValue(graph, "=" + NameUtils.getSafeName(graph, p.getPredicate()), Bindings.STRING);\r
-                                        }\r
-                                        index++;\r
-                                    }\r
-                                    \r
-                                }\r
-                            });\r
-                        }\r
-                    }\r
-\r
-                } catch (DatabaseException e1) {\r
-                    \r
-                    Logger.defaultLogError(e1);\r
-                    return;\r
-                    \r
-                }\r
-                \r
-            }\r
-        });\r
-\r
-        if(hasTypes) {\r
-            \r
-            setTypes.addSelectionListener(new SelectionAdapter() {\r
-                @Override\r
-                public void widgetSelected(SelectionEvent e) {\r
-                    if(editor.getEditor() != null)\r
-                        editor.getEditor().dispose();\r
-                    final List<Resource> propertiesToSet = \r
-                            new ArrayList<>();\r
-                    for(TableItem item : table.getSelection())\r
-                        propertiesToSet.add(((ComponentTypeViewerPropertyInfo)item.getData()).resource);\r
-                    \r
-                    if(propertiesToSet.size() != 1) return;\r
-\r
-                    Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();\r
-                    SetTypesDialog page = new SetTypesDialog(shell, getTypes(), "Select user types for property");\r
-                    if (page.open() == Window.OK) {\r
-                        final Object[] result = page.getResult();\r
-                        if (result != null && result.length > 0) {\r
-                            Simantics.getSession().async(new WriteRequest() {\r
-                                @Override\r
-                                public void perform(WriteGraph graph)\r
-                                        throws DatabaseException {\r
-                                    for(Object type : result) {\r
-                                        Layer0 L0 = Layer0.getInstance(graph);\r
-                                        graph.claim(propertiesToSet.get(0), L0.InstanceOf, null, (Resource)type);\r
-                                    }\r
-                                }\r
-                            });\r
-                        }\r
-                    }\r
-                    \r
-                }\r
-            });\r
-            \r
-        }\r
-\r
-        table.addDisposeListener(new DisposeListener() {\r
-            @Override\r
-            public void widgetDisposed(DisposeEvent e) {\r
-                tk.dispose();\r
-            }\r
-        });\r
-    }\r
-\r
-    public void update(ComponentTypePropertiesResult result) {\r
-        if (table.isDisposed())\r
-            return;\r
-        \r
-        // Save old selection\r
-        Set<ComponentTypeViewerPropertyInfo> selected = new HashSet<>();\r
-        List<TableItem> selectedItems = new ArrayList<>(selected.size());\r
-        for (int i : table.getSelectionIndices()) {\r
-            TableItem item = table.getItem(i);\r
-            selected.add((ComponentTypeViewerPropertyInfo) item.getData());\r
-        }\r
-\r
-        int topIndex = table.getTopIndex();\r
-\r
-        table.removeAll();\r
-\r
-        if(editor.getEditor() != null)\r
-            editor.getEditor().dispose();\r
-\r
-        for(ComponentTypeViewerPropertyInfo info : result.getProperties()) {\r
-            boolean immutable = result.isImmutable() || info.immutable;\r
-            Color fg = immutable ? table.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY) : null;\r
-            if(info.sectionSpecificData != null)\r
-                continue;\r
-\r
-            TableItem item = new TableItem(table, SWT.NONE);\r
-\r
-            item.setText(0, info.name);\r
-            item.setText(1, info.type);\r
-            item.setText(2, info.defaultValue);\r
-            item.setText(3, unitStr(info));\r
-            item.setText(4, rangeStr(info));\r
-            item.setText(5, info.label);\r
-            item.setText(6, info.description);\r
-\r
-            for (int columnIndex : IMMUTABLE_COLUMNS_WITH_IMMUTABLE_RELATION)\r
-                item.setForeground(columnIndex, fg);\r
-\r
-            item.setData(info);\r
-\r
-            if (selected.contains(info))\r
-                selectedItems.add(item);\r
-        }\r
-\r
-        // Restore old selection\r
-        table.setTopIndex(topIndex);\r
-        table.setSelection(selectedItems.toArray(new TableItem[selectedItems.size()]));\r
-        table.redraw();\r
-    }\r
-    \r
-    private Map<Resource, Pair<String, ImageDescriptor>> getTypes() {\r
-        try {\r
-            return Simantics.getSession().syncRequest(new UniqueRead<Map<Resource, Pair<String, ImageDescriptor>>>() {\r
-                @Override\r
-                public Map<Resource, Pair<String, ImageDescriptor>> perform(ReadGraph graph)\r
-                        throws DatabaseException {\r
-                    StructuralResource2 STR = StructuralResource2.getInstance(graph);\r
-                    Resource indexRoot = graph.syncRequest(new PossibleIndexRoot(data.componentType));\r
-                    Instances query = graph.adapt(STR.UserDefinedProperty, Instances.class);\r
-                    Collection<Resource> types = query.find(graph, indexRoot);\r
-                    Map<Resource, Pair<String, ImageDescriptor>> result = new HashMap<>();\r
-                    for(Resource type : types) {\r
-                        String name = NameUtils.getSafeLabel(graph, type);\r
-                        result.put(type, new Pair<String, ImageDescriptor>(name, null));\r
-                    }\r
-                    return result;\r
-                }\r
-            });\r
-        } catch (DatabaseException e) {\r
-            Logger.defaultLogError(e);\r
-            return Collections.emptyMap();\r
-        }\r
-    }\r
-\r
-    private String unitStr(ComponentTypeViewerPropertyInfo info) {\r
-        String unit = info.numberType == null ? null : info.numberType.getUnit();\r
-        if (unit == null)\r
-            unit = info.unit;\r
-        return unit != null ? unit : "";\r
-    }\r
-\r
-    private String rangeStr(ComponentTypeViewerPropertyInfo info) {\r
-        String range = info.numberType == null ? null : info.numberType.getRangeStr();\r
-        return range != null ? range : "";\r
-    }\r
-    \r
-    @Override\r
-    public void setReadOnly(boolean readOnly) {\r
-        boolean e = !readOnly;\r
-        newProperty.setEnabled(e);\r
-        removeProperty.setEnabled(e);\r
-        liftProperties.setEnabled(e);\r
-    }\r
-    \r
-    @Override\r
-    public Section getSection() {\r
-        return section;\r
-    }\r
-\r
-    @Override\r
-    public double getPriority() {\r
-        return 0;\r
-    }\r
-\r
-    @Override\r
-    public Object getSectionSpecificData(ReadGraph graph,\r
-            ComponentTypeViewerPropertyInfo info) throws DatabaseException {\r
-        return null;\r
-    }\r
-\r
-    @Override\r
-    public double getDataPriority() {\r
-        return 0.0;\r
-    }\r
-}\r
+package org.simantics.modeling.ui.componentTypeEditor;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.jface.layout.TableColumnLayout;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.ColumnWeightData;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.TableEditor;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.forms.widgets.Form;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Section;
+import org.simantics.Simantics;
+import org.simantics.databoard.Bindings;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.common.CommentMetadata;
+import org.simantics.db.common.request.ObjectsWithType;
+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.CopyHandler2;
+import org.simantics.db.layer0.adapter.Instances;
+import org.simantics.db.layer0.util.Layer0Utils;
+import org.simantics.db.layer0.variable.Variable;
+import org.simantics.db.layer0.variable.Variables;
+import org.simantics.layer0.Layer0;
+import org.simantics.modeling.ui.Activator;
+import org.simantics.modeling.ui.componentTypeEditor.LiftPropertiesDialog.LiftedProperty;
+import org.simantics.modeling.userComponent.ComponentTypeCommands;
+import org.simantics.selectionview.SelectionViewResources;
+import org.simantics.structural.stubs.StructuralResource2;
+import org.simantics.utils.datastructures.Pair;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ConfigurationPropertiesSection implements ComponentTypeViewerSection {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(ConfigurationPropertiesSection.class);
+
+    private static final String[] COLUMN_NAMES = {
+            Messages.ConfigurationPropertiesSection_Name,
+            Messages.ConfigurationPropertiesSection_Type,
+            Messages.ConfigurationPropertiesSection_DefaultValue,
+            Messages.ConfigurationPropertiesSection_Unit,
+            Messages.ConfigurationPropertiesSection_Range,
+            Messages.ConfigurationPropertiesSection_Label,
+            Messages.ConfigurationPropertiesSection_Description
+    };
+    private static final int[] COLUMN_LENGTHS =
+            new int[] { 120, 100, 100, 50, 100, 100, 100 };
+    private static final int[] COLUMN_WEIGHTS =
+            new int[] { 0, 0, 0, 0, 0, 50, 100 };
+
+    /**
+     * Configuration property table column indexes that are to be considered
+     * immutable when the property relation is immutable. Note that relation
+     * immutability does not make the asserted default value immutable.
+     */
+    private static final int[] IMMUTABLE_COLUMNS_WITH_IMMUTABLE_RELATION =
+        { 0, 1, 3, 4, 5, 6 };
+    ComponentTypeViewerData data;
+    
+    Table table;
+    TableColumn[] columns;
+    TableEditor editor;
+    Button newProperty;
+    Button removeProperty;
+    Button liftProperties;
+
+    boolean hasTypes = false;
+    Button setTypes;
+    
+    Section section;
+    
+    public ConfigurationPropertiesSection(ComponentTypeViewerData data) {
+        this.data = data;
+        FormToolkit tk = data.tk;
+        Form form = data.form;
+        
+        section = tk.createSection(form.getBody(), Section.TITLE_BAR | Section.EXPANDED);
+        section.setLayout(new FillLayout());
+        section.setText(Messages.ConfigurationPropertiesSection_ConfigurationProperties);
+
+        Composite sectionBody = tk.createComposite(section);
+        GridLayoutFactory.fillDefaults().numColumns(2).applyTo(sectionBody);
+        section.setClient(sectionBody);
+
+        Composite tableComposite = tk.createComposite(sectionBody);
+        GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).applyTo(tableComposite);
+        TableColumnLayout tcl = new TableColumnLayout();
+        tableComposite.setLayout(tcl);
+
+        table = tk.createTable(tableComposite, SWT.MULTI | SWT.FULL_SELECTION | SWT.BORDER);
+        table.setLinesVisible(true);
+        table.setHeaderVisible(true);
+
+        columns = new TableColumn[COLUMN_NAMES.length];
+        for(int i=0;i<COLUMN_NAMES.length;++i) {
+            TableColumn column = new TableColumn(table, SWT.NONE);
+            columns[i] = column;
+            tcl.setColumnData(column, new ColumnWeightData(COLUMN_WEIGHTS[i], COLUMN_LENGTHS[i], true));
+            column.setText(COLUMN_NAMES[i]);
+        }
+
+        // Table editor
+        editor = new TableEditor(table);
+        editor.grabHorizontal = true;
+        editor.grabVertical = true;
+        editor.horizontalAlignment = SWT.LEFT;
+        table.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mouseDown(MouseEvent e) {
+                // Clean up any previous editor control
+                Control oldEditor = editor.getEditor();
+                if (oldEditor != null) oldEditor.dispose();
+
+                if (data.readOnly)
+                    return;
+
+                // Relative position
+                Rectangle tableBounds = table.getClientArea();
+                int rx = e.x - tableBounds.x;
+                int ry = e.y - tableBounds.y;
+
+                // Find cell
+                TableItem selectedItem = null;
+                int selectedColumn = -1;
+                Rectangle selectedItemBounds = null;
+                for(TableItem item : table.getItems()) {
+                    for(int column = 0;column < COLUMN_NAMES.length;++column) {
+                        Rectangle bounds = item.getBounds(column);
+                        if(bounds.contains(rx, ry)) {
+                            selectedItemBounds = bounds;
+                            selectedItem = item;
+                            selectedColumn = column;
+                            break;
+                        }
+                    }
+                }
+                if(selectedItem == null) {
+                    return;
+                }
+
+                // Table editor
+                final int column = selectedColumn; 
+                final ComponentTypeViewerPropertyInfo propertyInfo = (ComponentTypeViewerPropertyInfo)selectedItem.getData();
+                final Resource resource = propertyInfo.resource;
+                switch (column) {
+                case 0:
+                    data.editName(table, editor, propertyInfo, selectedItem, column, ComponentTypeViewerData.PROPERTY_NAME_PATTERN);
+                    break;
+
+                case 1:
+                    data.editType(table, editor, propertyInfo, selectedItem, column, true);
+                    break;
+
+                case 2: {
+                    data.editValue(table, editor, propertyInfo, selectedItem, column, data.readOnly ? null : new StringWriter() {
+                        @Override
+                        public void perform(WriteGraph graph, String newValue) throws DatabaseException {
+                            graph.markUndoPoint();
+                            ComponentTypeCommands.setDefaultValue(graph, data.componentType, propertyInfo.resource, newValue);
+                        }
+                    }, null);
+                } break;
+
+                case 3:
+                    data.editUnit(table, editor, propertyInfo, selectedItem, column);
+                    break;
+
+                case 4:
+                    data.editRange(table, editor, propertyInfo, selectedItem, selectedItemBounds, column);
+                    break;
+
+                case 5:
+                    data.editValue(table, editor, propertyInfo, selectedItem, column, propertyInfo.immutable ? null : new StringWriter() {
+                        @Override
+                        public void perform(WriteGraph graph, String newValue) throws DatabaseException {
+                            graph.markUndoPoint();
+                            String value = newValue.isEmpty() ? null : newValue;
+                            ComponentTypeCommands.setLabel(graph, resource, value);
+                        }
+                    }, null);
+                    break;
+
+                case 6:
+                    data.editMultilineText(table, editor, propertyInfo, selectedItem, selectedItemBounds, column, new StringWriter() {
+                        @Override
+                        public void perform(WriteGraph graph, String newValue) throws DatabaseException {
+                            graph.markUndoPoint();
+                            String value = newValue.isEmpty() ? null : newValue;
+                            ComponentTypeCommands.setDescription(graph, resource, value);
+                        }
+                    });
+                    break;
+                }
+            }
+        });
+
+        // Buttons
+
+        Composite buttons = tk.createComposite(sectionBody);
+        GridDataFactory.fillDefaults().applyTo(buttons);
+        GridLayoutFactory.fillDefaults().applyTo(buttons);
+
+        newProperty = tk.createButton(buttons, Messages.ConfigurationPropertiesSection_NewProperty, SWT.PUSH);
+        GridDataFactory.fillDefaults().applyTo(newProperty);
+        removeProperty = tk.createButton(buttons, Messages.ConfigurationPropertiesSection_RemoveProperty, SWT.PUSH);
+        GridDataFactory.fillDefaults().applyTo(removeProperty);
+
+        liftProperties = tk.createButton(buttons, Messages.ConfigurationPropertiesSection_LiftProperties, SWT.PUSH);
+        GridDataFactory.fillDefaults().applyTo(liftProperties);
+
+        hasTypes = !getTypes().isEmpty();
+        
+        if(hasTypes) {
+            setTypes = tk.createButton(buttons, Messages.ConfigurationPropertiesSection_AssignTypes, SWT.PUSH);
+            GridDataFactory.fillDefaults().applyTo(setTypes);
+        }
+
+        // Actions
+
+        table.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                TableItem[] sel = table.getSelection();
+                for (TableItem item : sel) {
+                    ComponentTypeViewerPropertyInfo pi = (ComponentTypeViewerPropertyInfo) item.getData();
+                    if (pi.immutable) {
+                        removeProperty.setEnabled(false);
+                        return;
+                    }
+                }
+                removeProperty.setEnabled(true);
+            }
+        });
+
+        newProperty.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                if(editor.getEditor() != null)
+                    editor.getEditor().dispose();
+                Simantics.getSession().async(new WriteRequest() {
+                    @Override
+                    public void perform(WriteGraph graph)
+                            throws DatabaseException {
+                        ComponentTypeCommands.createPropertyWithDefaults(graph, data.componentType);
+                    }
+                });
+            }
+        });
+
+        removeProperty.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                if(editor.getEditor() != null)
+                    editor.getEditor().dispose();
+                final List<Resource> propertiesToBeRemoved = 
+                        new ArrayList<>();
+                for(TableItem item : table.getSelection()) {
+                    ComponentTypeViewerPropertyInfo info = (ComponentTypeViewerPropertyInfo) item.getData();
+                    if (!info.immutable)
+                        propertiesToBeRemoved.add(info.resource);
+                }
+                //System.out.println("remove " + propertiesToBeRemoved.size() + " resources"); //$NON-NLS-1$ //$NON-NLS-2$
+                if(!propertiesToBeRemoved.isEmpty())
+                    Simantics.getSession().async(new WriteRequest() {
+                        @Override
+                        public void perform(WriteGraph graph)
+                                throws DatabaseException {
+                            graph.markUndoPoint();
+                            for(Resource property : propertiesToBeRemoved)
+                                ComponentTypeCommands.removeProperty(graph, data.componentType, property);
+                        }
+                    });
+            }
+        });
+        
+        liftProperties.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                
+                if(editor.getEditor() != null)
+                    editor.getEditor().dispose();
+
+                try {
+                
+                    Map<LiftedProperty, Pair<String, ImageDescriptor>> map = Simantics.sync(new UniqueRead<Map<LiftedProperty,Pair<String,ImageDescriptor>>>() {
+    
+                        @Override
+                        public Map<LiftedProperty, Pair<String, ImageDescriptor>> perform(ReadGraph graph) throws DatabaseException {
+                            
+                            Map<LiftedProperty, Pair<String,ImageDescriptor>> map = new HashMap<>(); 
+                            
+                            Layer0 L0 = Layer0.getInstance(graph);
+                            StructuralResource2 STR = StructuralResource2.getInstance(graph);
+                            SelectionViewResources SEL = SelectionViewResources.getInstance(graph);
+    
+                            Resource composite = graph.getPossibleObject(data.componentType, STR.IsDefinedBy);
+                            if(composite == null) return map;
+
+
+                            Set<String> existing = new HashSet<>();
+                            for(Resource predicate : graph.getObjects(data.componentType, L0.DomainOf)) {
+                                if(graph.isSubrelationOf(predicate, L0.HasProperty)) {
+                                    existing.add(NameUtils.getSafeName(graph, predicate));
+                                }
+                            }
+                            
+                            for(Resource component : graph.syncRequest(new ObjectsWithType(composite, L0.ConsistsOf, STR.Component))) {
+    
+                                Resource type = graph.getPossibleType(component, STR.Component);
+                                if(type == null) continue;
+                                
+                                String componentName = NameUtils.getSafeName(graph, component);
+    
+                                for(Resource predicate : graph.getPredicates(component)) {
+                                    if(graph.isSubrelationOf(predicate, L0.HasProperty)) {
+    
+                                        // Do not list properties shown under other properties
+                                        if(graph.hasStatement(predicate, SEL.IsShownUnder)) continue;
+                                        
+                                        // Do not list properties that are not visible in selection view
+                                        if(!graph.hasStatement(predicate, SEL.HasStandardPropertyInfo)) continue;
+                                        
+                                        // Some properties are explicitly marked as non-liftable
+                                        Boolean canBeLifted = graph.getPossibleRelatedValue(predicate, SEL.canBeLifted, Bindings.BOOLEAN);
+                                        if(canBeLifted != null && !canBeLifted) continue;
+                                        
+                                        String predicateName = NameUtils.getSafeName(graph, predicate);
+                                        if(existing.contains(predicateName)) continue;
+                                        
+                                        String name = componentName + " " + predicateName; //$NON-NLS-1$
+                                        map.put(new LiftedProperty(component, type, predicate), new Pair<String, ImageDescriptor>(name, null));
+                                        
+                                    }
+                                }
+                                
+                            }
+                            
+                            return map;
+                            
+                        }
+    
+                    });
+                    
+                    Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+                    LiftPropertiesDialog dialog = new LiftPropertiesDialog(shell, map, Messages.ConfigurationPropertiesSection_SelectPropertiesToLift) {
+                        @Override
+                        protected IDialogSettings getBaseDialogSettings() {
+                            return Activator.getDefault().getDialogSettings();
+                        }
+                    };
+                    if (dialog.open() == Window.OK) {
+                        final Collection<LiftedProperty> _result = dialog.getResultT();
+                        final boolean mapProperties = dialog.getMapProperties();
+                        if (!_result.isEmpty()) {
+                            Simantics.getSession().async(new WriteRequest() {
+                                public Resource findAssertion(ReadGraph graph, Resource sourceType, Resource predicate) throws DatabaseException {
+                                    Collection<Resource> ass = graph.getAssertedObjects(sourceType, predicate);
+                                    if(ass.size() == 1) return ass.iterator().next();
+                                    return null;
+                                }
+                                
+                                public void processSubs(ReadGraph graph, Resource predicate, Resource component, Resource componentType, List<LiftedProperty> result, List<Resource> properties, List<Resource> assertions) throws DatabaseException {
+                                    SelectionViewResources SEL = SelectionViewResources.getInstance(graph);
+                                    for(Resource sub : graph.getObjects(predicate, SEL.UnderOf)) {
+                                        Resource ass = findAssertion(graph, componentType, sub);
+                                        if(ass == null) continue;
+                                        result.add(new LiftedProperty(component, componentType, sub));
+                                        properties.add(sub);
+                                        assertions.add(ass);
+                                        processSubs(graph, sub, component, componentType, result, properties, assertions);
+                                    }
+                                }
+                                
+                                @Override
+                                public void perform(WriteGraph graph) throws DatabaseException {
+                                    
+                                    Layer0 L0 = Layer0.getInstance(graph);
+                                    graph.markUndoPoint();
+                                    List<Resource> properties = new ArrayList<>();
+                                    List<Resource> assertions = new ArrayList<>();
+                                    
+                                    List<LiftedProperty> result = new ArrayList<>(); 
+                                    for(LiftedProperty p : _result) {
+                                        Resource ass = findAssertion(graph, p.getComponentType(), p.getPredicate());
+                                        if(ass == null) continue;
+                                        result.add(p);
+                                        properties.add(p.getPredicate());
+                                        assertions.add(ass);
+                                        processSubs(graph, p.getPredicate(), p.getComponent(), p.getComponentType(), result, properties, assertions);
+                                    }
+                                    
+                                    CopyHandler2 ch = Layer0Utils.getPossibleCopyHandler(graph, properties);
+                                    Collection<Resource> copies = Layer0Utils.copyTo(graph, data.componentType, null, ch, null);
+                                    int index = 0;
+                                    for(Resource copy : copies) {
+                                        Resource ass = assertions.get(index);
+                                        LiftedProperty p = result.get(index);
+                                        Collection<Resource> copyAss = Layer0Utils.copyTo(graph, null, ass);
+                                        if(copyAss.size() == 1) {
+                                            graph.deny(copy, L0.HasDomain);
+                                            graph.claim(data.componentType, L0.DomainOf, copy);
+                                            Layer0Utils.assert_(graph, data.componentType, copy, copyAss.iterator().next());
+                                            CommentMetadata cm = graph.getMetadata(CommentMetadata.class);
+                                            graph.addMetadata(cm.add("Lifted property " + NameUtils.getSafeName(graph, copy) + " into "+ NameUtils.getSafeName(graph, data.componentType))); //$NON-NLS-1$ //$NON-NLS-2$
+                                        }
+                                        if(mapProperties) {
+                                            Variable v = Variables.getVariable(graph, p.getComponent());
+                                            Variable property = v.getProperty(graph, p.getPredicate());
+                                            Variable displayValue = property.getProperty(graph, Variables.DISPLAY_VALUE);
+                                            displayValue.setValue(graph, "=" + NameUtils.getSafeName(graph, p.getPredicate()), Bindings.STRING); //$NON-NLS-1$
+                                        }
+                                        index++;
+                                    }
+                                    
+                                }
+                            });
+                        }
+                    }
+
+                } catch (DatabaseException e1) {
+                    
+                    LOGGER.error("Lifting properties failed", e1); //$NON-NLS-1$
+                    return;
+                    
+                }
+                
+            }
+        });
+
+        if(hasTypes) {
+            
+            setTypes.addSelectionListener(new SelectionAdapter() {
+                @Override
+                public void widgetSelected(SelectionEvent e) {
+                    if(editor.getEditor() != null)
+                        editor.getEditor().dispose();
+                    final List<Resource> propertiesToSet = 
+                            new ArrayList<>();
+                    for(TableItem item : table.getSelection())
+                        propertiesToSet.add(((ComponentTypeViewerPropertyInfo)item.getData()).resource);
+                    
+                    if(propertiesToSet.size() != 1) return;
+
+                    Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
+                    SetTypesDialog page = new SetTypesDialog(shell, getTypes(), Messages.ConfigurationPropertiesSection_SelectUserTypesForProp);
+                    if (page.open() == Window.OK) {
+                        final Object[] result = page.getResult();
+                        if (result != null && result.length > 0) {
+                            Simantics.getSession().async(new WriteRequest() {
+                                @Override
+                                public void perform(WriteGraph graph)
+                                        throws DatabaseException {
+                                    for(Object type : result) {
+                                        Layer0 L0 = Layer0.getInstance(graph);
+                                        graph.claim(propertiesToSet.get(0), L0.InstanceOf, null, (Resource)type);
+                                    }
+                                }
+                            });
+                        }
+                    }
+                    
+                }
+            });
+            
+        }
+
+        table.addDisposeListener(new DisposeListener() {
+            @Override
+            public void widgetDisposed(DisposeEvent e) {
+                tk.dispose();
+            }
+        });
+    }
+
+    public void update(ComponentTypePropertiesResult result) {
+        if (table.isDisposed())
+            return;
+        
+        // Save old selection
+        Set<ComponentTypeViewerPropertyInfo> selected = new HashSet<>();
+        List<TableItem> selectedItems = new ArrayList<>(selected.size());
+        for (int i : table.getSelectionIndices()) {
+            TableItem item = table.getItem(i);
+            selected.add((ComponentTypeViewerPropertyInfo) item.getData());
+        }
+
+        int topIndex = table.getTopIndex();
+
+        table.removeAll();
+
+        if(editor.getEditor() != null)
+            editor.getEditor().dispose();
+
+        for(ComponentTypeViewerPropertyInfo info : result.getProperties()) {
+            boolean immutable = result.isImmutable() || info.immutable;
+            Color fg = immutable ? table.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY) : null;
+            if(info.sectionSpecificData != null)
+                continue;
+
+            TableItem item = new TableItem(table, SWT.NONE);
+
+            item.setText(0, info.name);
+            item.setText(1, info.type);
+            item.setText(2, info.defaultValue);
+            item.setText(3, info.unitString());
+            item.setText(4, info.rangeString());
+            item.setText(5, info.label);
+            item.setText(6, info.description);
+
+            for (int columnIndex : IMMUTABLE_COLUMNS_WITH_IMMUTABLE_RELATION)
+                item.setForeground(columnIndex, fg);
+
+            item.setData(info);
+
+            if (selected.contains(info))
+                selectedItems.add(item);
+        }
+
+        // Restore old selection
+        table.setTopIndex(topIndex);
+        table.setSelection(selectedItems.toArray(new TableItem[selectedItems.size()]));
+        table.redraw();
+    }
+    
+    private Map<Resource, Pair<String, ImageDescriptor>> getTypes() {
+        try {
+            return Simantics.getSession().syncRequest(new UniqueRead<Map<Resource, Pair<String, ImageDescriptor>>>() {
+                @Override
+                public Map<Resource, Pair<String, ImageDescriptor>> perform(ReadGraph graph)
+                        throws DatabaseException {
+                    StructuralResource2 STR = StructuralResource2.getInstance(graph);
+                    Resource indexRoot = graph.syncRequest(new PossibleIndexRoot(data.componentType));
+                    Instances query = graph.adapt(STR.UserDefinedProperty, Instances.class);
+                    Collection<Resource> types = query.find(graph, indexRoot);
+                    Map<Resource, Pair<String, ImageDescriptor>> result = new HashMap<>();
+                    for(Resource type : types) {
+                        String name = NameUtils.getSafeLabel(graph, type);
+                        result.put(type, new Pair<String, ImageDescriptor>(name, null));
+                    }
+                    return result;
+                }
+            });
+        } catch (DatabaseException e) {
+            LOGGER.error("Finding UserDefinedProperties failed.", e); //$NON-NLS-1$
+            return Collections.emptyMap();
+        }
+    }
+
+    @Override
+    public void setReadOnly(boolean readOnly) {
+        boolean e = !readOnly;
+        newProperty.setEnabled(e);
+        removeProperty.setEnabled(e);
+        liftProperties.setEnabled(e);
+    }
+    
+    @Override
+    public Section getSection() {
+        return section;
+    }
+
+    @Override
+    public double getPriority() {
+        return 0;
+    }
+
+    @Override
+    public Object getSectionSpecificData(ReadGraph graph,
+            ComponentTypeViewerPropertyInfo info) throws DatabaseException {
+        return null;
+    }
+
+    @Override
+    public double getDataPriority() {
+        return 0.0;
+    }
+}