X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=blobdiff_plain;f=bundles%2Forg.simantics.diagram%2Fsrc%2Forg%2Fsimantics%2Fdiagram%2Fsynchronization%2Fgraph%2Flayer%2FGraphLayerManager.java;fp=bundles%2Forg.simantics.diagram%2Fsrc%2Forg%2Fsimantics%2Fdiagram%2Fsynchronization%2Fgraph%2Flayer%2FGraphLayerManager.java;h=b060adaadc2ed359705f0f80109d4ced2453a9ef;hp=0000000000000000000000000000000000000000;hb=969bd23cab98a79ca9101af33334000879fb60c5;hpb=866dba5cd5a3929bbeae85991796acb212338a08 diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/synchronization/graph/layer/GraphLayerManager.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/synchronization/graph/layer/GraphLayerManager.java new file mode 100644 index 000000000..b060adaad --- /dev/null +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/synchronization/graph/layer/GraphLayerManager.java @@ -0,0 +1,537 @@ +/******************************************************************************* + * Copyright (c) 2007, 2010 Association for Decentralized Information Management + * in Industry THTH ry. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * VTT Technical Research Centre of Finland - initial API and implementation + *******************************************************************************/ +package org.simantics.diagram.synchronization.graph.layer; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicInteger; + +import org.simantics.databoard.Bindings; +import org.simantics.db.AsyncReadGraph; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; +import org.simantics.db.common.procedure.adapter.AsyncProcedureAdapter; +import org.simantics.db.exception.CancelTransactionException; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.exception.ServiceException; +import org.simantics.db.procedure.AsyncProcedure; +import org.simantics.diagram.stubs.DiagramResource; +import org.simantics.diagram.synchronization.IModificationQueue; +import org.simantics.diagram.synchronization.ModificationAdapter; +import org.simantics.diagram.synchronization.graph.DiagramGraphUtil; +import org.simantics.g2d.diagram.DiagramHints; +import org.simantics.g2d.diagram.IDiagram; +import org.simantics.g2d.element.ElementHints; +import org.simantics.g2d.element.IElement; +import org.simantics.g2d.element.handler.ElementLayers; +import org.simantics.g2d.layers.IEditableLayer; +import org.simantics.g2d.layers.IEditableLayer.ILayerListener; +import org.simantics.g2d.layers.IEditableLayer.LayerChangeEvent; +import org.simantics.g2d.layers.ILayer; +import org.simantics.g2d.layers.ILayers; +import org.simantics.g2d.layers.ILayersEditor; +import org.simantics.g2d.layers.ILayersEditor.ILayersEditorListener; +import org.simantics.g2d.layers.SimpleLayer; +import org.simantics.g2d.layers.SimpleLayers; +import org.simantics.layer0.Layer0; + +/** + * @author Tuukka Lehtonen + */ +public class GraphLayerManager { + + class LayerListener implements ILayersEditorListener, ILayerListener { + @Override + public void layerAdded(final ILayer layer) { + modificationQueue.offer(new CreateLayer(layer, getDiagramResource()), null); + modificationQueue.flush(); + } + + @Override + public void layerRemoved(final ILayer layer) { + modificationQueue.offer(new RemoveLayer(layer, getDiagramResource()), null); + modificationQueue.flush(); + } + + @Override + public void layerActivated(ILayer layer) { + postActivation(layer, true); + } + + @Override + public void layerDeactivated(ILayer layer) { + postActivation(layer, false); + } + + @Override + public void ignoreFocusChanged(boolean value) { + // Ignore, not written to graph. + } + + @Override + public void ignoreVisibilityChanged(boolean value) { + // Ignore, not written to graph. + } + + void postActivation(ILayer layer, boolean activated) { + modificationQueue.offer(new LayerActivation(layer, activated), null); + modificationQueue.flush(); + } + + @Override + public void layerChanged(LayerChangeEvent event) { + LayerChange change = null; + if (IEditableLayer.PROP_NAME.equals(event.getProperty())) { + String oldName = (String) event.getOldValue(); + String newName = (String) event.getNewValue(); + synchronized (layers) { + GraphLayer gl = layers.remove(oldName); + if (gl == null) + return; + layers.put(newName, gl.withName(newName)); + change = new LayerChange(event, gl); + } + } + if (change != null) { + modificationQueue.offer(change, null); + modificationQueue.flush(); + } + } + } + + public static final boolean DEBUG_LAYERS = false; + + IModificationQueue modificationQueue; + Resource diagram; + Layer0 l0; + DiagramResource dia; + + /** + * All the layers currently loaded from the diagram. + */ + ConcurrentMap layers = new ConcurrentHashMap(); + + SimpleLayers layerEditor; + + /** + * The listener for ILayersEditor and all IEditableLayer. + */ + LayerListener layerListener = new LayerListener(); + + public GraphLayerManager(ReadGraph graph, IModificationQueue modificationQueue, Resource diagram) { + this.modificationQueue = modificationQueue; + this.diagram = diagram; + this.l0 = Layer0.getInstance(graph); + this.dia = DiagramResource.getInstance(graph); + } + + public void dispose() { + for (ILayer layer : layerEditor.getLayers()) { + if (layer instanceof IEditableLayer) { + ((IEditableLayer) layer).removeLayerListener(layerListener); + } + } + layers.clear(); + } + + Resource getDiagramResource() { + return diagram; + } + + // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + // LAYERS BEGIN + // ------------------------------------------------------------------------ + + class CreateLayer extends ModificationAdapter { + final ILayer layer; + + final Resource diagram; + + public CreateLayer(ILayer layer, Resource diagram) { + super(LOW_PRIORITY); + assert layer != null; + this.layer = layer; + this.diagram = diagram; + + if (layer instanceof IEditableLayer) { + ((IEditableLayer) layer).addLayerListener(layerListener); + } + } + + @Override + public void perform(WriteGraph g) throws Exception { + String newName = layer.getName(); + for (Resource l : g.getObjects(diagram, dia.HasLayer)) { + String name = g.getRelatedValue(l, l0.HasName); + if (newName.equals(name)) { + return; + } + } + GraphLayer l = new GraphLayerUtil(g).createLayer(newName, false); + g.claim(diagram, dia.HasLayer, l.getLayer()); + layers.put(newName, l); + } + } + + class RemoveLayer extends ModificationAdapter { + final ILayer layer; + + final Resource diagram; + + public RemoveLayer(ILayer layer, Resource diagram) { + super(LOW_PRIORITY); + assert layer != null; + this.layer = layer; + this.diagram = diagram; + + if (layer instanceof IEditableLayer) { + ((IEditableLayer) layer).removeLayerListener(layerListener); + } + } + + @Override + public void perform(WriteGraph g) throws Exception { + String removedName = layer.getName(); + for (Resource l : g.getObjects(diagram, dia.HasLayer)) { + String name = g.getRelatedValue(l, l0.HasName); + if (removedName.equals(name)) { + g.denyStatement(diagram, dia.HasLayer, l); + deleteLayer(g, l); + + // NOTE: leave the layer tags intact, remove them gradually + // by checking the validity of all layer tags during element + // writeback. + layers.remove(name); + return; + } + } + } + + void deleteLayer(WriteGraph g, Resource layer) throws DatabaseException { + g.deny(layer); + } + } + + class LayerActivation extends ModificationAdapter { + final ILayer layer; + + final boolean activated; + + public LayerActivation(ILayer layer, boolean activated) { + super(LOW_PRIORITY); + assert layer != null; + this.layer = layer; + this.activated = activated; + } + + @Override + public void perform(WriteGraph g) throws Exception { + GraphLayer gl = layers.get(layer.getName()); + if (gl == null) + throw new CancelTransactionException("Diagram has no matching layer description: " + layer.getName()); + + g.claimLiteral(gl.getLayer(), dia.IsActive, Boolean.valueOf(activated)); + } + } + + class LayerChange extends ModificationAdapter { + final LayerChangeEvent event; + + final GraphLayer gl; + + public LayerChange(LayerChangeEvent event, GraphLayer gl) { + super(LOW_PRIORITY); + assert event != null; + assert gl != null; + this.event = event; + this.gl = gl; + } + + @Override + public void perform(WriteGraph g) throws Exception { +// Resource name = g.getSingleObject(gl.getLayer(), b.HasName); +// g.claimValue(name, event.getSource().getName()); + g.claimLiteral(gl.getLayer(), l0.HasName, event.getSource().getName(), Bindings.STRING); + } + } + + void deleteTag(WriteGraph g, Resource tag) throws DatabaseException { + g.deny(tag); + } + + Collection getActiveLayers(ReadGraph g) throws DatabaseException { + Collection result = new ArrayList(); + for (GraphLayer gl : layers.values()) { + Boolean active = g.getPossibleRelatedValue(gl.getLayer(), dia.IsActive); + if (Boolean.TRUE.equals(active)) { + result.add(gl); + } + } + return result; + } + + void tagElementWithActiveLayers(WriteGraph g, Resource element) throws DatabaseException { + Collection activeLayers = getActiveLayers(g); + for (GraphLayer activeLayer : activeLayers) { + DiagramGraphUtil.tag(g, element, activeLayer.getVisible(), true); + DiagramGraphUtil.tag(g, element, activeLayer.getFocusable(), true); + } + } + + GraphLayer loadLayer(ReadGraph g, Resource layer) throws DatabaseException { + String name = g.getRelatedValue(layer, l0.HasName); + Resource visible = g.getSingleObject(layer, dia.HasVisibleTag); + Resource focusable = g.getSingleObject(layer, dia.HasFocusableTag); + return new GraphLayer(name, layer, visible, focusable); + } + + public ILayersEditor loadLayers(IDiagram diagram, ReadGraph g, Resource diagramResource) throws DatabaseException { + SimpleLayers result = new SimpleLayers(); + ConcurrentMap newLayers = new ConcurrentHashMap(); + + String[] fixed = diagram.getHint(DiagramHints.KEY_FIXED_LAYERS); + if (fixed != null) { + +// for (String name : fixed) { +// SimpleLayer l = new SimpleLayer(name); +// result.addLayer(l); +// result.activate(l); +// } + + // We need to put GraphLayer to newLayers so... + for (Resource layer : g.getObjects(diagramResource, dia.HasLayer)) { + GraphLayer gl = loadLayer(g, layer); + for (String name : fixed) { + if (name.equals(gl.getName())) { + SimpleLayer l = new SimpleLayer(gl.getName()); + newLayers.put(gl.getName(), gl); + result.addLayer(l); + result.activate(l); + } + } + } + + } else { + + if (DEBUG_LAYERS) + System.out.println("Loading layers"); + + for (Resource layer : g.getObjects(diagramResource, dia.HasLayer)) { + GraphLayer gl = loadLayer(g, layer); + SimpleLayer l = new SimpleLayer(gl.getName()); + + newLayers.put(gl.getName(), gl); + result.addLayer(l); + + Boolean active = g.getPossibleRelatedValue(layer, dia.IsActive); + if (active == null) + active = Boolean.FALSE; + + if (DEBUG_LAYERS) + System.out.println(" Loaded " + (active ? "active" : "inactive") + " layer '" + gl.getName() + "'"); + + l.addLayerListener(layerListener); + if (active) + result.activate(l); + } + + if (DEBUG_LAYERS) + System.out.println("Loaded " + newLayers.size() + " layers"); + + } + + this.layers = newLayers; + this.layerEditor = result; + this.layerEditor.addListener(layerListener); + + return result; + } + + public void loadLayersForElement(ReadGraph graph, ILayersEditor layersEditor, IElement e, Resource element) + throws DatabaseException { + if (DEBUG_LAYERS) + System.out.println("Loading layers for element " + element + " - " + e); + + Set visible = null; + Set focusable = null; + + for (ILayer l : layersEditor.getLayers()) { + GraphLayer gl = layers.get(l.getName()); + if (gl != null) { + if (graph.hasStatement(element, gl.getVisible(), element)) { + if (visible == null) + visible = new HashSet(4); + visible.add(l); + if (DEBUG_LAYERS) + System.out.println(" Visible on layer '" + gl.getName() + "'"); + } + if (graph.hasStatement(element, gl.getFocusable(), element)) { + if (focusable == null) + focusable = new HashSet(4); + focusable.add(l); + if (DEBUG_LAYERS) + System.out.println(" Focusable on layer '" + gl.getName() + "'"); + } + } + } + + if (visible == null) + visible = new HashSet(1); + if (focusable == null) + focusable = new HashSet(1); + + e.setHint(ElementHints.KEY_VISIBLE_LAYERS, visible); + e.setHint(ElementHints.KEY_FOCUS_LAYERS, focusable); + } + + /** + * @param graph + * @param layersEditor + * @param e + * @param element + * @param procedure a procedure whose exception method may be called 0 to + * many times depending on how many errors occur during layer loading + * @throws DatabaseException + */ + public void loadLayersForElement(AsyncReadGraph graph, ILayersEditor layersEditor, final IElement e, + Resource element, final AsyncProcedure callback) { + if (DEBUG_LAYERS) + System.out.println("Loading layers for element " + element + " - " + e); + + final Set visible = new HashSet(2); + final Set focusable = new HashSet(2); + + // NOTE: must not set layer hints into element until the layer sets have + // been properly loaded. + + Set allLayers = layersEditor.getLayers(); + if (allLayers.isEmpty()) { + e.setHint(ElementHints.KEY_VISIBLE_LAYERS, visible); + e.setHint(ElementHints.KEY_FOCUS_LAYERS, focusable); + callback.execute(graph, e); + return; + } + + final AtomicInteger ready = new AtomicInteger(allLayers.size() * 2); + + for (final ILayer l : allLayers) { + final GraphLayer gl = layers.get(l.getName()); + if (gl != null) { + graph.forHasStatement(element, gl.getVisible(), element, new AsyncProcedureAdapter() { + @Override + public void execute(AsyncReadGraph graph, Boolean result) { + synchronized (visible) { + visible.add(l); + } + if (DEBUG_LAYERS) + System.out.println(" Visible on layer '" + gl.getName() + "'"); + if (ready.decrementAndGet() == 0) { + e.setHint(ElementHints.KEY_VISIBLE_LAYERS, visible); + e.setHint(ElementHints.KEY_FOCUS_LAYERS, focusable); + callback.execute(graph, e); + } + } + @Override + public void exception(AsyncReadGraph graph, Throwable t) { + callback.exception(graph, t); + } + }); + graph.forHasStatement(element, gl.getFocusable(), element, new AsyncProcedureAdapter() { + @Override + public void execute(AsyncReadGraph graph, Boolean result) { + synchronized (focusable) { + focusable.add(l); + } + if (DEBUG_LAYERS) + System.out.println(" Focusable on layer '" + gl.getName() + "'"); + if (ready.decrementAndGet() == 0) { + e.setHint(ElementHints.KEY_VISIBLE_LAYERS, visible); + e.setHint(ElementHints.KEY_FOCUS_LAYERS, focusable); + callback.execute(graph, e); + } + } + @Override + public void exception(AsyncReadGraph graph, Throwable t) { + callback.exception(graph, t); + } + }); + + } else { + + if (ready.addAndGet(-2) == 0) { + e.setHint(ElementHints.KEY_VISIBLE_LAYERS, visible); + e.setHint(ElementHints.KEY_FOCUS_LAYERS, focusable); + callback.execute(graph, e); + } + + } + } + } + + void putElementOnVisibleLayers(IDiagram diagram, IElement element) { + // Make the new element visible and focusable on all currently + // active layers. + ILayers diagramLayers = diagram.getHint(DiagramHints.KEY_LAYERS); + if (diagramLayers != null) { + element.setHint(ElementHints.KEY_VISIBLE_LAYERS, new HashSet()); + element.setHint(ElementHints.KEY_FOCUS_LAYERS, new HashSet()); + Set visibleLayers = diagramLayers.getVisibleLayers(); + + if (DEBUG_LAYERS) + System.out.println("Marking element visible and focusable only on visible layers: " + visibleLayers); + + for (ElementLayers elementLayers : element.getElementClass().getItemsByClass(ElementLayers.class)) { + for (ILayer layer : visibleLayers) { + elementLayers.setVisibility(element, layer, true); + elementLayers.setFocusability(element, layer, true); + } + } + } + } + + public void putElementOnVisibleLayers(IDiagram diagram, WriteGraph g, Resource element) throws DatabaseException { + // Make the new element visible and focusable on all currently + // active layers. + ILayers diagramLayers = diagram.getHint(DiagramHints.KEY_LAYERS); + if (diagramLayers != null) { + Set visibleLayers = diagramLayers.getVisibleLayers(); + + if (DEBUG_LAYERS) + System.out.println("Marking element visible and focusable in the graph on visible layers: " + + visibleLayers); + + for (ILayer layer : visibleLayers) { + GraphLayer gl = layers.get(layer.getName()); + if (gl != null) { + DiagramGraphUtil.tag(g, element, gl.getVisible(), true); + DiagramGraphUtil.tag(g, element, gl.getFocusable(), true); + } + } + } + } + + public void removeFromAllLayers(WriteGraph graph, Resource element) throws ServiceException { + // Remove element from all layers. + graph.deny(element, dia.IsVisible); + graph.deny(element, dia.IsFocusable); + } + + public GraphLayer getGraphLayer(String name) { + return layers.get(name); + } + +}