1 /*******************************************************************************
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
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
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.modeling.ui.diagramEditor;
14 import java.util.ArrayList;
15 import java.util.Collection;
16 import java.util.Collections;
19 import org.eclipse.jface.layout.GridDataFactory;
20 import org.eclipse.jface.layout.GridLayoutFactory;
21 import org.eclipse.jface.viewers.CellLabelProvider;
22 import org.eclipse.jface.viewers.CheckStateChangedEvent;
23 import org.eclipse.jface.viewers.CheckboxTreeViewer;
24 import org.eclipse.jface.viewers.ICheckStateListener;
25 import org.eclipse.jface.viewers.ICheckStateProvider;
26 import org.eclipse.jface.viewers.ISelection;
27 import org.eclipse.jface.viewers.ISelectionChangedListener;
28 import org.eclipse.jface.viewers.ITreeContentProvider;
29 import org.eclipse.jface.viewers.SelectionChangedEvent;
30 import org.eclipse.jface.viewers.TreeViewer;
31 import org.eclipse.jface.viewers.Viewer;
32 import org.eclipse.jface.viewers.ViewerCell;
33 import org.eclipse.swt.SWT;
34 import org.eclipse.swt.custom.TreeEditor;
35 import org.eclipse.swt.events.KeyAdapter;
36 import org.eclipse.swt.events.KeyEvent;
37 import org.eclipse.swt.events.SelectionEvent;
38 import org.eclipse.swt.events.SelectionListener;
39 import org.eclipse.swt.graphics.Color;
40 import org.eclipse.swt.graphics.GC;
41 import org.eclipse.swt.graphics.Point;
42 import org.eclipse.swt.graphics.Rectangle;
43 import org.eclipse.swt.widgets.Button;
44 import org.eclipse.swt.widgets.Composite;
45 import org.eclipse.swt.widgets.Control;
46 import org.eclipse.swt.widgets.Event;
47 import org.eclipse.swt.widgets.Listener;
48 import org.eclipse.swt.widgets.Text;
49 import org.eclipse.swt.widgets.Tree;
50 import org.eclipse.swt.widgets.TreeColumn;
51 import org.eclipse.swt.widgets.TreeItem;
52 import org.eclipse.ui.part.Page;
53 import org.simantics.diagram.layer.ILayersViewPage;
54 import org.simantics.g2d.canvas.ICanvasContext;
55 import org.simantics.g2d.diagram.DiagramHints;
56 import org.simantics.g2d.diagram.IDiagram;
57 import org.simantics.g2d.diagram.participant.Selection;
58 import org.simantics.g2d.element.ElementClass;
59 import org.simantics.g2d.element.IElement;
60 import org.simantics.g2d.element.handler.ElementLayers;
61 import org.simantics.g2d.layers.IEditableLayer;
62 import org.simantics.g2d.layers.ILayer;
63 import org.simantics.g2d.layers.ILayers;
64 import org.simantics.g2d.layers.ILayersEditor;
65 import org.simantics.g2d.layers.SimpleLayer;
66 import org.simantics.g2d.layers.ILayersEditor.ILayersEditorListener;
67 import org.simantics.utils.datastructures.Arrays;
68 import org.simantics.utils.datastructures.hints.HintListenerAdapter;
69 import org.simantics.utils.datastructures.hints.IHintListener;
70 import org.simantics.utils.datastructures.hints.IHintObservable;
71 import org.simantics.utils.datastructures.hints.IHintContext.Key;
72 import org.simantics.utils.ui.ISelectionUtils;
74 public class DiagramLayersPage extends Page implements ILayersViewPage {
76 private static final String TEXT_APPLY_FOCUS_SETTINGS = "Focus Active";
77 private static final String TOOLTIP_APPLY_FOCUS_SETTINGS = "Only Focus Diagram Elements For Active Roles";
78 private static final String TEXT_IGNORE_FOCUS_SETTINGS = "Focus All";
79 private static final String TOOLTIP_IGNORE_FOCUS_SETTINGS = "Focus All Diagram Elements Regardless Of Active Roles";
81 private static final String TEXT_APPLY_VISIBILITY_SETTINGS = "Show Active";
82 private static final String TOOLTIP_APPLY_VISIBILITY_SETTINGS = "Only Show Diagram Elements For Active Roles";
83 private static final String TEXT_IGNORE_VISIBILITY_SETTINGS = "Show All";
84 private static final String TOOLTIP_IGNORE_VISIBILITY_SETTINGS = "Show All Diagram Elements Regardless Of Active Roles";
86 final private ICanvasContext context;
87 final private IDiagram diagram;
88 private CheckboxTreeViewer viewer;
89 private Composite composite;
90 private TreeEditor editor;
92 private Collection<IElement> elements = Collections.emptySet();
104 static Tristate to(Boolean b) {
105 return b == null ? null : b ? True : False;
107 boolean toBoolean() {
110 throw new IllegalStateException("cannot convert Tristate Both to boolean");
130 Tristate merge(Tristate state) {
157 Boolean getAttribute(IElement e, ILayer layer, Attribute attribute) {
158 ElementClass ec = e.getElementClass();
159 for (ElementLayers el : ec.getItemsByClass(ElementLayers.class)) {
162 return Boolean.valueOf(el.isVisible(e, layer));
164 return Boolean.valueOf(el.isFocusable(e, layer));
170 boolean setAttribute(IElement e, ILayer layer, Attribute attribute, boolean value) {
171 ElementClass ec = e.getElementClass();
172 for (ElementLayers el : ec.getItemsByClass(ElementLayers.class)) {
175 return el.setVisibility(e, layer, value);
177 return el.setFocusability(e, layer, value);
183 Tristate getJointAttribute(Collection<IElement> elements, ILayer layer, Attribute attribute) {
184 Tristate state = null;
185 for (IElement e : elements) {
186 Boolean attr = getAttribute(e, layer, attribute);
191 state = Tristate.to(attr);
193 state = state.merge(Tristate.to(attr));
199 int setAttribute(Collection<IElement> elements, ILayer layer, Attribute attribute, boolean value) {
201 for (IElement e : elements) {
202 if (setAttribute(e, layer, attribute, value))
209 final private IHintListener selectionListener = new HintListenerAdapter() {
212 public void hintChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) {
213 Collection<IElement> es = Collections.emptySet();
214 if (newValue instanceof Collection<?>) {
215 Collection<?> coll = (Collection<?>)newValue;
216 es = new ArrayList<IElement>(coll.size());
217 for (Object o : coll) {
218 if (!(o instanceof IElement))
220 es.add((IElement) o);
223 es = Collections.emptySet();
229 private void redraw() {
230 viewer.getControl().getDisplay().asyncExec(new Runnable() {
233 if (viewer.getControl().isDisposed())
235 viewer.getControl().redraw();
241 public DiagramLayersPage(IDiagram diagram, ICanvasContext context) {
243 assert(diagram != null);
245 this.diagram = diagram;
246 this.context = context;
248 context.getDefaultHintContext().addKeyHintListener(Selection.SELECTION0, selectionListener);
252 public void dispose() {
254 context.getDefaultHintContext().removeKeyHintListener(Selection.SELECTION0, selectionListener);
261 public void createControl(Composite parent) {
263 final ILayersEditor layers = diagram.getHint(DiagramHints.KEY_LAYERS_EDITOR);
264 layers.addListener(new ILayersEditorListener() {
267 public void layerRemoved(ILayer layer) {
272 public void layerAdded(ILayer layer) {
277 public void layerActivated(ILayer layer) {
282 public void layerDeactivated(ILayer layer) {
287 public void ignoreFocusChanged(boolean value) {
291 public void ignoreVisibilityChanged(boolean value) {
294 void scheduleRefresh() {
295 viewer.getControl().getDisplay().asyncExec(new Runnable() {
304 composite = new Composite(parent, SWT.NONE);
305 GridLayoutFactory.fillDefaults().numColumns(4).applyTo(composite);
307 Button addButton = new Button(composite, SWT.NONE);
308 addButton.setText("New");
309 addButton.setToolTipText("Create New Diagram Role");
310 addButton.addSelectionListener(new SelectionListener() {
312 String findFreshName(ILayers layers, String proposal) {
313 Set<ILayer> all = layers.getLayers();
314 String name = proposal;
317 boolean match = false;
318 for (ILayer layer : all) {
319 if (name.equals(layer.getName())) {
327 name = proposal + " " + i;
332 public void widgetSelected(SelectionEvent e) {
333 String name = findFreshName(layers, "New Role");
334 SimpleLayer layer = new SimpleLayer(name);
335 layers.addLayer(layer);
336 layers.activate(layer);
340 public void widgetDefaultSelected(SelectionEvent e) {
346 final Button removeButton = new Button(composite, SWT.NONE);
347 removeButton.setText("Remove");
348 removeButton.setToolTipText("Remove Selected Diagram Role");
349 removeButton.addSelectionListener(new SelectionListener() {
351 public void widgetSelected(SelectionEvent e) {
352 TreeItem[] items = viewer.getTree().getSelection();
353 if (items.length == 0)
356 TreeItem[] all = viewer.getTree().getItems();
357 int firstIndex = Arrays.indexOf(all, items[0]);
358 for (TreeItem item : items) {
359 int index = Arrays.indexOf(all, item);
361 ILayer layer = (ILayer)item.getData();
362 layers.removeLayer(layer);
364 int selectIndex = firstIndex - 1;
365 if (firstIndex == 0) {
366 for (int i = firstIndex; i < all.length; ++i)
367 if (all[i] != null) {
372 if (selectIndex >= 0) {
373 viewer.getTree().select(all[selectIndex]);
378 public void widgetDefaultSelected(SelectionEvent e) {
383 String ignoreVisibilityText = TEXT_IGNORE_VISIBILITY_SETTINGS;
384 String ignoreVisibilityTooltip = TOOLTIP_IGNORE_VISIBILITY_SETTINGS;
385 boolean ignoreVisibility = layers.getIgnoreVisibilitySettings();
386 if (ignoreVisibility) {
387 ignoreVisibilityText = TEXT_APPLY_VISIBILITY_SETTINGS;
388 ignoreVisibilityTooltip = TOOLTIP_APPLY_VISIBILITY_SETTINGS;
391 final Button ignoreVisibilityButton = new Button(composite, SWT.NONE);
392 ignoreVisibilityButton.setText(ignoreVisibilityText);
393 ignoreVisibilityButton.setToolTipText(ignoreVisibilityTooltip);
394 ignoreVisibilityButton.addSelectionListener(new SelectionListener() {
397 public void widgetSelected(SelectionEvent e) {
398 String ignoreText = TEXT_IGNORE_VISIBILITY_SETTINGS;
399 String ignoreTooltip= TOOLTIP_IGNORE_VISIBILITY_SETTINGS;
400 boolean ignore = layers.getIgnoreVisibilitySettings();
402 ignoreText = TEXT_APPLY_VISIBILITY_SETTINGS;
403 ignoreTooltip = TOOLTIP_APPLY_VISIBILITY_SETTINGS;
404 layers.setIgnoreVisibilitySettings(true);
406 layers.setIgnoreVisibilitySettings(false);
408 ignoreVisibilityButton.setText(ignoreText);
409 ignoreVisibilityButton.setToolTipText(ignoreTooltip);
411 context.getThreadAccess().asyncExec(new Runnable() {
415 if(context.isDisposed()) return;
416 context.getContentContext().setDirty();
423 public void widgetDefaultSelected(SelectionEvent e) {
429 String ignoreFocusText = TEXT_IGNORE_FOCUS_SETTINGS;
430 String ignoreFocusTooltip = TOOLTIP_IGNORE_FOCUS_SETTINGS;
431 boolean ignoreFocus = layers.getIgnoreFocusSettings();
433 ignoreFocusText = TEXT_APPLY_FOCUS_SETTINGS;
434 ignoreFocusTooltip = TOOLTIP_APPLY_FOCUS_SETTINGS;
437 final Button ignoreFocusButton = new Button(composite, SWT.NONE);
438 ignoreFocusButton.setText(ignoreFocusText);
439 ignoreFocusButton.setToolTipText(ignoreFocusTooltip);
440 ignoreFocusButton.addSelectionListener(new SelectionListener() {
443 public void widgetSelected(SelectionEvent e) {
444 String ignoreText = TEXT_IGNORE_FOCUS_SETTINGS;
445 String ignoreTooltip = TOOLTIP_IGNORE_FOCUS_SETTINGS;
446 boolean ignore = layers.getIgnoreFocusSettings();
448 ignoreText = TEXT_APPLY_FOCUS_SETTINGS;
449 ignoreTooltip = TOOLTIP_APPLY_FOCUS_SETTINGS;
450 layers.setIgnoreFocusSettings(true);
452 layers.setIgnoreFocusSettings(false);
454 ignoreFocusButton.setText(ignoreText);
455 ignoreFocusButton.setToolTipText(ignoreTooltip);
457 context.getThreadAccess().asyncExec(new Runnable() {
461 if(context.isDisposed()) return;
462 context.getContentContext().setDirty();
469 public void widgetDefaultSelected(SelectionEvent e) {
475 viewer = new CheckboxTreeViewer(composite, SWT.BORDER | SWT.FULL_SELECTION );
477 GridDataFactory.fillDefaults().grab(true, true).span(4, 1).applyTo(viewer.getControl());
478 viewer.getControl().setToolTipText("Selects the diagram to include in the exported document.");
479 viewer.setAutoExpandLevel(TreeViewer.ALL_LEVELS);
480 viewer.getTree().setHeaderVisible(true);
481 editor = new TreeEditor(viewer.getTree());
483 final TreeColumn column1 = new TreeColumn(viewer.getTree(), SWT.LEFT);
484 column1.setText("Role");
485 column1.setWidth(100);
486 final TreeColumn column2 = new TreeColumn(viewer.getTree(), SWT.LEFT);
487 column2.setText("Show");
488 column2.setWidth(50);
489 final TreeColumn column3 = new TreeColumn(viewer.getTree(), SWT.LEFT);
490 column3.setText("Focus");
491 column3.setWidth(50);
492 viewer.getTree().addListener(SWT.Resize, new Listener() {
494 public void handleEvent(Event event) {
495 Tree tree = viewer.getTree();
496 Point size = tree.getSize();
497 int w = Math.max(size.x - 100 - tree.getBorderWidth() * 2, 30);
502 viewer.getTree().addListener(SWT.PaintItem, new Listener() {
503 public void handleEvent(Event event) {
504 if ((event.index == 1 || event.index == 2) && !elements.isEmpty()) {
505 ILayer[] lz = layers.getLayers().toArray(new ILayer[0]);
507 TreeItem item = (TreeItem)event.item;
508 int index = viewer.getTree().indexOf(item);
511 if (event.index == 1)
512 width = (column2.getWidth() - 1);
513 if (event.index == 2)
514 width = (column3.getWidth() - 1);
516 Attribute attribute = Attribute.Visible;
517 if (event.index == 2)
518 attribute = Attribute.Focusable;
519 Tristate state = getJointAttribute(elements, lz[index], attribute);
524 color = viewer.getTree().getDisplay().getSystemColor(SWT.COLOR_RED);
527 color = viewer.getTree().getDisplay().getSystemColor(SWT.COLOR_GREEN);
530 color = viewer.getTree().getDisplay().getSystemColor(SWT.COLOR_GRAY);
535 Color foreground = gc.getForeground();
536 Color background = gc.getBackground();
537 gc.setBackground(color);
538 gc.setForeground(viewer.getTree().getDisplay().getSystemColor(SWT.COLOR_BLACK));
539 gc.fillRectangle(event.x, event.y, width-1, event.height-1);
540 Rectangle rect2 = new Rectangle(event.x, event.y, width-1, event.height-1);
541 gc.drawRectangle(rect2);
542 gc.setForeground(background);
543 gc.setBackground(foreground);
547 viewer.getTree().addKeyListener(new KeyAdapter() {
549 public void keyPressed(KeyEvent event) {
550 if (event.keyCode == SWT.F2) {
551 // FIXME: Eclipse currently eats F2 presses. This should be
552 // implemented as a command handler or find some way to
553 // force these listeners to have priority...
554 System.out.println("startediting");
556 TreeItem[] items = viewer.getTree().getSelection();
557 if(items.length != 1)
560 TreeItem item = items[0];
562 ILayer layer = ISelectionUtils.filterSingleSelection(viewer.getSelection(), ILayer.class);
566 startEditing(layer, item);
570 viewer.getTree().addListener(SWT.MouseDown, new Listener() {
571 public void handleEvent(Event event) {
572 if (viewer.getControl().isDisposed())
575 Point pt = new Point(event.x, event.y);
576 TreeItem item = viewer.getTree().getItem(pt);
580 int index = viewer.getTree().indexOf(item);
581 ILayer[] lz = layers.getLayers().toArray(new ILayer[0]);
582 ILayer layer = lz[index];
584 Rectangle rect = item.getBounds(0);
585 if (event.count == 2 && rect.contains(pt)) {
586 startEditing(layer, item);
590 // Cannot adjust visibility/focusability if no elements are selected.
591 if (elements.isEmpty())
594 rect = item.getBounds(1);
595 if (rect.contains(pt)) {
596 Tristate state = getJointAttribute(elements, layer, Attribute.Visible);
597 if (setAttribute(elements, layer, Attribute.Visible, state.toggle().toBoolean()) > 0) {
603 Rectangle rect2 = item.getBounds(2);
604 if (rect2.contains(pt)) {
605 Tristate state = getJointAttribute(elements, layer, Attribute.Focusable);
606 if (setAttribute(elements, layer, Attribute.Focusable, state.toggle().toBoolean()) > 0) {
613 private void refresh() {
614 viewer.getControl().redraw();
615 context.getThreadAccess().asyncExec(new Runnable() {
618 if (context.isDisposed())
620 context.getContentContext().setDirty();
626 viewer.addCheckStateListener(new ICheckStateListener(){
628 public void checkStateChanged(CheckStateChangedEvent event) {
629 ILayer layer = (ILayer)event.getElement();
630 if(event.getChecked()) layers.activate(layer);
631 else layers.deactivate(layer);
632 viewer.setSubtreeChecked(event.getElement(), event.getChecked());
633 context.getThreadAccess().asyncExec(new Runnable() {
637 if(context.isDisposed()) return;
638 context.getContentContext().setDirty();
645 viewer.addSelectionChangedListener(new ISelectionChangedListener() {
647 public void selectionChanged(SelectionChangedEvent event) {
648 ISelection s = event.getSelection();
650 removeButton.setEnabled(false);
652 removeButton.setEnabled(true);
657 viewer.setContentProvider(new ITreeContentProvider(){
659 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
662 public void dispose() {
665 public Object[] getElements(Object inputElement) {
666 return layers.getLayers().toArray();
669 public boolean hasChildren(Object element) {
673 public Object getParent(Object element) {
677 public Object[] getChildren(Object parentElement) {
678 return new Object[0];
681 viewer.setLabelProvider(new CellLabelProvider() {
683 public void update(ViewerCell cell) {
684 if(cell.getColumnIndex() == 0) {
685 ILayer layer = (ILayer)cell.getElement();
686 cell.setText(layer.getName());
692 viewer.setCheckStateProvider(new ICheckStateProvider() {
694 public boolean isChecked(Object element) {
695 ILayer layer = (ILayer)element;
696 final boolean isActive = layers.isActive(layer);
700 public boolean isGrayed(Object element) {
705 viewer.setInput(this);
707 for(ILayer layer : layers.getVisibleLayers()) {
708 viewer.setSubtreeChecked(layer, true);
713 public Control getControl() {
718 public void setFocus() {
722 public void addSelectionChangedListener(ISelectionChangedListener listener) {
726 public ISelection getSelection() {
731 public void removeSelectionChangedListener(ISelectionChangedListener listener) {
735 public void setSelection(ISelection selection) {
738 private boolean startEditing(final ILayer layer, final TreeItem item/*, final int columnIndex*/) {
740 // Column column = columns[columnIndex];
742 String initialText = layer.getName();
744 final Composite composite = new Composite(viewer.getTree(), SWT.NONE);
745 final Text text = new Text(composite, SWT.BORDER);
746 final int insetX = 0;
747 final int insetY = 0;
748 composite.addListener(SWT.Resize, new Listener() {
749 public void handleEvent(Event e) {
750 Rectangle rect = composite.getClientArea();
751 text.setBounds(rect.x + insetX, rect.y + insetY, rect.width - insetX * 2, rect.height
755 Listener textListener = new Listener() {
756 public void handleEvent(final Event e) {
761 if(layer instanceof IEditableLayer) {
762 IEditableLayer l = (IEditableLayer)layer;
763 l.setName(text.getText());
764 System.out.println("renamed layer to " + text.getText());
768 // // Item may be disposed if the tree gets reset after a previous editing.
769 // if (!item.isDisposed()) {
770 // item.setText(columnIndex, text.getText());
771 // queueSelectionRefresh(context);
774 // // System.out.println("validation error: " + error);
779 // newText = text.getText();
780 // error = modifier.isValid(newText);
781 // if (error != null) {
782 // text.setBackground(invalidModificationColor);
783 // // System.out.println("validation error: " + error);
785 // text.setBackground(null);
789 newText = text.getText();
790 String leftText = newText.substring(0, e.start);
791 String rightText = newText.substring(e.end, newText.length());
792 GC gc = new GC(text);
793 Point size = gc.textExtent(leftText + e.text + rightText);
795 size = text.computeSize(size.x, SWT.DEFAULT);
796 editor.horizontalAlignment = SWT.LEFT;
797 Rectangle itemRect = item.getBounds(0),
798 rect = viewer.getTree().getClientArea();
799 editor.minimumWidth = Math.max(size.x, itemRect.width) + insetX * 2;
800 int left = itemRect.x,
801 right = rect.x + rect.width;
802 editor.minimumWidth = Math.min(editor.minimumWidth, right - left);
803 editor.minimumHeight = size.y + insetY * 2;
808 case SWT.TRAVERSE_RETURN:
809 if(layer instanceof IEditableLayer) {
810 IEditableLayer l = (IEditableLayer)layer;
811 l.setName(text.getText());
812 //System.out.println("renamed layer to " + text.getText());
815 // error = modifier.isValid(text.getText());
816 // if (error == null) {
817 // modifier.modify(text.getText());
818 // if (!item.isDisposed()) {
819 // item.setText(columnIndex, text.getText());
820 // queueSelectionRefresh(context);
824 case SWT.TRAVERSE_ESCAPE:
829 //System.out.println("unhandled traversal: " + e.detail);
836 text.addListener(SWT.FocusOut, textListener);
837 text.addListener(SWT.Traverse, textListener);
838 text.addListener(SWT.Verify, textListener);
839 text.addListener(SWT.Modify, textListener);
840 editor.setEditor(composite, item, 0);
841 text.setText(initialText);
844 // lastItem[0] = item;