/******************************************************************************* * 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.modeling.ui.diagram.monitor; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; import org.simantics.browsing.ui.common.ErrorLogger; import org.simantics.common.color.Color; import org.simantics.databoard.Bindings; import org.simantics.db.AsyncReadGraph; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.Session; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.util.EvaluatingListener; import org.simantics.db.layer0.util.EvaluatingListener.Evaluation; import org.simantics.db.layer0.variable.RVI; import org.simantics.db.layer0.variable.Variables; import org.simantics.db.procedure.AsyncProcedure; import org.simantics.diagram.adapter.SyncElementFactory; import org.simantics.diagram.content.ConnectionUtil; import org.simantics.diagram.elements.MonitorClass; import org.simantics.diagram.stubs.DiagramResource; import org.simantics.diagram.stubs.G2DResource; import org.simantics.diagram.synchronization.CompositeHintSynchronizer; import org.simantics.diagram.synchronization.IHintSynchronizer; import org.simantics.diagram.synchronization.SynchronizationHints; import org.simantics.diagram.synchronization.graph.DiagramGraphUtil; import org.simantics.diagram.synchronization.graph.ElementLoader; import org.simantics.diagram.synchronization.graph.MonitorSynchronizer; import org.simantics.diagram.synchronization.graph.TransformSynchronizer; import org.simantics.diagram.ui.DiagramModelHints; import org.simantics.g2d.canvas.ICanvasContext; import org.simantics.g2d.diagram.IDiagram; import org.simantics.g2d.diagram.handler.DataElementMap; import org.simantics.g2d.diagram.handler.Relationship; import org.simantics.g2d.diagram.handler.RelationshipHandler; import org.simantics.g2d.element.ElementClass; import org.simantics.g2d.element.ElementHints; import org.simantics.g2d.element.ElementUtils; import org.simantics.g2d.element.IElement; import org.simantics.g2d.element.handler.TextEditor; import org.simantics.g2d.element.handler.impl.StaticObjectAdapter; import org.simantics.layer0.Layer0; import org.simantics.modeling.ModelingResources; import org.simantics.scl.runtime.function.Function1; import org.simantics.ui.colors.Colors; import org.simantics.ui.fonts.FontDescriptor; import org.simantics.ui.fonts.Fonts; import org.simantics.utils.datastructures.hints.IHintContext.Key; import org.simantics.utils.datastructures.hints.IHintContext.KeyOf; /** * TODO: recognize experiment disposal and reset monitor contents at that point * FIXME: monitor does not handle editing properly, and tries to include physical unit as part of numeric value */ public class MonitorClassFactory2 extends SyncElementFactory { private static final Key KEY_VARIABLE_LISTENER = new KeyOf(MonitorListener.class, "MONITOR_VARIABLE_LISTENER"); private static final String CLASS_ID = "Monitor"; private static final IHintSynchronizer HINT_SYNCHRONIZER = new CompositeHintSynchronizer( MonitorSynchronizer.INSTANCE, TransformSynchronizer.INSTANCE); public static ElementClass createMonitorClass(Resource elementType) { // set "default scale" to no scaling, 1.0, 1.0 return MonitorClass.create(1.0, 1.0, new StaticObjectAdapter(elementType)).setId(CLASS_ID); } // staticScale{X,Y} define the scale of the static monitor image public static ElementClass createMonitorClass(Resource elementType, IElement parentElement, HashMap substitutions, Object component, String suffix, double staticScaleX, double staticScaleY) { return MonitorClass.create(parentElement, substitutions, component, suffix, staticScaleX, staticScaleY, new StaticObjectAdapter(elementType)).setId(CLASS_ID); } @Override public void create(AsyncReadGraph graph, ICanvasContext canvas, IDiagram diagram, Resource elementType, AsyncProcedure procedure) { procedure.execute(graph, createMonitorClass(elementType)); } @Override public void load(ReadGraph graph, final ICanvasContext canvas, final IDiagram diagram, final Resource element, final IElement e) throws DatabaseException { if (!graph.hasStatement(element)) return; final Layer0 L0 = Layer0.getInstance(graph); final G2DResource G2D = G2DResource.getInstance(graph); final DiagramResource DIA = DiagramResource.getInstance(graph); // Must be done here to allow for the relationship manager to work properly. e.setHint(ElementHints.KEY_OBJECT, element); e.setHint(SynchronizationHints.HINT_SYNCHRONIZER, HINT_SYNCHRONIZER); AffineTransform at = DiagramGraphUtil.getAffineTransform(graph, element); ElementUtils.setTransform(e, at); // Load alignments Resource hAlign = graph.getPossibleObject(element, G2D.HasHorizontalAlignment); Resource vAlign = graph.getPossibleObject(element, G2D.HasVerticalAlignment); final Double borderWidth = graph.getPossibleRelatedValue(element, G2D.HasStrokeWidth); Double direction = graph.getPossibleRelatedValue(element, DIA.HasDirection); double bounds[] = DiagramGraphUtil.getPossibleRelatedDoubleArray(graph, element, G2D.HasBounds); if (bounds != null) { e.setHint(ElementHints.KEY_BOUNDS, new Rectangle2D.Double(bounds[0], bounds[1], bounds[2], bounds[3])); } if (hAlign != null) e.setHint(ElementHints.KEY_HORIZONTAL_ALIGN, DiagramGraphUtil.toAlignment(hAlign, G2D, MonitorClass.DEFAULT_HORIZONTAL_ALIGN)); if (vAlign != null) e.setHint(ElementHints.KEY_VERTICAL_ALIGN, DiagramGraphUtil.toVerticalAlignment(vAlign, G2D, MonitorClass.DEFAULT_VERTICAL_ALIGN)); if (direction != null) e.setHint(MonitorClass.KEY_DIRECTION, direction); if (borderWidth != null) e.setHint(MonitorClass.KEY_BORDER_WIDTH, borderWidth); String suffix = graph.getPossibleRelatedValue(element, DIA.HasMonitorSuffix, Bindings.STRING); if (suffix != null) e.setHint(MonitorClass.KEY_MONITOR_SUFFIX, suffix); String label = graph.getPossibleRelatedValue(element, L0.HasLabel); ElementUtils.setText(e, label); FontDescriptor fd = graph.getPossibleRelatedAdapter(element, DIA.HasFont, FontDescriptor.class); if(fd != null) ElementUtils.setTextFont(e, Fonts.awt(fd)); Color color = graph.getPossibleRelatedAdapter(element, DIA.HasColor, Color.class); if(color != null) ElementUtils.setTextColor(e, Colors.awt(color)); loadParentRelationships(graph, element, e); final Map substitutions = new HashMap(); substitutions.put("#v1", ""); final Resource diagramRuntime = diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RUNTIME_RESOURCE); if (diagramRuntime != null) { final Session session = graph.getSession(); // Resolve validator for monitor input if possible. final AtomicReference> validator = new AtomicReference>(); MonitorVariable monitorVariable = graph.syncRequest(new ResolveMonitorVariable(diagramRuntime, element)); boolean readOnly = true; if (monitorVariable != null) { Function1 func = monitorVariable.getVariable().getPossiblePropertyValue(graph, Variables.INPUT_VALIDATOR); validator.set( func ); if (func != null) e.setHint(MonitorClass.KEY_INPUT_VALIDATOR, func); RVI rvi = monitorVariable.getRVI(); if (rvi != null) e.setHint(MonitorClass.KEY_RVI, rvi); readOnly = Boolean.TRUE.equals( monitorVariable.getVariable().getPossiblePropertyValue(graph, Variables.READONLY, Bindings.BOOLEAN)); } TextEditor ed = null; if (!readOnly && (ed = e.getElementClass().getAtMostOneItemOfClass(TextEditor.class)) != null) { ed.setModifier(e, new TextEditor.Modifier() { @Override public String getValue(IElement e) { return MonitorClass.editText(e); } @Override public String isValid(IElement e, String text) { if (validator.get() != null) return validator.get().apply(text); return null; } @Override public void modify(final IElement e, final String text) { session.asyncRequest(new ResolveMonitorVariable(diagramRuntime, element), new EvaluatingListener( new EvaluatingListener.Criterion() { @Override public Evaluation evaluate(MonitorVariable result) { return result != null ? Evaluation.ACCEPT : Evaluation.IGNORE; } }) { @Override public void accepted(MonitorVariable var) { session.asyncRequest(new MonitorVariableWrite(var.getVariable(), text), e -> { if (e != null) ErrorLogger.defaultLogError(e); }); } @Override public void exception(Throwable t) { ErrorLogger.defaultLogError(t); } }); } }); } IElement mappedElement = diagram.getDiagramClass().getSingleItem(DataElementMap.class).getElement(diagram, element); MonitorListener monitorListener = new MonitorListener(element, canvas, diagram, substitutions); if (mappedElement != null) { MonitorListener oldListener = mappedElement.getHint(KEY_VARIABLE_LISTENER); if (oldListener != null) oldListener.dispose(); mappedElement.setHint(KEY_VARIABLE_LISTENER, monitorListener); } if(monitorVariable != null) graph.syncRequest(new MonitorVariableValueRequest(diagramRuntime, element), monitorListener); } } private void loadParentRelationships(ReadGraph graph, Resource element, IElement e) throws DatabaseException { DiagramResource DIA = DiagramResource.getInstance(graph); ModelingResources MOD = ModelingResources.getInstance(graph); Resource monitorComponent = graph.getPossibleObject(element, DIA.HasMonitorComponent); Resource parentDiagramElement = null; if (monitorComponent != null) parentDiagramElement = graph.getPossibleObject(monitorComponent, MOD.ComponentToElement); // Load parent relationship after all elements have been loaded. if (parentDiagramElement != null) { final Resource pde = parentDiagramElement; e.setHint(DiagramModelHints.KEY_ELEMENT_LOADER, new ElementLoader() { @Override public void load(ReadGraph g, IDiagram diagram, IElement element) throws DatabaseException { loadParentRelationship(g, diagram, element, pde); } }); } } boolean loadParentRelationship(ReadGraph g, IDiagram diagram, IElement element, Resource parentElementResource) throws DatabaseException { // If no relationship handler is available, stop loading. RelationshipHandler rh = diagram.getDiagramClass().getAtMostOneItemOfClass(RelationshipHandler.class); if (rh == null) return true; DiagramResource DIA = DiagramResource.getInstance(g); DataElementMap map = diagram.getDiagramClass().getSingleItem(DataElementMap.class); IElement parentElement = map.getElement(diagram, parentElementResource); if (parentElement != null) { element.setHint(ElementHints.KEY_PARENT_ELEMENT, parentElement); rh.claim(diagram, element, Relationship.CHILD_OF, parentElement); Resource tailNode = null; if (g.isInstanceOf(parentElementResource, DIA.Connection)) { tailNode = ConnectionUtil.getConnectionTailNode(g, parentElementResource); } if (tailNode != null) { IElement tailNodeElement = map.getElement(diagram, tailNode); if (parentElement != null) { element.setHint(ElementHints.KEY_PARENT_ELEMENT, tailNodeElement); rh.claim(diagram, element, Relationship.CHILD_OF, tailNodeElement); } } return true; } return false; } }