package org.spview.gui;
/*
 * Class to draw spectra and assignments
 */

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Locale;
import javax.swing.Box;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JProgressBar;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;

import org.spview.point.ExpJsynPoint;
import org.spview.point.PredJsynPoint;
import org.spview.filehandler.ExpFile;
import org.spview.filehandler.DataFile;
import org.spview.filehandler.PredFile;
import org.spview.filehandler.FortranFormat;

////////////////////////////////////////////////////////////

/**
 * This panel is used to paint the graphs.
 */
public class PanAff extends    JPanel
             implements ActionListener, MouseListener, MouseMotionListener, Printable {
    /**
	 * 
	 */
	private static final long serialVersionUID = -6454762258521638796L;
	private JobPlay jobplay;                                         // calling JobPlay
    private String  workd;                                           // working directory

    private Graphics2D g2d;                                          // to be able to rotate text
    private boolean    printing;                                     // print status

    // all variables beginning with p_... contain pixel values (int or double)
    // others are in their own units : frequency, intensity
    // the following are related only to screen drawing
    private int p_xmouse;                                            // x mouse position
    private int p_ymouse;                                            // y mouse position
    private int p_wxma;                                              // window width
    private int p_wyma;                                              // window height
    private int prevp_wxma;                                          // previous p_wxma
    private int p_cxmi;                                              // x min of data area
    private int p_cxma;                                              // x max of data area
    private int p_cwidth;                                            // x range
    private int p_epymi;                                             // exp/pred area y lower limit
    private int p_yatma;                                             // exp  area y upper limit
    private int p_yprmi;                                             // pred area y lower limit
    private int p_yprma;                                             // pred area y upper limit
    private int p_epyma;                                             // exp/pred area y upper limit
    private int p_gap;                                               // gap along axis
    private int p_cymi;                                              // y min of data area
    private int p_cyma;                                              // y max of data area
    private int p_cheight;                                           // y range
    private int p_ovray;                                             // oval ray
    private int p_ovwidth;                                           // oval long ray
    private int p_xhalf;                                             // half fuzzy width
    // sc_ variables define scale while drawing/printing
    private double sc_fontsz = .015;                                 // font size        % ddp_height
    private double sc_epymi  = .005;                                 // basic y gap      % ddp_height
    private double sc_gap    = .0035;                                // gap              % ddp_width
    private double sc_ovray  = .003;                                 // oval ray         % ddp_width
    private double sc_xhalf  = .003;                                 // half fuzzy width % ddp_width

    // data
    private String[]   ntfile;                                       // data file type
    private int[]      nbxy;                                         // number of points in data file
    private double[][] x;                                            // frequency
    private double[][] y;                                            // intensity
    private double[]   sxmi;                                         // X min (including shift)
    private double[]   sxma;                                         // X max (including shift)
    private int[]      ixdeb;                                        // window first point index
    private int[]      ixfin;                                        // window last  point index
    private boolean[]  use_red;                                      // use reduced data
    private int[][]    i_red;                                        // index of reduced data
    private int[]      nbxy_red;                                     // reduced number of points
    private int[]      ixdeb_red;                                    // window first reduced point index
    private int[]      ixfin_red;                                    // window last  reduced point index
    private boolean[]  show;                                         // show      the data file
    private boolean[]  yreverse;                                     // Y reverse the data file
    private double[]   xshifted;                                     // X shift value
    private double[]   yshifted;                                     // Y shift value
    private int        nbdf;                                         // number of data files
    private int        mxnbdf;                                       // max number of data files
    private int        nbpredasf;                                    // number of pred-as-stick files
    private boolean[]  dfready;                                      // data file ready

    // exp
    private ExpFile     expf;                                        // experiment file
    private int         efnbxy;                                      // number of points in exp file
    private double[]    efx;                                         // frequency
    private String[]    effasog;                                     // global frequency mark
    private double[]    efy;                                         // intensity
    private String[]    efsasog;                                     // global intensity mark
    private String[]    efsdobs;                                     // frequency and intensity standard deviations string
    private ArrayList<ExpJsynPoint>[] efejsynpt;                                   // exp assignment point
    private String[]    efexasg;                                     // EXASG
    private int         efixdeb;                                     // window first point index
    private int         efixfin;                                     // window last  point index
    private int         efass2;                                      // associated data file index
    private ArrayList<String>   alexasg;                                     // EXASG choice
    private String[]    exasg;                                       // easy access EXASG
    private int         nbexasg;                                     // number of EXASG
    private String      defexasg;                                    // default EXASG
    private boolean     efready;                                     // exp file ready

    // pred
    private int      pfnbxy;                                         // number of points in pred file
    private double[] pfx;                                            // frequency
    private double[] pfy;                                            // intensity
    private int[]    pfj;                                            // J inf
    private String[] pfjsyn;                                         // assignment string
    private int[]    pfiexpa;                                        // index of assigned exp
    private int      pfixdeb;                                        // window first point index
    private int      pfixfin;                                        // window last  point index
    private int      pfass2;                                         // associated data file index
    private boolean  pfready;                                        // pred file ready
    private double   predselx0;                                      // starting x of pred selection area
    private double   predselx1;                                      // ending   x of pred selection area

    //
    private double p_zx0;                                            // starting x of zoom area
    private double p_zy0;                                            // starting y of zoom area
    private double p_zx1;                                            // ending   x of zoom area
    private double p_zy1;                                            // ending   y of zoom area
    private double prevp_zx1;                                        // previous p_zx1
    private double prevp_zy1;                                        // previous p_zy1
    private double p_pfx0;                                           // starting x of pred selection area
    private double p_pfx1;                                           // ending   x of pred selection area
    //
    private int    p_paintx;                                         // starting X of myrepaint area
    private int    p_painty;                                         // starting Y of myrepaint area
    private int    p_paintwidth;                                     // width      of myrepaint area
    private int    p_paintheight;                                    // height     of myrepaint area
    //
    private double axmi;                                             // global frequency mini
    private double axma;                                             // global frequency maxi
    private double axdelta;                                          // global frequency range
    private double aymi;                                             // global intensity mini
    private double ayma;                                             // global intensity maxi
    private double aydelta;                                          // global intensity range
    private double ayrev;                                            // global intensity reverse axis
    private double saxmi;                                            // global frequency mini (including shift)
    private double saxma;                                            // global frequency maxi (including shift)
    private double bxmi;                                             // window frequency mini
    private double bxma;                                             // window frequency maxi
    private double bxdelta;                                          // window frequency range
    private double bymi;                                             // window intensity mini
    private double byma;                                             // window intensity maxi
    private double bydelta;                                          // window intensity range
    private double apasymi;                                          // pred-as-stick global intensity mini
    private double apasyma;                                          // pred-as-stick global intensity maxi
    private double apasydelta;                                       // pred-as-stick global intensity range
    private double apasyrev;                                         // pred-as-stick global intensity reverse axis
    private double bpasymi;                                          // pred-as-stick window intensity mini
    private double bpasyma;                                          // pred-as-stick window intensity maxi
    private double bpasydelta;                                       // pred-as-stick window intensity range

    // first tick , interval, nb of intervals, last tick
    private double[] acadreX    = {0., 0., 0., 0.};                  // global frequency
    private double[] acadreY    = {0., 0., 0., 0.};                  // global intensity
    private double[] bcadreX    = {0., 0., 0., 0.};                  // window frequency
    private double[] bcadreY    = {0., 0., 0., 0.};                  // window intensity
    private double[] acadrepasY = {0., 0., 0., 0.};                  // pred-as-stick global intensity
    private double[] bcadrepasY = {0., 0., 0., 0.};                  // pred-as-stick window intensity
    private Rectangle2D rectS;                                       // rectangle defined by a string

    private Color[]   coul;                                          // colors
    private int       nbcoul;                                        // number of colors
    private JTextArea jta;                                           // for selected exp and pred characteristics

    private boolean showbar;                                         // allow to show vertical bar
    private boolean movebar;                                         // allow to move vertical bar
    private double  xbar;                                            // X position of vertical bar
    private boolean zoomx;                                           // X zoom has to be defined
    private boolean zoomy;                                           // Y zoom has to be defined
    private boolean zoompasy;                                        // pred-as-stick Y zoom has to be defined
    private boolean zoom;                                            // draw zoom area
    private int     shiftedf;                                        // index of shifted data file
    private boolean shiftx;                                          // X shift has to be defined
    private boolean shifty;                                          // Y shift has to be defined
    private boolean shift;                                           // X or Y shift have to be defined
    private boolean predsel;                                         // pred selection running

    // mouse position
    private JLabel jlx;                                              // X in data scale
    private JLabel jly;                                              // Y in data scale
    private JLabel jlypredas;                                        // Y in pred-as-stick scale

    // data file area popup menu
    private JPopupMenu jpmdata;
    private JMenuItem  jmishowbar;                                   // show-set vertical bar
    private JMenuItem  jmihidebar;                                   // hide vertical bar
    private JMenuItem  jmizoomx;                                     // X zoom
    private JMenuItem  jmizoomy;                                     // Y zoom
    private JMenuItem  jmizoompasy;                                  // pred_as_stick Y zoom
    private JMenu      jmrescalex;                                   // X rescale
    private JMenu      jmrescaley;                                   // Y rescale
    private JMenu      jmrescalepasy;                                // pred-as-stick Y rescale
    private JMenuItem  jmirestorex;                                  // X restore
    private JMenuItem  jmirestorey;                                  // Y restore
    private JMenuItem  jmirestorepasy;                               // pred-as-stick Y restore

    // assignment area popup menu
    private JPopupMenu    jpmassign;
    private JMenuItem     jmiunselexp;                               // unselect exp
    private JMenu         jmunass;                                   // unassign
    private JMenuItem[]   jmiunass;                                  // select one assignment  to be unassigned
    private JMenuItem     jmiunassall;                               // select all assignments to be unassigned
    private String[]      xaso = { " ", "+", "-" };                  // possible values for frequency and intensity marks
    private int           nbxaso;                                    // number of values
    private JMenu         jmfaso;                                    // set frequency mark
    private JMenuItem[][] jmifaso;                                   // [assignments][frequency marks]
    private JMenu         jmsaso;                                    // set intensity mark
    private JMenuItem[][] jmisaso;                                   // [assignments][intensity marks]
    private JMenu         jmexasg;                                   // set EXASG
    private JMenuItem[]   jmiexasg;                                  // [exp data]
    private JMenu         jmcomm;                                    // set comment
    private JMenuItem[]   jmicomm;                                   // [exp data]
    private JMenuItem     jmiaddexasg;                               // add EXASG string
    private JMenu         jmdefexasg;                                // set default EXASG
    private JMenuItem[]   jmidefexasg;                               // [possible EXASG]

    // exp  selection popupmenu
    private JPopupMenu  jpmexp;
    private int         mxticks = 30;                                // max nb of ticks in an exp selection popup menu
    private JMenuItem[] jmiexp;                                      // selectable
    private int         ixexp;                                       // selected exp  index
    private int         ixdebexp;                                    // window first point index
    private int         ixfinexp;                                    // window last  point index

    // pred selection window
    private JFSelPred jfsp;                                          // selected pred display
    private boolean   lsallowed;                                     // local simulation allowed
    private boolean   locsim;                                        // loc-sim shown
    private double[]  lsx;                                           // loc-sim spectrum X
    private double[]  lsy;                                           // loc-sim spectrum Y
    private double    lsmi;                                          // loc-sim spectrum Ymin
    private double    lsma;                                          // loc-sim spectrum Ymax
    private double    lsmis;                                         // pred associated spectrum Ymin
    private double    lsmas;                                         // pred associated spectrum Ymax
    private int[]     ixpred;                                        // selected pred index
    private int[]     ixpreds;                                       // selected pred status >> -1: nothing(---), 0: already in exp(ASG), 1: selected(SEL)
    private int       nbixpred;                                      // nb of selected xpred
    private int       ixdebpred;                                     // pred area first point index
//    private int       ixfinpred;                                     // pred area last  point index
    private int       ixpredable;                                    // index of current selectable pred

    // for X/Y rescale
    private int[]       fact = { 2, 5, 10, 20, 50, 100 };            // scaling factors
    private int         nbfact;                                      // number of scaling factors
    private int         nbscalec;                                    // number of scaling choices
    private JMenuItem[] jmivalexpx;                                  // frequency choices
    private JMenuItem[] jmivalexpy;                                  // intensity choices
    private JMenuItem[] jmivalexppasy;                               // pred-as-stick intensity choices

    // format
    private String cStr;

    private ExpJsynPoint cejpt;                                      // to sort
    private String       nsavexp;                                    // saved exp file name
    private PrintWriter  out1;
    private String lnsep;                                            // line separator
    private String fisep;                                            // file separator
    private Font   mono14;                                           // Monospaced 14 font
    private int    fontsz = 14;                                      // font size
    
	public final static String EXASG_DEFAULT = "SPVIEW_EXASG                  ";
    
/////////////////////////////////////////////////////////////////////

    /**
     * Construct a new PanAff.
     *
     * @param cjobplay    calling JobPlay
     * @param cmxnbdf     max number of data files
     * @param ccoul       colors
     * @param cjta        to show selected exp and pred
     * @param cjlx        to show X mouse position (data scale)
     * @param cjly        to show Y mouse position (data scale)
     * @param cjlypredas  to show Y mouse position (pred-as-stick scale)
     */
    public PanAff( JobPlay cjobplay, int cmxnbdf, Color[] ccoul, JTextArea cjta, JLabel cjlx, JLabel cjly, JLabel cjlypredas) {

        setName("PanAff");                                           // for help files

        jobplay   = cjobplay;                                        // calling jobplay
        workd     = System.getProperty("user.dir");                  // working directory
        mxnbdf    = cmxnbdf;                                         // max nb of data files
        coul      = (Color[]) ccoul.clone();                         // color choice
        nbcoul    = ccoul.length;                                    // nb of colors
        jta       = cjta;                                            // to show selected exp and pred
        jlx       = cjlx;                                            // to show X mouse position (data scale)
        jly       = cjly;                                            // to show Y mouse position (data scale)
        jlypredas = cjlypredas;                                      // to show Y mouse position (pred-as-stick scale)

        lnsep  = System.getProperty("line.separator");
        fisep  = System.getProperty("file.separator");
        mono14 = new Font("Monospaced", Font.PLAIN, fontsz);         // default font
        // create arrays
        ntfile    = new String[mxnbdf];                              // data file type
        nbxy      = new int[mxnbdf];                                 // number of points in data file
        x         = new double[mxnbdf][];                            // frequency
        y         = new double[mxnbdf][];                            // intensity
        sxmi      = new double[mxnbdf];                              // X min (including shift)
        sxma      = new double[mxnbdf];                              // X max (including shift)
        ixdeb     = new int[mxnbdf];                                 // window first point index
        ixfin     = new int[mxnbdf];                                 // window last  point index
        use_red   = new boolean[mxnbdf];                             // use reduced data
        i_red     = new int[mxnbdf][];                               // index of reduced data
        nbxy_red  = new int[mxnbdf];                                 // reduced number of points
        ixdeb_red = new int[mxnbdf];                                 // window first reduced point index
        ixfin_red = new int[mxnbdf];                                 // window last  reduced point index
        show      = new boolean[mxnbdf];                             // show      the data file
        yreverse  = new boolean[mxnbdf];                             // Y reverse the data file
        xshifted  = new double[mxnbdf];                              // X shift value
        yshifted  = new double[mxnbdf];                              // Y shift value
        dfready   = new boolean[mxnbdf];                             // data file ready status
        Arrays.fill(dfready,false);                                  // NO data file ready

        efass2 = -1;                                                 // exp  NOT associated to a data file
        pfass2 = -1;                                                 // pred NOT associated to a data file

        nbdf      = 0;                                               // nb of data files
        nbpredasf = 0;                                               // nb of pred-as-stick files
        efnbxy    = 0;                                               // nb of exp  points
        ixexp     = -1;                                              // NO exp  data selected
        efready   = false;                                           // exp file NOT ready
        pfnbxy    = 0;                                               // nb of pred points
        nbixpred  = 0;                                               // NO selected pred points
        pfready   = false;                                           // pred file NOT ready

        nbxaso = xaso.length;                                        // nb of possible values for frequency and intensity marks

        // mouse survey
        addMouseListener(this);
        addMouseMotionListener(this);
        // printing status
        printing = false;
        // no waited action
        showbar   = false;                                           // no vertical bar to show
        movebar   = false;                                           // no move of vertical bar
        zoomx     = false;                                           // no x zoom area to define
        zoomy     = false;                                           // no y zoom area to define
        zoompasy  = false;                                           // no pred-as-stick y zoom area to define
        zoom      = false;                                           // no zoom area to draw
        shiftx    = false;                                           // no x shift area to define
        shifty    = false;                                           // no y shift area to define
        shift     = false;                                           // no shift area to draw
        predsel   = false;                                           // no running pred selection
        lsallowed = false;                                           // local simulation not allowed
        locsim    = false;                                           // no local simulation shown

        // define X/Y scaling menus
        nbfact        = fact.length;                                 // nb of scaling factors
        nbscalec      = 2*nbfact;                                    // nb of scaling choices (* and /)
        jmivalexpx    = new JMenuItem[nbscalec];                     // frequency choices
        jmivalexpy    = new JMenuItem[nbscalec];                     // intensity choices
        jmivalexppasy = new JMenuItem[nbscalec];                     // pred-as-stick intensity choices
        jmrescalex    = new JMenu("Rescale X");
        jmrescaley    = new JMenu("Rescale Y");
        jmrescalepasy = new JMenu("Rescale pred-as-stick Y");

        for( int i=0; i<nbfact; i++ ) {
            jmivalexpx[i]               = new JMenuItem("/"+fact[nbfact-i-1]);  // divide
            jmivalexpx[nbscalec-i-1]    = new JMenuItem("*"+fact[nbfact-i-1]);  // multiply
            jmivalexpy[i]               = new JMenuItem("/"+fact[nbfact-i-1]);
            jmivalexpy[nbscalec-i-1]    = new JMenuItem("*"+fact[nbfact-i-1]);
            jmivalexppasy[i]            = new JMenuItem("/"+fact[nbfact-i-1]);
            jmivalexppasy[nbscalec-i-1] = new JMenuItem("*"+fact[nbfact-i-1]);
        }
        //
        for( int i=0; i<nbscalec; i++ ) {                            // for each choice
            if( i == nbfact ) {
                jmrescalex.addSeparator();
                jmrescaley.addSeparator();
                jmrescalepasy.addSeparator();
            }
            jmivalexpx[i].addActionListener(this);
            jmrescalex.add(jmivalexpx[i]);
            jmivalexpy[i].addActionListener(this);
            jmrescaley.add(jmivalexpy[i]);
            jmivalexppasy[i].addActionListener(this);
            jmrescalepasy.add(jmivalexppasy[i]);
        }
        // menus
        jpmdata = new JPopupMenu();

        jmishowbar = new JMenuItem("Show/set vertical bar");
        jmishowbar.addActionListener(this);
        jpmdata.add(jmishowbar);

        jmihidebar = new JMenuItem("Hide vertical bar");
        jmihidebar.setEnabled(false);
        jmihidebar.addActionListener(this);
        jpmdata.add(jmihidebar);

        jpmdata.addSeparator();

        jmizoomx = new JMenuItem("Rescale X (zoom)");
        jmizoomx.addActionListener(this);
        jpmdata.add(jmizoomx);

        jpmdata.add(jmrescalex);

        jmirestorex = new JMenuItem("Restore X scale");
        jmirestorex.addActionListener(this);
        jpmdata.add(jmirestorex);

        jpmdata.addSeparator();

        jmizoomy = new JMenuItem("Rescale Y (zoom)");
        jmizoomy.addActionListener(this);
        jpmdata.add(jmizoomy);

        jpmdata.add(jmrescaley);

        jmirestorey = new JMenuItem("Restore Y scale");
        jmirestorey.addActionListener(this);
        jpmdata.add(jmirestorey);

        jpmdata.addSeparator();

        jmizoompasy = new JMenuItem("Rescale pred-as-stick Y (zoom)");
        jmizoompasy.setEnabled(false);
        jmizoompasy.addActionListener(this);
        jpmdata.add(jmizoompasy);

        jmrescalepasy.setEnabled(false);
        jpmdata.add(jmrescalepasy);

        jmirestorepasy = new JMenuItem("Restore pred-as-stick Y scale");
        jmirestorepasy.setEnabled(false);
        jmirestorepasy.addActionListener(this);
        jpmdata.add(jmirestorepasy);
    }

/////////////////////////////////////////////////////////////////////////////


    private void myrepaint(int x, int y, int width, int height) {
    // internally called
    // area slightly extended (1 pixel in each direction)
    // in PanAff, for all x,width or y,height in pixel unit : x0 = x, x1 = x0+width-1 (ie. width = x1-x0+1)

        int x0 = Math.max(0,x-1);                                    // 1 more pixel (left  side)
        int y0 = Math.max(0,y-1);                                    // 1 more pixel (upper side)
        int w0 = Math.min(x+width ,p_wxma)-x0+1;                     // 1 more pixel (right side)
        int h0 = Math.min(y+height,p_wyma)-y0+1;                     // 1 more pixel (lower side)
        repaint(x0, y0, w0, h0);
    }

    /**
     * Draw this panel content.
     */
    public void paintComponent(Graphics g) {

        super.paintComponent(g);                                     // prepare the panel

        p_wxma = this.getWidth() -1;                                 // pixel window X max
        p_wyma = this.getHeight()-1;                                 // pixel window Y max
        g2d = (Graphics2D) g;                                        // for extended functions

        boolean screen = true;                                       // screen drawing
        Dimension ss = Toolkit.getDefaultToolkit().getScreenSize();
        int p_width  = (int) ss.getWidth();                          // screen width
        int p_height = (int) ss.getHeight();                         // screen height
        drawData( g2d, p_wxma, p_wyma, screen, p_width, p_height );  // draw

    }

    /**
     * Print this panel content.
     */
    public int print(Graphics g, PageFormat pf, int pi) throws PrinterException {

        if (pi >= 1) {                                               // number of the page to print
            return Printable.NO_SUCH_PAGE;                           // only one
        }

        printing = true;                                             // used to disallowed some button actions while printing

        double pfIW = pf.getImageableWidth();
        double pfIH = pf.getImageableHeight();
        // we have to reduce printed area to fit various OS behaviours
        int prp_wxma = (int) (.9*pfIW -1);                           // paper printable X max
        int prp_wyma = (int) (.9*pfIH -1);                           // paper printable Y max

        g2d = (Graphics2D) g;                                        // for extended functions
        g2d.translate(pf.getImageableX()+.05*pfIW,                   // translate to printable area
                      pf.getImageableY()+.05*pfIH );

        boolean screen = false;                                      // NOT screen but printer drawing
        int p_width  = (int) pf.getWidth();                          // paper width
        int p_height = (int) pf.getHeight();                         // paper height
        drawData( g2d, prp_wxma, prp_wyma, screen, p_width, p_height );  // draw

        printing = false;                                            // NOT longer printing

        return Printable.PAGE_EXISTS;                                // page ready
    }

/////////////////////////////////////////////////////////////////////////////

    // create the page to print or draw on screen
    private void drawData(Graphics2D g2d, int ddp_wxma, int ddp_wyma, boolean screen, int ddp_width, int ddp_height) {

        // all dd_ and ddp_ variables are drawData local
        // usefull variables
        int    ddp_x0;
        int    ddp_x1;
        int    ddp_x2;
        int    ddp_y0;
        int    ddp_y1;
        int    ddp_y2;
        String dd_str;

        // paint
        g2d.setColor(Color.BLACK);                                   // black
        int dd_fontsz = (int) Math.round(ddp_height*sc_fontsz);      // font size
        Font monoFt = new Font("Monospaced", Font.PLAIN, dd_fontsz); // default font
        g2d.setFont( monoFt );
        FontMetrics fm = g2d.getFontMetrics();                       // for its methods

        // ddp_wxma and ddp_wyma are defined before call to drawData
        // in paintComponent (draw) or print
        int ddp_gap     = (int) Math.round(ddp_width*sc_gap);        // gap
        int ddp_cxmi    = 2*ddp_gap+(int) Math.round(dd_fontsz*2.5); // x min
        int ddp_cxma    = ddp_wxma-ddp_cxmi;                         // x max
        int ddp_cwidth  = ddp_cxma-ddp_cxmi+1;                       // x range
        int ddp_epymi   = (int) Math.round(ddp_height*sc_epymi);     // exp/pred area y lower limit (basic y gap)
        int ddp_ysaso   = ddp_epymi+ 3*ddp_epymi;                    // y of intensity mark
        int ddp_yfaso   = ddp_epymi+ 6*ddp_epymi;                    // y of frequency mark
        int ddp_yatmi   = ddp_epymi+ 7*ddp_epymi;                    // exp  area y lower limit
        int ddp_yatma   = ddp_epymi+11*ddp_epymi;                    // exp  area y upper limit
        int ddp_yprmi   = ddp_epymi+12*ddp_epymi;                    // pred area y lower limit
        int ddp_yprma   = ddp_epymi+16*ddp_epymi;                    // pred area y upper limit
        int ddp_epyma   = ddp_epymi+17*ddp_epymi;                    // exp/pred area y upper limit
        int ddp_cymi    = ddp_epyma+ddp_gap;                         // y min
        int ddp_cyma    = ddp_wyma-ddp_cxmi;                         // y max
        int ddp_cheight = ddp_cyma-ddp_cymi+1;                       // y range
        int ddp_ovray   = (int) Math.round(ddp_width*sc_ovray);      // oval ray
        int ddp_ovwidth   = 2*ddp_ovray+1;                           // oval long ray
        int ddp_sqhw   = ddp_ovray+1;                                // square half side
        int ddp_sqwidth   = 2*ddp_sqhw+1;                            // square side
        int ddp_xhalf   = (int) Math.round(ddp_width*sc_xhalf);      // half fuzzy width
        // save some for external usage (mouse location, reduced data)
        if( screen ) {
            p_gap     = ddp_gap;
            p_cxmi    = ddp_cxmi;
            p_cxma    = ddp_cxma;
            p_cwidth  = ddp_cwidth;
            p_epymi   = ddp_epymi;
            p_yatma   = ddp_yatma;
            p_yprmi   = ddp_yprmi;
            p_yprma   = ddp_yprma;
            p_epyma   = ddp_epyma;
            p_cymi    = ddp_cymi;
            p_cyma    = ddp_cyma;
            p_cheight = ddp_cheight;
            p_ovray   = ddp_ovray;
            p_ovwidth   = ddp_ovwidth;
            p_xhalf   = ddp_xhalf;
            if( ddp_wxma != prevp_wxma ) {                           // window size changed
                setRedSpect();                                       // reduce spectrum data if necessary
            }
            prevp_wxma = ddp_wxma;                                   // keep it for next call
        }

        if( nbdf == 0 ) {
            // no data file
            return;
        }

        // in-clip area to (re)paint
        Rectangle rect = g2d.getClipBounds();                        // in-clip rectangle
        int    ddp_clipxmi = Math.max(0,rect.x-1);                   // lower X pixel ??? one more needed
        int    ddp_clipxma = rect.x+rect.width;                      // upper X pixel ??? one more needed
        double dd_clipxmi  = p2X(ddp_clipxmi, ddp_cxmi, ddp_cxma);   // lower frequency
        double dd_clipxma  = p2X(ddp_clipxma, ddp_cxmi, ddp_cxma);   // upper frequency
        int    ddp_clipymi = rect.y;                                 // lower Y pixel
        int    ddp_clipyma = rect.y+rect.height-1;                   // upper Y pixel

        // axis and ticks
        // in-clip area not taken into account
        // X data axis
        ddp_y0 = ddp_cyma+ddp_gap;
        ddp_y1 = ddp_y0+ddp_gap;
        ddp_y2 = ddp_y1+(int) Math.round(dd_fontsz*1.2);             // text location
        g2d.drawLine( ddp_cxmi, ddp_y0,
                      ddp_cxma, ddp_y0);                             // draw axis
        for( int i = 0; i<=(int) bcadreX[2]; i++) {                  // for each tick mark
            ddp_x0 = ip_X(bcadreX[0]+i*bcadreX[1], ddp_cxmi, ddp_cxma);
            g2d.drawLine(ddp_x0, ddp_y0,
                         ddp_x0, ddp_y1);                            // draw tick mark
        }
        dd_str = ""+(float) bcadreX[0];
        rectS  = fm.getStringBounds(dd_str,g2d);
        ddp_x0 = Math.max(0, (int) Math.round( ip_X(bcadreX[0], ddp_cxmi, ddp_cxma)-rectS.getWidth()/2. ) );                                            // center value of 1st tick in limits
        g2d.drawString(dd_str,
                       ddp_x0, ddp_y2);
        dd_str = "("+(float) bcadreX[1]+")";
        rectS  = fm.getStringBounds(dd_str,g2d);
        ddp_x0 = (int) Math.round( ip_X((bcadreX[0]+bcadreX[3])/2., ddp_cxmi, ddp_cxma)-rectS.getWidth()/2. );                                          // center interval value
        g2d.drawString(dd_str,
                       ddp_x0, ddp_y2);
        dd_str = ""+(float) bcadreX[3];
        rectS  = fm.getStringBounds(dd_str,g2d);
        ddp_x0 = Math.min( (int) Math.round( ip_X(bcadreX[3], ddp_cxmi, ddp_cxma)-rectS.getWidth()/2. ), (int) Math.round(ddp_wxma-rectS.getWidth()));  // center value of last tick in limits
        g2d.drawString(dd_str,
                       ddp_x0, ddp_y2);

        // Y data axis
        ddp_x0 = ddp_cxmi-ddp_gap;
        ddp_x1 = ddp_x0-ddp_gap;
        g2d.drawLine(ddp_x0, ddp_cyma,
                     ddp_x0, ddp_cymi );                             // draw axis
        for( int i = 0; i<=(int) bcadreY[2]; i++) {                  // for each tick mark
            ddp_y0 = ip_Y(bcadreY[0]+i*bcadreY[1], ddp_cymi, ddp_cyma);
            g2d.drawLine(ddp_x0, ddp_y0,
                         ddp_x1, ddp_y0);                            // draw tick
        }
        ddp_x2 = ddp_x1-(int) Math.round(dd_fontsz*.4);              // text location
        dd_str = ""+ (float) bcadreY[0];
        rectS  = fm.getStringBounds(dd_str,g2d);
        ddp_y0 = Math.min( ddp_y1, (int) Math.round( ip_Y(bcadreY[0], ddp_cymi, ddp_cyma)+rectS.getWidth()/2. ) );                                                // center value of 1st tick in limits
        drawRotString(dd_str,
                      ddp_x2, ddp_y0);
        dd_str = "("+(float) bcadreY[1]+")";
        rectS  = fm.getStringBounds(dd_str,g2d);
        ddp_y0 = (int) Math.round( ip_Y((bcadreY[0]+bcadreY[3])/2., ddp_cymi, ddp_cyma)+rectS.getWidth()/2. );                                                   // center interval value
        drawRotString(dd_str,
                      ddp_x2, ddp_y0);
        dd_str = ""+ (float) bcadreY[3];
        rectS  = fm.getStringBounds(dd_str,g2d);
        ddp_y0 = Math.max( (int) Math.round( ip_Y(bcadreY[3], ddp_cymi, ddp_cyma)+rectS.getWidth()/2. ), (int) Math.round(ddp_cymi-ddp_gap/2+rectS.getWidth()));  //  center value of last tick in limits
        drawRotString(dd_str,
                      ddp_x2, ddp_y0);

        // Y pred-as-stick axis
        if( nbpredasf != 0 ) {
            ddp_x0 = ddp_cxma+ddp_gap;
            ddp_x1 = ddp_x0+ddp_gap;
            g2d.drawLine(ddp_x0, ddp_cyma,
                         ddp_x0, ddp_cymi );                         // draw axis
            for( int i = 0; i<=(int) bcadrepasY[2]; i++) {           // for each tick mark
                ddp_y0 = ip_pasY(bcadrepasY[0]+i*bcadrepasY[1], ddp_cymi, ddp_cyma);
                g2d.drawLine(ddp_x0, ddp_y0,
                             ddp_x1, ddp_y0);                        // draw tick
            }
            ddp_x2 = ddp_x1+(int) Math.round(dd_fontsz*1.2);         // text location
            dd_str = ""+ (float) bcadrepasY[0];
            rectS  = fm.getStringBounds(dd_str,g2d);
            ddp_y0 = Math.min( ddp_y1, (int) Math.round( ip_pasY(bcadrepasY[0], ddp_cymi, ddp_cyma)+rectS.getWidth()/2. ) );                                      // center value of 1st tick in limits
            drawRotString(dd_str,
                          ddp_x2, ddp_y0);
            dd_str = "("+(float) bcadrepasY[1]+")";
            rectS  = fm.getStringBounds(dd_str,g2d);
            ddp_y0 = (int) Math.round( ip_pasY((bcadrepasY[0]+bcadrepasY[3])/2., ddp_cymi, ddp_cyma)+rectS.getWidth()/2. );                                       // center interval value
            drawRotString(dd_str,
                          ddp_x2, ddp_y0);
            dd_str = ""+ (float) bcadrepasY[3];
            rectS  = fm.getStringBounds(dd_str,g2d);
            ddp_y0 = Math.max( (int) Math.round( ip_pasY(bcadrepasY[3], ddp_cymi, ddp_cyma)+rectS.getWidth()/2. ), (int) (ddp_cymi-ddp_gap/2+rectS.getWidth()));  //  center value of last tick in limits
            drawRotString(dd_str,
                          ddp_x2, ddp_y0);
        }

        // for each data
        if( (ddp_clipxma >= ddp_cxmi && ddp_clipxmi <= ddp_cxma) &&
            (ddp_clipyma >= ddp_cymi && ddp_clipymi <= ddp_cyma)    ) {  // in in-clip area

            // define out-clip area
            g2d.setClip( ddp_cxmi,   ddp_cymi,
                         ddp_cwidth, ddp_cheight );

            for( int j=0; j<nbdf; j++ ) {                            // for each data file
                if( dfready[j] ) {
                    if( show[j] ) {                                  // if show
                        g2d.setColor(coul[j - nbcoul*(j/nbcoul)]);   // color
                        // spectrum
                        if    ( ntfile[j].equals("spectrum") ) {
                            int ci;
                            int cim1;
                            int cixdeb;
                            int cixfin;
                            //
                            boolean reduce;
                            reduce = use_red[j] && screen;
                            if( reduce ) {
                                // use reduced data
                                cixdeb = ixdeb_red[j];
                                cixfin = ixfin_red[j];
                            }
                            else {
                                // use raw data (printing or no reduction)
                                cixdeb = ixdeb[j];
                                cixfin = ixfin[j];
                            }
                            //
                            if( cixdeb <= cixfin ) {                 // something to draw
                                if( yreverse[j] ) {                  // Y reverse
                                    for( int i=cixdeb+1; i<=cixfin; i++ ) {
                                        if( reduce ) {
                                            ci   = i_red[j][i];
                                            cim1 = i_red[j][i-1];
                                        }
                                        else {
                                            ci = i;
                                            cim1 = i-1;
                                        }
                                        if( x[j][ci] < dd_clipxmi ) {    // no need to draw
                                            continue;
                                        }
                                        if( x[j][cim1] > dd_clipxma ) {  // end of draw
                                            break;
                                        }
                                        ddp_x0 = ip_X(x[j][cim1], ddp_cxmi, ddp_cxma);
                                        ddp_x1 = ip_X(x[j][ci],   ddp_cxmi, ddp_cxma);
                                        ddp_y0 = ip_Y(ayrev-y[j][cim1], ddp_cymi, ddp_cyma);
                                        ddp_y1 = ip_Y(ayrev-y[j][ci],   ddp_cymi, ddp_cyma);
                                        g2d.drawLine( ddp_x0, ddp_y0, ddp_x1, ddp_y1);
                                    }
                                }
                                else {                                   // no Y reverse
                                    for( int i=cixdeb+1; i<=cixfin; i++ ) {
                                        if( reduce ) {
                                            ci   = i_red[j][i];
                                            cim1 = i_red[j][i-1];
                                        }
                                        else {
                                            ci = i;
                                            cim1 = i-1;
                                        }
                                        if( x[j][ci] < dd_clipxmi ) {
                                            continue;
                                        }
                                        if( x[j][cim1] > dd_clipxma ) {
                                            break;
                                        }
                                        ddp_x0 = ip_X(x[j][cim1], ddp_cxmi, ddp_cxma);
                                        ddp_x1 = ip_X(x[j][ci],   ddp_cxmi, ddp_cxma);
                                        ddp_y0 = ip_Y(y[j][cim1], ddp_cymi, ddp_cyma);
                                        ddp_y1 = ip_Y(y[j][ci],   ddp_cymi, ddp_cyma);
                                        g2d.drawLine( ddp_x0, ddp_y0, ddp_x1, ddp_y1);
                                    }
                                }
                            }
                        }
                        // stick
                        else if( ntfile[j].equals("stick") ) {
                            if( yreverse[j] ) {                          // Y reverse
                                for( int i=ixdeb[j]; i<=ixfin[j]; i++ ) {
                                    if( x[j][i] < dd_clipxmi ) {
                                        continue;
                                    }
                                    if( x[j][i] > dd_clipxma ) {
                                        break;
                                    }
                                    ddp_x0 = ip_X(x[j][i], ddp_cxmi, ddp_cxma);
                                    ddp_x1 = ddp_x0;
                                    ddp_y0 = ip_Y(ayrev-         yshifted[j],  ddp_cymi, ddp_cyma);
                                    ddp_y1 = ip_Y(ayrev-(y[j][i]+yshifted[j]), ddp_cymi, ddp_cyma);
                                    g2d.drawLine( ddp_x0, ddp_y0, ddp_x1, ddp_y1);
                                }
                            }
                            else {                                       // no Y reverse
                                for( int i=ixdeb[j]; i<=ixfin[j]; i++ ) {
                                    if( x[j][i] < dd_clipxmi ) {
                                        continue;
                                    }
                                    if( x[j][i] > dd_clipxma ) {
                                        break;
                                    }
                                    ddp_x0 = ip_X(x[j][i], ddp_cxmi, ddp_cxma);
                                    ddp_x1 = ddp_x0;
                                    ddp_y0 = ip_Y(        yshifted[j], ddp_cymi, ddp_cyma);
                                    ddp_y1 = ip_Y(y[j][i]+yshifted[j], ddp_cymi, ddp_cyma);
                                    g2d.drawLine( ddp_x0, ddp_y0, ddp_x1, ddp_y1);
                                }
                            }
                        }
                        // peakas
                        else if( ntfile[j].equals("peakas") ) {
                            if( yreverse[j] ) {                          // Y reverse
                                for( int i=ixdeb[j]; i<=ixfin[j]; i++ ) {
                                    if( x[j][i] < dd_clipxmi ) {
                                        continue;
                                    }
                                    if( x[j][i] > dd_clipxma ) {
                                        break;
                                    }
                                    ddp_x0 = ip_X(x[j][i], ddp_cxmi, ddp_cxma);
                                    ddp_x1 = ddp_x0;
                                    ddp_y0 = ip_Y(1.     +yshifted[j], ddp_cymi, ddp_cyma);
                                    ddp_y1 = ip_Y(y[j][i]+yshifted[j], ddp_cymi, ddp_cyma);
                                    g2d.drawLine( ddp_x0, ddp_y0, ddp_x1, ddp_y1);
                                }
                            }
                            else {                                       // no Y reverse
                                for( int i=ixdeb[j]; i<=ixfin[j]; i++ ) {
                                    if( x[j][i] < dd_clipxmi ) {
                                        continue;
                                    }
                                    if( x[j][i] > dd_clipxma ) {
                                        break;
                                    }
                                    ddp_x0 = ip_X(x[j][i], ddp_cxmi, ddp_cxma);
                                    ddp_x1 = ddp_x0;
                                    ddp_y0 = ip_Y(       +yshifted[j], ddp_cymi, ddp_cyma);
                                    ddp_y1 = ip_Y(y[j][i]+yshifted[j], ddp_cymi, ddp_cyma);
                                    g2d.drawLine( ddp_x0, ddp_y0, ddp_x1, ddp_y1);
                                }
                            }
                        }
                        // pred-as-stick
                        else {
                            if( yreverse[j] ) {                          // Y reverse
                                for( int i=ixdeb[j]; i<=ixfin[j]; i++ ) {
                                    if( x[j][i] < dd_clipxmi ) {
                                        continue;
                                    }
                                    if( x[j][i] > dd_clipxma ) {
                                        break;
                                    }
                                    ddp_x0 = ip_X(x[j][i], ddp_cxmi, ddp_cxma);
                                    ddp_x1 = ddp_x0;
                                    ddp_y0 = ip_pasY(apasyrev-         yshifted[j],  ddp_cymi, ddp_cyma);
                                    ddp_y1 = ip_pasY(apasyrev-(y[j][i]+yshifted[j]), ddp_cymi, ddp_cyma);
                                    g2d.drawLine( ddp_x0, ddp_y0, ddp_x1, ddp_y1);
                                }
                            }
                            else {                                       // no Y reverse
                                for( int i=ixdeb[j]; i<=ixfin[j]; i++ ) {
                                    if( x[j][i] < dd_clipxmi ) {
                                        continue;
                                    }
                                    if( x[j][i] > dd_clipxma ) {
                                        break;
                                    }
                                    ddp_x0 = ip_X(x[j][i], ddp_cxmi, ddp_cxma);
                                    ddp_x1 = ddp_x0;
                                    ddp_y0 = ip_pasY(        yshifted[j], ddp_cymi, ddp_cyma);
                                    ddp_y1 = ip_pasY(y[j][i]+yshifted[j], ddp_cymi, ddp_cyma);
                                    g2d.drawLine( ddp_x0, ddp_y0, ddp_x1, ddp_y1);
                                }
                            }
                        }
                    }
                }
            }
            // local simulation
            if( locsim ) {
                g2d.setColor(Color.RED);
                for( int i=1; i<lsx.length; i++ ) {                  // for each point
                    if( lsx[i] < dd_clipxmi ) {                      // no need to draw
                        continue;
                    }
                    if( lsx[i-1] > dd_clipxma ) {                    // end of draw
                        break;
                    }
                    ddp_x0 = ip_X(lsx[i-1], ddp_cxmi, ddp_cxma);
                    ddp_x1 = ip_X(lsx[i],   ddp_cxmi, ddp_cxma);
                    ddp_y0 = ip_Y(lsy[i-1], ddp_cymi, ddp_cyma);
                    ddp_y1 = ip_Y(lsy[i],   ddp_cymi, ddp_cyma);
                    g2d.drawLine( ddp_x0, ddp_y0, ddp_x1, ddp_y1);
                }
            }
        }

        // exp/pred area
        if( (ddp_clipxma >= ddp_cxmi && ddp_clipxmi <= ddp_cxma) ) {
            // define out-clip area
            g2d.setClip( ddp_cxmi,            ddp_epymi,
                         ddp_cxma-ddp_cxmi+1, ddp_epyma-ddp_epymi+1 );
            g2d.setColor(Color.BLACK);                               // black

            int cp_clipxmi;                                          // current in-clip X min
            int cp_clipxma;                                          // current in-clip X max
            int ddp_ovxmi;                                           // oval X min
            // exp
            if( (ddp_clipyma >= ddp_epymi && ddp_clipymi <= ddp_yatma) ) {   // in in-clip area
                cp_clipxmi = ddp_clipxmi;                            // current in-clip X min
                cp_clipxma = ddp_clipxma;                            // current in-clip X max
                if( efready ) {
                    // exp  file loaded
                    if( ixexp >= 0 ) {
                        // exp  selected
                        double xexp = efx[ixexp];
                        if( xexp >= bxmi &&
                            xexp <= bxma ) {
                            // in window
                            ddp_ovxmi = ip_X(xexp, ddp_cxmi, ddp_cxma)-ddp_ovray;
                            if( ddp_ovxmi+ddp_ovwidth >= ddp_clipxmi &&
                                ddp_ovxmi           <= ddp_clipxma    ) {
                                g2d.setColor(Color.RED);
                                g2d.fillOval(ddp_ovxmi, ddp_yatmi, ddp_ovwidth, ddp_yatma-ddp_yatmi+1);  // red oval
                                cp_clipxmi = Math.min(cp_clipxmi, ddp_ovxmi);
                                cp_clipxma = Math.max(cp_clipxma, ddp_ovxmi+ddp_ovwidth);
                            }
                        }
                    }
                    // associated to a data file ?
                    if( efass2 >= 0 ) {
                        // yes
                        g2d.setColor(coul[efass2 -nbcoul*(efass2/nbcoul)]);  // color
                    }
                    else {
                        // no
                        g2d.setColor(Color.BLACK);                   // black
                    }
                    // draw exp
                    String str;
                    for( int i=efixdeb; i<=efixfin; i++ ) {
                        ddp_x0 = ip_X(efx[i], ddp_cxmi, ddp_cxma);
                        if( ddp_x0 < cp_clipxmi ) {
                            continue;
                        }
                        if( ddp_x0 > cp_clipxma ) {
                            break;
                        }
                        // global frequency mark
                        if( ! effasog[i].equals(" ") ) {
                            str = String.valueOf(effasog[i]);
                            rectS = fm.getStringBounds(str, g2d);
                            g2d.drawString(str, (int) Math.round(ddp_x0-rectS.getWidth()/2.), ddp_yfaso);
                        }
                        // global intensity mark
                        if( ! efsasog[i].equals(" ") ) {
                            str = String.valueOf(efsasog[i]);
                            rectS = fm.getStringBounds(str, g2d);
                            g2d.drawString(str, (int) Math.round(ddp_x0-rectS.getWidth()/2.), ddp_ysaso);
                        }
                        // size
                        if( (i/5)*5 == i ) {
                            // big
                            ddp_y2 = ddp_yatmi;
                        }
                        else {
                            // plain
                            ddp_y2 = (ddp_yatmi+ddp_yatma)/2;
                        }
                        g2d.drawLine( ddp_x0, ddp_y2, ddp_x0, ddp_yatma );
                    }
                }
            }
            // pred selection area
            if( (ddp_clipyma >= ddp_yprmi && ddp_clipymi <= ddp_yprma) ) {   // in in-clip area
                cp_clipxmi = ddp_clipxmi;                              // current in-clip X min
                cp_clipxma = ddp_clipxma;                              // current in-clip X max
                if( ip_X(predselx0, ddp_cxmi, ddp_cxma) != ip_X(predselx1, ddp_cxmi, ddp_cxma) ) {  // null zone ignored
                    if( predsel ) {
                        // pred file loaded
                        // pred selection area
                        if( dd_clipxma >= predselx0 && dd_clipxmi <= predselx1 ) {
                            g2d.setColor(Color.LIGHT_GRAY);
                            g2d.fillRect(ip_X(Math.max(predselx0,dd_clipxmi), ddp_cxmi, ddp_cxma), ddp_yprmi,
                                         ip_X(Math.min(predselx1,dd_clipxma), ddp_cxmi, ddp_cxma)-ip_X(Math.max(predselx0,dd_clipxmi), ddp_cxmi, ddp_cxma)+1, ddp_yprma-ddp_yprmi+1);  // pred selection  area
                        }
                        if( nbixpred > 0 ) {
                            // pred selected
                            g2d.setColor(Color.GREEN);
                            for( int i=0; i<nbixpred; i++ ) {
                                ddp_x0 = ip_X(pfx[ixpred[i]], ddp_cxmi, ddp_cxma);
                                if( ddp_x0 < cp_clipxmi-ddp_ovray ) {
                                    continue;
                                }
                                if( ddp_x0 > cp_clipxma+ddp_ovray ) {
                                    break;
                                }
                                // in window
                                if( ixpreds[i] == 1) {
                                    // selected
                                    ddp_ovxmi = ddp_x0-ddp_ovray;
                                    if( ddp_ovxmi+ddp_ovwidth >= ddp_clipxmi &&
                                        ddp_ovxmi           <= ddp_clipxma    ) {
                                        g2d.fillOval(ddp_ovxmi, ddp_yprmi, ddp_ovwidth, ddp_yprma-ddp_yprmi+1); // green oval
                                        cp_clipxmi = Math.min(cp_clipxmi, ddp_ovxmi);
                                        cp_clipxma = Math.max(cp_clipxma, ddp_ovxmi+ddp_ovwidth);
                                    }
                                }
                            }
                            if( ixpredable != -1 ) {
                                g2d.setColor(Color.YELLOW);
                                ddp_x0 = ip_X(pfx[ixpredable], ddp_cxmi, ddp_cxma);
                                if( ddp_x0 >= cp_clipxmi-ddp_sqhw &&
                                    ddp_x0 <= cp_clipxma+ddp_sqhw    ) {
                                    ddp_ovxmi = ddp_x0-ddp_sqhw;
                                    g2d.fillRect(ddp_ovxmi, (ddp_yprmi+ddp_yprma)/2-ddp_sqhw, ddp_sqwidth, ddp_sqwidth); // yellow square
                                    cp_clipxmi = Math.min(cp_clipxmi, ddp_ovxmi);
                                    cp_clipxma = Math.max(cp_clipxma, ddp_ovxmi+ddp_sqwidth);
                                }
                            }
                        }
                    }
                }
                /*
                 * pred file ready
                 */
                if( pfready ) {
                    // show assigned pred
                    g2d.setColor(Color.ORANGE);
                    ddp_y2 = (ddp_yprmi+ddp_yprma)/2-ddp_ovray;
                    for( int i=pfixdeb; i<=pfixfin; i++ ) {
                        ddp_x0 = ip_X(pfx[i], ddp_cxmi, ddp_cxma);
                        if( ddp_x0 < cp_clipxmi-ddp_ovray ) {
                            continue;
                        }
                        if( ddp_x0 > cp_clipxma+ddp_ovray ) {
                            break;
                        }
                        if( pfiexpa[i] >= 0 ) {
                            ddp_ovxmi = ddp_x0-ddp_ovray;
                            g2d.fillOval(ddp_ovxmi, ddp_y2, ddp_ovwidth, ddp_ovwidth);  // orange disc
                            cp_clipxmi = Math.min(cp_clipxmi, ddp_ovxmi);
                            cp_clipxma = Math.max(cp_clipxma, ddp_ovxmi+ddp_ovwidth);
                        }
                    }
                }
                /*  exp selected
                 *  and
                 *  exp and pred file ready
                 */
                if( ixexp >= 0 &&
                    efready    &&
                    pfready       ) {
                    // show exp assigned pred
                    g2d.setColor(Color.RED);
                    ddp_y2 = (ddp_yprmi+ddp_yprma)/2-ddp_ovray;
                    for( int k=0; k<efejsynpt[ixexp].size(); k++ ) {    // for each assignment of selected exp
                        cejpt = (ExpJsynPoint)efejsynpt[ixexp].get(k);  // get its ExpJsynPoint
                        int ipreda = cejpt.getIpred();               // get pointed pred index
                        if( ipreda >= 0 ) {                          // if defined
                            double xpred = pfx[ipreda];              // its frequency
                            if( xpred >= bxmi &&
                                xpred <= bxma ) {                    // if in the window
                                ddp_x0 = ip_X(xpred, ddp_cxmi, ddp_cxma);
                                if( ddp_x0 >= cp_clipxmi-ddp_ovray &&
                                    ddp_x0 <= cp_clipxma+ddp_ovray    ) {
                                    ddp_ovxmi = ddp_x0-ddp_ovray;
                                    g2d.fillOval(ddp_ovxmi, ddp_y2, ddp_ovwidth, ddp_ovwidth);  // red disc
                                    cp_clipxmi = Math.min(cp_clipxmi, ddp_ovxmi);
                                    cp_clipxma = Math.max(cp_clipxma, ddp_ovxmi+ddp_ovwidth);
                                }
                            }
                        }
                    }
                }
                // pred
                if( pfready ) {                                      // pred file ready
                    // associated to a data file ?
                    if( pfass2 >= 0 ) {
                        // yes
                        g2d.setColor(coul[pfass2 - nbcoul*(pfass2/nbcoul)]);  // color
                    }
                    else {
                        // no
                        g2d.setColor(Color.BLACK);                   // black
                    }
                    // draw pred
                    ddp_y2 = (ddp_yprmi+ddp_yprma)/2;
                    for( int i=pfixdeb; i<=pfixfin; i++ ) {
                        ddp_x0 = ip_X(pfx[i], ddp_cxmi, ddp_cxma);
                        if( ddp_x0 < cp_clipxmi ) {
                            continue;
                        }
                        if( ddp_x0 > cp_clipxma ) {
                            break;
                        }
                        // size
                        if( (pfj[i]/2)*2 == pfj[i] ) {
                            // down
                            ddp_y0 = ddp_y2;
                            ddp_y1 = ddp_yprma;
                        }
                        else {
                            // up
                            ddp_y0 = ddp_yprmi;
                            ddp_y1 = ddp_y2;
                        }
                        g2d.drawLine( ddp_x0, ddp_y0, ddp_x0, ddp_y1);
                    }
                }
            }
        }
        // draw area frame
        g2d.setClip(null);
        g2d.setColor(Color.BLACK);                                   // black
        g2d.drawRect( ddp_cxmi, ddp_epymi, ddp_cxma-ddp_cxmi, ddp_epyma-ddp_epymi);

        // vertical bar
        if( showbar ) {
            // show it
            // in-clip area not taken into account
            int ddp_xbar = ip_X(xbar, ddp_cxmi, ddp_cxma);           // pixel X position
            if( ddp_xbar >= ddp_cxmi &&
                ddp_xbar <= ddp_cxma    ) {
                g2d.setColor(Color.RED);                             // color
                g2d.drawLine( ddp_xbar, ddp_epymi+ddp_gap/2, ddp_xbar, ddp_cyma+ddp_gap/2 );  // bar
            }
        }
        // zoom or shift to draw
        if( zoom || shift ) {
            // in-clip area not taken into account
            g2d.setColor(Color.BLACK);                               // black
            if( zoomx || shiftx ) {                                  // X zoom/shift
                g2d.drawRect((int) Math.min(p_zx0,p_zx1),   ddp_epymi+ddp_gap/2,
                             (int) Math.abs(p_zx1-p_zx0)+1, ddp_cyma-ddp_epymi);  // zoom area
            }
            if( zoomy || zoompasy || shifty ) {                      // Y zoom/pred-as-stick Y zoom/shift
                g2d.drawRect(ddp_cxmi-ddp_gap/2, (int) Math.min(p_zy0,p_zy1),
                             ddp_cwidth+ddp_gap, (int) Math.abs(p_zy1-p_zy0));  // zoom area
            }
        }
    }

    // convert X from double to pixel
    private int ip_X( double x, int cup_cxmi, int cup_cxma ) {

        double cup_cwidth = cup_cxma-cup_cxmi;
        return (int) Math.round(cup_cxmi+cup_cwidth*((x-bxmi)/bxdelta));
    }
    // convert X from pixel to double
    private double p2X( int p_x, int cup_cxmi, int cup_cxma ) {

        double cup_cwidth = cup_cxma-cup_cxmi;
        return bxmi+bxdelta*(p_x-cup_cxmi)/cup_cwidth;
    }
    // convert Y from double to pixel
    private int ip_Y( double y, int cup_cymi, int cup_cyma ) {

        double cup_cheight = cup_cyma-cup_cymi;
        return (int) Math.round(cup_cyma-cup_cheight*((y-bymi)/bydelta));
    }
    // convert Y from pixel to double
    private double p2Y( int p_y, int cup_cymi, int cup_cyma ) {

        double cup_cheight = cup_cyma-cup_cymi;
        return bymi+bydelta*(cup_cyma-p_y)/cup_cheight;
    }
    // convert pasY from double to pixel
    private int ip_pasY( double y, int cup_cymi, int cup_cyma ) {

        double cup_cheight = cup_cyma-cup_cymi;
        return (int) Math.round(cup_cyma-cup_cheight*((y-bpasymi)/bpasydelta));
    }
    // convert pasY from pixel to double
    private double p2pasY( int p_y, int cup_cymi, int cup_cyma ) {

        double cup_cheight = cup_cyma-cup_cymi;
        return bpasymi+bpasydelta*(cup_cyma-p_y)/cup_cheight;
    }
    // write a string along Y axis
    private void drawRotString( String s, int x, int y ) {

        AffineTransform at = g2d.getTransform();
        g2d.rotate(-Math.PI/2., x, y);
        g2d.drawString(s, x, y);
        //g2d.rotate( Math.PI/2., x, y);
        g2d.setTransform( at );
    }

    /**
     * Printing status.
     */
    public boolean getPrinting() {

        return printing;
    }

/////////////////////////////////////////////////////////////////////////////

    // mouse events management

    /**
     * Mouse button pressed.
     * <br> IN EXPERIMENT/PREDICTION AREA :
     * <br> left  button : experiment/prediction selection
     * <br> right button : assignment popup menu
     * <br>
     * <br> IN DATA DRAWING AREA :
     * <br> left  button : vertical bar capture OR zoom/shift starting point
     * <br> right button : data popup menu
     */
    public void mousePressed(MouseEvent mevt) {

        if( nbdf == 0 ) {
            // no data file
            return;
        }

        double p_x0 = mevt.getX();                                   // store X
        double p_y0 = mevt.getY();                                   // store Y

        // zoom starting point limited to frame
        p_zx0 = p_x0;
        p_zy0 = p_y0;
        if     ( p_x0 < p_cxmi-p_gap/2 ) {
            p_zx0 = p_cxmi-p_gap/2;
        }
        else if( p_x0 > p_cxma+p_gap/2 ) {
            p_zx0 = p_cxma+p_gap/2;
        }
        if     ( p_y0 < p_cymi-p_gap/2 ) {
            p_zy0 = p_cymi-p_gap/2;
        }
        else if( p_y0 > p_cyma+p_gap/2 ) {
            p_zy0 = p_cyma+p_gap/2;
        }
        // will be used to define clip area to redraw
        prevp_zx1 = p_zx0;
        prevp_zy1 = p_zy0;
        //
        if( zoomx || zoomy || zoompasy || shiftx || shifty ) {
            // zoom or shift
            if( ! SwingUtilities.isLeftMouseButton(mevt) ) {
                // not left button
                return;
            }
            // left button
            if( zoomx || zoomy || zoompasy ) {
                zoom = true;
            }
            else if( shiftx || shifty ) {
                shift = true;
            }
            return;
        }
        else {
            // neither zoom nor shift
            if( SwingUtilities.isRightMouseButton(mevt) ) {
                // right button
                // stop pred selection if any
                if( predsel ) {
                    endSelPred();
                }
                if( p_y0 > p_epyma ) {
                    // data popup
                    jpmdata.show(mevt.getComponent(), (int) p_zx0, (int) p_zy0);
                }
                else {
                    // assignment popup
                    if( setJpmAssign() ) {
                        jpmassign.show(mevt.getComponent(), (int) p_zx0, (int) p_zy0);
                    }
                    else {
                        JOptionPane.showMessageDialog(null,"This button is active in this area only if"+lnsep+
                                                           "a peak/experiment file is loaded");
                    }
                }
                return;
            }
            else if( SwingUtilities.isLeftMouseButton(mevt) ) {
                // left button
                if( p_x0 > p_cxmi &&
                    p_x0 < p_cxma ) {
                    // vertical bar
                    if( showbar                              &&
                        Math.abs(p_x0-ip_X(xbar, p_cxmi, p_cxma)) < p_xhalf  &&
                        p_y0 >= p_cymi                       &&
                        p_y0 <= p_cyma                          ) {
                        // vertical bar selected
                        movebar = true;
                    }
                    else {
                        // stop pred selection if any
                        if( predsel ) {
                            endSelPred();
                        }
                        if( efready         &&                       // exp file ready
                            p_y0 > p_epymi  &&
                            p_y0 < p_yatma     ) {                   // mouse in exp  area
                            // assignment selection
                            predsel = false;
                            selExp(p_zx0);
                        }
                        else if( pfready        &&                   // pred file ready
                                 p_y0 > p_yprmi &&
                                 p_y0 < p_epyma    ) {               // mouse in pred area
                            // pred selection starting
                            p_pfx0 = p_x0;                           // pred selection area starting point (pixel)
                            p_pfx1 = p_pfx0;                         // pred selection area ending   point (pixel)
                            predselx0 = p2X( (int) p_pfx0, p_cxmi, p_cxma );  // pred selection area starting point (double)
                            predselx1 = predselx0;                   // pred selection area ending   point (double)
                            predsel = true;                          // pred selection running
                        }
                    }
                }
            }
        }
    }

    /**
     * Mouse moved with button pressed.
     * <br> (zoom/shift area definition)
     */
    public void mouseDragged(MouseEvent mevt) {

        if( nbdf == 0 ) {
            // no data file
            return;
        }

        double p_x1 = mevt.getX();                                   // store X
        double p_y1 = mevt.getY();                                   // store Y
        p_xmouse = (int) p_x1;                                       // store X
        p_ymouse = (int) p_y1;                                       // store Y
        showXY();                                                    // show mouse position

        if( movebar || zoomx || zoomy || zoompasy || shiftx || shifty ) {
            if( ! SwingUtilities.isLeftMouseButton(mevt) ) {
                // not left button
                return;
            }
            p_zx1 = p_x1;                                            // store X (current end of zoom area)
            p_zy1 = p_y1;                                            // store Y (current end of zoom area)
            // limited to the frame
            if     ( p_x1 < p_cxmi-p_gap/2 ) {
                p_zx1 = p_cxmi-p_gap/2;
            }
            else if( p_x1 > p_cxma+p_gap/2 ) {
                p_zx1 = p_cxma+p_gap/2;
            }
            if     ( p_y1 < p_cymi-p_gap/2 ) {
                p_zy1 = p_cymi-p_gap/2;
            }
            else if( p_y1 > p_cyma+p_gap/2 ) {
                p_zy1 = p_cyma+p_gap/2;
            }
            // define clip area to redraw
            if( movebar || zoomx || shiftx ) {
                p_paintx      = (int) Math.min(prevp_zx1, p_zx1);
                p_painty      = p_epymi+p_gap/2;
                p_paintwidth  = (int) (Math.abs(p_zx1-prevp_zx1)+1);
                p_paintheight = (int) p_cyma-p_epymi+1;
                // vertical bar
                if( movebar ) {
                    xbar = p2X((int) p_zx1, p_cxmi, p_cxma);         // store its position
                    xbar = Math.max(xbar, bxmi);
                    xbar = Math.min(xbar, bxma);
                    p_paintx      = p_paintx-p_xhalf;                // due to vertical bar X selection area
                    p_paintwidth  = p_paintwidth+2*p_xhalf;
                }
            }
            else {
                // zoomy/zoompasy/shifty
                p_paintx      = p_cxmi-p_gap/2;
                p_painty      = (int) Math.min(prevp_zy1,p_zy1);
                p_paintwidth  = p_cwidth+p_gap;
                p_paintheight = (int) Math.abs(p_zy1-prevp_zy1)+1;
            }
            myrepaint(p_paintx, p_painty, p_paintwidth, p_paintheight);  // limited along X direction
            prevp_zx1 = p_zx1;
            prevp_zy1 = p_zy1;
        }
        else if( predsel ) {
            // predselection running
            p_pfx1 = p_x1;                                           // pred selection area ending point
            // limited to the frame
            if     ( p_x1 < p_cxmi ) {
                p_pfx1 = p_cxmi;
            }
            else if( p_x1 > p_cxma ) {
                p_pfx1 = p_cxma;
            }
            selPred(Math.min(p_pfx0,p_pfx1), Math.max(p_pfx0,p_pfx1));  // show pred area and selection window
            jfsp.setContent( nbixpred, ixdebpred, ixpreds, ixexp );  // set selection window content
        }
    }

    /**
     * Mouse button released.
     * <br> (zoom/shift ending point)
     */
	public void mouseReleased(MouseEvent mevt) {

		if (nbdf == 0) {
			// no data file
			return;
		}

		double p_x1 = mevt.getX(); // store X
		double p_y1 = mevt.getY(); // store Y

		if (movebar || zoomx || zoomy || zoompasy || shiftx || shifty) {
			// movebar, zoom or shift
			if (!SwingUtilities.isLeftMouseButton(mevt)) {
				// not left button
				return;
			}
			// left button
			p_zx1 = p_x1; // store X (end of zoom area)
			p_zy1 = p_y1; // store Y (end of zoom area)
			// limited to the frame
			if (p_x1 < p_cxmi - p_gap / 2) {
				p_zx1 = p_cxmi - p_gap / 2;
			} else if (p_x1 > p_cxma + p_gap / 2) {
				p_zx1 = p_cxma + p_gap / 2;
			}
			if (p_y1 < p_cymi - p_gap / 2) {
				p_zy1 = p_cymi - p_gap / 2;
			} else if (p_y1 > p_cyma + p_gap / 2) {
				p_zy1 = p_cyma + p_gap / 2;
			}
			//
			if (movebar || zoomx || shiftx) {
				p_painty = p_epymi + p_gap / 2;
				p_paintheight = p_cyma - p_epymi + 1;
				// zoom/shift area has to be not too small
				if (Math.abs(p_zx1 - p_zx0) < 5) {
					if (zoomx || shiftx) {
						p_paintx = (int) Math.min(p_zx0, p_zx1);
						p_paintwidth = (int) Math.abs(p_zx1 - p_zx0) + 1;
						JOptionPane.showMessageDialog(null, "zoom/shift area to small");
						if (zoomx) {
							zoomx = false;
							zoom = false;
						} else if (shiftx) {
							shiftx = false;
							shift = false;
						}
					}
					myrepaint(p_paintx, p_painty, p_paintwidth, p_paintheight); // limited along X direction
					return;
				}
				if (movebar) {
					p_paintx = (int) Math.min(prevp_zx1, p_zx1);
					p_paintwidth = (int) Math.abs(p_zx1 - prevp_zx1) + 1;
					xbar = p2X((int) p_zx1, p_cxmi, p_cxma);
					xbar = Math.max(xbar, bxmi);
					xbar = Math.min(xbar, bxma);
					movebar = false;
					myrepaint(p_paintx, p_painty, p_paintwidth, p_paintheight); // limited along X direction
					return;
				}
			} else {
				// zoomy/zoompasy/shifty
				p_paintx = p_cxmi - p_gap / 2;
				p_paintwidth = p_cwidth + p_gap;
				// zoom/shift area has to be not too small
				if (Math.abs(p_zy1 - p_zy0) < 5) {
					p_painty = (int) Math.min(p_zy0, p_zy1);
					p_paintheight = (int) Math.abs(p_zy1 - p_zy0) + 1;
					JOptionPane.showMessageDialog(null, "zoom/shift area to small");
					if (zoomy) {
						zoomy = false;
						zoom = false;
					} else if (zoompasy) {
						zoompasy = false;
						zoom = false;
					} else if (shifty) {
						shifty = false;
						shift = false;
					}
					myrepaint(p_paintx, p_painty, p_paintwidth, p_paintheight); // limited along Y direction
					return;
				}
			}
			//
			if (zoomx) {
				// X zoom
				bxma = bxmi + bxdelta * (Math.max(p_zx0, p_zx1) - p_cxmi) / p_cwidth;
				bxmi = bxmi + bxdelta * (Math.min(p_zx0, p_zx1) - p_cxmi) / p_cwidth;
				bxdelta = bxma - bxmi; // window frequency range
				setXax(); // set X axis characteristics
				zoomx = false; // no x zoom area to define
				zoom = false; // no zoom area to draw
				prevp_wxma = -1; // call to setRedSpect at next repaint
				repaint(); // all
				setUnsave(true);
				return;
			} else if (shiftx) {
				// X shift
				double cshift = bxdelta * (p_zx1 - p_zx0) / p_cwidth;
				for (int i = 0; i < nbxy[shiftedf]; i++) {
					// update shifted data file
					x[shiftedf][i] += cshift;
				}
				defxlim(shiftedf); // define first and last data points in window
				defxlim_red(shiftedf); // define first and last reduced data points in window
				// exp associated to this shifted data file ?
				if (efass2 == shiftedf) {
					// yes, update it
					for (int i = 0; i < efnbxy; i++) {
						efx[i] += cshift;
					}
					efdefxlim(); // define first and last exp points in window
				}
				// pred associated to this shifted data file ?
				if (pfass2 == shiftedf) {
					// yes, update it
					for (int i = 0; i < pfnbxy; i++) {
						pfx[i] += cshift;
					}
					pfdefxlim(); // define first and last pred points in window
				}
				xshifted[shiftedf] += cshift; // store actual shift value
				// set global X limits (including shift)
				sxmi[shiftedf] += cshift;
				sxma[shiftedf] += cshift;
				saxmi = sxmi[0];
				saxma = sxma[0];
				for (int i = 0; i < nbdf; i++) {
					saxmi = Math.min(sxmi[i], saxmi);
					saxma = Math.max(sxma[i], saxma);
				}
				//
				shiftx = false; // no x shift area to define
				shift = false; // no shift area to draw
				repaint(); // all
				setUnsave(true);
				return;
			}
			// Y axis
			if (zoomy) {
				// Y zoom
				byma = bymi + bydelta * (p_cyma - Math.min(p_zy0, p_zy1)) / p_cheight;
				bymi = bymi + bydelta * (p_cyma - Math.max(p_zy0, p_zy1)) / p_cheight;
				bydelta = byma - bymi;
				cadreint(bymi, bydelta, bcadreY); // resize the Y axis
				zoomy = false; // no y zoom area to define
				zoom = false; // no zoom area to draw
			} else if (zoompasy) {
				// pred-as-stick Y zoom
				bpasyma = bpasymi + bpasydelta * (p_cyma - Math.min(p_zy0, p_zy1)) / p_cheight;
				bpasymi = bpasymi + bpasydelta * (p_cyma - Math.max(p_zy0, p_zy1)) / p_cheight;
				bpasydelta = bpasyma - bpasymi;
				cadreint(bpasymi, bpasydelta, bcadrepasY); // resize the pred-as-stick Y axis
				zoompasy = false; // no pred-as-stick y zoom area to define
				zoom = false; // no zoom area to draw
			} else if (shifty) {
				// Y shift
				double cshift;
				if (ntfile[shiftedf].equals("predas")) {
					cshift = bpasydelta * (p_zy0 - p_zy1) / p_cheight; // p2pasY((int) p_zy1)-p2pasY((int) p_zy0)
				} else {
					cshift = bydelta * (p_zy0 - p_zy1) / p_cheight; // p2Y((int) p_zy1)-p2Y((int) p_zy0)
				}
				if (!ntfile[shiftedf].equals("peakas")) {
					if (yreverse[shiftedf]) {
						// Y reverse -> opposit sign
						cshift = -cshift;
					}
				}
				if (ntfile[shiftedf].equals("spectrum")) {
					// shift taken in account in y
					for (int i = 0; i < nbxy[shiftedf]; i++) {
						y[shiftedf][i] += cshift;
					}
				}
				yshifted[shiftedf] += cshift; // store actual shift value
				shifty = false; // no y shift area to define
				shift = false; // no shift area to draw
			}
			// redraw data area
			p_paintx = 0;
			p_painty = p_cymi - p_gap / 2;
			p_paintwidth = p_wxma + 1;
			p_paintheight = p_cheight + p_gap;
			myrepaint(p_paintx, p_painty, p_paintwidth, p_paintheight);
			setUnsave(true);
		} else if (predsel) {
			// prediction selection running
			p_pfx1 = p_x1; // pred selection area ending point
			// limited to the frame
			if (p_x1 < p_cxmi) {
				p_pfx1 = p_cxmi;
			} else if (p_x1 > p_cxma) {
				p_pfx1 = p_cxma;
			}
			if (p_pfx0 == p_pfx1) {
				// null selection area
				predsel = false;
				return;
			}
			selPred(Math.min(p_pfx0, p_pfx1), Math.max(p_pfx0, p_pfx1)); // show pred area and selection window
			if (nbixpred == 0) {
				// no pred point in selection area
				predsel = false;
			} else {
				jfsp.setContent(nbixpred, ixdebpred, ixpreds, ixexp); // set selection window content
				// loc-sim
				if (lsallowed) {
					// test if there is enough pred associated spectrum data points in area
					int i;
					int i0;
					int i1;
					int j = pfass2;
					for (i = ixdeb[j]; i < ixfin[j]; i++) {
						if (x[j][i] >= predselx0) {
							// first point found
							break;
						}
					}
					i0 = Math.min(i, nbxy[j] - 1);
					for (i = i0; i < nbxy[j]; i++) {
						if (x[j][i] > predselx1) {
							// last point exceeded
							break;
						}
					}
					i1 = Math.max(0, i - 1);
					if (i1 - i0 + 1 < 3) { // at least 3 points
						JOptionPane.showMessageDialog(null,
								"Not enough pred associated spectrum data points in selection area");
						endSelPred();
					} else {
						// set pred associated spectrum Ymin and Ymax in area
						// will be used to scale loc-sim to spectrum
						lsmis = y[j][i0];
						lsmas = y[j][i0];
						for (i = i0; i <= i1; i++) {
							double cy = y[j][i];
							if (cy < lsmis) {
								lsmis = cy;
							}
							if (cy > lsmas) {
								lsmas = cy;
							}
						}
						jfsp.setLsBasic(predselx0, predselx1, lsallowed); // set loc-sim basics in JFSelPred
					}
				}
			}
		}
	}

    /**
     * Mouse moved without button pressed.
     * <br> Show its position.
     */
    public void mouseMoved(MouseEvent mevt) {

        p_xmouse = mevt.getX();                                      // store X
        p_ymouse = mevt.getY();                                      // store Y
        showXY();                                                    // show mouse position
    }

    /**
     * Not implemented.
     */
    public void mouseClicked(MouseEvent mevt) {
    }
    /**
     * Not implemented.
     */
    public void mouseEntered(MouseEvent mevt) {
    }
    /**
     * Not implemented.
     */
    public void mouseExited(MouseEvent mevt) {
    }

/////////////////////////////////////////////////////////////////////

    // show mouse position
    private void showXY() {

        if( p_xmouse > p_cxmi &&                                     // inside frame
            p_xmouse < p_cxma &&
            p_ymouse > p_epymi       &&
            p_ymouse < p_cyma ) {
            // X
            jlx.setText(FortranFormat.formFreq(p2X(p_xmouse, p_cxmi, p_cxma)));
            // Y
			if (p_ymouse > p_cymi) {
				// inside the data area
				jly.setText(FortranFormat.formInt(p2Y(p_ymouse, p_cymi, p_cyma))); // intensity in data scale
				if (nbpredasf != 0) {
					jlypredas.setText(FortranFormat.formInt(p2pasY(p_ymouse, p_cymi, p_cyma))); // intensity in
																								// pred-as-stick scale
				}
            }
            else {
                // inside the exp/pred area
                jly.setText("");
                jlypredas.setText("");
            }
        }
        else {
            // ouside frame
            jlx.setText("");
            jly.setText("");
            jlypredas.setText("");
        }
    }

    // select an exp
    private void selExp(double cp_x0) {

        double bxdeb;
        double bxfin;
        bxdeb = p2X((int) cp_x0-p_xhalf, p_cxmi, p_cxma);            // begin choice area
        bxfin = p2X((int) cp_x0+p_xhalf, p_cxmi, p_cxma);            // end   choice area

        int nbep = 0;                                                // nb of selected points
        int nbjmi = 0;                                               // nb of associated jmi
        // select points
        for( int i=efixdeb; i<=efixfin; i++ ) {
            if( efx[i] < bxdeb ) {
                // before begin, next
                continue;
            }
            else if( efx[i] > bxfin ) {
                // after end, stop
                break;
            }
            nbep ++;                                                 // nb of selected points
            nbjmi += efejsynpt[i].size();                            // nb of associated jmi
            if( nbep == 1 ) {
                // 1st point, set begin index
                ixdebexp = i;
            }
            // always set end index
            ixfinexp = i;
            }

        // clean the old popup menu if any
        if( jpmexp != null ) {
            jpmexp.setVisible(false);
            jpmexp = null;
        }
        if( nbep > mxticks ) {
            JOptionPane.showMessageDialog(null,"More then "+mxticks+" selected ticks");
            return;
        }
        // if points are selected
        if( nbep >= 1 ) {
            // create popup menu for final choice
            jpmexp = new JPopupMenu();
            jmiexp = new JMenuItem[nbjmi];                           // for each point and each of its assignments
            int i;

            nbjmi = 0;
            for( int j=0; j<nbep; j++ ) {
                i = ixdebexp+j;
                cejpt = (ExpJsynPoint)efejsynpt[i].get(0);
                cStr = FortranFormat.formFreq(efx[i])+" | "+FortranFormat.formInt(efy[i])+" | "+cejpt.getJsyn();
                jmiexp[nbjmi] = new JMenuItem(cStr);
                jmiexp[nbjmi].setFont( mono14 );
                jmiexp[nbjmi].addActionListener(this);
                jpmexp.add(jmiexp[nbjmi]);
                nbjmi ++;
                int k = efejsynpt[i].size();
                if( k > 1 ) {
                    // more then 1 assignment
                    for( int l=1; l<k; l++ ) {
                        cejpt = (ExpJsynPoint)efejsynpt[i].get(l);
                        jmiexp[nbjmi] = new JMenuItem("                           | "+cejpt.getJsyn());
                        jmiexp[nbjmi].setFont( mono14 );
                        jmiexp[nbjmi].addActionListener(this);
                        jpmexp.add(jmiexp[nbjmi]);
                        nbjmi ++;
                    }
                }
            }
            jpmexp.show(this, (int) p_zx0, (int) p_zy0);             // show popup menu
        }
    }

    // select a pred in the selection area
    private void selPred(double cp_x0, double cp_x1) {

        if( cp_x0 == cp_x1 ) {                                       // null zone ignored
            return;
        }

        int p_paintx0 = ip_X(predselx0, p_cxmi, p_cxma);             // previous pred selection area bounds
        int p_paintx1 = ip_X(predselx1, p_cxmi, p_cxma);

        predselx0 = p2X( (int) cp_x0, p_cxmi, p_cxma );              // present  pred selection area bounds
        predselx1 = p2X( (int) cp_x1, p_cxmi, p_cxma );

        // repaint area
        p_paintx      = Math.min(p_paintx0, ip_X(predselx0, p_cxmi, p_cxma));
        p_paintwidth  = Math.max(p_paintx1, ip_X(predselx1, p_cxmi, p_cxma))-p_paintx+1;
        p_painty      = p_yprmi;
        p_paintheight = p_yprma-p_yprmi+1;

        nbixpred = 0;                                                // nb of selected points
        // select points
        for( int i=pfixdeb; i<=pfixfin; i++ ) {
            if     ( pfx[i] < predselx0 ) {
                // before begin, next
                continue;
            }
            else if( pfx[i] > predselx1 ) {
                // after end, stop
                break;
            }
            nbixpred ++;                                             // nb of selected points
            if( nbixpred == 1 ) {
                // 1st point, set begin index
                ixdebpred = i;
            }
            // always set end index
//            ixfinpred = i;
        }

        // if points are selected
        if( nbixpred > 0 ) {
            // create arrays
            ixpred  = new int[nbixpred];                             // selected pred index
            ixpreds = new int[nbixpred];                             // selected pred status >> -1: nothing(---), 0: already in exp(ASG), 1: selected(SEL)
            for( int j=0; j<nbixpred; j++ ) {                        // for each selected pred
                int ci = ixdebpred+j;                                // full index
                ixpred[j] = ci;                                      // set index
                if( pfiexpa[ci] >= 0 ) {
                    ixpreds[j] = 0;                                  // status assigned
                }
                else {
                    ixpreds[j] = -1;                                 // status unselected
                }
            }
        }
        else {
            // no selected point
            setJta();
        }
        myrepaint(p_paintx, p_painty, p_paintwidth, p_paintheight);  // limited to pred selection area
        return;
    }

/////////////////////////////////////////////////////////////////////

    // define frequency and intensity global marks
    // ? if some mark differs
    // the one mark if all marks are same
    private void setAsog(int ci) {

        String gxaso;
        boolean plus;
        boolean minus;
        if( efnbxy != 0 ) {                                          // if exp file ready
        int nbass = efejsynpt[ci].size();                            // nb of assignments
            plus  = false;
            minus = false;
            for( int i=0; i<nbass; i++ ) {                           // test all
                cejpt = (ExpJsynPoint)efejsynpt[ci].get(i);
                gxaso = cejpt.getFaso();
				if (gxaso.equals("-")) {
					minus = true;
				} else if (gxaso.equals("+")) {
					plus = true;
				}
            }
            if( plus && minus ) {
                effasog[ci] = "?";
            }
            else {
                if( plus ) {
                    effasog[ci] = "+";
                }
                else if( minus ) {
                    effasog[ci] = "-";
                }
                else {
                    effasog[ci] = " ";
                }
            }
            plus  = false;
            minus = false;
            for( int i=0; i<nbass; i++ ) {                           // test all others
                cejpt = (ExpJsynPoint)efejsynpt[ci].get(i);
                gxaso = cejpt.getSaso();
                if     ( gxaso.equals(" ") ) {
                }
                else if( gxaso.equals("+") ) {
                    plus  = true;
                }
                else {
                    minus = true;
                }
            }
            if( plus && minus ) {
                efsasog[ci] = "?";
            }
            else {
                if( plus ) {
                    efsasog[ci] = "+";
                }
                else if( minus ) {
                    efsasog[ci] = "-";
                }
                else {
                    efsasog[ci] = " ";
                }
            }
        }
    }

    /**
     * Update TextArea.
     */
    public void setJta() {

        jta.setText("");                                             // empty TextArea
        // exp selected
        if( ixexp >= 0 ) {
            // exp
            String cStrEXASG = efexasg[ixexp];
            if( cStrEXASG.length() == 0 ) {
                cStrEXASG = FortranFormat.bStr(30);                                // EXASG = 30 char
            }
            int k = efejsynpt[ixexp].size();
            for( int l=0; l<k; l++ ) {                               // for each assignment
                // type
                if( l == 0 ) {                                       // first one
                    jta.append("Exp  : ");
                }
                else {
                    jta.append("       ");
                }
                cejpt = ((ExpJsynPoint)efejsynpt[ixexp].get(l));
                // frequency, frequency mark, intensity, intensity mark
                jta.append(FortranFormat.formFreq(efx[ixexp])+" "+cejpt.getFaso()+" "+FortranFormat.formInt(efy[ixexp])+" "+cejpt.getSaso()+" | ");
                // assignment
                if( cejpt.getJsyn().trim().length() != 0 ) {
                    jta.append(cejpt.getJsyn());
                }
                else {
                    jta.append(FortranFormat.bStr(21));                            // jsyn = 21 char
                }
                boolean found = false;
                if( pfready ) {                                      // pred file ready
                    int ipreda = cejpt.getIpred();
                    if( ipreda >= 0 ) {
                        // pred frequency, pred intensity, EXASG, comment
                        jta.append(" ("+FortranFormat.formFreq(pfx[ipreda])+" "+FortranFormat.formInt(pfy[ipreda])+") | "+cStrEXASG+" "+cejpt.getComm()+lnsep);
                        found = true;
                    }
                }
                if( ! found ) {
                    // EXASG, comment
                    jta.append("                            | "+cStrEXASG+" "+cejpt.getComm()+lnsep);
                }
            }
        }
        // pred selected
        if( nbixpred > 0 ) {
            // type, frequency, intensity, assignment
            int nbs =0;
            for( int l=0; l<nbixpred; l++ ) {
                if( ixpreds[l] == 1 ) {
                    // only selected ones
                    if( nbs == 0 ) {
                        jta.append("Pred : ");
                        nbs ++;
                    }
                    else {
                        jta.append("       ");
                    }
                    jta.append(FortranFormat.formFreq(pfx[ixpred[l]])+"   "+FortranFormat.formInt(pfy[ixpred[l]])+"   | "+pfjsyn[ixpred[l]]+lnsep);
                }
            }
        }
    }

/////////////////////////////////////////////////////////////////////

    /**
     * Process popup menu events.
     */
    public void actionPerformed(ActionEvent evt) {

        // NO action while printing
        if( printing ) {
            JOptionPane.showMessageDialog(null,"No action while printing");
        }

        // -> show vertical bar
        if( evt.getSource() == jmishowbar ) {
            if( (evt.getModifiers()&InputEvent.BUTTON3_MASK) != 0 ) {
                // help
                PopFile.show(getName()+fisep+"jmishowbar");
                return;
            }
            if( showbar ) {
                // hide current bar
                myrepaint(ip_X( xbar, p_cxmi, p_cxma ), p_epymi+p_gap/2, 1, p_cyma-p_epymi+1 );  // limited along X direction
            }
            else {
                showbar = true;
                jmihidebar.setEnabled(true);
            }
            xbar = p2X(p_xmouse, p_cxmi, p_cxma);
            xbar = Math.max(xbar, bxmi);
            xbar = Math.min(xbar, bxma);
            myrepaint(ip_X( xbar, p_cxmi, p_cxma ), p_epymi+p_gap/2, 1, p_cyma-p_epymi+1 );  // limited along X direction
            return;
        }

        // -> hide vertical bar
        if( evt.getSource() == jmihidebar ) {
            if( (evt.getModifiers()&InputEvent.BUTTON3_MASK) != 0 ) {
                // help
                PopFile.show(getName()+fisep+"jmihidebar");
                return;
            }
            showbar = false;
            jmihidebar.setEnabled(false);
            myrepaint(ip_X( xbar, p_cxmi, p_cxma ), p_epymi+p_gap/2, 1, p_cyma-p_epymi+1 );  // limited along X direction
            return;
        }

        // -> X zoom
        if( evt.getSource() == jmizoomx ) {
            if( (evt.getModifiers()&InputEvent.BUTTON3_MASK) != 0 ) {
                // help
                PopFile.show(getName()+fisep+"jmizoomx");
                return;
            }
            zoomx = true;
            zoom  = false;
            return;
        }

        // -> Y zoom
        if( evt.getSource() == jmizoomy ) {
            if( (evt.getModifiers()&InputEvent.BUTTON3_MASK) != 0 ) {
                // help
                PopFile.show(getName()+fisep+"jmizoomy");
                return;
            }
            zoomy = true;
            zoom  = false;
            return;
        }

        // -> pred-as-stick Y zoom
        if( evt.getSource() == jmizoompasy ) {
            if( (evt.getModifiers()&InputEvent.BUTTON3_MASK) != 0 ) {
                // help
                PopFile.show(getName()+fisep+"jmizoompasy");
                return;
            }
            zoompasy = true;
            zoom     = false;
            return;
        }

        // -> X rescale
        for( int i=0; i<nbscalec; i++ ) {
            if( evt.getSource() == jmivalexpx[i] ) {
                if( (evt.getModifiers()&InputEvent.BUTTON3_MASK) != 0 ) {
                    // help
                    PopFile.show(getName()+fisep+"jmivalexpx");
                    return;
                }
                repropx(i);                                          // apply scaling choice
                repaint();                                           // all
                return;
            }
        }

        // -> Y rescale
        for( int i=0; i<nbscalec; i++ ) {
            if( evt.getSource() == jmivalexpy[i] ) {
                if( (evt.getModifiers()&InputEvent.BUTTON3_MASK) != 0 ) {
                    // help
                    PopFile.show(getName()+fisep+"jmivalexpy");
                    return;
                }
                repropy(i);                                          // apply scaling choice
                myrepaint(0, p_cymi-p_gap, p_wxma+1, p_cheight+3*p_gap);  // Y axis and all data area
                return;
            }
        }

        // -> pred-as-stick Y rescale
        for( int i=0; i<nbscalec; i++ ) {
            if( evt.getSource() == jmivalexppasy[i] ) {
                if( (evt.getModifiers()&InputEvent.BUTTON3_MASK) != 0 ) {
                    // help
                    PopFile.show(getName()+fisep+"jmivalexppasy");
                    return;
                }
                reproppasy(i);                                       // apply scaling choice
                myrepaint(0, p_cymi-p_gap, p_wxma+1, p_cheight+3*p_gap);  // Y axis and all data area
                return;
            }
        }

        // -> restore X scale
        if( evt.getSource() == jmirestorex ) {
            if( (evt.getModifiers()&InputEvent.BUTTON3_MASK) != 0 ) {
                // help
                PopFile.show(getName()+fisep+"jmirestorex");
                return;
            }
            bxmi    = (bxmi+bxma-axdelta)/2.;                        // let the window be X centered
            bxma    = bxmi+axdelta;                                  // full original width
            bxdelta = bxma-bxmi;                                     // window frequency range
            setXax();                                                // set X axis characteristics
            prevp_wxma = -1;                                         // call to setRedSpect at next repaint
            repaint();                                               // all
            setUnsave(true);
            return;
        }

        // -> restore Y scale
        if( evt.getSource() == jmirestorey ) {
            if( (evt.getModifiers()&InputEvent.BUTTON3_MASK) != 0 ) {
                // help
                PopFile.show(getName()+fisep+"jmirestorey");
                return;
            }
            bymi = (bymi+byma-aydelta)/2.;                           // let the window be Y centered
            byma = bymi+aydelta;                                     // full original height
            bydelta = aydelta;
            cadreint(bymi, bydelta, bcadreY);                        // resize the Y axis
            myrepaint(0, p_cymi-p_gap, p_wxma+1, p_cheight+3*p_gap);  // Y axis and all data area
            setUnsave(true);
            return;
        }

        // -> restore pred-as-stick Y scale
        if( evt.getSource() == jmirestorepasy ) {
            if( (evt.getModifiers()&InputEvent.BUTTON3_MASK) != 0 ) {
                // help
                PopFile.show(getName()+fisep+"jmirestorepasy");
                return;
            }
            // full original window
            bpasymi = apasymi;
            bpasyma = apasyma;
            bpasydelta = apasydelta;
            bcadrepasY = (double[]) acadrepasY.clone();              // set Y axis ticks
            myrepaint(0, p_cymi-p_gap, p_wxma+1, p_cheight+3*p_gap);  // Y axis and all data area
            return;
        }

        // exp selection
        if( jpmexp != null ) {
            int nbjmi = 0;                                           // number of jmi
            boolean found = false;                                   // event not found
            for( int j=0; j<ixfinexp-ixdebexp+1; j++ ) {             // for each visible point
                int ixep = ixdebexp+j;                               // current index
                for( int k=0; k<efejsynpt[ixep].size(); k++ ) {      // for each assignment
                    if( evt.getSource() == jmiexp[nbjmi] ) {
                        if( (evt.getModifiers()&InputEvent.BUTTON3_MASK) != 0 ) {
                            // help
                            PopFile.show(getName()+fisep+"jmiexp");
                            return;
                        }
                        // found
                        ixexp = ixep;                                // its index
                        found = true;
                        break;                                       // end this for
                    }
                    // not found
                    nbjmi ++;
                }
                if( found ) {
                    break;                                           // end search
                }
            }
            jpmexp = null;                                           // no more active
            if( found ) {
                myrepaint( p_cxmi, p_epymi, p_cwidth, p_epyma-p_epymi+1);  // all exp/pred area
                setJta();                                            // update TextArea
                return;
            }
        }

        // assignment management
        if( jpmassign != null ) {
            int nbass;
            // unselect exp
            if( jmiunselexp.isEnabled() ) {
                if( evt.getSource() == jmiunselexp ) {
                    if( (evt.getModifiers()&InputEvent.BUTTON3_MASK) != 0 ) {
                        // help
                        PopFile.show(getName()+fisep+"jmiunselexp");
                        return;
                    }
                    ixexp = -1;                                      // no exp selected
                    jpmassign = null;                                // no more active
                    myrepaint( p_cxmi, p_epymi, p_cwidth, p_epyma-p_epymi+1);  // all exp/pred area
                    setJta();                                        // update TextArea
                    return;
                }
            }
            // unassign
            if( jmunass.isEnabled() ) {
                boolean found = false;                               // event not found
                nbass = efejsynpt[ixexp].size();                     // number of assignments
                for( int i=0; i<nbass; i++ ) {                       // for each one
                    if( evt.getSource() == jmiunass[i] ) {
                        // unassign one
                        if( (evt.getModifiers()&InputEvent.BUTTON3_MASK) != 0 ) {
                            // help
                            PopFile.show(getName()+fisep+"jmiunass");
                            return;
                        }
                        // found
                        cejpt = (ExpJsynPoint)efejsynpt[ixexp].get(i);
                        pfiexpa[cejpt.getIpred()] = -1;
                        if( nbass == 1 ) {
                            // only one assignment, empty it
                            cejpt.setFaso(" ");
                            cejpt.setSaso(" ");
                            cejpt.setJsyn( "");
                            cejpt.setIpred(-1);
                            efexasg[ixexp] = "";
                        }
                        else {
                            // suppress it
                            efejsynpt[ixexp].remove(i);
                        }
                        found = true;                                // end search
                        break;
                    }
                }
                if( ! found ) {
                    if( evt.getSource() == jmiunassall ) {
                        // unassign all
                        if( (evt.getModifiers()&InputEvent.BUTTON3_MASK) != 0 ) {
                            // help
                            PopFile.show(getName()+fisep+"jmiunassall");
                            return;
                        }
                        for( int i=nbass-1; i>=0; i-- ) {
                            cejpt = (ExpJsynPoint)efejsynpt[ixexp].get(i);
                            pfiexpa[cejpt.getIpred()] = -1;
                            if( i == 0 ) {
                                // only one assignment, empty it
                                cejpt.setFaso(" ");
                                cejpt.setSaso(" ");
                                cejpt.setJsyn( "");
                                cejpt.setIpred(-1);
                                efexasg[ixexp] = "";
                            }
                            else {
                                // suppress it
                                efejsynpt[ixexp].remove(i);
                            }
                        }
                        found = true;                                // end search
                    }
                }
                if( found ) {
                    jpmassign = null;                                // no more active
                    setAsog(ixexp);                                  // set global frequency and intensity marks
                    myrepaint( p_cxmi, p_epymi, p_cwidth, p_epyma-p_epymi+1);  // all exp/pred area
                    setJta();                                        // update TextArea
                    updateExp(jobplay.getExpFileName());
                    return;
                }
            }
            // set frequency or intensity mark
            if( jmfaso.isEnabled() && jmsaso.isEnabled() ) {
                boolean found = false;
                nbass = efejsynpt[ixexp].size();                     // nb of assignments
                for( int i=0; i<nbass; i++ ) {                       // for each one
                    cejpt = (ExpJsynPoint)efejsynpt[ixexp].get(i);
                    for( int j=0; j<nbxaso; j++ ) {                  // for each possible value of freq/int mark
                        // frequency mark
                        if( evt.getSource() == jmifaso[i][j] ) {
                            if( (evt.getModifiers()&InputEvent.BUTTON3_MASK) != 0 ) {
                                // help
                                PopFile.show(getName()+fisep+"jmifaso");
                                return;
                            }
                            if( cejpt.getSaso().equals(" ") &&
                                xaso[j].equals(" ")            ) {
                                // unallowed
                                JOptionPane.showMessageDialog(null,"at least one mark (frequency or intensity) has to be set");
                            }
                            else {
                                // do it
                                cejpt.setFaso(xaso[j]);
                                found = true;
                            }
                            break;                                   // end this for
                        }
                        // intensity mark
                        if( evt.getSource() == jmisaso[i][j] ) {
                            if( (evt.getModifiers()&InputEvent.BUTTON3_MASK) != 0 ) {
                                // help
                                PopFile.show(getName()+fisep+"jmisaso");
                                return;
                            }
                            if( cejpt.getFaso().equals(" ") &&
                                xaso[j].equals(" ")            ) {
                                // unallowed
                                JOptionPane.showMessageDialog(null,"at least one mark (frequency or intensity) has to be set");
                            }
                            else {
                                // do it
                                cejpt.setSaso(xaso[j]);
                                found = true;
                            }
                            break;                                   // end this for
                        }
                    }
                    if( found ) {
                        break;                                       // end search
                    }
                }
                if( found ) {
                    jpmassign = null;                                // no more active
                    setAsog(ixexp);                                  // set global frequency and intensity marks
                    myrepaint(ip_X(efx[ixexp], p_cxmi, p_cxma)-p_ovray, p_epymi, p_ovwidth, p_yatma-p_epymi+1);  // limited along X direction
                    setJta();                                        // update TextArea
                    updateExp(jobplay.getExpFileName());
                    return;
                }
            }
            // set EXASG
            if( jmexasg.isEnabled() ) {
                for( int i=0; i<nbexasg; i++ ) {                     // for each possible EXASG
                    if( evt.getSource() == jmiexasg[i] ) {
                        if( (evt.getModifiers()&InputEvent.BUTTON3_MASK) != 0 ) {
                            // help
                            PopFile.show(getName()+fisep+"jmiexasg");
                            return;
                        }
                        // found
                        efexasg[ixexp] = exasg[i];
                        setJta();                                    // update TextArea
                        updateExp(jobplay.getExpFileName());
                        return;
                    }
                }
            }
            // set comment
            if( jmcomm.isEnabled() ) {
                nbass = efejsynpt[ixexp].size();                     // nb of assignments
                for( int i=0; i<nbass; i++ ) {                       // for each one
                    if( evt.getSource() == jmicomm[i] ) {
                        if( (evt.getModifiers()&InputEvent.BUTTON3_MASK) != 0 ) {
                            // help
                            PopFile.show(getName()+fisep+"jmicomm");
                            return;
                        }
                        // found
                        cejpt = (ExpJsynPoint)efejsynpt[ixexp].get(i);
                        JFnewText jfnt = new JFnewText( this, cejpt.getComm(), ixexp, i );  // choose the comment
                        jfnt.setVisible(true);
                        return;
                    }
                }
            }
            // add a new  EXASG
            if( evt.getSource() == jmiaddexasg ) {
                if( (evt.getModifiers()&InputEvent.BUTTON3_MASK) != 0 ) {
                    // help
                    PopFile.show(getName()+fisep+"jmiaddexasg");
                    return;
                }
                JFnewText jfnt = new JFnewText( this );              // choose the new EXASG
                jfnt.setVisible(true);
                return;
            }
            // set default EXASG
            for( int i=0; i<nbexasg; i++ ) {
                if( evt.getSource() == jmidefexasg[i] ) {
                    if( (evt.getModifiers()&InputEvent.BUTTON3_MASK) != 0 ) {
                        // help
                        PopFile.show(getName()+fisep+"jmidefexasg");
                        return;
                    }
                    defexasg = exasg[i];
                    return;
                }
            }
        }

    }

/////////////////////////////////////////////////////////////////////

    // set ticks and interval inside the range
    // input  :
    //   xdeb        <- beginning X
    //   delta       <- range
    // output :
    //   cd[0]       <- 1st  X tick  ( >= xdeb)
    //   cd[1]       <- interval     (1Exx, 2Exx ou 5Exx)
    //   cd[2]       <- nb of intervals
    //   cd[3]       <- last X tick  ( <= xdeb+delta)
    //
    // we dont try to set xdeb and delta on an interval multiple
    //
    private void cadreint( double xdeb, double delta ,double[] cd) {

        NumberFormat nf;                                             // to set interval
        DecimalFormat df;                                            // idem
        String cStr;                                                 // current string
        int iexp;                                                    // exponent
        int imant;                                                   // mantissa
        double cxdeb;                                                // 1st X tick
        double cdelta;                                               // current range
        double cint;                                                 // interval
        int    nbint;                                                // nb of intervals inside the range
        double cx;                                                   // variable

        nf = NumberFormat.getNumberInstance(Locale.US);              // ask a plain format
        df = (DecimalFormat)nf;                                      // reduce to decimal format
        df.applyPattern(".0E00");                                    // define pattern

        cdelta = Math.abs(delta);                                    // current range
        cStr = df.format(cdelta);                                    // format it
        cx = Double.parseDouble(cStr);                               // reread
        if( cx < cdelta ) {                                          // rounded up
            cdelta = cdelta + Double.parseDouble(".05"+cStr.substring(2));  // fit range
            cStr = df.format(cdelta);
        }
        iexp = Integer.parseInt(cStr.substring(3));                  // exponent
        imant = Integer.parseInt(cStr.substring(1,2));               // mantissa

        // interval = 1, 2 or 5Exx
        if( imant > 5) {
            imant = 1;
            iexp = iexp+1;
        }
        else if (imant > 2) {
            imant = 5;
        }
        cint = imant*Math.pow( 10, iexp-2 );                         // interval

        // 1st X tick
        long l = (long) (xdeb/cint);                                 // nb of intervals in xdeb
        cx = xdeb - l*cint;                                          // difference
        // xdeb positive and interval multiple ?
        if( xdeb > 0. ) {
            if( cx > 0. ) {                                          // no
                l++;
            }
        }
        cxdeb = l*cint;                                              // fit xdeb

        // nb of intervals
        cdelta = xdeb + Math.abs(delta) - cxdeb;
        nbint  = (int) (cdelta/cint);                                // nb of intervals in range

        cd[0] = cxdeb;
        cd[1] = cint;
        cd[2] = nbint;
        cd[3] = cxdeb+nbint*cint;
    }


/////////////////////////////////////////////////////////////////////

    /**
     * Show a progress bar in panel.
     *
     * @param text  waiting message
     */
    public void prepThread(String text) {

        removeAll();                                                 // clean
        setLayout(new BorderLayout());
        JProgressBar progressBar = new JProgressBar();               // create a progress bar
        progressBar.setStringPainted(true);                          // with title
        progressBar.setString(text);                                 // set it
        progressBar.setIndeterminate(true);                          // we dont know how long time
        Box box =Box.createVerticalBox();                            // box
        box.add(Box.createVerticalStrut( (int) (this.getHeight()/2.) ));  // at the middle of the window
        box.add(progressBar);                                        // add JProgressBar
        add(box,"North");                                            // add box
        revalidate();
    }

    /**
     * Redraw panel after thread.
     */
    public void endThread() {

        removeAll();                                                 // clean
        repaint();                                                   // all
    }

/////////////////////////////////////////////////////////////////////

    /**
     * Set experiment data and redraw panel.
     *
     * @param cexpf experiment file
     */
	@SuppressWarnings("unchecked")
	public void setExp(ExpFile cexpf) {

		efready = false; // exp file not ready
		expf = cexpf; // exp file
		efnbxy = cexpf.getNbxy(); // nb of exp data
		efx = new double[efnbxy]; // frequency
		efx = cexpf.getX();
		effasog = new String[efnbxy]; // global frequency mark
		efy = new double[efnbxy]; // intensity
		efy = cexpf.getY();
		efsasog = new String[efnbxy]; // global intensity mark
		efsdobs = new String[efnbxy]; // frequency and intensity standard deviations
		efsdobs = cexpf.getSdobs();
		efejsynpt = new ArrayList[efnbxy]; // ArrayList of ExpJsynPoint for each data
		efejsynpt = cexpf.getEjsynpt();
		efexasg = new String[efnbxy]; // EXASG
		efexasg = cexpf.getExasg();
		for (int i = 0; i < efnbxy; i++) {
			setAsog(i); // set global frequency mark and global intensity mark
		}
		alexasg = new ArrayList<String>(); // possible EXASG
		for (int i = 0; i < efnbxy; i++) {
			addExasg(efexasg[i]);
		}
		addExasg(PanAff.EXASG_DEFAULT); // default SPVIEW EXASG 30 char.
		setDefexasg(PanAff.EXASG_DEFAULT);

		efdefxlim(); // define first and last exp points in window
		epLink(); // link exp and pred through assignments
		efready = true; // exp file ready
		myrepaint((int) p_cxmi, p_epymi, (int) p_cwidth, p_yatma - p_epymi + 1); // all exp area
	}

    /**
     * Set prediction data and redraw panel.
     *
     * @param cpredf prediction file
     */
    public void setPred(PredFile cpredf) {

        pfready = false;                                             // pred file NOT ready
        pfnbxy  = cpredf.getNbxy();                                  // nb of pred data
        pfx     = new double[pfnbxy];                                // frequency
        pfx     = cpredf.getX();
        pfy     = new double[pfnbxy];                                // intensity
        pfy     = cpredf.getY();
        pfj     = new int[pfnbxy];                                   // J inf
        pfj     = cpredf.getJinf();
        pfjsyn  = new String[pfnbxy];                                // assignment
        pfjsyn  = cpredf.getJsyn();
        pfiexpa = new int[pfnbxy];                                   // index of assigned exp
        pfdefxlim();                                                 // define first and last pred points in window
        if( efnbxy != 0 ) {                                          // reload exp data (may be reduced)
            expf.read();
            setExp(expf);
        }
        else {
            epLink();                                                // link exp and pred through assignments
        }
        jfsp = new JFSelPred(this, (int) (this.getLocationOnScreen().getX()+.75*(p_cxma-p_cxmi)), (int) (this.getLocationOnScreen().getY()+p_epyma+10), pfx, pfy, pfjsyn );
        ixpredable = -1;                                             // NO current selectable pred
        pfready = true;                                              // pred file ready
        myrepaint( (int) p_cxmi, p_yprmi, (int) p_cwidth, p_yprma-p_yprmi+1);  // all pred area
    }

    /**
     * Set experiment associated to a data file.
     *
     * @param cdf index of the data file
     */
    public String expAss2(int cdf) {

        if( cdf > nbdf-1 ) {                                         // bad index
            return "!!! index out of bounds in PanAff.expAss2 ("+cdf+").";
        }
        if( cdf >= 0      &&
            cdf == pfass2    ) {                                     // the same file can't be associated to exp AND pred
            return "This data file is already associated to prediction";
        }
        if( efass2 >= 0 ) {
            // already associated, unshift if necessary
            if( xshifted[efass2] != 0. ) {
                for( int i=0; i<efnbxy; i++ ) {
                    efx[i] -= xshifted[efass2];
                }
            }
        }
        efass2 = cdf;
        if( efass2 >= 0 ) {                                          // not associated to null
            // shift if necessary
            if( xshifted[efass2] != 0. ) {
                for( int i=0; i<efnbxy; i++ ) {
                    efx[i] += xshifted[efass2];
                }
            }
        }
        efdefxlim();                                                 // define first and last exp  points in window
        myrepaint( p_cxmi, p_epymi, p_cwidth, p_yatma-p_epymi+1 );   // exp area
        return "";
    }

    /**
     * Set prediction associated to a data file.
     *
     * @param cdf index of the data file
     */
    public String predAss2(int cdf) {

        if( cdf > nbdf-1 ) {                                         // bad index
            return "!!! index out of bounds in PanAff.predAss2 ("+cdf+").";
        }
        if( cdf >= 0      &&
            cdf == efass2    ) {                                     // the same file can't be associated to exp AND pred
            return "This data file is already associated to experiment";
        }
        if( pfass2 >= 0 ) {
            // already associated, unshift if necessary
            if( xshifted[pfass2] != 0. ) {
                for( int i=0; i<pfnbxy; i++ ) {
                    pfx[i] -= xshifted[pfass2];
                }
            }
        }
        pfass2 = cdf;
        if( pfass2 >= 0 ) {                                          // not associated to null
            // shift if necessary
            if( xshifted[pfass2] != 0. ) {
                for( int i=0; i<pfnbxy; i++ ) {
                    pfx[i] += xshifted[pfass2];
                }
            }
            if( ntfile[pfass2].equals("spectrum") ) {
                lsallowed = true;
            }
        }
        else {
            lsallowed = false;
        }
        pfdefxlim();                                                 // define first and last pred points in window
        myrepaint( p_cxmi, p_yprmi, p_cwidth, p_yprma-p_yprmi+1 );   // pred area
        return "";
    }

    /**
     * Add data file and redraw panel.
     *
     * @param cdataf  data file to add
     */
    public void addData(DataFile cdataf) {

        if( nbdf == 0                         &&
            cdataf.getType().equals("predas")    ) {                 // the 1st loaded data file can't be a prediction file
            System.out.println("Invalid 1st data file in PanAff.addData()"         +lnsep+
                               "the 1st loaded data file can't be a prediction file");
            return;
        }

        // data file NOT ready
        ntfile[nbdf] = cdataf.getType();                             // type
        nbxy[nbdf]   = cdataf.getNbxy();                             // nb of points
        x[nbdf]      = new double[nbxy[nbdf]];                       // frequency
        x[nbdf]      = cdataf.getX();
        y[nbdf]      = new double[nbxy[nbdf]];                       // intensity
        y[nbdf]      = cdataf.getY();
        // default
        yreverse[nbdf] = false;
        xshifted[nbdf] = 0.;
        yshifted[nbdf] = 0.;
        // set frequency limits for this file
        // data have to be sorted according to X
        double cxmi = x[nbdf][0];                                    // lowest  freqency
        double cxma = x[nbdf][nbxy[nbdf]-1];                         // highest frequency
        // set intensity limits for this data file
        double cymi = y[nbdf][0];                                    // starting lowest  intensity
        double cyma = y[nbdf][0];                                    // starting highest intensity
        for( int i=0; i<nbxy[nbdf]; i++ ) {                          // for each point
            if( y[nbdf][i] < cymi ) {
                cymi = y[nbdf][i];                                   // lowest  intensity
            }
            if( y[nbdf][i] > cyma ) {
                cyma = y[nbdf][i];                                   // highest intensity
            }
        }
        if( ! ntfile[nbdf].equals("spectrum") ) {
            // stick files, 0. has to be in
            cymi = Math.min(cymi,0.);
            cyma = Math.max(cyma,0.);
        }
        // set global limits for pred-as-stick and others
        if( nbdf == 0 ) {
            // 1st data file (can't be pred-as-stick)
            axmi = cxmi;
            axma = cxma;
            aymi = cymi;
            ayma = cyma;
            apasyma = 0.;                                            // dummy default value, will be set by 1st pred-as-stick file load
            saxmi = axmi;
            saxma = axma;
        }
        else {
            axmi = Math.min(axmi, cxmi);
            axma = Math.max(axma, cxma);
            if( ntfile[nbdf].equals("predas") ) {                    // pred-as-stick have their own Y settings
                apasymi    = 0.;                                     // base value
                apasyma    = Math.max(apasyma, cyma);                // max value
                apasydelta = apasyma;                                // max range
                apasyrev   = apasyma;                                // reverse value
                cadreint(apasymi, apasydelta, acadrepasY);           // set pred-as-stick Y axis tics
                nbpredasf ++;                                        // nb of pred-as-stick files loaded
                // allow pred-as-stick menus
                jmizoompasy.setEnabled(true);
                jmrescalepasy.setEnabled(true);
                jmirestorepasy.setEnabled(true);
            }
            else {
                aymi = Math.min(aymi, cymi);
                ayma = Math.max(ayma, cyma);
            }
        }
        axdelta = axma-axmi;
        cadreint(axmi, axdelta, acadreX);
        aydelta = ayma-aymi;
        ayrev   = ayma+aymi;
        cadreint(aymi, aydelta, acadreY);
        // set current window limits
        // current window is (re)set to global limits each time a data file is loaded
        bxmi = axmi;                                                 // frequency mini
        bxma = axma;                                                 // frequency maxi
        bxdelta = axdelta;                                           // frequency range
        bcadreX = (double[]) acadreX.clone();                        // set X axis ticks
        if( ntfile[nbdf].equals("predas") ) {                        // pred-as-stick are specifically involved in Y settings
            bpasymi = apasymi;                                       // pred-as-stick intensity mini
            bpasyma = apasyma;                                       // pred-as-stick intensity maxi
            bpasydelta = apasydelta;                                 // pred-as-stick intensity range
            bcadrepasY = (double[]) acadrepasY.clone();              // set pred-as-stick Y axis ticks
        }
        else {
            bymi = aymi;                                             // intensity mini
            byma = ayma;                                             // intensity maxi
            bydelta = aydelta;                                       // intensity range
            bcadreY = (double[]) acadreY.clone();                    // set Y axis ticks
        }
        for( int i=0; i<=nbdf; i++ ) {                               // for each data file
            ixdeb[i] = 0;                                            // first data point in window
            ixfin[i] = nbxy[i]-1;                                    // last  data point in window
            show[nbdf] = true;                                       // show data
        }
        // set global X limits (including shift)
        sxmi[nbdf] = cxmi;
        sxma[nbdf] = cxma;
        //
        nbdf++;                                                      // one more data file
        //
        saxmi = sxmi[0];
        saxma = sxma[0];
        for( int i = 0; i<nbdf; i++ ) {
            saxmi = Math.min(sxmi[i],saxmi);
            saxma = Math.max(sxma[i],saxma);
        }
        //
        efdefxlim();                                                 // define first and last exp  points in window
        pfdefxlim();                                                 // define first and last pred points in window
        prevp_wxma = -1;                                             // call to setRedSpect at next repaint
        dfready[nbdf-1] = true;                                      // data file ready
    }

    /**
     * Reload data file and redraw panel.
     *
     * @param cdataf  data file to reload
     * @param ci      data file index
     */
    public void reloadData(DataFile cdataf, int ci) {

        double cshift;                                               // current shift
        // set data
        nbxy[ci] = cdataf.getNbxy();                                 // nb of points
        x[ci]    = cdataf.getX();                                    // frequency
        y[ci]    = cdataf.getY();                                    // intensity
        // X shift
        cshift = xshifted[ci];                                       // current shift
        if( cshift != 0. ) {
            for( int i=0; i<nbxy[ci]; i++ ) {
                // update shifted data
                x[ci][i] += cshift;
            }
        }
        defxlim(ci);                                                 // define first and last data points in window
        // set global X limits (including shift)
        sxmi[ci] = x[ci][0];
        sxma[ci] = x[ci][nbxy[ci]-1];;
        saxmi = sxmi[0];
        saxma = sxma[0];
        for( int i = 0; i<nbdf; i++ ) {
            saxmi = Math.min(sxmi[i],saxmi);
            saxma = Math.max(sxma[i],saxma);
        }
        // Y shift
        cshift = yshifted[ci];                                       // current shift
        if( cshift != 0. ) {
            for( int i=0; i<nbxy[ci]; i++ ) {
                // update shifted data
                y[ci][i] += cshift;
            }
        }
        prevp_wxma = -1;                                             // call to setRedSpect at next repaint
    }
    
    /**
     * 
     * @param cpredff
     * @since SPVIEW2
     */

	public void reloadPredf(PredFile cpredff) {

		// set data
        double cshift;                                               // current shift
		pfnbxy = cpredff.getNbxy(); // nb of points
		pfx = cpredff.getX(); // frequency
		pfy = cpredff.getY(); // intensity
		// X shift
		if (pfass2 >= 0) {
			cshift = xshifted[pfass2]; // current shift
			if (cshift != 0.) {
				for (int i = 0; i < pfnbxy; i++) {
					// update shifted data
					pfx[i] += cshift;
				}
			}
		}

		pfdefxlim(); // define first and last data points in window

		prevp_wxma = -1; // call to setRedSpect at next repaint
	}

/////////////////////////////////////////////////////////////////////
    
  
    /**
     * get/set the index of the color to associate to prediction file
     * @since SPVIEW2 
     */

    public void setPfass2(int val) {
    	pfass2 = val;
    }
    
    public int getPfass2() {
    	int indexColor;
    	indexColor = pfass2;
    	return indexColor;
    }
    
    /**
     * get/set the index of the color to associate to prediction file
     * @since SPVIEW2 
     */
    
    public void setEfass2(int val) {
    	efass2 = val;
    }
    
    public int getEfass2() {
    	int indexColor;
    	indexColor = efass2;
    	return indexColor;
    }

/////////////////////////////////////////////////////////////////////

    /**
     * Shift drawing one interval down along Y axis.
     */
    public void jbyplus() {

        if( nbdf == 0 ) {
            // no data file
            return;
        }
        bymi += bcadreY[1];                                          // one interval
        byma  = bymi+bydelta;                                        // current height
        cadreint(bymi, bydelta, bcadreY);                            // reset Y axis ticks
        myrepaint(0, p_cymi-p_gap, p_wxma+1, p_cheight+3*p_gap);     // Y axis and all data area
    }

    /**
     * Center drawing along Y axis.
     */
    public void jbyegal() {

        if( nbdf == 0 ) {
            // no data file
            return;
        }
        bymi = (aymi+ayma)/2.-bydelta/2.;                            // center
        byma = bymi+bydelta;                                         // current height
        cadreint(bymi, bydelta, bcadreY);                            // reset Y axis ticks
        myrepaint(0, p_cymi-p_gap, p_wxma+1, p_cheight+3*p_gap);     // Y axis and all data area
    }

    /**
     * Shift drawing one interval up along Y axis.
     */
    public void jbymoins() {

        if( nbdf == 0 ) {
            // no data file
            return;
        }
        bymi -= bcadreY[1];                                          // one interval
        byma  = bymi+bydelta;                                        // current height
        cadreint(bymi, bydelta, bcadreY);                            // reset Y axis ticks
        myrepaint(0, p_cymi-p_gap, p_wxma+1, p_cheight+3*p_gap);     // Y axis and all data area
    }

    /**
     * Shift drawing at the beginning of X axis.
     */
    public void jbxpipeinf() {

        if( nbdf == 0 ) {
            // no data file
            return;
        }
        // start global centered then simulate << button to find beginning
        bxmi = (axmi+axma)/2.-bxdelta/2.;
        while( bxmi > saxmi ) {
            bxmi -= bxdelta;
        }
        bxma = bxmi+bxdelta;                                         // current width
        setXax();                                                    // set X axis characteristics
        for( int i=0; i<nbdf; i++ ) {                                // for each data file
            defxlim_red(i);                                          // define 1st and last points in data area
        }
        repaint();                                                   // all
    }

    /**
     * Shift drawing one screen right along X axis.
     */
    public void jbx2inf() {

        if( nbdf == 0 ) {
            // no data file
            return;
        }
        bxmi -= bxdelta;                                             // one window width
        bxma  = bxmi+bxdelta;                                        // current width
        setXax();                                                    // set X axis characteristics
        for( int i=0; i<nbdf; i++ ) {                                // for each data file
            defxlim_red(i);                                          // define 1st and last points in data area
        }
        repaint();                                                   // all
    }

    /**
     * Shift drawing one interval right along X axis.
     */
    public void jbx1inf() {

        if( nbdf == 0 ) {
            // no data file
            return;
        }
        bxmi -= bcadreX[1];                                          // one interval
        bxma  = bxmi+bxdelta;                                        // current width
        setXax();                                                    // set X axis characteristics
        for( int i=0; i<nbdf; i++ ) {                                // for each data file
            defxlim_red(i);                                          // define 1st and last points in data area
        }
        repaint();                                                   // all
    }

    /**
     * Center X drawing.
     */
    public void jbxegal() {

        if( nbdf == 0 ) {
            // no data file
            return;
        }
        bxmi = (axmi+axma)/2.-bxdelta/2.;                            // center
        bxma = bxmi+bxdelta;                                         // current width
        setXax();                                                    // set X axis characteristics
        for( int i=0; i<nbdf; i++ ) {                                // for each data file
            defxlim_red(i);                                          // define 1st and last points in data area
        }
        repaint();                                                   // all
    }

    /**
     * Shift drawing one interval left along X axis.
     */
    public void jbx1sup() {

        if( nbdf == 0 ) {
            // no data file
            return;
        }
        bxmi += bcadreX[1];                                          // one interval
        bxma  = bxmi+bxdelta;                                        // current width
        setXax();                                                    // set X axis characteristics
        for( int i=0; i<nbdf; i++ ) {                                // for each data file
            defxlim_red(i);                                          // define 1st and last points in data area
        }
        repaint();                                                   // all
    }

    /**
     * Shift drawing one screen left along X axis.
     */
    public void jbx2sup() {

        if( nbdf == 0 ) {
            // no data file
            return;
        }
        bxmi += bxdelta;                                             // one width
        bxma  = bxmi+bxdelta;                                        // current width
        setXax();                                                    // set X axis characteristics
        for( int i=0; i<nbdf; i++ ) {                                // for each data file
            defxlim_red(i);                                          // define 1st and last points in data area
        }
        repaint();                                                   // all
    }

    /**
     * Shift drawing at the end of the X axis.
     */
    public void jbxpipesup() {

        if( nbdf == 0 ) {
            // no data file
            return;
        }
        // start global centered then simulate >> button to find end
        bxma = (axmi+axma)/2.+bxdelta/2.;
        while( bxma < saxma ) {
            bxma += bxdelta;
        }
        bxmi = bxma - bxdelta;                                       // current width
        setXax();                                                    // set X axis characteristics
        for( int i=0; i<nbdf; i++ ) {                                // for each data file
            defxlim_red(i);                                          // define 1st and last points in data area
        }
        repaint();                                                   // all
    }

    /**
     * Restore All in basic state.
     */
    public void resetAll() {

        if( nbdf == 0 ) {
            // no data file
            return;
        }
        // restore basic global state
        showbar = false;
        jmihidebar.setEnabled(false);
        // X axis
        bxmi = axmi;
        bxma = axma;
        bxdelta = axdelta;
        bcadreX = (double[]) acadreX.clone();
        // Y axis
        bymi = aymi;
        byma = ayma;
        bydelta = aydelta;
        bcadreY = (double[]) acadreY.clone();
        // pred-as-stick Y axis
        bpasymi = apasymi;
        bpasyma = apasyma;
        bpasydelta = apasydelta;
        bcadrepasY = (double[]) acadrepasY.clone();
        // data files
        for( int i=0; i<nbdf; i++ ) {
            show[i] = true;
            xUnshiftLoc(i);                                          // X unshift if necessary
            yUnshiftLoc(i);                                          // Y unshift if necessary
            yreverse[i] = false;
            // define first and last data points in window
            ixdeb[i] = 0;
            ixfin[i] = nbxy[i]-1;
        }
        //
        efdefxlim();                                                 // define first and last exp  points in window
        pfdefxlim();                                                 // define first and last pred points in window
        prevp_wxma = -1;                                             // call to setRedSpect at next repaint
        repaint();                                                   // all
    }
    

/////////////////////////////////////////////////////////////////////

    // set X axis characteristics
    private void setXax() {

        cadreint(bxmi, bxdelta, bcadreX);                            // set X axis ticks
        for( int i=0; i<nbdf; i++ ) {
            defxlim(i);                                              // define first and last data points in window
        }
        efdefxlim();                                                 // define first and last exp  points in window
        pfdefxlim();                                                 // define first and last pred points in window
    }

    // apply X scaling choice
    private void repropx(int ci) {

        int    jsf;                                                  // index of scaling factor
        double bxmed = bxmi+bxdelta/2.;                              // window center
        if( ci < nbfact ) {
            // multiply
            jsf = nbfact-ci-1;
            bxdelta *= fact[jsf];
        }
        else {
            // divide
            jsf = ci-nbfact;
            bxdelta /= fact[jsf];
        }
        // define window limits
        bxmi = bxmed-bxdelta/2.;                                     // keep centered
        bxma = bxmi+bxdelta;
        bxdelta = bxma-bxmi;                                         // window frequency range
        setXax();                                                    // set X axis characteristics
        prevp_wxma = -1;                                             // call to setRedSpect at next repaint
    }

    // apply Y scaling choice
    private void repropy(int ci) {

        int    jsf;                                                  // index of scaling factor
        double bymed = bymi+bydelta/2.;                              // window center
        if( ci < nbfact ) {
            // multiply
            jsf = nbfact-ci-1;
            bydelta *= fact[jsf];
        }
        else {
            // divide
            jsf = ci-nbfact;
            bydelta /= fact[jsf];
        }
        // define window limits
        bymi = bymed-bydelta/2.;                                     // keep centered
        byma = bymi+bydelta;
        cadreint(bymi, bydelta, bcadreY);                            // set Y axis characteristics
    }

    // apply pred-as-stick Y scaling choice
    private void reproppasy(int ci) {

        int    jsf;                                                  // index of scaling factor
        double bpasymed = bpasymi+bpasydelta/2.;                     // window center
        if( ci < nbfact ) {
            // multiply
            jsf = nbfact-ci-1;
            bpasydelta *= fact[jsf];
        }
        else {
            // divide
            jsf = ci-nbfact;
            bpasydelta /= fact[jsf];
        }
        // define window limits
        bpasymi = bpasymed-bpasydelta/2.;                            // keep centered
        bpasyma = bpasymi+bpasydelta;
        cadreint(bpasymi, bpasydelta, bcadrepasY);                   // set pred-as-stick Y axis characteristics
    }

    // define first and last data points in window
    private void defxlim(int cj) {

        int i;
        for( i=0; i<nbxy[cj]; i++ ) {                                // for each point
            if( x[cj][i] >= bxmi ) {
                // first point found
                break;
            }
        }
        ixdeb[cj] = Math.min(i,nbxy[cj]-1);                          // in array
        for( i=ixdeb[cj]; i<nbxy[cj]; i++ ) {                        // for each next points
            if( x[cj][i] > bxma ) {
                // last point exceeded
                break;
            }
        }
        ixfin[cj] = Math.max(0,i-1);                                 // at max last data point
        if( ntfile[cj].equals("spectrum") ) {
            // one more point at each end
            ixdeb[cj] = Math.max(0,           ixdeb[cj]-1);
            ixfin[cj] = Math.min(ixfin[cj]+1, nbxy[cj]-1 );
        }
    }

    // define first and last reduced spectrum data points in window
    private void defxlim_red(int cj) {

        int i;
        if( use_red[cj] ) {
            for( i=0; i<nbxy_red[cj]; i++ ) {                        // for each point
                if( x[cj][i_red[cj][i]] >= bxmi ) {
                    // first point found
                    break;
                }
            }
            ixdeb_red[cj] = Math.min(i,nbxy_red[cj]-1);              // at least first data point
            for( i=ixdeb_red[cj]; i<nbxy_red[cj]; i++ ) {            // for each next points
                if( x[cj][i_red[cj][i]] > bxma ) {
                    // last point exceeded
                    break;
                }
            }
            ixfin_red[cj] = Math.max(0,i-1);                         // at max last data point
            if( ntfile[cj].equals("spectrum") ) {
                // one more point at each end
                ixdeb_red[cj] = Math.max(0,               ixdeb_red[cj]-1);  // ATTENTION : ceci ne represente qu'une partie du pixel precedent
                ixfin_red[cj] = Math.min(ixfin_red[cj]+1, nbxy_red[cj]-1 );  // ATTENTION : ceci ne represente qu'une partie du pixel suivant
            }
        }
    }

    // define first and last exp  points in window
    private void efdefxlim() {

        if( efnbxy != 0 ) {
            // exp file loaded
            if( nbdf == 0 ) {
                // no data file -> all points
                efixdeb = 0;
                efixfin = efnbxy-1;
            }
            else {
                int j;
                for( j=0; j<efnbxy; j++ ) {                          // for each point
                    if( efx[j] >= bxmi ) {
                        // first point found
                        break;
                    }
                }
                efixdeb = Math.min(j,efnbxy-1);                      // at max last exp  point
                for( j=efixdeb; j<efnbxy; j++ ) {                    // for each next points
                    if( efx[j] > bxma ) {
                        // last point exceeded
                        break;
                    }
                }
                if( j == efnbxy ) {
                    efixfin = efnbxy-1;                              // in array
                }
                else {
                    efixfin = Math.max(0,j-1);                       // at max last exp  point
                }
            }
        }
    }

    // define first and last pred points in window
    private void pfdefxlim() {

        if( pfnbxy != 0 ) {
            if( nbdf == 0 ) {
                // no data file -> all points
                pfixdeb = 0;
                pfixfin = pfnbxy-1;
            }
            else {
                int j;
                for( j=0; j<pfnbxy; j++ ) {                          // for each point
                    if( pfx[j] >= bxmi ) {
                        // first point found
                        break;
                    }
                }
                pfixdeb = Math.min(j,pfnbxy-1);                      // at max last pred point
                for( j=pfixdeb; j<pfnbxy; j++ ) {                    // for each next points
                    if( pfx[j] > bxma ) {
                        // last point exceeded
                        break;
                    }
                }
                if( j == pfnbxy ) {
                    pfixfin = pfnbxy-1;                              // in array
                }
                else {
                    pfixfin = Math.max(0,j-1);                       // at max last pred point
                }
            }
        }
    }

/////////////////////////////////////////////////////////////////////

    /**
     * Set/unset data showable.
     *
     * @param cdf    data file index
     * @param cbool  show value
     */
    public void setShow(int cdf, boolean cbool) {

        if( (cdf < 0) || (cdf > (nbdf-1)) ) {
            System.out.println("!!! index out of bounds in PanAff.setShow ("+cdf+")");
            return;
        }
        show[cdf] = cbool;
        myrepaint( p_cxmi, p_cymi, p_cwidth, p_cheight);             // data area
    }

    /**
     * Reverse Y data reverse state.
     *
     * @param cdf  data file index
     */
    public void yReverse(int cdf) {

        if( (cdf < 0) || (cdf > (nbdf-1)) ) {
            System.out.println("!!! index out of bounds in PanAff.yReverse ("+cdf+")");
            return;
        }
        yreverse[cdf] = ! yreverse[cdf];
        myrepaint( p_cxmi, p_cymi, p_cwidth, p_cheight);             // data area
    }

    /**
     * Ask to shift X data.
     *
     * @param cdf  data file index
     */
    public void xShift(int cdf) {

        if( (cdf < 0) || (cdf > (nbdf-1)) ) {
            System.out.println("!!! index out of bounds in PanAff.xShift ("+cdf+")");
            return;
        }
        shiftedf = cdf;
        shiftx = true;
    }

    /**
     * Get current X shift.
     *
     * @param cdf  data file index
     */
    public double getXShift(int cdf) {

         if( (cdf < 0) || (cdf > (nbdf-1)) ) {
             System.out.println("!!! index out of bounds in PanAff.getXShift ("+cdf+")");
             return 0.;
         }
         return xshifted[cdf];
     }

    /**
     * Unshift X data to original position.
     *
     * @param cdf  data file index
     */
    public void xUnshift(int cdf) {

        if( (cdf < 0) || (cdf > (nbdf-1)) ) {
            System.out.println("!!! index out of bounds in PanAff.xUnshift ("+cdf+")");
            return;
        }
        xUnshiftLoc(cdf);
        myrepaint( p_cxmi, 0, p_cwidth, p_cyma+1);                   // exp/pred/data area
    }

    // local method to unshift X data to original position
    private void xUnshiftLoc(int cdf) {

        for( int i=0; i<nbxy[cdf]; i++) {                            // for each data point
            x[cdf][i] -= xshifted[cdf];                              // unshift it
        }
        defxlim(cdf);                                                // define first and last data points in window
        // set global X limits (including shift)
        sxmi[cdf] -= xshifted[cdf];
        sxma[cdf] -= xshifted[cdf];
        saxmi = sxmi[0];
        saxma = sxma[0];
        for( int i = 0; i<nbdf; i++ ) {
            saxmi = Math.min(sxmi[i],saxmi);
            saxma = Math.max(sxma[i],saxma);
        }
        //
        defxlim_red(cdf);                                            // define first and last data points in window
        if( efass2 == cdf ) {
            // exp  associated to this data file
            for( int i=0; i<efnbxy; i++ ) {                          // for each exp  point
                efx[i] -= xshifted[cdf];                             // unshift it
            }
            efdefxlim();                                             // define first and last exp  points in window
        }
        if( pfass2 == cdf ) {
            // pred associated to this data file
            for( int i=0; i<pfnbxy; i++ ) {                          // for each pred point
                pfx[i] -= xshifted[cdf];                             // unshift it
            }
            pfdefxlim();                                             // define first and last pred points in window
        }
        xshifted[cdf] = 0.;                                          // X shift value
    }

    /**
     * Ask to shift Y data.
     *
     * @param cdf  data file index
     */
    public void yShift(int cdf) {

        if( (cdf < 0) || (cdf > (nbdf-1)) ) {
            System.out.println("!!! index out of bounds in PanAff.yShift ("+cdf+")");
            return;
        }
        shiftedf = cdf;
        shifty = true;
    }
    
    /**
     * Set current Y shift to the value given in argument
     * @param xshift
     * @param cdf
     * 
     * @since SPVIEW2
     */
	public void setXShift(double xshift, int cdf) {
		xshifted[cdf] = xshift;
		for (int i = 0; i < nbxy[cdf]; i++) { // for each data point
			x[cdf][i] += xshifted[cdf];
		}
	}
	
    /**
     * Set current Y shift to the value given in argument
     * @param yshift
     * @param cdf
     * 
     * @since SPVIEW2
     */
	public void setYShift(double yshift, int cdf) {
		yshifted[cdf] = yshift;
		for (int i = 0; i < nbxy[cdf]; i++) { // for each data point
			y[cdf][i] += yshifted[cdf];
		}
	}

    /**
	 * Set current X zoom to the value given in argument
	 * 
	 * @param boundMin
	 * @param boundMax
	 * 
	 * @since SPVIEW2
	 */
	public void setXZoom(double boundMin, double boundMax) {
		// X zoom
		bxma = Math.max(boundMin, boundMax);
		bxmi = Math.min(boundMin, boundMax);
		bxdelta = bxma - bxmi; // window frequency range
		setXax(); // set X axis characteristics
		zoomx = false; // no x zoom area to define
		zoom = false; // no zoom area to draw
		prevp_wxma = -1; // call to setRedSpect at next repaint
		repaint(); // all
		return;
	}

    /**
	 * Set current Y zoom to the value given in argument
	 * 
	 * @param boundMin
	 * @param boundMax
	 * 
	 * @since SPVIEW2
	 */
	public void setYZoom(double boundMin, double boundMax) {
        byma = Math.max(boundMin, boundMax);
        bymi = Math.min(boundMin, boundMax);
        bydelta = byma-bymi;
        cadreint(bymi, bydelta, bcadreY);                    // resize the Y axis
        zoomy = false;                                       // no y zoom area to define
        zoom  = false;       
	}
	
    /**
     * Get p_zx0 value of X zoom.
     *
     * @since SPVIEW2
     */
     public Double getbxmi() {
    	 return bxmi;
     }

     /**
      * Get p_zx1 value of X zoom.
      *
      * @since SPVIEW2
      */
      public Double getbxma() {
     	 return bxma;
      }
      
      /**
       * Get p_zy0 value of Y zoom.
       *
       * @since SPVIEW2
       */
       public Double getbymi() {
      	 return bymi;
       }
       
       /**
        * Get p_zy1 value of Y zoom.
        *
        * @since SPVIEW2
        */
        public Double getbyma() {
       	 return byma;
        }

    /**
     * Get current Y shift.
     * @param cdf
     *
     * @param cdf  data file index
     */
     public double getYShift(int cdf) {

         if( (cdf < 0) || (cdf > (nbdf-1)) ) {
             System.out.println("!!! index out of bounds in PanAff.getYShift ("+cdf+")");
             return 0.;
         }
         return yshifted[cdf];
     }

    /**
     * Unshift Y data original position.
     *
     * @param cdf  data file index
     */
    public void yUnshift(int cdf) {

        if( (cdf < 0) || (cdf > (nbdf-1)) ) {
            System.out.println("!!! index out of bounds in PanAff.yUnshift ("+cdf+")");
            return;
        }
        yUnshiftLoc(cdf);
        myrepaint( p_cxmi, p_cymi, p_cwidth, p_cheight);             // data area
    }

    // local method to unshift Y data to original position
    private void yUnshiftLoc(int cdf) {

        if( ntfile[cdf].equals("spectrum") ) {                       // only for spectrum data file
            for( int i=0; i<nbxy[cdf]; i++) {                        // for each data point
                y[cdf][i] -= yshifted[cdf];                          // unshift it
            }
        }
        yshifted[cdf] = 0.;                                          // Y shift value
    }

//////////////////////////////////////////////////////////////////////

    // associate the corresponding pred assignment index to each exp assignment
    // each ExpJsynPoint MUST have his corresponding PredJsynPoint
//  private void epLink() {
	synchronized void epLink() {

		if (pfnbxy != 0) { // pred loading/loaded
			Arrays.fill(pfiexpa, -1); // default status NO assigned exp
			//
			if (efnbxy != 0) { // exp loading/loaded
				// store pred assignments in a PredJsynPoint array
				PredJsynPoint[] pjp = new PredJsynPoint[pfnbxy];
				for (int i = 0; i < pfnbxy; i++) {
					pjp[i] = new PredJsynPoint(pfjsyn[i], i);
				}
				// sort
				Arrays.sort(pjp);
				// search
				int nbrem = 0; // nb of removed orphan ExpJsynPoint
				for (int i = 0; i < efnbxy; i++) { // for each exp point
					boolean again = true; // do this loop again
					while (again) {
						again = false; // if no ExpJsynPoint removed
						int nbass = efejsynpt[i].size(); // nb of assignments
						for (int j = 0; j < nbass; j++) { // for each assignment
							cejpt = (ExpJsynPoint) efejsynpt[i].get(j);
							cStr = cejpt.getJsyn();
							if (cStr.trim().length() != 0) { // not an empty assignment
								int k = Arrays.binarySearch(pjp, new PredJsynPoint(cStr, 0)); // search it
								if (k >= 0) {
									// found
									int pjpInd = pjp[k].getInd();
									cejpt.setIpred(pjpInd); // index of assigned pred
									pfiexpa[pjpInd] = i; // index of assigned exp
								} else {
									// not found
			                        if( nbass == 1 ) {
										// only one assignment, empty it
										cejpt.setFaso(" ");
										cejpt.setSaso(" ");
										cejpt.setJsyn("");
										cejpt.setIpred(-1);
										efexasg[i] = "";
									} else {
			                            // suppress it
			                            efejsynpt[i].remove(j);
										again = true; // re-do the loop
			                        }
									nbrem++; // nb of removed ExpJsynPoint
									break; // now
								}
							}
						}
					}
				}
				setJta(); // update TextArea
				if (nbrem != 0) {
					int res = JOptionPane.showConfirmDialog(
									null, nbrem + " assignments not predicted are ignored." + lnsep
											+ "Do you want to update " + jobplay.getExpFileName() + "?",
									"Remove not predicted lines", JOptionPane.YES_NO_OPTION);
					if (res == JOptionPane.YES_OPTION) {
						updateExp(jobplay.getExpFileName());
						/* file has been updated, we need to update display */
						setExp(expf);
					}
				}
			}
		}
	}

    // create assignment management popup menu
    private boolean setJpmAssign() {

        int nbass;                                                   // nb of assignments

		if (!efready) {
            // no exp  file
            return false;
        }
        jpmassign = new JPopupMenu();
        // unselect exp
        jmiunselexp = new JMenuItem("Unselect exp");
        if( ixexp < 0 ) {
            // no exp  selected
            jmiunselexp.setEnabled(false);
        }
        else {
            jmiunselexp.addActionListener(this);
        }
        jpmassign.add(jmiunselexp);
        jpmassign.addSeparator();

        // remove an assignment
        jmunass   = new JMenu("Unassign");
        if( ixexp < 0 ) {
            // no exp  selected
            jmunass.setEnabled(false);
        }
        else {
            nbass = efejsynpt[ixexp].size();
            cejpt = (ExpJsynPoint)efejsynpt[ixexp].get(0);
            if( cejpt.getJsyn().length() == 0 ) {
                // empty assignment
                jmunass.setEnabled(false);
            }
            else {
                jmiunass = new JMenuItem[nbass];                     // a jmi for each assignment
                for( int i=0; i<nbass; i++ ) {
                    cejpt = (ExpJsynPoint)efejsynpt[ixexp].get(i);
                    jmiunass[i] = new JMenuItem(cejpt.getJsyn());
                    jmiunass[i].setFont( mono14 );
                    jmiunass[i].addActionListener(this);
                    jmunass.add(jmiunass[i]);
                }
            }
            jmiunassall = new JMenuItem("Unassign all");
            jmiunassall.addActionListener(this);
            jmunass.addSeparator();
            jmunass.add(jmiunassall);
        }
        jpmassign.add(jmunass);
        // set frequency and intensity mark
        jmfaso = new JMenu("Set frequency mark");
        jmsaso = new JMenu("Set intensity mark");
        if( ixexp < 0 ) {
            // no exp  selected
            jmfaso.setEnabled(false);
            jmsaso.setEnabled(false);
        }
        else {
            // create the related menu
            nbass = efejsynpt[ixexp].size();
            cejpt = ((ExpJsynPoint)efejsynpt[ixexp].get(0));
            if( nbass == 1                    &&
                cejpt.getJsyn().length() == 0    ) {
                // empty assignment
                jmfaso.setEnabled(false);
                jmsaso.setEnabled(false);
            }
            else {
                // a jmi for each assignment and xaso value
                // set frequency mark
                JMenu[] jmjsynfaso = new JMenu[nbass];
                jmifaso = new JMenuItem[nbass][];
                for( int i=0; i<nbass; i++ ) {
                    cejpt = ((ExpJsynPoint)efejsynpt[ixexp].get(i));
                    jmjsynfaso[i] = new JMenu(cejpt.getJsyn()+" ("+cejpt.getFaso()+")");
                    jmjsynfaso[i].setFont( mono14 );
                    jmifaso[i] = new JMenuItem[nbxaso];
                    for( int j=0; j<nbxaso; j++ ) {
                        jmifaso[i][j] = new JMenuItem("'"+xaso[j]+"'");
                        jmifaso[i][j].setFont( mono14 );
                        jmifaso[i][j].addActionListener(this);
                        jmjsynfaso[i].add(jmifaso[i][j]);
                    }
                    jmfaso.add(jmjsynfaso[i]);
                }
                // set intensity mark
                JMenu[] jmjsynsaso = new JMenu[nbass];
                jmisaso = new JMenuItem[nbass][];
                for( int i=0; i<nbass; i++ ) {
                    cejpt = ((ExpJsynPoint)efejsynpt[ixexp].get(i));
                    jmjsynsaso[i] = new JMenu(cejpt.getJsyn()+" ("+cejpt.getSaso()+")");
                    jmjsynsaso[i].setFont( mono14 );
                    jmisaso[i] = new JMenuItem[nbxaso];
                    for( int j=0; j<nbxaso; j++ ) {
                        jmisaso[i][j] = new JMenuItem("'"+xaso[j]+"'");
                        jmisaso[i][j].setFont( mono14 );
                        jmisaso[i][j].addActionListener(this);
                        jmjsynsaso[i].add(jmisaso[i][j]);
                    }
                    jmsaso.add(jmjsynsaso[i]);
                }
            }
        }
        jpmassign.add(jmfaso);
        jpmassign.add(jmsaso);
        // modify exasg
        if( ixexp < 0 ) {
            // no exp  selected
            jmexasg = new JMenu("Modify EXASG ()");
            jmexasg.setEnabled(false);
        }
        else {
            jmexasg = new JMenu("Modify EXASG ("+efexasg[ixexp]+")");  // with current value
            cejpt = (ExpJsynPoint)efejsynpt[ixexp].get(0);
            if( cejpt.getJsyn().length() == 0 ) {
                // empty assignment
                jmexasg.setEnabled(false);
            }
            else {
                // a jmi for each possible EXASG value
                jmiexasg = new JMenuItem[nbexasg];
                for( int i=0; i<nbexasg; i++ ) {
                    jmiexasg[i] = new JMenuItem("'"+exasg[i]+"'");
                    jmiexasg[i].setFont( mono14 );
                    jmiexasg[i].addActionListener(this);
                    jmexasg.add(jmiexasg[i]);
                }
            }
        }
        jpmassign.add(jmexasg);
        // modify comment
        jmcomm = new JMenu("Set comment");
        if( ixexp < 0 ) {
            // no exp  selected
            jmcomm.setEnabled(false);
        }
        else {
            // a jmi for each assignment
            nbass = efejsynpt[ixexp].size();
            jmicomm = new JMenuItem[nbass];
            for( int i=0; i<nbass; i++ ) {
                cejpt = (ExpJsynPoint)efejsynpt[ixexp].get(i);
                jmicomm[i] = new JMenuItem(cejpt.getJsyn()+" ("+cejpt.getComm()+")");
                jmicomm[i].setFont( mono14 );
                jmicomm[i].addActionListener(this);
                jmcomm.add(jmicomm[i]);
            }
        }
        jpmassign.add(jmcomm);
        jpmassign.addSeparator();
        // add a new EXASG
        jmiaddexasg = new JMenuItem("Add a new EXASG string");
        jmiaddexasg.addActionListener(this);
        jpmassign.add(jmiaddexasg);
        // set default EXASG
        jmdefexasg = new JMenu("Set default EXASG ("+defexasg+")");
        jmidefexasg = new JMenuItem[nbexasg];                        // a jmi for each possible EXASG value
        for( int i=0; i<nbexasg; i++ ) {
            jmidefexasg[i] = new JMenuItem("'"+exasg[i]+"'");
            jmidefexasg[i].setFont( mono14 );
            jmidefexasg[i].addActionListener(this);
            jmdefexasg.add(jmidefexasg[i]);
        }
        jpmassign.add(jmdefexasg);

        return true;
    }

    // sort an efejsynpt
    private void sortEfejsynpt( int ci ) {

        // copy in an array, sort, restore in original
        ExpJsynPoint[] aejpt;
        int nbass = efejsynpt[ci].size();
        if( nbass >1 ) {
            // more then one assignment
            aejpt = new ExpJsynPoint[nbass];                         // array
            for( int k=0; k<nbass; k++ ) {
                aejpt[k] = (ExpJsynPoint)efejsynpt[ci].get(k);       // copy
            }
            Arrays.sort(aejpt);                                      // sort
            efejsynpt[ci] = new ArrayList<ExpJsynPoint>();
            for( int k=0; k<nbass; k++ ) {
                efejsynpt[ci].add(aejpt[k]);                         // restore
            }
        }
    }

    /**
     * Add a new EXASG value.
     *
     * @param cexasg  new EXASG string
     */
    public void addExasg( String cexasg ) {

        String lexasg = cexasg;
        if( lexasg.trim().length() == 0 ) {
            // empty string
            return;
        }
        if( ! alexasg.contains(lexasg) ) {
            // not already in ArrayList
            alexasg.add(lexasg);                                     // add
            nbexasg = alexasg.size();                                // nb of possible EXASG
            exasg = new String[nbexasg];                             // update exasg[]
            for( int i=0; i<nbexasg; i++ ) {
                exasg[i] = (String)alexasg.get(i);
            }
            Arrays.sort(exasg);                                      // sort
        }
    }

    // set default EXASG
    private void setDefexasg( String cdefexasg ) {

        if( alexasg.contains(cdefexasg) ) {
            // found in ArrayList
            defexasg = cdefexasg;
        }
    }

    /**
     * Modify an exp assignment comment.
     *
     * @param cstr    comment string
     * @param cixexp  exp  index
     * @param ciass   assignment index
     */
    public void setComm( String cstr, int cixexp, int ciass ) {

        cejpt = (ExpJsynPoint)efejsynpt[cixexp].get(ciass);
        cejpt.setComm(cstr);
        setJta();                                                    // update TextArea
        updateExp(jobplay.getExpFileName());
    }
    
    private boolean updateExp(String filename) {
        try {
			out1 = new PrintWriter(new BufferedWriter(new FileWriter(filename)));

            for( int i=0; i<efnbxy; i++ ) {
                // for each exp point
                int nbass = efejsynpt[i].size();                     // nb of assignments
                for( int j=0; j<nbass; j++ ) {
                    // for each assignment
                    cejpt = (ExpJsynPoint)efejsynpt[i].get(j);
                    // NUO (5 char, RIGHT)
                    String cStrNUO = "     "+(""+(i+1)).trim();
                    cStrNUO = cStrNUO.substring(cStrNUO.length()-5,cStrNUO.length());
                    // SDFOBS,SDSOBS (16 char)
					String cStrSDOBS = efsdobs[i] + FortranFormat.bStr(16);
                    cStrSDOBS = cStrSDOBS.substring(0,16);
                    // JSYN (21 char)
					String cStrJSYN = cejpt.getJsyn() + FortranFormat.bStr(21);
                    cStrJSYN = cStrJSYN.substring(0,21);
                    // EXASG (30 char)
					String cStrEXASG = efexasg[i] + FortranFormat.bStr(30);
                    cStrEXASG = cStrEXASG.substring(0,30);
                    //
                    cStr = (" "              +                       // ISP
                            cStrNUO          +                       // NUO           line number
                            FortranFormat.formFreq(efx[i]) +                       // FOBS          frequency
                            " "              +
                            cejpt.getFaso()  +                       // FASO          frequency mark
                            FortranFormat.formInt(efy[i])  +                       // SOBS          intensity
                            " "              +
                            cejpt.getSaso()  +                       // SASO          intensity mark
                            cStrSDOBS        +                       // SDFOBS,SDSOBS frequency and intensity standard deviations string
                            "  "             +
                            cStrJSYN         +                       // JSYN          assignment
                            " "              +
                            cStrEXASG        +                       // EXASG
                            " "              +
                            cejpt.getComm()    );                    // comment
                    int l;
                    for( l=cStr.length(); l>0; l-- ) {
                        if( cStr.charAt(l-1) != ' ' ) {
                            break;
                        }
                    }
                    out1.println( cStr.substring(0, l) );            // without extra spaces
                }
            }
        }
		catch (IOException ioe) { // IO error
			JOptionPane.showMessageDialog(null, "IO error while writing file" + lnsep + nsavexp + lnsep + ioe);
			return false;
		} finally {
			// close the file
			if (out1 != null) {
				out1.close();
				if (out1.checkError()) {
					JOptionPane.showMessageDialog(null, "PrintWriter error while creating exp file" + lnsep + nsavexp);
					return false;
				}
			}
		}
		return true;
	}

	/**
	 * Save updated experiment file <br>
	 * following FORMAT 3000 of eq_tds.f
	 * 
	 * @since SPVIEW2 Or update current experiment file
	 */
	public void saveExp() {

		if (!efready) {
			// no exp file loaded
			return;
		}
		// choose output file
		JFileChooser jfcfile = new JFileChooser(workd); // default = working directory
		jfcfile.setSize(400, 300);
		jfcfile.setFileSelectionMode(JFileChooser.FILES_ONLY); // only files
		jfcfile.setDialogTitle("Define the name of the saved experiment file");
		Container parent = this.getParent();
		int choice = jfcfile.showDialog(parent, "Select"); // Dialog, Select
		if (choice == JFileChooser.APPROVE_OPTION) {
			nsavexp = jfcfile.getSelectedFile().getAbsolutePath(); // full file name
			if (updateExp(nsavexp)) {
				// finished
				JOptionPane.showMessageDialog(null, "Assignment file" + lnsep + nsavexp + lnsep + "is saved");

			}
		}
	}

    /**
     * Add assignment(s) to selected exp.
     */
	public void addAss2exp(boolean freq) {

		// add assignment(s)
		for (int k = 0; k < nbixpred; k++) {
			if (ixpreds[k] == 1) {
				// pred selected
				int cixpred = ixpred[k];
				// add it
				pfiexpa[cixpred] = ixexp; // index of assigned exp
				int nbass = efejsynpt[ixexp].size(); // number of assignments
				cejpt = (ExpJsynPoint) efejsynpt[ixexp].get(0);
				if (nbass == 1 && cejpt.getJsyn().length() == 0) {
					// the only assignment is empty, replace it
					if (freq) {
						// frequency
						cejpt.setFaso("+");
						cejpt.setSaso(" ");
					} else {
						// intensity
						cejpt.setFaso(" ");
						cejpt.setSaso("+");
					}
					cejpt.setJsyn(pfjsyn[cixpred]);
					cejpt.setIpred(cixpred);
					efexasg[ixexp] = defexasg; // 1st one -> default EXASG
				} else {
					// add one
					if (freq) {
						// frequency
						efejsynpt[ixexp].add(new ExpJsynPoint("+", " ", pfjsyn[cixpred], cixpred, ""));
					} else {
						// intensity
						efejsynpt[ixexp].add(new ExpJsynPoint(" ", "+", pfjsyn[cixpred], cixpred, ""));
					}
					sortEfejsynpt(ixexp); // sort -> easier to read in TextArea
				}
			}
		}
		setAsog(ixexp); // set global frequency and intensity marks
		myrepaint(ip_X(efx[ixexp], p_cxmi, p_cxma) - p_ovray, p_epymi, 2 * p_ovray, p_yatma - p_epymi + 1); // exp marks
																											// area
		endSelPred(); // end pred selection
        updateExp(jobplay.getExpFileName());
		return;
	}

    /**
	 * End pred selection.
	 */
	public void endSelPred() {

		if (predsel) {
			// pred selection running
			predsel = false; // No pred selection running
			nbixpred = 0; // NO pred selected
			jfsp.setContent(nbixpred, ixdebpred, ixpreds, ixexp); // set pred selection window content
			setLocSim(false); // NO loc-sim
			drawPredSel(); // draw pred selection area
			setJta(); // update TextArea
		}
		return;
	}

    /**
     * Draw pred selection area
     */
    public void drawPredSel() {

        myrepaint( ip_X(predselx0, p_cxmi, p_cxma)-p_ovray, p_yprmi, ip_X(predselx1, p_cxmi, p_cxma)-ip_X(predselx0, p_cxmi, p_cxma)+p_ovwidth+1, p_yprma-p_yprmi+1);  // pred selection area
    }

    /**
     * set current selectable pred.
     */
    public void setPredable(int cixpredable) {

        ixpredable = cixpredable;
        drawPredSel();
    }

    /**
     * Set local simulation spectrum.
     */
    public void setLocSim( boolean clocsim ) {

        boolean prevlocsim = locsim;                                 // previous locsim
        int  lslen    = 0;                                           // nb of loc-sim points
        int  p_prevx0 = 0;                                           // pixel starting point of previous loc-sim
        int  p_prevx1 = 0;                                           // pixel ending   point of previous loc-sim

        if( prevlocsim ) {
            // set previous loc-sim limits
            p_prevx0 = ip_X(lsx[0], p_cxmi, p_cxma);
            p_prevx1 = ip_X(lsx[lsx.length-1], p_cxmi, p_cxma);
        }

        locsim  = clocsim;
        if( locsim ) {
            // get points and show them
            lsx = jfsp.getLsx();
            lsy = jfsp.getLsy();
            lslen = lsx.length;
            if( lslen < 2 ) {
                // not enough points
                locsim = false;
            }
            else {
                // rescale to fit spectrum
                for( int i=0; i<lslen; i++ ) {
                    lsy[i] = lsmis+(lsy[i]-lsmi)*(lsmas-lsmis)/(lsma-lsmi);
                }
            }
        }

        // define limited draw area
        if( prevlocsim ) {
            if( locsim ) {
                p_paintx      = Math.min(p_prevx0, ip_X(lsx[0], p_cxmi, p_cxma));
                p_paintwidth  = Math.max(p_prevx1, ip_X(lsx[lslen-1], p_cxmi, p_cxma))-p_paintx+1;
            }
            else {
                p_paintx      = p_prevx0;
                p_paintwidth  = p_prevx1-p_paintx+1;
            }
        }
        else {
            if( locsim ) {
                p_paintx      = ip_X(lsx[0], p_cxmi, p_cxma);
                p_paintwidth  = ip_X(lsx[lslen-1], p_cxmi, p_cxma)-p_paintx+1;
            }
            else {
                return;
            }
        }
        myrepaint(p_paintx, p_cymi, p_paintwidth, p_cheight);        // limited along X direction
    }

    /**
     * Set loc-sim spectrum Ymin and Ymax.
     */
    public void setLsScale() {

        lsy = jfsp.getLsy();                                         // loc-sim Y
        int lslen = lsy.length;                                      // nb of points
        lsmi = lsy[0];
        lsma = lsy[0];
        for( int i=0; i<lslen; i++ ) {
            if( lsy[i] < lsmi ) {
                lsmi = lsy[i];
            }
            if( lsy[i] > lsma ) {
                lsma = lsy[i];
            }
        }
        return;
    }

    // Set reduced spectra for speeding draw
    private void setRedSpect() {

        if( nbdf == 0 ) {
            // no data file
            return;
        }

        int[] ci_red = new int[0];                                   // index of reduced data
        int    ind0;                                                 // running index : original (non reduced) data
        int    ip_0;                                                 // pixel position of running original point
        int    ind_red;                                              // running index : reduced  data
        int    ciymi;
        int    ciyma;
        double cymi;                                                 // Y min of current pixel area
        double cyma;                                                 // Y max of current pixel area
        double cy;                                                   // running y
        int    nbred;                                                // original nb of data points for one pixel width area

        for( int j=0; j<nbdf; j++ ) {                                // for each data file
            if( ntfile[j].equals("spectrum") ) {                     // spectrum style

                use_red[j] = false;                                  // status
                nbred      = 0;

                int nbpix = ip_X(x[j][nbxy[j]-1], p_cxmi, p_cxma)-ip_X(x[j][0], p_cxmi, p_cxma)+1;   // visible area full nb of pixel of the spectrum (in actual scale)
                nbpix = Math.max(1, nbpix);
                nbred = nbxy[j]/nbpix;                               // nb of points/pixel
                // the reduced spectrum has up to 4 points/pixel
                // 1st = 1st  original point with this pixel value
                // 2nd =      original point with this pixel value and Y min value
                // 3rd =      original point with this pixel value and Y max value
                // 4th = last original point with this pixel value
                // if 2nd and/or 3rd are yet 1st or 4th, they are not duplicated (usefull if nbred near 4)
                // they all have the same X value : mean frequency of these 1st and last original points
                if( nbred > 4 ) {                                    // do we have to reduce ?
                    // reduction
                    use_red[j] = true;

                    ci_red  = new int[nbxy[j]];                      // define array
                    ind0    = 0;                                     // index of 1st original point of 1st pixel area
                    ip_0 = ip_X(x[j][ind0], p_cxmi, p_cxma);         // pixel value of this pixel area
                    ciymi = ind0;
                    cymi = y[j][ciymi];                              // Y min of this pixel area
                    ciyma = ciymi;
                    cyma = y[j][ciyma];                              // Y max of this pixel area
                    ind_red = 0;                                     // index of 1st reduced point
                    for( int i=0; i<nbxy[j]; i++ ) {                 // for each original point
                        if( ip_X(x[j][i], p_cxmi, p_cxma) != ip_0      ||      // new pixel area
                            i                             == nbxy[j]-1    ) {  // end of the original data
                            // 1st point
                            ci_red[ind_red] = ind0;                  // frequency of this pixel area
                            ind_red ++;                              // next reduced point index
                            // MIN point if not the same as 1st and/or last one
                            if( cymi != y[j][ind0] &&                // NOT the same as the 1st  one
                                cymi != y[j][i-1]     ) {            // NOT the same as the last one
                                ci_red[ind_red] = ciymi;             // frequency of this pixel area
                                ind_red ++;                          // next reduced point index
                            }
                            // MAX point if not the same as 1st and/or last one
                            if( cyma != y[j][ind0] &&                // NOT the same as the 1st  one
                                cyma != y[j][i-1]     ) {            // NOT the same as the last one
                                ci_red[ind_red] = ciyma;             // frequency of this pixel area
                                ind_red ++;                          // next reduced point index
                            }
                            // last point
                            ci_red[ind_red] = i-1;                   // frequency of this pixel area
                            ind_red ++;                              // next reduced point index
                            //
                            ind0  = i;                               // index of the 1st original point of the next pixel area
                            ip_0 = ip_X(x[j][ind0], p_cxmi, p_cxma);  // pixel position of this point
                            ciymi = ind0;
                            cymi  = y[j][ciymi];                     // Y min of this pixel area
                            ciyma = ciymi;
                            cyma  = y[j][ciyma];                     // Y max of this pixel area
                        }
                        else {                                       // end of the pixel area
                            cy = y[j][i];                            // current Y
                            if( cy < cymi ) {                        // test min
                                ciymi = i;
                                cymi = cy;
                            }
                            if( cy > cyma ) {                        // test max
                                ciyma = i;
                                cyma = cy;
                            }
                        }
                    }
                    nbxy_red[j] = ind_red;                           // effective nb of reduced points
                    i_red[j] = new int[nbxy_red[j]];
                    System.arraycopy(ci_red,0,i_red[j],0,nbxy_red[j]);
                    defxlim_red(j);                                  // set 1st and last points in data area
                }
            }
        }
    }
    
	private void setUnsave(boolean unsave) {
		if (this.jobplay.getProject() != null) {
			this.jobplay.getProject().setUnsave(true);
		}
	}

}
