/* ==============================================
* 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
* Jean-Baptiste Lièvremont
*
*
* $Id: DiagramSelection.java,v 1.28 2009/01/08 17:04:08 ogor Exp $
*
* Changes
* -------
* 25-Sep-2003 : Initial public release (NB);
*
*/
package simtools.diagram;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Hashtable;
import java.util.List;
import java.util.Vector;
import javax.swing.event.UndoableEditEvent;
import javax.swing.undo.CompoundEdit;
import javax.swing.undo.UndoableEdit;
import simtools.diagram.gate.Connection;
import simtools.diagram.gate.Gate;
import simtools.diagram.gate.GatedComponent;
import simtools.diagram.undo.DeleteEdit;
import simtools.diagram.undo.DepthChangedEdit;
import simtools.diagram.undo.GateDisconnectEdit;
import simtools.diagram.undo.ResizeEdit;
import simtools.util.ListenerManager;
/**
* This class is used to define a selection area on a diagram.
* @see DiagramComponent
* @see ElementsContainer
*
* @author Claude Cazenave
*
* @version 1.1 2001
*/
public class DiagramSelection extends Rectangle {
/** The element container that contains the selected elements */
private ElementsContainer _elementContainer;
/** true if an area is selected */
private boolean _selArea=false;
/** the list of selected shapes */
private ArrayList _selectedElements;
/** a hashtable of selected shapes */
private Hashtable _selectedShapesMap;
/** the gid step to compute the translation */
private int _grid;
/** listeners */
private ListenerManager _listeners = new ListenerManager();
/**
* Creates a new empty diagram selection
*/
public DiagramSelection(ElementsContainer elementContainer){
_elementContainer = elementContainer;
_elementContainer.setSelection(this);
_selectedElements=new ArrayList();
_selectedShapesMap=new Hashtable();
_selArea=false;
width=-1;
_grid=0;
}
/**
* gets the list of elements in the diagram component using this diagram selection.
* @return the list of elements in the diagram component
*/
public ElementsContainer getElementContainer(){
return _elementContainer;
}
/////////////////////////////////////////////////////////////////////////////
/**
* Propagates to listeners
*/
protected void propagate(){
synchronized(_listeners) {
int n = _listeners.size(); // only one call outside loop
for (int i=0; i<n; ++i) {
DiagramSelectionListener dsl = (DiagramSelectionListener)_listeners.get(i);
if (dsl!=null) dsl.selectionChanged(this);
}
}
}
/**
* Adds a new Listener on component selection
*/
public void addListener(DiagramSelectionListener dcpl){
// take care of duplicates
_listeners.add(dcpl);
}
/**
* Removes a Listener on component selection
*/
public void removeListener(DiagramSelectionListener dcpl){
_listeners.remove(dcpl);
}
/**
* Removes all Listeners on component selection
*/
public void removeAllListeners(){
_listeners.clear();
}
/**
* Checks if one shape is selected
* @param s the shape
* @return true if it is selected
*/
public boolean isSelected(Shape s){
return _selectedShapesMap.containsKey(s);
}
/**
* Checks if one shape is selected
* @param s the shape
* @return the selected shape or null if not selected
*/
public ElementSelection getSelectedShape(Shape s){
return (ElementSelection)_selectedShapesMap.get(s);
}
/**
* Checks if a shape already selected contains the specified position
* @param ox point coordinate along X axis
* @param oy point coordinate along Y axis
* @return true if a selected shape exists at these coordinates
*/
public boolean isSelected(int ox, int oy){
boolean res = false;
for(int i=0; i<_selectedElements.size() && !res; i++){
ElementSelection ss=(ElementSelection)_selectedElements.get(i);
boolean isContained = ss.element.contains(ox,oy);
res = isContained;
}
return res;
}
/**
* Get the selected shape at these coordinates
* @param ox point coordinate along X axis
* @param oy point coordinate along Y axis
* @return selected shape at these coordinates or null if no selection at these coordinates
*/
public Shape getSelectedShapeAt(int ox, int oy){
Shape res = null;
for(int i=0; i<_selectedElements.size() && res==null;i++){
ElementSelection ss = (ElementSelection)_selectedElements.get(i);
Shape s=ss.element;
if(s.contains(ox,oy)){
res = s;
}
}
return res;
}
/**
* Checks if the selection is empty
* @return true if the selection is empty
*/
public boolean isEmpty(){
return _selectedElements.isEmpty();
}
/**
* Checks if the selection is defined by a rectangle shape
* and the user is modifiing its size
*/
public boolean isSizing(){
return _selArea;
}
/**
* Empties the selection
*/
public void unselect(){
clear();
_selArea=false;
width=-1;
propagate();
}
/**
* Selects one shape at the given point.
* Previous selected shapes are unselected.
*
* @param ox point coordinate along X axis
* @param oy point coordinate along Y axis
* @return true if a shape is added
*/
public boolean selectPoint(int ox, int oy) {
unselect();
boolean res = addShapeAt(ox, oy);
propagate();
return res;
}
/**
* Selects all the shapes. Previous selected shapes
* are unselected.
* @return true if a shape is added
*/
public boolean selectAll() {
unselect();
boolean res = addAllShapes();
propagate();
return res;
}
/**
* Gets the amount of shape in the selection
* @return the shape count
*/
public int getShapeCount(){
return _selectedElements.size();
}
/**
* Gets the nth shape in the selection
* @param i shape number
* @return the shape
*/
public Shape getShape(int i){
return getSelection(i).element;
}
public ElementSelection getSelection(int i){
ElementSelection ss=(ElementSelection)_selectedElements.get(i);
return ss;
}
/**
* @return he list of selected shapes
*/
public List getShapes(){
return new ArrayList(_selectedShapesMap.keySet());
}
/**
* Gets the nth DiagramSelectedShape in the selection
* @param i shape number
* @return the DiagramSelectedShape
*/
public ElementSelection getSelectedShape(int i){
return (ElementSelection)
_selectedElements.get(i);
}
/**
* Removes one shape
* @param i the shape number
*/
public void removeShapeAt(int i){
ElementSelection ss=(ElementSelection)_selectedElements.get(i);
_selectedElements.remove(i);
_selectedShapesMap.remove(ss.element);
}
/**
*
* If the point corresponds to an selected element, unselect it, otherwise add the corresponding selection.
* @param ox point coordinate along X axis
* @param oy point coordinate along Y axis
* @return true if selection has changed
*/
public boolean addPoint(int ox, int oy){
boolean selectionHasChanged = false;
_selArea = false;
width = -1;
// first look up in selected shapes
for(int i=0; i<_selectedElements.size() &&!selectionHasChanged; i++) {
ElementSelection ss=(ElementSelection) _selectedElements.get(i);
Shape s=ss.element;
if(s.contains(ox,oy)){
// Remove this shape from the selection
_selectedElements.remove(i);
_selectedShapesMap.remove(s);
selectionHasChanged = true;
}
}
if (!selectionHasChanged){
selectionHasChanged = addShapeAt(ox,oy);
}
if (selectionHasChanged){
propagate();
}
return selectionHasChanged;
}
/////////////////////////////////////////////////////////////////////////////
/**
* Selects shapes inside an area
* Previous selected shapes are unselected.
* @param ox origin along X axis
* @param oy origin along Y axis
* @param w area width
* @param h area height
* @return true if a shape is added
*/
public boolean selectArea(int ox,int oy,int w,int h){
_selArea=true;
clear();
if(w<0){
x=ox+w;
width=-w;
}
else{
x=ox;
width=w;
}
if(h<0){
y=oy+h;
height=-h;
}
else{
y=oy;
height=h;
}
boolean res=addShapesInside();
propagate();
return res;
}
/**
* End of the definition of the area size
* The area rectangle is no more needed
* Selected shapes are the one computed at the previous
* call of selectArea
* @return true if the area was sizing
*/
public boolean selectAreaEnd() {
if (_selArea) {
_selArea = false;
width = -1;
return true;
} else {
return false;
}
}
/**
* Sets the grid step for translation computation
* @param gris the gid step
*/
public void setGrid(int grid){
if(grid<0){
_grid=0;
}
else{
_grid=grid;
}
}
/**
* Gets the grid step for translation computation
* @return the grid step
*/
public int getGrid(){
return _grid;
}
/**
* Gets the grid nearest position
* @param v a coordinate
* @return the nearest grid position for this coordiante
*/
public int gridPosition(int v){
int m;
if(_grid==0){
return v;
}
m = v %_grid;
if(m>(_grid/2)){
return v+_grid-m;
} else{
return v-m;
}
}
/**
* Gets a list of selected diagram elements
*/
public Vector getSelectedElements(){
Vector v=new Vector(_selectedElements.size());
for(int i=0;i<_selectedElements.size();i++){
ElementSelection ss=(ElementSelection)
_selectedElements.get(i);
v.addElement(ss.element);
}
return v;
}
public Element cloneElement(Element e){
return null; // cannot clone element by default
}
/////////////////////////////////////////////////////////////////////////////
/**
* Creates a copy of the selected shapes.
* @param p the selection origin
* @return a vector of shape
*/
public Vector copySelection(Point p){
Vector v = new Vector(_selectedElements.size());
List selectedConnectors = new ArrayList();
List selectedGatedComponent = new ArrayList();
Hashtable clonedGates = new Hashtable();
// clone shapes and translate them according to
// new origin
for(int i=0;i<_selectedElements.size();i++){
ElementSelection ss=(ElementSelection)_selectedElements.get(i);
Element s=ss.element;
if (s instanceof Connection){
selectedConnectors.add(s);
} else {
Element ns = cloneElement(s);
if(ns!=null){
ns.translate(-p.x, -p.y);
v.addElement(ns);
}
if (s instanceof GatedComponent){
selectedGatedComponent.add(s);
// Kept the relation between the existing and the new gate
List oldGates = new ArrayList(((GatedComponent)s).getGates());
List newGates = new ArrayList(((GatedComponent)ns).getGates());
for(int j=0;j<oldGates.size(); j++){
clonedGates.put(oldGates.get(j), newGates.get(j));
}
}
}
}
// Now Copy the connectors
for (int i = 0; i < selectedConnectors.size(); i++) {
Connection connection = (Connection) selectedConnectors.get(i);
Element ns=cloneElement( (Element)connection );
if (ns!=null){
ns.translate(-p.x, -p.y);
v.addElement(ns);
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);
}
if (newFg != null){
((Connection)ns).connect(newFg, true);
}
if (newLg != null){
((Connection)ns).connect(newLg, false);
}
}
}
return v;
}
/////////////////////////////////////////////////////////////////////////////
/**
* Deletes the selected shapes
*/
public void deleteSelection(){
CompoundEdit ce = new CompoundEdit();
for(int i=0;i<_selectedElements.size();i++){
ElementSelection ss = (ElementSelection)_selectedElements.get(i);
Element as = ss.element;
as.processShapeRemoving();
ce.addEdit(new DeleteEdit((getElementContainer()), as));
if (ss.element instanceof GatedComponent){
GatedComponent gc = (GatedComponent)ss.element;
List gates = gc.getGates();
for(int k=0; k< gates.size(); k++){
Gate gate = ((Gate)gates.get(k));
List connections = gate.getConnections();
for(int j=0; j< connections.size(); j++){
Connection connection = (Connection)connections.get(j);
if (connection.isConnected(gate)){
ce.addEdit(new GateDisconnectEdit(connection, gate, connection.getFirstEndGate()!=null && connection.getFirstEndGate().equals(gate)));
connection.disconnect(gate);
}
}
}
}
if (ss.element instanceof Connection){
Connection gc = (Connection)ss.element;
if (gc.getFirstEndGate() != null){
ce.addEdit(new GateDisconnectEdit(gc, gc.getFirstEndGate(), true));
gc.disconnect(gc.getFirstEndGate());
}
if (gc.getLastEndGate() != null){
ce.addEdit(new GateDisconnectEdit(gc, gc.getLastEndGate(), false));
gc.disconnect(gc.getLastEndGate());
}
}
deleteShape(ss.element);
}
ce.end();
DiagramComponent dc = getElementContainer().getComponent();
dc.fireUndoableEditUpdate( new UndoableEditEvent(dc, ce));
unselect();
}
/////////////////////////////////////////////////////////////////////////////
/**
* Aligns the selected shapes
*/
public void alignSelection(){
int depx=0;
int depy=0;
CompoundEdit ce = new CompoundEdit();
for(int i=0;i<_selectedElements.size();i++){
ElementSelection ss = (ElementSelection)_selectedElements.get(i);
depx = gridPosition(ss.currentPoint.x) - ss.currentPoint.x;
depy = gridPosition(ss.currentPoint.y) - ss.currentPoint.y;
ss.translateShape(depx, depy);
ce.addEdit(ss.translateShapeEnd());
}
ce.end();
DiagramComponent dc = getElementContainer().getComponent();
dc._compoundEdit.addEdit(ce);
}
/////////////////////////////////////////////////////////////////////////////
/**
* Check if a point can be translated.
* A point translation can be performed if:
* <ul>
* <li>Only one shape is selected
* <li>The selection is a {@link TranslatableShapePointsSelection} and there is a point at given position
* </ul>
* @param posX - current x position
* @param posY - current x position
* @return
*/
public boolean canTranslatePointAt(int posX, int posY) {
boolean res = false;
if (getShapeCount() == 1 && getSelection(0) instanceof TranslatableShapePointsSelection){
res = ((TranslatableShapePointsSelection)getSelection(0)).hasPointAt(posX, posY);
}
return res;
}
/**
* Translate the selected point.
* @param dx
* @param dy
* @param translationWay
*/
public void translatePoint(int dx, int dy){
if ( getShapeCount() == 1 && getSelection(0) instanceof TranslatableShapePointsSelection){
((TranslatableShapePointsSelection) _selectedElements.get(0)).translatePoint(dx, dy);
}
}
public void translatePointEnd(){
UndoableEdit ue = null;
if ( getShapeCount() == 1 && getSelection(0) instanceof TranslatableShapePointsSelection){
ue = ((TranslatableShapePointsSelection) _selectedElements.get(0)).translatePointEnd();
}
if (ue != null){
DiagramComponent dc = getElementContainer().getComponent();
dc._compoundEdit.addEdit(ue);
}
}
/////////////////////////////////////////////////////////////////////////////
/**
* Check if resize or translate is required.
*
* This implementation can resize elements if:
* <ul>
* <li>The selection contains only one component
* <li>The selected component is resizable
* <li>This is not a selection of point
* </ul>
*
*
* @param x the mouse location
* @param y the mouse location
* @return 0 if no resize else the resize way 1=N, 2=NE,..
*/
public int checkResizeWay(int x, int y) {
int res = 0;
if (_selectedElements.size() == 1) {
ElementSelection ss=(ElementSelection) _selectedElements.get(0);
if ( (! (ss instanceof ShapePointsSelection) ) && ss.element!=null && ss.element instanceof Resizable) {
Rectangle2D b=ss.element.getBounds2D();
b.setFrame(b.getX()+5.,b.getY()+5.,b.getWidth()-10.,b.getHeight()-10.);
// look for the closest corner
int oc=b.outcode(x,y);
switch(oc){
case Rectangle2D.OUT_TOP:
res = 1;
break;
case Rectangle2D.OUT_TOP|Rectangle2D.OUT_RIGHT:
res = 2;
break;
case Rectangle2D.OUT_RIGHT:
res = 3;
break;
case Rectangle2D.OUT_RIGHT|Rectangle2D.OUT_BOTTOM:
res = 4;
break;
case Rectangle2D.OUT_BOTTOM:
res = 5;
break;
case Rectangle2D.OUT_BOTTOM|Rectangle2D.OUT_LEFT:
res = 6;
break;
case Rectangle2D.OUT_LEFT:
res = 7;
break;
case Rectangle2D.OUT_LEFT|Rectangle2D.OUT_TOP:
res = 8;
break;
}
}
}
return res;
}
/**
* Resize the selection.
*
* This implementation can resize elements if:
* <ul>
* <li>The selection contains only one component
* <li>This element is a resizable object
* <li> This element is not a connection selection
* </ul>
*
* @param dx resize value on X axis
* @param dy resize value on Y axis
* @param way the resize way : 1=N, 2=NE, ...
*/
public void resizeSelection(int dx, int dy, int way){
int depx= dx;
int depy= dy;
int dtw=0;
int dth=0;
int deltax=0;
int deltay=0;
int dtx=0;
int dty=0;
if (_selectedElements.size() == 1) {
ElementSelection selection = (ElementSelection) _selectedElements.get(0);
if (selection.element!=null && selection.element instanceof Resizable) {
CompoundEdit ce= new CompoundEdit();
switch(way){
case 1:
case 5:
dx=0;
break;
case 3:
case 7:
dy=0;
break;
}
Rectangle oldBounds, newBounds;
oldBounds = selection.element.getBounds();
switch(way){
case 1:
dtw=0;
dth=-depy;
break;
case 2:
dtw=depx;
dth=-depy;
break;
case 3:
dtw=depx;
dth=0;
break;
case 4:
dtw=depx;
dth=depy;
break;
case 5:
dtw=0;
dth=depy;
break;
case 6:
dtw=-depx;
dth=depy;
break;
case 7:
dtw=-depx;
dth=0;
break;
case 8:
dtw=-depx;
dth=-depy;
break;
}
// Resize the shape
resizeShape(selection.element, dtw, dth);
if(ce!=null){
ce.addEdit(new ResizeEdit(dtw, dth, (Resizable)selection.element));
}
newBounds = selection.element.getBounds();
deltax = newBounds.width - oldBounds.width;
deltay = newBounds.height - oldBounds.height;
switch(way){
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
dtx=0;
dty=deltay;
break;
case 5:
dtx=0;
dty=deltay;
break;
case 6:
dtx=-deltax;
dty=deltay;
break;
case 7:
dtx=-deltax;
dty=0;
break;
case 8:
dtx=-deltax;
dty=0;
break;
}
// Resize the shape
selection.translateShape(dtx, dty);
ce.end();
DiagramComponent dc = getElementContainer().getComponent();
dc._compoundEdit.addEdit(ce);
}
}
}
/**
* Ends the resize of selected shapes
*/
public void resizeShapesEnd(){
CompoundEdit ce = new CompoundEdit();
for (int i=0;i<_selectedElements.size();i++) {
ElementSelection ss = (ElementSelection)_selectedElements.get(i);
UndoableEdit ue = ss.translateShapeEnd();
if (ue != null && ue.isSignificant()){
ce.addEdit(ue);
}
}
ce.end();
if(ce.isSignificant()) {
DiagramComponent dc = getElementContainer().getComponent();
dc._compoundEdit.addEdit(ce);
}
}
/////////////////////////////////////////////////////////////////////////////
/**
* Translates the selected shapes
*/
public void translateShapes(int dx, int dy){
// Compute the translation step according to the grid
int depx = 0;
int depy = 0;
if (!_selectedElements.isEmpty()){
ElementSelection selection = (ElementSelection)_selectedElements.get(0);
selection.translateReferencePoint(dx, dy);
depx = gridPosition(selection.referencePoint.x) - selection.currentPoint.x;
depy = gridPosition(selection.referencePoint.y) - selection.currentPoint.y;
}
// Apply the translation to selected shapes
for (int i=0;i<_selectedElements.size();i++) {
ElementSelection selection = (ElementSelection)_selectedElements.get(i);
selection.translateShape(depx, depy);
}
// Translate the selection area
translate(dx,dy);
}
/**
* Ends the tranlation of selected shapes
*/
public void translateShapesEnd(){
CompoundEdit ce = new CompoundEdit();
for (int i=0;i<_selectedElements.size();i++) {
ElementSelection ss = (ElementSelection)_selectedElements.get(i);
UndoableEdit ue = ss.translateShapeEnd();
if (ue != null && ue.isSignificant()){
ce.addEdit(ue);
}
}
ce.end();
if(ce.isSignificant()) {
DiagramComponent dc = getElementContainer().getComponent();
dc._compoundEdit.addEdit(ce);
}
}
/////////////////////////////////////////////////////////////////////////////
/**
* Brings the selected shapes to the top of the display
*/
public void bringSelectionToFront() {
List selectedElements = getSelectedElements();
int[] indices = new int[selectedElements.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]=getElementContainer().indexOf(selectedElements.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=getElementContainer().get(indice);
getElementContainer().remove(indice);
getElementContainer().add(tmp);
ce.addEdit(new DepthChangedEdit(getElementContainer(), indice, getElementContainer().size()-1));
}
ce.end();
getElementContainer().getComponent().fireUndoableEditUpdate(new UndoableEditEvent(getElementContainer(), ce));
getElementContainer().getComponent().repaint();
}
/**
* Brings the selected shapes one step closer to the top of the display
*/
public void bringSelectionForward() {
List selectedElements = getSelectedElements();
int[] indices = new int[selectedElements.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]=getElementContainer().indexOf(selectedElements.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 < getElementContainer().size()-1) {
tmp=getElementContainer().get(indice);
getElementContainer().remove(indice);
getElementContainer().add(indice+1, tmp);
ce.addEdit(new DepthChangedEdit(getElementContainer(), indice, indice+1));
}
}
ce.end();
getElementContainer().getComponent().fireUndoableEditUpdate(new UndoableEditEvent(getElementContainer(), ce));
getElementContainer().getComponent().repaint();
}
/**
* Sends the selected shapes one step deeper in the document
*/
public void sendSelectionBackward() {
List selectedElements = getSelectedElements();
int[] indices = new int[selectedElements.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]= getElementContainer().indexOf(selectedElements.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=getElementContainer().get(indice);
getElementContainer().remove(indice);
getElementContainer().add(indice-1, tmp);
ce.addEdit(new DepthChangedEdit(getElementContainer(), indice, indice-1));
}
}
ce.end();
getElementContainer().getComponent().fireUndoableEditUpdate(new UndoableEditEvent(getElementContainer(), ce));
getElementContainer().getComponent().repaint();
}
/**
* Sends the selected shapes to the bottom of the document
*/
public void sendSelectionToBack() {
List selectedElements = getSelectedElements();
int[] indices = new int[selectedElements.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]=getElementContainer().indexOf(selectedElements.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=getElementContainer().get(indice);
getElementContainer().remove(indice);
getElementContainer().add(i, tmp);
ce.addEdit(new DepthChangedEdit(getElementContainer(), indice, i));
}
ce.end();
getElementContainer().getComponent().fireUndoableEditUpdate(new UndoableEditEvent(getElementContainer(), ce));
getElementContainer().getComponent().repaint();
}
/////////////////////////////////////////////////////////////////////////////
/**
* Align the content of the selection on the top
* of the highest shape
*/
public void alignSelectionToTop() {
int topBound = Integer.MAX_VALUE;
for(int i=0; i<_selectedElements.size(); i++) {
Shape s = ((ElementSelection)_selectedElements.get(i)).getShape();
if(s.getBounds().y < topBound) {
topBound = s.getBounds().y;
}
}
CompoundEdit ce = new CompoundEdit();
int dy;
for(int i=0; i<_selectedElements.size(); i++) {
ElementSelection ss = (ElementSelection)_selectedElements.get(i);
dy = topBound - ss.element.getBounds().y;
ss.translateShape(0, dy);
if(ce!=null){
ce.addEdit(ss.translateShapeEnd());
}
}
ce.end();
DiagramComponent dc = getElementContainer().getComponent();
dc.fireUndoableEditUpdate(new UndoableEditEvent(dc, ce));
dc.repaint();
}
/**
* Align the content of the selection on the bottom
* of the lowest shape
*/
public void alignSelectionToBottom() {
int bottomBound = Integer.MIN_VALUE;
for(int i=0; i<_selectedElements.size(); i++) {
Shape s = ((ElementSelection)_selectedElements.get(i)).getShape();
if(s.getBounds().y + s.getBounds().height > bottomBound) {
bottomBound = s.getBounds().y + s.getBounds().height;
}
}
CompoundEdit ce = new CompoundEdit();
int dy;
for(int i=0; i<_selectedElements.size(); i++) {
ElementSelection ss = (ElementSelection)_selectedElements.get(i);
dy = bottomBound - ss.element.getBounds().y - ss.element.getBounds().height;
ss.translateShape(0, dy);
if(ce!=null){
ce.addEdit(ss.translateShapeEnd());
}
}
ce.end();
DiagramComponent dc = getElementContainer().getComponent();
dc.fireUndoableEditUpdate(new UndoableEditEvent(dc, ce));
dc.repaint();
}
/**
* Align the content of the selection on the left
* of the leftmost shape
*/
public void alignSelectionToLeft() {
int leftBound = Integer.MAX_VALUE;
for(int i=0; i<_selectedElements.size(); i++) {
Shape s = ((ElementSelection)_selectedElements.get(i)).getShape();
if(s.getBounds().x < leftBound) {
leftBound = s.getBounds().x;
}
}
CompoundEdit ce = new CompoundEdit();
int dx;
for(int i=0; i<_selectedElements.size(); i++) {
ElementSelection ss = (ElementSelection)_selectedElements.get(i);
dx = leftBound - ss.element.getBounds().x;
ss.translateShape(dx,0);
if(ce!=null){
ce.addEdit(ss.translateShapeEnd());
}
}
ce.end();
DiagramComponent dc = getElementContainer().getComponent();
dc.fireUndoableEditUpdate(new UndoableEditEvent(dc, ce));
dc.repaint();
}
/**
* Align the content of the selection on the right
* of the rightmost shape
*/
public void alignSelectionToRight() {
int rightBound = Integer.MIN_VALUE;
for(int i=0; i<_selectedElements.size(); i++) {
Shape s = ((ElementSelection)_selectedElements.get(i)).getShape();
if(s.getBounds().x + s.getBounds().width > rightBound) {
rightBound = s.getBounds().x + s.getBounds().width;
}
}
CompoundEdit ce = new CompoundEdit();
int dx;
for(int i=0; i<_selectedElements.size(); i++) {
ElementSelection ss = (ElementSelection)_selectedElements.get(i);
dx = rightBound - ss.element.getBounds().x - ss.element.getBounds().width;
ss.translateShape(dx,0);
if(ce!=null){
ce.addEdit(ss.translateShapeEnd());
}
}
ce.end();
DiagramComponent dc = getElementContainer().getComponent();
dc.fireUndoableEditUpdate(new UndoableEditEvent(dc, ce));
dc.repaint();
}
/**
* Align the content of the selection on its vertical barycentre
*/
public void alignSelectionVCenter() {
int vCenter = 0;
Rectangle bounds;
for(int i=0; i<_selectedElements.size(); i++) {
Shape s = ((ElementSelection)_selectedElements.get(i)).getShape();
bounds = s.getBounds();
vCenter += bounds.y + bounds.height/2;
}
vCenter /= _selectedElements.size();
CompoundEdit ce = new CompoundEdit();
int dy;
for(int i=0; i<_selectedElements.size(); i++) {
ElementSelection ss = (ElementSelection)_selectedElements.get(i);
dy = vCenter - ss.element.getBounds().y - ss.element.getBounds().height/2;
ss.translateShape(0,dy);
if(ce!=null){
ce.addEdit(ss.translateShapeEnd());
}
}
ce.end();
DiagramComponent dc = getElementContainer().getComponent();
dc.fireUndoableEditUpdate(new UndoableEditEvent(dc, ce));
dc.repaint();
}
/**
* Align the content of the selection on its horizontal barycentre
*/
public void alignSelectionHCenter() {
int hCenter = 0;
Rectangle bounds;
for(int i=0; i<_selectedElements.size(); i++) {
Shape s = ((ElementSelection)_selectedElements.get(i)).getShape();
bounds = s.getBounds();
hCenter += bounds.x + bounds.width/2;
}
hCenter /= _selectedElements.size();
CompoundEdit ce = new CompoundEdit();
int dx;
for(int i=0; i<_selectedElements.size(); i++) {
ElementSelection ss = (ElementSelection)_selectedElements.get(i);
dx = hCenter - ss.element.getBounds().x - ss.element.getBounds().width/2;
ss.translateShape(dx,0);
if(ce!=null){
ce.addEdit(ss.translateShapeEnd());
}
}
ce.end();
DiagramComponent dc = getElementContainer().getComponent();
dc.fireUndoableEditUpdate(new UndoableEditEvent(dc, ce));
dc.repaint();
}
/**
* Sort shapes according their horizontal position.
* Shapes are sorted form left to right
* @author zxpletran007
*
*/
public class ShapeHorizontalPositionComparator implements Comparator{
public int compare(Object o1, Object o2){
return ( (((ElementSelection)o1)).element.getBounds().x - (((ElementSelection)o2)).element.getBounds().x); // Throw classCastException if objects to compare are not instance of AbstractShape
}
}
/**
* Sort shapes according their vertical position.
* Shapes are sorted form top to botton
* @author zxpletran007
*
*/
public class ShapeVerticalPositionComparator implements Comparator{
public int compare(Object o1, Object o2){
return ( (((ElementSelection)o1)).element.getBounds().y - (((ElementSelection)o2)).element.getBounds().y); // Throw classCastException if objects to compare are not instance of AbstractShape
}
}
/**
* Distributes the selected shapes so there is equal horizontal distance between the edges of all them
*/
public void distributeSelectionHorizontally() {
List selection = (List)_selectedElements.clone(); // make a copy
Collections.sort(selection, new ShapeHorizontalPositionComparator());
// Compute a mean distance
int meanDistance=0;
Rectangle bounds, previousBounds;
for(int i=1; i<selection.size(); i++) {
previousBounds= ((ElementSelection)selection.get(i-1)).getShape().getBounds();
bounds = ((ElementSelection)selection.get(i)).getShape().getBounds();
meanDistance+= bounds.x - (previousBounds.x + previousBounds.width);
}
meanDistance/=selection.size()-1;
// Trnaslate shapes
CompoundEdit ce = new CompoundEdit();
Shape shape, previousShape;
for(int i=1; i<selection.size(); i++) {
ElementSelection ss = ((ElementSelection)selection.get(i));
shape = ss.getShape();
previousShape= ((ElementSelection)selection.get(i-1)).getShape();
int dx = previousShape.getBounds().x + previousShape.getBounds().width + meanDistance - shape.getBounds().x;
ss.translateShape(dx,0);
if(ce!=null){
ce.addEdit(ss.translateShapeEnd());
}
}
ce.end();
DiagramComponent dc = getElementContainer().getComponent();
dc.fireUndoableEditUpdate(new UndoableEditEvent(dc, ce));
dc.repaint();
}
/**
* Distributes the selected shapes so there is equal vertical distance between the edges of all them
*/
public void distributeSelectionVertically() {
List selection = (List)_selectedElements.clone(); // make a copy
Collections.sort(selection, new ShapeVerticalPositionComparator());
// Compute a mean distance
int meanDistance=0;
Rectangle bounds, previousBounds;
for(int i=1; i<selection.size(); i++) {
previousBounds= ((ElementSelection)selection.get(i-1)).getShape().getBounds();
bounds = ((ElementSelection)selection.get(i)).getShape().getBounds();
meanDistance+= bounds.y - (previousBounds.y + previousBounds.height);
}
meanDistance/=selection.size()-1;
// Translate shapes
CompoundEdit ce = new CompoundEdit();
Shape shape, previousShape;
for(int i=1; i<selection.size(); i++) {
ElementSelection ss = ((ElementSelection)selection.get(i));
shape = ss.getShape();
previousShape= ((ElementSelection)selection.get(i-1)).getShape();
int dy = previousShape.getBounds().y + previousShape.getBounds().height + meanDistance - shape.getBounds().y;
ss.translateShape(0, dy);
if(ce!=null){
ce.addEdit(ss.translateShapeEnd());
}
}
ce.end();
DiagramComponent dc = getElementContainer().getComponent();
dc.fireUndoableEditUpdate(new UndoableEditEvent(dc, ce));
dc.repaint();
}
/**
* Computes shape origin before its move
* for a rectangle the top-left point coordinate is returned
* for a default shape the top-left point coordinate of its bounds is returned
* By overriding this method, specif shapes can be managed
* @param s the shape
* @param p a Point to put coordinates into
* (if null then a new Point is returned)
* @return a Point with coordinates
*/
public Point getShapeOrigin(Shape s, Point p){
Point res= ((p==null) ? (new Point()) : p );
if(s instanceof Rectangle){
Rectangle r=(Rectangle)s;
res.x=r.x;
res.y=r.y;
} else{
Rectangle r=s.getBounds();
res.x=r.x;
res.y=r.y;
}
return res;
}
/**
* Resizes a shape
* By overriding this method, specif shapes can be managed
* @param s the resizable shape
* @param dx x resizing
* @param dy y resizing
*/
public void resizeShape(Shape s, int dx, int dy){
if(s instanceof Resizable){
Resizable r=(Resizable)s;
r.resize(dx, dy);
}
}
/**
* Deletse a shape
* By overriding this method, one can use an other iterator
* on the shapes.
* @param s the shape
*/
public void deleteShape(Shape s){
if(getElementContainer() !=null){
getElementContainer().remove(s);
}
}
/**
* Removes all the selected shapes
*/
protected void clear(){
_selectedElements.clear();
_selectedShapesMap.clear();
}
/**
* Adds a selected shape if it is not already selected
* @return true if shape is added
*/
protected boolean add(ElementSelection dss){
int selectionOldCount = getShapeCount();
if(!_selectedShapesMap.containsKey(dss.element)){
// Selected shapes are sorted according to their depth
int index=0;
if (getElementContainer() != null){
for(index=0;index<_selectedElements.size();index++){
if (getElementContainer().indexOf( ((ElementSelection)_selectedElements.get(index)).element) >getElementContainer().indexOf(dss.element))
break;
}
}
_selectedElements.add(index, dss);
_selectedShapesMap.put(dss.element,dss);
}
return selectionOldCount != getShapeCount();
}
/**
* Add all the shapes to the selection.
* @return true if selection has changed
*/
protected boolean addAllShapes(){
int selectionOldCount = getShapeCount();
if (! (getElementContainer() == null || getElementContainer().size() == 0)) {
for (int i = 0; i < getElementContainer().size(); i++) {
Element s = (Element) getElementContainer().get(i);
add(createShapeSelection(s));
}
}
return selectionOldCount != getShapeCount();
}
/**
* The first shape which contains the given coordinates
* is added to the selected shapes list.
* By overriding this method, one can use an other iterator
* on the shapes.
* @param ox the x coordinate
* @param oy the y coordinate
* @return true if selection has changed
*/
protected boolean addShapeAt(int ox, int oy){
int selectionOldCount = getShapeCount();
if(getElementContainer() != null){
boolean addedShape = false;
for(int i=0; i<getElementContainer().size() && !addedShape;i++){
Element s = (Element)getElementContainer().get(i);
if(s.contains(ox,oy)){
add(createShapeSelection(s));
}
}
}
return selectionOldCount != getShapeCount();
}
protected ElementSelection createShapeSelection(Element s){
ElementSelection res;
Point shapeOrigin = getShapeOrigin(s,null);
if(s instanceof SelectableShapeInterface){
res = (((SelectableShapeInterface)s).createSelection(shapeOrigin, this));
} else {
res = (new ElementSelection(s, shapeOrigin));
}
return res;
}
/**
* Get the first shape found at given position which is not a connection.
* @param ox
* @param oy
* @return the first shape found at given position
*/
public Shape getShapeAt(int ox, int oy){
Shape res = null;
if(getElementContainer() != null){
for(int i=0; i<getElementContainer().size() && res==null;i++){
Shape s = (Shape)getElementContainer().get(i);
if( !(s instanceof Connection) && s.contains(ox,oy)){
res = s;
}
}
}
return res;
}
/**
* Get the first connection found at given position.
* @param ox
* @param oy
* @return the first connection found at given position
*/
public Connection getConnectionAt(int ox, int oy){
Connection res = null;
if(getElementContainer() != null){
for(int i=0; i<getElementContainer().size() && res==null;i++){
Shape s = (Shape)getElementContainer().get(i);
if((s instanceof Connection) && s.contains(ox,oy)){
res = (Connection)s;
}
}
}
return res;
}
/**
* All the shapes which are inside the selection area
* By overriding this method, one can use an other iterator
* on the shapes.
* @return true if selection has changed
*/
protected boolean addShapesInside(){
int selectionOldCount = getShapeCount();
if(getElementContainer() !=null){
for(int i=0;i<getElementContainer().size();i++){
Element s=(Element)getElementContainer().get(i);
if(contains(s.getBounds())){
add(createShapeSelection(s));
}
}
}
return selectionOldCount != getShapeCount();
}
}