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

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
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.*;
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 {
    public final static String EXASG_DEFAULT = "SPVIEW_EXASG                  ";
    /**
     *
     */
    private static final long serialVersionUID = -6454762258521638796L;
    private static double threshold = -1;
    private final JobPlay jobplay;                                         // calling JobPlay
    private final String workd;                                           // working directory
    // data
    private final String[] ntfile;                                       // data file type
    private final int[] nbxy;                                         // number of points in data file
    private final double[][] x;                                            // frequency
    private final double[][] y;                                            // intensity
    private final double[] sxmi;                                         // X min (including shift)
    private final double[] sxma;                                         // X max (including shift)
    private final int[] ixdeb;                                        // window first point index
    private final int[] ixfin;                                        // window last  point index
    private final boolean[] use_red;                                      // use reduced data
    private final int[][] i_red;                                        // index of reduced data
    private final int[] nbxy_red;                                     // reduced number of points
    private final int[] ixdeb_red;                                    // window first reduced point index
    private final int[] ixfin_red;                                    // window last  reduced point index
    private final boolean[] show;                                         // show      the data file
    private final boolean[] yreverse;                                     // Y reverse the data file
    private final double[] xshifted;                                     // X shift value
    private final double[] yshifted;                                     // Y shift value
    private final boolean[] dfready;                                      // data file ready
    // first tick , interval, nb of intervals, last tick
    private final double[] acadreX = {0., 0., 0., 0.};                  // global frequency
    private final double[] acadreY = {0., 0., 0., 0.};                  // global intensity
    private final double[] acadrepasY = {0., 0., 0., 0.};                  // pred-as-stick global intensity
    private final Color[] coul;                                          // colors
    private final int nbcoul;                                        // number of colors
    private final JTextArea jta;                                           // for selected exp and pred characteristics
    // mouse position
    private final JLabel jlx;                                              // X in data scale
    private final JLabel jly;                                              // Y in data scale
    private final JLabel jlypredas;                                        // Y in pred-as-stick scale
    // data file area popup menu
    private final JPopupMenu jpmdata;
    private final JMenuItem jmishowbar;                                   // show-set vertical bar
    private final JMenuItem jmihidebar;                                   // hide vertical bar
    private final JMenuItem jmizoomx;                                     // X zoom
    private final JMenuItem jmizoomy;                                     // Y zoom
    private final JMenuItem jmizoompasy;                                  // pred_as_stick Y zoom
    private final JMenu jmrescalepasy;                                // pred-as-stick Y rescale
    private final JMenuItem jmirestorex;                                  // X restore
    private final JMenuItem jmirestorey;                                  // Y restore
    private final JMenuItem jmirestorepasy;                               // pred-as-stick Y restore
    private final String[] xaso = {" ", "+", "-"};                  // possible values for frequency and intensity marks
    private final int nbxaso;                                    // number of values
    // for X/Y rescale
    private final int[] fact = {2, 5, 10, 20, 50, 100};            // scaling factors
    private final int nbfact;                                      // number of scaling factors
    private final int nbscalec;                                    // number of scaling choices
    private final JMenuItem[] jmivalexpx;                                  // frequency choices
    private final JMenuItem[] jmivalexpy;                                  // intensity choices
    private final JMenuItem[] jmivalexppasy;                               // pred-as-stick intensity choices
    private final String lnsep;                                            // line separator
    private final Font mono14;                                           // Monospaced 14 font
    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
    private int nbdf;                                         // number of data files
    private int nbpredasf;                                    // number of pred-as-stick files
    // 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
    private double[] bcadreX = {0., 0., 0., 0.};                  // window frequency
    private double[] bcadreY = {0., 0., 0., 0.};                  // window intensity
    private double[] bcadrepasY = {0., 0., 0., 0.};                  // pred-as-stick window intensity
    private boolean showbar;                                         // allow showing vertical bar
    private boolean movebar;                                         // allow moving 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
    // 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 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 JMenuItem[] jmidefexasg;                               // [possible EXASG]
    // exp  selection popupmenu
    private JPopupMenu jpmexp;
    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
    // format
    private String cStr;
    private ExpJsynPoint cejpt;                                      // to sort
    private String nsavexp;                                    // saved exp file name
    private PrintWriter out1;
    private JMenuItem jmithreshold;                                 // prediction threshold


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

    /**
     * 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
        // max number of data files
        coul = 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");
        // font size
        int fontsz = 14;
        mono14 = new Font("Monospaced", Font.PLAIN, fontsz);         // default font
        // create arrays
        ntfile = new String[cmxnbdf];                              // data file type
        nbxy = new int[cmxnbdf];                                 // number of points in data file
        x = new double[cmxnbdf][];                            // frequency
        y = new double[cmxnbdf][];                            // intensity
        sxmi = new double[cmxnbdf];                              // X min (including shift)
        sxma = new double[cmxnbdf];                              // X max (including shift)
        ixdeb = new int[cmxnbdf];                                 // window first point index
        ixfin = new int[cmxnbdf];                                 // window last  point index
        use_red = new boolean[cmxnbdf];                             // use reduced data
        i_red = new int[cmxnbdf][];                               // index of reduced data
        nbxy_red = new int[cmxnbdf];                                 // reduced number of points
        ixdeb_red = new int[cmxnbdf];                                 // window first reduced point index
        ixfin_red = new int[cmxnbdf];                                 // window last  reduced point index
        show = new boolean[cmxnbdf];                             // show      the data file
        yreverse = new boolean[cmxnbdf];                             // Y reverse the data file
        xshifted = new double[cmxnbdf];                              // X shift value
        yshifted = new double[cmxnbdf];                              // Y shift value
        dfready = new boolean[cmxnbdf];                             // 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
        // X rescale
        JMenu jmrescalex = new JMenu("Rescale X");
        // Y rescale
        JMenu 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);
    }

    public static double getThreshold() {
        return threshold;
    }

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


    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

        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, true, 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 disallow 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);

        int p_width = (int) pf.getWidth();                          // paper width
        int p_height = (int) pf.getHeight();                         // paper height
        drawData(g2d, prp_wxma, prp_wyma, false, 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
        // sc_ variables define scale while drawing/printing
        // font size        % ddp_height
        double sc_fontsz = .015;
        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
        // gap              % ddp_width
        double sc_gap = .0035;
        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
        // basic y gap      % ddp_height
        double sc_epymi = .005;
        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
        // oval ray         % ddp_width
        double sc_ovray = .003;
        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
        // half fuzzy width % ddp_width
        double sc_xhalf = .003;
        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];
        // rectangle defined by a string
        Rectangle2D 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.0), (int) Math.round(ddp_cymi - ddp_gap / 2.0 + 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
                        switch (ntfile[j]) {
                            case "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);
                                        }
                                    }
                                }
                                break;
                            // stick
                            case "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);
                                    }
                                }
                                break;
                            // peakas
                            case "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);
                                    }
                                }
                                break;
                            // pred-as-stick
                            default:
                                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);
                                    }
                                }
                                break;
                        }
                    }
                }
            }
            // 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 = 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;
                        }
                        if (pfy[i] < getThreshold()) continue;
                        // 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.0) {
            p_zx0 = p_cxmi - p_gap / 2.0;
        } else if (p_x0 > p_cxma + p_gap / 2.0) {
            p_zx0 = p_cxma + p_gap / 2.0;
        }
        if (p_y0 < p_cymi - p_gap / 2.0) {
            p_zy0 = p_cymi - p_gap / 2.0;
        } else if (p_y0 > p_cyma + p_gap / 2.0) {
            p_zy0 = p_cyma + p_gap / 2.0;
        }
        // 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;
            }
        } 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");
                    }
                }
            } 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.0) {
                p_zx1 = p_cxmi - p_gap / 2.0;
            } else if (p_x1 > p_cxma + p_gap / 2.0) {
                p_zx1 = p_cxma + p_gap / 2.0;
            }
            if (p_y1 < p_cymi - p_gap / 2.0) {
                p_zy1 = p_cymi - p_gap / 2.0;
            } else if (p_y1 > p_cyma + p_gap / 2.0) {
                p_zy1 = p_cyma + p_gap / 2.0;
            }
            // 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 = 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.0) {
                p_zx1 = p_cxmi - p_gap / 2.0;
            } else if (p_x1 > p_cxma + p_gap / 2.0) {
                p_zx1 = p_cxma + p_gap / 2.0;
            }
            if (p_y1 < p_cymi - p_gap / 2.0) {
                p_zy1 = p_cymi - p_gap / 2.0;
            } else if (p_y1 > p_cyma + p_gap / 2.0) {
                p_zy1 = p_cyma + p_gap / 2.0;
            }
            //
            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();
                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();
                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();
        } 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, true); // 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;
        }
        // max nb of ticks in an exp selection popup menu
        int mxticks = 30;
        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 = 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 than 1 assignment
                    for (int l = 1; l < k; l++) {
                        cejpt = 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 (pfy[i] < getThreshold()) {
                continue;
            }
            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;
            }
        }

        // 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)
            int offset = 0;
            for (int j = 0; j < nbixpred; j++) {
                // for each selected pred
                while (pfy[ixdebpred + offset + j] < getThreshold()) offset++;
                int ci = ixdebpred + offset + 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
    }

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

    // 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 = 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 = efejsynpt[ci].get(i);
                gxaso = cejpt.getSaso();
                switch (gxaso) {
                    case " ":
                        // do nothing
                        break;
                    case "+":
                        plus = true;
                        break;
                    default:
                        minus = true;
                        break;
                }
            }
            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 = 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 (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) {
            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;
        }

        // -> set prediction threshold
        if (evt.getSource() == jmithreshold) {
            ThresholdDialog dialog = new ThresholdDialog();
            double th = dialog.getThreshold();
            threshold = th != -Double.MAX_VALUE ? th : threshold;
            jobplay.reloadPredFile();
            return;
        }

        // -> X zoom
        if (evt.getSource() == jmizoomx) {
            zoomx = true;
            zoom = false;
            return;
        }

        // -> Y zoom
        if (evt.getSource() == jmizoomy) {
            zoomy = true;
            zoom = false;
            return;
        }

        // -> pred-as-stick Y zoom
        if (evt.getSource() == jmizoompasy) {
            zoompasy = true;
            zoom = false;
            return;
        }

        // -> X rescale
        for (int i = 0; i < nbscalec; i++) {
            if (evt.getSource() == jmivalexpx[i]) {
                repropx(i);                                          // apply scaling choice
                repaint();                                           // all
                return;
            }
        }

        // -> Y rescale
        for (int i = 0; i < nbscalec; i++) {
            if (evt.getSource() == jmivalexpy[i]) {
                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]) {
                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) {
            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();
            return;
        }

        // -> restore Y scale
        if (evt.getSource() == jmirestorey) {
            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();
            return;
        }

        // -> restore pred-as-stick Y scale
        if (evt.getSource() == jmirestorepasy) {
            // full original window
            bpasymi = apasymi;
            bpasyma = apasyma;
            bpasydelta = apasydelta;
            bcadrepasY = 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]) {
                        // 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) {
                    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
                        // found
                        cejpt = 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
                        for (int i = nbass - 1; i >= 0; i--) {
                            cejpt = 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 = 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 (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 (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]) {
                        // 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]) {
                        // found
                        cejpt = 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) {
                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]) {
                    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 progress bar
        progressBar.setStringPainted(true);                          // with title
        progressBar.setString(text);                                 // set it
        progressBar.setIndeterminate(true);                          // we don't 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<>(); // possible EXASG
        for (int i = 0; i < efnbxy; i++) {
            addExasg(efexasg[i]);
        }
        addExasg(PanAff.EXASG_DEFAULT); // default SPVIEW EXASG 30 char.
        setDefexasg();

        efdefxlim(); // define first and last exp points in window
        epLink(); // link exp and pred through assignments
        efready = true; // exp file ready
        myrepaint(p_cxmi, p_epymi, 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);
        jfsp.requestFocus();
        ixpredable = -1;                                             // NO current selectable pred
        pfready = true;                                              // pred file ready
        myrepaint(p_cxmi, p_yprmi, 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 = 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 = acadrepasY.clone();              // set pred-as-stick Y axis ticks
        } else {
            bymi = aymi;                                             // intensity mini
            byma = ayma;                                             // intensity maxi
            bydelta = aydelta;                                       // intensity range
            bcadreY = 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
    }

    /**
     * @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
    }

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

    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 setPfass2(int val) {
        pfass2 = val;
    }

    public int getEfass2() {
        int indexColor;
        indexColor = efass2;
        return indexColor;
    }

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

    public void setEfass2(int val) {
        efass2 = val;
    }

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

    /**
     * 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 = acadreX.clone();
        // Y axis
        bymi = aymi;
        byma = ayma;
        bydelta = aydelta;
        bcadreY = acadreY.clone();
        // pred-as-stick Y axis
        bpasymi = apasymi;
        bpasyma = apasyma;
        bpasydelta = apasydelta;
        bcadrepasY = 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 X shift to the value given in argument
     *
     * @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];
        }
        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
        }
    }

    /**
     * Set current Y shift to the value given in argument
     *
     * @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
     *
     * @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
    }

    /**
     * Set current Y zoom to the value given in argument
     *
     * @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.
     */
    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 = 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();

        jmithreshold = new JMenuItem("Set prediction threshold");
        jmithreshold.setEnabled(true);
        jmithreshold.addActionListener(this);
        jpmassign.add(jmithreshold);

        jpmassign.addSeparator();

        // remove an assignment
        jmunass = new JMenu("Unassign");
        if (ixexp < 0) {
            // no exp  selected
            jmunass.setEnabled(false);
        } else {
            nbass = efejsynpt[ixexp].size();
            cejpt = 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 = 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 = 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 = 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 = 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 = 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 = 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
        // set default EXASG
        JMenu 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 than one assignment
            aejpt = new ExpJsynPoint[nbass];                         // array
            for (int k = 0; k < nbass; k++) {
                aejpt[k] = efejsynpt[ci].get(k);       // copy
            }
            Arrays.sort(aejpt);                                      // sort
            efejsynpt[ci] = new ArrayList<>();
            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) {

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

    // set default EXASG
    private void setDefexasg() {

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

    /**
     * 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 = 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 = efejsynpt[i].get(j);
                    // NUO (5 char, RIGHT)
                    String cStrNUO = "     " + ("" + (i + 1)).trim();
                    cStrNUO = cStrNUO.substring(cStrNUO.length() - 5);
                    // 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);

                    /* we don't want to store shifted experiment data, so we unshift for the record if any */
                    double sefx = efx[i];
                    double sefy = efy[i];

                    if (efass2 >= 0) {
                        sefx -= xshifted[efass2];
                        sefy -= yshifted[efass2];
                    }

                    cStr = (" " +                       // ISP
                            cStrNUO +                       // NUO           line number
                            FortranFormat.formFreq(sefx) +                       // FOBS          frequency
                            " " +
                            cejpt.getFaso() +                       // FASO          frequency mark
                            FortranFormat.formInt(sefy) +                       // 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 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 = 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());
    }

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

    /**
     * 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
        // nb of points
        lsmi = lsy[0];
        lsma = lsy[0];
        for (double v : lsy) {
            if (v < lsmi) {
                lsmi = v;
            }
            if (v > lsma) {
                lsma = v;
            }
        }
    }

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

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

        int[] ci_red;                                   // 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

                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() {
        if (this.jobplay.getProject() != null) {
            this.jobplay.getProject().setUnsave(true);
        }
    }

}
