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.ILayers.ILayersListener;
65 import org.simantics.g2d.layers.ILayersEditor;
66 import org.simantics.g2d.layers.SimpleLayer;
67 import org.simantics.utils.datastructures.Arrays;
68 import org.simantics.utils.datastructures.disposable.IDisposable;
69 import org.simantics.utils.datastructures.disposable.IDisposeListener;
70 import org.simantics.utils.datastructures.hints.HintListenerAdapter;
71 import org.simantics.utils.datastructures.hints.IHintContext.Key;
72 import org.simantics.utils.datastructures.hints.IHintListener;
73 import org.simantics.utils.datastructures.hints.IHintObservable;
74 import org.simantics.utils.ui.ISelectionUtils;
76 public class DiagramLayersPage extends Page implements ILayersViewPage {
78 private static final String TEXT_IGNORE_FOCUS_SETTINGS = Messages.DiagramLayersPage_FocusAll;
79 private static final String TOOLTIP_IGNORE_FOCUS_SETTINGS = Messages.DiagramLayersPage_FocusAllTT;
81 private static final String TEXT_IGNORE_VISIBILITY_SETTINGS = Messages.DiagramLayersPage_ShowAll;
82 private static final String TOOLTIP_IGNORE_VISIBILITY_SETTINGS = Messages.DiagramLayersPage_ShowAllTT;
84 final private ICanvasContext context;
85 private CheckboxTreeViewer viewer;
86 private Button ignoreVisibilityButton;
87 private Button ignoreFocusButton;
88 private Composite composite;
89 private TreeEditor editor;
91 private Collection<IElement> elements = Collections.emptySet();
92 private ILayersEditor layers;
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"); //$NON-NLS-1$
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 if (viewer != null) {
231 viewer.getControl().getDisplay().asyncExec(new Runnable() {
234 if (viewer.getControl().isDisposed())
236 viewer.getControl().redraw();
243 final private IDisposeListener contextDisposeListener = new IDisposeListener() {
246 public void onDisposed(IDisposable sender) {
247 if (getControl() != null) getControl().getDisplay().asyncExec(new Runnable() {
256 public DiagramLayersPage(ICanvasContext context) {
257 this.context = context;
259 context.getDefaultHintContext().addKeyHintListener(Selection.SELECTION0, selectionListener);
260 context.addDisposeListener(contextDisposeListener);
264 public void dispose() {
266 context.getDefaultHintContext().removeKeyHintListener(Selection.SELECTION0, selectionListener);
267 context.removeDisposeListener(contextDisposeListener);
268 if (layers != null && layersListener != null) {
269 layers.removeLayersListener(layersListener);
270 layersListener = null;
277 public void createControl(Composite parent) {
278 composite = new Composite(parent, SWT.NONE);
279 GridLayoutFactory.fillDefaults().numColumns(4).applyTo(composite);
281 IDiagram diagram = context.getDefaultHintContext().getHint(DiagramHints.KEY_DIAGRAM);
282 if (diagram != null) onDiagramSet(diagram);
283 context.getDefaultHintContext().addKeyHintListener(DiagramHints.KEY_DIAGRAM, new IHintListener() {
285 public void hintChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) {
286 IDiagram diagram = (IDiagram)newValue;
287 onDiagramSet(diagram);
291 public void hintRemoved(IHintObservable sender, Key key, Object oldValue) {
297 private void onDiagramSet(IDiagram diagram) {
298 if (diagram != null) {
299 layers = diagram.getHint(DiagramHints.KEY_LAYERS_EDITOR);
300 if (layers != null) initialize(layers, diagram);
301 diagram.addKeyHintListener(DiagramHints.KEY_LAYERS_EDITOR, new IHintListener() {
305 public void hintChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) {
306 if (newValue != null) {
307 initialize(layers, diagram);
311 public void hintRemoved(IHintObservable sender, Key key, Object oldValue) {
312 // TODO Auto-generated method stub
313 System.out.println("REM");
320 private void initialize(ILayersEditor layers, IDiagram diagram) {
321 composite.getDisplay().asyncExec(new Runnable() {
324 initialize2(layers, diagram);
329 private ILayersListener layersListener = new ILayersListener() {
332 public void changed() {
336 void scheduleRefresh() {
337 viewer.getControl().getDisplay().asyncExec(new Runnable() {
341 ignoreVisibilityButton.setSelection(layers.getIgnoreVisibilitySettings());
342 ignoreFocusButton.setSelection(layers.getIgnoreFocusSettings());
343 if (!context.isDisposed())
344 context.getContentContext().setDirty();
350 private void initialize2(ILayersEditor layers, IDiagram diagram) {
351 layers.addLayersListener(layersListener);
353 Button addButton = new Button(composite, SWT.NONE);
354 addButton.setText(Messages.DiagramLayersPage_New);
355 addButton.setToolTipText(Messages.DiagramLayersPage_NewTT);
356 addButton.addSelectionListener(new SelectionListener() {
358 String findFreshName(ILayers layers, String proposal) {
359 Set<ILayer> all = layers.getLayers();
360 String name = proposal;
363 boolean match = false;
364 for (ILayer layer : all) {
365 if (name.equals(layer.getName())) {
373 name = proposal + " " + i; //$NON-NLS-1$
378 public void widgetSelected(SelectionEvent e) {
379 String name = findFreshName(layers, Messages.DiagramLayersPage_NewRole);
380 SimpleLayer layer = new SimpleLayer(name);
381 layers.addLayer(layer);
382 layers.activate(layer);
386 public void widgetDefaultSelected(SelectionEvent e) {
392 final Button removeButton = new Button(composite, SWT.NONE);
393 removeButton.setText(Messages.DiagramLayersPage_Remove);
394 removeButton.setToolTipText(Messages.DiagramLayersPage_RemoveTT);
395 removeButton.addSelectionListener(new SelectionListener() {
397 public void widgetSelected(SelectionEvent e) {
398 TreeItem[] items = viewer.getTree().getSelection();
399 if (items.length == 0)
402 TreeItem[] all = viewer.getTree().getItems();
403 int firstIndex = Arrays.indexOf(all, items[0]);
404 for (TreeItem item : items) {
405 int index = Arrays.indexOf(all, item);
407 ILayer layer = (ILayer)item.getData();
408 layers.removeLayer(layer);
410 int selectIndex = firstIndex - 1;
411 if (firstIndex == 0) {
412 for (int i = firstIndex; i < all.length; ++i)
413 if (all[i] != null) {
418 if (selectIndex >= 0) {
419 viewer.getTree().select(all[selectIndex]);
421 context.getThreadAccess().asyncExec(new Runnable() {
425 if(context.isDisposed()) return;
426 context.getContentContext().setDirty();
433 public void widgetDefaultSelected(SelectionEvent e) {
438 ignoreVisibilityButton = new Button(composite, SWT.CHECK);
439 ignoreVisibilityButton.setText(TEXT_IGNORE_VISIBILITY_SETTINGS);
440 ignoreVisibilityButton.setToolTipText(TOOLTIP_IGNORE_VISIBILITY_SETTINGS);
441 ignoreVisibilityButton.setSelection(layers.getIgnoreVisibilitySettings());
442 ignoreVisibilityButton.addSelectionListener(new SelectionListener() {
445 public void widgetSelected(SelectionEvent e) {
446 layers.setIgnoreVisibilitySettings(!layers.getIgnoreVisibilitySettings());
447 context.getThreadAccess().asyncExec(new Runnable() {
451 if(context.isDisposed()) return;
452 context.getContentContext().setDirty();
459 public void widgetDefaultSelected(SelectionEvent e) {
465 ignoreFocusButton = new Button(composite, SWT.CHECK);
466 ignoreFocusButton.setText(TEXT_IGNORE_FOCUS_SETTINGS);
467 ignoreFocusButton.setToolTipText(TOOLTIP_IGNORE_FOCUS_SETTINGS);
468 ignoreFocusButton.setSelection(layers.getIgnoreFocusSettings());
469 ignoreFocusButton.addSelectionListener(new SelectionListener() {
472 public void widgetSelected(SelectionEvent e) {
473 layers.setIgnoreFocusSettings(!layers.getIgnoreFocusSettings());
474 context.getThreadAccess().asyncExec(new Runnable() {
478 if(context.isDisposed()) return;
479 context.getContentContext().setDirty();
486 public void widgetDefaultSelected(SelectionEvent e) {
492 viewer = new CheckboxTreeViewer(composite, SWT.BORDER | SWT.FULL_SELECTION );
494 GridDataFactory.fillDefaults().grab(true, true).span(4, 1).applyTo(viewer.getControl());
495 viewer.getControl().setToolTipText(Messages.DiagramLayersPage_SelectTT);
496 viewer.setAutoExpandLevel(TreeViewer.ALL_LEVELS);
497 viewer.getTree().setHeaderVisible(true);
498 editor = new TreeEditor(viewer.getTree());
500 final TreeColumn column1 = new TreeColumn(viewer.getTree(), SWT.LEFT);
501 column1.setText(Messages.DiagramLayersPage_Role);
502 column1.setWidth(100);
503 final TreeColumn column2 = new TreeColumn(viewer.getTree(), SWT.LEFT);
504 column2.setText(Messages.DiagramLayersPage_Show);
505 column2.setWidth(50);
506 final TreeColumn column3 = new TreeColumn(viewer.getTree(), SWT.LEFT);
507 column3.setText(Messages.DiagramLayersPage_Focus);
508 column3.setWidth(50);
509 viewer.getTree().addListener(SWT.Resize, new Listener() {
511 public void handleEvent(Event event) {
512 Tree tree = viewer.getTree();
513 Point size = tree.getSize();
514 int w = Math.max(size.x - 100 - tree.getBorderWidth() * 2, 30);
519 viewer.getTree().addListener(SWT.PaintItem, new Listener() {
520 public void handleEvent(Event event) {
521 if ((event.index == 1 || event.index == 2) && !elements.isEmpty()) {
522 ILayer[] lz = layers.getLayers().toArray(new ILayer[0]);
524 TreeItem item = (TreeItem)event.item;
525 int index = viewer.getTree().indexOf(item);
528 if (event.index == 1)
529 width = (column2.getWidth() - 1);
530 if (event.index == 2)
531 width = (column3.getWidth() - 1);
533 Attribute attribute = Attribute.Visible;
534 if (event.index == 2)
535 attribute = Attribute.Focusable;
536 Tristate state = getJointAttribute(elements, lz[index], attribute);
540 color = viewer.getTree().getDisplay().getSystemColor(SWT.COLOR_GRAY);
544 color = viewer.getTree().getDisplay().getSystemColor(SWT.COLOR_RED);
547 color = viewer.getTree().getDisplay().getSystemColor(SWT.COLOR_GREEN);
550 color = viewer.getTree().getDisplay().getSystemColor(SWT.COLOR_GRAY);
556 Color foreground = gc.getForeground();
557 Color background = gc.getBackground();
558 gc.setBackground(color);
559 gc.setForeground(viewer.getTree().getDisplay().getSystemColor(SWT.COLOR_BLACK));
560 gc.fillRectangle(event.x, event.y, width-1, event.height-1);
561 Rectangle rect2 = new Rectangle(event.x, event.y, width-1, event.height-1);
562 gc.drawRectangle(rect2);
563 gc.setForeground(background);
564 gc.setBackground(foreground);
568 viewer.getTree().addKeyListener(new KeyAdapter() {
570 public void keyPressed(KeyEvent event) {
571 if (event.keyCode == SWT.F2) {
572 // FIXME: Eclipse currently eats F2 presses. This should be
573 // implemented as a command handler or find some way to
574 // force these listeners to have priority...
575 System.out.println("startediting"); //$NON-NLS-1$
577 TreeItem[] items = viewer.getTree().getSelection();
578 if(items.length != 1)
581 TreeItem item = items[0];
583 ILayer layer = ISelectionUtils.filterSingleSelection(viewer.getSelection(), ILayer.class);
587 startEditing(layer, item);
591 viewer.getTree().addListener(SWT.MouseDown, new Listener() {
592 public void handleEvent(Event event) {
593 if (viewer.getControl().isDisposed())
596 Point pt = new Point(event.x, event.y);
597 TreeItem item = viewer.getTree().getItem(pt);
601 int index = viewer.getTree().indexOf(item);
602 ILayer[] lz = layers.getLayers().toArray(new ILayer[0]);
603 ILayer layer = lz[index];
605 Rectangle rect = item.getBounds(0);
606 if (event.count == 2 && rect.contains(pt)) {
607 startEditing(layer, item);
611 // Cannot adjust visibility/focusability if no elements are selected.
612 if (elements.isEmpty())
615 rect = item.getBounds(1);
616 if (rect.contains(pt)) {
617 Tristate state = getJointAttribute(elements, layer, Attribute.Visible);
618 if (setAttribute(elements, layer, Attribute.Visible, state.toggle().toBoolean()) > 0) {
624 Rectangle rect2 = item.getBounds(2);
625 if (rect2.contains(pt)) {
626 Tristate state = getJointAttribute(elements, layer, Attribute.Focusable);
627 if (setAttribute(elements, layer, Attribute.Focusable, state.toggle().toBoolean()) > 0) {
634 private void refresh() {
635 viewer.getControl().redraw();
636 context.getThreadAccess().asyncExec(new Runnable() {
639 if (context.isDisposed())
641 context.getContentContext().setDirty();
647 viewer.addCheckStateListener(new ICheckStateListener(){
649 public void checkStateChanged(CheckStateChangedEvent event) {
650 ILayer layer = (ILayer)event.getElement();
651 if(event.getChecked()) layers.activate(layer);
652 else layers.deactivate(layer);
653 viewer.setSubtreeChecked(event.getElement(), event.getChecked());
654 context.getThreadAccess().asyncExec(new Runnable() {
658 if(context.isDisposed()) return;
659 context.getContentContext().setDirty();
666 viewer.addSelectionChangedListener(new ISelectionChangedListener() {
668 public void selectionChanged(SelectionChangedEvent event) {
669 ISelection s = event.getSelection();
671 removeButton.setEnabled(false);
673 removeButton.setEnabled(true);
678 viewer.setContentProvider(new ITreeContentProvider(){
680 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
683 public void dispose() {
686 public Object[] getElements(Object inputElement) {
687 return layers.getLayers().toArray();
690 public boolean hasChildren(Object element) {
694 public Object getParent(Object element) {
698 public Object[] getChildren(Object parentElement) {
699 return new Object[0];
702 viewer.setLabelProvider(new CellLabelProvider() {
704 public void update(ViewerCell cell) {
705 if(cell.getColumnIndex() == 0) {
706 ILayer layer = (ILayer)cell.getElement();
707 cell.setText(layer.getName());
709 cell.setText(""); //$NON-NLS-1$
713 viewer.setCheckStateProvider(new ICheckStateProvider() {
715 public boolean isChecked(Object element) {
716 ILayer layer = (ILayer)element;
717 final boolean isActive = layers.isActive(layer);
721 public boolean isGrayed(Object element) {
726 viewer.setInput(this);
728 for(ILayer layer : layers.getVisibleLayers()) {
729 viewer.setSubtreeChecked(layer, true);
736 public Control getControl() {
741 public void setFocus() {
745 public void addSelectionChangedListener(ISelectionChangedListener listener) {
749 public ISelection getSelection() {
754 public void removeSelectionChangedListener(ISelectionChangedListener listener) {
758 public void setSelection(ISelection selection) {
761 private boolean startEditing(final ILayer layer, final TreeItem item/*, final int columnIndex*/) {
763 // Column column = columns[columnIndex];
765 String initialText = layer.getName();
767 final Composite composite = new Composite(viewer.getTree(), SWT.NONE);
768 final Text text = new Text(composite, SWT.BORDER);
769 final int insetX = 0;
770 final int insetY = 0;
771 composite.addListener(SWT.Resize, new Listener() {
772 public void handleEvent(Event e) {
773 Rectangle rect = composite.getClientArea();
774 text.setBounds(rect.x + insetX, rect.y + insetY, rect.width - insetX * 2, rect.height
778 Listener textListener = new Listener() {
779 public void handleEvent(final Event e) {
784 if(layer instanceof IEditableLayer) {
785 IEditableLayer l = (IEditableLayer)layer;
786 l.setName(text.getText());
787 System.out.println("renamed layer to " + text.getText()); //$NON-NLS-1$
791 // // Item may be disposed if the tree gets reset after a previous editing.
792 // if (!item.isDisposed()) {
793 // item.setText(columnIndex, text.getText());
794 // queueSelectionRefresh(context);
797 // // System.out.println("validation error: " + error);
802 // newText = text.getText();
803 // error = modifier.isValid(newText);
804 // if (error != null) {
805 // text.setBackground(invalidModificationColor);
806 // // System.out.println("validation error: " + error);
808 // text.setBackground(null);
812 newText = text.getText();
813 String leftText = newText.substring(0, e.start);
814 String rightText = newText.substring(e.end, newText.length());
815 GC gc = new GC(text);
816 Point size = gc.textExtent(leftText + e.text + rightText);
818 size = text.computeSize(size.x, SWT.DEFAULT);
819 editor.horizontalAlignment = SWT.LEFT;
820 Rectangle itemRect = item.getBounds(0),
821 rect = viewer.getTree().getClientArea();
822 editor.minimumWidth = Math.max(size.x, itemRect.width) + insetX * 2;
823 int left = itemRect.x,
824 right = rect.x + rect.width;
825 editor.minimumWidth = Math.min(editor.minimumWidth, right - left);
826 editor.minimumHeight = size.y + insetY * 2;
831 case SWT.TRAVERSE_RETURN:
832 if(layer instanceof IEditableLayer) {
833 IEditableLayer l = (IEditableLayer)layer;
834 l.setName(text.getText());
835 //System.out.println("renamed layer to " + text.getText());
838 // error = modifier.isValid(text.getText());
839 // if (error == null) {
840 // modifier.modify(text.getText());
841 // if (!item.isDisposed()) {
842 // item.setText(columnIndex, text.getText());
843 // queueSelectionRefresh(context);
847 case SWT.TRAVERSE_ESCAPE:
852 //System.out.println("unhandled traversal: " + e.detail);
859 text.addListener(SWT.FocusOut, textListener);
860 text.addListener(SWT.Traverse, textListener);
861 text.addListener(SWT.Verify, textListener);
862 text.addListener(SWT.Modify, textListener);
863 editor.setEditor(composite, item, 0);
864 text.setText(initialText);
867 // lastItem[0] = item;