]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.scenegraph.ui/src/org/simantics/scenegraph/ui/SceneGraphViewPart.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.scenegraph.ui / src / org / simantics / scenegraph / ui / SceneGraphViewPart.java
diff --git a/bundles/org.simantics.scenegraph.ui/src/org/simantics/scenegraph/ui/SceneGraphViewPart.java b/bundles/org.simantics.scenegraph.ui/src/org/simantics/scenegraph/ui/SceneGraphViewPart.java
new file mode 100644 (file)
index 0000000..d6e9539
--- /dev/null
@@ -0,0 +1,549 @@
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2010 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.scenegraph.ui;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.PrintStream;\r
+import java.lang.ref.WeakReference;\r
+import java.util.List;\r
+\r
+import org.eclipse.core.commands.Command;\r
+import org.eclipse.core.commands.State;\r
+import org.eclipse.jface.layout.TreeColumnLayout;\r
+import org.eclipse.jface.resource.ImageDescriptor;\r
+import org.eclipse.jface.resource.JFaceResources;\r
+import org.eclipse.jface.resource.LocalResourceManager;\r
+import org.eclipse.jface.viewers.ColumnLabelProvider;\r
+import org.eclipse.jface.viewers.ColumnWeightData;\r
+import org.eclipse.jface.viewers.DoubleClickEvent;\r
+import org.eclipse.jface.viewers.IDoubleClickListener;\r
+import org.eclipse.jface.viewers.ISelectionChangedListener;\r
+import org.eclipse.jface.viewers.IStructuredSelection;\r
+import org.eclipse.jface.viewers.ITreeContentProvider;\r
+import org.eclipse.jface.viewers.SelectionChangedEvent;\r
+import org.eclipse.jface.viewers.TreePath;\r
+import org.eclipse.jface.viewers.TreeViewer;\r
+import org.eclipse.jface.viewers.TreeViewerColumn;\r
+import org.eclipse.jface.viewers.Viewer;\r
+import org.eclipse.jface.viewers.ViewerCell;\r
+import org.eclipse.swt.SWT;\r
+import org.eclipse.swt.dnd.Clipboard;\r
+import org.eclipse.swt.dnd.TextTransfer;\r
+import org.eclipse.swt.dnd.Transfer;\r
+import org.eclipse.swt.graphics.Image;\r
+import org.eclipse.swt.widgets.Composite;\r
+import org.eclipse.swt.widgets.Shell;\r
+import org.eclipse.ui.IPartListener2;\r
+import org.eclipse.ui.IPartService;\r
+import org.eclipse.ui.IWorkbenchPage;\r
+import org.eclipse.ui.IWorkbenchPart;\r
+import org.eclipse.ui.IWorkbenchPartReference;\r
+import org.eclipse.ui.IWorkbenchWindow;\r
+import org.eclipse.ui.PlatformUI;\r
+import org.eclipse.ui.commands.ICommandService;\r
+import org.eclipse.ui.contexts.IContextActivation;\r
+import org.eclipse.ui.contexts.IContextService;\r
+import org.eclipse.ui.part.ViewPart;\r
+import org.simantics.scenegraph.ILookupService;\r
+import org.simantics.scenegraph.INode;\r
+import org.simantics.scenegraph.g2d.G2DParentNode;\r
+import org.simantics.scenegraph.g2d.G2DSceneGraph;\r
+import org.simantics.scenegraph.g2d.IG2DNode;\r
+import org.simantics.scenegraph.g2d.nodes.BoundsNode;\r
+import org.simantics.scenegraph.g2d.nodes.BranchPointNode;\r
+import org.simantics.scenegraph.g2d.nodes.EdgeNode;\r
+import org.simantics.scenegraph.g2d.nodes.GridNode;\r
+import org.simantics.scenegraph.g2d.nodes.LinkNode;\r
+import org.simantics.scenegraph.g2d.nodes.NavigationNode;\r
+import org.simantics.scenegraph.g2d.nodes.PageBorderNode;\r
+import org.simantics.scenegraph.g2d.nodes.RulerNode;\r
+import org.simantics.scenegraph.g2d.nodes.SVGNode;\r
+import org.simantics.scenegraph.g2d.nodes.ShapeNode;\r
+import org.simantics.scenegraph.g2d.nodes.SingleElementNode;\r
+import org.simantics.scenegraph.g2d.nodes.TransformNode;\r
+import org.simantics.scenegraph.utils.NodeUtil;\r
+import org.simantics.scenegraph.utils.NodeUtil.NodeProcedure;\r
+\r
+/**\r
+ * This view shows the contents of a 2D/3D canvas scenegraph through a tree\r
+ * viewer.\r
+ * \r
+ * <p>\r
+ * The viewer sources its scene graph input from the currently active workbench\r
+ * editor. It does not automatically track the active editor part but instead\r
+ * has to be refreshed manually (F5).\r
+ * </p>\r
+ * \r
+ * <p>\r
+ * References to actual scene graph nodes are only kept as {@link WeakReference}\r
+ * instances allowing them to be garbage collected if the scene graph is\r
+ * disposed of.\r
+ * </p>\r
+ * \r
+ * @author Tuukka Lehtonen\r
+ */\r
+public class SceneGraphViewPart extends ViewPart {\r
+\r
+    TreeViewer           tree;\r
+    LocalResourceManager resourceManager;\r
+    boolean              bootstrapped = false;\r
+    IContextActivation   contextActivation;\r
+    int                  currentNodeCount = 0;\r
+    boolean              linkToPart;\r
+    IWorkbenchPart       lastPart;\r
+    AttributeDialog      attributeDialog;\r
+\r
+    final ImageDescriptor ROOT = ImageDescriptor.createFromURL(getClass().getResource("bullet_home.png"));\r
+    final ImageDescriptor CANVAS_BOUNDS = ImageDescriptor.createFromURL(getClass().getResource("application.png"));\r
+    final ImageDescriptor SHAPE = ImageDescriptor.createFromURL(getClass().getResource("shape_shadow.png"));\r
+    final ImageDescriptor NAVIGATION = ImageDescriptor.createFromURL(getClass().getResource("arrow_out_longer.png"));\r
+    final ImageDescriptor SVG = ImageDescriptor.createFromURL(getClass().getResource("script_code.png"));\r
+    final ImageDescriptor TRANSFORM = ImageDescriptor.createFromURL(getClass().getResource("arrow_nsew.png"));\r
+    final ImageDescriptor ELEMENT = ImageDescriptor.createFromURL(getClass().getResource("shape_handles.png"));\r
+    final ImageDescriptor PARENT = ImageDescriptor.createFromURL(getClass().getResource("share.png"));\r
+    final ImageDescriptor GRID = ImageDescriptor.createFromURL(getClass().getResource("border_all.png"));\r
+    final ImageDescriptor RULER = ImageDescriptor.createFromURL(getClass().getResource("text_ruler.png"));\r
+    final ImageDescriptor PAGE_BORDER = ImageDescriptor.createFromURL(getClass().getResource("page_white.png"));\r
+    final ImageDescriptor EDGE = ImageDescriptor.createFromURL(getClass().getResource("arrow_ew.png"));\r
+    final ImageDescriptor BRANCH_POINT = ImageDescriptor.createFromURL(getClass().getResource("bullet_black.png"));\r
+    final ImageDescriptor LINK = ImageDescriptor.createFromURL(getClass().getResource("link.png"));\r
+\r
+    NodeProcedure<NodeProxy> nodeProcedure = new NodeProcedure<NodeProxy>() {\r
+        @Override\r
+        public NodeProxy execute(INode node, String id) {\r
+            return new NodeProxy(node, id);\r
+        }\r
+    };\r
+\r
+    class ContentProvider implements ITreeContentProvider {\r
+\r
+        @Override\r
+        public Object[] getChildren(Object parentElement) {\r
+            if (parentElement instanceof NodeProxy) {\r
+                NodeProxy np = (NodeProxy) parentElement;\r
+                INode n = np.getNode();\r
+                if (n != null) {\r
+                    List<NodeProxy> children = NodeUtil.forChildren(n, nodeProcedure);\r
+                    return children.toArray();\r
+                }\r
+            }\r
+            return new Object[0];\r
+        }\r
+\r
+        @Override\r
+        public Object getParent(Object element) {\r
+            return null;\r
+        }\r
+\r
+        @Override\r
+        public boolean hasChildren(Object element) {\r
+            if (element instanceof NodeProxy) {\r
+                NodeProxy np = (NodeProxy) element;\r
+                INode n = np.getNode();\r
+                if (n != null)\r
+                    return NodeUtil.hasChildren(n);\r
+            }\r
+            return false;\r
+        }\r
+\r
+        @Override\r
+        public Object[] getElements(Object inputElement) {\r
+            if (inputElement instanceof INode[]) {\r
+                INode[] ns = (INode[]) inputElement;\r
+                NodeProxy[] result = new NodeProxy[ns.length];\r
+                for (int i = 0; i < ns.length; ++i)\r
+                    result[i] = new NodeProxy(ns[i], "root");\r
+                return result;\r
+            }\r
+            if (inputElement instanceof INode) {\r
+                INode n = (INode) inputElement;\r
+                return new Object[] { new NodeProxy(n, "root") };\r
+            }\r
+            return new Object[0];\r
+        }\r
+\r
+        @Override\r
+        public void dispose() {\r
+        }\r
+\r
+        @Override\r
+        public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {\r
+        }\r
+    }\r
+\r
+    private Image toImage(NodeProxy proxy) {\r
+        INode n = proxy.getNode();\r
+        if (n == null)\r
+            return null;\r
+\r
+        if (n instanceof G2DSceneGraph) {\r
+            return resourceManager.createImage(ROOT);\r
+        } else if (n instanceof SingleElementNode) {\r
+            return resourceManager.createImage(ELEMENT);\r
+        } else if (n instanceof TransformNode) {\r
+            return resourceManager.createImage(TRANSFORM);\r
+        } else if (n instanceof NavigationNode) {\r
+            return resourceManager.createImage(NAVIGATION);\r
+        } else if (n instanceof BoundsNode) {\r
+            return resourceManager.createImage(CANVAS_BOUNDS);\r
+        } else if (n instanceof EdgeNode) {\r
+            return resourceManager.createImage(EDGE);\r
+        } else if (n instanceof BranchPointNode) {\r
+            return resourceManager.createImage(BRANCH_POINT);\r
+        } else if (n instanceof ShapeNode) {\r
+            return resourceManager.createImage(SHAPE);\r
+        } else if (n instanceof SVGNode) {\r
+            return resourceManager.createImage(SVG);\r
+        } else if (n instanceof G2DParentNode) {\r
+            return resourceManager.createImage(PARENT);\r
+        } else if (n instanceof GridNode) {\r
+            return resourceManager.createImage(GRID);\r
+        } else if (n instanceof RulerNode) {\r
+            return resourceManager.createImage(RULER);\r
+        } else if (n instanceof PageBorderNode) {\r
+            return resourceManager.createImage(PAGE_BORDER);\r
+        } else if (n instanceof LinkNode) {\r
+            return resourceManager.createImage(LINK);\r
+        }\r
+        return null;\r
+    }\r
+\r
+    class InternalIdLabelProvider extends ColumnLabelProvider {\r
+        @Override\r
+        public void update(ViewerCell cell) {\r
+            NodeProxy proxy = (NodeProxy) cell.getElement();\r
+            cell.setText(proxy.getInternalId());\r
+            //cell.setImage(toImage(proxy));\r
+        }\r
+    }\r
+    class TypeLabelProvider extends ColumnLabelProvider {\r
+        @Override\r
+        public void update(ViewerCell cell) {\r
+            NodeProxy proxy = (NodeProxy) cell.getElement();\r
+            cell.setText(proxy.getTypeName());\r
+            //cell.setImage(toImage(proxy));\r
+        }\r
+    }\r
+    class IdLabelProvider extends ColumnLabelProvider {\r
+        @Override\r
+        public void update(ViewerCell cell) {\r
+            NodeProxy proxy = (NodeProxy) cell.getElement();\r
+            cell.setText(proxy.getId());\r
+            cell.setImage(toImage(proxy));\r
+        }\r
+    }\r
+    class LookupIdLabelProvider extends ColumnLabelProvider {\r
+        @Override\r
+        public void update(ViewerCell cell) {\r
+            NodeProxy proxy = (NodeProxy) cell.getElement();\r
+            INode node = proxy.getNode();\r
+            String lookupId = null;\r
+            if (node != null) {\r
+                ILookupService lut = NodeUtil.tryGetLookupService(node);\r
+                if (lut != null)\r
+                    lookupId = lut.lookupId(node);\r
+            }\r
+            cell.setText(lookupId != null ? lookupId : "");\r
+        }\r
+    }\r
+    class ZLabelProvider extends ColumnLabelProvider {\r
+        @Override\r
+        public void update(ViewerCell cell) {\r
+            NodeProxy proxy = (NodeProxy) cell.getElement();\r
+            INode node = proxy.getNode();\r
+            if (node instanceof IG2DNode) {\r
+                IG2DNode n = (IG2DNode) node;\r
+                cell.setText(String.valueOf(n.getZIndex()));\r
+            } else {\r
+                cell.setText("-");\r
+            }\r
+        }\r
+    }\r
+\r
+    @Override\r
+    public void createPartControl(Composite parent) {\r
+\r
+        tree = new TreeViewer(parent, SWT.SINGLE | SWT.FULL_SELECTION);\r
+        resourceManager = new LocalResourceManager(JFaceResources.getResources(), tree.getTree());\r
+\r
+        TreeColumnLayout ad = new TreeColumnLayout();\r
+        parent.setLayout(ad);\r
+\r
+        //tree.getTree().setLayout(new FillLayout());\r
+        tree.setContentProvider(new ContentProvider());\r
+        tree.getTree().setHeaderVisible(true);\r
+        //tree.getTree().setLinesVisible(true);\r
+        tree.setUseHashlookup(true);\r
+        tree.setAutoExpandLevel(3);\r
+\r
+        TreeViewerColumn nameColumn = new TreeViewerColumn(tree, SWT.LEFT);\r
+        TreeViewerColumn typeColumn = new TreeViewerColumn(tree, SWT.LEFT);\r
+        TreeViewerColumn idColumn = new TreeViewerColumn(tree, SWT.LEFT);\r
+        TreeViewerColumn lookupIdColumn = new TreeViewerColumn(tree, SWT.LEFT);\r
+        TreeViewerColumn zColumn = new TreeViewerColumn(tree, SWT.LEFT);\r
+\r
+        nameColumn.setLabelProvider(new IdLabelProvider());\r
+        typeColumn.setLabelProvider(new TypeLabelProvider());\r
+        idColumn.setLabelProvider(new InternalIdLabelProvider());\r
+        lookupIdColumn.setLabelProvider(new LookupIdLabelProvider());\r
+        zColumn.setLabelProvider(new ZLabelProvider());\r
+\r
+        nameColumn.getColumn().setText("Name");\r
+        nameColumn.getColumn().setWidth(20);\r
+        ad.setColumnData(nameColumn.getColumn(), new ColumnWeightData(80, 100));\r
+        typeColumn.getColumn().setText("Type");\r
+        typeColumn.getColumn().setWidth(20);\r
+        ad.setColumnData(typeColumn.getColumn(), new ColumnWeightData(20, 120));\r
+        idColumn.getColumn().setText("ID");\r
+        idColumn.getColumn().setWidth(20);\r
+        ad.setColumnData(idColumn.getColumn(), new ColumnWeightData(10, 50));\r
+        lookupIdColumn.getColumn().setText("Lookup ID");\r
+        lookupIdColumn.getColumn().setWidth(20);\r
+        ad.setColumnData(lookupIdColumn.getColumn(), new ColumnWeightData(50, 100));\r
+        zColumn.getColumn().setText("Z");\r
+        zColumn.getColumn().setWidth(70);\r
+        ad.setColumnData(zColumn.getColumn(), new ColumnWeightData(10, 70));\r
+\r
+        tree.addSelectionChangedListener(new ISelectionChangedListener() {\r
+            @Override\r
+            public void selectionChanged(SelectionChangedEvent event) {\r
+                updateContentDescription();\r
+            }\r
+        });\r
+        tree.addDoubleClickListener(new IDoubleClickListener() {\r
+            @Override\r
+            public void doubleClick(DoubleClickEvent event) {\r
+                openAttributeDialog();\r
+            }\r
+        });\r
+\r
+        contextActivation = ((IContextService) getSite()\r
+                .getService(IContextService.class))\r
+                .activateContext("org.simantics.scenegraph.viewer");\r
+\r
+        ICommandService commandService = (ICommandService) PlatformUI.getWorkbench().getService(ICommandService.class);\r
+        Command command = commandService.getCommand(LinkToActiveWorkbenchPartHandler.COMMAND);\r
+        State state = command.getState(LinkToActiveWorkbenchPartHandler.STATE);\r
+        this.linkToPart = Boolean.TRUE.equals(state.getValue());\r
+\r
+        // No need to remove this listener, the part service is local to this site.\r
+        IPartService partService = (IPartService) getSite().getService(IPartService.class);\r
+        partService.addPartListener(partListener);\r
+    }\r
+\r
+    @Override\r
+    public void dispose() {\r
+        closeAttributeDialog();\r
+    }\r
+\r
+    protected void openAttributeDialog() {\r
+        if (attributeDialog != null) {\r
+            Shell shell = attributeDialog.getShell();\r
+            if (shell == null || shell.isDisposed())\r
+                attributeDialog = null;\r
+        }\r
+        if (attributeDialog == null) {\r
+            attributeDialog = new AttributeDialog(getSite().getShell(), tree);\r
+            attributeDialog.setBlockOnOpen(false);\r
+            attributeDialog.open();\r
+        }\r
+    }\r
+\r
+    protected void closeAttributeDialog() {\r
+        if (attributeDialog != null) {\r
+            attributeDialog.close();\r
+            attributeDialog = null;\r
+        }\r
+    }\r
+\r
+    IPartListener2 partListener = new IPartListener2() {\r
+        @Override\r
+        public void partVisible(IWorkbenchPartReference partRef) {\r
+        }\r
+        @Override\r
+        public void partOpened(IWorkbenchPartReference partRef) {\r
+        }\r
+        @Override\r
+        public void partInputChanged(IWorkbenchPartReference partRef) {\r
+        }\r
+        @Override\r
+        public void partHidden(IWorkbenchPartReference partRef) {\r
+        }\r
+        @Override\r
+        public void partDeactivated(IWorkbenchPartReference partRef) {\r
+        }\r
+        @Override\r
+        public void partClosed(IWorkbenchPartReference partRef) {\r
+            if (linkToPart) {\r
+                IWorkbenchPart part = partRef.getPart(false);\r
+                if (part != null)\r
+                    refresh(null);\r
+            }\r
+        }\r
+        @Override\r
+        public void partBroughtToTop(IWorkbenchPartReference partRef) {\r
+        }\r
+        @Override\r
+        public void partActivated(IWorkbenchPartReference partRef) {\r
+            if (linkToPart) {\r
+                IWorkbenchPart part = partRef.getPart(false);\r
+                if (part != null) {\r
+                    if (part != lastPart) {\r
+                        refresh(part);\r
+                    }\r
+                }\r
+            }\r
+        }\r
+    };\r
+\r
+    @Override\r
+    public void setFocus() {\r
+        tree.getTree().setFocus();\r
+        if (!bootstrapped) {\r
+            bootstrapped = true;\r
+            refresh();\r
+        }\r
+    }\r
+\r
+    protected void refresh() {\r
+        IWorkbenchPart part = null;\r
+        try {\r
+            IWorkbenchWindow window = getSite().getWorkbenchWindow();\r
+            if (window == null)\r
+                return;\r
+            IWorkbenchPage page = window.getActivePage();\r
+            if (page == null)\r
+                return;\r
+            part = page.getActiveEditor();\r
+            if (part == null)\r
+                return;\r
+        } finally {\r
+            if (part == null) {\r
+                setContentDescription("No scene graph nodes available.");\r
+                // TODO: Show info page instead of tree view.\r
+            }\r
+        }\r
+\r
+        refresh(part);\r
+    }\r
+\r
+    /**\r
+     * @param part <code>null</code> to reset the view to a blank state.\r
+     * @return\r
+     */\r
+    protected boolean refresh(IWorkbenchPart part) {\r
+        boolean foundInput = true;\r
+        try {\r
+            Object obj = null;\r
+            if (part != null) {\r
+                obj = part.getAdapter(INode[].class);\r
+                if (obj == null)\r
+                    obj = part.getAdapter(INode.class);\r
+            }\r
+\r
+            if (obj != null) {\r
+                TreePath[] expanded = tree.getExpandedTreePaths();\r
+                tree.setInput(obj);\r
+                tree.setExpandedTreePaths(expanded);\r
+                this.currentNodeCount = countNodes(obj);\r
+                updateContentDescription();\r
+                foundInput = true;\r
+            }\r
+            lastPart = part;\r
+            return foundInput;\r
+        } finally {\r
+            if (!foundInput) {\r
+                setContentDescription("No scene graph nodes available.");\r
+                // TODO: Show info page instead of tree view.\r
+            }\r
+        }\r
+    }\r
+\r
+    private void updateContentDescription() {\r
+        StringBuilder desc = new StringBuilder();\r
+        desc.append(currentNodeCount + " nodes in total.");\r
+\r
+        IStructuredSelection ss = (IStructuredSelection) tree.getSelection();\r
+        Object obj = ss.getFirstElement();\r
+        if (obj instanceof NodeProxy) {\r
+            NodeProxy np = (NodeProxy) obj;\r
+            INode n = np.getNode();\r
+            if (n != null) {\r
+                int depth = NodeUtil.getDepth(n);\r
+                desc.append(" Selection ");\r
+                desc.append("at depth ");\r
+                desc.append(depth);\r
+                desc.append(".");\r
+            }\r
+        }\r
+\r
+        setContentDescription(desc.toString());\r
+    }\r
+\r
+    private int countNodes(Object obj) {\r
+        if (obj instanceof INode) {\r
+            INode n = (INode) obj;\r
+            return NodeUtil.countTreeNodes(n);\r
+        } else if (obj instanceof INode[]) {\r
+            INode[] ns = (INode[]) obj;\r
+            int result = 0;\r
+            for (INode n : ns)\r
+                result += NodeUtil.countTreeNodes(n);\r
+            return result;\r
+        }\r
+        return 0;\r
+    }\r
+\r
+    void copySelectionToClipboard() {\r
+        IStructuredSelection selection = (IStructuredSelection) tree.getSelection();\r
+        Object obj = selection.getFirstElement();\r
+        if (!(obj instanceof NodeProxy))\r
+            return;\r
+\r
+        NodeProxy np = (NodeProxy) obj;\r
+        INode n = np.getNode();\r
+        if (n == null)\r
+            return;\r
+\r
+        ByteArrayOutputStream bytes = new ByteArrayOutputStream(100000);\r
+        PrintStream stream = new PrintStream(bytes);\r
+        NodeUtil.printSceneGraph(stream, 0, n);\r
+        String textData = new String(bytes.toByteArray());\r
+        if (textData.isEmpty())\r
+            return;\r
+\r
+        Clipboard clipboard = new Clipboard(tree.getControl().getDisplay());\r
+        TextTransfer textTransfer = TextTransfer.getInstance();\r
+        Transfer[] transfers = new Transfer[]{textTransfer};\r
+        Object[] data = new Object[]{textData};\r
+        clipboard.setContents(data, transfers);\r
+        clipboard.dispose();\r
+    }\r
+\r
+    void collapseAll() {\r
+        for (Object obj : tree.getExpandedElements()) {\r
+            tree.setExpandedState(obj, false);\r
+        }\r
+    }\r
+\r
+    void expandSelectedNode() {\r
+        IStructuredSelection ss = (IStructuredSelection) tree.getSelection();\r
+        for (Object obj : ss.toList()) {\r
+            tree.expandToLevel(obj, TreeViewer.ALL_LEVELS);\r
+        }\r
+    }\r
+\r
+    public void linkToActiveWorkbenchPart(boolean value) {\r
+        this.linkToPart = value;\r
+    }\r
+\r
+}\r