/* ==============================================
* Simtools : The tools library used in JSynoptic
* ==============================================
*
* Project Info: http://jsynoptic.sourceforge.net/index.html
*
* This library 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 library 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
* library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* (C) Copyright 1999-2003, by :
* Corporate:
* Astrium SAS
* EADS CRC
* Individual:
* Claude Cazenave
* Nicolas Brodu
*
*
* $Id: ShapesContainer.java,v 1.34 2009/01/09 09:56:56 ogor Exp $
*
* Changes
* -------
* 25-Sep-2003 : Initial public release (NB);
*
*/
package simtools.shapes;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.print.PageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;
import javax.swing.event.UndoableEditEvent;
import javax.swing.undo.CompoundEdit;
import simtools.diagram.DiagramClipboard;
import simtools.diagram.DiagramComponent;
import simtools.diagram.Element;
import simtools.diagram.ElementsContainer;
import simtools.diagram.DiagramParameters;
import simtools.diagram.ElementSelection;
import simtools.diagram.DiagramSelection;
import simtools.diagram.gate.Connection;
import simtools.diagram.gate.Gate;
import simtools.diagram.gate.GatedComponent;
import simtools.diagram.undo.CreateEdit;
import simtools.diagram.undo.DepthChangedEdit;
import simtools.diagram.undo.GateConnectEdit;
import simtools.diagram.undo.TranslateEdit;
import simtools.shapes.undo.GroupEdit;
import simtools.shapes.undo.ReGroupEdit;
import simtools.shapes.undo.UnGroupEdit;
/**
* This class is a container to display shapes. It contains a DiagramComponent to
* be layout in other swing containers and with edition functionalities. Internalization
* is used to save and restore this container into/from a file
*
* @author Claude Cazenave
*
* @version 1.0 2001
*/
public class ShapesContainer extends Vector implements java.io.Serializable, ElementsContainer {
static final long serialVersionUID = 4552602745191436089L;
protected DiagramParameters _diagParam;
protected static Font _sFont=new Font("TimesRoman",Font.PLAIN,10);
/**
* The transient shape diagram component
*/
protected transient ShapesDiagramComponent _comp;
/**
* The transient shape diagram selection
*/
protected transient ShapeDiagramSelection _selection;
protected Vector _oldCompounds;
public ShapesContainer(String name) {
super();
_diagParam=new DiagramParameters();
_diagParam.name=name;
_oldCompounds=new Vector();
createSelectionAndComponent();
}
/**
* Create the diagram selection and the JComponent related to this shape container
*/
private void createSelectionAndComponent() {
// 1- Create the shape diagram c selection
_selection = createShapeSelection();
// 2- Create the shape diagram component
_comp = createShapesComponent();
}
/* (non-Javadoc)
* @see simtools.diagram.ElementsContainer#setComponent(simtools.diagram.DiagramComponent)
*/
public void setComponent(DiagramComponent dc) {
_comp = (ShapesDiagramComponent)dc;
}
/* (non-Javadoc)
* @see simtools.diagram.ElementsContainer#getComponent()
*/
public DiagramComponent getComponent() {
return _comp;
}
/* (non-Javadoc)
* @see simtools.diagram.ElementsContainer#getSelection()
*/
public DiagramSelection getSelection() {
return _selection;
}
/* (non-Javadoc)
* @see simtools.diagram.ElementsContainer#setSelection(simtools.diagram.DiagramSelection)
*/
public void setSelection(DiagramSelection ds) {
_selection = (ShapeDiagramSelection)ds;
}
public DiagramParameters getDiagramParameters(){
return _diagParam;
}
public class ShapesDiagramComponent extends DiagramComponent implements ShapeListener {
/**
* This timer is used to gather shape changed events
* and send them at a reasonable rate to Swing.
*/
protected Timer timer=null;
protected final Object timerLock=new Object();
/**
* An hash table to gather the shape changed events
*/
protected HashMap paintedAreas=new HashMap();
public ShapesDiagramComponent(DiagramParameters param, ShapesContainer sc){
super(param, sc);
}
/**
* Override finalize to make sure we stop thread and remove all listeners
*/
protected void finalize() throws Throwable {
synchronized(timerLock){
if (timer!=null) {
timer.cancel();
timer = null;
}
}
super.finalize();
}
public ShapesContainer getContainer() {
return ShapesContainer.this;
}
// methods to deal with group,ungroup,regroup operations
public boolean canGroup(){
return getElementContainer().getSelection().getShapeCount() > 1;
}
public boolean canUnGroup(){
for(int i=0;i<getElementContainer().getSelection().getShapeCount();i++){
AbstractShape as=(AbstractShape)getElementContainer().getSelection().getShape(i);
if(as instanceof CompoundShape){
return true;
}
}
return false;
}
/**
* Regroup action is possible when one of the selected shapes is contained into
* one of the recorded compound shapes
* @return
*/
public boolean canReGroup(){
// Check if it is possible to regroup some shapes
for(int i=0;i<_oldCompounds.size();i++){
CompoundShape cs=(CompoundShape)_oldCompounds.elementAt(i);
for(int j=0;j<getElementContainer().getSelection().getShapeCount();j++){
if((cs.contains((AbstractShape)getElementContainer().getSelection().getShape(j))) && (cs.size()>1)){
return true;
}
}
}
return false;
}
public void group(){
if(getElementContainer().getSelection().getShapeCount()<2){
return;
}
Vector shapesDeletedInCompoundShapes = new Vector();
Vector deletedFromOldCompounds = new Vector();
// update the old compound shapes
for(int j=0;j<_oldCompounds.size();j++){
CompoundShape cs=(CompoundShape)_oldCompounds.get(j);
// Reminds that some shapes have been deleted for undo group operation
Vector shapesRetained = cs.retainShapes(ShapesContainer.this);
if (shapesRetained.size()!=0){
Object[] shapesDeletedInCompoundShape = new Object[2];
shapesDeletedInCompoundShape[0] = cs;
shapesDeletedInCompoundShape[1] = shapesRetained;
shapesDeletedInCompoundShapes.add(shapesDeletedInCompoundShape);
}
if ((cs.size()==0) && !deletedFromOldCompounds.contains(cs)){
deletedFromOldCompounds.add(cs);
}
}
// create a new CompoundShape
int maxShapeDephtPosition=0;
CompoundShape cs=new CompoundShape();
for(int k=0;k<getElementContainer().getSelection().getShapeCount();k++){
AbstractShape as=(AbstractShape)getElementContainer().getSelection().getShape(k);
// check if it was in a old compound shape and mark it
for(int j=0;j<_oldCompounds.size();j++){
CompoundShape oc=(CompoundShape)_oldCompounds.get(j);
if(oc.contains(as)){
oc.removeShape(as);
// Reminds that this shape has been deleted for undo group operation
Object[] shapesDeletedInCompoundShape = new Object[2];
Vector shapesRemoved = new Vector();
shapesRemoved.add(as);
shapesDeletedInCompoundShape[0] = oc;
shapesDeletedInCompoundShape[1] = shapesRemoved;
shapesDeletedInCompoundShapes.add(shapesDeletedInCompoundShape);
if ( (oc.size()==0) && !deletedFromOldCompounds.contains(oc)){
deletedFromOldCompounds.add(oc);
}
}
}
// add it
cs.addShape(as);
int shapeDephtPosition = ShapesContainer.this.indexOf(as);
if (shapeDephtPosition>maxShapeDephtPosition)
maxShapeDephtPosition = shapeDephtPosition;
}
for(int j=0;j<deletedFromOldCompounds.size();j++){
_oldCompounds.remove((CompoundShape)deletedFromOldCompounds.get(j));
}
// add the compoundshape to the container
Vector res = new Vector();
res.add(cs);
ShapesContainer.this.addAll(maxShapeDephtPosition, res);
// Then remove selection from the container
for(int k=0;k<getElementContainer().getSelection().getShapeCount();k++){
ShapesContainer.this.removeElement((AbstractShape)getElementContainer().getSelection().getShape(k));
}
// update the selection
((ShapeDiagramSelection)getElementContainer().getSelection()).setToShape(cs);
// Update undoable operations
fireUndoableEditUpdate(new UndoableEditEvent(this, new GroupEdit(this, cs, shapesDeletedInCompoundShapes, deletedFromOldCompounds)));
// repaint
repaint();
}
/**
* undo group operation
* @param shapes
*/
public void undoGroup(CompoundShape cs, Vector shapesDeletedInCompoundShapes, Vector deletedFromOldCompounds){
// First undo group operation
// remove it from the container
ShapesContainer.this.removeElement(cs);
// restore the shapes
Vector selection = new Vector();
for(int i=0;i<cs.size();i++){
AbstractShape as = cs.getShape(i);
ShapesContainer.this.add(as);
selection.add(as);
}
// update the selection
((ShapeDiagramSelection)getElementContainer().getSelection()).setToShapes(selection);
repaint();
// Finally, undo modifications on other compoundShapes and on oldShapes
for(int i=0;i<deletedFromOldCompounds.size();i++){
_oldCompounds.add(deletedFromOldCompounds.get(i));
}
for(int i=0;i<shapesDeletedInCompoundShapes.size();i++){
CompoundShape co = (CompoundShape)(((Object[])shapesDeletedInCompoundShapes.get(i))[0]);
Vector v = (Vector)(((Object[])shapesDeletedInCompoundShapes.get(i))[1]);
for(int j=0;j<v.size();j++){
co.addShape( (AbstractShape)v.get(j));
}
}
}
/**
* redo group operation
* @param shapes
*/
public void redoGroup(CompoundShape cs, Vector shapesDeletedInCompoundShapes, Vector deletedFromOldCompounds){
// First, redo modification on other compoundShapes and on oldShapes
for(int i=0;i<deletedFromOldCompounds.size();i++){
_oldCompounds.remove(deletedFromOldCompounds.get(i));
}
for(int i=0;i<shapesDeletedInCompoundShapes.size();i++){
CompoundShape co = (CompoundShape)(((Object[])shapesDeletedInCompoundShapes.get(i))[0]);
Vector v = (Vector)(((Object[])shapesDeletedInCompoundShapes.get(i))[1]);
for(int j=0;j<v.size();j++){
co.removeShape( (AbstractShape)v.get(j));
}
}
// Then redo group operation
for(int i=0;i<cs.size();i++){
// remove it from the container
ShapesContainer.this.removeElement(cs.getShape(i));
}
// add the compoundshape to the container
Vector res = new Vector();
res.add(cs);
ShapesContainer.this.addAll(res);
// update the selection
((ShapeDiagramSelection)getElementContainer().getSelection()).setToShape(cs);
repaint();
}
/**
* unGroup operation
*/
public void unGroup(){
Vector undoUngroup =new Vector();
Vector selection = new Vector();
for(int i=0;i<getElementContainer().getSelection().getShapeCount();i++){
AbstractShape as=(AbstractShape)getElementContainer().getSelection().getShape(i);
if(as instanceof CompoundShape){
CompoundShape cs=(CompoundShape)as;
undoUngroup.add(cs);
Vector res = new Vector();
for(int j=0;j<cs.size();j++){
res.add(cs.getShape(j));
}
selection.addAll(res);
// When ungrouping, add coumpoundShape into the list of old compounds, used for regroup action
_oldCompounds.add(cs);
ShapesContainer.this.addAll( ShapesContainer.this.indexOf(as), res);
// remove it from the container
ShapesContainer.this.removeElement(as);
}
}
// update the selection
((ShapeDiagramSelection)getElementContainer().getSelection()).setToShapes(selection);
// Update undoable operations
fireUndoableEditUpdate(new UndoableEditEvent(this, new UnGroupEdit(this, undoUngroup)));
repaint();
}
/**
* Undo ungroup operation
* @param old compound shape to restore
*/
public void undoUngroup(CompoundShape compoundShape){
// First undo ungroup operation
for(int k=0;k<compoundShape.size();k++){
AbstractShape as= compoundShape.getShape(k);
ShapesContainer.this.removeElement(as);
}
// add the compoundshape to the container
Vector res = new Vector();
res.add(compoundShape);
ShapesContainer.this.addAll(res);
// update the selection
((ShapeDiagramSelection)getElementContainer().getSelection()).setToShape(compoundShape);
// repaint
repaint();
// Finally, restore oldCompounds
_oldCompounds.remove(compoundShape);
}
/**
* redo ungroup operation
* @param old compound shape to restore
*/
public void redoUngroup(CompoundShape compoundShape){
// Fisrt update oldCompounds
_oldCompounds.add(compoundShape);
// Then redo ungroup operation
Vector res=new Vector();
for(int i=0;i<compoundShape.size();i++){
AbstractShape as= compoundShape.getShape(i);
res.add(as);
}
// remove it from the container
ShapesContainer.this.removeElement(compoundShape);
// restore the shapes
ShapesContainer.this.addAll(res);
// update the selection
((ShapeDiagramSelection)getElementContainer().getSelection()).setToShapes(res);
repaint();
}
public void reGroup(){
// loop on the old compound shapes
Vector deletedFromOldCompounds = new Vector();
Vector shapesDeletedInCompoundShapes = new Vector();
for(int i=0;i<_oldCompounds.size();i++){
CompoundShape cs=(CompoundShape)_oldCompounds.get(i);
// update it
// Reminds that some shapes have been deleted for undo group operation
Vector shapesRetained = cs.retainShapes(ShapesContainer.this);
if (shapesRetained.size()!=0){
Object[] shapesDeletedInCompoundShape = new Object[2];
shapesDeletedInCompoundShape[0] = cs;
shapesDeletedInCompoundShape[1] = shapesRetained;
shapesDeletedInCompoundShapes.add(shapesDeletedInCompoundShape);
}
if ( (cs.size()==0) && !deletedFromOldCompounds.contains(cs)) {
deletedFromOldCompounds.add(cs);
}
// look for selelected elements member of the
// current old compound shape
for(int k=0;k<getElementContainer().getSelection().getShapeCount();k++){
AbstractShape as=(AbstractShape)getElementContainer().getSelection().getShape(k);
if(cs.contains(as)){
// remove form the selection and from the
// container all the shapes that belong
// to this compound shape
Vector v=new Vector();
int maxShapeDepthPosition=0;
for(int j=0;j<cs.size();j++){
v.add(cs.getShape(j));
int shapeDepthPosition = ShapesContainer.this.indexOf(cs.getShape(j));
if (shapeDepthPosition>maxShapeDepthPosition)
maxShapeDepthPosition = shapeDepthPosition;
}
((ShapeDiagramSelection)getElementContainer().getSelection()).replaceShapes(v,cs);
cs.computeBounds();
cs.synchroSizes();
ShapesContainer.this.add(maxShapeDepthPosition, cs);
ShapesContainer.this.removeAll(v);
// This is no more an old one
deletedFromOldCompounds.add(cs);
// go to next compound shape
break;
}
}
}
for(int i=0;i<deletedFromOldCompounds.size();i++)
_oldCompounds.remove(deletedFromOldCompounds.get(i));
// Update undoable operations
fireUndoableEditUpdate(new UndoableEditEvent(this, new ReGroupEdit(this,shapesDeletedInCompoundShapes, deletedFromOldCompounds)));
repaint();
}
/**
* undo regroup operation
* @param shapes
*/
public void undoReGroup(Vector compoundShapes, Vector shapesDeletedInCompoundShapes){
// First, undo regroup operation for all compound shapes
for(int i=0;i<compoundShapes.size();i++){
CompoundShape cs = (CompoundShape)compoundShapes.get(i);
_oldCompounds.add(cs);
// remove it from the container
ShapesContainer.this.removeElement(cs);
Vector res=new Vector();
for(int j=0;j<cs.size();j++){
res.add(cs.getShape(j));
}
// restore the shapes
ShapesContainer.this.addAll(res);
// update the selection
((ShapeDiagramSelection)getElementContainer().getSelection()).setToShapes(res);
}
repaint();
// Finally, undo modification on other compoundShapes and on oldShapes
for(int i=0;i<shapesDeletedInCompoundShapes.size();i++){
CompoundShape co = (CompoundShape)(((Object[])shapesDeletedInCompoundShapes.get(i))[0]);
Vector v = (Vector)(((Object[])shapesDeletedInCompoundShapes.get(i))[1]);
for(int j=0;j<v.size();j++){
co.addShape( (AbstractShape)v.get(j));
}
}
}
/**
* redo regroup operation
* @param shapes
*/
public void redoReGroup(Vector compoundShapes, Vector shapesDeletedInCompoundShapes){
// First, redo modification on other compoundShapes and on oldShapes
for(int i=0;i<shapesDeletedInCompoundShapes.size();i++){
CompoundShape co = (CompoundShape)(((Object[])shapesDeletedInCompoundShapes.get(i))[0]);
Vector v = (Vector)(((Object[])shapesDeletedInCompoundShapes.get(i))[1]);
for(int j=0;j<v.size();j++){
co.removeShape( (AbstractShape)v.get(j));
}
}
// Then, redo regroup operation for all compound shapes
for(int i=0;i<compoundShapes.size();i++){
CompoundShape cs = (CompoundShape)compoundShapes.get(i);
_oldCompounds.remove(cs);
// remove form the selection and from the
// container all the shapes that belong
// to this compound shape
Vector v=new Vector();
for(int j=0;j<cs.size();j++){
v.add(cs.getShape(j));
}
((ShapeDiagramSelection)getElementContainer().getSelection()).replaceShapes(v,cs);
ShapesContainer.this.removeAll(v);
cs.computeBounds();
cs.synchroSizes();
ShapesContainer.this.add(cs);
}
repaint();
}
// Implementation of the abstract methods
protected void translate(int x, int y){
CompoundEdit ce = new CompoundEdit();
for(int i=0;i<ShapesContainer.this.size();i++){
AbstractShape s=(AbstractShape)elementAt(i);
s.translate(x,y);
ce.addEdit(new TranslateEdit(x, y, s));
}
ce.end();
fireUndoableEditUpdate(new UndoableEditEvent(this, ce));
}
protected void getMin(Point p){
for(int i=0;i<ShapesContainer.this.size();i++){
AbstractShape s=(AbstractShape)elementAt(i);
s.getMin(p);
}
}
/* (non-Javadoc)
* @see simtools.diagram.DiagramComponent#copyAt(java.util.Vector, int, int)
*/
protected void copyAt(Vector v, int x, int y){
CompoundEdit compoundEdit = new CompoundEdit();
List selectedConnectors = new ArrayList();
List selectedGatedComponent = new ArrayList();
Hashtable clonedGates = new Hashtable();
for(int i=0;i<v.size();i++){
Object o=v.elementAt(i);
if (o instanceof Connection){
selectedConnectors.add(o);
} else if (o instanceof AbstractShape){
AbstractShape as=((AbstractShape)o).cloneShape();
if (o instanceof GatedComponent){
selectedGatedComponent.add(o);
// Kept the relation between the existing and the new gate
List oldGates = new ArrayList(((GatedComponent)o).getGates());
List newGates = new ArrayList(((GatedComponent)as).getGates());
for(int j=0;j<oldGates.size(); j++){
clonedGates.put(oldGates.get(j), newGates.get(j));
}
}
as.translate(x,y);
ShapesContainer.this.add(as);
compoundEdit.addEdit(new CreateEdit(ShapesContainer.this,as));
}
}
// Now Copy the connectors
for (int i = 0; i < selectedConnectors.size(); i++) {
Connection connection = (Connection) selectedConnectors.get(i);
if (connection instanceof AbstractShape){
Gate fisrtGate = (Gate) connection.getFirstEndGate();
Gate lastGate = (Gate) connection.getLastEndGate();
Gate newFg = null;
Gate newLg = null;
if ((fisrtGate!=null) && selectedGatedComponent.contains(fisrtGate.getOwner())){
newFg = (Gate) clonedGates.get(fisrtGate);
}
if ((lastGate!=null) && selectedGatedComponent.contains(lastGate.getOwner())){
newLg = (Gate) clonedGates.get(lastGate);
}
AbstractShape newConnector = ((AbstractShape)connection).cloneShape();
newConnector.translate(x,y);
if (newFg != null){
((Connection)newConnector).connect(newFg, true);
compoundEdit.addEdit(new GateConnectEdit( (Connection)newConnector, newFg, true));
}
if (newLg != null){
((Connection)newConnector).connect(newLg, false);
compoundEdit.addEdit(new GateConnectEdit( (Connection)newConnector, newLg, false));
}
// Add it ton container
ShapesContainer.this.add(newConnector);
compoundEdit.addEdit(new CreateEdit(ShapesContainer.this,newConnector));
}
}
compoundEdit.end();
fireUndoableEditUpdate(new UndoableEditEvent(this, compoundEdit));
}
public Shape getShapeAt(int ox, int oy){
for(int i=(ShapesContainer.this.size()-1); i>=0; i--){
Shape s=(Shape)elementAt(i);
if(s.contains(ox,oy)){
return s;
}
}
return null;
}
protected void drawDiagramElements(Graphics2D g2, Point pMax){
Stroke oldStrocke = g2.getStroke();
Color oldColor = g2.getColor();
Font olfFont = g2.getFont();
g2.setFont(_sFont);
if(_diagParam.backgr!=null){
g2.setColor(_diagParam.forgr);
g2.setBackground(_diagParam.backgr);
}
else{
g2.setColor(Color.black);
g2.setBackground(DiagramParameters.DEFAULT_COLOR);
}
g2.setStroke(new BasicStroke(0));
// loop on objects to be displayed
//--------------------------------
for(int i=0;i<ShapesContainer.this.size();i++){
AbstractShape r=(AbstractShape)elementAt(i);
r.draw(g2);
ElementSelection dss=getElementContainer().getSelection().getSelectedShape(r);
if(dss!=null){
drawBounds(g2,dss);
}
// update drawing size
r.getMax(pMax);
}
g2.setFont(olfFont);
g2.setColor(oldColor);
g2.setStroke(oldStrocke);
}
protected void drawBounds(Graphics2D g2, ElementSelection dss){
dss.drawBounds(g2);
}
protected void drawClipboardElements(Graphics2D g2, Point pMax){
int dx=_dragPoint.x;
int dy=_dragPoint.y;
g2.translate(dx,dy);
DiagramClipboard dc=DiagramClipboard.get();
// loop on objects to be displayed
//--------------------------------
g2.setColor(Color.green);
for(int i=0;i<dc.size();i++){
// connectors are not connected to connections
// can't call getConnections() on
// connections are connected to connectors
// and have been copied to clipboard
Object o=dc.elementAt(i);
if(o instanceof AbstractShape){
AbstractShape r=(AbstractShape)o;
r.draw(g2);
// update drawing size
r.getMax(pMax);
}
}
g2.translate(-dx,-dy);
}
protected void printDiagramElements(Graphics2D g2, Point pMax) {
g2.setFont(_sFont);
g2.setColor(Color.black);
// Loop on objects to be displayed
// Print only the selection if requested, or all if there is no selection
boolean hasSelection = false;
if (_printSelectionOnly) for (int i=0; i<ShapesContainer.this.size(); i++) {
AbstractShape as = (AbstractShape)elementAt(i);
if (getElementContainer().getSelection().isSelected(as)) {
as.draw(g2);
// update drawing size
as.getMax(pMax);
hasSelection = true;
}
}
// If there is no selection, then print everything
if (!hasSelection) for (int i=0; i<ShapesContainer.this.size(); i++) {
AbstractShape as = (AbstractShape)elementAt(i);
as.draw(g2);
as.getMax(pMax);
}
}
protected double computePrintScaleFactor(PageFormat pf) {
if (!_printSelectionOnly) return super.computePrintScaleFactor(pf);
Point max = new Point();
boolean hasSelection = false;
for (int i=0; i<ShapesContainer.this.size(); i++) {
AbstractShape as = (AbstractShape)elementAt(i);
if (getElementContainer().getSelection().isSelected(as)) {
as.getMax(max);
hasSelection = true;
}
}
if (!hasSelection) return super.computePrintScaleFactor(pf);
double sx = pf.getImageableWidth()/(double)max.x;
double totalHeight = (double)max.y;
if (hasHeader()) totalHeight += (double)(_param.headerHeight + DiagramParameters.HEADER_MARGIN);
double sy = pf.getImageableHeight() / totalHeight;
double s = Math.min(sx,sy);
return s;
}
/**
* A cyclic task ensures that repainted requests are not performed
* faster than an configurable parameter
* In addition, if several updates on a given shapes are performed
* during this configurable period then the painted area of that shape
* is growing if different rectangles of painting are required
* but the shape will be painted one time only
*
* @see simtools.shapes.ShapeListener#shapeChanged(simtools.shapes.AbstractShape, java.awt.Rectangle)
*/
public void shapeChanged(AbstractShape shape, Rectangle changedArea) {
Rectangle r=(Rectangle)paintedAreas.get(shape);
if(r==null){
synchronized(paintedAreas){
paintedAreas.put(shape,changedArea);
}
}
else{
r.add(changedArea);
}
synchronized(timerLock){
if(timer!=null){
return; // there is a timer let it do its job
}
timer = new Timer(true); // do not block the application on exit
timer.schedule(new TimerTask() {
public void run() {
int n=0;
synchronized(paintedAreas){
Iterator it=paintedAreas.keySet().iterator();
if(!it.hasNext()){
synchronized(timerLock){
timer.cancel();
timer=null;
}
return;
}
while(it.hasNext()){
AbstractShape as=(AbstractShape)it.next();
Rectangle area=(Rectangle)paintedAreas.get(as);
repaint(area);
it.remove(); // this remove the shape from the hastable
n++;
}
}
}
},0,AbstractShape.REFRESH_PERIOD);
}
}
public void repaint(){
// clear the hastable, everything is going to pe painted
// also a lazy way to remove shapes that are no more used
synchronized(paintedAreas){
paintedAreas.clear();
}
super.repaint();
}
/**
* Overridden in order to handle Undo/Redo on translation events.
*/
public void pointTranslationEnd() {
// _compoundEdit has been
_compoundEdit.end();
fireUndoableEditUpdate(
new UndoableEditEvent(
this, _compoundEdit
)
);
super.pointTranslationEnd();
}
/**
* Overridden in order to handle Undo/Redo on translation events.
*/
public void translationEnd() {
// _compoundEdit has been
_compoundEdit.end();
fireUndoableEditUpdate(
new UndoableEditEvent(
this, _compoundEdit
)
);
super.translationEnd();
}
/**
* Overridden in order to handle Undo/Redo on resize events.
*/
public void resizeEnd() {
_compoundEdit.end();
fireUndoableEditUpdate(
new UndoableEditEvent(
this, _compoundEdit
)
);
super.resizeEnd();
}
public void performCreate(AbstractShape shape) {
fireUndoableEditUpdate(
new UndoableEditEvent(this, new CreateEdit(ShapesContainer.this, shape))
);
}
public void fireUndoableEditUpdate(UndoableEditEvent _e) {
super.fireUndoableEditUpdate(_e);
}
}
/**
*
* A selection of shapes on a DiagramComponentElements
* @author zxpletran007
*
*/
public class ShapeDiagramSelection extends DiagramSelection {
ShapeDiagramSelection(ShapesContainer sc){
super(sc);
}
public Point getShapeOrigin(Shape s, Point p){
Point res= ((p==null) ? (new Point()) : p );
if(s instanceof AbstractShape){
AbstractShape r=(AbstractShape)s;
res.x=r._ox;
res.y=r._oy;
return res;
}
return super.getShapeOrigin(s,res);
}
/* (non-Javadoc)
* @see simtools.diagram.DiagramSelection#addAllShapes()
*/
protected boolean addAllShapes(){
int selectionOldCount = getShapeCount();
for(int i = size()-1; i>=0; i--){
Element s = (Element)elementAt(i);
add(createShapeSelection(s));
}
return selectionOldCount != getShapeCount();
}
/* (non-Javadoc)
* @see simtools.diagram.DiagramSelection#addShapeAt(int, int)
*/
protected boolean addShapeAt(int ox, int oy){
int selectionOldCount = getShapeCount();
boolean hasBeenAdd = false;
for(int i=size()-1; i>=0 && !hasBeenAdd; i--){
Element s=(Element)elementAt(i);
if(s.contains(ox,oy)){
add(createShapeSelection(s));
hasBeenAdd = true;
}
}
return selectionOldCount != getShapeCount();
}
protected void setToShape(AbstractShape as){
super.clear();
selectAreaEnd();
width=-1;
add(createShapeSelection(as));
propagate();
}
protected void setToShapes(Vector v){
super.clear();
for(int i=0;i<v.size();i++){
AbstractShape as=(AbstractShape)v.elementAt(i);
add(createShapeSelection(as));
}
propagate();
}
protected void addShapes(Vector v){
for(int i=0;i<v.size();i++){
AbstractShape as=(AbstractShape)v.elementAt(i);
add(createShapeSelection(as));
}
propagate();
}
/**
* Replace a list of shapes by a new one
* @param v the shapes to delete
* @param as the shape to add
*/
protected void replaceShapes(Vector v, AbstractShape as){
int k=getShapeCount()-1;
for(int i=k;i>=0;i--){
Shape s=getShape(i);
if(v.contains(s)){
removeShapeAt(i);
}
}
add(createShapeSelection(as));
propagate();
}
/* (non-Javadoc)
* @see simtools.diagram.DiagramSelection#cloneElement(simtools.diagram.Element)
*/
public Element cloneElement(Element e){
Element res;
if (e instanceof AbstractShape){
res = ((AbstractShape)e).cloneShape();
} else {
res = super.cloneElement(e);
}
return res;
}
}
// Take care of serialisation. Diagram is transient
private void readObject(java.io.ObjectInputStream in) throws java.lang.ClassNotFoundException, java.io.IOException {
in.defaultReadObject();
createSelectionAndComponent();
}
/**
* @param param
* @return
*/
protected ShapeDiagramSelection createShapeSelection() {
return new ShapeDiagramSelection(this);
}
/**
* @param param
* @return
*/
protected ShapesDiagramComponent createShapesComponent() {
return new ShapesDiagramComponent(_diagParam, this);
}
/* (non-Javadoc)
* @see java.util.Collection#add(java.lang.Object)
*/
public synchronized boolean add(Object o) {
if (o instanceof AbstractShape) ((AbstractShape)o).addListener(_comp);
return super.add(o);
}
/* (non-Javadoc)
* @see java.util.List#add(int, java.lang.Object)
*/
public void add(int index, Object element) {
if (element instanceof AbstractShape) ((AbstractShape)element).addListener(_comp);
super.add(index, element);
}
/* (non-Javadoc)
* @see java.util.Collection#addAll(java.util.Collection)
*/
public synchronized boolean addAll(Collection c) {
for (Iterator it = c.iterator(); it.hasNext(); ) {
Object o = it.next();
if (o instanceof AbstractShape) ((AbstractShape)o).addListener(_comp);
}
return super.addAll(c);
}
/* (non-Javadoc)
* @see java.util.List#addAll(int, java.util.Collection)
*/
public synchronized boolean addAll(int index, Collection c) {
for (Iterator it = c.iterator(); it.hasNext(); ) {
Object o = it.next();
if (o instanceof AbstractShape) ((AbstractShape)o).addListener(_comp);
}
return super.addAll(index, c);
}
/* (non-Javadoc)
* @see java.util.Vector#addElement(java.lang.Object)
*/
public synchronized void addElement(Object obj) {
if (obj instanceof AbstractShape) {
((AbstractShape)obj).addListener(_comp);
_comp.performCreate((AbstractShape)obj);
}
super.addElement(obj);
}
/* (non-Javadoc)
* @see java.util.Collection#clear()
*/
public void clear() {
for (Iterator it = iterator(); it.hasNext(); ) {
Object o = it.next();
if (o instanceof AbstractShape) ((AbstractShape)o).removeListener(_comp);
}
super.clear();
}
/* (non-Javadoc)
* @see java.util.Vector#insertElementAt(java.lang.Object, int)
*/
public synchronized void insertElementAt(Object obj, int index) {
if (obj instanceof AbstractShape) ((AbstractShape)obj).addListener(_comp);
super.insertElementAt(obj, index);
}
/* (non-Javadoc)
* @see java.util.List#remove(int)
*/
public synchronized Object remove(int index) {
Object o = get(index);
if (o instanceof AbstractShape) {
((AbstractShape)o).removeListener(_comp);
}
return super.remove(index);
}
/* (non-Javadoc)
* @see java.util.Collection#remove(java.lang.Object)
*/
public boolean remove(Object o) {
if (o instanceof AbstractShape) {
((AbstractShape)o).removeListener(_comp);
}
return super.remove(o);
}
/* (non-Javadoc)
* @see java.util.Collection#removeAll(java.util.Collection)
*/
public synchronized boolean removeAll(Collection c) {
for (Iterator it = c.iterator(); it.hasNext(); ) {
Object o = it.next();
if (o instanceof AbstractShape) {
((AbstractShape)o).removeListener(_comp);
}
}
return super.removeAll(c);
}
/* (non-Javadoc)
* @see java.util.Vector#removeAllElements()
*/
public synchronized void removeAllElements() {
for (Iterator it = iterator(); it.hasNext(); ) {
Object o = it.next();
if (o instanceof AbstractShape) {
AbstractShape as = (AbstractShape)o;
as.removeListener(_comp);
}
}
super.removeAllElements();
}
/* (non-Javadoc)
* @see java.util.Vector#removeElement(java.lang.Object)
*/
public synchronized boolean removeElement(Object obj) {
if (obj instanceof AbstractShape) {
AbstractShape as = (AbstractShape)obj;
as.removeListener(_comp);
}
return super.removeElement(obj);
}
/* (non-Javadoc)
* @see java.util.Vector#removeElementAt(int)
*/
public synchronized void removeElementAt(int index) {
Object o = get(index);
if (o instanceof AbstractShape) {
AbstractShape as = (AbstractShape)o;
as.removeListener(_comp);
}
super.removeElementAt(index);
}
/* (non-Javadoc)
* @see java.util.AbstractList#removeRange(int, int)
*/
protected void removeRange(int fromIndex, int toIndex) {
for (int i=fromIndex; i<=toIndex; ++i) {
Object o = get(i);
if (o instanceof AbstractShape) {
AbstractShape as = (AbstractShape)o;
as.removeListener(_comp);
}
}
super.removeRange(fromIndex, toIndex);
}
/* (non-Javadoc)
* @see java.util.List#set(int, java.lang.Object)
*/
public synchronized Object set(int index, Object element) {
Object o = get(index);
if (o instanceof AbstractShape) ((AbstractShape)o).removeListener(_comp);
if (element instanceof AbstractShape) ((AbstractShape)element).addListener(_comp);
return super.set(index, element);
}
/* (non-Javadoc)
* @see java.util.Vector#setElementAt(java.lang.Object, int)
*/
public synchronized void setElementAt(Object obj, int index) {
Object o = get(index);
if (o instanceof AbstractShape) ((AbstractShape)o).removeListener(_comp);
if (obj instanceof AbstractShape) ((AbstractShape)obj).addListener(_comp);
super.setElementAt(obj, index);
}
/**
* Brings a set of selected shapes to the 'front' of the container, i.e at the
* top in terms of depth
* @param shapes The list of shapes to bring to front
*/
public synchronized void bringToFront(Vector shapes) {
int[] indices = new int[shapes.size()];
int i;
// Construct the list of indices
for(i=0; i<indices.length; i++) {
// The objects are necessarily shapes from this object
indices[i]=this.indexOf(shapes.get(i));
}
// Sort the indices in order to preserve the depth inside of the selection
Arrays.sort(indices);
Object tmp;
int indice;
CompoundEdit ce = new CompoundEdit();
for(i=0; i<indices.length; i++) {
indice=indices[i]-i;
tmp=this.elementAt(indice);
this.removeElementAt(indice);
this.add(tmp);
ce.addEdit(new DepthChangedEdit(this, indice, size()-1));
}
ce.end();
_comp.fireUndoableEditUpdate(new UndoableEditEvent(this, ce));
_comp.repaint();
}
/**
* Brings a set of shapes closer to the 'top' of the container
* @param shape
*/
public synchronized void bringForward(Vector shapes) {
int[] indices = new int[shapes.size()];
int i;
// Construct the list of indices
for(i=0; i<indices.length; i++) {
// The objects are necessarily shapes from this object
indices[i]=this.indexOf(shapes.get(i));
}
// Sort the indices in order to preserve the depth inside of the selection
Arrays.sort(indices);
Object tmp;
int indice;
CompoundEdit ce = new CompoundEdit();
for(i=indices.length; i>0; i--) {
indice=indices[i-1];
if(indice < size()-1) {
tmp=this.elementAt(indice);
this.removeElementAt(indice);
this.insertElementAt(tmp, indice+1);
ce.addEdit(new DepthChangedEdit(this, indice, indice+1));
}
}
ce.end();
_comp.fireUndoableEditUpdate(new UndoableEditEvent(this, ce));
_comp.repaint();
}
/**
* Sends a set of selected shapes to the 'back' of the container, i.e at the
* bottom in terms of depth
* @param shapes The list of shapes to bring to front
*/
public synchronized void sendToBack(Vector shapes) {
int[] indices = new int[shapes.size()];
int i;
// Construct the list of indices
for(i=0; i<indices.length; i++) {
// The objects are necessarily shapes from this object
indices[i]=this.indexOf(shapes.get(i));
}
// Sort the indices in order to preserve the depth inside of the selection
Arrays.sort(indices);
Object tmp;
int indice;
CompoundEdit ce = new CompoundEdit();
for(i=0; i<indices.length; i++) {
indice=indices[i];
tmp=this.elementAt(indice);
this.removeElementAt(indice);
this.insertElementAt(tmp, i);
ce.addEdit(new DepthChangedEdit(this, indice, i));
}
ce.end();
_comp.fireUndoableEditUpdate(new UndoableEditEvent(this, ce));
_comp.repaint();
}
/**
* Brings a shape closer to the 'bottom' of the container
* @param shape
*/
public synchronized void sendBackward(Vector shapes) {
int[] indices = new int[shapes.size()];
int i;
// Construct the list of indices
for(i=0; i<indices.length; i++) {
// The objects are necessarily shapes from this object
indices[i]=this.indexOf(shapes.get(i));
}
// Sort the indices in order to preserve the depth inside of the selection
Arrays.sort(indices);
Object tmp;
int indice;
CompoundEdit ce = new CompoundEdit();
for(i=0; i<indices.length; i++) {
indice=indices[i];
if(indice > 0) {
tmp=this.elementAt(indice);
this.removeElementAt(indice);
this.insertElementAt(tmp, indice-1);
ce.addEdit(new DepthChangedEdit(this, indice, indice-1));
}
}
ce.end();
_comp.fireUndoableEditUpdate(new UndoableEditEvent(this, ce));
_comp.repaint();
}
}