]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.team.ui/src/org/simantics/team/ui/StagingView.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.team.ui / src / org / simantics / team / ui / StagingView.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
3  * in Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.team.ui;\r
13 \r
14 import java.util.Map;\r
15 import java.util.Vector;\r
16 \r
17 import org.eclipse.jface.layout.GridDataFactory;\r
18 import org.eclipse.jface.layout.TreeColumnLayout;\r
19 import org.eclipse.jface.resource.JFaceResources;\r
20 import org.eclipse.jface.resource.LocalResourceManager;\r
21 import org.eclipse.jface.viewers.ColumnLabelProvider;\r
22 import org.eclipse.jface.viewers.ColumnWeightData;\r
23 import org.eclipse.jface.viewers.ITreeContentProvider;\r
24 import org.eclipse.jface.viewers.LabelProvider;\r
25 import org.eclipse.jface.viewers.TreeViewer;\r
26 import org.eclipse.jface.viewers.TreeViewerColumn;\r
27 import org.eclipse.jface.viewers.Viewer;\r
28 import org.eclipse.jface.viewers.ViewerCell;\r
29 import org.eclipse.jface.viewers.ViewerFilter;\r
30 import org.eclipse.jface.window.ToolTip;\r
31 import org.eclipse.swt.SWT;\r
32 import org.eclipse.swt.graphics.Image;\r
33 import org.eclipse.swt.graphics.Point;\r
34 import org.eclipse.swt.layout.GridData;\r
35 import org.eclipse.swt.layout.GridLayout;\r
36 import org.eclipse.swt.widgets.Composite;\r
37 import org.eclipse.swt.widgets.Control;\r
38 import org.eclipse.swt.widgets.Event;\r
39 import org.eclipse.swt.widgets.Label;\r
40 import org.eclipse.swt.widgets.Text;\r
41 import org.eclipse.swt.widgets.Tree;\r
42 import org.eclipse.ui.IMemento;\r
43 import org.eclipse.ui.IViewSite;\r
44 import org.eclipse.ui.PartInitException;\r
45 import org.eclipse.ui.part.ViewPart;\r
46 import org.simantics.browsing.ui.GraphExplorer;\r
47 import org.simantics.browsing.ui.graph.impl.SessionContextInputSource;\r
48 import org.simantics.browsing.ui.swt.DefaultSelectionDataResolver;\r
49 import org.simantics.browsing.ui.swt.GraphExplorerFactory;\r
50 import org.simantics.browsing.ui.swt.ViewArgumentUtils;\r
51 import org.simantics.db.Session;\r
52 import org.simantics.db.exception.DatabaseException;\r
53 import org.simantics.db.management.ISessionContext;\r
54 import org.simantics.db.management.ISessionContextChangedListener;\r
55 import org.simantics.db.management.ISessionContextProvider;\r
56 import org.simantics.db.management.SessionContextChangedEvent;\r
57 import org.simantics.db.service.TeamSupport;\r
58 import org.simantics.project.ProjectKeys;\r
59 import org.simantics.team.Utils;\r
60 import org.simantics.team.internal.Images;\r
61 import org.simantics.ui.SimanticsUI;\r
62 import org.simantics.utils.datastructures.disposable.DisposeState;\r
63 import org.simantics.utils.datastructures.hints.HintListenerAdapter;\r
64 import org.simantics.utils.datastructures.hints.HintTracker;\r
65 import org.simantics.utils.datastructures.hints.IHintContext.Key;\r
66 import org.simantics.utils.datastructures.hints.IHintListener;\r
67 import org.simantics.utils.datastructures.hints.IHintObservable;\r
68 import org.simantics.utils.datastructures.hints.IHintTracker;\r
69 import org.simantics.utils.ui.LayoutUtils;\r
70 \r
71 /**\r
72  * @author Kalle Kondelin\r
73  */\r
74 public class StagingView extends ViewPart {\r
75 \r
76     public StagingView() {\r
77     }\r
78 \r
79     public class SessionContextProjectTracker extends HintTracker {\r
80         public SessionContextProjectTracker() {\r
81             IHintListener activeProjectListener = new HintListenerAdapter() {\r
82                 @Override\r
83                 public void hintChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) {\r
84                     applySessionContext(getSessionContext());\r
85                 }\r
86             };\r
87             addKeyHintListener(ProjectKeys.KEY_PROJECT, activeProjectListener);\r
88         }\r
89     }\r
90 \r
91     protected LocalResourceManager resourceManager;\r
92     protected Composite parent;\r
93     protected TreeViewer treeViewer;\r
94     private Map<String, String> args;\r
95     private ISessionContextProvider contextProvider;\r
96     private ISessionContext sessionContext;\r
97     protected IMemento memento;\r
98     private IHintTracker sessionContextTracker = new SessionContextProjectTracker();\r
99     private DisposeState disposeState = DisposeState.Alive;\r
100 \r
101     protected ISessionContextChangedListener contextChangeListener = new ISessionContextChangedListener() {\r
102         @Override\r
103         public void sessionContextChanged(SessionContextChangedEvent event) {\r
104             sessionContext = event.getNewValue();\r
105             sessionContextTracker.track(sessionContext);\r
106         }\r
107     };\r
108 \r
109     protected void setSessionContextTracker(IHintTracker tracker) {\r
110         this.sessionContextTracker = tracker;\r
111     }\r
112 \r
113     protected Map<String, String> getViewArguments() {\r
114         return args;\r
115     }\r
116 \r
117     protected DisposeState getDisposeState() {\r
118         return disposeState;\r
119     }\r
120 \r
121     public ISessionContext getSessionContext() {\r
122         return sessionContext;\r
123     }\r
124 \r
125     public ISessionContextProvider getSessionContextProvider() {\r
126         return contextProvider;\r
127     }\r
128 \r
129     @Override\r
130     public void createPartControl(Composite parent) {\r
131         this.parent = parent;\r
132         this.treeViewer = new TreeViewer(parent, SWT.SINGLE | SWT.FULL_SELECTION);\r
133         this.resourceManager = new LocalResourceManager(JFaceResources.getResources(parent.getDisplay()), treeViewer.getTree());\r
134         Images.getInstance(JFaceResources.getResources());\r
135         TreeColumnLayout ad = new TreeColumnLayout();\r
136         parent.setLayout(ad);\r
137         treeViewer.getTree().setHeaderVisible(true);\r
138         TreeViewerColumn nameColumn = new TreeViewerColumn(treeViewer, SWT.LEFT);\r
139         nameColumn.getColumn().setText("Comment");\r
140         nameColumn.getColumn().setWidth(200);\r
141 //        TreeViewerColumn typeColumn = new TreeViewerColumn(treeViewer, SWT.LEFT);\r
142 \r
143         nameColumn.setLabelProvider(new MyColumnLabelProvider());\r
144 //        typeColumn.setLabelProvider(new MyColumnLabelProvider());\r
145 \r
146         ad.setColumnData(nameColumn.getColumn(), new ColumnWeightData(80, 100));\r
147 //        typeColumn.getColumn().setText("Comment");\r
148 //        typeColumn.getColumn().setWidth(20);\r
149 //        ad.setColumnData(typeColumn.getColumn(), new ColumnWeightData(20, 120));\r
150 \r
151         createControls(parent);\r
152         attachToSession();\r
153 \r
154     }\r
155 \r
156     /**\r
157      * Invoked when this viewpart is disposed. Unhooks the view from its\r
158      * ISessionContextProvider. Overriding is allowed but super.dispose() must\r
159      * be called.\r
160      *\r
161      * @see org.eclipse.ui.part.WorkbenchPart#dispose()\r
162      */\r
163     @Override\r
164     public void dispose() {\r
165         disposeState = DisposeState.Disposing;\r
166         try {\r
167             if (contextProvider != null) {\r
168                 contextProvider.removeContextChangedListener(contextChangeListener);\r
169                 contextProvider = null;\r
170             }\r
171             sessionContextTracker.untrack();\r
172             resourceManager.dispose();\r
173             resourceManager = null;\r
174             args = null;\r
175             sessionContext = null;\r
176             parent = null;\r
177             super.dispose();\r
178         } finally {\r
179            disposeState = DisposeState.Disposed;\r
180         }\r
181     }\r
182 \r
183     @Override\r
184     public void setFocus() {\r
185 \r
186     }\r
187 \r
188     @Override\r
189     public void init(IViewSite site) throws PartInitException {\r
190         super.init(site);\r
191         this.args = ViewArgumentUtils.parseViewArguments(this);\r
192     }\r
193 \r
194     @Override\r
195     public void init(IViewSite site, IMemento memento) throws PartInitException {\r
196         super.init(site, memento);\r
197         this.args = ViewArgumentUtils.parseViewArguments(this);\r
198         this.memento = memento;\r
199     }\r
200 \r
201     @Override\r
202     public void saveState(IMemento memento) {\r
203         if (this.memento != null) {\r
204             memento.putMemento(this.memento);\r
205         }\r
206     }\r
207 \r
208     protected final void attachToSession() {\r
209         // Track active ISessionContext changes\r
210         contextProvider = SimanticsUI.getSessionContextProvider(getViewSite().getWorkbenchWindow());\r
211         contextProvider.addContextChangedListener(contextChangeListener);\r
212 \r
213         // Perform first-time initialization of the explorer and its input.\r
214         applySessionContext(contextProvider.getSessionContext());\r
215     }\r
216 \r
217     private abstract class NameAndDescriptionToolTip extends ToolTip {\r
218         public NameAndDescriptionToolTip(Control control, int style) {\r
219             super(control, style, false);\r
220         }\r
221 \r
222         protected abstract Object getModelElement(Event event);\r
223 \r
224         /**\r
225          * Adds logic to only show a tooltip if a meaningful item is under the cursor.\r
226          */\r
227         @Override\r
228         protected boolean shouldCreateToolTip(Event event) {\r
229             if (!super.shouldCreateToolTip(event))\r
230                 return false;\r
231             Object tableElement = getModelElement(event);\r
232             return tableElement != null && tableElement instanceof DisplayElement;\r
233         }\r
234 \r
235         @Override\r
236         protected Composite createToolTipContentArea(Event event, Composite parent) {\r
237             Object modelElement = getModelElement(event);\r
238 \r
239             Image iconImage = null;\r
240             String nameString = "no name";\r
241 \r
242             if (modelElement instanceof DisplayElement) {\r
243                 iconImage = null;\r
244                 nameString = "name";\r
245             }\r
246 \r
247             // Create the content area\r
248             Composite composite = new Composite(parent, SWT.NONE);\r
249             composite.setBackground(parent.getDisplay().getSystemColor(\r
250                     SWT.COLOR_INFO_BACKGROUND));\r
251             composite.setLayout(new GridLayout(2, false));\r
252 \r
253             // The title area with the icon (if there is one) and label.\r
254             Label title = createEntry(composite, iconImage, nameString);\r
255 //            title.setFont(tooltipHeading);\r
256             GridDataFactory.createFrom((GridData)title.getLayoutData())\r
257                 .hint(SWT.DEFAULT, SWT.DEFAULT)\r
258 //                .minSize(MIN_TOOLTIP_WIDTH, 1)\r
259                 .applyTo(title);\r
260 \r
261             // The description (if there is one)\r
262 //            String descriptionString = "description";\r
263 //            if (descriptionString != null)\r
264 //                createEntry(composite, null, descriptionString);\r
265 \r
266             // Other Content to add\r
267             addContent(composite, modelElement);\r
268 \r
269             return composite;\r
270         }\r
271 \r
272         /**\r
273          * Adds a line of information to <code>parent</code>. If\r
274          * <code>icon</code> is not <code>null</code>, an icon is placed on the\r
275          * left, and then a label with <code>text</code>.\r
276          *\r
277          * @param parent\r
278          *            the composite to add the entry to\r
279          * @param icon\r
280          *            the icon to place next to the text. <code>null</code> for\r
281          *            none.\r
282          * @param text\r
283          *            the text to display\r
284          * @return the created label\r
285          */\r
286         protected Label createEntry(Composite parent, Image icon, String text) {\r
287             if (icon != null) {\r
288                 Label iconLabel = new Label(parent, SWT.NONE);\r
289                 iconLabel.setImage(icon);\r
290                 iconLabel.setBackground(parent.getDisplay().getSystemColor(\r
291                         SWT.COLOR_INFO_BACKGROUND));\r
292                 iconLabel.setData(new GridData());\r
293             }\r
294 \r
295             Label textLabel = new Label(parent, SWT.WRAP);\r
296 \r
297             if(icon == null) {\r
298                 GridDataFactory.generate(textLabel, 2, 1);\r
299             } else {\r
300                 GridDataFactory.generate(textLabel, 1, 1);\r
301             }\r
302 \r
303             textLabel.setText(text);\r
304             textLabel.setBackground(parent.getDisplay().getSystemColor(\r
305                     SWT.COLOR_INFO_BACKGROUND));\r
306             return textLabel;\r
307         }\r
308 \r
309         /**\r
310          * Adds a line of information to <code>parent</code>. If\r
311          * <code>icon</code> is not <code>null</code>, an icon is placed on the\r
312          * left, and then a label with <code>text</code>, which supports using\r
313          * anchor tags to creates links\r
314          *\r
315          * @param parent\r
316          *            the composite to add the entry to\r
317          * @param icon\r
318          *            the icon to place next to the text. <code>null</code> for\r
319          *            none.\r
320          * @param text\r
321          *            the text to display\r
322          * @return the created link\r
323          */\r
324         protected Text createEntryWithText(Composite parent, Image icon, String text) {\r
325             if (icon != null) {\r
326                 Label iconLabel = new Label(parent, SWT.NONE);\r
327                 iconLabel.setImage(icon);\r
328                 iconLabel.setBackground(parent.getDisplay().getSystemColor(\r
329                         SWT.COLOR_INFO_BACKGROUND));\r
330                 iconLabel.setData(new GridData());\r
331             }\r
332             Text texts = new Text(parent, SWT.READ_ONLY | SWT.MULTI | SWT.V_SCROLL);\r
333             if(icon == null) {\r
334                 GridDataFactory.generate(texts, 2, 1);\r
335             }\r
336             texts.setText(text);\r
337             texts.setBackground(parent.getDisplay().getSystemColor(\r
338                     SWT.COLOR_INFO_BACKGROUND));\r
339             return texts;\r
340         }\r
341 \r
342         protected void addContent(Composite destination, Object modelElement) {\r
343         }\r
344     }\r
345 \r
346     private class ItemDetailToolTip extends NameAndDescriptionToolTip {\r
347         private final boolean DEBUG = false;\r
348         private TreeViewer viewer;\r
349         private Tree tree;\r
350         private ItemDetailToolTip(TreeViewer viewer, Tree tree, ViewerFilter filter) {\r
351             super(tree,NO_RECREATE);\r
352             this.tree = tree;\r
353             this.viewer = viewer;\r
354             this.setHideOnMouseDown(false);\r
355         }\r
356         @Override\r
357         public Point getLocation(Point tipSize, Event event) {\r
358             // try to position the tooltip at the bottom of the cell\r
359             ViewerCell cell = viewer.getCell(new Point(event.x, event.y));\r
360             if( cell != null )\r
361                 return tree.toDisplay(event.x,cell.getBounds().y+cell.getBounds().height);\r
362             return super.getLocation(tipSize, event);\r
363         }\r
364         @Override\r
365         protected Object getToolTipArea(Event event) {\r
366             // Ensure that the tooltip is hidden when the cell is left\r
367             return viewer.getCell(new Point(event.x, event.y));\r
368         }\r
369         @Override\r
370         protected void addContent(Composite destination, Object modelElement) {\r
371             final DisplayElement item = (DisplayElement)modelElement;\r
372             if (DEBUG)\r
373                 System.out.println("add content");\r
374             String text = null;\r
375             if (null != item) {\r
376                 text = modelElement.toString();\r
377                 createEntryWithText(destination, null, text.toString());\r
378             }\r
379         }\r
380         @Override\r
381         protected Object getModelElement(Event event) {\r
382             org.eclipse.swt.widgets.TreeItem treeItem = tree.getItem(new Point(event.x, event.y));\r
383             if (treeItem == null)\r
384                 return null;\r
385             return treeItem.getData();\r
386         }\r
387     }\r
388     class MyColumnLabelProvider extends ColumnLabelProvider {\r
389         @Override\r
390         public void update(ViewerCell cell) {\r
391             //NodeProxy proxy = (NodeProxy) cell.getElement();\r
392             cell.setText("Kraa");\r
393             cell.setImage(Images.getInstance().OTHER_IMAGE);\r
394         }\r
395     }\r
396     protected void createControls(final Composite parent) {\r
397         parent.setLayout(LayoutUtils.createNoBorderGridLayout(1, false));\r
398         GridDataFactory.fillDefaults().grab(true, true).applyTo(treeViewer.getTree());\r
399         treeViewer.setContentProvider(new CommentContentProvider(SimanticsUI.getSession()));\r
400         treeViewer.setLabelProvider(new LabelProvider() {\r
401             @Override\r
402             public Image getImage(Object element) {\r
403                 if (element instanceof ChangeSetElement)\r
404                     return Images.getInstance().CHANGE_SET_IMAGE;\r
405                 else if (element instanceof DisplayElement)\r
406                     return Images.getInstance().DISPLAY_IMAGE;\r
407                 else if (element instanceof ResourceElement)\r
408                     return Images.getInstance().RESOURCE_IMAGE;\r
409                 else if (element instanceof StringElement)\r
410                     return Images.getInstance().STRING_IMAGE;\r
411                 else if (element instanceof UndoCombinedElement)\r
412                     return Images.getInstance().COMBINED_IMAGE;\r
413                 else if (element instanceof RedoContextElement)\r
414                     return Images.getInstance().REDO_IMAGE;\r
415                 else if (element instanceof UndoContextElement)\r
416                     return Images.getInstance().UNDO_IMAGE;\r
417                 else\r
418                     return Images.getInstance().OTHER_IMAGE;\r
419             }\r
420             @Override\r
421             public String getText(Object element) {\r
422                 return element.toString();\r
423             }\r
424         });\r
425         //viewer.setSorter(new ViewerSorter());\r
426         treeViewer.setInput(this);\r
427 //        getViewSite().getActionBars().getToolBarManager().add(new Action("GetIncoming", Activator.DOOR_IN_ICON) {\r
428 //            @Override\r
429 //            public void run() {\r
430 //                treeViewer.setContentProvider(new IncomingContentProvider(SimanticsUI.getSession(), parent.getShell()));\r
431 //            }\r
432 //        });\r
433         new ItemDetailToolTip(treeViewer, treeViewer.getTree(), null);\r
434     }\r
435 \r
436     /**\r
437      * @param parent\r
438      * @return\r
439      */\r
440     protected GraphExplorer createExplorerControl(Composite parent) {\r
441         return GraphExplorerFactory.getInstance().selectionDataResolver(new DefaultSelectionDataResolver()).create(parent);\r
442     }\r
443 \r
444     /**\r
445      * Invoke this to reinitialize the explorer and reset its input. The input\r
446      * will be resolved from the specified ISessionContext based on the\r
447      * {@link SessionContextInputSource} that is currently in use. If the input\r
448      * is identical to the previous input, nothing will be done.\r
449      *\r
450      * @param context\r
451      */\r
452     protected final boolean applySessionContext(ISessionContext context) {\r
453         // If control is not alive anymore, do nothing.\r
454         if (disposeState != DisposeState.Alive)\r
455             return false;\r
456 \r
457         this.sessionContext = context;\r
458 \r
459         // Start tracking the session context.\r
460         // This will cause IHintListeners of the sessionContextTracker to fire.\r
461         // For this we need the above input equality (identity) checking.\r
462         sessionContextTracker.track(sessionContext);\r
463         return true;\r
464     }\r
465 }\r
466 class CommentContentProvider implements ITreeContentProvider {\r
467     protected Viewer viewer = null;\r
468     private final Vector<String >comments = new Vector<String>();\r
469     private final Session session;\r
470     private final TeamSupport teamSupport;\r
471     CommentContentProvider(Session session) {\r
472         this.session = session;\r
473         teamSupport = this.session.getService(TeamSupport.class);\r
474         try {\r
475             teamSupport.getComments(Utils.getTeamFolder(), comments);\r
476         } catch (DatabaseException e) {\r
477             comments.add(e.getMessage());\r
478         }\r
479     }\r
480     protected Object[] getElements() {\r
481         final int SIZE = comments.size();\r
482         Object[] objects = new Object[SIZE];\r
483         for (int i=0; i<SIZE; ++i)\r
484             objects[i] = new StagingContextElement(i, comments.get(i));\r
485         return objects;\r
486     }\r
487     @Override\r
488     public void dispose() {\r
489         viewer = null;\r
490     }\r
491     @Override\r
492     public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {\r
493         this.viewer = viewer;\r
494     }\r
495     @Override\r
496     public Object[] getElements(Object inputElement) {\r
497         return getElements();\r
498     }\r
499     @Override\r
500     public Object[] getChildren(Object parentElement) {\r
501         if (parentElement instanceof  TreeElement)\r
502             return ((TreeElement)parentElement).getChildren();\r
503         else\r
504             return null;\r
505     }\r
506     @Override\r
507     public Object getParent(Object childElement) {\r
508             return null;\r
509     }\r
510     @Override\r
511     public boolean hasChildren(Object element) {\r
512         if (element instanceof  TreeElement)\r
513             return ((TreeElement)element).hasChildren();\r
514         return false;\r
515     }\r
516 }\r
517 \r
518 abstract class StagingViewElement extends TreeElement {\r
519     StagingViewElement() {\r
520     }\r
521     @Override\r
522     boolean hasChildren() {\r
523             return false;\r
524     }\r
525     @Override\r
526     Object[] getChildren() {\r
527         return new Object[0];\r
528     }\r
529 }\r
530 \r
531 class StagingContextElement extends StagingViewElement {\r
532     protected String name = "Staging";\r
533     private final String comment;\r
534     StagingContextElement(int index, String comment) {\r
535         this.comment = comment;\r
536     }\r
537     @Override\r
538     protected Image getIdImage() {\r
539         return Images.getInstance().OTHER_IMAGE;\r
540     }\r
541     @Override\r
542     protected String getIdText() {\r
543         String s = toString();\r
544         return s.substring(0, Math.min(10, s.length()));\r
545     }\r
546     @Override\r
547     public String toString() {\r
548         return comment;\r
549     }\r
550 }\r