Package simtools.diagram

Source Code of simtools.diagram.DiagramComponent

/* ==============================================
* 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;
    }

   
}
TOP

Related Classes of simtools.diagram.DiagramComponent

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.