/* ========================
* JSynoptic : a free Synoptic editor
* ========================
*
* Project Info: http://jsynoptic.sourceforge.net/index.html
*
* This program is free software; you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Foundation;
* either version 2.1 of the License, or (at your option) any later version.
*
* This program 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* (C) Copyright 2001-2006, by :
* Corporate:
* EADS Astrium SAS
* Individual:
* Mathias Choquet
*
*
* $Id$
*
*/
package jsynoptic.plugins.svg;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.Arrays;
import java.util.List;
import java.util.ResourceBundle;
import jsynoptic.builtin.Abstract2DShape;
import jsynoptic.plugins.svg.SvgMapper.SvgFile;
import jsynoptic.plugins.svg.ui.SvgShapePropertiesPanel;
import simtools.data.DataException;
import simtools.data.DataSource;
import simtools.data.DataSourcePool;
import simtools.data.UnsupportedOperation;
import simtools.images.svg.SVGImageFactory;
import simtools.shapes.AbstractShape;
import simtools.shapes.ShapeListener;
import simtools.ui.JPropertiesPanel;
import simtools.ui.ResourceFinder;
import simtools.util.CurrentPathProvider;
import simtools.util.FileSerializer;
/**
* Class .SvgShape svg shape will draw an image (how suprising!) based on a svg
* file It will use SVGImageFactory to handle images and svg files
*/
public class SvgShape extends Abstract2DShape implements SVGImageFactory.FactoryListener {
static final long serialVersionUID = 8701487979765549739L;
public static ResourceBundle resources = ResourceFinder.get(SvgShape.class);
/**
* The factory instance
*/
public static SVGImageFactory factory = new SVGImageFactory();
/**
* The file serializer instance
*/
public static FileSerializer serializer = new FileSerializer();
/**
* image : reference to the BufferedImage in the ImageHolder Class
*/
protected transient BufferedImage image = null;
/**
* the source file of the bufferedImage displayed dynamically (by default or
* by the SvgMapper)
*/
protected transient File dynamicFile = null;
/**
* mapper : SvgMapper to manipulate DataSource source
*/
protected SvgMapper mapper;
protected transient DataSource dynamicSvgDs;
/**
* The index in the data source (used or not)
*/
protected transient long index;
/**
* boolean to indicate when the DataSource source has changed
*/
protected transient boolean dirtyImage = false;
/**
* the source file of the bufferedImage displayed by default This field is
* nice to have for the user, when changing the image => keep the location
*/
protected File defaultFile = null;
/**
* true : the image should be scaled to fit the shape size, else the shape
* will be resized
*/
protected boolean autoFit;
/**
* @param ox
* @param oy
* @param width
* @param height
*/
public SvgShape(int ox, int oy, int width, int height) {
// call the Abstract2DShape constructor
super(ox, oy, width, height);
image = null;
autoFit = true; // since 2.3.0 autoFit is true by default
}
/**
* If the image should be scaled to fit the shape size (autoFit true), else
* the shape will be resized
*/
public boolean isAutoFit() {
return autoFit;
}
/**
* If the image should be scaled to fit the shape size (autoFit true), else
* the shape will be resized
*/
public void setAutoFit(boolean autoFit) {
this.autoFit = autoFit;
}
/*
* (non-Javadoc)
*
* @see jsynoptic.builtin.Abstract1DShape#getDelegateShape()
*/
protected Shape getDelegateShape() {
Rectangle res = new Rectangle(_ox, _oy - _h, _w - 1, _h - 1);
return res;
}
/*
* (non-Javadoc)
*
* @see simtools.shapes.AbstractShape#cloneShape()
*/
protected AbstractShape cloneShape() {
SvgShape clone = (SvgShape) super.cloneShape();
if (clone.dynamicSvgDs != null) {
clone.dynamicSvgDs.addListener(clone);
clone.dynamicSvgDs.addEndNotificationListener(clone);
}
dynamicFile = getDynamicSvgFile();
// Update image
File f = (dynamicFile != null) ? dynamicFile : defaultFile;
if (defaultFile != null) {
if (!clone.autoFit) {
factory.load(f, clone);
} else {
factory.load(f, clone._w, clone._h, clone);
}
}
return clone;
}
/*
* (non-Javadoc)
*
* @see jsynoptic.builtin.Abstract1DShape#drawHook(java.awt.Graphics2D,
* boolean)
*/
protected void drawHook(Graphics2D g, boolean shapeDrawn) {
// if shape is already drawn
if (shapeDrawn) {
return;
}
// draw image before frame
if (image != null) {
g.drawImage(image, _ox, _oy - _h, _w, _h, null);
}
}
/* (non-Javadoc)
* @see jsynoptic.builtin.Abstract2DShape#getPanel(java.util.List)
*/
public JPropertiesPanel getPanel(List properties) {
if (properties == null){
return null;
}
if (properties.containsAll(Arrays.asList(new SvgShapePropertiesNames().getPropertyNames()))){
return new SvgShapePropertiesPanel(SvgPlugin.resources.getString("SvgShape"));
} else {
return super.getPanel(properties);
}
}
/* (non-Javadoc)
* @see simtools.shapes.AbstractShape#getShapeName()
*/
public String getShapeName(){
return SvgPlugin.resources.getString("SvgShape");
}
/**
* @return
* @throws DataException
*/
protected Object getSourceValue() throws DataException {
if (dynamicSvgDs != null) {
return dynamicSvgDs.getValue(index);
}
return null;
}
/**
* @return a SVG image File that matches with mapped data source value
* or null if not match found
*/
protected File getDynamicSvgFile() {
File f = null;
if ((mapper != null) && (dynamicSvgDs != null)) {
try {
SvgFile sf = (SvgFile) mapper.getMapping(getSourceValue());
f = (sf != null) ? sf.getfile() : null;
} catch (DataException e) {
}
}
return f;
}
/*
* (non-Javadoc)
*
* @see simtools.data.EndNotificationListener#notificationEnd(java.lang.Object)
*/
public void notificationEnd(Object referer) {
// DataSource source has changed
if (dirtyImage) {
dirtyImage = false;
boolean repaint = false;
dynamicFile = getDynamicSvgFile();
File f = (dynamicFile != null) ? dynamicFile : defaultFile;
if (f != null) {
// SVGFactory get the best BufferedImage he can, immediatly
BufferedImage oldImage = factory.get(f, _w, _h);
if ((oldImage == null) || (oldImage.getHeight() != _h) || (oldImage.getWidth() != _w)) {
// if the image is null or is not at the good size,
// SVGFactory scale it
factory.scale(f, _w, _h, this);
}
if (oldImage != null) {
// in each case, if the image is not null, the image (good
// or not) will be drawn
image = oldImage;
repaint = true;
}
} else {
if (image != null) {
// if f == null, drawn an empty SvgShape (without image)
repaint = true;
}
image = null;
}
// repaint our area if the image changed, do a redraw only when
// necessary
if (repaint) {
dirtyRectangle = new Rectangle(_ox, _oy - _h, _w, _h);
dirty = true;
}
}
super.notificationEnd(referer);
}
/*
* (non-Javadoc)
*
* @see simtools.data.DataSourceListener#DataSourceReplaced(simtools.data.DataSource,
* simtools.data.DataSource)
*/
public void DataSourceReplaced(DataSource oldData, DataSource newData) {
if (oldData == dynamicSvgDs) {
dynamicSvgDs = newData;
if (dynamicSvgDs != null) {
dynamicSvgDs.addListener(this);
dynamicSvgDs.addEndNotificationListener(this);
try {
index = dynamicSvgDs.getLastIndex();
} catch (UnsupportedOperation e) {
index = 0;
}
}
oldData.removeListener(this);
oldData.removeEndNotificationListener(this);
dirtyImage = true;
}
super.DataSourceReplaced(oldData, newData);
}
/*
* (non-Javadoc)
*
* @see simtools.data.DataSourceListener#DataSourceIndexRangeChanged(simtools.data.DataSource,
* long, long)
*/
public void DataSourceIndexRangeChanged(DataSource ds, long startIndex, long lastIndex) {
// update the datasource index and
if (ds.equals(this.dynamicSvgDs)) {
index = lastIndex;
dirtyImage = true;
}
super.DataSourceIndexRangeChanged(ds, startIndex, lastIndex);
}
/*
* (non-Javadoc)
*
* @see simtools.data.DataSourceListener#DataSourceValueChanged(simtools.data.DataSource,
* long, long)
*/
public void DataSourceValueChanged(DataSource ds, long minIndex, long maxIndex) {
if (ds.equals(this.dynamicSvgDs)) {
// We care only about our index value change
if ((index >= minIndex) && (index <= maxIndex)) {
dirtyImage = true;
}
}
super.DataSourceValueChanged(ds, minIndex, maxIndex);
}
/* (non-Javadoc)
* @see simtools.shapes.AbstractShape#subscribeToDataNotifications()
*/
public void processShapeRestoring(){
if (dynamicSvgDs!=null) {
dynamicSvgDs.addListener(this);
dynamicSvgDs.addEndNotificationListener(this);
try {
index = dynamicSvgDs.getLastIndex();
} catch (UnsupportedOperation uo1) {
index = 0;
}
}
super.processShapeRestoring();
}
/* (non-Javadoc)
* @see simtools.shapes.AbstractShape#unsubscribeToDataNotifications()
*/
public void processShapeRemoving(){
if (dynamicSvgDs != null) {
dynamicSvgDs.removeListener(this);
dynamicSvgDs.removeEndNotificationListener(this);
}
super.processShapeRemoving();
}
/*
* (non-Javadoc)
*
* @see jsynoptic.builtin.Abstract1DShape#resize(int, int)
*/
public void resize(int dx, int dy) {
super.resize(dx, dy);
if (canResize()) {
File f = (dynamicFile != null) ? dynamicFile : defaultFile;
if (f != null) {
factory.scale(f, _w, _h, this);
}
}
}
/**
* Method <b>writeObject</b> method to write the shape in an OutputStream
* (for example a file) Parameters:
*
* @param out
* the stream in which the object is saved
* @throws java.io.IOException
*
*/
private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException {
out.defaultWriteObject();
// write the Datasource
DataSourcePool.global.writeDataSource(out, dynamicSvgDs);
// nodef is true if the there is not a Svg file selected by the shape
boolean nodef = defaultFile == null;
out.writeBoolean(nodef);
if (!nodef) {
// write the svg default file if nodef is false
factory.write(out, defaultFile, this, serializer, CurrentPathProvider.currentPathProvider.getCurrentPath());
}
}
/**
* Method <b>readObject</b> method to read the shape from an InputStream
* (for example a file) Parameters:
*
* @param in
* out the stream from which the object is read
* @throws java.lang.ClassNotFoundException
* @throws java.io.IOException
*
*/
private void readObject(java.io.ObjectInputStream in) throws java.lang.ClassNotFoundException, java.io.IOException {
in.defaultReadObject();
dynamicSvgDs = DataSourcePool.global.readDataSource(in);
// read the nodef boolean
boolean nodef = in.readBoolean();
// nodef is true if there is not a Svg file selected by the shape
if (!nodef) {
// read the svg default file if nodef is false
defaultFile = factory.read(in, this, serializer, CurrentPathProvider.currentPathProvider.getCurrentPath());
} else {
defaultFile = null;
}
// read the datasource and attach it to the shape
if (dynamicSvgDs != null) {
try {
dynamicSvgDs.addListener(this);
dynamicSvgDs.addEndNotificationListener(this);
index = dynamicSvgDs.getLastIndex();
} catch (UnsupportedOperation uo1) {
index = 0;
}
}
if (mapper != null) {
mapper = SvgMapper.updateSvgMapper(mapper);
}
dynamicFile = getDynamicSvgFile();
// Update image
File f = (dynamicFile != null) ? dynamicFile : defaultFile;
if (f != null) {
if (!autoFit) {
// scale image of the default file to fit object size
factory.load(f, this);
} else {
// change object size to fit image of the default file
factory.load(f, _w, _h, this);
}
}
}
/*
* (non-Javadoc)
*
* @see simtools.shapes.AbstractShape#getProperty(java.lang.String)
*/
public Object getPropertyValue(String name) {
Object res = super.getPropertyValue(name);
if (name.equalsIgnoreCase("SVG_FIT_TO_OBJECT")) {
res = new Boolean(autoFit);
} else if (name.equalsIgnoreCase("SVG_FILE")) {
res = defaultFile;
} else if (name.equalsIgnoreCase("SVG_MAPPER")) {
res = mapper;
} else if (name.equalsIgnoreCase("SVG_MAPPER_SOURCE")) {
res = dynamicSvgDs;
}
return res;
}
/*
* (non-Javadoc)
*
* @see simtools.shapes.AbstractShape#setProperty(java.lang.String,
* java.lang.Object)
*/
public void setPropertyValue(String name, Object value) {
super.setPropertyValue(name, value);
if (name.equalsIgnoreCase("SVG_FIT_TO_OBJECT")) {
if (value instanceof Boolean) {
// set the autoFit value
autoFit = ((Boolean) value).booleanValue();
}
} else if (name.equalsIgnoreCase("SVG_FILE")) {
if (value instanceof File) {
// set the default file
defaultFile = (File) value;
} else {
defaultFile = null;
image = null;
}
dirty = true;
} else if (name.equalsIgnoreCase("SVG_MAPPER")) {
if (value instanceof SvgMapper) {
// set the svg mapper
mapper = (SvgMapper) value;
} else {
mapper = null;
}
} else if (name.equalsIgnoreCase("SVG_MAPPER_SOURCE")) {
setSvgDynamicDataSource((DataSource) value);
}
}
protected void setSvgDynamicDataSource(DataSource value){
if (dynamicSvgDs != null) {
// if there is already a source for this shape,
// remove it
dynamicSvgDs.removeListener(this);
dynamicSvgDs.removeEndNotificationListener(this);
}
if (value instanceof DataSource) {
// set and attach the Datasource source to the svgShape
dynamicSvgDs = (DataSource) value;
try {
index = dynamicSvgDs.getLastIndex();
} catch (UnsupportedOperation e) {
index = 0;
}
dynamicSvgDs.addListener(SvgShape.this);
dynamicSvgDs.addEndNotificationListener(SvgShape.this);
} else {
dynamicSvgDs = null;
}
dynamicFile = getDynamicSvgFile();
File f = (dynamicFile != null) ? dynamicFile : defaultFile;
if (f != null) {
if (!autoFit) {
// scale image of the default file to fit object size
factory.load(f, this);
} else {
// change object size to fit image of the default file
factory.load(f, _w, _h, this);
}
}
}
/*
* (non-Javadoc)
*
* @see simtools.images.svg.SVGImageFactory.FactoryListener#done(java.awt.image.BufferedImage)
*/
public void done(BufferedImage img) {
image = img;
if (img == null) {
return;
}
Rectangle oldRectangle = new Rectangle(_ox, _oy - _h, _w, _h);
// update the shape size
_w = img.getWidth();
_h = img.getHeight();
updateBounds();
dirty = true;
notifyChange(oldRectangle);
}
/*
* (non-Javadoc)
*
* @see simtools.images.svg.SVGImageFactory.FactoryListener#failed(java.lang.String)
*/
public void failed(String msg) {
System.err.println("Cannot load " + defaultFile.getName() + " reason is : " + msg);
image = null;
}
/*
* (non-Javadoc)
*
* @see simtools.shapes.AbstractShape#removeListener(simtools.shapes.ShapeListener)
*/
public void removeListener(ShapeListener sl) {
super.removeListener(sl);
if ((listeners.size() == 0) && (defaultFile != null)) {
factory.release(defaultFile, this);
}
}
public String[] getPropertyNames() {
if (_propertyNames == null) {
_propertyNames = new SvgShapePropertiesNames().getPropertyNames();
}
return _propertyNames;
}
public static class SvgShapePropertiesNames extends Abstract2DShapePropertiesNames {
private static transient String[] props = new String[] {
"SVG_FIT_TO_OBJECT",
"SVG_FILE",
"SVG_MAPPER",
"SVG_MAPPER_SOURCE" };
public SvgShapePropertiesNames() {
super();
for (int i = 0; i < props.length; i++) {
propertyNames.add(props[i]);
}
}
}
/*
* (non-Javadoc)
*
* @see simtools.shapes.AbstractShape#wipeOff()
*/
public void wipeOff() {
factory.wipeOff();
super.wipeOff();
}
}