Package jsynoptic.builtin

Source Code of jsynoptic.builtin.Plot

/* ========================
* JSynoptic : a free Synoptic editor
* ========================
*
* Project Info:  http://jsynoptic.sourceforge.net/index.html
*
* This program is free software; you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Foundation;
* either version 2.1 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* (C) Copyright 2001-2003, by :
*     Corporate:
*         Astrium SAS
*         EADS CRC
*     Individual:
*         Nicolas Brodu
*
* $Id: Plot.java,v 1.64 2009/01/08 15:21:52 ogor Exp $
*
* Changes
* -------
* 25-Sep-2003 : Initial public release (NB);
*
*/

package jsynoptic.builtin;

import java.awt.Color;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.MouseEvent;
import java.io.Serializable;
import java.text.ChoiceFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.ResourceBundle;
import java.util.Vector;

import javax.swing.JDialog;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.CompoundEdit;

import jsynoptic.base.ContextualActionProvider;
import jsynoptic.base.DataSourceConsumer;
import jsynoptic.base.Linkable;
import jsynoptic.base.SelectionContextualActionProvider;

import jsynoptic.builtin.ui.CurvePropertiesPanel;
import jsynoptic.builtin.ui.LimitPropertiesPanel;
import jsynoptic.builtin.ui.PlotPropertiesDialogBox;
import jsynoptic.builtin.ui.PlotPropertiesPanel;
import jsynoptic.builtin.ui.CurvePropertiesPanel.CurveParameters;
import jsynoptic.ui.JSynoptic;
import jsynoptic.ui.LongAction;
import simtools.data.DataException;
import simtools.data.DataInfo;
import simtools.data.DataSource;
import simtools.data.DataSourceCollection;
import simtools.data.DataSourcePool;
import simtools.data.UnsupportedOperation;
import simtools.diagram.DiagramSelection;
import simtools.diagram.DiagramComponent.ContextualDrawing;
import simtools.diagram.DiagramComponent.ContextualDrawingProvider;
import simtools.shapes.AbstractShape;
import simtools.shapes.AxisLabelFormatter;
import simtools.shapes.AxisShape;
import simtools.shapes.CurveShape;
import simtools.shapes.LimitShape;
import simtools.shapes.PlotShape;
import simtools.shapes.StrokeParameters;
import simtools.shapes.AxisShape.AxePropertiesNames;
import simtools.shapes.CurveShape.CurvePoint;
import simtools.shapes.CurveShape.CurveShapePropertiesNames;
import simtools.shapes.LimitShape.LimitShapePropertiesNames;
import simtools.shapes.undo.PropertyChangeEdit;
import simtools.ui.CustomizedLocale;
import simtools.ui.JPropertiesPanel;
import simtools.ui.MenuResourceBundle;
import simtools.ui.PlotInformationDialog;


