com.chart.SwingChart.java Source code

Java tutorial

Introduction

Here is the source code for com.chart.SwingChart.java

Source

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.chart;

import com.Global;
import com.interfaz.skeleton.Skeleton;
import com.interfaz.skeleton.PseudoModalDialog;
import demo.CircleDrawer;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import static java.lang.Math.random;
import java.text.Format;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.embed.swing.SwingNode;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.SnapshotParameters;
import javafx.scene.control.ColorPicker;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextField;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.input.Clipboard;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.ContextMenuEvent;
import javafx.scene.input.DataFormat;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import javafx.stage.Window;
import javafx.util.Callback;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.KeyStroke;
import org.jfree.chart.ChartMouseEvent;
import org.jfree.chart.ChartMouseListener;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.annotations.XYPointerAnnotation;
import org.jfree.chart.axis.AxisLocation;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.NumberTickUnit;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.entity.XYItemEntity;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.title.LegendTitle;
import org.jfree.data.general.SeriesException;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.ui.RectangleEdge;
import org.jfree.ui.RectangleInsets;

/**
 *
 * @author MIGUEL_ANGEL
 * @see JFreeChart
 */
public class SwingChart implements ChartInterface {
    /**
     * List of axes
     */
    List<AxisChart> axes;
    /**
     * Parent
     */
    Skeleton skeleton;

    /**
     * Chart name
     */
    String name;

    /**
     * JFreeChart
     */
    public JFreeChart chart;
    /**
     * JFreeChart chart panel
     */
    public ChartPanel chartPanel;

    /**
     * List of series
     */
    public List<Series> seriesList = new ArrayList<>();

    /**
     * List of datasets
     */
    List<XYSeriesCollection> datasetList = new ArrayList<>();

    /**
     * List of axes
     */
    public List<NumberAxis> AxesList = new ArrayList<>();

    /**
     * Abcissa axis
     */
    public ValueAxis abcissaAxis;

    /**
     * Plot
     */
    XYPlot plot;

    /**
     * Context menu item list
     */
    public List<MenuItem> contextMenuList = new ArrayList<>();

    /**
     * Chart frame
     */
    VBox chartFrame;

    /**
     * Legend frame
     */
    VBox legendFrame = new VBox(5);

    /**
     * Context menu
     */
    ContextMenu menu;

    /**
     * Background color as string
     */
    public String strBackgroundColor = "#343434";
    /**
     * background color of chart as string
     */
    public String strChartBackgroundColor = "#226262";
    /**
     * Tick color
     */
    public String strTickColor = "white";
    /**
     * Gridline color
     */
    public String strGridlineColor = "white";

    /**
     * Font size
     */
    public float fontSize = 11;

    /**
     * Abcissa format
     */
    protected Format abcissaFormat;
    /**
     * Ordinate format
     */
    protected final Format ordinateFormat;

    /**
     * Chart mouse listener
     */
    protected ChartMouseListener cml;

