/* * This file is part of the openSCADA project * Copyright (C) 2006-2011 TH4 SYSTEMS GmbH (http://th4-systems.com) * * openSCADA is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * openSCADA is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with openSCADA. If not, see * <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License. */ package org.openscada.vi.ui.draw2d; import java.net.URI; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.eclipse.draw2d.ConnectionLayer; import org.eclipse.draw2d.FigureCanvas; import org.eclipse.draw2d.IFigure; import org.eclipse.draw2d.Layer; import org.eclipse.draw2d.LayeredPane; import org.eclipse.draw2d.ManhattanConnectionRouter; import org.eclipse.draw2d.StackLayout; import org.eclipse.draw2d.geometry.Dimension; import org.eclipse.draw2d.geometry.PrecisionDimension; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.resource.LocalResourceManager; import org.eclipse.swt.events.ControlAdapter; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.statushandlers.StatusManager; import org.openscada.ui.utils.status.StatusHelper; import org.openscada.vi.model.VisualInterface.Primitive; import org.openscada.vi.model.VisualInterface.Symbol; import org.openscada.vi.ui.draw2d.impl.ScalableLayeredPane; import org.openscada.vi.ui.draw2d.loader.StaticSymbolLoader; import org.openscada.vi.ui.draw2d.loader.SymbolLoader; import org.openscada.vi.ui.draw2d.loader.XMISymbolLoader; import org.openscada.vi.ui.draw2d.preferences.PreferenceConstants; import org.openscada.vi.ui.draw2d.primitives.Controller; public class VisualInterfaceViewer extends Composite { private final ViewElementFactory factory; private final FigureCanvas canvas; private final LocalResourceManager manager; private SymbolController controller; private final Map<String, Object> scriptObjects; private final Map<String, String> initialProperties; private Boolean zooming; private LayeredPane pane; private IFigure figure; private Symbol symbol; private Layer layer; private ConnectionLayer connectionLayer; /** * Create a new viewer * @param parent the parent composite * @param style the composite style * @param uri the URI from which the root symbol should be loaded * @param scriptObjects optionally some script objects that get injected into the script context. May be <code>null</code>. * @param properties additional properties for symbol creation. May be <code>null</code>. These properties override the symbols properties. */ public VisualInterfaceViewer ( final Composite parent, final int style, final URI uri, final Map<String, Object> scriptObjects, final Map<String, String> properties ) { this ( parent, style, uri.toString (), scriptObjects, properties ); } /** * Create a new viewer * @param parent the parent composite * @param style the composite style * @param uri the URI from which the root symbol should be loaded * @param scriptObjects optionally some script objects that get injected into the script context. May be <code>null</code> * @param properties additional properties for symbol creation. May be <code>null</code>. These properties override the symbols properties. */ public VisualInterfaceViewer ( final Composite parent, final int style, final String uri, final Map<String, Object> scriptObjects, final Map<String, String> properties ) { this ( parent, style, new XMISymbolLoader ( uri ), scriptObjects, properties ); } public VisualInterfaceViewer ( final Composite parent, final int style, final Symbol symbol, final ClassLoader classLoader, final Map<String, Object> scriptObjects, final Map<String, String> properties ) { this ( parent, style, new StaticSymbolLoader ( symbol, classLoader ), scriptObjects, properties ); } public VisualInterfaceViewer ( final Composite parent, final int style, final SymbolLoader loader, final Map<String, Object> scriptObjects, final Map<String, String> properties ) { super ( parent, style ); this.initialProperties = properties == null ? Collections.<String, String> emptyMap () : properties; this.scriptObjects = scriptObjects; this.manager = new LocalResourceManager ( JFaceResources.getResources () ); addDisposeListener ( new DisposeListener () { @Override public void widgetDisposed ( final DisposeEvent e ) { internalDispose (); } } ); setLayout ( new FillLayout () ); this.canvas = createCanvas (); setZooming ( null ); this.factory = new ViewElementFactory ( this.canvas, this.manager ); try { this.pane = createPane (); this.layer = new Layer (); this.connectionLayer = new ConnectionLayer (); this.connectionLayer.setConnectionRouter ( new ManhattanConnectionRouter () ); this.layer.setLayoutManager ( new StackLayout () ); this.pane.add ( this.connectionLayer ); this.pane.add ( this.layer ); loader.load (); this.symbol = loader.getSymbol (); create ( loader.getSymbol (), loader.getClassLoader () ); applyColor ( loader.getSymbol () ); } catch ( final Exception e ) { this.canvas.setContents ( Helper.createErrorFigure ( e ) ); } } private LayeredPane createPane () { if ( Activator.getDefault ().getPreferenceStore ().getBoolean ( PreferenceConstants.P_DEFAULT_HAIRLINE ) ) { return new ScalableLayeredPane (); } else { return new org.eclipse.draw2d.ScalableLayeredPane (); } } public boolean isZooming () { if ( this.zooming == null ) { return Activator.getDefault ().getPreferenceStore ().getBoolean ( PreferenceConstants.P_DEFAULT_ZOOMING ); } else { return this.zooming; } } public void setZooming ( final Boolean zooming ) { this.zooming = zooming; this.canvas.setScrollBarVisibility ( isZooming () ? FigureCanvas.NEVER : FigureCanvas.AUTOMATIC ); } private void applyColor ( final Symbol symbol ) { final RGB color = org.openscada.vi.ui.draw2d.primitives.Helper.makeColor ( symbol.getBackgroundColor () ); if ( color != null ) { this.canvas.setBackground ( this.manager.createColor ( color ) ); } } protected FigureCanvas createCanvas () { final FigureCanvas canvas = new FigureCanvas ( this ); addControlListener ( new ControlAdapter () { @Override public void controlResized ( final ControlEvent e ) { handleResize ( getBounds () ); } } ); return canvas; } protected void handleResize ( final Rectangle bounds ) { if ( !isZooming () ) { setZoom ( 1.0 ); return; } final Dimension prefSize = getPreferredSize ( bounds ); final double ar = prefSize.preciseWidth () / prefSize.preciseHeight (); double newHeight = bounds.width / ar; final double zoom; if ( newHeight > bounds.height ) { newHeight = bounds.height; } zoom = newHeight / prefSize.preciseHeight (); setZoom ( zoom ); } private Dimension getPreferredSize ( final Rectangle bounds ) { if ( this.symbol != null && this.symbol.getDesignSize () != null ) { return new PrecisionDimension ( this.symbol.getDesignSize ().getWidth (), this.symbol.getDesignSize ().getHeight () ); } else if ( this.figure != null ) { return this.figure.getPreferredSize ( bounds.width, bounds.height ); } else { return new PrecisionDimension ( bounds.width, bounds.height ); } } private void setZoom ( final double newZoom ) { if ( this.pane instanceof org.eclipse.draw2d.ScalableLayeredPane ) { ( (org.eclipse.draw2d.ScalableLayeredPane)this.pane ).setScale ( newZoom ); } else if ( this.pane instanceof ScalableLayeredPane ) { ( (ScalableLayeredPane)this.pane ).setScale ( newZoom ); } } protected void create ( final Symbol symbol, final ClassLoader classLoader ) { try { final Map<String, String> properties = new HashMap<String, String> ( symbol.getProperties ().size () ); for ( final Map.Entry<String, String> entry : symbol.getProperties ().entrySet () ) { properties.put ( entry.getKey (), entry.getValue () ); } properties.putAll ( this.initialProperties ); this.controller = new SymbolController ( symbol, classLoader, properties, this.scriptObjects ); final Controller controller = create ( symbol.getRoot () ); this.controller.init (); this.layer.add ( this.figure = controller.getFigure () ); this.factory.createConnections ( this.connectionLayer, this.controller, symbol.getConnections () ); } catch ( final Exception e ) { StatusManager.getManager ().handle ( StatusHelper.convertStatus ( Activator.PLUGIN_ID, e ), StatusManager.LOG ); this.layer.add ( this.figure = Helper.createErrorFigure ( e ) ); } this.canvas.setContents ( this.pane ); } protected Controller create ( final Primitive element ) { return this.factory.create ( this.controller, element ); } private void internalDispose () { if ( this.controller != null ) { this.controller.dispose (); } if ( this.manager != null ) { this.manager.dispose (); } } public void addSummaryListener ( final SummaryListener listener ) { if ( this.controller != null ) { this.controller.addSummaryListener ( listener ); } } public void removeSummaryListener ( final SummaryListener listener ) { if ( this.controller != null ) { this.controller.removeSummaryListener ( listener ); } } }