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
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.team.ui;
\r
14 import java.util.Map;
\r
15 import java.util.Vector;
\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
72 * @author Kalle Kondelin
\r
74 public class StagingView extends ViewPart {
\r
76 public StagingView() {
\r
79 public class SessionContextProjectTracker extends HintTracker {
\r
80 public SessionContextProjectTracker() {
\r
81 IHintListener activeProjectListener = new HintListenerAdapter() {
\r
83 public void hintChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) {
\r
84 applySessionContext(getSessionContext());
\r
87 addKeyHintListener(ProjectKeys.KEY_PROJECT, activeProjectListener);
\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
101 protected ISessionContextChangedListener contextChangeListener = new ISessionContextChangedListener() {
\r
103 public void sessionContextChanged(SessionContextChangedEvent event) {
\r
104 sessionContext = event.getNewValue();
\r
105 sessionContextTracker.track(sessionContext);
\r
109 protected void setSessionContextTracker(IHintTracker tracker) {
\r
110 this.sessionContextTracker = tracker;
\r
113 protected Map<String, String> getViewArguments() {
\r
117 protected DisposeState getDisposeState() {
\r
118 return disposeState;
\r
121 public ISessionContext getSessionContext() {
\r
122 return sessionContext;
\r
125 public ISessionContextProvider getSessionContextProvider() {
\r
126 return contextProvider;
\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
143 nameColumn.setLabelProvider(new MyColumnLabelProvider());
\r
144 // typeColumn.setLabelProvider(new MyColumnLabelProvider());
\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
151 createControls(parent);
\r
157 * Invoked when this viewpart is disposed. Unhooks the view from its
\r
158 * ISessionContextProvider. Overriding is allowed but super.dispose() must
\r
161 * @see org.eclipse.ui.part.WorkbenchPart#dispose()
\r
164 public void dispose() {
\r
165 disposeState = DisposeState.Disposing;
\r
167 if (contextProvider != null) {
\r
168 contextProvider.removeContextChangedListener(contextChangeListener);
\r
169 contextProvider = null;
\r
171 sessionContextTracker.untrack();
\r
172 resourceManager.dispose();
\r
173 resourceManager = null;
\r
175 sessionContext = null;
\r
179 disposeState = DisposeState.Disposed;
\r
184 public void setFocus() {
\r
189 public void init(IViewSite site) throws PartInitException {
\r
191 this.args = ViewArgumentUtils.parseViewArguments(this);
\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
202 public void saveState(IMemento memento) {
\r
203 if (this.memento != null) {
\r
204 memento.putMemento(this.memento);
\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
213 // Perform first-time initialization of the explorer and its input.
\r
214 applySessionContext(contextProvider.getSessionContext());
\r
217 private abstract class NameAndDescriptionToolTip extends ToolTip {
\r
218 public NameAndDescriptionToolTip(Control control, int style) {
\r
219 super(control, style, false);
\r
222 protected abstract Object getModelElement(Event event);
\r
225 * Adds logic to only show a tooltip if a meaningful item is under the cursor.
\r
228 protected boolean shouldCreateToolTip(Event event) {
\r
229 if (!super.shouldCreateToolTip(event))
\r
231 Object tableElement = getModelElement(event);
\r
232 return tableElement != null && tableElement instanceof DisplayElement;
\r
236 protected Composite createToolTipContentArea(Event event, Composite parent) {
\r
237 Object modelElement = getModelElement(event);
\r
239 Image iconImage = null;
\r
240 String nameString = "no name";
\r
242 if (modelElement instanceof DisplayElement) {
\r
244 nameString = "name";
\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
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
261 // The description (if there is one)
\r
262 // String descriptionString = "description";
\r
263 // if (descriptionString != null)
\r
264 // createEntry(composite, null, descriptionString);
\r
266 // Other Content to add
\r
267 addContent(composite, modelElement);
\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
278 * the composite to add the entry to
\r
280 * the icon to place next to the text. <code>null</code> for
\r
283 * the text to display
\r
284 * @return the created label
\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
295 Label textLabel = new Label(parent, SWT.WRAP);
\r
298 GridDataFactory.generate(textLabel, 2, 1);
\r
300 GridDataFactory.generate(textLabel, 1, 1);
\r
303 textLabel.setText(text);
\r
304 textLabel.setBackground(parent.getDisplay().getSystemColor(
\r
305 SWT.COLOR_INFO_BACKGROUND));
\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
316 * the composite to add the entry to
\r
318 * the icon to place next to the text. <code>null</code> for
\r
321 * the text to display
\r
322 * @return the created link
\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
332 Text texts = new Text(parent, SWT.READ_ONLY | SWT.MULTI | SWT.V_SCROLL);
\r
334 GridDataFactory.generate(texts, 2, 1);
\r
336 texts.setText(text);
\r
337 texts.setBackground(parent.getDisplay().getSystemColor(
\r
338 SWT.COLOR_INFO_BACKGROUND));
\r
342 protected void addContent(Composite destination, Object modelElement) {
\r
346 private class ItemDetailToolTip extends NameAndDescriptionToolTip {
\r
347 private final boolean DEBUG = false;
\r
348 private TreeViewer viewer;
\r
350 private ItemDetailToolTip(TreeViewer viewer, Tree tree, ViewerFilter filter) {
\r
351 super(tree,NO_RECREATE);
\r
353 this.viewer = viewer;
\r
354 this.setHideOnMouseDown(false);
\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
361 return tree.toDisplay(event.x,cell.getBounds().y+cell.getBounds().height);
\r
362 return super.getLocation(tipSize, event);
\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
370 protected void addContent(Composite destination, Object modelElement) {
\r
371 final DisplayElement item = (DisplayElement)modelElement;
\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
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
385 return treeItem.getData();
\r
388 class MyColumnLabelProvider extends ColumnLabelProvider {
\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
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
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
418 return Images.getInstance().OTHER_IMAGE;
\r
421 public String getText(Object element) {
\r
422 return element.toString();
\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
429 // public void run() {
\r
430 // treeViewer.setContentProvider(new IncomingContentProvider(SimanticsUI.getSession(), parent.getShell()));
\r
433 new ItemDetailToolTip(treeViewer, treeViewer.getTree(), null);
\r
440 protected GraphExplorer createExplorerControl(Composite parent) {
\r
441 return GraphExplorerFactory.getInstance().selectionDataResolver(new DefaultSelectionDataResolver()).create(parent);
\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
452 protected final boolean applySessionContext(ISessionContext context) {
\r
453 // If control is not alive anymore, do nothing.
\r
454 if (disposeState != DisposeState.Alive)
\r
457 this.sessionContext = context;
\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
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
475 teamSupport.getComments(Utils.getTeamFolder(), comments);
\r
476 } catch (DatabaseException e) {
\r
477 comments.add(e.getMessage());
\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
488 public void dispose() {
\r
492 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
\r
493 this.viewer = viewer;
\r
496 public Object[] getElements(Object inputElement) {
\r
497 return getElements();
\r
500 public Object[] getChildren(Object parentElement) {
\r
501 if (parentElement instanceof TreeElement)
\r
502 return ((TreeElement)parentElement).getChildren();
\r
507 public Object getParent(Object childElement) {
\r
511 public boolean hasChildren(Object element) {
\r
512 if (element instanceof TreeElement)
\r
513 return ((TreeElement)element).hasChildren();
\r
518 abstract class StagingViewElement extends TreeElement {
\r
519 StagingViewElement() {
\r
522 boolean hasChildren() {
\r
526 Object[] getChildren() {
\r
527 return new Object[0];
\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
538 protected Image getIdImage() {
\r
539 return Images.getInstance().OTHER_IMAGE;
\r
542 protected String getIdText() {
\r
543 String s = toString();
\r
544 return s.substring(0, Math.min(10, s.length()));
\r
547 public String toString() {
\r