/*
 * HistogramDialog.java
 *
 * Created on June 23, 2008, 2:49 PM
 */

package org.statcato.dialogs.graph;

import org.statcato.*;
import org.statcato.spreadsheet.*;
import org.statcato.utils.HelperFunctions;
import org.statcato.statistics.BasicStatistics;
import java.util.*;
import javax.swing.ButtonGroup;
import org.jfree.chart.*;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.statistics.HistogramDataset;
import org.jfree.data.statistics.HistogramType;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.NumberTickUnit;
import org.jfree.chart.axis.TickUnits;


/**
 * A dialog for creating histograms.  Allows the user to specify the
 * data variables to be graphed, whether frequency or relative frequency
 * is represented by the heights of bars, the number of, minimum, and maximum
 * of bins, axis labels, title, and whether a legend is displayed.
 * 
 * @author  Margaret Yau
 * @version %I%, %G%
 * @see org.jfree.chart
 * @since 1.0
 */
public class HistogramDialog extends StatcatoDialog {
    
    /** Creates new form HistogramDialog */
    public HistogramDialog(java.awt.Frame parent, boolean modal, Statcato app) {
        super(parent, modal);
        this.app = app;
        ParentSpreadsheet = app.getSpreadsheet();
        initComponents();
        
        ButtonGroup group1 = new ButtonGroup();
        group1.add(FreqRadioButton);
        group1.add(RelFreqRadioButton);
        
        ButtonGroup group2 = new ButtonGroup();
        group2.add(CenterRadioButton);
        group2.add(BetweenRadioButton);
        
        ButtonGroup group3 = new ButtonGroup();
        group3.add(X1RadioButton);
        group3.add(X2RadioButton);
        
        ParentSpreadsheet.populateColumnsList(InputVarList);
        ParentSpreadsheet.populateComboBox(GroupByComboBox);
        
        getRootPane().setDefaultButton(OKButton);
        setHelpFile("graph-histogram");
        name = "Histogram";
        description = "For creating histograms that show the frequencies of " +
                "a number of bins (or classes) with numerical boundaries. ";
        helpStrings.add("Select the column variable(s) containing data values " +
                "for which the plot will be created. A different color is used " +
                "for each column variable.");
        helpStrings.add("Select whether to plot the frequencies or " +
                "the relative frequencies of the bins.");
        helpStrings.add("Select the method of binning values on the x-axis.");
        helpStrings.add("Specify a plot title and labels for the axes.");
        pack();
    }
    
    /**
     * Updates elements on the dialog so that they have the most
     * current values.  Called by {@link #setVisible} to make
     * sure the dialog displays current values when made visible again.
     * 
     * @see #setVisible(boolean)
     */
    @Override
    public void updateElements() {
        updateComboBox(GroupByComboBox);
        updateColumnsList(InputVarList);        
    }
    
    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        OKButton = new javax.swing.JButton();
        CancelButton = new javax.swing.JButton();
        jLabel14 = new javax.swing.JLabel();
        jLabel20 = new javax.swing.JLabel();
        jPanel1 = new javax.swing.JPanel();
        GroupByComboBox = new javax.swing.JComboBox();
        jScrollPane1 = new javax.swing.JScrollPane();
        InputVarList = new javax.swing.JList();
        jLabel1 = new javax.swing.JLabel();
        RelFreqRadioButton = new javax.swing.JRadioButton();
        jLabel2 = new javax.swing.JLabel();
        jLabel4 = new javax.swing.JLabel();
        FreqRadioButton = new javax.swing.JRadioButton();
        jLabel8 = new javax.swing.JLabel();
        jPanel2 = new javax.swing.JPanel();
        jLabel13 = new javax.swing.JLabel();
        jLabel22 = new javax.swing.JLabel();
        jLabel21 = new javax.swing.JLabel();
        jLabel10 = new javax.swing.JLabel();
        X2RadioButton = new javax.swing.JRadioButton();
        X1RadioButton = new javax.swing.JRadioButton();
        jLabel12 = new javax.swing.JLabel();
        jLabel19 = new javax.swing.JLabel();
        Min2TextField = new javax.swing.JTextField();
        jLabel7 = new javax.swing.JLabel();
        NumBinsTextField = new javax.swing.JTextField();
        CenterRadioButton = new javax.swing.JRadioButton();
        MinTextField = new javax.swing.JTextField();
        XTextField = new javax.swing.JTextField();
        jLabel23 = new javax.swing.JLabel();
        MaxTextField = new javax.swing.JTextField();
        jLabel9 = new javax.swing.JLabel();
        BetweenRadioButton = new javax.swing.JRadioButton();
        jLabel11 = new javax.swing.JLabel();
        ClassWidthTextField = new javax.swing.JTextField();
        jLabel6 = new javax.swing.JLabel();
        jPanel3 = new javax.swing.JPanel();
        jLabel16 = new javax.swing.JLabel();
        YTextField = new javax.swing.JTextField();
        jLabel5 = new javax.swing.JLabel();
        jLabel17 = new javax.swing.JLabel();
        YTickMarkTextField = new javax.swing.JTextField();
        jLabel15 = new javax.swing.JLabel();
        jPanel4 = new javax.swing.JPanel();
        TitleTextField = new javax.swing.JTextField();
        LegendCheckBox = new javax.swing.JCheckBox();
        jLabel3 = new javax.swing.JLabel();
        jLabel18 = new javax.swing.JLabel();

        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
        setTitle("Histogram");

