/* ========================
* 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-2003, by :
* Corporate:
* Astrium SAS
* EADS CRC
* Individual:
* Nicolas Brodu
*
* $Id: AutomatonShape.java,v 1.21 2008/09/05 12:23:22 ogor Exp $
*
* Changes
* -------
* 26-Nov-2003 : Initial version (NB);
*
*/
package jsynoptic.builtin;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.io.Serializable;
import java.text.NumberFormat;
import java.util.Iterator;
import java.util.ResourceBundle;
import java.util.Vector;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.undo.CompoundEdit;
import jsynoptic.base.ContextualActionProvider;
import jsynoptic.base.Linkable;
import jsynoptic.ui.JSynoptic;
import jsynoptic.ui.LongAction;
import simtools.data.DataException;
import simtools.data.DataInfo;
import simtools.data.DataSource;
import simtools.data.DataSourceListener;
import simtools.data.DataSourcePool;
import simtools.data.EndNotificationListener;
import simtools.data.UnsupportedOperation;
import simtools.shapes.AbstractShape;
import simtools.ui.NumberField;
import simtools.ui.ResourceFinder;
import simtools.ui.SourceTree;
/**
* A 2D automaton, driven by a data source. It consists in a grid which cells have configurable
* background and label.
* Actions are associated to data source values or ranges. They are processed dynamically
* when data arrives.
*/
public class AutomatonShape extends AbstractShape implements simtools.diagram.Resizable, DataSourceListener, ContextualActionProvider, Linkable, EndNotificationListener {
static final long serialVersionUID = -2166910190999229946L;
// Reuse resources from Abstract 1D shape.
// Even if this class does not extends it, it uses common text
public static ResourceBundle resources = ResourceFinder.get(AutomatonShape.class, Abstract1DShape.resources);
public static int MIN_CELL_WIDTH = 5;
public static int MIN_CELL_HEIGHT = 5;
protected AutomatonActionMapper mapper;
protected transient DataSource source;
protected transient long index;
protected boolean allowResize;
protected boolean fixedRatio;
protected double ratio;
protected int activeX, activeY;
/** Reference font and dimensions used to compute sizes when resizing */
protected static int referenceHeight = -1;
protected static int referenceWidth = -1;
protected static int referenceBaseline = -1;
protected static Font referenceFont = null;
static{
// "Monospaced" is guaranteed to exist by the JVM, see javadoc
referenceFont = Font.decode("Monospaced");
// Use a big font to minimize rounding errors dramatic effect when scaling size > referenceFont.size
referenceFont = referenceFont.deriveFont(100.0f);
FontMetrics fm = TextShape.referenceGraphics.getFontMetrics(referenceFont);
referenceBaseline = fm.getDescent();
referenceHeight = fm.getAscent() + referenceBaseline;
// As this is a monospaced font, all characters have same width => use one of them to compute width
// As I'm paranoid, I use the largest one...
referenceWidth = fm.stringWidth("W");
}
// Linkable shape
protected String link;
public String getLink() {
return link;
}
public void setLink(String link) {
this.link = link;
}
public static class Cell implements Serializable {
public Color frameColor, backgroundColor, textColor;
public String text;
Cell() {
frameColor = Color.black;
textColor = Color.black;
}
}
protected Cell[][] cells;
protected transient boolean dirtyState = false;
public AutomatonShape(int ox, int oy) {
this(ox, oy, 5, 5);
}
public AutomatonShape(int ox, int oy, int nx, int ny) {
this(ox, oy, nx, ny, nx * MIN_CELL_WIDTH, ny * MIN_CELL_HEIGHT);
}
public AutomatonShape(int ox, int oy, int nx, int ny, int width, int height) {
super(ox, oy);
_w = width;
_h = height;
cells = new Cell[ny][nx];
for (int y=0; y<cells.length; ++y) for (int x=0; x<cells[y].length; ++x) {
cells[y][x] = new Cell();
}
// Our own mapper
mapper = new AutomatonActionMapper();
allowResize = true;
}
/* (non-Javadoc)
* @see simtools.shapes.AbstractShape#draw(java.awt.Graphics2D)
*/
public void draw(Graphics2D g) {
Color oldColor = g.getColor();
Color oldBackground = g.getBackground();
Object antialiasHint = g.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
AbstractShape.ANTI_ALIASING
? RenderingHints.VALUE_ANTIALIAS_ON
: RenderingHints.VALUE_ANTIALIAS_OFF);
int lenx = _w / cells[0].length;
int leny = _h / cells.length;
// Prevent the text to get out of bounds
Rectangle oldclip = g.getClipBounds();
Font saveFont = g.getFont();
// Draw each cell
for (int y=0; y<cells.length; ++y) for (int x=0; x<cells[y].length; ++x) {
Cell cell = cells[y][x];
if (cell==null) continue;
// Compute coordinates
int x0 =_ox + x * lenx;
int y0 = _oy - _h + y * leny;
// Fill background, if any
if (cell.backgroundColor!=null) {
g.setColor(cell.backgroundColor);
g.fillRect(x0,y0,lenx, leny);
}
// Write text, if any
if ((cell.textColor!=null) && (cell.text!=null) && (!cell.text.equals(""))) {
g.setColor(cell.textColor);
g.setClip(x0, y0, lenx, leny);
Font f = getFontToFitText(cell.text, lenx, leny);
if (f!=null) {
g.setFont(f);
g.drawString(cell.text, x0 + TextShape.MARGINX, y0 + leny - TextShape.MARGINY - g.getFontMetrics(f).getDescent());
}
g.setClip(oldclip);
}
// Draw frame, if any
if (cell.frameColor!=null) {
g.setColor(cell.frameColor);
g.drawRect(x0,y0,lenx-1,leny-1);
}
}
g.setFont(saveFont);
g.setClip(oldclip);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, antialiasHint);
g.setBackground(oldBackground);
g.setColor(oldColor);
}
/** Utility method that returns a Font object, as would be used to draw the text
* according to widht and height
* May return null if the desired operation results in no visible text.
*/
public static Font getFontToFitText(String text, int width, int height) {
double innerw = width-2*TextShape.MARGINX;
double innerh = height-2*TextShape.MARGINY;
if ((innerw<=0) || (innerh<=0)) return null;
double scaleX = innerw/(text.length() * referenceWidth);
double scaleY = innerh/referenceHeight;
return referenceFont.deriveFont(AffineTransform.getScaleInstance(scaleX, scaleY));
}
/* (non-Javadoc)
* @see jsynoptic.base.ContextualActionProvider#getActions(double, double, java.lang.Object, int)
*/
public String[] getActions(double x, double y, Object o, int context) {
if (context==MOUSE_OVER_CONTEXT) {
return null;
}
if (context==MOUSE_OUT_CONTEXT) {
return null;
}
if (context==MOUSE_PRESSED_CONTEXT) {
return null;
}
if (context==EDITOR_CONTEXT) {
}
Vector v = new Vector();
v.add(resources.getString("properties"));
return (String[])v.toArray(new String[v.size()]);
}
/* (non-Javadoc)
* @see jsynoptic.base.ContextualActionProvider#doAction(double, double, java.lang.Object, java.lang.String)
*/
public boolean doAction(double x, double y, Object o, String action, CompoundEdit undoableEdit) {
if (action.equals(resources.getString("properties"))) {
new LongAction(LongAction.LONG_ACTION_SHAPE, null, this) {
protected void doAction() {
PropertiesPanel panel = createPanel();
int result = JOptionPane.showConfirmDialog(JSynoptic.gui.getOwner(), panel,
panel.getShapeName() + resources.getString("propertiesTitle"), JOptionPane.OK_CANCEL_OPTION,
JOptionPane.PLAIN_MESSAGE);
if (result == JOptionPane.OK_OPTION) {
Rectangle bounds = getBounds();
panel.updateProperties();
bounds.add(getBounds());
notifyChange(bounds);
}
}
}.start();
return true;
}
return false;
}
/* (non-Javadoc)
* @see jsynoptic.base.ContextualActionProvider#canDoAction(double, double, java.lang.Object, java.lang.String, int)
*/
public boolean canDoAction(double x, double y, Object o, String action, int context) {
return true;
}
/** A JPanel do display and change the properties of the SimpleShape
*/
public class PropertiesPanel extends JPanel {
protected JButton bdrawcolor;
protected Color noColor;
protected JCheckBox cbResize;
protected JCheckBox cbRatio;
protected JCheckBox cbdraw; // optional
protected JLabel lwidth, lheight;
protected NumberField nfWidth, nfHeight;
protected transient DataSource sourceCopy;
protected SourceTree dstree;
protected AutomatonActionMapper mapperCopy;
protected String _shapeName;
private NumberField nfActiveX;
private NumberField nfActiveY;
private NumberField nfNumCellX;
private NumberField nfNumCellY;
public PropertiesPanel(String shapeName) {
_shapeName = shapeName;
// Initialize our copy fields (allow users to cancel changes)
sourceCopy = source;
try {
mapperCopy = (AutomatonActionMapper)mapper.clone();
} catch (CloneNotSupportedException e) {
mapperCopy = mapper;
}
Box content = Box.createVerticalBox();
Box topBox = Box.createHorizontalBox();
JPanel gridPanel = new JPanel(new BorderLayout());
gridPanel.setBorder(BorderFactory.createTitledBorder(resources.getString("GridArrayParameters")));
JPanel colpane = new JPanel(new GridLayout(2,2));
colpane.add(new JLabel(resources.getString("NumberOfColumns")));
colpane.add(nfNumCellX = new NumberField(cells[0].length,5));
colpane.add(new JLabel(resources.getString("ActiveColumn")));
colpane.add(nfActiveX = new NumberField(activeX,5));
gridPanel.add(colpane,BorderLayout.WEST);
JPanel rowpane = new JPanel(new GridLayout(2,2));
rowpane.add(new JLabel(resources.getString("NumberOfRows")));
rowpane.add(nfNumCellY = new NumberField(cells.length,5));
rowpane.add(new JLabel(resources.getString("ActiveRow")));
rowpane.add(nfActiveY = new NumberField(activeY,5));
gridPanel.add(rowpane,BorderLayout.EAST);
topBox.add(gridPanel);
JPanel dimensionsPanel = new JPanel(new BorderLayout());
dimensionsPanel.setBorder(BorderFactory.createTitledBorder(resources.getString("Dimensions")));
Box box = Box.createHorizontalBox();
box.add(cbResize = new JCheckBox(resources.getString("AllowResize"),allowResize));
box.add(Box.createHorizontalGlue());
box.add(cbRatio = new JCheckBox(resources.getString("FixedRatio"),fixedRatio));
dimensionsPanel.add(box,BorderLayout.NORTH);
box = Box.createHorizontalBox();
box.add(lwidth = new JLabel(resources.getString("Width")));
box.add(nfWidth = new NumberField((long)_w));
nfWidth.setHorizontalAlignment(JTextField.RIGHT);
box.add(Box.createHorizontalGlue());
box.add(lheight = new JLabel(resources.getString("Height")));
box.add(nfHeight = new NumberField((long)_h));
nfHeight.setHorizontalAlignment(JTextField.RIGHT);
dimensionsPanel.add(box,BorderLayout.SOUTH);
topBox.add(dimensionsPanel);
content.add(topBox);
box = Box.createHorizontalBox();
JPanel panel = mapperCopy.createPanel(new JDialog(),null); // TODO refactoring on Automaton shape, do like
// all other builtin shapes !
panel.setBorder(BorderFactory.createTitledBorder(resources.getString("ActionMapper")));
box.add(panel);
panel = new JPanel(new BorderLayout());
dstree = SourceTree.getFromPool("PropertiesPanel0");
JScrollPane dslistScrollPane = new JScrollPane(dstree);
panel.add(dslistScrollPane);
panel.setBorder(BorderFactory.createTitledBorder(resources.getString("DataSource")));
box.add(panel);
dstree.setSelectedValue(sourceCopy);
dstree.removeAllTreeSelectionListeners();
dstree.addTreeSelectionListener(new TreeSelectionListener() {
public void valueChanged(TreeSelectionEvent e) {
if (!e.isAddedPath()) return;
Object o = dstree.getSelectedSourceOrCollection();
if (o instanceof DataSource) sourceCopy = (DataSource)o;
}
});
content.add(box);
add(content);
}
/**
*
*/
public void updateProperties() {
int nx = (int)nfNumCellX.getLongValue();
int ny = (int)nfNumCellY.getLongValue();
int ax = (int)nfActiveX.getLongValue();
int ay = (int)nfActiveY.getLongValue();
if (nx<=0) nx = cells[0].length;
if (ny<=0) ny = cells.length;
if (ax<0) ax = 0; if (ax>=nx) ax = nx-1;
if (ay<0) ay = 0; if (ay>=ny) ay = ny-1;
activeX = ax;
activeY = ay;
// If the aray size was changed, update it
if ((nx!=cells[0].length) || (ny!=cells.length)) {
Cell[][] newCells = new Cell[ny][nx];
int maxx = Math.min(nx,cells[0].length);
int maxy = Math.min(ny,cells.length);
// Copy the old array elements
for (int j=0; j<maxy; ++j) {
System.arraycopy(cells[j], 0, newCells[j], 0, maxx);
// Initialize remaining cells on this row, if any
for (int i=maxx; i<nx; ++i) newCells[j][i] = new Cell();
}
// Initialize remaining cells, if any
for (int j=maxy; j<ny; ++j) for (int i=0; i<nx; ++i) newCells[j][i] = new Cell();
cells = newCells;
}
allowResize = cbResize.isSelected();
fixedRatio = cbRatio.isSelected();
_w = (int)nfWidth.getLongValue();
_h = (int)nfHeight.getLongValue();
if (_w < nx * MIN_CELL_WIDTH) _w = nx * MIN_CELL_WIDTH;
if (_h < ny * MIN_CELL_HEIGHT) _h = ny * MIN_CELL_HEIGHT;
// round up to nearest multiple of number of cells
_w = (int)(Math.ceil((double)_w / (double)nx) * nx);
_h = (int)(Math.ceil((double)_h / (double)ny) * ny);
ratio = (double)_w / (double)_h;
if (source!=null) {
source.removeListener(AutomatonShape.this);
source.removeEndNotificationListener(AutomatonShape.this);
}
source = sourceCopy;
if (source!=null) {
try {
index = source.getLastIndex();
} catch (UnsupportedOperation e) {
index = 0;
}
source.addListener(AutomatonShape.this);
source.addEndNotificationListener(AutomatonShape.this);
}
mapper = mapperCopy;
}
public String getShapeName() {
return _shapeName;
}
}
/**
* @return
*/
public PropertiesPanel createPanel() {
return new PropertiesPanel(Builtin.resources.getString("Automaton"));
}
/** Apply the automaton rules on the active cell */
public void applyRules() {
AutomatonActionList list = (AutomatonActionList)mapper.getMapping(source, index);
if (list==null) return; // No rules, no change
// Apply the actions in order
for (Iterator it = list.iterator(); it.hasNext(); ) {
AutomatonAction rule = (AutomatonAction)it.next();
switch (rule.action) {
case AutomatonAction.MOVE_UP:
activeY--;
if (activeY>=0) break; // OK
if (AutomatonAction.OPTION_WRAP.equals(rule.option)) {
activeY = cells.length-1;
break;
}
if (AutomatonAction.OPTION_EXTEND.equals(rule.option)) {
activeY = 0;
Cell[][] newCells = new Cell[cells.length+1][cells[0].length];
// Initialize first row
for (int i=0; i<cells[0].length; ++i) newCells[0][i] = new Cell();
// Copy the others
for (int j=0; j<cells.length; ++j) newCells[j+1] = cells[j];
cells = newCells;
if (_h < cells.length * MIN_CELL_HEIGHT) _h = cells.length * MIN_CELL_HEIGHT;
// round up to nearest multiple of number of cells
_h = (int)(Math.ceil((double)_h / (double)cells.length) * cells.length);
if (fixedRatio) {
_w = (int)(Math.ceil((double)(_h*ratio) / (double)cells[0].length) * cells[0].length);
}
break;
}
// option null or set to resources.getString("DoNothing")
activeY = 0;
break;
case AutomatonAction.MOVE_RIGHT:
activeX++;
if (activeX<cells[0].length) break; // OK
if (AutomatonAction.OPTION_WRAP.equals(rule.option)) {
activeX = 0;
break;
}
if (AutomatonAction.OPTION_EXTEND.equals(rule.option)) {
activeX = cells[0].length;
Cell[][] newCells = new Cell[cells.length][cells[0].length+1];
for (int j=0; j<cells.length; ++j) {
// Copy existing cells
System.arraycopy(cells[j],0,newCells[j],0,cells[0].length);
// initialize the last one
newCells[j][cells[0].length] = new Cell();
}
cells = newCells;
if (_w < cells[0].length * MIN_CELL_WIDTH) _w = cells[0].length * MIN_CELL_WIDTH;
// round up to nearest multiple of number of cells
_w = (int)(Math.ceil((double)_w / (double)cells[0].length) * cells[0].length);
if (fixedRatio) {
_h = (int)(Math.ceil((double)(_w/ratio) / (double)cells.length) * cells.length);
}
break;
}
// option null or set to resources.getString("DoNothing")
activeX = cells[0].length-1;
break;
case AutomatonAction.MOVE_DOWN:
activeY++;
if (activeY<cells.length) break; // OK
if (AutomatonAction.OPTION_WRAP.equals(rule.option)) {
activeY = 0;
break;
}
if (AutomatonAction.OPTION_EXTEND.equals(rule.option)) {
activeY = cells.length;
Cell[][] newCells = new Cell[cells.length+1][cells[0].length];
// Initialize last row
for (int i=0; i<cells[0].length; ++i) newCells[cells.length][i] = new Cell();
// Copy the others
for (int j=0; j<cells.length; ++j) newCells[j] = cells[j];
cells = newCells;
if (_h < cells.length * MIN_CELL_HEIGHT) _h = cells.length * MIN_CELL_HEIGHT;
// round up to nearest multiple of number of cells
_h = (int)(Math.ceil((double)_h / (double)cells.length) * cells.length);
if (fixedRatio) {
_w = (int)(Math.ceil((double)(_h*ratio) / (double)cells[0].length) * cells[0].length);
}
break;
}
// option null or set to resources.getString("DoNothing")
activeY = cells.length-1;
break;
case AutomatonAction.MOVE_LEFT:
activeX--;
if (activeX>=0) break; // OK
if (AutomatonAction.OPTION_WRAP.equals(rule.option)) {
activeX = cells[0].length-1;
break;
}
if (AutomatonAction.OPTION_EXTEND.equals(rule.option)) {
activeX = 0;
Cell[][] newCells = new Cell[cells.length][cells[0].length+1];
for (int j=0; j<cells.length; ++j) {
// initialize the first one
newCells[j][0] = new Cell();
// Copy existing cells
System.arraycopy(cells[j],0,newCells[j],1,cells[0].length);
}
cells = newCells;
if (_w < cells[0].length * MIN_CELL_WIDTH) _w = cells[0].length * MIN_CELL_WIDTH;
// round up to nearest multiple of number of cells
_w = (int)(Math.ceil((double)_w / (double)cells[0].length) * cells[0].length);
if (fixedRatio) {
_h = (int)(Math.ceil((double)(_w/ratio) / (double)cells.length) * cells.length);
}
break;
}
// option null or set to resources.getString("DoNothing")
activeX = 0;
break;
case AutomatonAction.SET_BACKGROUND_COLOR:
if ((rule.option==null) || (!(rule.option instanceof Color))) break;
cells[activeY][activeX].backgroundColor = (Color)rule.option;
break;
case AutomatonAction.SET_FRAME_COLOR:
if ((rule.option==null) || (!(rule.option instanceof Color))) break;
cells[activeY][activeX].frameColor = (Color)rule.option;
break;
case AutomatonAction.SET_TEXT_COLOR:
if ((rule.option==null) || (!(rule.option instanceof Color))) break;
cells[activeY][activeX].textColor = (Color)rule.option;
break;
case AutomatonAction.SET_TEXT:
if ((rule.option==null) || (!(rule.option instanceof Object[]))) break;
Object[] opt = (Object[])rule.option;
int format = ((Integer)opt[0]).intValue();
if (format == TextShape.FORMAT_MAPPER) {
cells[activeY][activeX].text = opt[1]==null ? null : opt[1].toString();
break;
}
Object o;
try {
o = source.getValue(index);
} catch (DataException e) {
cells[activeY][activeX].text = null;
break;
}
if (!(o instanceof Number)) {
cells[activeY][activeX].text = o.toString();
break;
}
Number n = (Number)o;
switch(format) {
case TextShape.FORMAT_DECIMAL:
case TextShape.FORMAT_SCIENTIFIC:
cells[activeY][activeX].text = ((NumberFormat)opt[1]).format(n);
break;
case TextShape.FORMAT_HEXADECIMAL:
cells[activeY][activeX].text = "0x" + Long.toHexString(n.longValue()).toUpperCase();
break;
case TextShape.FORMAT_OCTAL:
cells[activeY][activeX].text = "0" + Long.toOctalString(n.longValue());
break;
case TextShape.FORMAT_BINARY:
cells[activeY][activeX].text = Long.toBinaryString(n.longValue());
break;
default:
case TextShape.FORMAT_STRING:
cells[activeY][activeX].text = o.toString();
}
break;
}
}
notifyChange();
}
/* (non-Javadoc)
* @see simtools.data.DataSourceListener#DataSourceIndexRangeChanged(simtools.data.DataSource, long, long)
*/
public void DataSourceIndexRangeChanged(DataSource ds, long startIndex, long lastIndex) {
if (!ds.equals(this.source)) return;
index = lastIndex;
dirtyState = true;
}
/* (non-Javadoc)
* @see simtools.data.DataSourceListener#DataSourceInfoChanged(simtools.data.DataSource, simtools.data.DataInfo)
*/
public void DataSourceInfoChanged(DataSource ds, DataInfo newInfo) {
// don't care about this one
}
/* (non-Javadoc)
* @see simtools.data.DataSourceListener#DataSourceOrderChanged(simtools.data.DataSource, int)
*/
public void DataSourceOrderChanged(DataSource ds, int newOrder) {
// don't care about this one
}
/* (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.source)) return;
// We care only about our index value change
if ((index < minIndex) || (index > maxIndex)) return;
dirtyState = true;
}
/* (non-Javadoc)
* @see simtools.data.DataSourceListener#DataSourceValueRangeChanged(simtools.data.DataSource)
*/
public void DataSourceValueRangeChanged(DataSource ds) {
// don't care about this one
}
/* (non-Javadoc)
* @see simtools.data.DataSourceListener#DataSourceReplaced(simtools.data.DataSource, simtools.data.DataSource)
*/
public void DataSourceReplaced(DataSource oldData, DataSource newData) {
if(oldData==source){
source=newData;
if (source!=null) try {
source.addListener(this);
source.addEndNotificationListener(this);
index = source.getLastIndex();
}
catch (UnsupportedOperation uo1) {
index = 0;
}
dirtyState=true;
}
}
/* (non-Javadoc)
* @see simtools.shapes.AbstractShape#subscribeToDataNotifications()
*/
public void processShapeRestoring(){
if (source!=null) {
source.addListener(this);
source.addEndNotificationListener(this);
try {
index = source.getLastIndex();
} catch (UnsupportedOperation uo1) {
index = 0;
}
}
super.processShapeRestoring();
}
/* (non-Javadoc)
* @see simtools.shapes.AbstractShape#unsubscribeToDataNotifications()
*/
public void processShapeRemoving(){
if (source != null) {
source.removeListener(this);
source.removeEndNotificationListener(this);
}
super.processShapeRemoving();
}
/* (non-Javadoc)
* @see simtools.data.EndNotificationListener#notificationEnd(java.lang.Object)
*/
public void notificationEnd(Object referer) {
if (dirtyState) applyRules();
dirtyState = false; // for next time
}
/* (non-Javadoc)
* @see simtools.diagram.Resizable#resize(int, int)
*/
public void resize(int dx, int dy) {
if (!allowResize) return;
_w += dx; _h += dy;
if (fixedRatio) {
double dw = Math.max((double)_w, _h*ratio);
_w = (int) dw;
_h = (int)(dw / ratio);
}
int nx = cells[0].length;
int ny=cells.length;
if (_w < nx * MIN_CELL_WIDTH) _w = nx * MIN_CELL_WIDTH;
if (_h < ny * MIN_CELL_HEIGHT) _h = ny * MIN_CELL_HEIGHT;
// round up to nearest multiple of number of cells
if (dx>=0) _w = (int)(Math.ceil((double)_w / (double)nx) * nx);
else _w = (int)(Math.floor((double)_w / (double)nx) * nx);
if (dy>=0) _h = (int)(Math.ceil((double)_h / (double)ny) * ny);
else _h = (int)(Math.floor((double)_h / (double)ny) * ny);
if (!fixedRatio) ratio = (double)_w / (double)_h;
}
// Take care of serialisation. Special handling for datasources
private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException {
out.defaultWriteObject();
DataSourcePool.global.writeDataSource(out, source);
}
private void readObject(java.io.ObjectInputStream in) throws java.lang.ClassNotFoundException, java.io.IOException {
in.defaultReadObject();
source = DataSourcePool.global.readDataSource(in);
if (source!=null) try {
source.addListener(this);
source.addEndNotificationListener(this);
index = source.getLastIndex();
}
catch (UnsupportedOperation uo1) {
index = 0;
}
}
}