]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scenegraph.ui/src/org/simantics/scenegraph/ui/SceneGraphViewPart.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.scenegraph.ui / src / org / simantics / scenegraph / ui / SceneGraphViewPart.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.scenegraph.ui;
13
14 import java.io.ByteArrayOutputStream;
15 import java.io.PrintStream;
16 import java.lang.ref.WeakReference;
17 import java.util.List;
18
19 import org.eclipse.core.commands.Command;
20 import org.eclipse.core.commands.State;
21 import org.eclipse.jface.layout.TreeColumnLayout;
22 import org.eclipse.jface.resource.ImageDescriptor;
23 import org.eclipse.jface.resource.JFaceResources;
24 import org.eclipse.jface.resource.LocalResourceManager;
25 import org.eclipse.jface.viewers.ColumnLabelProvider;
26 import org.eclipse.jface.viewers.ColumnWeightData;
27 import org.eclipse.jface.viewers.DoubleClickEvent;
28 import org.eclipse.jface.viewers.IDoubleClickListener;
29 import org.eclipse.jface.viewers.ISelectionChangedListener;
30 import org.eclipse.jface.viewers.IStructuredSelection;
31 import org.eclipse.jface.viewers.ITreeContentProvider;
32 import org.eclipse.jface.viewers.SelectionChangedEvent;
33 import org.eclipse.jface.viewers.TreePath;
34 import org.eclipse.jface.viewers.TreeViewer;
35 import org.eclipse.jface.viewers.TreeViewerColumn;
36 import org.eclipse.jface.viewers.Viewer;
37 import org.eclipse.jface.viewers.ViewerCell;
38 import org.eclipse.swt.SWT;
39 import org.eclipse.swt.dnd.Clipboard;
40 import org.eclipse.swt.dnd.TextTransfer;
41 import org.eclipse.swt.dnd.Transfer;
42 import org.eclipse.swt.graphics.Image;
43 import org.eclipse.swt.widgets.Composite;
44 import org.eclipse.swt.widgets.Shell;
45 import org.eclipse.ui.IPartListener2;
46 import org.eclipse.ui.IPartService;
47 import org.eclipse.ui.IWorkbenchPage;
48 import org.eclipse.ui.IWorkbenchPart;
49 import org.eclipse.ui.IWorkbenchPartReference;
50 import org.eclipse.ui.IWorkbenchWindow;
51 import org.eclipse.ui.PlatformUI;
52 import org.eclipse.ui.commands.ICommandService;
53 import org.eclipse.ui.contexts.IContextActivation;
54 import org.eclipse.ui.contexts.IContextService;
55 import org.eclipse.ui.part.ViewPart;
56 import org.simantics.scenegraph.ILookupService;
57 import org.simantics.scenegraph.INode;
58 import org.simantics.scenegraph.g2d.G2DParentNode;
59 import org.simantics.scenegraph.g2d.G2DSceneGraph;
60 import org.simantics.scenegraph.g2d.IG2DNode;
61 import org.simantics.scenegraph.g2d.nodes.BoundsNode;
62 import org.simantics.scenegraph.g2d.nodes.BranchPointNode;
63 import org.simantics.scenegraph.g2d.nodes.EdgeNode;
64 import org.simantics.scenegraph.g2d.nodes.GridNode;
65 import org.simantics.scenegraph.g2d.nodes.LinkNode;
66 import org.simantics.scenegraph.g2d.nodes.NavigationNode;
67 import org.simantics.scenegraph.g2d.nodes.PageBorderNode;
68 import org.simantics.scenegraph.g2d.nodes.RulerNode;
69 import org.simantics.scenegraph.g2d.nodes.SVGNode;
70 import org.simantics.scenegraph.g2d.nodes.ShapeNode;
71 import org.simantics.scenegraph.g2d.nodes.SingleElementNode;
72 import org.simantics.scenegraph.g2d.nodes.TransformNode;
73 import org.simantics.scenegraph.utils.NodeUtil;
74 import org.simantics.scenegraph.utils.NodeUtil.NodeProcedure;
75
76 /**
77  * This view shows the contents of a 2D/3D canvas scenegraph through a tree
78  * viewer.
79  * 
80  * <p>
81  * The viewer sources its scene graph input from the currently active workbench
82  * editor. It does not automatically track the active editor part but instead
83  * has to be refreshed manually (F5).
84  * </p>
85  * 
86  * <p>
87  * References to actual scene graph nodes are only kept as {@link WeakReference}
88  * instances allowing them to be garbage collected if the scene graph is
89  * disposed of.
90  * </p>
91  * 
92  * @author Tuukka Lehtonen
93  */
94 public class SceneGraphViewPart extends ViewPart {
95
96     TreeViewer           tree;
97     LocalResourceManager resourceManager;
98     boolean              bootstrapped = false;
99     IContextActivation   contextActivation;
100     int                  currentNodeCount = 0;
101     boolean              linkToPart;
102     IWorkbenchPart       lastPart;
103     AttributeDialog      attributeDialog;
104
105     final ImageDescriptor ROOT = ImageDescriptor.createFromURL(getClass().getResource("bullet_home.png"));
106     final ImageDescriptor CANVAS_BOUNDS = ImageDescriptor.createFromURL(getClass().getResource("application.png"));
107     final ImageDescriptor SHAPE = ImageDescriptor.createFromURL(getClass().getResource("shape_shadow.png"));
108     final ImageDescriptor NAVIGATION = ImageDescriptor.createFromURL(getClass().getResource("arrow_out_longer.png"));
109     final ImageDescriptor SVG = ImageDescriptor.createFromURL(getClass().getResource("script_code.png"));
110     final ImageDescriptor TRANSFORM = ImageDescriptor.createFromURL(getClass().getResource("arrow_nsew.png"));
111     final ImageDescriptor ELEMENT = ImageDescriptor.createFromURL(getClass().getResource("shape_handles.png"));
112     final ImageDescriptor PARENT = ImageDescriptor.createFromURL(getClass().getResource("share.png"));
113     final ImageDescriptor GRID = ImageDescriptor.createFromURL(getClass().getResource("border_all.png"));
114     final ImageDescriptor RULER = ImageDescriptor.createFromURL(getClass().getResource("text_ruler.png"));
115     final ImageDescriptor PAGE_BORDER = ImageDescriptor.createFromURL(getClass().getResource("page_white.png"));
116     final ImageDescriptor EDGE = ImageDescriptor.createFromURL(getClass().getResource("arrow_ew.png"));
117     final ImageDescriptor BRANCH_POINT = ImageDescriptor.createFromURL(getClass().getResource("bullet_black.png"));
118     final ImageDescriptor LINK = ImageDescriptor.createFromURL(getClass().getResource("link.png"));
119
120     NodeProcedure<NodeProxy> nodeProcedure = new NodeProcedure<NodeProxy>() {
121         @Override
122         public NodeProxy execute(INode node, String id) {
123             return new NodeProxy(node, id);
124         }
125     };
126
127     class ContentProvider implements ITreeContentProvider {
128
129         @Override
130         public Object[] getChildren(Object parentElement) {
131             if (parentElement instanceof NodeProxy) {
132                 NodeProxy np = (NodeProxy) parentElement;
133                 INode n = np.getNode();
134                 if (n != null) {
135                     List<NodeProxy> children = NodeUtil.forChildren(n, nodeProcedure);
136                     return children.toArray();
137                 }
138             }
139             return new Object[0];
140         }
141
142         @Override
143         public Object getParent(Object element) {
144             return null;
145         }
146
147         @Override
148         public boolean hasChildren(Object element) {
149             if (element instanceof NodeProxy) {
150                 NodeProxy np = (NodeProxy) element;
151                 INode n = np.getNode();
152                 if (n != null)
153                     return NodeUtil.hasChildren(n);
154             }
155             return false;
156         }
157
158         @Override
159         public Object[] getElements(Object inputElement) {
160             if (inputElement instanceof INode[]) {
161                 INode[] ns = (INode[]) inputElement;
162                 NodeProxy[] result = new NodeProxy[ns.length];
163                 for (int i = 0; i < ns.length; ++i)
164                     result[i] = new NodeProxy(ns[i], "root");
165                 return result;
166             }
167             if (inputElement instanceof INode) {
168                 INode n = (INode) inputElement;
169                 return new Object[] { new NodeProxy(n, "root") };
170             }
171             return new Object[0];
172         }
173
174         @Override
175         public void dispose() {
176         }
177
178         @Override
179         public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
180         }
181     }
182
183     private Image toImage(NodeProxy proxy) {
184         INode n = proxy.getNode();
185         if (n == null)
186             return null;
187
188         if (n instanceof G2DSceneGraph) {
189             return resourceManager.createImage(ROOT);
190         } else if (n instanceof SingleElementNode) {
191             return resourceManager.createImage(ELEMENT);
192         } else if (n instanceof TransformNode) {
193             return resourceManager.createImage(TRANSFORM);
194         } else if (n instanceof NavigationNode) {
195             return resourceManager.createImage(NAVIGATION);
196         } else if (n instanceof BoundsNode) {
197             return resourceManager.createImage(CANVAS_BOUNDS);
198         } else if (n instanceof EdgeNode) {
199             return resourceManager.createImage(EDGE);
200         } else if (n instanceof BranchPointNode) {
201             return resourceManager.createImage(BRANCH_POINT);
202         } else if (n instanceof ShapeNode) {
203             return resourceManager.createImage(SHAPE);
204         } else if (n instanceof SVGNode) {
205             return resourceManager.createImage(SVG);
206         } else if (n instanceof G2DParentNode) {
207             return resourceManager.createImage(PARENT);
208         } else if (n instanceof GridNode) {
209             return resourceManager.createImage(GRID);
210         } else if (n instanceof RulerNode) {
211             return resourceManager.createImage(RULER);
212         } else if (n instanceof PageBorderNode) {
213             return resourceManager.createImage(PAGE_BORDER);
214         } else if (n instanceof LinkNode) {
215             return resourceManager.createImage(LINK);
216         }
217         return null;
218     }
219
220     class InternalIdLabelProvider extends ColumnLabelProvider {
221         @Override
222         public void update(ViewerCell cell) {
223             NodeProxy proxy = (NodeProxy) cell.getElement();
224             cell.setText(proxy.getInternalId());
225             //cell.setImage(toImage(proxy));
226         }
227     }
228     class TypeLabelProvider extends ColumnLabelProvider {
229         @Override
230         public void update(ViewerCell cell) {
231             NodeProxy proxy = (NodeProxy) cell.getElement();
232             cell.setText(proxy.getTypeName());
233             //cell.setImage(toImage(proxy));
234         }
235     }
236     class IdLabelProvider extends ColumnLabelProvider {
237         @Override
238         public void update(ViewerCell cell) {
239             NodeProxy proxy = (NodeProxy) cell.getElement();
240             cell.setText(proxy.getId());
241             cell.setImage(toImage(proxy));
242         }
243     }
244     class LookupIdLabelProvider extends ColumnLabelProvider {
245         @Override
246         public void update(ViewerCell cell) {
247             NodeProxy proxy = (NodeProxy) cell.getElement();
248             INode node = proxy.getNode();
249             String lookupId = null;
250             if (node != null) {
251                 ILookupService lut = NodeUtil.tryGetLookupService(node);
252                 if (lut != null)
253                     lookupId = lut.lookupId(node);
254             }
255             cell.setText(lookupId != null ? lookupId : "");
256         }
257     }
258     class ZLabelProvider extends ColumnLabelProvider {
259         @Override
260         public void update(ViewerCell cell) {
261             NodeProxy proxy = (NodeProxy) cell.getElement();
262             INode node = proxy.getNode();
263             if (node instanceof IG2DNode) {
264                 IG2DNode n = (IG2DNode) node;
265                 cell.setText(String.valueOf(n.getZIndex()));
266             } else {
267                 cell.setText("-");
268             }
269         }
270     }
271
272     @Override
273     public void createPartControl(Composite parent) {
274
275         tree = new TreeViewer(parent, SWT.SINGLE | SWT.FULL_SELECTION);
276         resourceManager = new LocalResourceManager(JFaceResources.getResources(), tree.getTree());
277
278         TreeColumnLayout ad = new TreeColumnLayout();
279         parent.setLayout(ad);
280
281         //tree.getTree().setLayout(new FillLayout());
282         tree.setContentProvider(new ContentProvider());
283         tree.getTree().setHeaderVisible(true);
284         //tree.getTree().setLinesVisible(true);
285         tree.setUseHashlookup(true);
286         tree.setAutoExpandLevel(3);
287
288         TreeViewerColumn nameColumn = new TreeViewerColumn(tree, SWT.LEFT);
289         TreeViewerColumn typeColumn = new TreeViewerColumn(tree, SWT.LEFT);
290         TreeViewerColumn idColumn = new TreeViewerColumn(tree, SWT.LEFT);
291         TreeViewerColumn lookupIdColumn = new TreeViewerColumn(tree, SWT.LEFT);
292         TreeViewerColumn zColumn = new TreeViewerColumn(tree, SWT.LEFT);
293
294         nameColumn.setLabelProvider(new IdLabelProvider());
295         typeColumn.setLabelProvider(new TypeLabelProvider());
296         idColumn.setLabelProvider(new InternalIdLabelProvider());
297         lookupIdColumn.setLabelProvider(new LookupIdLabelProvider());
298         zColumn.setLabelProvider(new ZLabelProvider());
299
300         nameColumn.getColumn().setText("Name");
301         nameColumn.getColumn().setWidth(20);
302         ad.setColumnData(nameColumn.getColumn(), new ColumnWeightData(80, 100));
303         typeColumn.getColumn().setText("Type");
304         typeColumn.getColumn().setWidth(20);
305         ad.setColumnData(typeColumn.getColumn(), new ColumnWeightData(20, 120));
306         idColumn.getColumn().setText("ID");
307         idColumn.getColumn().setWidth(20);
308         ad.setColumnData(idColumn.getColumn(), new ColumnWeightData(10, 50));
309         lookupIdColumn.getColumn().setText("Lookup ID");
310         lookupIdColumn.getColumn().setWidth(20);
311         ad.setColumnData(lookupIdColumn.getColumn(), new ColumnWeightData(50, 100));
312         zColumn.getColumn().setText("Z");
313         zColumn.getColumn().setWidth(70);
314         ad.setColumnData(zColumn.getColumn(), new ColumnWeightData(10, 70));
315
316         tree.addSelectionChangedListener(new ISelectionChangedListener() {
317             @Override
318             public void selectionChanged(SelectionChangedEvent event) {
319                 updateContentDescription();
320             }
321         });
322         tree.addDoubleClickListener(new IDoubleClickListener() {
323             @Override
324             public void doubleClick(DoubleClickEvent event) {
325                 openAttributeDialog();
326             }
327         });
328
329         contextActivation = ((IContextService) getSite()
330                 .getService(IContextService.class))
331                 .activateContext("org.simantics.scenegraph.viewer");
332
333         ICommandService commandService = (ICommandService) PlatformUI.getWorkbench().getService(ICommandService.class);
334         Command command = commandService.getCommand(LinkToActiveWorkbenchPartHandler.COMMAND);
335         State state = command.getState(LinkToActiveWorkbenchPartHandler.STATE);
336         this.linkToPart = Boolean.TRUE.equals(state.getValue());
337
338         // No need to remove this listener, the part service is local to this site.
339         IPartService partService = (IPartService) getSite().getService(IPartService.class);
340         partService.addPartListener(partListener);
341     }
342
343     @Override
344     public void dispose() {
345         closeAttributeDialog();
346     }
347
348     protected void openAttributeDialog() {
349         if (attributeDialog != null) {
350             Shell shell = attributeDialog.getShell();
351             if (shell == null || shell.isDisposed())
352                 attributeDialog = null;
353         }
354         if (attributeDialog == null) {
355             attributeDialog = new AttributeDialog(getSite().getShell(), tree);
356             attributeDialog.setBlockOnOpen(false);
357             attributeDialog.open();
358         }
359     }
360
361     protected void closeAttributeDialog() {
362         if (attributeDialog != null) {
363             attributeDialog.close();
364             attributeDialog = null;
365         }
366     }
367
368     IPartListener2 partListener = new IPartListener2() {
369         @Override
370         public void partVisible(IWorkbenchPartReference partRef) {
371         }
372         @Override
373         public void partOpened(IWorkbenchPartReference partRef) {
374         }
375         @Override
376         public void partInputChanged(IWorkbenchPartReference partRef) {
377         }
378         @Override
379         public void partHidden(IWorkbenchPartReference partRef) {
380         }
381         @Override
382         public void partDeactivated(IWorkbenchPartReference partRef) {
383         }
384         @Override
385         public void partClosed(IWorkbenchPartReference partRef) {
386             if (linkToPart) {
387                 IWorkbenchPart part = partRef.getPart(false);
388                 if (part != null)
389                     refresh(null);
390             }
391         }
392         @Override
393         public void partBroughtToTop(IWorkbenchPartReference partRef) {
394         }
395         @Override
396         public void partActivated(IWorkbenchPartReference partRef) {
397             if (linkToPart) {
398                 IWorkbenchPart part = partRef.getPart(false);
399                 if (part != null) {
400                     if (part != lastPart) {
401                         refresh(part);
402                     }
403                 }
404             }
405         }
406     };
407
408     @Override
409     public void setFocus() {
410         tree.getTree().setFocus();
411         if (!bootstrapped) {
412             bootstrapped = true;
413             refresh();
414         }
415     }
416
417     protected void refresh() {
418         IWorkbenchPart part = null;
419         try {
420             IWorkbenchWindow window = getSite().getWorkbenchWindow();
421             if (window == null)
422                 return;
423             IWorkbenchPage page = window.getActivePage();
424             if (page == null)
425                 return;
426             part = page.getActiveEditor();
427             if (part == null)
428                 return;
429         } finally {
430             if (part == null) {
431                 setContentDescription("No scene graph nodes available.");
432                 // TODO: Show info page instead of tree view.
433             }
434         }
435
436         refresh(part);
437     }
438
439     /**
440      * @param part <code>null</code> to reset the view to a blank state.
441      * @return
442      */
443     protected boolean refresh(IWorkbenchPart part) {
444         boolean foundInput = true;
445         try {
446             Object obj = null;
447             if (part != null) {
448                 obj = part.getAdapter(INode[].class);
449                 if (obj == null)
450                     obj = part.getAdapter(INode.class);
451             }
452
453             if (obj != null) {
454                 TreePath[] expanded = tree.getExpandedTreePaths();
455                 tree.setInput(obj);
456                 tree.setExpandedTreePaths(expanded);
457                 this.currentNodeCount = countNodes(obj);
458                 updateContentDescription();
459                 foundInput = true;
460             }
461             lastPart = part;
462             return foundInput;
463         } finally {
464             if (!foundInput) {
465                 setContentDescription("No scene graph nodes available.");
466                 // TODO: Show info page instead of tree view.
467             }
468         }
469     }
470
471     private void updateContentDescription() {
472         StringBuilder desc = new StringBuilder();
473         desc.append(currentNodeCount + " nodes in total.");
474
475         IStructuredSelection ss = (IStructuredSelection) tree.getSelection();
476         Object obj = ss.getFirstElement();
477         if (obj instanceof NodeProxy) {
478             NodeProxy np = (NodeProxy) obj;
479             INode n = np.getNode();
480             if (n != null) {
481                 int depth = NodeUtil.getDepth(n);
482                 desc.append(" Selection ");
483                 desc.append("at depth ");
484                 desc.append(depth);
485                 desc.append(".");
486             }
487         }
488
489         setContentDescription(desc.toString());
490     }
491
492     private int countNodes(Object obj) {
493         if (obj instanceof INode) {
494             INode n = (INode) obj;
495             return NodeUtil.countTreeNodes(n);
496         } else if (obj instanceof INode[]) {
497             INode[] ns = (INode[]) obj;
498             int result = 0;
499             for (INode n : ns)
500                 result += NodeUtil.countTreeNodes(n);
501             return result;
502         }
503         return 0;
504     }
505
506     void copySelectionToClipboard() {
507         IStructuredSelection selection = (IStructuredSelection) tree.getSelection();
508         Object obj = selection.getFirstElement();
509         if (!(obj instanceof NodeProxy))
510             return;
511
512         NodeProxy np = (NodeProxy) obj;
513         INode n = np.getNode();
514         if (n == null)
515             return;
516
517         ByteArrayOutputStream bytes = new ByteArrayOutputStream(100000);
518         PrintStream stream = new PrintStream(bytes);
519         NodeUtil.printSceneGraph(stream, 0, n);
520         String textData = new String(bytes.toByteArray());
521         if (textData.isEmpty())
522             return;
523
524         Clipboard clipboard = new Clipboard(tree.getControl().getDisplay());
525         TextTransfer textTransfer = TextTransfer.getInstance();
526         Transfer[] transfers = new Transfer[]{textTransfer};
527         Object[] data = new Object[]{textData};
528         clipboard.setContents(data, transfers);
529         clipboard.dispose();
530     }
531
532     void collapseAll() {
533         for (Object obj : tree.getExpandedElements()) {
534             tree.setExpandedState(obj, false);
535         }
536     }
537
538     void expandSelectedNode() {
539         IStructuredSelection ss = (IStructuredSelection) tree.getSelection();
540         for (Object obj : ss.toList()) {
541             tree.expandToLevel(obj, TreeViewer.ALL_LEVELS);
542         }
543     }
544
545     public void linkToActiveWorkbenchPart(boolean value) {
546         this.linkToPart = value;
547     }
548
549 }