/* * 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.primitives; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import javax.script.ScriptException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.draw2d.Border; import org.eclipse.draw2d.CompoundBorder; import org.eclipse.draw2d.GroupBoxBorder; import org.eclipse.draw2d.IFigure; import org.eclipse.draw2d.LineBorder; import org.eclipse.draw2d.MarginBorder; import org.eclipse.draw2d.MouseEvent; import org.eclipse.draw2d.MouseListener; import org.eclipse.draw2d.TitleBarBorder; import org.eclipse.draw2d.geometry.PrecisionDimension; import org.eclipse.draw2d.geometry.PrecisionRectangle; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.jface.resource.ResourceManager; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.statushandlers.StatusManager; import org.openscada.ui.utils.blink.AbstractBlinker; import org.openscada.utils.script.ScriptExecutor; import org.openscada.vi.model.VisualInterface.Cursor; import org.openscada.vi.model.VisualInterface.Dimension; import org.openscada.vi.model.VisualInterface.Figure; import org.openscada.vi.model.VisualInterface.SystemCursor; import org.openscada.vi.model.VisualInterface.VisualInterfaceFactory; import org.openscada.vi.ui.draw2d.Activator; import org.openscada.vi.ui.draw2d.SymbolController; /** * A figure controller * <p> * This figure controller needs to know when the figure it controls was added * or removed. Since draw2d does not provide a "addRemovedListener" for IFigure * but a {@link IFigure#removeNotify()} that can be overridden it is the * responsibility of the implementing controller to call {@link #start()} and * {@link #stop()} when the implementing {@link IFigure#addNotify()} and * {@link IFigure#removeNotify()} get called. * So create your figure like the following snippet shows: * <code><pre> IFigure figure = new PolylineShape () { public void addNotify () { super.addNotify (); start (); // FigureController.start (); } public void removeNotify () { stop (); // FigureController.stop (); super.removeNotify (); } }; * </pre></code> * </p> * @author Jens Reimann * */ public abstract class FigureController implements Controller { private static interface ColorApplier { public void applyColor ( IFigure figure, Color color ); } private static class ForegroundApplier implements ColorApplier { @Override public void applyColor ( final IFigure figure, final Color color ) { figure.setForegroundColor ( color ); } } private static class BackgroundApplier implements ColorApplier { @Override public void applyColor ( final IFigure figure, final Color color ) { figure.setBackgroundColor ( color ); } } private static interface ColorHandler { public void start (); public void stop (); } private static class DefaultColor implements ColorHandler { private final IFigure figure; private final ColorApplier applier; public DefaultColor ( final IFigure figure, final ColorApplier applier ) { this.figure = figure; this.applier = applier; } @Override public void start () { this.applier.applyColor ( this.figure, null ); } @Override public void stop () { // nothing to do } } private static class StaticColor implements ColorHandler { private final IFigure figure; private final ColorApplier applier; private final Color color; public StaticColor ( final IFigure figure, final ColorApplier applier, final Color color ) { this.figure = figure; this.applier = applier; this.color = color; } @Override public void start () { this.applier.applyColor ( this.figure, this.color ); } @Override public void stop () { // nothing to do } } private static class BlinkingColor extends AbstractBlinker implements ColorHandler { private final IFigure figure; private final ColorApplier applier; private final Color onColor; private final Color offColor; private final int frequency; public BlinkingColor ( final IFigure figure, final ColorApplier applier, final Color onColor, final Color offColor, final int frequency ) { this.figure = figure; this.applier = applier; this.onColor = onColor; this.offColor = offColor; this.frequency = frequency; } @Override public void start () { enableBlinking ( this.frequency ); } @Override public void stop () { super.dispose (); } @Override public void toggle ( final boolean on ) { this.applier.applyColor ( this.figure, on ? this.onColor : this.offColor ); } } protected final ResourceManager manager; private final SymbolController controller; private ScriptExecutor onClick; private ScriptExecutor onDoubleClick; private final Map<Object, ColorHandler> colorHandler = new HashMap<Object, FigureController.ColorHandler> (); private boolean started; public FigureController ( final SymbolController controller, final ResourceManager manager ) { this.manager = manager; this.controller = controller; } protected void applyCommon ( final Figure figure ) { setBorder ( figure.getBorder () ); setBackgroundColor ( figure.getBackgroundColor () ); setForegroundColor ( figure.getForegroundColor () ); setPreferredSize ( create ( figure.getSize () ) ); setCursor ( convert ( figure.getCursor () ) ); setVisible ( figure.isVisible () ); setOpaque ( figure.getOpaque () ); try { this.onClick = this.controller.createScriptExecutor ( figure.getOnClick () ); this.onDoubleClick = this.controller.createScriptExecutor ( figure.getOnDoubleClick () ); } catch ( final ScriptException e ) { throw new RuntimeException ( "Failed to initialize", e ); } if ( this.onClick != null || this.onDoubleClick != null ) { getFigure ().addMouseListener ( new MouseListener.Stub () { @Override public void mouseReleased ( final MouseEvent me ) { handleOnClick ( me ); } @Override public void mouseDoubleClicked ( final MouseEvent me ) { handleOnDoubleClick ( me ); } } ); } } public void setCursor ( final String cursor ) { getFigure ().setCursor ( getCursor ( cursor ) ); } public static enum SWTCursors { ARROW ( SWT.CURSOR_ARROW ), HAND ( SWT.CURSOR_HAND ), HELP ( SWT.CURSOR_HELP ), WAIT ( SWT.CURSOR_WAIT ), CROSS ( SWT.CURSOR_CROSS ), IBEAM ( SWT.CURSOR_IBEAM ), NO ( SWT.CURSOR_NO ); private int id; private SWTCursors ( final int id ) { this.id = id; } public int id () { return this.id; } } protected org.eclipse.swt.graphics.Cursor getCursor ( final String cursor ) { try { final SWTCursors cursorValue = SWTCursors.valueOf ( cursor ); return Display.getDefault ().getSystemCursor ( cursorValue.id () ); } catch ( final Exception e ) { // TODO: try to load as resource instead return null; } } private String convert ( final Cursor cursor ) { if ( cursor instanceof SystemCursor ) { return ( (SystemCursor)cursor ).getType ().toString (); } return null; } protected void handleOnDoubleClick ( final MouseEvent me ) { this.controller.debugLog ( String.format ( "Double click: %s", me ) ); final Map<String, Object> scriptObjects = new LinkedHashMap<String, Object> ( 1 ); scriptObjects.put ( "event", me ); this.controller.execute ( this.onDoubleClick, scriptObjects ); } protected void handleOnClick ( final MouseEvent me ) { this.controller.debugLog ( String.format ( "Click: %s", me ) ); final Map<String, Object> scriptObjects = new LinkedHashMap<String, Object> ( 1 ); scriptObjects.put ( "event", me ); this.controller.execute ( this.onClick, scriptObjects ); } public void setPreferredSize ( final org.eclipse.draw2d.geometry.Dimension size ) { getFigure ().setPreferredSize ( size ); } public void setSize ( final org.eclipse.draw2d.geometry.Dimension size ) { if ( size != null ) { getFigure ().setSize ( size ); } } public void setSize ( final double width, final double height ) { final Rectangle b = getFigure ().getBounds (); getFigure ().setBounds ( new PrecisionRectangle ( b.preciseX (), b.preciseY (), width, height ) ); } public void setWidth ( final double width ) { final Rectangle b = getFigure ().getBounds (); getFigure ().setBounds ( new PrecisionRectangle ( b.preciseX (), b.preciseY (), width, b.preciseHeight () ) ); } public void setHeight ( final double height ) { final Rectangle b = getFigure ().getBounds (); getFigure ().setBounds ( new PrecisionRectangle ( b.preciseX (), b.preciseY (), b.preciseWidth (), height ) ); } public Dimension getPreferredSize () { final Dimension dimension = VisualInterfaceFactory.eINSTANCE.createDimension (); final org.eclipse.draw2d.geometry.Dimension size = getFigure ().getPreferredSize (); dimension.setHeight ( size.preciseWidth () ); dimension.setWidth ( size.preciseWidth () ); return dimension; } public void setPreferredWidth ( final double value ) { final PrecisionDimension dim = new PrecisionDimension (); dim.setPreciseWidth ( value ); dim.setPreciseHeight ( getFigure ().getPreferredSize ().preciseHeight () ); getFigure ().setPreferredSize ( dim ); } public void setPreferredHeight ( final double value ) { final PrecisionDimension dim = new PrecisionDimension (); dim.setPreciseWidth ( getFigure ().getPreferredSize ().preciseWidth () ); dim.setPreciseHeight ( value ); getFigure ().setPreferredSize ( dim ); } public void setBorder ( final String border ) { getFigure ().setBorder ( makeBorder ( border ) ); } public void setBackgroundColor ( final String color ) { final ColorHandler colorHandler = makeColorHandler ( color, new BackgroundApplier () ); setColor ( "backgroundColor", colorHandler ); } public void setForegroundColor ( final String color ) { final ColorHandler colorHandler = makeColorHandler ( color, new ForegroundApplier () ); setColor ( "foregroundColor", colorHandler ); } protected void setColor ( final Object key, final ColorHandler colorHandler ) { final ColorHandler oldColorHandler = this.colorHandler.put ( key, colorHandler ); if ( oldColorHandler != null ) { // dispose oldColorHandler.stop (); } // we are started, so start the color if ( this.started ) { colorHandler.start (); } } public void start () { if ( this.started ) { return; } this.started = true; // start all colors for ( final ColorHandler color : this.colorHandler.values () ) { color.start (); } } public void stop () { if ( !this.started ) { return; } this.started = false; // stop all colors for ( final ColorHandler color : this.colorHandler.values () ) { color.stop (); } } public void setVisible ( final boolean flag ) { getFigure ().setVisible ( flag ); } public abstract void setOpaque ( final Boolean flag ); protected void setOpaque ( final Boolean flag, final boolean defaultValue ) { if ( flag == null ) { getFigure ().setOpaque ( defaultValue ); } else { getFigure ().setOpaque ( flag ); } } /** * Create a new border * <p> * 3 * GROUP:x * COMPOUND:[GROUP:2|GROUP:3] * </P> * @param border * @return */ protected Border makeBorder ( final String border ) { if ( border == null || border.isEmpty () ) { return null; } if ( border.matches ( "[0-9]+" ) ) { return new LineBorder ( Integer.parseInt ( border ) ); } if ( border.startsWith ( "LINE:" ) ) { final Map<String, String> args = parseBorderArguments ( "lineWidth", border.substring ( "LINE:".length () ) ); final LineBorder lineBorder = new LineBorder (); if ( args.containsKey ( "lineWidth" ) ) { lineBorder.setWidth ( Integer.parseInt ( args.get ( "lineWidth" ) ) ); } lineBorder.setColor ( createColor ( Helper.makeColor ( args.get ( "color" ) ) ) ); return lineBorder; } if ( border.startsWith ( "GROUP:" ) ) { final GroupBoxBorder groupBorder = new GroupBoxBorder (); final Map<String, String> args = parseBorderArguments ( "text", border.substring ( "GROUP:".length () ) ); if ( args.containsKey ( "text" ) ) { groupBorder.setLabel ( args.get ( "text" ) ); } final Color color = createColor ( Helper.makeColor ( args.get ( "textColor" ) ) ); if ( color != null ) { groupBorder.setTextColor ( color ); } return groupBorder; } if ( border.startsWith ( "TITLE:" ) ) { final TitleBarBorder titleBorder = new TitleBarBorder (); final Map<String, String> args = parseBorderArguments ( "text", border.substring ( "TITLE:".length () ) ); if ( args.containsKey ( "text" ) ) { titleBorder.setLabel ( args.get ( "text" ) ); } final Color color = createColor ( Helper.makeColor ( args.get ( "textColor" ) ) ); if ( color != null ) { titleBorder.setTextColor ( color ); } final Color backgroundColor = createColor ( Helper.makeColor ( args.get ( "backgroundColor" ) ) ); if ( backgroundColor != null ) { titleBorder.setBackgroundColor ( backgroundColor ); } return titleBorder; } if ( border.startsWith ( "MARGIN:" ) ) { final Map<String, String> args = parseBorderArguments ( "inset", border.substring ( "MARGIN:".length () ) ); final int inset; if ( args.containsKey ( "inset" ) ) { inset = Integer.parseInt ( args.get ( "inset" ) ); } else { inset = 0; } return new MarginBorder ( inset ); } if ( border.startsWith ( "COMPOUND:" ) ) { final Map<String, String> args = parseBorderArguments ( "", border.substring ( "COMPOUND:".length () ) ); return new CompoundBorder ( makeBorder ( args.get ( "outer" ) ), makeBorder ( args.get ( "inner" ) ) ); } StatusManager.getManager ().handle ( new Status ( IStatus.WARNING, Activator.PLUGIN_ID, "Invalid border string: " + border ), StatusManager.LOG ); return null; } protected Map<String, String> parseBorderArguments ( final String singleArgumentName, final String border ) { if ( border == null || border.isEmpty () ) { return Collections.emptyMap (); } if ( border.startsWith ( "[" ) && border.endsWith ( "]" ) ) { final Map<String, String> result = new LinkedHashMap<String, String> (); final String str = border.substring ( 1, border.length () - 1 ); for ( final String tok : str.split ( "[, \\t\\n\\r]" ) ) { final String[] ele = tok.split ( "=", 2 ); if ( ele.length > 1 ) { result.put ( ele[0], ele[1] ); } } return result; } else { final Map<String, String> result = new LinkedHashMap<String, String> ( 1 ); result.put ( singleArgumentName, border ); return result; } } protected org.eclipse.draw2d.geometry.Dimension create ( final Dimension dimension ) { if ( dimension == null ) { return null; } return new PrecisionDimension ( dimension.getWidth (), dimension.getHeight () ); } protected ColorHandler makeColorHandler ( final String color, final ColorApplier applier ) { if ( color == null ) { return new DefaultColor ( getFigure (), applier ); } if ( color.startsWith ( "#" ) && !color.contains ( "|" ) ) { return new StaticColor ( getFigure (), applier, createColor ( Helper.makeColor ( color ) ) ); } else if ( ( color.startsWith ( "#" ) || color.startsWith ( "|" ) ) && color.contains ( "|" ) ) { final String tok[] = color.split ( "\\|" ); final Color onColor = createColor ( Helper.makeColor ( tok[0] ) ); Color offColor; int frequency = 1; if ( tok.length > 1 ) { offColor = createColor ( Helper.makeColor ( tok[1] ) ); } else { offColor = null; } if ( tok.length > 2 ) { frequency = Integer.parseInt ( tok[2] ); } return new BlinkingColor ( getFigure (), applier, onColor, offColor, frequency ); } return new DefaultColor ( getFigure (), applier ); } protected Color createColor ( final RGB rgb ) { if ( rgb == null ) { return null; } return this.manager.createColor ( rgb ); } }