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