    /**
     * Update background, tick and gridline colors
     * @param cfg cfg[0] Background, cfg[1] Chart background, cfg[2] y cfg[3] gridline
     */
    public void updateChartColors(String[] cfg) {
        strBackgroundColor = cfg[0];
        for (Node le : legendFrame.getChildren()) {
            if (le instanceof LegendAxis) {
                le.setStyle("-fx-background-color:" + strBackgroundColor);
                ((LegendAxis) le).selected = false;
            }
        }
        chart.setBackgroundPaint(scene2awtColor(javafx.scene.paint.Color.web(strBackgroundColor)));
        chartPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4),
                BorderFactory.createLineBorder(scene2awtColor(javafx.scene.paint.Color.web(strBackgroundColor)))));
        chartPanel.setBackground(scene2awtColor(javafx.scene.paint.Color.web(strBackgroundColor)));

        legendFrame.setStyle("marco: " + strBackgroundColor + ";-fx-background-color: marco;");

        strChartBackgroundColor = cfg[1];
        ;
        plot.setBackgroundPaint(scene2awtColor(javafx.scene.paint.Color.web(strChartBackgroundColor)));

        for (Node le : legendFrame.getChildren()) {
            if (le instanceof LegendAxis) {
                le.setStyle("-fx-background-color:" + strBackgroundColor);
                ((LegendAxis) le).selected = false;
                for (Node nn : ((LegendAxis) le).getChildren()) {
                    if (nn instanceof Label) {
                        ((Label) nn).setStyle("fondo: " + strChartBackgroundColor
                                + ";-fx-background-color: fondo;-fx-text-fill: ladder(fondo, white 49%, black 50%);-fx-padding:5px;-fx-background-radius: 5;-fx-font-size: "
                                + String.valueOf(fontSize) + "px");
                    }
                }
            }
        }

        strGridlineColor = cfg[2];
        ;
        plot.setDomainGridlinePaint(scene2awtColor(javafx.scene.paint.Color.web(strGridlineColor)));
        plot.setRangeGridlinePaint(scene2awtColor(javafx.scene.paint.Color.web(strGridlineColor)));

        strTickColor = cfg[3];
        ;
        abcissaAxis.setLabelPaint(scene2awtColor(javafx.scene.paint.Color.web(strTickColor)));
        abcissaAxis.setTickLabelPaint(scene2awtColor(javafx.scene.paint.Color.web(strTickColor)));
        for (NumberAxis ejeOrdenada : AxesList) {
            ejeOrdenada.setLabelPaint(scene2awtColor(javafx.scene.paint.Color.web(strTickColor)));
            ejeOrdenada.setTickLabelPaint(scene2awtColor(javafx.scene.paint.Color.web(strTickColor)));
        }
    }

    /**
     * 
     * @param max maximum item count for all series
     */
    public void setMaxItemCount(int max) {
        for (Series series : seriesList) {
            series.setMaximumItemCount(max);
        }
    }

    /**
     * Abcissa format
     * @param af Abcissa format to set
     */
    public void setAbcissaFormat(Format af) {
        this.abcissaFormat = af;
    }

    /**
     * 
     * @param i Index
     * @return default color by index
     */
    public javafx.scene.paint.Color getColor(int i) {
        switch (i) {
        case 0:
            return javafx.scene.paint.Color.AQUA;
        case 1:
            return javafx.scene.paint.Color.BLUE;
        case 2:
            return javafx.scene.paint.Color.DARKORCHID;
        case 3:
            return javafx.scene.paint.Color.BROWN;
        case 4:
            return javafx.scene.paint.Color.CADETBLUE;
        case 5:
            return javafx.scene.paint.Color.CHARTREUSE;
        case 6:
            return javafx.scene.paint.Color.CORAL;
        case 7:
            return javafx.scene.paint.Color.CRIMSON;
        case 8:
            return javafx.scene.paint.Color.DARKBLUE;
        case 9:
            return javafx.scene.paint.Color.DARKGREEN;
        case 10:
            return javafx.scene.paint.Color.DARKORANGE;
        case 11:
            return javafx.scene.paint.Color.DEEPSKYBLUE;
        case 12:
            return javafx.scene.paint.Color.FORESTGREEN;
        case 13:
            return javafx.scene.paint.Color.FUCHSIA;
        case 14:
            return javafx.scene.paint.Color.GOLD;
        case 15:
            return javafx.scene.paint.Color.LAWNGREEN;
        default:
            return new javafx.scene.paint.Color(random(), random(), random(), 1);
        }
    }

    /**
     * 
     * @param color JavaFX color
     * @return AWT Color
     */
    final java.awt.Color scene2awtColor(javafx.scene.paint.Color color) {
        return new java.awt.Color((float) color.getRed(), (float) color.getGreen(), (float) color.getBlue());
    }

    /**
     * 
     * @param name Axis name
     * @return Legend axis by name
     */
    LegendAxis getLegendAxis(String name) {
        for (Node n : legendFrame.getChildren()) {
            if (n instanceof LegendAxis) {
                if (((LegendAxis) n).getName().equals(name)) {
                    return (LegendAxis) n;
                }
            }
        }
        return null;
    }

    /**
     * Export chart values to a csv file
     * @param file CSV file
     */
    private void export(File file) {
        String registro;
        int n = 0;
        int ne = 0;
        String cabecera = "Time;";
        for (int i = 0; i < plot.getDatasetCount(); i++) {
            XYSeriesCollection ds = (XYSeriesCollection) plot.getDataset(i);
            for (int j = 0; j < ds.getSeriesCount(); j++) {
                n++;
                XYSeries s = (XYSeries) ds.getSeries(j);
                cabecera += s.getKey().toString() + ";";
                if (n == 1) {
                    ne = s.getItemCount();
                }
            }
        }
        String[][] exportacion = new String[ne][n + 1];
        n = 0;
        for (int i = 0; i < plot.getDatasetCount(); i++) {
            XYSeriesCollection ds = (XYSeriesCollection) plot.getDataset(i);
            for (int j = 0; j < ds.getSeriesCount(); j++) {
                XYSeries s = (XYSeries) ds.getSeries(j);
                n++;
                for (int e = 0; e < ne; e++) {
                    try {
                        exportacion[e][n] = ordinateFormat.format(s.getDataItem(e).getYValue());
                    } catch (Exception ex) {
                        exportacion[e][n] = "";
                    }
                    if (n == 1) {

                        exportacion[e][0] = abcissaFormat.format(s.getDataItem(e).getXValue());

                    }
                }
            }
        }
        FileWriter fichero;

        PrintWriter pw;

        try {
            fichero = new FileWriter(file.getAbsolutePath(), false);
            pw = new PrintWriter(fichero);
            pw.println(cabecera);
            for (String[] exp : exportacion) {
                String strRegistro = "";
                for (String e : exp) {
                    strRegistro += e + ";";
                }
                pw.println(strRegistro);
            }

            pw.close();
        } catch (IOException ex) {
            Global.info.log(ex);
        }
    }

    /**
     * Copy to clipboard
     * @param node JavaFX Node to copy
     */
    public final void copyClipboard(final Node node) {

        WritableImage captura = node.snapshot(new SnapshotParameters(), null);

        ImageView iv = new ImageView(captura);

        Clipboard clipboard = Clipboard.getSystemClipboard();
        ClipboardContent content = new ClipboardContent();
        content.put(DataFormat.IMAGE, captura);
        clipboard.setContent(content);

    }

    /**
     * 
     * @param name Chart name
     * @param parent Skeleton parent
     * @param axes Configuration of axes
     * @param abcissaName Abcissa name
     */
    public SwingChart(String name, final Skeleton parent, List<AxisChart> axes, String abcissaName) {
        this.skeleton = parent;
        this.axes = axes;
        this.name = name;

        this.abcissaFormat = NumberFormat.getInstance(Locale.getDefault());
        this.ordinateFormat = NumberFormat.getInstance(Locale.getDefault());

        plot = new XYPlot();
        plot.setBackgroundPaint(scene2awtColor(javafx.scene.paint.Color.web(strChartBackgroundColor)));
        plot.setDomainGridlinePaint(scene2awtColor(javafx.scene.paint.Color.web(strGridlineColor)));
        plot.setRangeGridlinePaint(scene2awtColor(javafx.scene.paint.Color.web(strGridlineColor)));
        plot.setAxisOffset(new RectangleInsets(5.0, 5.0, 5.0, 5.0));

        abcissaAxis = new NumberAxis(abcissaName);
        ((NumberAxis) abcissaAxis).setAutoRangeIncludesZero(false);
        abcissaAxis.setTickLabelFont(new Font("SansSerif", Font.PLAIN, 12));
        abcissaAxis.setLabelFont(new Font("SansSerif", Font.PLAIN, 12));
        abcissaAxis.setLabelPaint(scene2awtColor(javafx.scene.paint.Color.web(strTickColor)));
        abcissaAxis.setTickLabelPaint(scene2awtColor(javafx.scene.paint.Color.web(strTickColor)));
        abcissaAxis.setAutoRange(true);
        abcissaAxis.setLowerMargin(0.0);
        abcissaAxis.setUpperMargin(0.0);
        abcissaAxis.setTickLabelsVisible(true);
        abcissaAxis.setLabelFont(abcissaAxis.getLabelFont().deriveFont(fontSize));
        abcissaAxis.setTickLabelFont(abcissaAxis.getLabelFont().deriveFont(fontSize));

        plot.setDomainAxis(abcissaAxis);

        for (int i = 0; i < axes.size(); i++) {
            AxisChart categoria = axes.get(i);
            addAxis(categoria.getName());

            for (int j = 0; j < categoria.configSerieList.size(); j++) {
                SimpleSeriesConfiguration cs = categoria.configSerieList.get(j);
                addSeries(categoria.getName(), cs);
            }
        }
        chart = new JFreeChart("", new Font("SansSerif", Font.BOLD, 16), plot, false);

        chart.setBackgroundPaint(scene2awtColor(javafx.scene.paint.Color.web(strBackgroundColor)));

        chartPanel = new ChartPanel(chart);
        chartPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4),
                BorderFactory.createLineBorder(scene2awtColor(javafx.scene.paint.Color.web(strBackgroundColor)))));

        chartPanel.getInputMap().put(KeyStroke.getKeyStroke("ESCAPE"), "escape");
        chartPanel.getActionMap().put("escape", new AbstractAction() {

            @Override
            public void actionPerformed(java.awt.event.ActionEvent e) {
                for (int i = 0; i < plot.getDatasetCount(); i++) {
                    XYDataset test = plot.getDataset(i);
                    XYItemRenderer r = plot.getRenderer(i);
                    r.removeAnnotations();
                }
            }
        });

        chartPanel.addChartMouseListener(cml = new ChartMouseListener() {
            @Override
            public void chartMouseClicked(ChartMouseEvent event) {
            }

            @Override
            public void chartMouseMoved(ChartMouseEvent event) {
                try {
                    XYItemEntity xyitem = (XYItemEntity) event.getEntity(); // get clicked entity
                    XYDataset dataset = (XYDataset) xyitem.getDataset(); // get data set    
                    double x = dataset.getXValue(xyitem.getSeriesIndex(), xyitem.getItem());
                    double y = dataset.getYValue(xyitem.getSeriesIndex(), xyitem.getItem());

                    final XYPlot plot = chart.getXYPlot();
                    for (int i = 0; i < plot.getDatasetCount(); i++) {
                        XYDataset test = plot.getDataset(i);
                        XYItemRenderer r = plot.getRenderer(i);
                        r.removeAnnotations();
                        if (test == dataset) {
                            NumberAxis ejeOrdenada = AxesList.get(i);
                            double y_max = ejeOrdenada.getUpperBound();
                            double y_min = ejeOrdenada.getLowerBound();
                            double x_max = abcissaAxis.getUpperBound();
                            double x_min = abcissaAxis.getLowerBound();
                            double angulo;
                            if (y > (y_max + y_min) / 2 && x > (x_max + x_min) / 2) {
                                angulo = 3.0 * Math.PI / 4.0;
                            } else if (y > (y_max + y_min) / 2 && x < (x_max + x_min) / 2) {
                                angulo = 1.0 * Math.PI / 4.0;
                            } else if (y < (y_max + y_min) / 2 && x < (x_max + x_min) / 2) {
                                angulo = 7.0 * Math.PI / 4.0;
                            } else {
                                angulo = 5.0 * Math.PI / 4.0;
                            }

                            CircleDrawer cd = new CircleDrawer((Color) r.getSeriesPaint(xyitem.getSeriesIndex()),
                                    new BasicStroke(2.0f), null);
                            //XYAnnotation bestBid = new XYDrawableAnnotation(dataset.getXValue(xyitem.getSeriesIndex(), xyitem.getItem()), dataset.getYValue(xyitem.getSeriesIndex(), xyitem.getItem()), 11, 11, cd);
                            String txt = "X:" + abcissaFormat.format(x) + ", Y:" + ordinateFormat.format(y);
                            XYPointerAnnotation anotacion = new XYPointerAnnotation(txt,
                                    dataset.getXValue(xyitem.getSeriesIndex(), xyitem.getItem()),
                                    dataset.getYValue(xyitem.getSeriesIndex(), xyitem.getItem()), angulo);
                            anotacion.setTipRadius(10.0);
                            anotacion.setBaseRadius(35.0);
                            anotacion.setFont(new Font("SansSerif", Font.PLAIN, 10));

                            if (Long.parseLong((strChartBackgroundColor.replace("#", "")), 16) > 0xffffff / 2) {
                                anotacion.setPaint(Color.black);
                                anotacion.setArrowPaint(Color.black);
                            } else {
                                anotacion.setPaint(Color.white);
                                anotacion.setArrowPaint(Color.white);
                            }

                            //bestBid.setPaint((Color) r.getSeriesPaint(xyitem.getSeriesIndex()));
                            r.addAnnotation(anotacion);
                        }
                    }

                    //LabelValorVariable.setSize(LabelValorVariable.getPreferredSize());
                } catch (NullPointerException | ClassCastException ex) {

                }
            }
        });

        chartPanel.setPopupMenu(null);
        chartPanel.setBackground(scene2awtColor(javafx.scene.paint.Color.web(strBackgroundColor)));

        SwingNode sn = new SwingNode();
        sn.setContent(chartPanel);
        chartFrame = new VBox();
        chartFrame.getChildren().addAll(sn, legendFrame);
        VBox.setVgrow(sn, Priority.ALWAYS);
        VBox.setVgrow(legendFrame, Priority.NEVER);

        chartFrame.getStylesheets().addAll(SwingChart.class.getResource("overlay-chart.css").toExternalForm());

        legendFrame.setStyle("marco: " + strBackgroundColor + ";-fx-background-color: marco;");

        MenuItem mi;
        mi = new MenuItem("Print");
        mi.setOnAction((ActionEvent t) -> {
            print(chartFrame);
        });
        contextMenuList.add(mi);

        sn.setOnMouseClicked((MouseEvent t) -> {
            if (menu != null) {
                menu.hide();
            }
            if (t.getClickCount() == 2) {
                backgroundEdition();
            }
        });

        mi = new MenuItem("Copy to clipboard");
        mi.setOnAction((ActionEvent t) -> {
            copyClipboard(chartFrame);
        });
        contextMenuList.add(mi);

        mi = new MenuItem("Export values");
        mi.setOnAction((ActionEvent t) -> {
            FileChooser fileChooser = new FileChooser();
            fileChooser.setTitle("Export to file");
            fileChooser.getExtensionFilters()
                    .addAll(new FileChooser.ExtensionFilter("Comma Separated Values", "*.csv"));

            Window w = null;
            try {
                w = parent.getScene().getWindow();
            } catch (NullPointerException e) {

            }
            File file = fileChooser.showSaveDialog(w);
            if (file != null) {
                export(file);
            }
        });
        contextMenuList.add(mi);

        chartFrame.setOnContextMenuRequested((ContextMenuEvent t) -> {
            if (menu != null) {
                menu.hide();
            }
            menu = new ContextMenu();
            menu.getItems().addAll(contextMenuList);
            menu.show(chartFrame, t.getScreenX(), t.getScreenY());
        });

    }

    /**
     * Send node to printer
     * @param node JavaFX node
     */
    public final void print(final Node node) {
        LegendTitle legend = new LegendTitle(chart.getPlot());
        legend.setItemFont(legend.getItemFont().deriveFont(fontSize));
        chart.addLegend(legend);
        legend.setPosition(RectangleEdge.BOTTOM);
        chart.setTitle(name);
        chartPanel.createChartPrintJob();
        chart.removeLegend();
        chart.setTitle("");
    }

    /**
     * Series edition
     * @param series Series to edit
     */
    void editSeries(final Series series) {
        String[] style = series.getStyle().split(";");
        String strColor = "black";
        final TextField editWidth = new TextField();

        String tempS = "null";
        for (String e : style) {
            if (e.contains("color: ")) {
                strColor = e.replace("color: ", "");
            } else if (e.contains("width: ")) {
                editWidth.setText(e.replace("width: ", ""));
            } else if (e.contains("shape: ")) {
                tempS = e.replace("shape: ", "");
            }
        }
        final String symbol = tempS;

        final List<SeriesShape> symbolList = new ArrayList<>();
        final ObservableList<SeriesShape> symbolListModel;
        final ListView<SeriesShape> comboSymbol = new ListView();
        symbolList.add(new SeriesShape("null", javafx.scene.paint.Color.web(strColor)));
        symbolList.add(new SeriesShape("rectangle", javafx.scene.paint.Color.web(strColor)));
        symbolList.add(new SeriesShape("circle", javafx.scene.paint.Color.web(strColor)));
        symbolList.add(new SeriesShape("triangle", javafx.scene.paint.Color.web(strColor)));
        symbolList.add(new SeriesShape("crux", javafx.scene.paint.Color.web(strColor)));
        symbolList.add(new SeriesShape("diamond", javafx.scene.paint.Color.web(strColor)));
        symbolList.add(new SeriesShape("empty rectangle", javafx.scene.paint.Color.web(strColor)));
        symbolList.add(new SeriesShape("empty circle", javafx.scene.paint.Color.web(strColor)));
        symbolList.add(new SeriesShape("empty triangle", javafx.scene.paint.Color.web(strColor)));
        symbolList.add(new SeriesShape("empty diamond", javafx.scene.paint.Color.web(strColor)));

        symbolListModel = FXCollections.observableList(symbolList);
        comboSymbol.setItems(symbolListModel);
        comboSymbol.setCellFactory(new Callback<ListView<SeriesShape>, ListCell<SeriesShape>>() {
            @Override
            public ListCell<SeriesShape> call(ListView<SeriesShape> p) {
                ListCell<SeriesShape> cell = new ListCell<SeriesShape>() {
                    @Override
                    protected void updateItem(SeriesShape t, boolean bln) {
                        super.updateItem(t, bln);
                        if (t != null) {
                            setText("");
                            setGraphic(t.getShapeGraphic());
                        }
                    }
                };

                return cell;
            }
        });
        for (SeriesShape smb : symbolListModel) {
            if (smb.getName().equals(symbol)) {
                comboSymbol.getSelectionModel().select(smb);
            }
        }

        final ColorPicker colorPicker = new ColorPicker(javafx.scene.paint.Color.web(strColor));

        colorPicker.setOnAction((ActionEvent t) -> {
            String sc = colorPicker.getValue().toString();
            symbolListModel.clear();
            symbolListModel.add(new SeriesShape("null", javafx.scene.paint.Color.web(sc)));
            symbolListModel.add(new SeriesShape("rectangle", javafx.scene.paint.Color.web(sc)));
            symbolListModel.add(new SeriesShape("circle", javafx.scene.paint.Color.web(sc)));
            symbolListModel.add(new SeriesShape("triangle", javafx.scene.paint.Color.web(sc)));
            symbolListModel.add(new SeriesShape("crux", javafx.scene.paint.Color.web(sc)));
            symbolListModel.add(new SeriesShape("diamond", javafx.scene.paint.Color.web(sc)));
            symbolListModel.add(new SeriesShape("empty rectangle", javafx.scene.paint.Color.web(sc)));
            symbolListModel.add(new SeriesShape("empty circle", javafx.scene.paint.Color.web(sc)));
            symbolListModel.add(new SeriesShape("empty triangle", javafx.scene.paint.Color.web(sc)));
            symbolListModel.add(new SeriesShape("empty diamond", javafx.scene.paint.Color.web(sc)));

            comboSymbol.setItems(symbolListModel);
            for (SeriesShape smb : symbolListModel) {
                if (smb.getName().equals(symbol)) {
                    comboSymbol.getSelectionModel().select(smb);
                }
            }
        });

        GridPane grid = new GridPane();

        grid.setHgap(10);
        grid.setVgap(10);
        grid.setPadding(new Insets(0, 10, 0, 10));

        grid.add(new Label("Series"), 0, 0);
        grid.add(new Label(series.getKey().toString()), 1, 0);
        grid.add(new Label("Color"), 0, 1);
        grid.add(colorPicker, 1, 1);
        grid.add(new Label("Width"), 0, 2);
        grid.add(editWidth, 1, 2);
        grid.add(new Label("Shape"), 0, 3);
        grid.add(comboSymbol, 1, 3);

        new PseudoModalDialog(skeleton, grid, true) {
            @Override
            public boolean validation() {
                String strColor = colorPicker.getValue().toString();
                String strWidth = editWidth.getText();
                double dWidth = Double.valueOf(strWidth);
                String strSimbolo = "null";
                SeriesShape simb = new SeriesShape(comboSymbol.getSelectionModel().getSelectedItem().toString(),
                        javafx.scene.paint.Color.web(strColor));

                XYItemRenderer renderer = (XYItemRenderer) plot.getRenderer(series.getAxisIndex());

                renderer.setSeriesPaint(series.getSeriesIndex(), scene2awtColor(colorPicker.getValue()));

                try {
                    if (Double.valueOf(strWidth) > 0) {
                        ((XYLineAndShapeRenderer) renderer).setSeriesLinesVisible(series.getSeriesIndex(), true);
                        renderer.setSeriesStroke(series.getSeriesIndex(),
                                new BasicStroke(Integer.valueOf(strWidth)));
                    } else {
                        ((XYLineAndShapeRenderer) renderer).setSeriesLinesVisible(series.getSeriesIndex(), false);
                    }
                } catch (NumberFormatException ex) {

                }

                if (simb.getName().contains("null")) {
                    ((XYLineAndShapeRenderer) renderer).setSeriesShapesVisible(series.getSeriesIndex(), false);
                    renderer.setSeriesShape(series.getSeriesIndex(), null);
                } else {
                    ((XYLineAndShapeRenderer) renderer).setSeriesShapesVisible(series.getSeriesIndex(), true);
                    renderer.setSeriesShape(series.getSeriesIndex(), simb.getShapeAWT());
                    if (simb.getName().contains("empty")) {
                        ((XYLineAndShapeRenderer) renderer).setSeriesShapesFilled(series.getSeriesIndex(), false);
                    } else {
                        ((XYLineAndShapeRenderer) renderer).setSeriesShapesFilled(series.getSeriesIndex(), true);
                    }

                }

                series.setStyle(
                        "color: " + strColor + ";width: " + editWidth.getText() + ";shape: " + strSimbolo + ";");

                for (Node le : legendFrame.getChildren()) {
                    if (le instanceof LegendAxis) {
                        for (Node nn : ((LegendAxis) le).getChildren()) {
                            if (nn instanceof Label) {
                                if (((Label) nn).getText().equals(series.getKey().toString())) {
                                    ((Label) nn).setGraphic(simb.getShapeGraphic());
                                }
                            }
                        }
                    }
                }
                return true;
            }
        }.show();

    }

    /**
     * Background edition
     */
    final public void backgroundEdition() {
        final ColorPicker colorPickerChartBackground = new ColorPicker(
                javafx.scene.paint.Color.web(strChartBackgroundColor));
        colorPickerChartBackground.setMaxWidth(Double.MAX_VALUE);
        final ColorPicker colorPickerGridline = new ColorPicker(javafx.scene.paint.Color.web(strGridlineColor));
        colorPickerGridline.setMaxWidth(Double.MAX_VALUE);
        final ColorPicker colorPickerBackground = new ColorPicker(javafx.scene.paint.Color.web(strBackgroundColor));
        colorPickerBackground.setMaxWidth(Double.MAX_VALUE);
        final ColorPicker colorPickerTick = new ColorPicker(javafx.scene.paint.Color.web(strTickColor));
        colorPickerTick.setMaxWidth(Double.MAX_VALUE);
        final TextField tfFontSize = new TextField();
        tfFontSize.setMaxWidth(Double.MAX_VALUE);

        GridPane grid = new GridPane();
        grid.setHgap(10);
        grid.setVgap(10);
        grid.setPadding(new Insets(0, 10, 0, 10));

        grid.add(new Label("Background color"), 0, 0);
        grid.add(colorPickerChartBackground, 1, 0);
        grid.add(new Label("Gridline color"), 0, 1);
        grid.add(colorPickerGridline, 1, 1);
        grid.add(new Label("Frame color"), 0, 2);
        grid.add(colorPickerBackground, 1, 2);
        grid.add(new Label("Tick color"), 0, 3);
        grid.add(colorPickerTick, 1, 3);
        grid.add(new Label("Font size"), 0, 4);
        grid.add(tfFontSize, 1, 4);
        tfFontSize.setText(String.valueOf(fontSize));

        new PseudoModalDialog(skeleton, grid, true) {

            @Override
            public boolean validation() {
                fontSize = Float.valueOf(tfFontSize.getText().replace(",", "."));
                strBackgroundColor = colorPickerBackground.getValue().toString().replace("0x", "#");
                for (Node le : legendFrame.getChildren()) {
                    if (le instanceof LegendAxis) {
                        le.setStyle("-fx-background-color:" + strBackgroundColor);
                        ((LegendAxis) le).selected = false;
                    }
                }
                chart.setBackgroundPaint(scene2awtColor(javafx.scene.paint.Color.web(strBackgroundColor)));
                chartPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4),
                        BorderFactory.createLineBorder(
                                scene2awtColor(javafx.scene.paint.Color.web(strBackgroundColor)))));
                chartPanel.setBackground(scene2awtColor(javafx.scene.paint.Color.web(strBackgroundColor)));

                legendFrame.setStyle("marco: " + colorPickerBackground.getValue().toString().replace("0x", "#")
                        + ";-fx-background-color: marco;");

                strChartBackgroundColor = colorPickerChartBackground.getValue().toString().replace("0x", "#");
                plot.setBackgroundPaint(scene2awtColor(javafx.scene.paint.Color.web(strChartBackgroundColor)));

                for (Node le : legendFrame.getChildren()) {
                    if (le instanceof LegendAxis) {
                        le.setStyle("-fx-background-color:" + strBackgroundColor);
                        ((LegendAxis) le).selected = false;
                        for (Node nn : ((LegendAxis) le).getChildren()) {
                            if (nn instanceof Label) {
                                ((Label) nn).setStyle("fondo: "
                                        + colorPickerChartBackground.getValue().toString().replace("0x", "#")
                                        + ";-fx-background-color: fondo;-fx-text-fill: ladder(fondo, white 49%, black 50%);-fx-padding:5px;-fx-background-radius: 5;-fx-font-size: "
                                        + String.valueOf(fontSize) + "px");
                            }
                        }
                    }
                }

                strGridlineColor = colorPickerGridline.getValue().toString().replace("0x", "#");
                plot.setDomainGridlinePaint(scene2awtColor(javafx.scene.paint.Color.web(strGridlineColor)));
                plot.setRangeGridlinePaint(scene2awtColor(javafx.scene.paint.Color.web(strGridlineColor)));

                strTickColor = colorPickerTick.getValue().toString().replace("0x", "#");
                abcissaAxis.setLabelPaint(scene2awtColor(javafx.scene.paint.Color.web(strTickColor)));
                abcissaAxis.setTickLabelPaint(scene2awtColor(javafx.scene.paint.Color.web(strTickColor)));
                abcissaAxis.setLabelFont(abcissaAxis.getLabelFont().deriveFont(fontSize));
                abcissaAxis.setTickLabelFont(abcissaAxis.getLabelFont().deriveFont(fontSize));

                for (NumberAxis ejeOrdenada : AxesList) {
                    ejeOrdenada.setLabelPaint(scene2awtColor(javafx.scene.paint.Color.web(strTickColor)));
                    ejeOrdenada.setTickLabelPaint(scene2awtColor(javafx.scene.paint.Color.web(strTickColor)));
                    ejeOrdenada.setLabelFont(ejeOrdenada.getLabelFont().deriveFont(fontSize));
                    ejeOrdenada.setTickLabelFont(ejeOrdenada.getLabelFont().deriveFont(fontSize));
                }
                return true;
            }
        }.show();

    }

    /**
     * Set lower and upper limits for an ordinate
     * @param axis Axis to configure
     */
    void setOrdinateRange(final NumberAxis axis) {
        axis.setAutoRange(false);
        GridPane grid = new GridPane();
        grid.setHgap(10);
        grid.setVgap(10);
        grid.setPadding(new Insets(0, 10, 0, 10));
        final TextField tfMax;
        final TextField tfMin;
        final TextField tfTick;
        final TextField tfFuente;
        grid.add(new Label("Axis"), 0, 0);
        grid.add(new Label(axis.getLabel()), 1, 0);
        grid.add(new Label("Lower"), 0, 1);
        grid.add(tfMin = new TextField(), 1, 1);
        grid.add(new Label("Upper"), 0, 2);
        grid.add(tfMax = new TextField(), 1, 2);
        grid.add(new Label("Space"), 0, 3);
        grid.add(tfTick = new TextField(), 1, 3);

        tfMin.setText(String.valueOf(axis.getLowerBound()));
        tfMax.setText(String.valueOf(axis.getUpperBound()));
        tfTick.setText(String.valueOf(axis.getTickUnit().getSize()));

        new PseudoModalDialog(skeleton, grid, true) {
            @Override
            public boolean validation() {
                axis.setLowerBound(Double.valueOf(tfMin.getText()));
                axis.setUpperBound(Double.valueOf(tfMax.getText()));
                axis.setTickUnit(new NumberTickUnit(Double.valueOf(tfTick.getText())));
                return true;
            }
        }.show();

    }

    /**
     * Set lower and upper limits for an ordinate
     * @param i Axis index
     * @param lower Lower limit
     * @param upper Upper limit
     * @param tick Tick unit
     */
    public void setOrdinateRange(int i, double lower, double upper, double tick) {
        NumberAxis e = AxesList.get(i);
        e.setAutoRange(false);
        e.setLowerBound(lower);
        e.setUpperBound(upper);
        e.setTickUnit(new NumberTickUnit(tick));

    }

    /**
     * Add axis
     * @param name new axis name 
     */
    @Override
    public final void addAxis(String name) {
        boolean encontrado = false;
        for (AxisChart categoria : axes) {
            if (categoria.getName().equals(name)) {
                encontrado = true;
                break;
            }
        }
        if (!encontrado) {
            axes.add(new AxisChart((name)));
        }

        XYSeriesCollection dataset = new XYSeriesCollection();
        NumberAxis ejeOrdenada = new NumberAxis(name);

        ejeOrdenada.setLabelPaint(scene2awtColor(javafx.scene.paint.Color.web(strTickColor)));
        ejeOrdenada.setTickLabelPaint(scene2awtColor(javafx.scene.paint.Color.web(strTickColor)));
        ejeOrdenada.setLabelFont(ejeOrdenada.getLabelFont().deriveFont(fontSize));
        ejeOrdenada.setTickLabelFont(ejeOrdenada.getLabelFont().deriveFont(fontSize));

        int i = datasetList.size();

        datasetList.add(dataset);
        AxesList.add(ejeOrdenada);
        plot.setDataset(i, dataset);
        plot.setRangeAxis(i, ejeOrdenada);
        plot.setRangeAxisLocation(i, AxisLocation.BOTTOM_OR_LEFT);
        plot.mapDatasetToRangeAxis(i, i);
        XYItemRenderer renderer = new XYLineAndShapeRenderer(true, true);
        if (i == 0) {
            plot.setRenderer(renderer);
        } else {
            plot.setRenderer(i, renderer);
        }

        final LegendAxis le;
        final int indiceLeyenda = legendFrame.getChildren().size();

        legendFrame.getChildren().add(le = new LegendAxis(name));

        le.setOnMouseClicked((MouseEvent t) -> {
            if (le.selected && t.getClickCount() == 2) {
                setOrdinateRange(AxesList.get(indiceLeyenda));
            }
        });

        le.setOnMouseEntered((MouseEvent t) -> {
            le.setStyle("-fx-background-color:blue");
            le.selected = true;
            AxesList.get(indiceLeyenda).setLabelPaint(scene2awtColor(javafx.scene.paint.Color.web("blue")));
            AxesList.get(indiceLeyenda).setTickLabelPaint(scene2awtColor(javafx.scene.paint.Color.web("blue")));
        });

        le.setOnMouseExited((MouseEvent t) -> {
            le.setStyle("-fx-background-color:" + strBackgroundColor);
            le.selected = false;
            AxesList.get(indiceLeyenda).setLabelPaint(scene2awtColor(javafx.scene.paint.Color.web(strTickColor)));
            AxesList.get(indiceLeyenda)
                    .setTickLabelPaint(scene2awtColor(javafx.scene.paint.Color.web(strTickColor)));
        });
    }

    /**
     * 
     * @param axis Axis name to wich the new series belongs
     * @param cs Series Coinfiguration
     */
    @Override
    public final void addSeries(String axis, SimpleSeriesConfiguration cs) {
        for (int i = 0; i < axes.size(); i++) {
            if (axes.get(i).getName().equals(axis)) {
                String strColor;
                javafx.scene.paint.Color color;
                int indice = seriesList.size();
                if (cs.getColor() == null) {
                    color = getColor(indice);
                } else {
                    color = cs.getColor();
                }
                strColor = color.toString();

                XYSeriesCollection dataset = datasetList.get(i);
                Series series = new Series(cs.getName(), "color: " + strColor + ";width: "
                        + String.valueOf(cs.getLineWidth()) + ";shape: " + cs.getShapeName() + ";", i,
                        dataset.getSeriesCount());
                dataset.addSeries(series);

                XYItemRenderer renderer = plot.getRenderer(i);
                renderer.setSeriesPaint(dataset.getSeriesCount() - 1, scene2awtColor(color));

                SeriesShape simb = new SeriesShape(cs.getShapeName(),
                        javafx.scene.paint.Color.web(strColor.replace("#", "0x")));

                if (cs.getLineWidth() > 0) {
                    ((XYLineAndShapeRenderer) renderer).setSeriesLinesVisible(dataset.getSeriesCount() - 1, true);
                    renderer.setSeriesStroke(dataset.getSeriesCount() - 1, new BasicStroke(cs.getLineWidth()));
                } else {
                    ((XYLineAndShapeRenderer) renderer).setSeriesLinesVisible(dataset.getSeriesCount() - 1, false);
                }

                if (cs.getShapeName().equals("null")) {
                    renderer.setSeriesShape(dataset.getSeriesCount() - 1, null);
                    ((XYLineAndShapeRenderer) renderer).setSeriesShapesVisible(dataset.getSeriesCount() - 1, false);
                } else {
                    renderer.setSeriesShape(dataset.getSeriesCount() - 1, simb.getShapeAWT());
                    ((XYLineAndShapeRenderer) renderer).setSeriesShapesVisible(dataset.getSeriesCount() - 1, true);
                    if (cs.getShapeName().contains("empty")) {
                        ((XYLineAndShapeRenderer) renderer).setSeriesShapesFilled(dataset.getSeriesCount() - 1,
                                false);
                    } else {
                        ((XYLineAndShapeRenderer) renderer).setSeriesShapesFilled(dataset.getSeriesCount() - 1,
                                true);
                    }
                }

                if (i == 0) {
                    plot.setRenderer(renderer);
                } else {
                    plot.setRenderer(i, renderer);
                }

                seriesList.add(series);

                final LegendAxis le = getLegendAxis(axis);
                final Label label = new Label(cs.toString());
                Platform.runLater(() -> {
                    label.setStyle("fondo: " + strChartBackgroundColor
                            + ";-fx-background-color: fondo;-fx-text-fill: ladder(fondo, white 49%, black 50%);-fx-padding:5px;-fx-background-radius: 5;-fx-font-size: "
                            + String.valueOf(fontSize) + "px");
                });

                label.setOnMouseClicked((MouseEvent t) -> {
                    if (t.getClickCount() == 2) {
                        for (int i1 = 0; i1 < seriesList.size(); i1++) {
                            if (seriesList.get(i1).getKey().toString().equals(label.getText())) {
                                editSeries(seriesList.get(i1));
                                break;
                            }
                        }
                    }
                });
                label.setOnMouseExited((MouseEvent t) -> {
                    label.setStyle(
                            label.getStyle().replace("-fx-background-color: blue", "-fx-background-color: fondo"));
                });
                label.setOnMouseEntered((MouseEvent t) -> {
                    label.setStyle(
                            label.getStyle().replace("-fx-background-color: fondo", "-fx-background-color: blue"));
                    for (Node le1 : legendFrame.getChildren()) {
                        if (le1 instanceof LegendAxis) {
                            le1.setStyle("-fx-background-color:" + strBackgroundColor);
                            ((LegendAxis) le1).selected = false;
                        }
                    }
                });
                label.setStyle("fondo: " + strChartBackgroundColor
                        + ";-fx-text-fill: white;-fx-background-color: fondo;-fx-padding:5px;-fx-background-radius: 5;-fx-font-size: "
                        + String.valueOf(fontSize) + "px");

                le.getChildren().add(label);
                label.setGraphic(simb.getShapeGraphic());

                break;
            }
        }
    }

    /**
     * Add smaples for all series
     * @param x abcissa
     * @param values ordinates or values 
     */
    @Override
    public void addSample(double x, double[] values) {
        try {
            for (int i = 0; i < values.length; i++) {
                seriesList.get(i).add(x, values[i]);
            }
        } catch (SeriesException e) {

        } catch (Exception e) {

        }
    }

    /**
     * Add sample to a series
     * @param x Abcissa
     * @param y Ordinate
     * @param seriesName Series name 
     */
    @Override
    public void addSample(double x, double y, String seriesName) {
        for (XYSeries serie : seriesList) {
            if (serie.getKey().toString().equals(seriesName)) {
                serie.add(x, y);
                break;
            }
        }
    }

    /**
     * Clear a series
     * @param seriesName Name of series to clear
     */
    public void clear(String seriesName) {
        for (XYSeries serie : seriesList) {
            if (serie.getKey().toString().equals(seriesName)) {
                serie.clear();
                break;
            }
        }
    }

    /**
     * Clear all series
     */
    public void clear() {
        for (XYSeries serie : seriesList) {
            serie.clear();
        }
    }

    /**
     * 
     * @return chart frame
     */
    @Override
    public Pane getChart() {

        return chartFrame;
    }

    /**
     * Set abcissa upper and lower limits
     * @param lower Lower limit
     * @param upper Upper limit
     */
    @Override
    public void setAbcissaRange(double lower, double upper) {
        abcissaAxis.setAutoRange(false);
        abcissaAxis.setUpperBound(upper);
        abcissaAxis.setLowerBound(lower);
    }

}