/**
* A Plot shape to display curves using up to 2 pairs of axes
* X and Y primary and secondary axes are independent
*
* @author Nicolas Brodu
* @author Claude CAZENAVE
*/
public class Plot extends PlotShape
implements
ContextualActionProvider,
SelectionContextualActionProvider,
Linkable,
DataSourceConsumer,
ContextualDrawingProvider
{

    static final long serialVersionUID = 4941787241403275071L;

    /**
     * Prefered number of rows 
     * if the value is < 2 then the user don't want
     * to fix a prefered number of rows
     */
    static public int preferredXGraduation = 0;

    /**
     * Prefered number of columns 
     * if the value is < 0 then the user don't want
     * to fix a prefered number of colmuns
     */
    static public int preferredYGraduation = 0;


    static public boolean DEFAULT_DISPLAY_DATA_SOURCE_ID = false;


    // Linkable shape
    protected String link;

    // Contextual drawings : zoom and translate drawings
    protected transient ContextualDrawing zoomDrawing, translateDrawing;

    public String getLink() {
        return link;
    }

    public void setLink(String link) {
        this.link = link;
    }
   
   
    /* (non-Javadoc)
     * @see simtools.shapes.AbstractShape#getShapeName()
     */
    public String getShapeName(){
        return Builtin.resources.getString("Plot");
    }
   

    /** Resources */
    public static MenuResourceBundle resources;
    static {
        try {
            resources = (MenuResourceBundle) ResourceBundle.getBundle(
                    "jsynoptic.builtin.resources.PlotResources",
                    CustomizedLocale.get()
            );
        } catch (Exception e) {
            System.err.println("Can't load Plot resources");
            System.exit(0);
        }
    }

    protected static Color[] defaultPalette = {
        Color.blue,
        Color.red,
        Color.green,
        Color.magenta,
        Color.cyan,
        Color.orange,
        Color.pink,
        Color.black,
        Color.yellow
    };
    protected int paletteIndex;

    protected transient DataSource primaryX;
    protected transient DataSource secondaryX;
    protected Vector primaryCurves;   // CurveShapes using primary Y axis
    protected Vector secondaryCurves; // CurveShapes using secondary Y axis

    // Index management
    protected boolean isPrimaryBounded;
    protected long primaryStartIndex, primaryEndIndex;
    protected boolean isSecondaryBounded;
    protected long secondaryStartIndex, secondaryEndIndex;

    protected transient int nbXGraduation = preferredXGraduation;
    protected transient int nbYGraduation = preferredYGraduation;

    /**@deprecated Do not use these attributes anymore, use axesRangeArray[PX].min, axesRangeArray[PX].max, axesRangeArray[PX].step instead.*/
    protected double pxmin, pxmax, pxstep;
    /**@deprecated Do not use this attribute anymore, use axesRangeArray[PX].label instead*/
    protected String pxlabel;
    /**@deprecated Do not use this attribute anymore, use axesRangeArray[PX].auto instead*/
    protected boolean autopx=true;
    /**@deprecated Do not use these attributes anymore, use axesRangeArray[PY].min, axesRangeArray[PY].max, axesRangeArray[PY].step instead.*/
    protected double pymin, pymax, pystep;
    /**@deprecated Do not use this attribute anymore, use axesRangeArray[PY].label instead*/
    protected String pylabel;
    /**@deprecated Do not use this attribute anymore, use axesRangeArray[PY].auto instead*/
    protected boolean autopy=true;
    /**@deprecated Do not use these attributes anymore, use axesRangeArray[SX].min, axesRangeArray[SX].max, axesRangeArray[SX].step instead.*/
    protected double sxmin, sxmax, sxstep;
    /**@deprecated Do not use this attribute anymore, use axesRangeArray[SX].label instead*/
    protected String sxlabel;
    /**@deprecated Do not use this attribute anymore, use axesRangeArray[SX].auto instead*/
    protected boolean autosx=true;
    /**@deprecated Do not use these attributes anymore, use axesRangeArray[SY].min, axesRangeArray[SY].max, axesRangeArray[SY].step instead.*/
    protected double symin, symax, systep;
    /**@deprecated Do not use this attribute anymore, use axesRangeArray[SY].label instead*/
    protected String sylabel;
    /**@deprecated Do not use this attribute anymore, use axesRangeArray[PX].auto instead*/
    protected boolean autosy=true;
    /**@deprecated Do not use these attributes anymore, use axesRangeArray[PX].validity, axesRangeArray[PY].validity, axesRangeArray[SX].validity, axesRangeArray[SY].validity instead.*/
    protected boolean pxvaluesOK, pyvaluesOK, sxvaluesOK, syvaluesOK;

    /**Constant indexes to find PX, PY, SX and SY limits values and validity.*/
    protected static final int PX=0, PY=1, SX=2, SY=3;
    /** A constant to know the number of axes.*/
    protected static final int AXE_NUMBER = 4;
    /** axes names for property managment */
    protected static final String[] AXIS_ID = {"primX","primY","secX","secY"};
    /**The array of AxeRange.*/
    protected AxeRange[] axesLimitsArray = new AxeRange[AXE_NUMBER];

    public Plot(int ox, int oy, int width, int height) {
        super(ox, oy, width, height);
        primaryX = null;
        secondaryX = null;
        primaryCurves = null;
        secondaryCurves = null;
        //initialiez axes ranges.
        axesLimitsArray = new AxeRange[AXE_NUMBER];
        for (int i = 0; i < axesLimitsArray.length; i++) {
            axesLimitsArray[i] = new AxeRange();
            //Axes limits are not valid (because not initialized yet).
            axesLimitsArray[i].validity = false;
        }
        paletteIndex = 0;
    }

    protected AbstractShape cloneShape() {
        Plot p = (Plot)super.cloneShape();
        if (primaryCurves!=null){
            p.primaryCurves=new Vector();
            for(int i=0;i<primaryCurves.size();i++){
                int rank=_curves.indexOf(getCurve((CurveShape)primaryCurves.get(i)));
                CurveShape cs=(CurveShape)((Curve)p._curves.get(rank)).shape;
                p.primaryCurves.add(cs);
            }
        }
        if (secondaryCurves!=null){
            p.secondaryCurves=new Vector();
            for(int i=0;i<secondaryCurves.size();i++){
                int rank=_curves.indexOf(getCurve((CurveShape)secondaryCurves.get(i)));
                CurveShape cs=(CurveShape)((Curve)p._curves.get(rank)).shape;
                p.secondaryCurves.add(cs);
            }
        }   
        p.axesLimitsArray = new AxeRange[AXE_NUMBER];
        for (int i = 0; i < p.axesLimitsArray.length; i++) {
            p.axesLimitsArray[i] = new AxeRange(axesLimitsArray[i].min,
                    axesLimitsArray[i].max,
                    axesLimitsArray[i].step,
                    axesLimitsArray[i].validity,
                    axesLimitsArray[i].auto,
                    axesLimitsArray[i].label,
                    axesLimitsArray[i].floatingAxe,
                    axesLimitsArray[i].floatingAxeRange);
        }
        return p;
    }

    public boolean setPrimaryX(DataSource ds, boolean background) {
        return setX(ds, true, background);
    }

    public DataSource getPrimaryX() {
        return primaryX;
    }

    public boolean addPrimaryY(DataSource ds, boolean background) {
        return addY(ds,true,true, background);
    }

    public boolean setSecondaryX(DataSource ds, boolean background) {
        return setX(ds, false, background);
    }
    public DataSource getSecondaryX() {
        return secondaryX;
    }
    public boolean addSecondaryY(DataSource ds, boolean background) {
        return addY(ds,false,true, background);
    }

    public boolean addSecondaryYForSecondaryX(DataSource ds, boolean background) {
        return addY(ds,false,false, background);
    }

    public boolean addPrimaryYForSecondaryX(DataSource ds, boolean background) {
        return addY(ds,true,false, background);
    }

//  Our own functions

    public void setX(DataSource ds, boolean primary) {
        if (ds!=null){
            Rectangle bounds = getBounds();
            try {
                long istart = ds.computeStartIndex();
                long iend = ds.computeLastIndex();
                if (primary) {
                    isPrimaryBounded = true;
                    primaryStartIndex = istart;
                    primaryEndIndex = iend;
                } else {
                    isSecondaryBounded = true;
                    secondaryStartIndex = istart;
                    secondaryEndIndex = iend;
                }
            }
            catch (UnsupportedOperation uo1) {
                if (primary) isPrimaryBounded = false;
                else isSecondaryBounded = false;
            }
            DataSource oldX;
            String label = DEFAULT_DISPLAY_DATA_SOURCE_ID? DataInfo.getAliasOrIdwithUnit(ds) : DataInfo.getAliasOrLabelwithUnit(ds);
            if (primary) {
                oldX = primaryX;
                primaryX = ds;
                axesLimitsArray[PX].label = label;
            }
            else {
                setSecondaryAxis(true, true);
                oldX = secondaryX;
                secondaryX = ds;
                axesLimitsArray[SX].label = label;
            }

            // update all curves using the old x source
            if (primaryCurves!=null) {
                for (Iterator it = primaryCurves.iterator(); it.hasNext(); ) {
                    CurveShape cs = (CurveShape)it.next();
                    try{
                        if (cs.getXSource()==oldX) {
                            cs.setData(ds, cs.getYSource());
                            if (isPrimaryBounded) cs.setSlice(primaryStartIndex, primaryEndIndex);
                        }
                    }catch (DataException e){}
                }
            }
            if (secondaryCurves!=null) {
                for (Iterator it = secondaryCurves.iterator(); it.hasNext(); ) {
                    CurveShape cs = (CurveShape)it.next();
                    try{
                        if (cs.getXSource()==oldX) {
                            cs.setData(ds, cs.getYSource());
                            if (isSecondaryBounded) cs.setSlice(secondaryStartIndex, secondaryEndIndex);
                        }
                    }catch (DataException e){}
                }
            }
            repaintDiagram(bounds);
        }
    }


    // primary/secondary => no code dup
    protected boolean setX(DataSource ds, boolean primary, boolean background) {
        if (ds==null) return false;

        if (background){
            new LongAction(LongAction.LONG_ACTION_SHAPE | LongAction.LONG_ACTION_SOURCE,
                    new Object[]{ds, new Boolean(primary), new Boolean(background)},
                    new Object[]{ds, this} ){

                protected void doAction() {

                    Object[] obar = (Object[])param;
                    DataSource ds = (DataSource)obar[0];
                    boolean primary = ((Boolean)obar[1]).booleanValue();

                    setX(ds, primary);
                }
            }.start(background);

        }else{
            setX(ds, primary);
        }
        return !background;
    }

    protected LimitShape doAddLimitAction(boolean isVertical, boolean useSecondary, double position, String name){
        Rectangle bounds = getBounds();

        LimitShape cs = new LimitShape(isVertical, position);
        insertLimit(cs,useSecondary);
        setLimitLabel(cs, name);
        setLimitColor(cs, defaultPalette[paletteIndex++]);
        if (paletteIndex >= defaultPalette.length) paletteIndex = 0;
        repaintDiagram(bounds);
        return cs;
    }
   
    protected CurveShape createCurveShape(DataSource xds, DataSource yds){
        return new CurveShape(xds, yds);
    }

    protected CurveShape doAddYAction(Object[] obar){
        DataSource ds = (DataSource)obar[0];
        boolean primary = ((Boolean)obar[1]).booleanValue();
        boolean usePrimaryX = ((Boolean)obar[2]).booleanValue();

        Rectangle bounds = getBounds();
        CurveShape cs;
        if (usePrimaryX) {
            cs = createCurveShape(primaryX, ds);
            if (isPrimaryBounded) cs.setSlice(primaryStartIndex, primaryEndIndex);
        } else {
            cs = createCurveShape(secondaryX, ds);
            if (isSecondaryBounded) cs.setSlice(secondaryStartIndex, secondaryEndIndex);
        }
        String label = DEFAULT_DISPLAY_DATA_SOURCE_ID? DataInfo.getAliasOrIdwithUnit(ds) : DataInfo.getAliasOrLabelwithUnit(ds);
        if (primary) {
            if (primaryCurves==null) primaryCurves = new Vector();
            primaryCurves.add(cs);
            insertCurve(cs, !usePrimaryX, false);
        }
        else {
            setSecondaryAxis(false, true);
            if (secondaryCurves==null) secondaryCurves = new Vector();
            secondaryCurves.add(cs);
            insertCurve(cs, !usePrimaryX, true);
        }
        if (_curves.size()>0) setLegendVisible(true);
        setCurveLabel(cs, label);
        setCurveColor(cs, defaultPalette[paletteIndex++]);
        if (paletteIndex >= defaultPalette.length) paletteIndex = 0;
        repaintDiagram(bounds);
        return cs;
    }

    protected boolean addY(DataSource ds, boolean primary, boolean usePrimaryX, boolean background) {
        if (ds==null) return false;

        new LongAction(LongAction.LONG_ACTION_SHAPE | LongAction.LONG_ACTION_SOURCE,
                new Object[]{ds, new Boolean(primary), new Boolean(usePrimaryX)},
                new Object[]{ds, this} ) {

            protected void doAction() {

                doAddYAction((Object[])param);
            }
        }.start(background);
        return !background;
    }

    /**
     * Computes the "best" step to go from min to max
     * Average number of graduations is set to 10
     * Algo from simgo/util/geom/WaveAxis.java
     */
    public static double computeStep(double min, double max,int averageNbGrad) {

        // To understand the following, note that :
        // Let x = ampli/(_averageNbGrad-1) .
        // Let y = x/10^E(log10(x/0.75)) .
        // Then: 0.75 <= y < 7.5
        // Depending on the value of y, the graduations will be
        // every 1, every 2 or every 5 ( times 10^n ).
        double ampli = Math.abs(max - min);

        if(averageNbGrad<=1){
            averageNbGrad=10;
        }
        double x = ampli/(averageNbGrad-1);
        double ln10 = Math.log(10);
        double n = Math.floor(Math.log(x / 0.75)/ln10);
        double zeros = Math.exp(n*ln10)// = 10^n
        double y = x/zeros;
        int roundVal = 2;
        if(y<1.5) roundVal = 1;
        else if(y>=3.5) roundVal = 5;
        double ret=roundVal * zeros;
        if(ret<1e-6){
            return ret;
        }
        else{
            return Math.rint(ret*1e6)/1e6;
        }
    }

    public void repaintDiagram(Rectangle oldBounds) {

        //Compute for PX
        computeHorizontalBoundAndStep(axesLimitsArray[PX], primaryX, isPrimaryBounded);
        if (axesLimitsArray[PX].validity){
            setX(axesLimitsArray[PX].min, axesLimitsArray[PX].max, axesLimitsArray[PX].step);
        }
        setLabel(axesLimitsArray[PX].label,true,false);
      
        //Compute for PY
        computeVerticalBoundAndStep(axesLimitsArray[PY], primaryCurves);
        if (axesLimitsArray[PY].validity){
            setY(axesLimitsArray[PY].min, axesLimitsArray[PY].max, axesLimitsArray[PY].step);
        }
        setLabel(axesLimitsArray[PY].label,false,false);
    
        //Compute for SX
        computeHorizontalBoundAndStep(axesLimitsArray[SX], secondaryX, isSecondaryBounded);
        if (axesLimitsArray[SX].validity){
            setSecondaryX(axesLimitsArray[SX].min, axesLimitsArray[SX].max, axesLimitsArray[SX].step);
        }
        setLabel(axesLimitsArray[SX].label,true,true);
   
        //Compute for SY
        computeVerticalBoundAndStep(axesLimitsArray[SY], secondaryCurves);
        if (axesLimitsArray[SY].validity){
            setSecondaryY(axesLimitsArray[SY].min, axesLimitsArray[SY].max, axesLimitsArray[SY].step);
        }
        setLabel(axesLimitsArray[SY].label,false,true);

        //Store bounds.
        if (oldBounds!=null){
            oldBounds.add(getBounds());
        }
        notifyChange(oldBounds);
    }


    /* (non-Javadoc)
     * @see simtools.shapes.PlotShape#removeCurve(simtools.shapes.CurveShape)
     */
    public void removeCurve(CurveShape curveShape){
        super.removeCurve(curveShape);

        if (primaryCurves!=null && primaryCurves.contains(curveShape)){
            primaryCurves.remove(curveShape);

        }else if (secondaryCurves!=null && secondaryCurves.contains(curveShape)){
            secondaryCurves.remove(curveShape);
       

        // Remove secondary Y axis if no more curves are displayed on it.
        boolean displaySecondaryYaxis=false;
        for(int i=0;i<_curves.size();i++){
            displaySecondaryYaxis|=((Curve)_curves.get(i)).secondaryYaxis;
        }
        if (!displaySecondaryYaxis){
            axesLimitsArray[SY].validity=false;
            _asy2=null;
        }
    }

    protected  boolean canUseCurveSourcesMinMax(AxeRange xAxerange, CurveShape curveShape){
        boolean res;
        try {
            res = (xAxerange.min <= curveShape.getXSource().getDoubleMin()) && (xAxerange.max >= curveShape.getXSource().getDoubleMax());
        } catch (DataException e) {
            res = false;
        }
        return res;
    }
   
    /**
     * Auto-scale a vertical axe regarding values on
     * @param axeRange - The range related to the axe to be scaled.
     * @param curves - Curves that belong to that axe
     */
    protected void computeVerticalBoundAndStep(AxeRange yAxeRange, Vector curves) {
        if ((!yAxeRange.validity) && (curves!=null) && (curves.size()>0)) {

            // Choice format is available only if all Y data sources can provide the same Choice format
            ChoiceFormat format;
            try {
                format = ((CurveShape)curves.get(0)).getYSource().getChoiceFormat();
            } catch (DataException e1) {
                format = null;
            }
            yAxeRange.min= Double.POSITIVE_INFINITY;
            yAxeRange.max= Double.NEGATIVE_INFINITY;
           
            for(int i=0; i < curves.size(); i++) {
                CurveShape curveShape = (CurveShape)curves.get(i);
                AxeRange xAxerange = getCurve(curveShape).secondaryXaxis? axesLimitsArray[SX] : axesLimitsArray[PX];
               
                boolean useYdataMinandMax = canUseCurveSourcesMinMax(xAxerange, curveShape);
                try {

                    // When curve X range is not included into current X axis range, we have to compute the curve Y range related to X axis range
                    if (!useYdataMinandMax) {
                       
                        curveShape.computeYLocalRange(xAxerange.min, xAxerange.max);
                       
                        double curveYmin = curveShape.getLocalRangeMinPoint().y;
                        double curveYmax = curveShape.getLocalRangeMaxPoint().y;

                        if (curveYmin < yAxeRange.min) {
                            yAxeRange.min = curveYmin;
                        }
                        if (curveYmax > yAxeRange.max) {
                            yAxeRange.max = curveYmax;
                        }
                    }

                } catch (DataException e) {
                    useYdataMinandMax = true;
                }

                // When curve X range is included into current X axis range, we can direct get curve Y mon and max from data source
                try {
                    if (useYdataMinandMax) {
                        DataSource currentYSource = curveShape.getYSource();
                        double candidateMin = currentYSource.getDoubleMin();
                        double candidateMax = currentYSource.getDoubleMax();

                        if (candidateMin < yAxeRange.min){
                            yAxeRange.min = candidateMin;
                        }
                        if (candidateMax > yAxeRange.max){
                            yAxeRange.max = candidateMax;
                        }  
                    }

                    // Update format
                    if (format!=null && !format.equals(curveShape.getYSource().getChoiceFormat())){
                        format = null;
                    }
                   
                } catch (DataException e) {
                    useYdataMinandMax = true;
                }
            }


            // Update format
            if(format!=null) {
                // check if it is compatible
                double[] limits=format.getLimits();
                double lmin=limits[0];
                double lmax=limits[limits.length-1];
                double l=(lmax-lmin)/2;

                if(lmin> yAxeRange.min && (lmin-yAxeRange.min)>l){
                    format=null;

                } else if(lmax<yAxeRange.max && (yAxeRange.max-lmax)>l){
                    format=null;

                } else {
                    yAxeRange.step=1;
                }
            }
            if (yAxeRange.equals(axesLimitsArray[PY])) {
                pyformat = format;
            } else {
                syformat = format;
            }

            // Floating axe
            if (yAxeRange.floatingAxe) {
                if (yAxeRange.max - yAxeRange.min > yAxeRange.floatingAxeRange){
                    yAxeRange.min = yAxeRange.max - yAxeRange.floatingAxeRange;
                else {
                    yAxeRange.max =  yAxeRange.min +  yAxeRange.floatingAxeRange;
                }
            }

            // Compute step and validate axe range
            yAxeRange.computeStep(nbYGraduation);

        }
    }


    /**
     * Method <b>computeHorizontalBoundAndStep</b>
     * <br><b>Summary:</b><br>
     * This method compute the bounds and the step of an horizontal axe.
     * Parameters:
     * @param axeRange              The axe to set bounds and step.                
     * @param x                     The dataSource for the axe.
     * @param isBounded             Trus if the axe is bounded.
     *
     */
    private void computeHorizontalBoundAndStep(AxeRange axeRange, DataSource x, boolean isBounded) {
        if ((!axeRange.validity) && (x!=null)) {
            try {
                if (isBounded) {
                    int order = x.sortedOrder();
                    if (order==1) {
                        axeRange.min = x.getDoubleValue(primaryStartIndex);
                        axeRange.max = x.getDoubleValue(primaryEndIndex);
                    }
                    else if (order==-1) {
                        axeRange.min = x.getDoubleValue(primaryEndIndex);
                        axeRange.max = x.getDoubleValue(primaryStartIndex);
                    }
                    else {
                        axeRange.min = x.getDoubleMin();
                        axeRange.max = x.getDoubleMax();
                    }
                }
                else {
                    axeRange.min = x.getDoubleMin();
                    axeRange.max = x.getDoubleMax();
                }
                if (axeRange.floatingAxe){
                    if (axeRange.max - axeRange.min > axeRange.floatingAxeRange){
                        axeRange.min = axeRange.max - axeRange.floatingAxeRange;
                    } else {
                        axeRange.max =  axeRange.min +  axeRange.floatingAxeRange;
                    }
                }
                //Compute the step of axeRange and validate axis
                axeRange.computeStep(nbXGraduation);
            }
            catch (UnsupportedOperation uo) {} catch (DataException e) {
            }
        }
    }


    /* (non-Javadoc)
     * @see jsynoptic.base.SelectionContextualActionProvider#getCollectiveActions()
     */
    public LinkedHashMap getCollectiveActions(DiagramSelection sel, double x, double y, Object o, int context) {
        LinkedHashMap res=new LinkedHashMap();

        if (((primaryCurves!=null) && (primaryCurves.size()!=0))
                || ((secondaryCurves!=null) && (secondaryCurves.size()!=0))) {       // do both primary and secondary
            res.put(resources.getStringValue("autoscale"), null);
            res.put(resources.getStringValue("autoscaleOnY"), null);

            if (sel.getShapeCount() > 1) {
                res.put(resources.getStringValue("adjustOnX") + ";" +  resources.getStringValue("union"), null);
                res.put(resources.getStringValue("adjustOnX") + ";" +  resources.getStringValue("intersection"), null);
                res.put(resources.getStringValue("adjustOnX") + ";" +  resources.getStringValue("firstPlotSelected"), null);
            }
        }

        if (o instanceof DataSource) {
            res.put(resources.getStringValue("setX"), null);
        }

        return res;
    }

    public String[] getActions(double x, double y, Object o, int context) {
       
        if (context==MOUSE_OVER_CONTEXT) {
            return new String[] {"mouseOver"};
        }

        if (context==MOUSE_OUT_CONTEXT) {
            return new String[] {"mouseOut"};
        }
       
        if (context==MOUSE_PRESSED_CONTEXT) {
            MouseEvent e = (MouseEvent)o;
            // Monitor left click
            if ((e.getModifiers() & MouseEvent.BUTTON1_MASK) != MouseEvent.BUTTON1_MASK)
                return null;
            return null;
        }

        Vector v = new Vector();
       
        if (o instanceof DataSource) {
            v.add(resources.getStringValue("setX"));
            if (primaryX != null) {
                v.add(resources.getStringValue("addY"));
                v.add(resources.getStringValue("addSecY"));
                v.add(resources.getStringValue("setSecX"));
                if (secondaryX != null) {
                    v.add(resources.getStringValue("addYSecX"));
                    v.add(resources.getStringValue("addSecYSecX"));
                }
            }
       
        } else if (o instanceof DataSourceCollection) {
            v.add(resources.getStringValue("setXY1Yn"));
            if (primaryX != null) {
                v.add(resources.getStringValue("addY1Yn"));
                v.add(resources.getStringValue("addSecY1Yn"));
                v.add(resources.getStringValue("setSecXY1Yn"));
                if (secondaryX != null)
                    v.add(resources.getStringValue("addSecY1YnForSecX"));
            }
       
        } else {
            JSynoptic.setStatus(resources.getStringValue("noSource"));
        }
     
        if (context==EDITOR_CONTEXT) {
            v.add(0,resources.getStringValue("properties"));
            if (_curves.size()!=0){
                v.add(0,resources.getStringValue("information"));

                if (_magnetizedCurve != null) {
                    v.add(resources.getStringValue("selectPoint"));
                    v.add(resources.getStringValue("disableMagneticCoordinates") + _magnetizedCurve.getLabel());
                }

                for(int i=0;i<_curves.size();i++){
                    // magnetize a curve action
                    if (!((_magnetizedCurve!=null) && (((Curve)_curves.get(i)).equals(_magnetizedCurve)) ))
                        v.add(resources.getStringValue("enableMagneticCoordinates")+ ";" + ((Curve)_curves.get(i)).getLabel() )
                }

                for(int i=0;i<_curves.size();i++){
                    //  Delete curve action
                    v.add(resources.getStringValue("deleteCurve")+ ";" + ((Curve)_curves.get(i)).getLabel() );
                }
            }
        }
        return (String[])v.toArray(new String[v.size()]);
    }

    public boolean canDoAction(double x, double y, Object o, String action, int context) {
        return true;
    }

    public void displayCursorLocation(double x, double y){
        String msg = "";
        if (_magnetizedCurve!=null){
            CurvePoint currentPoint = _magnetizedCurve.shape.getCurrentPoint();
            msg += _magnetizedCurve.getLabel() + ": ";
            msg += " x = " + currentPoint.x;
            msg += ",  y = " + currentPoint.y;
            msg += "  ---  Slope : ";
            msg += " x = " + currentPoint.slop_x;
            msg += ",  y = " + currentPoint.slop_y;
           
            CurvePoint refPoint = _magnetizedCurve.shape.getReferencePoint();
            if (refPoint != null){
                msg += "  ---  Delta from tagged point : ";
                msg += " x = " + (currentPoint.x - refPoint.x);
                msg += ",  y = " + (currentPoint.y - refPoint.y);
            }
           
        } else {
            if (_asx1!=null) {
                if (axesLimitsArray[PX].label!=null) msg += axesLimitsArray[PX].label;
                else msg+= "x";
                msg += "=" + AxisLabelFormatter.labelFormat(_asx1.getMin() + (x - _ox) / _asx1.getScale());
            }
            if (_asy1!=null) {
                if (!msg.equals("")) msg+="   ";
                if (axesLimitsArray[PY].label!=null) msg += axesLimitsArray[PY].label;
                else msg+= "y";
                msg += "=" + AxisLabelFormatter.labelFormat(_asy1.getMin() + (_oy - y) / _asy1.getScale());
            }
            if (_asx2!=null) {
                if (!msg.equals("")) msg+="   ";
                if (axesLimitsArray[SX].label!=null) msg += axesLimitsArray[SX].label;
                else msg+= "x'";
                msg += "=" + AxisLabelFormatter.labelFormat(_asx2.getMin() + (x - _ox) / _asx2.getScale());
            }
            if (_asy2!=null) {
                if (!msg.equals("")) msg+="   ";
                if (axesLimitsArray[SY].label!=null) msg += axesLimitsArray[SY].label;
                else msg+= "y'";
                msg += "=" + AxisLabelFormatter.labelFormat(_asy2.getMin() + (_oy - y) / _asy2.getScale());
            }
        }
        if (!msg.equals("")) JSynoptic.setStatus(msg);
    }


    /* (non-Javadoc)
     * @see jsynoptic.base.SelectionContextualActionProvider#doAction(simtools.diagram.DiagramSelection, double, double, java.lang.Object, java.lang.String, javax.swing.undo.CompoundEdit)
     */
    public boolean doCollectiveAction(DiagramSelection sel, double x, double y, Object o, String action, CompoundEdit undoableEdit){
        boolean res = false;
        boolean canDoAction = true;
        Vector v = sel.getSelectedElements();
        if (!v.isEmpty()) {

            if (action.startsWith(resources.getStringValue("adjustOnX"))) { // compute the collective X primary and secondary ranges

                double pxmin=0, pxmax=0, sxmin=0, sxmax=0;

                // Get current selected plot  X range  
                Plot firstPlot = null;
                for (int i = 0; i < sel.getShapeCount() && firstPlot == null; i++) {
                    Shape s = sel.getShape(i);
                    if ((s.contains(x,y)) && (s instanceof Plot) ){
                        firstPlot = (Plot)s;
                    }
                }

                if (firstPlot == null) {
                    canDoAction = false;
                } else {
                    pxmin = firstPlot._asx1.getMin();
                    pxmax = firstPlot._asx1.getMax();

                    boolean adjustSecondaryXRange = (_asx2 != null);

                    if (adjustSecondaryXRange){
                        sxmin = firstPlot._asx2.getMin();
                        sxmax = firstPlot._asx2.getMax();
                    }

                    // loop on other selected plots
                    if (! action.equals(resources.getStringValue("adjustOnX") + ";" +  resources.getStringValue("firstPlotSelected"))){
                        boolean isUnion =  action.equals(resources.getStringValue("adjustOnX") + ";" +  resources.getStringValue("union"));

                        for(int i=0; i < v.size(); i++) {
                            Plot plot  = (Plot)v.get(i);

                            if (isUnion){
                                pxmin = Math.min(pxmin, plot._asx1.getMin());
                                pxmax = Math.max(pxmax, plot._asx1.getMax());
                            } else {
                                pxmin = Math.max(pxmin, plot._asx1.getMin());
                                pxmax = Math.min(pxmax, plot._asx1.getMax());
                            }

                            adjustSecondaryXRange &= (plot._asx2 != null);
                            if (adjustSecondaryXRange){
                                if (isUnion){
                                    sxmin = Math.min(sxmin, plot._asx2.getMin());
                                    sxmax = Math.max(sxmax, plot._asx2.getMax());
                                } else {
                                    sxmin = Math.max(sxmin, plot._asx2.getMin());
                                    sxmax = Math.min(sxmax, plot._asx2.getMax());
                                }
                            }
                        }
                    }

                    if ( (pxmin >= pxmax) ||  ( adjustSecondaryXRange && (sxmin >= sxmax))){
                        canDoAction = false;    // cannot set such an X range
                    } else {
                        o = new Double[4];
                        ((Double[])o)[0= new Double(pxmin);
                        ((Double[])o)[1] = new Double(pxmax);
                        ((Double[])o)[2= adjustSecondaryXRange? new Double(sxmin) : null;
                        ((Double[])o)[3] = adjustSecondaryXRange? new Double(sxmax) : null;
                    }
                }
              
            }

           
            // Do action on selection
            if (canDoAction){
                res = true;
                for(int i=0; i < v.size(); i++) {
                    Object element = v.get(i);
                    if (element instanceof Plot){
                        res &= ((Plot)element).doAction(x, y,o, action, undoableEdit);
                    }
                }
            }
        }
        return res;   
    }

    public boolean doAction(double x, double y, Object o, String action, CompoundEdit undoableEdit) {
        if (action==null) return false;

        if (action.equals("mouseOver")) {
            if (_magnetizedCurve!=null){
                AxisShape axe_x = _magnetizedCurve.secondaryXaxis? _asx2 : _asx1;
                double pos_x = axe_x.getMin() + (x - _ox) / axe_x.getScale();
                try{
                    _magnetizedCurve.shape.setCurrentPoint(pos_x);

                    //  Display red circle on magnetized point coordinates : 
                    notifyChange(getBounds());
                }catch (DataException e){
                    JSynoptic.setStatus("This curve cannot be magnetized");
                }
            }
            displayCursorLocation(x,y);
            return true;
        }
  

        if (action.equals("mouseOut")) {
            JSynoptic.setStatus("");
            return true;
        }
      
       
        if (action.equals( resources.getStringValue("selectPoint"))) {
            _magnetizedCurve.shape.setReferencePoint(_magnetizedCurve.shape.getCurrentPoint());
            repaintDiagram(getBounds());
            return true;
        }
        if (action.equals(resources.getStringValue("information"))) {

            PlotInformationDialog di = new PlotInformationDialog(this);
            JDialog jd = di.createDialog(JSynoptic.gui.getOwner());
            if (jd!=null) jd.show();
            return true;
        }
        for(int i=0;i<_curves.size();i++){

            // magnetize a curve action
            if (action.equals(resources.getStringValue("enableMagneticCoordinates") + ";" +((Curve)_curves.get(i)).getLabel())){
                _magnetizedCurve = (Curve)_curves.get(i);
                return true;
            }

            // Delete a curve action
            if (action.equals(resources.getStringValue("deleteCurve") + ";" +((Curve)_curves.get(i)).getLabel())){
                HashMap oldValues = getCurvesProperties(null);
                removeCurve(((Curve)_curves.get(i)).shape);

                repaintDiagram(getBounds());


                PlotCompoundEdit ce=new PlotCompoundEdit(oldValues);
                if(undoableEdit != null){
                    undoableEdit.addEdit(ce);
                }
                return true;
            }
        }

        if (( _magnetizedCurve!=null) && action.equals(resources.getStringValue("disableMagneticCoordinates") + _magnetizedCurve.getLabel())) {
            _magnetizedCurve.shape.setReferencePoint(null);
            _magnetizedCurve=null;
            repaintDiagram(getBounds());
            return true;
        }
       
        if (action.equals(resources.getStringValue("properties"))) {    
            new LongAction(LongAction.LONG_ACTION_SHAPE, null, this) {
                protected void doAction() {
                    JPropertiesPanel panel=createPanel();

                    /** Only one shape properties dialog box can be displayed at once
                     * When user opens a dialog box properties while another one is still displayed: this last dislaog box is closed
                     */
                    Point oldLocation=null;
                    if (currentDialogBox!=null){
                        oldLocation= currentDialogBox.getBounds().getLocation();
                        currentDialogBox.dispose();
                    }
                    ArrayList shapes = new ArrayList();
                    shapes.add(Plot.this);
                    PlotPropertiesDialogBox optionDialog = new PlotPropertiesDialogBox(JSynoptic.gui.getOwner(), panel, panel.getShapeName(), shapes);

                    if (oldLocation!=null)
                        optionDialog.setLocation(oldLocation);

                    currentDialogBox = optionDialog;
                    optionDialog.show();
                }
            }.start();
            return true;
        }

        if (action.startsWith(resources.getStringValue("adjustOnX"))) {
            if (o instanceof Double[]){
                HashMap oldValues=getAxesProperties(null, -1);

                Double[] xranges = ((Double[])o);

                axesLimitsArray[PX].min = xranges[0].doubleValue();
                axesLimitsArray[PX].max = xranges[1].doubleValue();
                axesLimitsArray[PX].step = computeStep(axesLimitsArray[PX].min, axesLimitsArray[PX].max, preferredXGraduation);

                if ( (xranges[2] != null) && (_asx2 != null)) {
                    axesLimitsArray[SX].min = xranges[2].doubleValue();
                    axesLimitsArray[SX].max = xranges[3].doubleValue();
                    axesLimitsArray[SX].step = computeStep(axesLimitsArray[SX].min, axesLimitsArray[SX].max, preferredXGraduation);
                }

                repaintDiagram(getBounds());


                PlotCompoundEdit ce=new PlotCompoundEdit(oldValues);
                if(undoableEdit != null){
                    undoableEdit.addEdit(ce);
                }
                return true;
            } else {
                return false;
            }


        }
        if (action.equals(resources.getStringValue("autoscale"))) {
            // get the old values for the 4 axes
            HashMap oldValues=getAxesProperties(null, -1);

            for(int i =0; i< AXE_NUMBER;i++){
                axesLimitsArray[i].validity = false;
            }
            repaintDiagram(getBounds());

            PlotCompoundEdit ce=new PlotCompoundEdit(oldValues);
            if(undoableEdit != null){
                undoableEdit.addEdit(ce);
            }
            return true;
        }
        if (action.equals(resources.getStringValue("autoscaleOnY"))) {
            // get the old values for the 4 axes
            HashMap oldValues=getAxesProperties(null, -1);

            // auto-scale on primary Y
            axesLimitsArray[PY].validity = false;

            // auto-scale on secondary Y
            axesLimitsArray[SY].validity = false;

            repaintDiagram(getBounds());

            PlotCompoundEdit ce=new PlotCompoundEdit(oldValues);
            if(undoableEdit != null){
                undoableEdit.addEdit(ce);
            }
            return true;
        }

        if (o instanceof DataSource) {
            boolean res = false;

            DataSource ds = (DataSource)o;
            if (action.equals(resources.getStringValue("setX"))) {

                res =  setPrimaryX(ds, true);

            }
            if (action.equals(resources.getStringValue("addY"))) {

                res =  addPrimaryY(ds, true);


            }
            if (action.equals(resources.getStringValue("addYSecX"))) {

                res =  addPrimaryYForSecondaryX(ds, true);

            }
            if (action.equals(resources.getStringValue("addSecY"))) {

                res =  addSecondaryY(ds, true);

            }
            if (action.equals(resources.getStringValue("setSecX"))) {

                res =  setSecondaryX(ds, true);

            }
            if (action.equals(resources.getStringValue("addSecYSecX"))) {

                res =  addSecondaryYForSecondaryX(ds, true);

            }

            return res;
        }

        if (o instanceof DataSourceCollection) {
            DataSourceCollection dsc = (DataSourceCollection)o;
            if (action.equals(resources.getStringValue("setXY1Yn"))) {
                if (dsc.size()<1) return false;
                new LongAction(0, new Object[] {dsc,action}) {
                    protected void doAction() {
                        DataSourceCollection dsc = (DataSourceCollection)((Object[])param)[0];
                        setPrimaryX((DataSource)dsc.get(0), false);
                        boolean hasValues = axesLimitsArray[PY].validity;
                        axesLimitsArray[PY].validity = true; // compute only once all curve are inserted
                        for (int i=1; i<dsc.size()-1; i++)
                            addPrimaryY((DataSource)dsc.get(i), false);
                        axesLimitsArray[PY].validity = hasValues;
                        if (dsc.size()>1) addPrimaryY((DataSource)dsc.get(dsc.size()-1), false);
                        Rectangle bounds = getBounds();
                        setTitle(DataInfo.getLabel(dsc));
                        repaintDiagram(bounds);
                    }
                }.start();
                return false;
            }

            if (action.equals(resources.getStringValue("addY1Yn"))) {
                if (dsc.size()<1) return false;
                new LongAction(0, new Object[] {dsc,action}) {
                    protected void doAction() {
                        DataSourceCollection dsc = (DataSourceCollection)((Object[])param)[0];
                        boolean hasValues = axesLimitsArray[PY].validity;
                        axesLimitsArray[PY].validity = true; // compute only once all curve are inserted
                        for (int i=0; i<dsc.size()-1; i++)
                            addPrimaryY((DataSource)dsc.get(i), false);
                        axesLimitsArray[PY].validity = hasValues;
                        if (dsc.size()>0) addPrimaryY((DataSource)dsc.get(dsc.size()-1), false);
                        Rectangle bounds = getBounds();
                        repaintDiagram(bounds);
                    }
                }.start();
                return false;
            }

            if (action.equals(resources.getStringValue("addSecY1Yn"))) {
                if (dsc.size()<1) return false;
                new LongAction(0, new Object[] {dsc,action}) {
                    protected void doAction() {
                        DataSourceCollection dsc = (DataSourceCollection)((Object[])param)[0];
                        boolean hasValues = axesLimitsArray[SY].validity;
                        boolean hasLabel = (axesLimitsArray[SY].label!=null);
                        axesLimitsArray[SY].validity = true; // compute only once all curve are inserted
                        for (int i=0; i<dsc.size()-1; i++)
                            addSecondaryY((DataSource)dsc.get(i), false);
                        axesLimitsArray[SY].validity = hasValues;
                        if (dsc.size()>0) addSecondaryY((DataSource)dsc.get(dsc.size()-1), false);
                        Rectangle bounds = getBounds();
                        if ((!hasLabel) && (dsc.size()>1)) {axesLimitsArray[SY].label = null; setLegendVisible(true);}
                        repaintDiagram(bounds);
                    }
                }.start();
                return false;
            }

            if (action.equals(resources.getStringValue("setSecXY1Yn"))) {
                if (dsc.size()<1) return false;
                new LongAction(0, new Object[] {dsc,action}) {
                    protected void doAction() {
                        DataSourceCollection dsc = (DataSourceCollection)((Object[])param)[0];
                        setSecondaryX((DataSource)dsc.get(0), false);
                        boolean hasValues = axesLimitsArray[SY].validity;
                        boolean hasLabel = (axesLimitsArray[SY].label!=null);
                        axesLimitsArray[SY].validity = true; // compute only once all curve are inserted
                        for (int i=1; i<dsc.size()-1; i++)
                            addSecondaryY((DataSource)dsc.get(i), false);
                        axesLimitsArray[SY].validity = hasValues;
                        if (dsc.size()>1) addSecondaryY((DataSource)dsc.get(dsc.size()-1), false);
                        Rectangle bounds = getBounds();
                        if ((!hasLabel) && (dsc.size()>2)) {axesLimitsArray[SY].label = null; setLegendVisible(true);}
                        repaintDiagram(bounds);
                    }
                }.start();
                return false;
            }

            if (action.equals(resources.getStringValue("addSecY1YnForSecX"))) {
                if (dsc.size()<1) return false;
                new LongAction(0, new Object[] {dsc,action}) {
                    protected void doAction() {
                        DataSourceCollection dsc = (DataSourceCollection)((Object[])param)[0];
                        boolean hasValues = axesLimitsArray[SY].validity;
                        boolean hasLabel = (axesLimitsArray[SY].label!=null);
                        axesLimitsArray[SY].validity = true; // compute only once all curve are inserted
                        for (int i=0; i<dsc.size()-1; i++)
                            addSecondaryYForSecondaryX((DataSource)dsc.get(i), false);
                        axesLimitsArray[SY].validity = hasValues;
                        if (dsc.size()>0) addSecondaryYForSecondaryX((DataSource)dsc.get(dsc.size()-1), false);
                        Rectangle bounds = getBounds();
                        if ((!hasLabel) && (dsc.size()>1)) {axesLimitsArray[SY].label = null; setLegendVisible(true);}
                        repaintDiagram(bounds);
                    }
                }.start();
                return false;
            }
        }

        return false;
    }

    /**
     * Changes the axes defintion according to a zoom defined with 2 coordinates
     * @param x1 The first zoom area x coordinate.
     * @param x2 The second zoom area x coordinate.
     * @param y1 The first zoom area y coordinate.
     * @param y2 The second zoom area y coordinate.
     * @param xOnly True zoom only the X axis
     */
    public void zoom(double x1, double x2, double y1, double y2, boolean xOnly){ 
        if (_asx1 != null) {
            computeHorizontalAxeRange(axesLimitsArray[PX], _asx1, logpx, x1, x2);
            axesLimitsArray[PX].validity = true;
        } else {
            axesLimitsArray[PX].validity = false;
        }
        if(!xOnly){
            if (_asy1 != null) {
                computeVerticalAxeRange(axesLimitsArray[PY], _asy1, logpy, y1, y2);
                axesLimitsArray[PY].validity = true;
            } else {
                axesLimitsArray[PY].validity = false;
            }
        }
        if (_asx2 != null) {
            computeHorizontalAxeRange(axesLimitsArray[SX], _asx2, logsx, x1, x2);
            axesLimitsArray[SX].validity = true;
        } else {
            axesLimitsArray[SX].validity = false;
        }
        if(!xOnly){
            if (_asy2 != null) {
                computeVerticalAxeRange(axesLimitsArray[SY], _asy2, logsy, y1, y2);
                axesLimitsArray[SY].validity = true;
            } else {
                axesLimitsArray[SY].validity = false;
            }
        }
    }

    /**
     * Make a zoom box around (x,y) position.
     * @param x
     * @param y
     * @param zoomWheelFactor
     * @param zoomIn, if true zoom in , otherwise zoom out
     */
    public void zoomWheel(double x, double y, double zoomWheelFactor, boolean zoomIn){ 
        if (_asx1 != null) {
            computeZoomWheelHorizontalAxeRange(axesLimitsArray[PX], _asx1, logpx, x, zoomIn,zoomWheelFactor);
            axesLimitsArray[PX].validity = true;
        } else {
            axesLimitsArray[PX].validity = false;
        }
        if (_asy1 != null) {
            computeZoomWheelVerticalAxeRange(axesLimitsArray[PY], _asy1, logpy, y, zoomIn,zoomWheelFactor);
            axesLimitsArray[PY].validity = true;
        } else {
            axesLimitsArray[PY].validity = false;
        }

        if (_asx2 != null) {
            computeZoomWheelHorizontalAxeRange(axesLimitsArray[SX], _asx2, logsx, x, zoomIn,zoomWheelFactor);
            axesLimitsArray[SX].validity = true;
        } else {
            axesLimitsArray[SX].validity = false;
        }

        if (_asy2 != null) {
            computeZoomWheelVerticalAxeRange(axesLimitsArray[SY], _asy2, logsy, y, zoomIn,zoomWheelFactor);
            axesLimitsArray[SY].validity = true;
        } else {
            axesLimitsArray[SY].validity = false;
        }
    }

    /**
     * Changes the axes defintion according to a translation defined with 2 points
     * @param x1 The first translation point x coordinate.
     * @param x2 The second translation point x coordinate.
     * @param y1 The first translation point y coordinate.
     * @param y2 The second translation point y coordinate.
     */
    public void translate(double x1, double x2, double y1, double y2){ 
        if (_asx1 != null) {
            computeTranslationAxeRange(axesLimitsArray[PX], _asx1, logpx, x1- x2);
            axesLimitsArray[PX].validity = true;
        } else {
            axesLimitsArray[PX].validity = false;
        }
        if (_asy1 != null) {
            computeTranslationAxeRange(axesLimitsArray[PY], _asy1, logpy, y2- y1);
            axesLimitsArray[PY].validity = true;
        } else {
            axesLimitsArray[PY].validity = false;
        }
        if (_asx2 != null) {
            computeTranslationAxeRange(axesLimitsArray[SX], _asx2, logsx, x1-x2);
            axesLimitsArray[SX].validity = true;
        } else {
            axesLimitsArray[SX].validity = false;
        }
        if (_asy2 != null) {
            computeTranslationAxeRange(axesLimitsArray[SY], _asy2, logsy, y2 - y1);
            axesLimitsArray[SY].validity = true;
        } else {
            axesLimitsArray[SY].validity = false;
        }
    }



    /**
     * Computes a zoom for an vertical axis (PY or SY).
     * @param axeRange The <b>AxeRange</b> axeRange to compute.
     * @param axe The <b>AxisShape</b> that use this AxeRange.
     * @param log True if the axe is under log format, false otherwise.
     * @param y1 The first zoom area y coordinate.
     * @param y2 The second zoom area y coordinate.
     */
    protected void computeVerticalAxeRange(AxeRange axeRange, AxisShape axe,
            boolean log, double y1, double y2) {
        double v1 = axe.getMin() + ((_oy - Math.min(y1, y2)) / axe.getScale());
        double v2 = axe.getMin() + ((_oy - Math.max(y1, y2)) / axe.getScale());
        double vmax = (v2>v1)? v2 : v1;
        double vmin = (v2>v1)? v1 : v2;
        if (log) {
            axeRange.max = Math.pow(10, Math.ceil(vmax));
            axeRange.min = Math.pow(10, Math.floor(vmin));
        } else {
            axeRange.min = vmin;
            axeRange.max = vmax;
            // Compute the step of axeRange, and re-evaluate bounds.
            axeRange.computeStep(nbYGraduation);
        }
    }

    /**
     * Computes a zoom for an horizontal axis (PX or SX). Parameters:
     * @param axeRange The <b>AxeRange</b> axeRange to compute.
     * @param axe The <b>AxisShape</b> that use this AxeRange.
     * @param log True if the axe is under log format, false otherwise.
     * @param x1 The first zoom area x coordinate.
     * @param x2 The second zoom area x coordinate.
     */
    protected void computeHorizontalAxeRange(AxeRange axeRange, AxisShape axe,
            boolean log, double x1, double x2) {
        double v1 = axe.getMin() + (Math.min(x1, x2) -_ox) / axe.getScale();
        double v2 = axe.getMin() + (Math.max(x1, x2) -_ox) / axe.getScale();
        double vmax = (v2>v1)? v2 : v1;
        double vmin = (v2>v1)? v1 : v2;
        if (log) {
            axeRange.min = Math.pow(10, Math.floor(vmin));
            axeRange.max = Math.pow(10, Math.ceil(vmax));
        } else {
            axeRange.min = vmin;
            axeRange.max = vmax;
            axeRange.computeStep(nbXGraduation);
        }
    }

    /**
     * Computes an axis coordinate from a mouse position in plot shape
     * @param axe The <b>AxisShape</b>
     * @param x1 A mouse position in plot hsape.
     * @return computed axis coordinate
     */
    public Double getAxeValue(int axeIndex, int x){
        AxisShape axe=null;
        switch (axeIndex){
        case Plot.PX: axe = _asx1; break;
        case Plot.SX: axe = _asx2; break;
        case Plot.PY: axe = _asy1; break;
        case Plot.SY: axe = _asy2; break;
        }
        Double ret=null;
        if (axe!=null){

            if (axe!=null){
                double anchor = axe.isVertical()? _oy : _ox;
                double value = axe.getMin() + (x - anchor) / axe.getScale();
                if (axe.isLog()) {
                    value = Math.pow(10, Math.floor(value));
                }
                ret = new Double(value);
            }
        }
        return ret;
    }

    /**
     * Computes a translation for an axis (PX or SX). Parameters:
     * @param axeRange The <b>AxeRange</b> axeRange to compute.
     * @param axe The <b>AxisShape</b> that use this AxeRange.
     * @param log True if the axe is under log format, false otherwise.
     * @param x1 The translation value
     */
    protected void computeTranslationAxeRange(AxeRange axeRange, AxisShape axe,
            boolean log, double x) {
        double v =x / axe.getScale();
        if (log) {
            double logmin = Math.log(axeRange.min) + v;
            double logmax = Math.log(axeRange.max) + v;
            axeRange.min = Math.pow(10, Math.floor(logmin));
            axeRange.max = Math.pow(10, Math.ceil(logmax));
        } else {
            axeRange.min += v;
            axeRange.max += v;
            axeRange.computeStep(nbXGraduation);
        }
    }



    protected void computeZoomWheelHorizontalAxeRange(AxeRange axeRange, AxisShape axe,
            boolean log, double x, boolean zoomIn, double zoomWheelFactor) {

        double v = axe.getMin() + (x - _ox ) / axe.getScale();
        double l = axeRange.max - axeRange.min;

        if (zoomIn)
            l/=zoomWheelFactor;
        else
            l*=zoomWheelFactor;

        if (log) {
            //TODO

            axeRange.min = Math.pow(10, Math.floor(v));
            axeRange.max = Math.pow(10, Math.ceil(v));
        } else {
            double ratio = (v - axeRange.min) / (axeRange.max - axeRange.min);
            axeRange.min = v - l*ratio;
            axeRange.max = v + l*(1-ratio);
            axeRange.computeStep(nbXGraduation);
        }
    }

    protected void computeZoomWheelVerticalAxeRange(AxeRange axeRange, AxisShape axe,
            boolean log, double y, boolean zoomIn, double zoomWheelFactor) {

        double v = axe.getMin() + (_oy- y) / axe.getScale();
        double l = axeRange.max - axeRange.min;

        if (zoomIn)
            l/=zoomWheelFactor;
        else
            l*=zoomWheelFactor;

        if (log) {
            //TODO
            axeRange.min = Math.pow(10, Math.floor(v));
            axeRange.max = Math.pow(10, Math.ceil(v));
        } else {
            double ratio = (v - axeRange.min) / (axeRange.max - axeRange.min);
            axeRange.min = v - l*ratio;
            axeRange.max = v + l*(1-ratio);
            axeRange.computeStep(nbYGraduation);
        }
    }


    /* (non-Javadoc)
     * @see jsynoptic.base.DataSourceConsumer#addDataSource(simtools.data.DataSource)
     */
    public boolean addDataSource(DataSource d) {
        if(primaryX==null){
            setPrimaryX(d, true);
        }
        else{
            addPrimaryY(d,true);
        }
        return true;
    }

    /* (non-Javadoc)
     * @see jsynoptic.base.DataSourceConsumer#canAddDataSource(simtools.data.DataSource)
     */
    public boolean canAddDataSource(DataSource d) {
        return true;
    }

//  Inner classes

    /* (non-Javadoc)
     * @see simtools.data.EndNotificationListener#notificationEnd(java.lang.Object)
     */
    // listener override to benefit from optimisation 
    public void notificationEnd(Object referer) {
        if (axesLimitsArray==null || axesLimitsArray[SY]==null) {
            // may occur during deserialization
            // because PlotShape upper class desrialization restores
            // data sources and the related listeners before the end
            // of the deserialization of Plot object
            // TODO find a more generic and more accurate solution
            return;
        }
        if(axesLimitsArray[PX].auto){
            axesLimitsArray[PX].validity = false;
        }
       
        if(axesLimitsArray[PY].auto){
            axesLimitsArray[PY].validity = false;
        }
       
        if(axesLimitsArray[SX].auto){
            axesLimitsArray[SX].validity = false;
        }
        if(axesLimitsArray[SY].auto){
            axesLimitsArray[SY].validity = false;
        }
       
        if (primaryX != null) {
            try {
                primaryStartIndex = primaryX.getStartIndex();
            } catch (UnsupportedOperation e) {
            }
            try {
                primaryEndIndex = primaryX.getLastIndex();
            } catch (UnsupportedOperation e) {
            }
        }
        if (secondaryX != null) {
            try {
                secondaryStartIndex = secondaryX.getStartIndex();
            } catch (UnsupportedOperation e) {
            }
            try {
                secondaryEndIndex = secondaryX.getLastIndex();
            } catch (UnsupportedOperation e) {
            }
        }
       
        isNotifying = false;
        super.notificationEnd(referer);
        isNotifying = true;
        repaintDiagram(getBounds());
    }


    // hack to avoid double parent notification, see notificationEnd
    protected transient boolean isNotifying = true;

    protected synchronized void notifyChange() {
        if (isNotifying) super.notifyChange();
    }

    public void shapeDataChanged(CurveShape csc, DataSource oldData, DataSource newData, boolean onX) {
        super.shapeDataChanged(csc, oldData, newData, onX);
        if (primaryCurves!=null && onX) {
            for (Iterator it = primaryCurves.iterator(); it.hasNext(); ) {
                CurveShape cs = (CurveShape)it.next();
                if (csc==cs){
                    if(primaryX == oldData) {
                        primaryX=newData;
                        if (isPrimaryBounded && (primaryX!=null)) {
                            try {
                                long imin = primaryX.getStartIndex();
                                long imax = primaryX.getLastIndex();
                                if (imin>primaryStartIndex) primaryStartIndex = imin;
                                if (imax<primaryEndIndex) primaryEndIndex = imax;
                            }
                            catch (UnsupportedOperation uo1) {
                                isPrimaryBounded = false;
                            }
                        }
                    }
                }
            }
        }
        if (secondaryCurves!=null && onX) {
            for (Iterator it = secondaryCurves.iterator(); it.hasNext(); ) {
                CurveShape cs = (CurveShape)it.next();
                if (csc==cs){
                    if(secondaryX == oldData) {
                        secondaryX=newData;
                        if (isSecondaryBounded && (secondaryX!=null)) {
                            try {
                                long imin = secondaryX.getStartIndex();
                                long imax = secondaryX.getLastIndex();
                                if (imin>secondaryStartIndex) secondaryStartIndex = imin;
                                if (imax<secondaryEndIndex) secondaryEndIndex = imax;
                            }
                            catch (UnsupportedOperation uo2) {
                                isSecondaryBounded = false;
                            }
                        }
                    }
                }
            }
        }
    }

//  Take care of serialisation. Special handling for datasources

    private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException {
        out.defaultWriteObject();
        DataSourcePool.global.writeDataSource(out, primaryX);
        DataSourcePool.global.writeDataSource(out, secondaryX);
    }

    private void readObject(java.io.ObjectInputStream in) throws java.lang.ClassNotFoundException, java.io.IOException {
        in.defaultReadObject();
        primaryX = DataSourcePool.global.readDataSource(in);
        secondaryX = DataSourcePool.global.readDataSource(in);

        nbXGraduation = preferredXGraduation;
        nbYGraduation = preferredYGraduation;

        if (isPrimaryBounded && (primaryX!=null)) {
            try {
                primaryStartIndex = primaryX.computeStartIndex();
                primaryEndIndex = primaryX.computeLastIndex();

            }
            catch (UnsupportedOperation uo1) {
                isPrimaryBounded = false;
            }
        }
        if (isSecondaryBounded && (secondaryX!=null)) {
            try {
                secondaryStartIndex = secondaryX.computeStartIndex();
                secondaryEndIndex = secondaryX.computeLastIndex();
            }
            catch (UnsupportedOperation uo2) {
                isSecondaryBounded = false;
            }
        }
        isNotifying = true;

        // fix old bugs on cloned plots
        for(int k=0;k<2;k++){
            Vector curves=(k==0) ? primaryCurves : secondaryCurves;
            if (curves != null) {
                for (int i = 0; i < curves.size(); i++) {
                    CurveShape cs = (CurveShape) curves.get(i);
                    Curve c = getCurve(cs);
                    if (c == null) {
                        System.err.print("Try to fix wrong curve shape ...");
                        for (int j = 0; j < _curves.size(); j++) {
                            c = (Curve) _curves.get(j);
                            try{
                                if ((c.shape.getXSource() == cs.getXSource())
                                        && (c.shape.getYSource() == cs.getYSource())) {
                                    c.shape = cs;
                                    System.err.print(" OK");
                                    break;
                                }
                            }catch (DataException e){}
                        }
                        System.err.println("");
                    }
                }
            }
        }

        // Update curves label in case display label mode has changed
        for (int i=0;i<_curves.size();i++){
            CurveShape curveShape = ((Curve)_curves.get(i)).shape;  
            try{
                DataSource ds= curveShape.getYSource();
                if (ds != null) {
                    String label = DEFAULT_DISPLAY_DATA_SOURCE_ID? DataInfo.getAliasOrIdwithUnit(ds) : DataInfo.getAliasOrLabelwithUnit(ds);
                    setCurveLabel(curveShape,label);
                }
            }catch (DataException e){}
        }
        // X axis legends
        if (primaryX != null){
            String label = DEFAULT_DISPLAY_DATA_SOURCE_ID? DataInfo.getAliasOrIdwithUnit(primaryX) : DataInfo.getAliasOrLabelwithUnit(primaryX);
            axesLimitsArray[PX].label = label;
        }

        if (secondaryX != null){
            String label = DEFAULT_DISPLAY_DATA_SOURCE_ID? DataInfo.getAliasOrIdwithUnit(secondaryX) : DataInfo.getAliasOrLabelwithUnit(secondaryX);
            axesLimitsArray[SX].label = label;
        }


        //Check for axeRange evolution.
        if(axesLimitsArray == null || axesLimitsArray[0] == null || !axesLimitsArray[0].created){
            //If first axe limit has not been created, it means, we have restored a file
            // That is prior to axeLimit introduction.
            //Do the transformation
            axesLimitsArray = new AxeRange[AXE_NUMBER];
            //Set PX AxeRange.
            axesLimitsArray[PX] = new AxeRange(pxmin, pxmax, pxstep, pxvaluesOK, autopx, pxlabel);
            //Set PY AxeRange.
            axesLimitsArray[PY] = new AxeRange(pymin, pymax, pystep, pyvaluesOK, autopy, pylabel);
            //Set SX AxeRange.
            axesLimitsArray[SX] = new AxeRange(sxmin, sxmax, sxstep, sxvaluesOK, autosx, sxlabel);
            //Set SY AxeRange.
            axesLimitsArray[SY] = new AxeRange(symin, symax, systep, syvaluesOK, autosy, sylabel);
        }
    }

    //-----------------------------------------------------------------------------
    // Properties managment
    //-----------------------------------------------------------------------------
    public JPropertiesPanel createPanel() {
        int curvesSize =  ((primaryCurves!=null ? primaryCurves.size() : 0) (secondaryCurves!=null ? secondaryCurves.size() : 0));
        return new PlotPropertiesPanel(curvesSize, Builtin.resources.getString("Plot"));
    }


    public String[] getPropertyNames(){
        if (_propertyNames==null)
            _propertyNames = new PlotPropertiesNames().getPropertyNames();
        return _propertyNames;
    }

    public static class PlotPropertiesNames extends AbstractShapePropertiesNames{
        public PlotPropertiesNames(){
            super();

            // four axes
            propertyNames.addAll(Arrays.asList(new AxePropertiesNames("primX").getPropertyNames()));
            propertyNames.addAll(Arrays.asList(new AxePropertiesNames("primY").getPropertyNames()));
            propertyNames.addAll(Arrays.asList(new AxePropertiesNames("secX").getPropertyNames()));
            propertyNames.addAll(Arrays.asList(new AxePropertiesNames("secY").getPropertyNames()));

            // curves
            propertyNames.addAll(Arrays.asList((new CurveShapePropertiesNames()).getPropertyNames()));

            // limits
            propertyNames.addAll(Arrays.asList((new LimitShapePropertiesNames()).getPropertyNames()));

            propertyNames.add("PLOT_LEGEND");
            propertyNames.add("PLOT_TITLE");
            propertyNames.add("PLOT_CURVE_INFORMATION")
        }
    }

    public HashMap getAxesProperties(HashMap map, int axeNumber){
        String[] list=getPropertyNames();
        HashMap res=(map == null) ? new HashMap() : map;
        if (list!=null){
            for(int i=0;i<list.length;i++){
                if(list[i].startsWith("PLOT_AXE_")
                        && (list[i].indexOf("_MAX")>0
                                || list[i].indexOf("_MIN")>0
                                || list[i].indexOf("_STEP")>0)
                                && (axeNumber==-1
                                        || list[i].indexOf(AXIS_ID[axeNumber])>0)){
                    res.put(list[i],getPropertyValue(list[i]));
                }
            }
        }
        return res;
    }

    public HashMap getCurvesProperties(HashMap map){
        HashMap res=(map == null) ? new HashMap() : map;

        Vector curves= new Vector();
        if (primaryCurves!=null)
            curves.addAll(primaryCurves);
        if (secondaryCurves!=null)
            curves.addAll(secondaryCurves);
        res.put("PLOT_CURVE_LIST", getCurveParametersList(curves));
        return res;
    }

    public void setPropertyValue(String name, Object value) {
        super.setPropertyValue(name, value);
        if (name.equalsIgnoreCase("PLOT_TITLE")) {
            if(value instanceof String){
                setTitle((String) value);
            }
            else{
                setTitle(null);
            }
        }
        else if (name.equalsIgnoreCase("PLOT_CURVE_INFORMATION")&& (value instanceof Boolean)) {
            setCurveInformationVisible((Boolean)value);
        }
        else if (name.equalsIgnoreCase("PLOT_LEGEND")&& (value instanceof Boolean)) {
            setLegendVisible(((Boolean)value).booleanValue());
        }
        else if(name.startsWith("PLOT_AXE_")){
            boolean vb;
            double vd;
            String vs;
            for(int i=0;i<AXIS_ID.length;i++){
                if(name.startsWith(AxisShape.getPrefix(AXIS_ID[i]))){
                    if (name.endsWith("_OK") && (value instanceof Boolean)) {
                        vb=((Boolean)value).booleanValue();
                        axesLimitsArray[i].validity=vb;
                    }
                    else if (name.endsWith("_GRID") && (value instanceof Boolean)) {
                        vb=((Boolean)value).booleanValue();
                        if(i==0 && _asx1!=null) _asx1.setWithGridlines(vb);
                        else if(i==1 && _asy1!=null) _asy1.setWithGridlines(vb);
                        else if(i==2 && _asx2!=null) _asx2.setWithGridlines(vb);
                        else if(_asy2!=null) _asy2.setWithGridlines(vb);
                    }
                    else if (name.endsWith("_DASHEDGRID") && (value instanceof Boolean)) {
                        vb=((Boolean)value).booleanValue();
                        if(i==0 && _asx1!=null) _asx1.setWithDashedGrid(vb);
                        else if(i==1 && _asy1!=null) _asy1.setWithDashedGrid(vb);
                        else if(i==2 && _asx2!=null) _asx2.setWithDashedGrid(vb);
                        else if(_asy2!=null) _asy2.setWithDashedGrid(vb);
                    }
                    else if (name.endsWith("_AUTO")&& (value instanceof Boolean)) {
                        vb=((Boolean)value).booleanValue();
                        axesLimitsArray[i].auto=vb;
                    }
                    else if (name.endsWith("_LOG")&& (value instanceof Boolean)) {
                        vb=((Boolean)value).booleanValue();
                        if(i==0) logpx=vb; else if(i==1) logpy=vb;
                        else if(i==2) logsx=vb; else logsy=vb;
                    }
                    else if (name.endsWith("_MIN")&& (value instanceof Double)) {
                        vd=((Double)value).doubleValue();
                        axesLimitsArray[i].min=vd;
                    }
                    else if (name.endsWith("_MAX")&& (value instanceof Double)) {
                        vd=((Double)value).doubleValue();
                        axesLimitsArray[i].max=vd;
                    }
                    else if (name.endsWith("_STEP")&& (value instanceof Double)) {
                        vd=((Double)value).doubleValue();
                        if (vd >0.0)
                            axesLimitsArray[i].step=vd;
                    }
                    else if (name.endsWith("_LABEL")) {
                        if(value instanceof String) vs=(String)value; else vs=null;
                        axesLimitsArray[i].label=vs;
                    }
                    else if (name.endsWith("_FLOATING")) {
                        axesLimitsArray[i].floatingAxe = ((Boolean)value).booleanValue();
                    }
                    else if (name.endsWith("_FLOATING_RANGE")) {
                       if (value instanceof Double){
                           vd=((Double)value).doubleValue();
                           if ((vd >0.0) && axesLimitsArray[i].validity && axesLimitsArray[i].floatingAxe) {
                               axesLimitsArray[i].floatingAxeRange = vd;
                           }
                       }
                    }
                }
            }
        }
        else if ( (name.startsWith("PLOT_CURVE_LIST")) && (value instanceof Vector)){
            Vector curves= new Vector();
            if (primaryCurves!=null)
                curves.addAll(primaryCurves);
            if (secondaryCurves!=null)
                curves.addAll(secondaryCurves);
            if(!getCurveParametersList(curves).equals(value)){
                setCurves((Vector)value);
            }
        }
        else if ( (name.startsWith("PLOT_LIMIT_LIST")) && (value instanceof Vector)){
            if(!getLimitParameters(_limits).equals(value)){
                setLimitParamters((Vector)value);
            }
        }
    }

    public Object getPropertyValue(String name) {
        Object res = super.getPropertyValue(name);
        if (name.equalsIgnoreCase("PLOT_TITLE")) {
            res=getTitle();
        }
        else if (name.equalsIgnoreCase("PLOT_LEGEND")) {
            res=new Boolean(isLegendVisible());
        }
        else if (name.equalsIgnoreCase("PLOT_CURVE_INFORMATION")) {
            res= isCurveInformationVisible();
        }

        else if(name.startsWith("PLOT_AXE_")){
            for(int i=0;i<AXIS_ID.length;i++){
                if(name.startsWith(AxisShape.getPrefix(AXIS_ID[i]))){
                    if (name.endsWith("_OK")) {
                        res=new Boolean(axesLimitsArray[i].validity);
                    }
                    else if (name.endsWith("_GRID")) {
                        if(i==0) res=new Boolean(_asx1!=null ? _asx1.isWithGridlines() : false);
                        else if(i==1) res=new Boolean(_asy1!=null ? _asy1.isWithGridlines() : false);
                        else if(i==2) res=new Boolean(_asx2!=null ? _asx2.isWithGridlines() : false);
                        else res=new Boolean(_asy2!=null ? _asy2.isWithGridlines() : false);

                    }else if (name.endsWith("_DASHEDGRID")) {
                        if(i==0) res=new Boolean(_asx1!=null ? _asx1.isWithDashedGrid() : false);
                        else if(i==1) res=new Boolean(_asy1!=null ? _asy1.isWithDashedGrid() : false);
                        else if(i==2) res=new Boolean(_asx2!=null ? _asx2.isWithDashedGrid() : false);
                        else res=new Boolean(_asy2!=null ? _asy2.isWithDashedGrid() : false);
                    }
                    else if (name.endsWith("_AUTO")) {
                        res=new Boolean(axesLimitsArray[i].auto);
                    }
                    else if (name.endsWith("_LOG")) {
                        if(i==0) res=new Boolean(logpx); else if(i==1) res=new Boolean(logpy);
                        else if(i==2) res=new Boolean(logsx); else res=new Boolean(logsy);
                    }
                    else if (name.endsWith("_MIN")) {
                        res=new Double(axesLimitsArray[i].min);
                    }
                    else if (name.endsWith("_MAX")) {
                        res=new Double(axesLimitsArray[i].max);
                    }
                    else if (name.endsWith("_STEP")) {
                        res=new Double(axesLimitsArray[i].step);
                    }
                    else if (name.endsWith("_LABEL")) {
                        res=axesLimitsArray[i].label;
                    }
                    else if (name.endsWith("_FLOATING")) {
                        res=new Boolean(axesLimitsArray[i].floatingAxe);
                    }
                    else if (name.endsWith("_FLOATING_RANGE")){
                        res= new Double(axesLimitsArray[i].floatingAxeRange);
                   
                }
            }
        }
        else if (name.equalsIgnoreCase("PLOT_CURVE_LIST")){
            Vector curves= new Vector();
            if (primaryCurves!=null)
                curves.addAll(primaryCurves);
            if (secondaryCurves!=null)
                curves.addAll(secondaryCurves);
            res=getCurveParametersList(curves);
        }
        else if (name.equalsIgnoreCase("PLOT_LIMIT_LIST")){
            res=getLimitParameters(_limits);
        }
        return res;
    }

    /**
     * Get a list of curve parameter from the given list of curves
     * @param curves - list of curves
     * @return - list of curve parameter
     */
    protected Vector getCurveParametersList(Vector curves){
        Vector l=new Vector();
        if(curves!=null){
            for(int i=0;i<curves.size();i++){
                CurveShape cs = (CurveShape) curves.get(i);
                l.add(getCurveParameters(cs));
            }
        }
        return l;
    }
   
    /**
     * Get curve parameters from the curve shape
     * @param cs - curve shape
     * @return
     */
    protected CurveParameters getCurveParameters(CurveShape cs){
        CurveParameters res = createCurveParameters();

        try{
            res.xds=cs.getXSource();
        }catch (DataException e){
            res.xds=null;
        }
        try{
            res.yds=cs.getYSource();
        }catch (DataException e){
            res.yds=null;
        }
       
        Curve c=getCurve(cs);
        res.points=c.showPoints;
        res.drawBars=c.drawBars;
        res.strokeParams=new StrokeParameters(c.dashParam1 ,c.dashParam2);
        res.color=c.color;
        res.usePrimaryX= !c.secondaryXaxis;
        res.usePrimaryY= !c.secondaryYaxis;
       
        return res;
    }
   
    protected CurveParameters createCurveParameters(){
        return new CurveParameters();
    }


    /**
     * Create new curves using given list of curve parameters
     * @param l - list of curve parameters
     */
    public void setCurves(Vector l){

        // Remove old curves
        Vector oldCurves = (Vector)_curves.clone();
        for(int i=0;i<oldCurves.size();i++){
            removeCurve(((Curve)oldCurves.get(i)).shape);
        }

        // Add new curves
        for(int i=0;i<l.size();i++){
            CurvePropertiesPanel.CurveParameters cp=(CurvePropertiesPanel.CurveParameters)l.get(i);
            CurveShape cs = doAddYAction(new Object[]{cp.yds, new Boolean(cp.usePrimaryY), new Boolean(cp.usePrimaryX)});
            setCurve(getCurve(cs), cp);
        }
    }
   
    /**
     * Set a curve from given curve parameters
     * @param c - curve to be set
     * @param cp - curve parameters
     */
    public void setCurve(Curve c, CurveParameters cp){
        c.setShowPoints(cp.points);
        c.setDrawBars(cp.drawBars);
        c.setDashParameters(cp.strokeParams.param1,cp.strokeParams.param2);
        c.color=cp.color;
    }


    protected Vector getLimitParameters(ArrayList al){
        Vector l=new Vector();
        if(al!=null){
            for(int i=0;i<al.size();i++){
                Limit c = (Limit) al.get(i);
                LimitShape cs = c.shape;
                LimitPropertiesPanel.LimitParameters cp=new LimitPropertiesPanel.LimitParameters(c.getLabel());    
                cp.strokeParams = new StrokeParameters(c.dashParam1 ,c.dashParam2);
                cp.color=c.color;
                cp.isVertical = cs.getIsVertical();
                cp.secondaryAxis = c.secondaryAxis;
                cp.position = cs.getPosition();
                l.add(cp);
            }
        }
        return l;
    }
   
    public void setLimitParamters(Vector l){
        // clean previous
        _limits.clear();

        for(int i=0;i<l.size();i++){
            LimitPropertiesPanel.LimitParameters cp=(LimitPropertiesPanel.LimitParameters)l.get(i);

            LimitShape cs = doAddLimitAction(cp.isVertical, cp.secondaryAxis, cp.position, cp.name);
            Limit c=getLimit(cs);

            c.setLabel(cp.name);
            c.secondaryAxis = cp.secondaryAxis;
            c.setDashParameters(cp.strokeParams.param1,cp.strokeParams.param2);
            c.color=cp.color;
        }
    }

    /**
     * Class AxeRange.
     * Summary:
     * This class represent an axe range, and contains
     * properties for the axe.
     */
    public static class AxeRange implements Serializable{
        static final long serialVersionUID =  1147229299688184239L;

        /** The min, max an step value of the axe.*/
        public double min, max, step;
        /** A boolean to know if the axe's range value are valid.*/
        public boolean validity;
        /** A boolean to keep the auto status.*/
        public boolean auto;
        /** The label to apply to the axe.*/
        public String label;
        /** A boolean to keep the floating property status.*/
        public boolean floatingAxe;
        /** Floating range value used when floatingAxe is enabled.*/
        public double floatingAxeRange;


        /**
         * A boolean to know if AxeRange has been created by serialization while
         * reading an old file where AxeRange did not exist, or created by a new version.
         * AxeRange created by new Version has their created boolean to true.
         */
        protected boolean created = false;


        /**
         * Constructor AxeRange.
         */
        public AxeRange(){
            created = true;
            //Set auto scale to true, as default behavior.
            auto = true;
            floatingAxe = false;
            floatingAxeRange = 1000.0;
        }

        /**
         * Constructor AxeRange.
         * @param _min          The min value of the axe.
         * @param _max          The max value of the axe.
         * @param _step         The step value of the axe.
         * @param _validity     A boolean to know if the axe's range value are valid.
         * @param _auto         A boolean to keep the auto status
         * @param _label        The label to apply to the axe
         */
        public AxeRange(double _min, double _max, double _step, boolean _validity, boolean _auto, String _label, boolean _floatingAxe, double _floatingAxeRange){
            created = true;
            //set attributes.
            min=_min;
            max=_max;
            step=_step;
            validity=_validity;
            auto=_auto;
            label=_label;
            floatingAxe = _floatingAxe;
            floatingAxeRange = _floatingAxeRange;
        }

        public AxeRange(double _min, double _max, double _step, boolean _validity, boolean _auto, String _label){
            this(_min,_max,_step,_validity,_auto,_label,false,0.0);
        }

        /**
         * Method <b>set</b>
         * <br><b>Summary:</b><br>
         * This method sets some axe range values.
         * Parameters:
         * @param _min          The min value of the axe.
         * @param _max          The max value of the axe.
         * @param _step         The step value of the axe.
         * @param _validity     A boolean to know if the axe's range value are valid.
         *
         */
        public void set(double _min, double _max, double _step, boolean _validity) {
            //set attributes.
            min = _min;
            max = _max;
            step = _step;
            validity = _validity;
        }




        /**
         * Method <b>computeStep</b>
         * <br><b>Summary:</b><br>
         * This method compute the step to apply to the axeRange.
         * Parameters:
         * @param nbGraduation      The numberOfGraduation to apply.
         *
         */
        public void computeStep(int nbGraduation) {
            //If min and max are equals, increase interval to have correct step/min/max values.
            if(min == max){
                min--;
                max++;
            }
            step = Plot.computeStep(min, max,nbGraduation);
            validity = true; // no exception at this point => OK
        }
    }

    /* (non-Javadoc)
     * @see simtools.diagram.DiagramComponent.ContextualDrawingProvider#getContextualDrawing()
     */
    public ContextualDrawing getContextualDrawing(DiagramSelection s) {
        return new PlotZoom(s);
    }


    public class PlotCompoundEdit extends CompoundEdit{
        public PlotCompoundEdit(){
        }

        public PlotCompoundEdit(HashMap oldValues){
            Iterator it=oldValues.keySet().iterator();
            while(it.hasNext()){
                String n=(String)it.next();
                addEdit(new PropertyChangeEdit(Plot.this, n, oldValues.get(n),getPropertyValue(n)));
            }
            end();
        }
        /* (non-Javadoc)
         * @see javax.swing.undo.CompoundEdit#redo()
         */
        public void redo() throws CannotRedoException {
            super.redo();
            repaintDiagram(getBounds());
        }
        /* (non-Javadoc)
         * @see javax.swing.undo.CompoundEdit#undo()
         */
        public void undo() throws CannotUndoException {
            super.undo();
            repaintDiagram(getBounds());
        }
    };



    /* (non-Javadoc)
     * @see simtools.shapes.AbstractShape#update()
     */
    public void refresh(){
        super.refresh();
        repaintDiagram(getBounds());
    }

}

TOP

Related Classes of jsynoptic.builtin.Plot

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.