/******************************************************************************* * 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.participant; import java.awt.Color; import java.awt.Font; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import org.simantics.Simantics; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.common.request.PossibleIndexRoot; import org.simantics.db.common.request.UnaryRead; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.adapter.Instances; import org.simantics.db.layer0.request.IsActive; import org.simantics.db.layer0.util.Layer0Utils; import org.simantics.db.procedure.Listener; import org.simantics.diagram.elements.TextNode; import org.simantics.diagram.stubs.DiagramResource; import org.simantics.g2d.canvas.Hints; import org.simantics.g2d.canvas.ICanvasContext; import org.simantics.g2d.canvas.SGDesignation; import org.simantics.g2d.canvas.impl.AbstractCanvasParticipant; import org.simantics.g2d.canvas.impl.HintReflection.HintListener; import org.simantics.g2d.canvas.impl.SGNodeReflection.SGCleanup; import org.simantics.g2d.canvas.impl.SGNodeReflection.SGInit; import org.simantics.g2d.utils.Alignment; import org.simantics.layer0.Layer0; import org.simantics.modeling.ModelingResources; import org.simantics.scenegraph.g2d.G2DParentNode; import org.simantics.structural.stubs.StructuralResource2; import org.simantics.utils.datastructures.hints.IHintContext; import org.simantics.utils.datastructures.hints.IHintContext.Key; import org.simantics.utils.datastructures.hints.IHintContext.KeyOf; import org.simantics.utils.datastructures.hints.IHintObservable; import org.simantics.utils.ui.ErrorLogger; /** * DiagramModelActivityTracker tracks whether or not the specified input * resource is part of the diagram model or not and modifies the canvas * background color accordingly. If the diagram is not in an active model, an * INACTIVE MODEL banner is also shown in the background using * {@link TextNode}. * * @author Tuukka Lehtonen */ public class DiagramModelActivityTracker extends AbstractCanvasParticipant { public static final Key KEY_IN_ACTIVE_MODEL = new KeyOf(String.class, "IN_ACTIVE_MODEL"); public static final Key KEY_ACTIVE_BACKGROUND = new KeyOf(Color.class, "ACTIVE_BACKGROUND"); public static final Key KEY_INACTIVE_BACKGROUND = new KeyOf(Color.class, "INACTIVE_BACKGROUND"); private static final Color DEFAULT_ACTIVE_BACKGROUND = Color.WHITE; private static final Color DEFAULT_INACTIVE_BACKGROUND = new Color(242, 242, 242); Resource input; IsInActiveModelListener listener; TextNode bannerNode; public DiagramModelActivityTracker(Resource input) { if (input == null) throw new NullPointerException("null input"); this.input = input; } @HintListener(Class=Hints.class, Field="KEY_CONTROL_BOUNDS") public void controlBoundsChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) { if (bannerNode == null) return; if (newValue != null) { Rectangle2D bounds = (Rectangle2D) newValue; AffineTransform at = new AffineTransform(); at.translate(5,5); // at.translate(bounds.getWidth() / 2, bounds.getHeight() / 2); // at.scale(2, 2); // at.rotate(-Math.PI/6); // at.translate(bounds.getWidth() - 150, bounds.getHeight() - 30); // at.scale(1, 1); bannerNode.setTransform(at); } else { // Disable rendering bannerNode.setColor(null); } } @HintListener(Class=DiagramModelActivityTracker.class, Field="KEY_IN_ACTIVE_MODEL") public void containingModelActivityChanged(IHintObservable sender, Key key, Object oldValue, Object _newValue) { String newValue = (String)_newValue; if (bannerNode == null) return; if (newValue == null) { bannerNode.setText(""); } else { bannerNode.setText(newValue); //bannerNode.setColor(new Color(192, 32, 32, 128)); //bannerNode.setColor(new Color(192, 32, 32, 32)); } } @Override public void addedToContext(ICanvasContext ctx) { super.addedToContext(ctx); listener = new IsInActiveModelListener(ctx); Simantics.getSession().async(new IsActiveDiagram(input), listener); if (!ctx.getHintStack().containsHint(KEY_ACTIVE_BACKGROUND)) ctx.getDefaultHintContext().setHint(KEY_ACTIVE_BACKGROUND, DEFAULT_ACTIVE_BACKGROUND); if (!ctx.getHintStack().containsHint(KEY_INACTIVE_BACKGROUND)) ctx.getDefaultHintContext().setHint(KEY_INACTIVE_BACKGROUND, DEFAULT_INACTIVE_BACKGROUND); } @Override public void removedFromContext(ICanvasContext ctx) { if (listener != null) { listener.dispose(); listener = null; } super.removedFromContext(ctx); } @SGInit(designation = SGDesignation.CONTROL) public void initSG(G2DParentNode parent) { bannerNode = parent.addNode("inactivity-banner", TextNode.class); bannerNode.setZIndex(Integer.MIN_VALUE / 4); //bannerNode.setZIndex(Integer.MAX_VALUE / 4); bannerNode.setHorizontalAlignment((byte) Alignment.LEADING.ordinal()); bannerNode.setVerticalAlignment((byte) Alignment.LEADING.ordinal()); //bannerNode.setText("Model is not active."); bannerNode.setText(""); bannerNode.setFont(Font.decode("Arial 16")); //bannerNode.setBackgroundColor(new Color(192, 192, 192, 128)); bannerNode.setPadding(10, 10); bannerNode.setBorderColor(Color.GRAY); bannerNode.setBorderWidth(0); bannerNode.setEditable(false); } @SGCleanup public void cleanupSG() { if (bannerNode != null) { bannerNode.remove(); bannerNode = null; } } static class IsActiveDiagram extends UnaryRead { public IsActiveDiagram(Resource parameter) { super(parameter); } @Override public String perform(ReadGraph graph) throws DatabaseException { Resource indexRoot = graph.syncRequest( new PossibleIndexRoot(parameter) ); if (indexRoot == null) { return "This diagram is not part of any model or shared library"; } Layer0 L0 = Layer0.getInstance(graph); StructuralResource2 STR = StructuralResource2.getInstance(graph); ModelingResources MOD = ModelingResources.getInstance(graph); Resource composite = graph.getPossibleObject(parameter, MOD.DiagramToComposite); if(composite == null) return "This diagram does not have a corresponding configuration"; Resource componentType = graph.getPossibleObject(composite, STR.Defines); if(componentType != null) { // This is a component type boolean published = Layer0Utils.isPublished(graph, componentType); if(published) return "This diagram defines a published (read-only) user component"; } if(graph.isInstanceOf(indexRoot, L0.SharedOntology)) { return ""; } boolean activeModel = graph.syncRequest( new IsActive(indexRoot) ); if(!activeModel) return "The model of this diagram is not active"; DiagramResource DIA = DiagramResource.getInstance(graph); Instances query = graph.adapt(DIA.DiagramActivityCondition, Instances.class); for(Resource condition : query.find(graph, indexRoot)) { String value = Simantics.tryInvokeSCL(graph, condition, DIA.DiagramActivityCondition_test, parameter); if(value != null && !value.isEmpty()) return value; } return ""; } } static class IsInActiveModelListener implements Listener { ICanvasContext context; public IsInActiveModelListener(ICanvasContext context) { this.context = context; } @Override public void execute(final String result) { final Color color = (result.isEmpty()) ? context.getHintStack().getHint(KEY_ACTIVE_BACKGROUND) : context.getHintStack().getHint(KEY_INACTIVE_BACKGROUND); context.getThreadAccess().asyncExec(new Runnable() { @Override public void run() { ICanvasContext ctx = context; if (ctx == null) return; if (ctx.isDisposed()) return; IHintContext hints = ctx.getDefaultHintContext(); if (color != null) hints.setHint(Hints.KEY_BACKGROUND_COLOR, color); hints.setHint(KEY_IN_ACTIVE_MODEL, result); } }); } public void dispose() { context = null; } @Override public boolean isDisposed() { ICanvasContext context = this.context; return context == null || context.isDisposed(); } @Override public void exception(Throwable t) { ErrorLogger.defaultLogError(t); } } }