/* ========================
* 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: HistoryTextShape.java,v 1.8 2009/01/08 15:23:40 ogor Exp $
*
* Changes
* -------
* 16-Jan-2008 : Initial version (NB);
*
*/
package jsynoptic.builtin;
import java.awt.Graphics2D;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.ResourceBundle;
import java.util.Vector;
import javax.swing.undo.CompoundEdit;
import jsynoptic.base.ContextualActionProvider;
import jsynoptic.base.DataSourceConsumer;
import jsynoptic.base.Linkable;
import jsynoptic.builtin.ui.HistoryTextPropertiesPanel;
import jsynoptic.ui.JSynoptic;
import jsynoptic.ui.LongAction;
import simtools.data.DataException;
import simtools.data.DataInfo;
import simtools.data.DataSource;
import simtools.data.EndNotificationListener;
import simtools.diagram.Resizable;
import simtools.shapes.AbstractShape;
import simtools.shapes.ui.AbstractShapePropertiesDialogBox;
import simtools.ui.JPropertiesPanel;
import simtools.ui.ResourceFinder;
/**
* @author zxpletran007
*
*/
public class HistoryTextShape extends AbstractShape implements Resizable, ContextualActionProvider, Linkable,
EndNotificationListener, DataSourceConsumer{
private static final long serialVersionUID = 3278291921289728517L;
public static ResourceBundle resources = ResourceFinder.get(HistoryTextShape.class);
/** The array cells are of type HistoryTextShape */
protected ArrayList cells;
/** Linkable shape */
protected String link;
/**
* Build an History shape with the given history size (number of time values
* to retain). The width argument is the array width, the height one is the
* height of <b>one</b> cell.
*
* @param histSize
* The number of time values to retain
* @param width
* The array width @ param height The height of one cell in the array
*/
public HistoryTextShape(int ox, int oy, int histSize, int width, int cellHeight) {
super(ox, oy);
cells = new ArrayList();
if (cellHeight < TextShape.MIN_HEIGHT) {
cellHeight = TextShape.MIN_HEIGHT;
}
// Fisrt cell text
TextShape fisrtCell;
fisrtCell= createTextShape(Builtin.resources.getString("Text"), width, cellHeight);
fisrtCell.setAnchor(_ox, _oy + cellHeight);
fisrtCell.setDimensions(fisrtCell.getBounds().width, cellHeight); // force height
cells.add(fisrtCell);
// Add other cells
for (int i = 1; i < histSize; ++i) {
TextShape ts = new TextShape(Builtin.resources.getString("Text"), fisrtCell.getBounds().width, cellHeight);
ts.setAnchor(_ox, _oy + (i + 1) * cellHeight);
ts.setDimensions(fisrtCell.getBounds().width, cellHeight); // force height
cells.add(ts);
}
if (cells.size() > 0) {
((TextShape)cells.get(0)).setDelegateEndNotificationListener(this);
}
// Set witdh
if (cells.size() > 0) {
_w = ((TextShape) cells.get(0)).getBounds().width;
}
if (_w < TextShape.MIN_WIDTH) {
_w = TextShape.MIN_WIDTH;
}
// Set height
_h = cellHeight * histSize;
_y = _h;
}
/**
* @param histSize
* @param width
* @param cellHeight
*/
public HistoryTextShape(int histSize, int width, int cellHeight) {
this(0, 0, histSize, width,cellHeight);
}
public String getLink() {
return link;
}
public void setLink(String link) {
this.link = link;
}
/**
* @return Returns the cells.
*/
public ArrayList getCells() {
return cells;
}
/*
* (non-Javadoc)
*
* @see simtools.shapes.AbstractShape#translate(int, int)
*/
public void translate(int dx, int dy) {
super.translate(dx, dy);
for (Iterator it = cells.iterator(); it.hasNext();) {
((TextShape) it.next()).translate(dx, dy);
}
}
/*
* (non-Javadoc)
*
* @see simtools.shapes.AbstractShape#setAnchor(int, int)
*/
public void setAnchor(int ox, int oy) {
super.setAnchor(ox, oy);
positionCells();
}
/*
* (non-Javadoc)
*
* @see simtools.shapes.AbstractShape#draw(java.awt.Graphics2D)
*/
public void draw(Graphics2D g) {
for (Iterator it = cells.iterator(); it.hasNext();) {
((TextShape) it.next()).draw(g);
}
}
/*
* (non-Javadoc)
*
* @see jsynoptic.base.DataSourceConsumer#addDataSource(simtools.data.DataSource)
*/
public boolean addDataSource(DataSource d) {
// Only set the source of first text cell
((TextShape) cells.get(0)).addDataSource(d);
setCellsTextValues();
notifyChange();
return true;
}
/**
* Propagate cells color and text values.
*/
protected void propagate(){
if (cells.size() > 0){
TextShape lastCell = (TextShape) cells.get(cells.size()-1);
for (int i = cells.size() - 1; i > 0; i--) {
TextShape previousCell = (TextShape) cells.get(i - 1);
lastCell.setPropertyValue("STROKE_COLOR", previousCell.getDrawColor());
lastCell.setPropertyValue("FILL_COLOR", previousCell.getFillColor());
lastCell.setPropertyValue("TEXT_COLOR", previousCell.getTextColor());
lastCell.setPropertyValue("TEXT", previousCell.getPropertyValue("TEXT"));
lastCell = previousCell;
}
}
}
protected void setCellsTextValues(){
if (cells.size() > 0){
TextShape ts0 = (TextShape) cells.get(0);
DataSource source = ts0.source;
if (source != null) {
try {
long dsIndex = source.getLastIndex();
for (int i = 0; i < cells.size(); i++) {
// Get text value from fisrt cell properties
String text = "";
if (ts0.displayDsName.booleanValue()) {
String name = DataInfo.getLabel(source);
if ((name != null) && !name.equals("")) {
text += name;
if (ts0.displayDsValue.booleanValue()) {
text += "=";
}
}
}
try {
if (ts0.displayDsValue.booleanValue()) {
text += ts0.formatSourceValue(source.getValue(dsIndex));
}
} catch (DataException e) {
text += TextShape.resources.getString("NoValue");
}
if (ts0.displayDsUnit.booleanValue()) {
String unit = DataInfo.getUnit(source);
if ((unit != null) && !unit.equals("")) {
text += " [" + unit + "]";
}
}
if (ts0.displayDsComments.booleanValue()) {
String comments = DataInfo.getComment(source);
if ((comments != null) && !comments.equals("")) {
text += " (" + comments + ")";
}
}
// Set hookedTextShape value
TextShape ts = (TextShape) cells.get(i);
ts.setPropertyValue("TEXT", text);
dsIndex--;
}
} catch (DataException e) {
// data source doesn't hold dsIndex values
}
}
}
}
/**
* This function supposes dimensions are correct, it will not check that
* Also, it does not notify changes.
*/
protected void positionCells() {
int cellHeight = _h / cells.size();
for (int i = 0; i < cells.size(); ++i) {
TextShape ts = (TextShape) cells.get(i);
ts.setAnchor(_ox, _oy + (i + 1) * cellHeight);
ts.setDimensions(_w, cellHeight);
}
}
/*
* (non-Javadoc)
*
* @see simtools.diagram.Resizable#resize(int, int)
*/
public void resize(int dx, int dy) {
_w += dx;
_h += dy;
if (_w < TextShape.MIN_WIDTH) {
_w = TextShape.MIN_WIDTH;
}
// round _h to the nearest multiple of the number of cells so each cell
// has equal height
int cellHeight = (int) Math.rint((double) _h / (double) cells.size());
if (cellHeight < TextShape.MIN_HEIGHT) {
cellHeight = TextShape.MIN_HEIGHT;
}
_h = cellHeight * cells.size();
_oy += _y - _h;
_y = _h;
positionCells();
}
/* (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)
|| (context == MOUSE_OUT_CONTEXT)
|| (context == MOUSE_PRESSED_CONTEXT)
){
return null;
}
Vector v = new Vector();
v.add(resources.getString("Properties..."));
if (o instanceof DataSource && canAddDataSource(((DataSource) o))) {
v.add(resources.getString("SetSource"));
}
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("SetSource"))) {
return addDataSource((DataSource) o);
}
if (action.equals(resources.getString("Properties..."))) {
new LongAction(LongAction.LONG_ACTION_SHAPE, null, this) {
protected void doAction() {
HistoryTextPropertiesPanel panel = (HistoryTextPropertiesPanel) createPanel();
/**
* Only one shape properties dialog box can be displayed at
* once When user opens a dialog box properties while
* another one is still displayed: this last dislaog box is
* closed
*/
Point oldLocation = null;
if (currentDialogBox != null) {
oldLocation = currentDialogBox.getBounds().getLocation();
currentDialogBox.dispose();
}
ArrayList shapes = new ArrayList();
shapes.add(HistoryTextShape.this);
AbstractShapePropertiesDialogBox optionDialog = new AbstractShapePropertiesDialogBox(JSynoptic.gui
.getOwner(), panel, panel.getShapeName() + resources.getString("propertiesTitle"),
shapes, JSynoptic.gui.getActiveComponent());
if (oldLocation != null) {
optionDialog.setLocation(oldLocation);
}
currentDialogBox = optionDialog;
// Show dialog box
optionDialog.setVisible(true);
}
}.start();
return true;
}
return false;
}
/*
* (non-Javadoc)
*
* @see jsynoptic.builtin.Abstract1DShape#createPanel()
*/
public JPropertiesPanel createPanel() {
return new HistoryTextPropertiesPanel(Builtin.resources.getString("History"));
}
/* (non-Javadoc)
* @see simtools.shapes.AbstractShape#getShapeName()
*/
public String getShapeName(){
return Builtin.resources.getString("History");
}
/*
* (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;
}
/**
* By overwriting this method, it is possible to create different kind of text cells
* @param name
* @param width
* @param height
* @return a text shape belonging to text array
*/
protected TextShape createTextShape(String name, int width, int height) {
return new TextShape(name, width, height);
}
/**
* - Set first cell properties
* - Propagate static attributes to other cells
* @param cellProperties
*/
public void setCells(ArrayList cellProperties) {
_w = 0;
if (cells.size() > 0) {
// Set first cell properties
TextShape fisrtCell = (TextShape) cells.get(0);
// First cell is update with all properties
// other cell are updated only regarding static attributes
for (int j = 0; j < cellProperties.size(); j++) {
Object[] property = (Object[]) cellProperties.get(j);
String pname = (String) property[0];
Object value = property[1];
fisrtCell.setPropertyValue(pname, value);
if (
(pname.equals("STROKE_COLOR"))
|| (pname.equals("ALLOW_RESIZE"))
|| (pname.equals("FIXED_RATIO"))
|| (pname.equals("STROKE_PARAMS"))
|| (pname.equals("FILL_COLOR"))
|| (pname.equals("TEXT_COLOR"))
|| (pname.equals("FONT_LOCK"))
|| (pname.equals("FRAME_LOCK"))
|| (pname.equals("CHAR_NUMBER"))
|| (pname.equals("FONT"))
|| (pname.equals("ENABLE_MARGIN"))
// Update text value ?
){
for (int i = 1; i < cells.size(); i++) {
((TextShape) cells.get(i)).setPropertyValue(pname, value);
}
}
}
// Update cell text values
setCellsTextValues();
// Adjust size
for (int i = 1; i < cells.size(); i++) {
int width = ((TextShape) cells.get(i)).getBounds().width;
if (width > _w) {
_w = width;
}
}
positionCells();
notifyChange();
}
}
/*
* (non-Javadoc)
*
* @see jsynoptic.base.DataSourceConsumer#canAddDataSource(simtools.data.DataSource)
*/
public boolean canAddDataSource(DataSource d) {
return true;
}
/*
* (non-Javadoc)
*
* @see simtools.shapes.AbstractShape#cloneShape()
*/
protected AbstractShape cloneShape() {
HistoryTextShape clonedHistory = (HistoryTextShape) super.cloneShape();
clonedHistory.cells = new ArrayList();
for (Iterator it = cells.iterator(); it.hasNext();) {
clonedHistory.cells.add(((TextShape) it.next()).cloneShape());
}
if (clonedHistory.cells.size() > 0){
((TextShape)clonedHistory.cells.get(0)).setDelegateEndNotificationListener(clonedHistory);
}
clonedHistory.positionCells();
return clonedHistory;
}
private void readObject(java.io.ObjectInputStream in) throws java.lang.ClassNotFoundException, java.io.IOException {
in.defaultReadObject();
if ((cells != null) && (cells.size() > 0)){
// Subscribe history shape to first cell data source notifications
((TextShape)cells.get(0)).setDelegateEndNotificationListener(this);
}
// Set cells text values with first data source values
setCellsTextValues();
}
/*
* (non-Javadoc)
*
* @see simtools.data.EndNotificationListener#notificationEnd(java.lang.Object)
*/
public void notificationEnd(Object referer) {
TextShape firstCell = (TextShape)cells.get(0);
if (firstCell != null){
// Propagate only
if (firstCell.dirtyText || firstCell.dirtyColor || firstCell.dirtyDrawColor || firstCell.dirtyFillColor) {
propagate();
}
// Then update first cell
firstCell.notificationEnd(referer);
// Refresh history
notifyChange(getBounds());
}
}
/* (non-Javadoc)
* @see simtools.shapes.AbstractShape#getPropertyValue(java.lang.String)
*/
public Object getPropertyValue(String name) {
Object res = null;
if (name.equalsIgnoreCase("HISTORY_SIZE")) {
res = new Integer(cells.size());
} else if (name.equalsIgnoreCase("CELL_PROPERTIES")) {
ArrayList cellProperties = new ArrayList();
if (cells.size() > 0) {
String[] props = ((TextShape) cells.get(0)).getPropertyNames();
if (props != null) {
for (int j = 0; j < props.length; j++) {
// Each property has a name and a value
Object[] property = new Object[2];
String pname = props[j];
Object value = ((TextShape) cells.get(0)).getPropertyValue(pname);
property[0] = pname;
property[1] = value;
cellProperties.add(property);
}
}
}
res = cellProperties;
}
return res;
}
/* (non-Javadoc)
* @see simtools.shapes.AbstractShape#setPropertyValue(java.lang.String, java.lang.Object)
*/
public void setPropertyValue(String name, Object value) {
if (name.equalsIgnoreCase("HISTORY_SIZE")) {
if (value instanceof Integer) {
int histSize = ((Integer) value).intValue();
int oldSize = cells.size();
if ((histSize >= 0) && (histSize != oldSize)) {
int cellHeight = cells.size() == 0 ? TextShape.MIN_HEIGHT : _h / cells.size();
if (cellHeight < TextShape.MIN_HEIGHT) {
cellHeight = TextShape.MIN_HEIGHT;
}
if (oldSize > histSize) {
for (int i = oldSize; i > histSize; i--) {
cells.remove(i - 1);
}
} else {
for (int i = oldSize; i < histSize; i++) {
TextShape ts = new TextShape(Builtin.resources.getString("Text"), _w,
cellHeight);
ts.setAnchor(_ox, _oy + (i + 1) * cellHeight);
cells.add(ts);
}
}
_h = cellHeight * histSize;
_y = _h;
}
}
} else if (name.equalsIgnoreCase("CELL_PROPERTIES")){
if (value instanceof ArrayList) {
setCells((ArrayList) value);
}
}
}
/* (non-Javadoc)
* @see simtools.shapes.AbstractShape#subscribeToDataNotifications()
*/
public void processShapeRestoring(){
TextShape ts = (TextShape) cells.get(0);
ts.processShapeRestoring();
super.processShapeRestoring();
}
/* (non-Javadoc)
* @see simtools.shapes.AbstractShape#unsubscribeToDataNotifications()
*/
public void processShapeRemoving(){
TextShape ts = (TextShape) cells.get(0);
ts.processShapeRemoving();
super.processShapeRemoving();
}
public String[] getPropertyNames() {
if (_propertyNames == null) {
_propertyNames = new HistoryTextShapePropertiesNames().getPropertyNames();
}
return _propertyNames;
}
public static class HistoryTextShapePropertiesNames extends AbstractShapePropertiesNames {
private static transient String[] props = new String[] { "HISTORY_SIZE", "CELL_PROPERTIES"};
public HistoryTextShapePropertiesNames() {
super();
for (int i = 0; i < props.length; i++) {
propertyNames.add(props[i]);
}
}
}
}