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.synchronization.graph.layer;
\r
14 import java.util.ArrayList;
\r
15 import java.util.Collection;
\r
16 import java.util.HashSet;
\r
17 import java.util.Set;
\r
18 import java.util.concurrent.ConcurrentHashMap;
\r
19 import java.util.concurrent.ConcurrentMap;
\r
20 import java.util.concurrent.atomic.AtomicInteger;
\r
22 import org.simantics.databoard.Bindings;
\r
23 import org.simantics.db.AsyncReadGraph;
\r
24 import org.simantics.db.ReadGraph;
\r
25 import org.simantics.db.Resource;
\r
26 import org.simantics.db.WriteGraph;
\r
27 import org.simantics.db.common.procedure.adapter.AsyncProcedureAdapter;
\r
28 import org.simantics.db.exception.CancelTransactionException;
\r
29 import org.simantics.db.exception.DatabaseException;
\r
30 import org.simantics.db.exception.ServiceException;
\r
31 import org.simantics.db.procedure.AsyncProcedure;
\r
32 import org.simantics.diagram.stubs.DiagramResource;
\r
33 import org.simantics.diagram.synchronization.IModificationQueue;
\r
34 import org.simantics.diagram.synchronization.ModificationAdapter;
\r
35 import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;
\r
36 import org.simantics.g2d.diagram.DiagramHints;
\r
37 import org.simantics.g2d.diagram.IDiagram;
\r
38 import org.simantics.g2d.element.ElementHints;
\r
39 import org.simantics.g2d.element.IElement;
\r
40 import org.simantics.g2d.element.handler.ElementLayers;
\r
41 import org.simantics.g2d.layers.IEditableLayer;
\r
42 import org.simantics.g2d.layers.IEditableLayer.ILayerListener;
\r
43 import org.simantics.g2d.layers.IEditableLayer.LayerChangeEvent;
\r
44 import org.simantics.g2d.layers.ILayer;
\r
45 import org.simantics.g2d.layers.ILayers;
\r
46 import org.simantics.g2d.layers.ILayersEditor;
\r
47 import org.simantics.g2d.layers.ILayersEditor.ILayersEditorListener;
\r
48 import org.simantics.g2d.layers.SimpleLayer;
\r
49 import org.simantics.g2d.layers.SimpleLayers;
\r
50 import org.simantics.layer0.Layer0;
\r
53 * @author Tuukka Lehtonen
\r
55 public class GraphLayerManager {
\r
57 class LayerListener implements ILayersEditorListener, ILayerListener {
\r
59 public void layerAdded(final ILayer layer) {
\r
60 modificationQueue.offer(new CreateLayer(layer, getDiagramResource()), null);
\r
61 modificationQueue.flush();
\r
65 public void layerRemoved(final ILayer layer) {
\r
66 modificationQueue.offer(new RemoveLayer(layer, getDiagramResource()), null);
\r
67 modificationQueue.flush();
\r
71 public void layerActivated(ILayer layer) {
\r
72 postActivation(layer, true);
\r
76 public void layerDeactivated(ILayer layer) {
\r
77 postActivation(layer, false);
\r
81 public void ignoreFocusChanged(boolean value) {
\r
82 // Ignore, not written to graph.
\r
86 public void ignoreVisibilityChanged(boolean value) {
\r
87 // Ignore, not written to graph.
\r
90 void postActivation(ILayer layer, boolean activated) {
\r
91 modificationQueue.offer(new LayerActivation(layer, activated), null);
\r
92 modificationQueue.flush();
\r
96 public void layerChanged(LayerChangeEvent event) {
\r
97 LayerChange change = null;
\r
98 if (IEditableLayer.PROP_NAME.equals(event.getProperty())) {
\r
99 String oldName = (String) event.getOldValue();
\r
100 String newName = (String) event.getNewValue();
\r
101 synchronized (layers) {
\r
102 GraphLayer gl = layers.remove(oldName);
\r
105 layers.put(newName, gl.withName(newName));
\r
106 change = new LayerChange(event, gl);
\r
109 if (change != null) {
\r
110 modificationQueue.offer(change, null);
\r
111 modificationQueue.flush();
\r
116 public static final boolean DEBUG_LAYERS = false;
\r
118 IModificationQueue modificationQueue;
\r
121 DiagramResource dia;
\r
124 * All the layers currently loaded from the diagram.
\r
126 ConcurrentMap<String, GraphLayer> layers = new ConcurrentHashMap<String, GraphLayer>();
\r
128 SimpleLayers layerEditor;
\r
131 * The listener for ILayersEditor and all IEditableLayer.
\r
133 LayerListener layerListener = new LayerListener();
\r
135 public GraphLayerManager(ReadGraph graph, IModificationQueue modificationQueue, Resource diagram) {
\r
136 this.modificationQueue = modificationQueue;
\r
137 this.diagram = diagram;
\r
138 this.l0 = Layer0.getInstance(graph);
\r
139 this.dia = DiagramResource.getInstance(graph);
\r
142 public void dispose() {
\r
143 for (ILayer layer : layerEditor.getLayers()) {
\r
144 if (layer instanceof IEditableLayer) {
\r
145 ((IEditableLayer) layer).removeLayerListener(layerListener);
\r
151 Resource getDiagramResource() {
\r
155 // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
\r
157 // ------------------------------------------------------------------------
\r
159 class CreateLayer extends ModificationAdapter {
\r
160 final ILayer layer;
\r
162 final Resource diagram;
\r
164 public CreateLayer(ILayer layer, Resource diagram) {
\r
165 super(LOW_PRIORITY);
\r
166 assert layer != null;
\r
167 this.layer = layer;
\r
168 this.diagram = diagram;
\r
170 if (layer instanceof IEditableLayer) {
\r
171 ((IEditableLayer) layer).addLayerListener(layerListener);
\r
176 public void perform(WriteGraph g) throws Exception {
\r
177 String newName = layer.getName();
\r
178 for (Resource l : g.getObjects(diagram, dia.HasLayer)) {
\r
179 String name = g.getRelatedValue(l, l0.HasName);
\r
180 if (newName.equals(name)) {
\r
184 GraphLayer l = new GraphLayerUtil(g).createLayer(newName, false);
\r
185 g.claim(diagram, dia.HasLayer, l.getLayer());
\r
186 layers.put(newName, l);
\r
190 class RemoveLayer extends ModificationAdapter {
\r
191 final ILayer layer;
\r
193 final Resource diagram;
\r
195 public RemoveLayer(ILayer layer, Resource diagram) {
\r
196 super(LOW_PRIORITY);
\r
197 assert layer != null;
\r
198 this.layer = layer;
\r
199 this.diagram = diagram;
\r
201 if (layer instanceof IEditableLayer) {
\r
202 ((IEditableLayer) layer).removeLayerListener(layerListener);
\r
207 public void perform(WriteGraph g) throws Exception {
\r
208 String removedName = layer.getName();
\r
209 for (Resource l : g.getObjects(diagram, dia.HasLayer)) {
\r
210 String name = g.getRelatedValue(l, l0.HasName);
\r
211 if (removedName.equals(name)) {
\r
212 g.denyStatement(diagram, dia.HasLayer, l);
\r
215 // NOTE: leave the layer tags intact, remove them gradually
\r
216 // by checking the validity of all layer tags during element
\r
218 layers.remove(name);
\r
224 void deleteLayer(WriteGraph g, Resource layer) throws DatabaseException {
\r
229 class LayerActivation extends ModificationAdapter {
\r
230 final ILayer layer;
\r
232 final boolean activated;
\r
234 public LayerActivation(ILayer layer, boolean activated) {
\r
235 super(LOW_PRIORITY);
\r
236 assert layer != null;
\r
237 this.layer = layer;
\r
238 this.activated = activated;
\r
242 public void perform(WriteGraph g) throws Exception {
\r
243 GraphLayer gl = layers.get(layer.getName());
\r
245 throw new CancelTransactionException("Diagram has no matching layer description: " + layer.getName());
\r
247 g.claimLiteral(gl.getLayer(), dia.IsActive, Boolean.valueOf(activated));
\r
251 class LayerChange extends ModificationAdapter {
\r
252 final LayerChangeEvent event;
\r
254 final GraphLayer gl;
\r
256 public LayerChange(LayerChangeEvent event, GraphLayer gl) {
\r
257 super(LOW_PRIORITY);
\r
258 assert event != null;
\r
260 this.event = event;
\r
265 public void perform(WriteGraph g) throws Exception {
\r
266 // Resource name = g.getSingleObject(gl.getLayer(), b.HasName);
\r
267 // g.claimValue(name, event.getSource().getName());
\r
268 g.claimLiteral(gl.getLayer(), l0.HasName, event.getSource().getName(), Bindings.STRING);
\r
272 void deleteTag(WriteGraph g, Resource tag) throws DatabaseException {
\r
276 Collection<GraphLayer> getActiveLayers(ReadGraph g) throws DatabaseException {
\r
277 Collection<GraphLayer> result = new ArrayList<GraphLayer>();
\r
278 for (GraphLayer gl : layers.values()) {
\r
279 Boolean active = g.getPossibleRelatedValue(gl.getLayer(), dia.IsActive);
\r
280 if (Boolean.TRUE.equals(active)) {
\r
287 void tagElementWithActiveLayers(WriteGraph g, Resource element) throws DatabaseException {
\r
288 Collection<GraphLayer> activeLayers = getActiveLayers(g);
\r
289 for (GraphLayer activeLayer : activeLayers) {
\r
290 DiagramGraphUtil.tag(g, element, activeLayer.getVisible(), true);
\r
291 DiagramGraphUtil.tag(g, element, activeLayer.getFocusable(), true);
\r
295 GraphLayer loadLayer(ReadGraph g, Resource layer) throws DatabaseException {
\r
296 String name = g.getRelatedValue(layer, l0.HasName);
\r
297 Resource visible = g.getSingleObject(layer, dia.HasVisibleTag);
\r
298 Resource focusable = g.getSingleObject(layer, dia.HasFocusableTag);
\r
299 return new GraphLayer(name, layer, visible, focusable);
\r
302 public ILayersEditor loadLayers(IDiagram diagram, ReadGraph g, Resource diagramResource) throws DatabaseException {
\r
303 SimpleLayers result = new SimpleLayers();
\r
304 ConcurrentMap<String, GraphLayer> newLayers = new ConcurrentHashMap<String, GraphLayer>();
\r
306 String[] fixed = diagram.getHint(DiagramHints.KEY_FIXED_LAYERS);
\r
307 if (fixed != null) {
\r
309 // for (String name : fixed) {
\r
310 // SimpleLayer l = new SimpleLayer(name);
\r
311 // result.addLayer(l);
\r
312 // result.activate(l);
\r
315 // We need to put GraphLayer to newLayers so...
\r
316 for (Resource layer : g.getObjects(diagramResource, dia.HasLayer)) {
\r
317 GraphLayer gl = loadLayer(g, layer);
\r
318 for (String name : fixed) {
\r
319 if (name.equals(gl.getName())) {
\r
320 SimpleLayer l = new SimpleLayer(gl.getName());
\r
321 newLayers.put(gl.getName(), gl);
\r
322 result.addLayer(l);
\r
323 result.activate(l);
\r
331 System.out.println("Loading layers");
\r
333 for (Resource layer : g.getObjects(diagramResource, dia.HasLayer)) {
\r
334 GraphLayer gl = loadLayer(g, layer);
\r
335 SimpleLayer l = new SimpleLayer(gl.getName());
\r
337 newLayers.put(gl.getName(), gl);
\r
338 result.addLayer(l);
\r
340 Boolean active = g.getPossibleRelatedValue(layer, dia.IsActive);
\r
341 if (active == null)
\r
342 active = Boolean.FALSE;
\r
345 System.out.println(" Loaded " + (active ? "active" : "inactive") + " layer '" + gl.getName() + "'");
\r
347 l.addLayerListener(layerListener);
\r
349 result.activate(l);
\r
353 System.out.println("Loaded " + newLayers.size() + " layers");
\r
357 this.layers = newLayers;
\r
358 this.layerEditor = result;
\r
359 this.layerEditor.addListener(layerListener);
\r
364 public void loadLayersForElement(ReadGraph graph, ILayersEditor layersEditor, IElement e, Resource element)
\r
365 throws DatabaseException {
\r
367 System.out.println("Loading layers for element " + element + " - " + e);
\r
369 Set<ILayer> visible = null;
\r
370 Set<ILayer> focusable = null;
\r
372 for (ILayer l : layersEditor.getLayers()) {
\r
373 GraphLayer gl = layers.get(l.getName());
\r
375 if (graph.hasStatement(element, gl.getVisible(), element)) {
\r
376 if (visible == null)
\r
377 visible = new HashSet<ILayer>(4);
\r
380 System.out.println(" Visible on layer '" + gl.getName() + "'");
\r
382 if (graph.hasStatement(element, gl.getFocusable(), element)) {
\r
383 if (focusable == null)
\r
384 focusable = new HashSet<ILayer>(4);
\r
387 System.out.println(" Focusable on layer '" + gl.getName() + "'");
\r
392 if (visible == null)
\r
393 visible = new HashSet<ILayer>(1);
\r
394 if (focusable == null)
\r
395 focusable = new HashSet<ILayer>(1);
\r
397 e.setHint(ElementHints.KEY_VISIBLE_LAYERS, visible);
\r
398 e.setHint(ElementHints.KEY_FOCUS_LAYERS, focusable);
\r
403 * @param layersEditor
\r
406 * @param procedure a procedure whose exception method may be called 0 to
\r
407 * many times depending on how many errors occur during layer loading
\r
408 * @throws DatabaseException
\r
410 public void loadLayersForElement(AsyncReadGraph graph, ILayersEditor layersEditor, final IElement e,
\r
411 Resource element, final AsyncProcedure<IElement> callback) {
\r
413 System.out.println("Loading layers for element " + element + " - " + e);
\r
415 final Set<ILayer> visible = new HashSet<ILayer>(2);
\r
416 final Set<ILayer> focusable = new HashSet<ILayer>(2);
\r
418 // NOTE: must not set layer hints into element until the layer sets have
\r
419 // been properly loaded.
\r
421 Set<ILayer> allLayers = layersEditor.getLayers();
\r
422 if (allLayers.isEmpty()) {
\r
423 e.setHint(ElementHints.KEY_VISIBLE_LAYERS, visible);
\r
424 e.setHint(ElementHints.KEY_FOCUS_LAYERS, focusable);
\r
425 callback.execute(graph, e);
\r
429 final AtomicInteger ready = new AtomicInteger(allLayers.size() * 2);
\r
431 for (final ILayer l : allLayers) {
\r
432 final GraphLayer gl = layers.get(l.getName());
\r
434 graph.forHasStatement(element, gl.getVisible(), element, new AsyncProcedureAdapter<Boolean>() {
\r
436 public void execute(AsyncReadGraph graph, Boolean result) {
\r
437 synchronized (visible) {
\r
441 System.out.println(" Visible on layer '" + gl.getName() + "'");
\r
442 if (ready.decrementAndGet() == 0) {
\r
443 e.setHint(ElementHints.KEY_VISIBLE_LAYERS, visible);
\r
444 e.setHint(ElementHints.KEY_FOCUS_LAYERS, focusable);
\r
445 callback.execute(graph, e);
\r
449 public void exception(AsyncReadGraph graph, Throwable t) {
\r
450 callback.exception(graph, t);
\r
453 graph.forHasStatement(element, gl.getFocusable(), element, new AsyncProcedureAdapter<Boolean>() {
\r
455 public void execute(AsyncReadGraph graph, Boolean result) {
\r
456 synchronized (focusable) {
\r
460 System.out.println(" Focusable on layer '" + gl.getName() + "'");
\r
461 if (ready.decrementAndGet() == 0) {
\r
462 e.setHint(ElementHints.KEY_VISIBLE_LAYERS, visible);
\r
463 e.setHint(ElementHints.KEY_FOCUS_LAYERS, focusable);
\r
464 callback.execute(graph, e);
\r
468 public void exception(AsyncReadGraph graph, Throwable t) {
\r
469 callback.exception(graph, t);
\r
475 if (ready.addAndGet(-2) == 0) {
\r
476 e.setHint(ElementHints.KEY_VISIBLE_LAYERS, visible);
\r
477 e.setHint(ElementHints.KEY_FOCUS_LAYERS, focusable);
\r
478 callback.execute(graph, e);
\r
485 void putElementOnVisibleLayers(IDiagram diagram, IElement element) {
\r
486 // Make the new element visible and focusable on all currently
\r
488 ILayers diagramLayers = diagram.getHint(DiagramHints.KEY_LAYERS);
\r
489 if (diagramLayers != null) {
\r
490 element.setHint(ElementHints.KEY_VISIBLE_LAYERS, new HashSet<ILayer>());
\r
491 element.setHint(ElementHints.KEY_FOCUS_LAYERS, new HashSet<ILayer>());
\r
492 Set<ILayer> visibleLayers = diagramLayers.getVisibleLayers();
\r
495 System.out.println("Marking element visible and focusable only on visible layers: " + visibleLayers);
\r
497 for (ElementLayers elementLayers : element.getElementClass().getItemsByClass(ElementLayers.class)) {
\r
498 for (ILayer layer : visibleLayers) {
\r
499 elementLayers.setVisibility(element, layer, true);
\r
500 elementLayers.setFocusability(element, layer, true);
\r
506 public void putElementOnVisibleLayers(IDiagram diagram, WriteGraph g, Resource element) throws DatabaseException {
\r
507 // Make the new element visible and focusable on all currently
\r
509 ILayers diagramLayers = diagram.getHint(DiagramHints.KEY_LAYERS);
\r
510 if (diagramLayers != null) {
\r
511 Set<ILayer> visibleLayers = diagramLayers.getVisibleLayers();
\r
514 System.out.println("Marking element visible and focusable in the graph on visible layers: "
\r
517 for (ILayer layer : visibleLayers) {
\r
518 GraphLayer gl = layers.get(layer.getName());
\r
520 DiagramGraphUtil.tag(g, element, gl.getVisible(), true);
\r
521 DiagramGraphUtil.tag(g, element, gl.getFocusable(), true);
\r
527 public void removeFromAllLayers(WriteGraph graph, Resource element) throws ServiceException {
\r
528 // Remove element from all layers.
\r
529 graph.deny(element, dia.IsVisible);
\r
530 graph.deny(element, dia.IsFocusable);
\r
533 public GraphLayer getGraphLayer(String name) {
\r
534 return layers.get(name);
\r