/* ==============================================
* 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 Lievremont
*
*
* $Id: DiagramComponent.java,v 1.37 2009/02/02 10:32:31 ogor Exp $
*
* Changes
* -------
* 25-Sep-2003 : Initial public release (NB);
* 07-Jan-2004 : Full Screen initial support (NB);
* 05-Nov-2004 : Undo/Redo initial support (JBL);
*/
package simtools.diagram;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPopupMenu;
import javax.swing.JViewport;
import javax.swing.KeyStroke;
import javax.swing.Scrollable;
import javax.swing.SwingConstants;
import javax.swing.event.EventListenerList;
import javax.swing.event.UndoableEditEvent;
import javax.swing.event.UndoableEditListener;
import javax.swing.undo.CompoundEdit;
import javax.swing.undo.UndoManager;
import simtools.diagram.gate.Connection;
import simtools.diagram.gate.ConnectionPathSelection;
import simtools.diagram.gate.Gate;
import simtools.diagram.gate.GatedComponent;
import simtools.diagram.gate.Path;
import simtools.diagram.undo.CreateEdit;
import simtools.diagram.undo.GateConnectEdit;
import simtools.diagram.undo.GateDisconnectEdit;
import simtools.diagram.undo.UndoHandler;
import simtools.shapes.ShapesContainer;
import simtools.ui.MenuResourceBundle;
import simtools.ui.ResourceFinder;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
/**
* This class is an abstract diagram with editor functions such as
* zoom, grid, multiple selection, resize, copy/cut/paste, ...
* and a link capability between the elements of the diagram
*
* @author Claude Cazenave
* @author Jean-Baptiste Lièvremont
*
* @version 1.1 2001
*/
public abstract class DiagramComponent extends JComponent
implements Scrollable, Printable,
MouseListener, MouseMotionListener,MouseWheelListener,
ActionListener, KeyListener{
public static MenuResourceBundle diagramResources = ResourceFinder.getMenu(DiagramComponent.class);
/** If true, sheets are displayed as pages (pages border are displayed).
* Otherwise, sheet are displayed without limits.
* By default: false
*/
protected static boolean isPrintViewEnabled = false;
protected static final int minimumPageHeight = 200;
protected static final int minimumPageWidth = 200;
public static final double ZOOM_FACTOR = 1.5;
public static final double ZOOM_MIN = 0.1;
public static final double ZOOM_MAX = 10;
protected static int pageHeight = 800;
protected static int pageWidth = 800;
/** an array of predefined cursors for selection translation and sizing */
private static Cursor[] _cursors={
Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR),
Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR),
Cursor.getPredefinedCursor(Cursor.NE_RESIZE_CURSOR),
Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR),
Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR),
Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR),
Cursor.getPredefinedCursor(Cursor.SW_RESIZE_CURSOR),
Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR),
Cursor.getPredefinedCursor(Cursor.NW_RESIZE_CURSOR),
};
private static Cursor _handCursor, _creatingElementCursor;
static{
Toolkit toolkit = Toolkit.getDefaultToolkit();
// mouse hand
Image mouseHandImage = toolkit.getImage(diagramResources.getURL("mouseHand"));
_handCursor = toolkit.createCustomCursor(mouseHandImage , new Point(0,0), "mouseHand");
// Element creation
Image creatingElementImage = toolkit.getImage(diagramResources.getURL("creatingElement"));
_creatingElementCursor = toolkit.createCustomCursor(creatingElementImage , new Point(0,0), "creatingElement");
}
/** a stroked used to display the selection rectangle */
protected static BasicStroke _dashStroke;
/** the default stroke */
protected static BasicStroke _defaultStroke;
static{
float[] df={5.f};
_dashStroke=new BasicStroke(0.1f,
BasicStroke.CAP_SQUARE,
BasicStroke.JOIN_MITER,
10.f, df,0.f);
_defaultStroke=new BasicStroke();
}
/** the grid color */
protected static Color _gridColor=Color.lightGray;
/** the grid strocke */
protected static Stroke _gridStrocke = new BasicStroke(0);
/** the selection color */
protected static Color _selectionColor=Color.red;
/** the default color */
protected static Color _defaultColor=Color.black;
/** the diagram size */
protected Dimension _size;
/**
* a rectangle used to increase the diagram size
* automatically when the mouse is near the right or
* the bottom of the diagram
*/
protected Rectangle _autoScrollRect;
/**
* X coordinate of the beginning of the selection
*/
private int _selX=0;
/**
* Y coordinate of the beginning of the selection
*/
private int _selY=0;
/**
* value of the selection mode along the X axis
*/
private int _moveX=0;
/**
* value of the selection mode along the Y axis
*/
private int _moveY=0;
// Diagram modes
/**
* true if dragging operation is in use
*/
protected boolean _dragging;
/**
* true if shapes translation operation is in use
*/
protected boolean _translatingShapes;
/**
* true if sheet translation operation is in use
*/
protected boolean _translatingSheet;
/**
* true if an element point is being translated
*/
protected boolean _translatingPoint;
/**
* true is an element has been created
*/
protected boolean _elementHasBeenCreated;
/**
* Coordinates of the dragging start point
*/
protected Point _dragPoint;
/**
* resize way : N = 1, NE =2 , ...
*/
protected int _resizeWay;
/**
* The current tracked gates
*/
protected List _currentTrackedGates;
/**
* The current tracked component
*/
protected List _currentTrackedComponent;
/**
* diagram selection
*/
protected ElementsContainer _elementContainer;
/**
* Do we print just the diagram selection or everything?
*/
protected boolean _printSelectionOnly;
/**
* if true, background color is printed
*/
protected boolean _printBackgroundColor;
/**
* if true, all sheet is scale to fit to page dimension during printing process
*/
protected boolean _scaleSheet;
/**
* diagram parameters (zoom, grid,...)
*/
protected DiagramParameters _param;
/**
* diagram action listener
*/
protected DiagramAction _action;
/**
* diagram popup menu called when the right mouse buttom is clicked
*/
protected JPopupMenu _popup;
/**
* @deprecated
* Use _param.headerTitle
*/
protected String _headerTitle;
/**
* If autofit is true, the diagram shall automatically fill up all the space when put in a JViewPort
*
* If autofit is false, the diagram
*
* Default is true
**/
protected boolean autofit = true;
/** Can we edit the diagram
* Default is true
*/
protected boolean canEdit = true;
/** Full screen variable is true when this mode is active*/
protected boolean fullScreen;
protected GraphicsDevice fullScreenDevice;
protected JFrame fullScreenFrame;
protected Container windowedModeParent;
/** List of listeners, used by undo/redo feature */
protected transient EventListenerList _listenerList;
/** The <code>UndoHandler</code> catches undoable edits and
* sends them to the <code>UndoManager</code> */
protected transient UndoHandler _undoHandler;
protected transient UndoManager _undoManager;
/**
* This attribute is used by classes who need to register
* several undoable edits once for all (e.g translation and resize)
*/
protected transient CompoundEdit _compoundEdit;
/**
* The current contextual drawing
* @see ContextualDrawing
*/
protected ContextualDrawing contextualDrawing;
/**
* This attribute is true whenever the diagam component has been modified since last backup
*/
protected transient boolean hasBeenModified;
protected transient ElementCreator currentElementCreator;
/**
* Creates a new diagram
* @param the diagram parameters
*/
public DiagramComponent(DiagramParameters param, ElementsContainer elementContainer){
_param=param;
if(_param.backgr!=null){
setOpaque(true);
}
_selX=0;
_selY=0;
_moveX=0;
_moveY=0;
_dragging=false;
_dragPoint=new Point();
_resizeWay=0;
_translatingShapes=false;
_translatingSheet = false;
_translatingPoint = false;
_elementHasBeenCreated = false;
_currentTrackedGates = new ArrayList();
_currentTrackedComponent = new ArrayList();
_printSelectionOnly=false;
_printBackgroundColor = false;
_scaleSheet = true;
_elementContainer = elementContainer;
_elementContainer.setComponent(this);
_elementContainer.getSelection().setGrid(_param.grid);
_size=new Dimension(_param.width,_param.height);
_autoScrollRect=new Rectangle(0,0,32,32);
registerActions();
_popup=null;
_action=null;
_listenerList = new EventListenerList();
_undoManager = new UndoManager();
_undoHandler = new UndoHandler(_undoManager);
_compoundEdit = new CompoundEdit();
setDiagramSize(_param.width, _param.height);
addKeyListener(this);
addMouseListener(this);
addMouseMotionListener(this);
addUndoableEventListener(_undoHandler);
addMouseWheelListener(this);
}
/**
* Get the undo manager of this component to send
* undo/redo actions from SW
*/
public UndoManager getUndoManager(){
return _undoManager;
}
/**
* Enable/disable diagram editing
* @param s true to disable editing
*/
public void disableEditing(boolean s){
canEdit=!s;
}
/**
* Check if diagram editing is disabled
* @return true if editing is disabled
*/
public boolean isEditingDisbled(){
return canEdit==false;
}
/**
* Get the diagram parameters
*/
public DiagramParameters getParameters() {
return _param;
}
/**
* Sets the digram popup menu
* @param menu the new popup menu
*/
public void setPopupMenu(JPopupMenu menu){
_popup=menu;
}
/**
* Gets the diagram popup menu
* @return the popup menu or null if none
*/
public JPopupMenu getPopupMenu(){
return _popup;
}
/**
* Sets the diagram action listener
* Only one action listener is allowed
* @param act the listener
*/
public void setAction(DiagramAction act){
_action=act;
}
/**
* Gets the diagram action listener
* @return the listener
*/
public DiagramAction getAction(){
return _action;
}
/**
* Gets the name of the diagram
* @return the name
*/
public String getName(){
return _param.name;
}
/**
* @return true if the diagram has been modified since its last backup
*/
public boolean hasBeenModified(){
return hasBeenModified;
}
/**
* Sets the name of the diagram
* @param name the name
*/
public void setName(String name){
_param.name = name;
}
/**
* Set the diagram modification status
*/
public void setHasBeenModified(boolean hasBeenModified){
this.hasBeenModified = hasBeenModified;
}
/**
* Gets the name of the diagram
* @return the name
*/
public String toString(){
return _param.name;
}
/**
* @return
*/
public boolean isAutofit() {
return autofit;
}
/**
* @param autofit
*/
public void setAutofit(boolean autofit) {
this.autofit = autofit;
}
/**
* Gets the diagram left and right margin
* @return the margin
*/
public int getXMargin(){
return _param.xmargin;
}
/**
* Gets the diagram top and bottom margin
* @return the margin
*/
public int getYMargin(){
return _param.ymargin;
}
/**
* Sets the diagram margins
* @param x the left and right margin
* @param y the top and bottom margin
*/
public void setMargin(int x, int y){
_param.xmargin=x;
_param.ymargin=y;
}
/**
* Gets the diagram header height for printing
* @return the height
*/
public int getHeaderHeight(){
return _param.headerHeight;
}
/**
* Sets the diagram header height for printing
* @param h the height
*/
public void setHeaderHeight(int h){
_param.headerHeight=h;
}
/**
* Sets the diagram start size
* @param w the width
* @param h the height
*/
public void setDiagramSize(int w, int h){
_param.width=Math.max(_param.minimumWidth,w);
_param.height=Math.max(_param.minimumHeight,h);
_size.width=(int)(_param.scale*_param.width);
_size.height=(int)(_param.scale*_param.height);
setPreferredSize(_size);
setSize(_size);
}
/**
* Gets the diagram start width
* @return the width
*/
public int getDiagramWidth(){
return _param.width;
}
/**
* Gets the diagram start height
* @return the height
*/
public int getDiagramHeight(){
return _param.height;
}
/**
* Sets the diagram grid step
* @param grid the step (if =0 then no grid)
*/
public void setGrid(int grid){
if(grid<0){
_param.grid=0;
}
else{
_param.grid=grid;
}
_elementContainer.getSelection().setGrid(_param.grid);
setDiagramSize(_param.width, _param.height);
repaint();
}
/**
* Set current {@link ElementCreator}.
* When current {@link ElementCreator} is not null, elements can be created by
* clicking and dragging the mouse.
*
* @param ec
*/
public void setElementCreator(ElementCreator ec){
currentElementCreator = ec;
updateCursor();
}
/**
* Sets the diagram grid display status
* @param state =true to display the grid
*/
public void setGridDisplay(boolean state){
if(_param.gridDisplay!=state){
setDiagramSize(_param.width, _param.height);
repaint();
}
_param.gridDisplay=state;
}
/**
* Gets the diagram grid step
* @return the step (if =0 then no grid)
*/
public int getGrid(){
return _param.grid;
}
/**
* Gets the diagram grid display status
* @return true if the gris is displayed
*/
public boolean getGridDisplay(){
return _param.gridDisplay;
}
/**
* Sets the diagram zoom factor
* @param scale the scale factor
*/
public void setZoom(double newZoom){
if (newZoom < DiagramComponent.ZOOM_MIN){
newZoom = DiagramComponent.ZOOM_MIN;
} else if (newZoom > DiagramComponent.ZOOM_MAX){
newZoom = DiagramComponent.ZOOM_MAX;
}
if (newZoom != _param.scale){
_param.scale= newZoom;
setDiagramSize(_param.width, _param.height);
repaint();
}
}
/**
* Gets the diagram zoom factor
* @return the scale factor
*/
public double getZoom(){
return _param.scale;
}
/**
* Adjusts the diagram size to take into account components
* at the left of the left margin or upper the top margin
*/
public void adjust(){
// Check if a component has coordinates lower than left or up
// margins
Point p=new Point(0,0);
getMin(p);
// in this case translate each component
if((p.x!=0)||(p.y!=0)){
_elementContainer.getSelection().unselect();
if(_param.grid>0){
p.x=(p.x/_param.grid -1)*_param.grid;
p.y=(p.y/_param.grid -1)*_param.grid;
}
translate(-p.x, -p.y);
repaint();
}
}
/**
* Aligns the selection on the grid
*/
public void align(){
_elementContainer.getSelection().alignSelection();
repaint();
}
/**
* Paints the diagram according to zoom and grid
* This methods setup the transformation, draw
* the grid and the call the drawDiagram method.
* @param g the current graphics
*/
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
g2.scale(_param.scale,_param.scale);
// fill background
if(_param.backgr!=null){
g2.setColor(_param.backgr);
g2.fillRect(0,0,_param.width,_param.height);
if (!isAutofit()) {
int w,h;
if (fullScreen) {
w=fullScreenFrame.getWidth();
h=fullScreenFrame.getHeight();
}
else if (getParent() instanceof JViewport) {
w=((JViewport)getParent()).getWidth();
h=((JViewport)getParent()).getHeight();
}
else {w = _param.width; h = _param.height;}
if((w>_param.width)||(h>_param.height)){
g2.setColor(getBackground());
g2.fillRect(0,_param.height,w,h-_param.height);
g2.fillRect(_param.width,0,w-_param.width,h);
}
}
}
// fill grid
int gr=getGrid();
if( (gr > 0) && _param.gridDisplay) {
g2.translate(_param.xmargin,_param.ymargin);
Stroke oldStroke = g2.getStroke();
g2.setStroke(_gridStrocke);
g2.setColor(_gridColor);
int lx=(_param.width-2*_param.xmargin);
int ly=(_param.height-2*_param.ymargin);
int nx=lx/gr;
int ny=ly/gr;
for(int i=0;i<=nx;i++){
g2.drawLine(i*gr,0,i*gr,ly);
//g2.fillRect(i*gr,0,1,ly);
}
for(int j=0;j<=ny;j++){
g2.drawLine(0,j*gr,lx,j*gr);
//g2.fillRect(0,j*gr,lx,1);
}
g2.setStroke(oldStroke);
g2.translate(-_param.xmargin,-_param.ymargin);
}
// Display page format limits if option is enable
if (isPrintViewEnabled) {
g2.setColor(Color.WHITE);
g2.fillRect(pageWidth - _param.xmargin, 0, _param.width, _param.height);
g2.fillRect(0, pageHeight - _param.ymargin, pageWidth - _param.xmargin, _param.height);
g2.fillRect(0, 0 , _param.xmargin, pageHeight - _param.ymargin);
g2.fillRect(_param.xmargin, 0, pageWidth - 2*_param.xmargin, _param.ymargin);
g2.setColor(Color.BLACK);
g2.drawRect(_param.xmargin, _param.ymargin, pageWidth - 2*_param.xmargin, pageHeight - 2* _param.ymargin);
g2.fillRect(pageWidth - _param.xmargin, _param.ymargin + 4 , 2, pageHeight - 2* _param.ymargin - 2 );
g2.fillRect(_param.xmargin + 4, pageHeight - _param.ymargin , pageWidth - 2*_param.xmargin - 2, 2);
}
drawDiagram(g2);
}
public void repaint(Rectangle r) {
repaint(r,true);
}
public void repaint(Rectangle r, boolean withMargins) {
if (withMargins) {
// one additonal pixel is added to take into account
// scaling rounding effect (scale < 1)
r.setBounds(
(int)((r.x+_param.xmargin) * _param.scale ),
(int)((r.y+_param.ymargin) * _param.scale ),
(int)((r.width) * _param.scale)+1,
(int)((r.height) * _param.scale)+1
);
}
super.repaint(r);
}
/**
* Paints the diagram itself
* @param g2 the current graphics
*/
protected void drawDiagram(Graphics2D g2) {
g2.setStroke(_defaultStroke);
Point pMax=new Point();
// add left and top margin
//------------------------
g2.translate(_param.xmargin,_param.ymargin);
// draw diagram elements
//----------------------
drawDiagramElements(g2, pMax);
// draw clipboard elements
//----------------------
if(_dragging){
drawClipboardElements(g2, pMax);
}
// Draw tracked gates and components
//----------------------
Stroke oldStroke = g2.getStroke();
g2.setStroke(new BasicStroke(0));
// draw tracked component gates
for(int i=0;i<_currentTrackedComponent.size(); i++){
GatedComponent gc = ((GatedComponent) _currentTrackedComponent.get(i));
List gates = gc.getGates();
for(int j=0; j < gates.size(); j++){
((Gate)gates.get(j)).drawGate(g2);
}
}
// draw tracked gate information
if (!_currentTrackedGates.isEmpty()){
drawTrackedGatesInformation(g2);
}
g2.setStroke(oldStroke);
// display selection area
//-----------------------
if(_elementContainer.getSelection().width>=0){
oldStroke = g2.getStroke();
Color oldColor = g2.getColor();
g2.setColor(_selectionColor);
g2.setStroke(_dashStroke);
g2.draw(_elementContainer.getSelection());
// update drawing size
if((_elementContainer.getSelection().x+_elementContainer.getSelection().width)>pMax.x){
pMax.x=_elementContainer.getSelection().x+_elementContainer.getSelection().width;
}
if((_elementContainer.getSelection().y+_elementContainer.getSelection().height)>pMax.y){
pMax.y=_elementContainer.getSelection().y+_elementContainer.getSelection().height;
}
g2.setStroke(oldStroke);
g2.setColor(oldColor);
}
if(contextualDrawing != null){
contextualDrawing.draw(g2,pMax);
}
// add right and bottom margin
//----------------------------
g2.translate(-_param.xmargin,-_param.ymargin);
// update diagram size
//---------------------
pMax.x+=2*_param.xmargin;
pMax.y+=2*_param.ymargin;
if (fullScreen) {
// round up to avoid repeated resizes
setDiagramSize((int)(fullScreenFrame.getWidth() / _param.scale + 0.5), (int)(fullScreenFrame.getHeight() / _param.scale + 0.5));
} else if (isAutofit() && (getParent() instanceof JViewport)) {
int w = Math.max((int)(pMax.x*_param.scale), ((JViewport)getParent()).getWidth());
int h = Math.max((int)(pMax.y*_param.scale), ((JViewport)getParent()).getHeight());
// round up to avoid repeated resizes
setDiagramSize((int)(w / _param.scale + 0.5), (int)(h / _param.scale + 0.5));
}
else if((pMax.y!=_param.height)||(pMax.x!=_param.width)){
setDiagramSize(pMax.x,pMax.y);
}
}
protected void drawHeader(Graphics2D g2) {
if (hasHeader()) {
g2.setColor(_defaultColor);
g2.draw(new Rectangle(0,_param.height + _param.HEADER_MARGIN, _param.width, _param.headerHeight));
g2.drawString(
_param.headerTitle,
_param.width / 2,
_param.height + 10 + _param.headerHeight /2
);
/*g2.drawString(
_param.headerTitle,
_param.headerHeight/8,
_param.height+(3*_param.headerHeight)/8
);*/
}
}
/**
* Paints the diagram itself
* @param g2 the current graphics
*/
public void printDiagram(Graphics2D g2) {
Point pMax=new Point();
// add left and top margin
//------------------------
g2.translate(_param.xmargin,_param.ymargin);
// draw diagram elements
//----------------------
printDiagramElements(g2, pMax);
drawHeader(g2);
}
/**
* Gets max size of the diagram if printed
* @param g2 the current graphics
* @return the diagram right/bottom corner coordinates
*/
public Point getDiagramMaxPoint(Graphics2D g2) {
Point pMax=new Point();
// add left and top margin
//------------------------
g2.translate(_param.xmargin,_param.ymargin);
// draw diagram elements
//----------------------
printDiagramElements(g2, pMax);
return pMax;
}
/**
* Sets the header for this diagram component
* @param header The diagram header to print
*/
public void setHeader(String header) {
_param.headerTitle = header;
}
/**
* If we have a valid header
*/
public boolean hasHeader() {
return (_param.headerHeight > 0) && (_param.headerTitle!=null);
}
/**
* Sets the print mode
* @param selectionOnly If true, will print only the selection
*/
public void setSelectionPrintMode(boolean selectionOnly) {
_printSelectionOnly = selectionOnly;
}
/**
* Set the print Background Color boolean property
* @param printBackgroundColor
*/
public void setPrintBackgroundColor(boolean printBackgroundColor) {
_printBackgroundColor = printBackgroundColor;
}
/**
* Set the print Background Color boolean property
* @param printBackgroundColor
*/
public void setScaleSheet(boolean scaleSheet) {
_scaleSheet = scaleSheet;
}
/**
* Get the print Background Color boolean property
* @return printBackgroundColor
*/
public boolean getPrintBackgroundColor(){
return _printBackgroundColor;
}
/**
* Paints the diagram and a header
* @param g2 the current graphics
*/
public void drawDiagramWithHeader(Graphics2D g2) {
drawDiagram(g2);
drawHeader(g2);
}
/**
* Starts the creation of a new element
* This implementation creates nothing
*/
public void executeNewElement(){
}
/**
* Select all the shapes
*/
public void executeSelectAll(){
_elementContainer.getSelection().selectAll();
repaint();
}
/**
* If a connection end is selected, disconnect it from its gate
*/
protected void disconnectConnectionEnd(){
CompoundEdit ce = new CompoundEdit();
if (_elementContainer.getSelection().getShapeCount()==1 && _elementContainer.getSelection().getSelectedShape(0) instanceof ConnectionPathSelection){
ConnectionPathSelection cs = (ConnectionPathSelection)_elementContainer.getSelection().getSelectedShape(0);
Connection connection = cs.getConnection();
if( cs.isFirstEndSelected() && connection.getFirstEndGate()!=null ){
Gate firstGate = connection.getFirstEndGate();
connection.disconnect(firstGate);
ce.addEdit(new GateDisconnectEdit(connection, firstGate,true));
} else if( cs.isLastEndSelected() && connection.getLastEndGate()!=null ){
Gate lastGate = connection.getLastEndGate();
connection.disconnect(lastGate);
ce.addEdit(new GateDisconnectEdit(connection, lastGate,false));
}
}
ce.end();
if (ce.isSignificant()){
_compoundEdit.addEdit(ce);
}
}
/**
* Cuts the selection and put a copy into the clipbaord
*/
public void executeSelectionCut(){
_dragPoint.x=_moveX;
_dragPoint.y=_moveY;
// create a copy of the selected shapes
DiagramClipboard.set(_elementContainer.getSelection().copySelection(_dragPoint));
_dragging=false;
// delete selection
_elementContainer.getSelection().deleteSelection();
repaint();
}
/**
* Erases the selection
*/
public void executeSelectionDelete(){
// remove selection
_elementContainer.getSelection().deleteSelection();
_translatingShapes=false;
_resizeWay=0;
repaint();
}
/**
* Puts a copy of the selection into the clipbaord
*/
public void executeSelectionCopy(){
_dragPoint.x=_selX;
_dragPoint.y=_selY;
// create a copy of the selected shapes
DiagramClipboard.set(_elementContainer.getSelection().copySelection(_dragPoint));
_dragging=false;
// unselect
_elementContainer.getSelection().unselect();
_translatingShapes=false;
_resizeWay=0;
repaint();
}
/**
* Pastes the clipbaord content into the diagram
*/
public void executeClipboardPaste(){
// unselect
_elementContainer.getSelection().unselect();
_dragPoint.x=_moveX;
_dragPoint.y=_moveY;
// change cursor for a drag
setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
_dragging=true;
_translatingShapes=false;
_resizeWay=0;
repaint();
}
/**
* Checks if selection is empty
* @return true if it is empty
*/
public boolean isSelectionEmpty(){
return _elementContainer.getSelection().isEmpty();
}
/**
* Gets the selection elements
* @return a vector of elements
*/
public Vector getSelection(){
return _elementContainer.getSelection().getSelectedElements();
}
/**
* @return the element container managed by this diagram
*/
public ElementsContainer getElementContainer(){
return _elementContainer;
}
/**
* Gets the selection
* @return the diagram selection
*/
public DiagramSelection getDiagramSelection(){
return _elementContainer.getSelection();
}
/**
* Abstract method to translate the diagram elements
* @param x translation value along the X axis
* @param y translation value along the Y axis
*/
protected abstract void translate(int x, int y);
/**
* Gets the coordinates ot the element at the top left of the diagram
* @param p the coordinates to be compared with the elements coordinates
* and to be modified if an element has lower coordinates
*/
protected abstract void getMin(Point p);
/**
* To copy a list of diagram elements at the given coordinates
* @param v a vector of diagram elements
* @param x the x coordinate destination
* @param y the y coordinate destination
*/
protected abstract void copyAt(Vector v, int x, int y);
/**
* This method is called by the drawDiagram method to perform
* the drawing of each diagram elements
* @param g2 the current graphics
* @param pMax the coordinates of the elements at the bottom right
*/
protected abstract void drawDiagramElements(Graphics2D g2, Point pMax);
/**
* This method is called by the drawDiagram method to perform
* the printing of each diagram elements
* @param g2 the current graphics
* @param pMax the coordinates of the elements at the bottom right
*/
protected abstract void printDiagramElements(Graphics2D g2, Point pMax);
/**
* This method is called by the drawDiagram method to perform
* the drawing of each clipboard elements
* @param g2 the current graphics
* @param pMax the coordinates of the elements at the bottom right
*/
protected abstract void drawClipboardElements(Graphics2D g2, Point pMax);
/**
* This method is called by the drawDiagram method to
* allow tracked gates to display information about their contents.
* @param g2
*/
protected void drawTrackedGatesInformation(Graphics2D g2){
for(int i=0;i<_currentTrackedGates.size(); i++){
TrackedGate tg = (TrackedGate)_currentTrackedGates.get(i);
// Check whether or not a connection is allowed with the current gate
boolean allowConnection = true;
Gate otherGate;
if (tg.connection != null){
if (tg.isFirstEnd){
otherGate = tg.connection.getLastEndGate();
}else {
otherGate = tg.connection.getFirstEndGate();
}
allowConnection = tg.gate.allowConnection(otherGate);
}
// Draw information on the current available gate
tg.gate.drawGateInformation(g2, allowConnection);
}
}
/**
* This method is called at the end of a translation of points
* of the selection i.e when the mouse is released
*/
protected void pointTranslationEnd(){
// Reset _compoundEdit
_compoundEdit = new CompoundEdit();
}
/**
* If a connection is selected and a gate is currently available, perform the connection.
* @return true if one or more connections have been performed
*/
protected boolean connectConnectionToTrackedGates(){
boolean res = false;
if (!_currentTrackedGates.isEmpty()){
for(int i=0;i<_currentTrackedGates.size();i++){
TrackedGate tg = (TrackedGate)_currentTrackedGates.get(i);
Gate otherGate = tg.isFirstEnd? tg.connection.getFirstEndGate() : tg.connection.getLastEndGate();
if (tg.gate.allowConnection(otherGate)){
// Perform the connection
tg.connection.connect(tg.gate, tg.isFirstEnd);
// Update connection ends position
tg.connection.gatePositionHasChanged(tg.gate);
// Add connection edit
_compoundEdit.addEdit(new GateConnectEdit(tg.connection, tg.gate,tg.isFirstEnd));
res = true;
}
}
}
return res;
}
/**
* This method is called at the end of the transaltion
* of the selection i.e when the mouse is released
*/
protected void translationEnd(){
// Reset _compoundEdit
_compoundEdit = new CompoundEdit();
}
/**
* This method is called at the end of the resize
* of the selection i.e when the mouse is released
*/
protected void resizeEnd(){
// Reset _compoundEdit
_compoundEdit = new CompoundEdit();
}
//
// MouseListener interface
//
public void mouseClicked(MouseEvent e){
if(canEdit){
if(getContextualDrawing()!=null){
if(getContextualDrawing().consumeMouseEvent(e)){
return;
}
}
if ( (e.getClickCount()==2) && isMouseButton1(e) ){
if(_action!=null){
_action.executeAction(0,this);
}
}
}
}
public void mouseEntered(MouseEvent e){
if(getContextualDrawing()!=null){
if(getContextualDrawing().consumeMouseEvent(e)){
return;
}
}
requestFocusInWindow();
}
public void mouseExited(MouseEvent e){
if(getContextualDrawing()!=null){
getContextualDrawing().consumeMouseEvent(e);
}
updateCursor();
}
/* (non-Javadoc)
* @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
*/
public void mousePressed(MouseEvent e){
if(canEdit) {
if(getContextualDrawing()!=null){
if(getContextualDrawing().consumeMouseEvent(e)){
return;
}
}
_selX=(int)((double)e.getX()/_param.scale)-_param.xmargin;
_selY=(int)((double)e.getY()/_param.scale)-_param.ymargin;
_moveX=_selX;
_moveY=_selY;
_translatingShapes=false;
_resizeWay=0;
_translatingPoint = false;
if (currentElementCreator == null){
_currentTrackedGates.clear();
_currentTrackedComponent.clear();
}
if (!_translatingSheet){
if (isMouseButton3(e)) {
if(_popup!=null){
_popup.show(this,e.getX(),e.getY());
}
} else if (isMouseButton2(e)){
Shape selectedShape = _elementContainer.getSelection().getSelectedShapeAt(_selX,_selY);
if (selectedShape instanceof ContextualDrawingProvider && getContextualDrawing()==null) {
setContextualDrawing(((ContextualDrawingProvider)selectedShape).getContextualDrawing(_elementContainer.getSelection()));
getContextualDrawing().consumeMouseEvent(e);
}
} else if (isMouseButton1(e)) {
if((e.getClickCount()==2)){
if(_action!=null){
_action.executeAction(0,this);
}
} else if (currentElementCreator == null){
if(_dragging){
// add the clipboard to the diagram
copyAt(DiagramClipboard.get(), _selX, _selY);
_dragging=false;
updateCursor();
repaint();
} else if(_elementContainer.getSelection().isEmpty()){
if(_elementContainer.getSelection().selectPoint(_selX,_selY)){
// Enable the translation of the selection
_translatingShapes = true;
setCursor(_cursors[0]);
repaint();
}
} else {
if((e.getModifiers()&MouseEvent.SHIFT_MASK)== MouseEvent.SHIFT_MASK){
if(_elementContainer.getSelection().addPoint(_selX,_selY)){
repaint();
}
} else {
if(_elementContainer.getSelection().isSelected(_selX, _selY)){
if((e.getModifiers()&MouseEvent.CTRL_MASK)==
MouseEvent.CTRL_MASK){
_dragPoint.x=_selX;
_dragPoint.y=_selY;
// create a copy of the selected shapes
DiagramClipboard.set(_elementContainer.getSelection().copySelection(_dragPoint));
// change cursor for a drag
_dragging=true;
updateCursor();
// selection end
_elementContainer.getSelection().unselect();
repaint();
} else {
int cursorType;
_translatingPoint = _elementContainer.getSelection().canTranslatePointAt(_selX, _selY);
if (_translatingPoint) {
TranslatableShapePointsSelection tps = ((TranslatableShapePointsSelection)_elementContainer.getSelection().getSelection(0));
tps.selectPointAt(_selX, _selY);
cursorType = tps.getPointTranslationWay();
} else {
_resizeWay = _elementContainer.getSelection().checkResizeWay(_moveX,_moveY);
cursorType = _resizeWay;
if(_resizeWay == 0){
_translatingShapes = true;
}
}
setCursor(_cursors[cursorType]);
repaint();
}
} else {
// try to select something else
_elementContainer.getSelection().selectPoint(_selX,_selY);
// Enable the translation of the selection
_translatingShapes = true;
setCursor(_cursors[0]);
repaint();
}
}
}
}
}
}
}
}
public void mouseReleased(MouseEvent e){
if(!canEdit){
return;
}
if(getContextualDrawing()!=null){
if(getContextualDrawing().consumeMouseEvent(e)){
return;
}
}
if(isMouseButton1(e)) {
if(_dragging){
copyAt(DiagramClipboard.get(), _dragPoint.x, _dragPoint.y); // add the clipboard to the diagram
_dragging=false;
} else if(_translatingPoint) {
// If needed, make a gate connection
connectConnectionToTrackedGates();
// Terminate the point translation
_elementContainer.getSelection().translatePointEnd();
pointTranslationEnd();
_translatingPoint = false;
} else if(_translatingShapes) {
// If needed, make a gate connection
connectConnectionToTrackedGates();
// Terminate the shapes translation
_elementContainer.getSelection().translateShapesEnd();
translationEnd();
_translatingShapes=false;
} else if(_resizeWay>0) {
// Terminate the shapes resizing
_elementContainer.getSelection().resizeShapesEnd();
resizeEnd();
_resizeWay=0;
}
}
_elementContainer.getSelection().selectAreaEnd();
_elementHasBeenCreated = false;
updateCursor();
repaint();
}
/**
* Update the mouse cursor according the the current operation
*/
protected void updateCursor(){
Cursor cursor;
if (currentElementCreator != null) {
cursor = _creatingElementCursor;
} else if (_translatingSheet){
cursor = _handCursor;
} else if (_dragging){
cursor = (Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
} else {
cursor = Cursor.getDefaultCursor();
}
setCursor(cursor);
}
//
// MouseMotionListener interface
//
public void mouseDragged(MouseEvent e){
if(canEdit){
if(getContextualDrawing()!=null){
if(getContextualDrawing().consumeMouseEvent(e)){
return;
}
}
if (isMouseButton2(e)) {
Shape selectedShape = _elementContainer.getSelection().getSelectedShapeAt(_selX,_selY);
if (selectedShape instanceof ContextualDrawingProvider && getContextualDrawing()==null) {
setContextualDrawing(((ContextualDrawingProvider)selectedShape).getContextualDrawing(_elementContainer.getSelection()));
getContextualDrawing().consumeMouseEvent(e);
}
} else if (isMouseButton1(e)) {
if (_translatingSheet){
int nx = e.getX() - _param.xmargin;
int ny= e.getY() - _param.ymargin;
int dx=nx-_moveX;
int dy=ny-_moveY;
_moveX = nx;
_moveY = ny;
// Compute the view coordinates that appear in the upper left hand corner of the viewport
JViewport viewPort = (JViewport)getParent();
Point viewpos = viewPort.getViewPosition();
Dimension viewDim = viewPort.getViewSize();
viewpos.x += dx;
viewpos.y += dy;
if (viewpos.x < 0){
viewpos.x = 0;
}
if ( viewpos.x > viewDim.width - viewPort.getWidth() ){
viewpos.x = viewDim.width - viewPort.getWidth();
}
if (viewpos.y < 0){
viewpos.y = 0;
}
if ( viewpos.y > viewDim.height - viewPort.getHeight() ){
viewpos.y = viewDim.height - viewPort.getHeight();
}
viewPort.setViewPosition(viewpos);
} else {
int nx=(int)((double)e.getX()/_param.scale)-_param.xmargin;
int ny=(int)((double)e.getY()/_param.scale)-_param.ymargin;
int dx = nx - _moveX;
int dy = ny - _moveY;
_moveX = nx;
_moveY = ny;
if(_dragging){
// move dragged boxes
setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
_dragPoint.translate(dx,dy);
}else if (currentElementCreator != null && !_elementHasBeenCreated){
_elementHasBeenCreated = true;
Gate selectedGate = null;
if(!_currentTrackedGates.isEmpty() ){
selectedGate = ((TrackedGate)_currentTrackedGates.get(0)).gate;
}
// Create a new element
Element createdElement = currentElementCreator.create(_selX,_selY, dx, dy);
if (createdElement != null){
_elementContainer.add(createdElement);
_compoundEdit.addEdit(new CreateEdit(_elementContainer,createdElement));
// If the element created is a connection and if a gate has been tracked,
// then perform the connection of the first connection end.
if (selectedGate!=null && createdElement instanceof Connection){
Connection createdConnection = (Connection)createdElement;
createdConnection.connect(selectedGate, true);
createdConnection.gatePositionHasChanged(selectedGate);
_compoundEdit.addEdit(new GateConnectEdit(createdConnection, selectedGate, true));
}
// clear tracked gates and components
_currentTrackedGates.clear();
_currentTrackedComponent.clear();
// Select the created element
_elementContainer.getSelection().clear();
ElementSelection es = _elementContainer.getSelection().createShapeSelection(createdElement);
_elementContainer.getSelection().add(es);
if (es instanceof ConnectionPathSelection){
((ConnectionPathSelection)es).selectLastEnd();
_translatingPoint = true;
} else {
_resizeWay = 4; // TODO: allow the creation of shape whatever the way of creation
}
}
} else if(_elementContainer.getSelection().isEmpty() || _elementContainer.getSelection().isSizing()){
// set area size
_elementContainer.getSelection().selectArea(_selX, _selY, nx-_selX, ny-_selY);
} else {
if (_resizeWay > 0) { // continue resizing
_elementContainer.getSelection().resizeSelection(dx, dy, _resizeWay);
} else if (_translatingShapes) { // continue translating
if ( canTranslate()){
_elementContainer.getSelection().translateShapes(dx, dy);
// Gate tracking
if(canTrackGate()) {
trackGatesAndComponents(); // update the list of tracked gates
}
}
} else if (_translatingPoint){ // continue translating selected point
// Disconnect the connector
disconnectConnectionEnd();
_elementContainer.getSelection().translatePoint(dx, dy);
// Gate tracking
if(canTrackGate()) {
trackGatesAndComponents(); // update the list of tracked gates
}
} else {
int cursorType;
_translatingPoint = _elementContainer.getSelection().canTranslatePointAt(_selX, _selY);
if (_translatingPoint) {
// Disconnect the connector
disconnectConnectionEnd();
TranslatableShapePointsSelection tps = ((TranslatableShapePointsSelection)_elementContainer.getSelection().getSelection(0));
tps.selectPointAt(_selX, _selY);
cursorType = tps.getPointTranslationWay();
_elementContainer.getSelection().translatePoint(dx, dy);
// Gate tracking
if(canTrackGate()) {
trackGatesAndComponents(); // update the list of tracked gates
}
} else {
_resizeWay = _elementContainer.getSelection().checkResizeWay(_moveX, _moveY);
cursorType = _resizeWay;
if (_resizeWay > 0) { // resize
_elementContainer.getSelection().resizeSelection(dx, dy, _resizeWay);
} else { // translate
if (canTranslate()){
_elementContainer.getSelection().translateShapes(dx, dy);
_translatingShapes = true;
// Gate tracking
if(canTrackGate()) {
trackGatesAndComponents(); // update the list of tracked gates
}
}
}
}
setCursor(_cursors[cursorType]);
}
}
}
}
repaint();
}
}
/**
* Check whether or not the current selection can be translated.
* In this implementation, the selection can be translated if
* <ul>
* <li>For each selected connection, one of the connection ends gate is a non selected gate
* </ul>
* @return
*/
protected boolean canTranslate(){
boolean res = true;
int selectionSize= _elementContainer.getSelection().getShapeCount();
for (int i=0;i< selectionSize && res; i++){
ElementSelection ds = _elementContainer.getSelection().getSelectedShape(i);
if (ds instanceof ConnectionPathSelection){
ConnectionPathSelection cs = (ConnectionPathSelection)ds;
Connection connection = cs.getConnection();
Gate firstGate = connection.getFirstEndGate();
if (firstGate!=null && !_elementContainer.getSelection().isSelected((Shape)firstGate.getOwner())) {
res = false;
}
Gate lastGate = connection.getLastEndGate();
if (lastGate!=null && !_elementContainer.getSelection().isSelected((Shape)lastGate.getOwner())) {
res = false;
}
}
}
return res;
}
/**
* Check whether or not gates can be tracked upon mouse move.
* In this implementation, the gate tracking can be performed on one of following cases:
* <ul>
* <li>A connection end is being translated
* <li>A connection is being translated
* <li>A gated component is being translated
* <li>An element that needs the gate tracking is being created.
* </ul>
* @return true if the gate tracking can be performed.
*/
protected boolean canTrackGate(){
boolean res = false;
if (currentElementCreator != null && !_elementHasBeenCreated) {
res = currentElementCreator.canTrackGates();
} else if (_elementContainer.getSelection().getShapeCount()== 1){
ElementSelection ss = _elementContainer.getSelection().getSelection(0);
if (_translatingPoint){
res = (ss instanceof ConnectionPathSelection) && ((ConnectionPathSelection)ss).isOneEndSelected();
} else if (_translatingShapes){
Shape s = ss.element;
res = (s instanceof Connection) || (s instanceof GatedComponent) ;
}
}
return res;
}
/**
* Track gates and gated component
* @return true if the list of tracked gates or the list of tracked component are not empty
*/
private boolean trackGatesAndComponents(){
_currentTrackedGates.clear();
_currentTrackedComponent.clear();
if (currentElementCreator!=null && !_elementHasBeenCreated){
// track gates and components at current mouse position
Point p = new Point(_moveX, _moveY);
GatedComponent gc = getGateComponentAt(p);
if (gc != null){
_currentTrackedComponent.add(gc);
}
Gate gate = getGateAt(p);
if (gate != null){
_currentTrackedGates.add(new TrackedGate(null, true, gate));
}
} else if (_elementContainer.getSelection().getShapeCount()== 1){
// track gates and components for selected gated components or connections
ElementSelection ss = _elementContainer.getSelection().getSelection(0);
// look up for gate at selected end
if (_translatingPoint && ss instanceof ConnectionPathSelection && ((ConnectionPathSelection)ss).isOneEndSelected()){
ConnectionPathSelection cps = ((ConnectionPathSelection)ss);
Connection connection = cps.getConnection();
boolean isFirstEndSelected = ((ConnectionPathSelection)ss).isFirstEndSelected();
Point p = isFirstEndSelected? connection.getPath().getNode(0) : connection.getPath().getNode(connection.getPath().getNodeNumber()-1);
// Component
GatedComponent gc = getGateComponentAt(p);
if (gc != null){
_currentTrackedComponent.add(gc);
}
// Gate
Gate gate = getGateAt(p);
if (gate != null){
_currentTrackedGates.add(new TrackedGate(connection, isFirstEndSelected, gate));
}
} else if (_translatingShapes){
Shape s = ss.element;
// For each connection ends, look up for a gate
if (ss instanceof ConnectionPathSelection){
Connection connection = ((ConnectionPathSelection)ss).getConnection();
Path connectionPath = connection.getPath();
Point firstEnd = connectionPath.getNode(0); // first end
GatedComponent gc = getGateComponentAt(firstEnd);
if (gc != null){
_currentTrackedComponent.add(gc);
}
Gate gate = getGateAt(firstEnd);
if (gate != null){
_currentTrackedGates.add(new TrackedGate(connection, true, gate));
}
Point lastEnd = connectionPath.getNode(connectionPath.getNodeNumber()-1); // first end
gc = getGateComponentAt(lastEnd);
if (gc != null){
_currentTrackedComponent.add(gc);
}
gate = getGateAt(lastEnd);
if (gate != null){
_currentTrackedGates.add(new TrackedGate(connection, false, gate));
}
// For each gates, look up for a connection ends
} else if (s instanceof GatedComponent){
GatedComponent gc = ((GatedComponent)s);
List gates = gc.getGates();
for(int i =0;i<gates.size();i++){
Gate gate = (Gate)gates.get(i);
Point gateAnchor = gate.getAnchor();
Connection connection = _elementContainer.getSelection().getConnectionAt(gateAnchor.x, gateAnchor.y);
if (connection != null && !connection.isConnected(gate)){
// first connection bound is free
if ( gate.canBeHanged(connection.getPath().getNode(0)) && connection.getFirstEndGate()==null){
_currentTrackedGates.add(new TrackedGate(connection, true, gate));
// last connection bound is free
} else if ( gate.canBeHanged(connection.getPath().getNode(connection.getPath().getNodeNumber()-1))
&& connection.getLastEndGate()==null){
_currentTrackedGates.add(new TrackedGate(connection, false, gate));
}
}
}
}
}
}
return !_currentTrackedGates.isEmpty() || !_currentTrackedComponent.isEmpty();
}
/**
* Return the gate found on current diagram at given position
* @param p
* @return the gate found on current diagram at given position, or null if not gate found.
*/
private Gate getGateAt(Point p){
Gate res = null;
GatedComponent gc = getGateComponentAt(p);
if (gc != null){
res = gc.getGateAt(p.x, p.y);
}
return res;
}
private GatedComponent getGateComponentAt(Point p){
GatedComponent res = null;
Shape shape = _elementContainer.getSelection().getShapeAt(p.x, p.y);
if (shape != null && shape instanceof GatedComponent){
res = ((GatedComponent)shape);
}
return res;
}
/**
* TrackedGate holds a gate and a connection end that can be connected to
* @author zxpletran007
*
*/
protected static class TrackedGate{
public Gate gate;
public Connection connection;
public boolean isFirstEnd;
public TrackedGate(Connection connection, boolean isFirstEnd, Gate gate) {
super();
this.connection = connection;
this.isFirstEnd = isFirstEnd;
this.gate = gate;
}
}
public void mouseMoved(MouseEvent e){
if(!canEdit){
return;
}
if(getContextualDrawing()!=null){
if(getContextualDrawing().consumeMouseEvent(e)){
return;
}
}
int nx=(int)((double)e.getX()/_param.scale)-_param.xmargin;
int ny=(int)((double)e.getY()/_param.scale)-_param.ymargin;
boolean repaint = false;
if(_dragging){
// move dragged boxes
int dx=nx-_moveX;
int dy=ny-_moveY;
_dragPoint.translate(dx,dy);
_moveX=nx;
_moveY=ny;
repaint = true;
} else {
_translatingShapes = false;
_translatingPoint = false;
_currentTrackedGates.clear();
_currentTrackedComponent.clear();
_resizeWay=0;
_moveX=nx;
_moveY=ny;
}
updateCursor();
if(canTrackGate()) {
trackGatesAndComponents();
repaint =true;
}
if (repaint){
repaint();
}
}
public void mouseWheelMoved(MouseWheelEvent e){
boolean canProccessMouseWheelEvent = false;
if(canEdit){
_selX=(int)((double)e.getX()/_param.scale)-_param.xmargin;
_selY=(int)((double)e.getY()/_param.scale)-_param.ymargin;
if(_elementContainer.getSelection().isSelected(_selX, _selY)){
Shape s2=_elementContainer.getSelection().getSelectedShapeAt(_selX,_selY);
if(s2 instanceof ContextualDrawingProvider && getContextualDrawing()==null){
setContextualDrawing(((ContextualDrawingProvider)s2).getContextualDrawing(_elementContainer.getSelection()));
getContextualDrawing().consumeMouseEvent(e);
canProccessMouseWheelEvent = true;
}
}
}
if ( !canProccessMouseWheelEvent && ((e.getModifiers() & MouseEvent.CTRL_MASK) == MouseEvent.CTRL_MASK)) { // zoom in or out
boolean zoomIn = (((MouseWheelEvent)e).getWheelRotation()<0);
// Compute zoom to apply
double oldZoom = getZoom();
double newZoom = ((zoomIn)? oldZoom * ZOOM_FACTOR : oldZoom / ZOOM_FACTOR);
if (newZoom < ZOOM_MIN){
newZoom = ZOOM_MIN;
} else if (newZoom > ZOOM_MAX){
newZoom = ZOOM_MAX;
}
// Apply zoom
setZoom(newZoom);
canProccessMouseWheelEvent = true;
if ((oldZoom != newZoom) && (getParent() instanceof JViewport) ){
// Update view position regarding mouse position
JViewport viewPort = (JViewport)getParent();
Point viewpos = viewPort.getViewPosition();
// Compute ratio to appy to view position
double ratioX = ( e.getX() - viewpos.x) / (double)( viewPort.getWidth());
double ratioY = ( e.getY() - viewpos.y) / (double)( viewPort.getHeight());
// Find mouse position in new zoom view
double mousePosX = (e.getX()/ oldZoom) * newZoom;
double mousePosY = (e.getY()/ oldZoom) * newZoom;
viewpos.x = (int) (mousePosX - viewPort.getWidth()* ratioX );
viewpos.y = (int) (mousePosY - viewPort.getHeight() * ratioY);
// Cannot exceed component dimension
if (viewpos.x < 0){
viewpos.x = 0;
}
if (viewpos.y < 0){
viewpos.y = 0;
}
Dimension viewDim = viewPort.getViewSize();
if (viewpos.x + viewPort.getWidth() > viewDim.getWidth()){
viewpos.x = (int) (viewDim.getWidth() - viewPort.getWidth());
}
if (viewpos.y + viewPort.getHeight() > viewDim.getHeight()){
viewpos.y = (int) (viewDim.getHeight() - viewPort.getHeight());
}
viewPort.setViewPosition(viewpos);
}
}
if (!canProccessMouseWheelEvent){ // dispatch event to parent
getParent().dispatchEvent(e);
}
}
public void keyPressed(java.awt.event.KeyEvent arg0){
if (!_translatingSheet && arg0.getKeyCode() == KeyEvent.VK_SPACE){
_translatingSheet = true;
updateCursor();
}
}
public void keyReleased(java.awt.event.KeyEvent arg0){
if (arg0.getKeyCode() == KeyEvent.VK_SPACE){
_translatingSheet = false;
updateCursor();
}
}
public void keyTyped(java.awt.event.KeyEvent arg0){
}
//
// ActionListener interface
//
public void actionPerformed(ActionEvent e){
if(!canEdit){
return;
}
String cmd=e.getActionCommand();
if(cmd.equals("n")){
executeNewElement();
}
else if(cmd.equals("c")){
executeSelectionCopy();
}
else if(cmd.equals("v")){
executeClipboardPaste();
}
else if(cmd.equals("x")){
executeSelectionCut();
}
else if(cmd.equals("d")){
executeSelectionDelete();
}
else if(cmd.equals("exitFullScreen")){
if (fullScreen) setFullScreen(false);
}
else if(cmd.equals("a")){
executeSelectAll();
}
}
/**
* Computes the scale factor between parameter space and paper coordinates
* @param pf The PageFormat to se for printing
* @return The number by which to scale the Graphics so that parameter
* coordinates are correct.
*/
protected double computePrintScaleFactor(PageFormat pf) {
double sx=pf.getImageableWidth()/(double)( _param.width + _param.xmargin);
double totalHeight = (double)(_param.height + _param.ymargin);
if (hasHeader()) totalHeight += (double)_param.headerHeight + DiagramParameters.HEADER_MARGIN;
double sy = pf.getImageableHeight() / totalHeight;
double s = Math.min(sx,sy);
if (s>1.) s=1.;
return s;
}
//
// Printable interface
//
public int print(Graphics g, PageFormat pf, int pnum) throws PrinterException {
Graphics2D g2 = (Graphics2D)g;
g2.translate((int) pf.getImageableX(), (int) pf.getImageableY());
// shape the graphic if option has been selected
if (_scaleSheet) {
double s = computePrintScaleFactor(pf);
g2.scale(s, s);
}
// First, print a background if option has been selected
if ( (_param.backgr != null) && _printBackgroundColor){
g2.setColor(_param.backgr);
g2.fillRect(0,0, getBounds().width, getBounds().height);
}
// Then print diagram elements
printDiagram(g2);
return Printable.PAGE_EXISTS;
}
//
// Scrollable interface
//
/**
* Returns the preferred display size of a Canvas.
* @return a Dimension object containing the preferred size
*/
public Dimension getPreferredScrollableViewportSize() {
return getPreferredSize();
}
/**
* Returns the amount to increment when scrolling.
*
* @param visibleRect The view area visible within the viewport
* @param orientation Either SwingConstants.VERTICAL or SwingConstants.HORIZONTAL.
* @param direction Less than zero to scroll up/left, greater than zero for down/right.
* @return The "unit" increment for scrolling in the specified direction
* @see JScrollBar#setUnitIncrement
*/
public int getScrollableUnitIncrement(Rectangle visibleRect,
int orientation, int direction) {
return Math.max(1,((orientation == SwingConstants.VERTICAL)
? visibleRect.height : visibleRect.width)/10);
}
/**
* Returns the amount for a block inrecment, which is the height or
* width of <code>visibleRect</code>, based on <code>orientation</code>.
*
* @param visibleRect The view area visible within the viewport
* @param orientation Either SwingConstants.VERTICAL or SwingConstants.HORIZONTAL.
* @param direction Less than zero to scroll up/left, greater than zero for down/right.
* @return The "block" increment for scrolling in the specified direction.
* @see JScrollBar#setBlockIncrement
*/
public int getScrollableBlockIncrement(Rectangle visibleRect,
int orientation, int direction) {
return (orientation == SwingConstants.VERTICAL)
? visibleRect.height : visibleRect.width;
}
/**
* Returns false to indicate that the width of the viewport does not
* determine the width of the table, unless the preferred width of
* the canvas is smaller than the viewports width. In other words:
* ensure that the canvas is never smaller than its viewport.
* @return false
* @see Scrollable#getScrollableTracksViewportWidth
*/
public boolean getScrollableTracksViewportWidth() {
/* if (getParent() instanceof JViewport) {
return (((JViewport)getParent()).getWidth() > getPreferredSize().width);
}*/
return false;
}
/**
* Returns false to indicate that the height of the viewport does not
* determine the height of the table, unless the preferred height
* of the canvas is smaller than the viewports height. In other words:
* ensure that the canvas is never smaller than its viewport.
* @return false
* @see Scrollable#getScrollableTracksViewportHeight
*/
public boolean getScrollableTracksViewportHeight() {
/* if (getParent() instanceof JViewport) {
return (((JViewport)getParent()).getHeight() > getPreferredSize().height);
}*/
return false;
}
//
// Miscelaneous
//
protected void registerActions(){
setRequestFocusEnabled(true);
KeyStroke ks;
ks=KeyStroke.getKeyStroke(KeyEvent.VK_L,
KeyEvent.CTRL_MASK);
registerKeyboardAction(this, "l",
ks, WHEN_FOCUSED);
ks=KeyStroke.getKeyStroke(KeyEvent.VK_W,
KeyEvent.CTRL_MASK);
registerKeyboardAction(this, "l",
ks, WHEN_FOCUSED);
ks=KeyStroke.getKeyStroke(KeyEvent.VK_S,
KeyEvent.CTRL_MASK);
registerKeyboardAction(this, "s",
ks, WHEN_FOCUSED);
ks=KeyStroke.getKeyStroke(KeyEvent.VK_N,
KeyEvent.CTRL_MASK);
registerKeyboardAction(this, "n",
ks, WHEN_FOCUSED);
ks=KeyStroke.getKeyStroke(KeyEvent.VK_C,
KeyEvent.CTRL_MASK);
registerKeyboardAction(this, "c",
ks, WHEN_FOCUSED);
ks=KeyStroke.getKeyStroke(KeyEvent.VK_COPY,0);
registerKeyboardAction(this, "c",
ks, WHEN_FOCUSED);
ks=KeyStroke.getKeyStroke(KeyEvent.VK_X,
KeyEvent.CTRL_MASK);
registerKeyboardAction(this, "x",
ks, WHEN_FOCUSED);
ks=KeyStroke.getKeyStroke(KeyEvent.VK_CUT,0);
registerKeyboardAction(this, "x",
ks, WHEN_FOCUSED);
ks=KeyStroke.getKeyStroke(KeyEvent.VK_V,
KeyEvent.CTRL_MASK);
registerKeyboardAction(this, "v",
ks, WHEN_FOCUSED);
ks=KeyStroke.getKeyStroke(KeyEvent.VK_PASTE,0);
registerKeyboardAction(this, "v",
ks, WHEN_FOCUSED);
ks=KeyStroke.getKeyStroke(KeyEvent.VK_DELETE,0);
registerKeyboardAction(this, "d",
ks, WHEN_FOCUSED);
ks=KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE,0);
registerKeyboardAction(this, "d",
ks, WHEN_FOCUSED);
ks=KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE,0);
registerKeyboardAction(this, "exitFullScreen",
ks, WHEN_FOCUSED);
ks=KeyStroke.getKeyStroke(KeyEvent.VK_A,
KeyEvent.CTRL_MASK);
registerKeyboardAction(this, "a",
ks, WHEN_FOCUSED);
}
/**
* @return Returns the fullScreen status.
*/
public boolean isFullScreen() {
return fullScreen;
}
/**
* Use this function with "false" as argument to exit full screen mode.
* When using "true" as argument, this methods only works if a graphics
* device was previously specified using the other setFullScreen method.
* @param fullScreen The fullScreen mode to set.
*/
public void setFullScreen(boolean fullScreenState) {
if ((fullScreenDevice==null) || (fullScreenFrame==null)) fullScreenState=false;
// delegate to other method to enter full screen mode
if (fullScreenState) {
setFullScreen(fullScreenDevice);
return;
}
// exit full screen mode when active
if (fullScreen) {
if (fullScreenDevice!=null) fullScreenDevice.setFullScreenWindow(null);
if (windowedModeParent!=null) windowedModeParent.add(this);
setVisible(true);
revalidate();
fullScreenFrame.hide();
}
fullScreen = false;
}
/**
* Use this method with a valid graphics device to enter full screen mode.
* Use null as argument to exit full screen mode.
*/
public void setFullScreen(GraphicsDevice device) {
// delegate to other method to exit full screen mode
if (device==null) {
setFullScreen(false);
return;
}
fullScreenDevice = device;
if (fullScreenFrame==null) {
fullScreenFrame = new JFrame();
fullScreenFrame.setUndecorated(true);
fullScreenFrame.setResizable(false);
if(_param.backgr!=null) fullScreenFrame.setBackground(_param.backgr);
}
if (!fullScreen) windowedModeParent = getParent();
fullScreenFrame.getContentPane().add(this);
device.setFullScreenWindow(fullScreenFrame);
fullScreen = true;
// round up to avoid repeated resizes
setDiagramSize((int)(device.getDisplayMode().getWidth() / _param.scale + 0.5), (int)(device.getDisplayMode().getHeight() / _param.scale + 0.5));
fullScreenFrame.show();
}
/**
* Adds an <code>UndoableEditListener</code> to the list of event listeners,
* to provide the component with undo/redo features.
* @param listener
*/
public void addUndoableEventListener(UndoableEditListener listener) {
_listenerList.add(UndoableEditListener.class, listener);
}
/**
* Removes an <code>UndoableEditListener</code> from the list of event listeners.
* @see addUndoableEventListener(UndoableEditListener)
* @param listener
*/
public void removeUndoableEventListener(UndoableEditListener listener) {
_listenerList.remove(UndoableEditListener.class, listener);
}
/**
* Notify all <code>UndoableEditListener</code> that an <code>UndoableEditEvent</code>
* has happened.
* @param event
*/
public void fireUndoableEditUpdate(UndoableEditEvent event) {
// Guaranteed to return a non-null array
Object[] listeners = _listenerList.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i = listeners.length-2; i>=0; i-=2) {
if (listeners[i]==UndoableEditListener.class) {
((UndoableEditListener)listeners[i+1]).undoableEditHappened(event);
}
}
hasBeenModified =true;
}
/**
* @return Returns the <code>UndoHandler</code> associated with this component
*/
public UndoHandler getUndoHandler() {
return _undoHandler;
}
/**
* Install a new contextual drawing element
* @param cd the element
*/
public void setContextualDrawing(ContextualDrawing cd){
if(contextualDrawing instanceof DiagramSelectionListener){
_elementContainer.getSelection().removeListener((DiagramSelectionListener)contextualDrawing);
}
contextualDrawing=cd;
if(contextualDrawing==null){
updateCursor();
}
if(contextualDrawing instanceof DiagramSelectionListener){
_elementContainer.getSelection().addListener((DiagramSelectionListener)contextualDrawing);
}
}
/**
* Gets the current contextual drawing element
* @return the element or null if none installed
*/
public ContextualDrawing getContextualDrawing(){
return contextualDrawing;
}
/**
* An interface for additional drawing to be performed
* in specific context
*/
public interface ContextualDrawing {
/**
* Do the additional drawing
* @param g2 the current graphic
* @param max the max point to compute diagram size
*/
public void draw(Graphics2D g2, Point max);
/**
* During this specific context, the mouse events
* can be consumed and thus nt handled as in normal
* mode
* @param e the mouse event (including motion events)
* @return true if it is consumed by the specific
* mouse event handler
*/
public boolean consumeMouseEvent(MouseEvent e);
/**
* During this specific context, the key events
* can be consumed and thus nt handled as in normal
* mode
* @param e the key event
* @return true if it is consumed by the specific
* key event handler
*/
public boolean consumeKeyEvent(KeyEvent e);
}
/**
* An interface implemented by diagram elements
* which provide a ContextualDrawing when mouse
* button 2 is pressed on them while they are selected
*/
public interface ContextualDrawingProvider {
public ContextualDrawing getContextualDrawing(DiagramSelection s);
}
public static boolean isMouseButton1(MouseEvent e){
return ((e.getModifiers()&MouseEvent.BUTTON1_MASK) == MouseEvent.BUTTON1_MASK);
}
public static boolean isMouseButton2(MouseEvent e){
return ((e.getModifiers()&MouseEvent.BUTTON2_MASK) == MouseEvent.BUTTON2_MASK);
}
public static boolean isMouseButton3(MouseEvent e){
return ((e.getModifiers()&MouseEvent.BUTTON3_MASK) == MouseEvent.BUTTON3_MASK);
}
public static int getPageHeight() {
return pageHeight;
}
public static void setPageHeight(int pageHeight) {
if (pageHeight < minimumPageHeight){
pageHeight = minimumPageHeight;
}
DiagramComponent.pageHeight = pageHeight;
}
public static int getPageWidth() {
return pageWidth;
}
public static void setPageWidth(int pageWidth) {
if (pageWidth < minimumPageWidth){
pageWidth = minimumPageWidth;
}
DiagramComponent.pageWidth = pageWidth;
}
public static boolean isPrintViewEnabled() {
return isPrintViewEnabled;
}
public static void setPageModeEnabled(boolean isPageModeEnabled) {
DiagramComponent.isPrintViewEnabled = isPageModeEnabled;
}
}