        OKButton.setText("OK");
        OKButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                OKButtonActionPerformed(evt);
            }
        });

        CancelButton.setText("Cancel");
        CancelButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                CancelButtonActionPerformed(evt);
            }
        });

        jLabel14.setText("jLabel14");

        jPanel1.setBorder(javax.swing.BorderFactory.createTitledBorder("Graph Variables"));

        GroupByComboBox.addItemListener(new java.awt.event.ItemListener() {
            public void itemStateChanged(java.awt.event.ItemEvent evt) {
                GroupByComboBoxItemStateChanged(evt);
            }
        });

        jScrollPane1.setViewportView(InputVarList);

        jLabel1.setFont(new java.awt.Font("Tahoma", 1, 11));
        jLabel1.setText("Graph Variables:");

        RelFreqRadioButton.setText("Relative Frequency");

        jLabel2.setText("Ctrl-click to select multiple variables");

        jLabel4.setText("<html>Grouped By Categories in:<br> [optional]");

        FreqRadioButton.setSelected(true);
        FreqRadioButton.setText("Frequency");

        jLabel8.setFont(new java.awt.Font("Tahoma", 1, 11));
        jLabel8.setText("Heights of bars represent:");

        javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(jPanel1Layout.createSequentialGroup()
                        .addContainerGap()
                        .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(jLabel1)
                            .addComponent(jLabel2)
                            .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 125, javax.swing.GroupLayout.PREFERRED_SIZE)))
                    .addGroup(jPanel1Layout.createSequentialGroup()
                        .addGap(36, 36, 36)
                        .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(FreqRadioButton)
                            .addComponent(RelFreqRadioButton)))
                    .addGroup(jPanel1Layout.createSequentialGroup()
                        .addContainerGap()
                        .addComponent(jLabel8))
                    .addGroup(jPanel1Layout.createSequentialGroup()
                        .addContainerGap()
                        .addComponent(jLabel4)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(GroupByComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 81, javax.swing.GroupLayout.PREFERRED_SIZE)))
                .addContainerGap(11, Short.MAX_VALUE))
        );
        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jLabel1)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jLabel2)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 89, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel4)
                    .addComponent(GroupByComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addGap(18, 18, 18)
                .addComponent(jLabel8)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addComponent(FreqRadioButton)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addComponent(RelFreqRadioButton)
                .addContainerGap())
        );

        jPanel2.setBorder(javax.swing.BorderFactory.createTitledBorder("X-Axis"));

        jLabel13.setText("Position of tick marks:");

        jLabel22.setText("Minimum:");

        jLabel21.setText("Class width:");

        jLabel10.setText("Maximum:");

        X2RadioButton.setText("Provide the class width and the minimum");
        X2RadioButton.addChangeListener(new javax.swing.event.ChangeListener() {
            public void stateChanged(javax.swing.event.ChangeEvent evt) {
                X2RadioButtonStateChanged(evt);
            }
        });

        X1RadioButton.setSelected(true);
        X1RadioButton.setText("Provide the number of classes, minimum, and maximum");
        X1RadioButton.addChangeListener(new javax.swing.event.ChangeListener() {
            public void stateChanged(javax.swing.event.ChangeEvent evt) {
                X1RadioButtonStateChanged(evt);
            }
        });

        jLabel12.setText("[automatic if left blank]");

        jLabel19.setText("Class width = (maximum - minimum) / classes");

        Min2TextField.setEnabled(false);

        jLabel7.setText("Number of bins (classes):");

        NumBinsTextField.setText("10");

        CenterRadioButton.setText("Center of bar");

        jLabel23.setText("[automatic if left blank]");

        jLabel9.setText("Minimum:");

        BetweenRadioButton.setSelected(true);
        BetweenRadioButton.setText("Between bars");

        jLabel11.setFont(new java.awt.Font("Tahoma", 1, 11));
        jLabel11.setText("X-axis (horizontal)");

        ClassWidthTextField.setEnabled(false);

        jLabel6.setText("Label:");

        javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2);
        jPanel2.setLayout(jPanel2Layout);
        jPanel2Layout.setHorizontalGroup(
            jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel2Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(jPanel2Layout.createSequentialGroup()
                        .addComponent(jLabel11)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 182, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addGroup(jPanel2Layout.createSequentialGroup()
                        .addGap(22, 22, 22)
                        .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addGroup(jPanel2Layout.createSequentialGroup()
                                .addComponent(jLabel13)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(CenterRadioButton)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(BetweenRadioButton))
                            .addGroup(jPanel2Layout.createSequentialGroup()
                                .addComponent(jLabel6)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(XTextField, javax.swing.GroupLayout.DEFAULT_SIZE, 232, Short.MAX_VALUE))
                            .addComponent(X2RadioButton)
                            .addComponent(X1RadioButton)
                            .addGroup(jPanel2Layout.createSequentialGroup()
                                .addGap(21, 21, 21)
                                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                    .addGroup(jPanel2Layout.createSequentialGroup()
                                        .addComponent(jLabel7)
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                        .addComponent(NumBinsTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 72, javax.swing.GroupLayout.PREFERRED_SIZE))
                                    .addComponent(jLabel19)
                                    .addGroup(jPanel2Layout.createSequentialGroup()
                                        .addComponent(jLabel9)
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                        .addComponent(MinTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 54, javax.swing.GroupLayout.PREFERRED_SIZE)
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                        .addComponent(jLabel10)
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                        .addComponent(MaxTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 44, javax.swing.GroupLayout.PREFERRED_SIZE)
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                        .addComponent(jLabel12))))
                            .addGroup(jPanel2Layout.createSequentialGroup()
                                .addGap(21, 21, 21)
                                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                                    .addComponent(jLabel21)
                                    .addComponent(jLabel22))
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                                    .addComponent(Min2TextField)
                                    .addComponent(ClassWidthTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 79, javax.swing.GroupLayout.PREFERRED_SIZE))
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(jLabel23)))))
                .addContainerGap())
        );

        jPanel2Layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {MaxTextField, MinTextField, NumBinsTextField});

        jPanel2Layout.setVerticalGroup(
            jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel2Layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jLabel11)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(X1RadioButton)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jLabel19)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel7)
                    .addComponent(NumBinsTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel9)
                    .addComponent(MinTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jLabel10)
                    .addComponent(MaxTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jLabel12))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(X2RadioButton)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel21)
                    .addComponent(ClassWidthTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                        .addComponent(Min2TextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addComponent(jLabel23))
                    .addComponent(jLabel22))
                .addGap(22, 22, 22)
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel6)
                    .addComponent(XTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel13)
                    .addComponent(CenterRadioButton)
                    .addComponent(BetweenRadioButton))
                .addContainerGap(15, Short.MAX_VALUE))
        );

        jPanel3.setBorder(javax.swing.BorderFactory.createTitledBorder("Y-Axis"));

        jLabel16.setText("Tick mark units:");

        jLabel5.setText("Label:");

        jLabel17.setText("automatic if left blank");

        jLabel15.setFont(new java.awt.Font("Tahoma", 1, 11));
        jLabel15.setText("Y-axis (vertical)");

        javax.swing.GroupLayout jPanel3Layout = new javax.swing.GroupLayout(jPanel3);
        jPanel3.setLayout(jPanel3Layout);
        jPanel3Layout.setHorizontalGroup(
            jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel3Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jLabel15)
                    .addGroup(jPanel3Layout.createSequentialGroup()
                        .addGap(24, 24, 24)
                        .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addGroup(jPanel3Layout.createSequentialGroup()
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(jLabel16)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(YTickMarkTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 89, javax.swing.GroupLayout.PREFERRED_SIZE)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(jLabel17))
                            .addGroup(jPanel3Layout.createSequentialGroup()
                                .addComponent(jLabel5)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(YTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 305, javax.swing.GroupLayout.PREFERRED_SIZE)))))
                .addContainerGap(44, Short.MAX_VALUE))
        );
        jPanel3Layout.setVerticalGroup(
            jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel3Layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jLabel15)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(YTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jLabel5))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(YTickMarkTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jLabel17)
                    .addComponent(jLabel16))
                .addContainerGap(23, Short.MAX_VALUE))
        );

        jPanel4.setBorder(javax.swing.BorderFactory.createTitledBorder("Other Options"));

        TitleTextField.setText("Histogram");

        LegendCheckBox.setSelected(true);
        LegendCheckBox.setText("Show Legend");

        jLabel3.setText("Title:");

        jLabel18.setFont(new java.awt.Font("Tahoma", 1, 11));
        jLabel18.setText("Plot");

        javax.swing.GroupLayout jPanel4Layout = new javax.swing.GroupLayout(jPanel4);
        jPanel4.setLayout(jPanel4Layout);
        jPanel4Layout.setHorizontalGroup(
            jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel4Layout.createSequentialGroup()
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(LegendCheckBox)
                    .addComponent(jLabel18)
                    .addGroup(jPanel4Layout.createSequentialGroup()
                        .addComponent(jLabel3)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(TitleTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 194, javax.swing.GroupLayout.PREFERRED_SIZE))))
        );
        jPanel4Layout.setVerticalGroup(
            jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel4Layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jLabel18)
                .addGap(15, 15, 15)
                .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel3)
                    .addComponent(TitleTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addComponent(LegendCheckBox)
                .addContainerGap(10, Short.MAX_VALUE))
        );

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(jPanel4, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(OKButton)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(CancelButton)
                        .addContainerGap())
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(jPanel2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                        .addContainerGap())
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(jPanel3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addComponent(jLabel20))))
        );

        layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {CancelButton, OKButton});

        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addGap(8, 8, 8))
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, 301, Short.MAX_VALUE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)))
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addGroup(layout.createSequentialGroup()
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jPanel4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addComponent(jPanel3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addGap(11, 11, 11)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(jLabel20)
                    .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                        .addComponent(OKButton)
                        .addComponent(CancelButton)))
                .addContainerGap())
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    @SuppressWarnings("unchecked") 
    private void OKButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_OKButtonActionPerformed
        app.compoundEdit = new DialogEdit("histogram");
        
        // get selected input variables
        Object[] SelectedColumns = InputVarList.getSelectedValues();
        int[] columnNumbers = ParentSpreadsheet.convertColumnLabelsToNumbers(SelectedColumns);
        
        // check that at least one input variable is selected
        if (columnNumbers.length == 0) {
            app.showErrorDialog("Select at least one input variable.");
            return;
        }
        
        String text = "";
        HistogramDataset dataset = new HistogramDataset();
        if (FreqRadioButton.isSelected())
            dataset.setType(HistogramType.FREQUENCY);
        else
            dataset.setType(HistogramType.RELATIVE_FREQUENCY);
        // check if using a group by variable
        String byVarLabel = GroupByComboBox.getSelectedItem().toString();
        int byVarColNum = -1;   // BY variable column number
        Vector<Cell> byVarColVector = null;   // BY variable column vector
        boolean hasByVar = false;
        TreeSet groupSet = new TreeSet();
        
        if (!byVarLabel.equals("")) { // has BY variable
            text += "Group by " + byVarLabel + "<br>";
            hasByVar = true;
            byVarColNum = ParentSpreadsheet.parseColumnNumber(byVarLabel);
            byVarColVector = ParentSpreadsheet.getColumn(byVarColNum);
            
            // create hashset of group names
            for (Enumeration e = byVarColVector.elements(); e.hasMoreElements();) {
                Cell next = (Cell) e.nextElement();
                if (!next.getContents().equals(""))
                    groupSet.add(next.getContents());
            }
        }
        
        // get the number of bins
        int bins = -1;
        double classWidth = -1;
        if (X1RadioButton.isSelected()) {
            try {
                bins = Integer.parseInt(NumBinsTextField.getText());
                if (bins < 1) 
                    throw new NumberFormatException();            
            }
            catch (NumberFormatException e) {
                app.showErrorDialog("Enter a positive integer for the number " +
                        "of bins");
                return;
            }
        }
        else {
            // read class width from text box
            try {
                classWidth = Double.parseDouble(ClassWidthTextField.getText());
                if (classWidth <= 0)
                    throw new NumberFormatException();            
            }
            catch (NumberFormatException e) {
                app.showErrorDialog("Enter a valid class width.");
                return;
            }
            text += "Class width = " + classWidth + "<br>";
        }
             
        
        double min = Double.POSITIVE_INFINITY, max = Double.NEGATIVE_INFINITY;
        boolean useUserMax = false;
        boolean useUserMin = false;
        // get the maximum and minimum bin boundaries
        if (X1RadioButton.isSelected()) {
            if (!MinTextField.getText().equals("") || 
                    !MaxTextField.getText().equals("")) {
                try {
                    min = Double.parseDouble(MinTextField.getText());
                }
                catch (NumberFormatException e) {
                    app.showErrorDialog("Enter a valid minimum bin boundary value.");
                    return;
                }
                try {
                    max = Double.parseDouble(MaxTextField.getText());
                }
                catch (NumberFormatException e) {
                    app.showErrorDialog("Enter a valid maximum bin boundary value.");
                    return;
                }
                if (min >= max) {
                    app.showErrorDialog("Minimum must be less than maximum.");
                    return;
                }
                useUserMax = true;
                useUserMin = true;
            }
        }
        else {
            if (!Min2TextField.getText().equals("")) {
                try {
                    min = Double.parseDouble(Min2TextField.getText());
                    max = min + classWidth;
                }
                catch (NumberFormatException e) {
                    app.showErrorDialog("Enter a valid minimum bin boundary value.");
                    return;
                }
                useUserMin = true;
            }
        }
        TickUnits units = new TickUnits(); 
        Vector<Vector<Double>> vectors = new Vector<Vector<Double>>();
        Vector<String> labels = new Vector<String>();
        
        text += "Graph variable(s) = ";
        // for each input variable
        for (int i = 0; i < columnNumbers.length; ++i) {            
            Vector<Cell> StrColumnVector = ParentSpreadsheet.getColumn(columnNumbers[i]);
            String columnLabel = (String) SelectedColumns[i];
            text += columnLabel + " ";
            // get input column of doubles
            Vector<Double> ColumnVector = 
                    HelperFunctions.ConvertInputVectorToDoubles(StrColumnVector);
            if (ColumnVector == null) {
                app.showErrorDialog("Invalid input column " + columnLabel +
                        ": all data must be numbers.");
                return;
            }
            
            // find min and max, if not provided
            if (!useUserMax) {
                double thisMax = BasicStatistics.max(
                        HelperFunctions.removeNullValues(ColumnVector));
                if (thisMax > max)
                    max = thisMax;
            }
            if (!useUserMin) {
                double thisMin = BasicStatistics.min(
                        HelperFunctions.removeNullValues(ColumnVector));
                if (thisMin < min)
                    min = thisMin;                
            }
            
            if (hasByVar) { // has BY variable
                for (Iterator iter = groupSet.iterator(); iter.hasNext();) {
                    String groupName = (String) iter.next();
                    // build a vector of doubles just for this group
                    Vector<Double> subVector = new Vector<Double>(0);
                    for (int j = 0; j < ColumnVector.size(); ++j) {
                        String label = byVarColVector.elementAt(j).getContents();
                        if (label.equals(groupName))
                            subVector.addElement(ColumnVector.elementAt(j));
                    }
                    
                    subVector = HelperFunctions.removeNullValues(subVector);
                    labels.addElement(columnLabel + "-" + groupName);
                    if (max != -1 && min != -1) {
                        vectors.addElement(subVector);                        
                    }
                    else {
                        vectors.addElement(subVector);
                    }
                }
            } else {
                ColumnVector = HelperFunctions.removeNullValues(ColumnVector);
                labels.addElement(columnLabel);
                if (max != -1 && min != -1)
                    vectors.addElement(ColumnVector);
                else
                    vectors.addElement(ColumnVector);
            }
        }
        
        // adjusts max when class width is used, then calculates # bins
        if (X2RadioButton.isSelected()) {
            bins = 0;
            while ((bins * classWidth + min) < max) {
                bins++;
            }
            max = min + bins * classWidth;
        }
        
        for (int i = 0; i < vectors.size(); ++i) {
            Vector<Double> vec = (Vector<Double>)vectors.elementAt(i);
            String label = (String) labels.elementAt(i);
            dataset.addSeries(label, 
                    HelperFunctions.ConvertDoubleVectorToArray(vec), 
                    bins, min, max);
        }
        
        // add tick units
        for (int i = 0; i < dataset.getSeriesCount(); ++i) {
            double width = (dataset.getEndX(i, 0).doubleValue() - 
                    dataset.getStartX(i, 0).doubleValue());
            units.add(new NumberTickUnit(width));
        }
        // get the minimum of all bins
        double minBinStart = dataset.getStartX(0,0).doubleValue();
        double minBin = dataset.getEndX(0,0).doubleValue() - 
                dataset.getStartX(0,0).doubleValue();
        for (int i = 1; i < dataset.getSeriesCount(); ++i) {
            if (dataset.getStartX(i, 0).doubleValue() < minBin)
                minBinStart = dataset.getStartX(i, 0).doubleValue();
            double width = dataset.getEndX(i, 0).doubleValue() - 
                    dataset.getStartX(i, 0).doubleValue(); 
            if (width < minBin)
                minBin = width;
        }
        
        JFreeChart chart = ChartFactory.createHistogram(
            TitleTextField.getText(), 
            XTextField.getText(), 
            YTextField.getText(), 
            dataset, 
            PlotOrientation.VERTICAL, 
            LegendCheckBox.isSelected(), 
            false, 
            false
        );

        XYPlot xyplot = chart.getXYPlot();
        xyplot.setForegroundAlpha(0.75f);
        
        // format x axis
        final NumberAxis numberaxis = (NumberAxis)xyplot.getDomainAxis();
        numberaxis.setStandardTickUnits(units);  
        //numberaxis.setLowerBound(minBinStart - minBin);
        numberaxis.setLowerBound(minBinStart);
        numberaxis.setLowerMargin(0);
        
        double firstTick;   
        if (CenterRadioButton.isSelected()) {   // center of bar
            firstTick = minBinStart + minBin / 2;
        }
        else {  // between bars
            firstTick = minBinStart;
        }
        final double xmin = firstTick;
        NumberAxis domainAxis_x = new NumberAxis("X Values"){
           @Override 
           protected double calculateLowestVisibleTickValue() {
               return xmin;
           }
       };

        domainAxis_x.setStandardTickUnits(units);
        domainAxis_x.setRange(numberaxis.getRange());
        domainAxis_x.setAutoRangeIncludesZero(false);
        domainAxis_x.setLabel(XTextField.getText());
        xyplot.setDomainAxis(domainAxis_x);
        
        // format y axis
        if (!YTickMarkTextField.getText().equals("")) {
            try {
                double ticks = Double.parseDouble(YTickMarkTextField.getText());
                if (ticks <= 0)
                    throw new NumberFormatException();
                else {
                    NumberAxis yaxis = (NumberAxis)xyplot.getRangeAxis();
                    yaxis.setTickUnit(new NumberTickUnit(ticks));
                }
            }
            catch (NumberFormatException e) {
                app.showErrorDialog("Enter a valid number for the y-axis " +
                        "tick mark units.");
                return;
            }
        }

        text += "<br>Number of bins = " + bins;
        
        app.addLogParagraph("histogram", text);

        app.compoundEdit.end();
        app.addCompoundEdit(app.compoundEdit);
        
        StatcatoChartFrame frame = 
                new StatcatoChartFrame(TitleTextField.getText(), chart, app);
        frame.pack();
        frame.setVisible(true);

        setVisible(false);
    }//GEN-LAST:event_OKButtonActionPerformed

    
    private void CancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_CancelButtonActionPerformed
        setVisible(false);
    }//GEN-LAST:event_CancelButtonActionPerformed

    private void GroupByComboBoxItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_GroupByComboBoxItemStateChanged
        if (GroupByComboBox.getSelectedItem() != null) {
            if (GroupByComboBox.getSelectedItem().toString().equals("")) {
                LegendCheckBox.setSelected(false);
            } else {
                LegendCheckBox.setSelected(true);
            }
        }
    }//GEN-LAST:event_GroupByComboBoxItemStateChanged

    private void X1RadioButtonStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_X1RadioButtonStateChanged
       if (X1RadioButton.isSelected()) {
           NumBinsTextField.setEnabled(true);
           MinTextField.setEnabled(true);
           MaxTextField.setEnabled(true);
       }
       else {
           NumBinsTextField.setEnabled(false);
           MinTextField.setEnabled(false);
           MaxTextField.setEnabled(false);
       }
    }//GEN-LAST:event_X1RadioButtonStateChanged

    private void X2RadioButtonStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_X2RadioButtonStateChanged
        if (X2RadioButton.isSelected()) {
            ClassWidthTextField.setEnabled(true);
            Min2TextField.setEnabled(true);
        }
        else {
            ClassWidthTextField.setEnabled(false);
            Min2TextField.setEnabled(false);
        }
    }//GEN-LAST:event_X2RadioButtonStateChanged
    
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JRadioButton BetweenRadioButton;
    private javax.swing.JButton CancelButton;
    private javax.swing.JRadioButton CenterRadioButton;
    private javax.swing.JTextField ClassWidthTextField;
    private javax.swing.JRadioButton FreqRadioButton;
    private javax.swing.JComboBox GroupByComboBox;
    private javax.swing.JList InputVarList;
    private javax.swing.JCheckBox LegendCheckBox;
    private javax.swing.JTextField MaxTextField;
    private javax.swing.JTextField Min2TextField;
    private javax.swing.JTextField MinTextField;
    private javax.swing.JTextField NumBinsTextField;
    private javax.swing.JButton OKButton;
    private javax.swing.JRadioButton RelFreqRadioButton;
    private javax.swing.JTextField TitleTextField;
    private javax.swing.JRadioButton X1RadioButton;
    private javax.swing.JRadioButton X2RadioButton;
    private javax.swing.JTextField XTextField;
    private javax.swing.JTextField YTextField;
    private javax.swing.JTextField YTickMarkTextField;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel10;
    private javax.swing.JLabel jLabel11;
    private javax.swing.JLabel jLabel12;
    private javax.swing.JLabel jLabel13;
    private javax.swing.JLabel jLabel14;
    private javax.swing.JLabel jLabel15;
    private javax.swing.JLabel jLabel16;
    private javax.swing.JLabel jLabel17;
    private javax.swing.JLabel jLabel18;
    private javax.swing.JLabel jLabel19;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel20;
    private javax.swing.JLabel jLabel21;
    private javax.swing.JLabel jLabel22;
    private javax.swing.JLabel jLabel23;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JLabel jLabel4;
    private javax.swing.JLabel jLabel5;
    private javax.swing.JLabel jLabel6;
    private javax.swing.JLabel jLabel7;
    private javax.swing.JLabel jLabel8;
    private javax.swing.JLabel jLabel9;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JPanel jPanel2;
    private javax.swing.JPanel jPanel3;
    private javax.swing.JPanel jPanel4;
    private javax.swing.JScrollPane jScrollPane1;
    // End of variables declaration//GEN-END:variables
    
}
