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.diagram.handler;
\r
14 import java.awt.BasicStroke;
\r
15 import java.awt.Color;
\r
16 import java.awt.Shape;
\r
17 import java.awt.geom.Area;
\r
18 import java.awt.geom.Path2D;
\r
19 import java.util.Collection;
\r
20 import java.util.concurrent.Future;
\r
21 import java.util.concurrent.TimeUnit;
\r
23 import org.simantics.g2d.canvas.ICanvasContext;
\r
24 import org.simantics.g2d.canvas.SGDesignation;
\r
25 import org.simantics.g2d.canvas.impl.AbstractCanvasParticipant;
\r
26 import org.simantics.g2d.canvas.impl.SGNodeReflection.SGCleanup;
\r
27 import org.simantics.g2d.canvas.impl.SGNodeReflection.SGInit;
\r
28 import org.simantics.g2d.element.ElementClass;
\r
29 import org.simantics.g2d.element.ElementHints;
\r
30 import org.simantics.g2d.element.ElementUtils;
\r
31 import org.simantics.g2d.element.IElement;
\r
32 import org.simantics.g2d.element.handler.SelectionOutline;
\r
33 import org.simantics.scenegraph.g2d.G2DParentNode;
\r
34 import org.simantics.scenegraph.g2d.nodes.ShapeNode;
\r
35 import org.simantics.scenegraph.g2d.nodes.SingleElementNode;
\r
36 import org.simantics.utils.datastructures.hints.IHintContext.Key;
\r
37 import org.simantics.utils.datastructures.hints.IHintListener;
\r
38 import org.simantics.utils.datastructures.hints.IHintObservable;
\r
39 import org.simantics.utils.threads.ThreadUtils;
\r
42 * This participant highlights the specified DiagramSelection on its source
\r
43 * diagram as a stroked frame tightly surrounding the selected elements.
\r
45 * @author Tuukka Lehtonen
\r
47 public class HighlightMode extends AbstractCanvasParticipant implements IHintListener {
\r
49 private final Color CUT_COLOR = Color.ORANGE;
\r
50 private final Color COPY_COLOR = new Color(128, 220, 150);
\r
52 private final BasicStroke DEFAULT_STROKE = new BasicStroke(3.0f);
\r
54 DiagramSelection selection;
\r
58 SingleElementNode highlightNode;
\r
59 Collection<IElement> highlightElements;
\r
61 public HighlightMode(DiagramSelection selection, int selectionId, int paintPriority) {
\r
62 this.selection = selection;
\r
63 this.selectionId = selectionId;
\r
64 this.paintPriority = paintPriority;
\r
67 @SGInit(designation = SGDesignation.CANVAS)
\r
68 public void init(G2DParentNode parent) {
\r
69 highlightNode = parent.addNode("cut/copy source highlight", SingleElementNode.class);
\r
70 highlightNode.setZIndex(paintPriority);
\r
71 highlightNode.setVisible(false);
\r
73 // This slows rendering down too much to use.
\r
74 //highlightNode.setComposite(AlphaComposite.SrcOver.derive(0.4f));
\r
76 this.highlightElements = selection.getOriginalElements();
\r
78 for (IElement e : highlightElements)
\r
79 e.addHintListener(this);
\r
81 paintSelectionFrames(highlightNode, selection, selectionId);
\r
85 public void cleanupSG() {
\r
86 for (IElement e : highlightElements)
\r
87 e.removeHintListener(this);
\r
89 highlightNode.remove();
\r
92 void paintSelectionFrames(G2DParentNode parentNode, DiagramSelection selection, int selectionId) {
\r
93 Area cutArea = new Area();
\r
94 for (IElement e : selection.getOriginalElements()) {
\r
95 ElementClass ec = e.getElementClass();
\r
96 SelectionOutline so = ec.getAtMostOneItemOfClass(SelectionOutline.class);
\r
97 Shape shape = so != null ? so.getSelectionShape(e) : ElementUtils.getElementShapeOrBoundsOnDiagram(e);
\r
98 cutArea.add(new Area(shape));
\r
100 if (!cutArea.isEmpty()) {
\r
101 // Is rendering of Area slower than Path2D?
\r
102 Path2D.Double path = new Path2D.Double(cutArea);
\r
104 ShapeNode shapeNode = parentNode.getOrCreateNode("highlight", ShapeNode.class);
\r
105 shapeNode.setShape(path);
\r
106 shapeNode.setScaleStroke(true);
\r
107 shapeNode.setStroke(DEFAULT_STROKE);
\r
108 shapeNode.setFill(false);
\r
109 shapeNode.setColor(selection.isCut() ? CUT_COLOR : COPY_COLOR);
\r
111 highlightNode.setVisible(true);
\r
116 public void hintChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) {
\r
117 if (key == ElementHints.KEY_TRANSFORM) {
\r
118 //System.out.println("transform changed for " + sender + " to " + newValue);
\r
119 highlightNode.setVisible(false);
\r
120 deferredHighlightUpdate();
\r
125 public void hintRemoved(IHintObservable sender, Key key, Object oldValue) {
\r
128 Future<?> updateTask;
\r
130 private synchronized void deferredHighlightUpdate() {
\r
131 if (updateTask == null) {
\r
132 updateTask = ThreadUtils.getNonBlockingWorkExecutor().schedule(
\r
135 TimeUnit.MILLISECONDS);
\r
139 Runnable updateScheduler = new Runnable() {
\r
141 public void run() {
\r
142 ICanvasContext ctx = getContext();
\r
144 ThreadUtils.asyncExec(ctx.getThreadAccess(), painter);
\r
148 Runnable painter = new Runnable() {
\r
150 public void run() {
\r
152 paintSelectionFrames(highlightNode, selection, selectionId);
\r