package jas2.hist;

import jas2.plot.Axis;
import jas2.plot.DataArea;
import jas2.plot.HasPopupItems;
import jas2.plot.JASPlotMouseListener;
import jas2.plot.Legend;
import jas2.plot.PlotComponent;
import jas2.plot.PlotPanel;
import jas2.plot.PrintHelper;
import jas2.plot.Title;

import java.awt.AWTEvent;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.io.Writer;
import java.util.Enumeration;
import java.util.Vector;

import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComponent;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;

/**
 * JASHist is the main component used for plotting histograms, scatterplots etc. The type of
 * display produced depends on the DataSource hooked to the component. If the data source is
 * observable, then the JASHist will update as it receives notifications from the data source.
 *
 * JASHist supports overlaying of data and fitting of functions (to 1D histograms).
 */

public class JASHist
extends JComponent
implements JASPlotMouseListener
{
    
        /**
         * Create a new JASHist component with no initial data source
         */
    
    public JASHist()
    {
        setLayout(new BorderLayout());
        add(contentPane, BorderLayout.CENTER);
        setPreferredSize(new Dimension(200,200));
        
        Axis xAxis = new ManagedAxis(Axis.HORIZONTAL);
        Axis yAxis = new ManagedAxis(Axis.VERTICAL);
        
        legend = new Legend();
        contentPane.add(legend);
        legend.setVisible(false);
        
        stats = new StatisticsBlock();
        contentPane.add(stats);
        stats.setVisible(false);
        
        dataArea = new JASHistDataArea(xAxis,yAxis);
        dataManager = new DefaultDataManager(this,dataArea);
        contentPane.add(dataArea);
        
        changed = false;
        isInit = false;
    }
    DataManager getDataManager()
    {
        return dataManager;
    }
    // Made public to allow DataArea to be set as a DnD target.
    public DataArea getDataArea()
    {
        return dataArea;
    }
        /**
         * @return true if a call to showProperties can currently be honoured
         * @see #showProperties()
         */
    public boolean supportsProperties()
    {
        return !(dataManager instanceof DefaultDataManager);
    }
        /**
         * Pops up the Properties dialog box for this plot
         * @see #showProperties(byte)
         */
    public void showProperties()
    {
        showProperties(JASHistPropertyDialog.DEFAULT);
    }
        /**
         * Pops up the properties dialog box for this plot with the
         * specified axis tab initially open
         * @param axis The axis tab to opened (defined??)
         * @see #showProperties()
         */
    //TODO: Document argument properly
    public void showProperties(final byte axis)
    {
        new JASHistPropertyDialog((Frame) SwingUtilities.getAncestorOfClass(Frame.class, this), this, axis).show();
    }
        /**
         * Overrides addNotify in JComponent
         * Starts listening for changes in data
         */
    public void addNotify() {
        isInit = true;
        if (dataManager != null) {
            if (dataManager.numberOfDataSources() != 0) {
                Enumeration e = dataManager.getDataSources();
                while (e.hasMoreElements()) {
                    JASHistData d = (JASHistData) e.nextElement();
                    d.restoreNormalizationObserver();
                    d.show(true);
                }
            }
            if (dataManager instanceof BinnedDataManager) {
                int nFunc = ((BinnedDataManager) dataManager).numberOfFunctions();
                if (nFunc > 0) {
                    Enumeration e = ((BinnedDataManager) dataManager).getFunctions();
                    while (e.hasMoreElements()) {
                        JASHistData d = (JASHistData) e.nextElement();
                        d.show(true);
                    }                    
                }
            }
            dataManager.init();
        }
        super.addNotify();
    }
    
    /**
     * Overrides removeNotify in JComponent
     * Stops listening for changes in data
     */
    public void removeNotify() {
        isInit = false;
        if (dataManager != null) {
            dataManager.setRealized(false);
            if (dataManager.numberOfDataSources() != 0) {
                Enumeration e = dataManager.getDataSources();
                while (e.hasMoreElements()) {
                    JASHistData d = (JASHistData) e.nextElement();
                    d.deleteNormalizationObserver();
                    d.show(false);
                }
            }
            if (dataManager instanceof BinnedDataManager) {
                int nFunc = ((BinnedDataManager) dataManager).numberOfFunctions();
                if (nFunc > 0) {
                    Enumeration e = ((BinnedDataManager) dataManager).getFunctions();
                    while (e.hasMoreElements()) {
                        JASHistData d = (JASHistData) e.nextElement();
                        d.show(false);
                    }                    
                }
            }
        }
        super.removeNotify();
    }
    
    
        /**
         * Writes the histogram, scatterplot etc that we are displaying as XML.
         * @param pw A PrintWriter to send the output to
         * @param snapshot A snapshot of the current data is stored if true, otherwise a reference to the datasource
         * @see XML How To
         */
    public void writeXML(Writer w,boolean snapshot)
    {
        XMLPrintWriter pw =  new XMLPrintWriter(w);
        pw.print(this,snapshot);
    }
        /**
         * Test if user direct user interaction with the plot is allowed
         * @return True if user interaction is currently allowed
         * @see #setAllowUserInteraction(boolean)
         */
    public boolean getAllowUserInteraction()
    {
        return contentPane.getAllowUserInteraction();
    }
        /**
         * Controls whether end users are allowed to directly interact with
         * the plot by way of popup menus or "clicking and dragging". By
         * default user interaction is allowed. If no
         *
         * @param allow True if user interactions are to be allowed.
         */
    public void setAllowUserInteraction(boolean allow)
    {
        contentPane.setAllowUserInteraction(allow);
        setChanged();
    }
    public boolean getAllowPopupMenus()
    {
        return contentPane.getAllowPopupMenus();
    }
    public void setAllowPopupMenus(boolean allow)
    {
        contentPane.setAllowPopupMenus(allow);
        setChanged();
    }
    public void setShowStatistics(boolean show)
    {
        stats.setVisible(show);
        contentPane.revalidate();
        setChanged();
    }
    public boolean getShowStatistics()
    {
        return stats.isVisible();
    }
        /**
         * Set the color used to paint the background (but not the data area)
         * @param c The new Color to use, or null to set the background to be transparent
         * @see #setDataAreaColor(Color)
         * @see #setForegroundColor(Color)
         */
    public void setBackground(Color c)
    {
        contentPane.setBackground(c);
        setChanged();
    }
        /**
         * Get the background color.
         */
    public Color getBackground()
    {
        if (contentPane.isPaintingBackground()) return contentPane.getBackground();
        else return super.getBackground();
    }
        /**
         * Sets the current foreground color, used by default as the color
         * for the axis, labels, title, legend etc. By default the foreground
         * color of the plots container is used
         * @param c The new foreground color
         */
    public void setForegroundColor(Color c)
    {
        super.setForeground(c);
        setChanged();
    }
        /**
         * Return the current data area background color
         * @return The current data area background color
         * @see #setDataAreaColor(Color)
         */
    public Color getDataAreaColor()
    {
        return dataArea.getBackground();
    }
        /**
         * Set the color used to paint the background of the data area
         * @param c The new color to be used to paint the data area background, or null`to set the bacground back to the default value
         * @see #setBackground(Color)
         * @see #setForeground(Color)
         */
    public void setDataAreaColor(Color c)
    {
        dataArea.setBackground(c);
        setChanged();
    }
    
    public final static int NONE = PlotComponent.NONE;
    public final static int BEVEL_IN = PlotComponent.BEVEL_IN;
    public final static int BEVEL_OUT = PlotComponent.BEVEL_OUT;
    public final static int ETCHED = PlotComponent.ETCHED;
    public final static int LINE = PlotComponent.LINE;
    public final static int SHADOW = PlotComponent.SHADOW;
        /**
         * Set the border to place around the data area
         * @param type One of NONE,BEVEL_IN,BEVEL_OUT,ETCHED,LINE,SHADOW
         */
    
    public void setDataAreaBorderType(int type)
    {
        dataArea.setBorderType(type);
        dataArea.revalidate();
        setChanged();
    }
        /**
         * Get the current data area border type
         * @return One of NONE,BEVEL_IN,BEVEL_OUT,ETCHED,LINE,SHADOW,OTHER
         */
    public int getDataAreaBorderType()
    {
        return dataArea.getBorderType();
    }
    public void setDataAreaBorder(Border b)
    {
        dataArea.setBorder(b);
    }
    public Border getDataAreaBorder()
    {
        return dataArea.getBorder();
    }
        /**
         * Gets the X axis
         * @return The current X axis
         */
    public JASHistAxis getXAxis()
    {
        return dataManager.getXAxis();
    }
        /**
         * Gets the default Y Axis
         * @return The current default Y Axis
         * @see #getYAxis(int)
         */
    public JASHistAxis getYAxis()
    {
        return dataManager.getYAxis(0);
    }
        /**
         * Gets either Y Axis.
         * @param index The axis to get, 0 = left (default), 1 = right
         * @return The requested Axis
         */
    public JASHistAxis getYAxis(int index)
    {
        return dataManager.getYAxis(index);
    }
        /**
         * Get an array containing all of the Y Axes
         */
    public JASHistAxis[] getYAxes()
    {
        return dataManager.getYAxes();
    }
    final public static int LEGEND_NEVER = 0;
    final public static int LEGEND_AUTOMATIC = 1;
    final public static int LEGEND_ALWAYS = 2;
        /**
         * Set when the legend will be shown.	By default the option is
         * set to LEGEND_AUTOMATIC, which means that the legend will be shown
         * whenever there is more than one DataSource attached to the plot.
         * @param legend One of LEGEND_NEVER,LEGEND_AUTOMATIC,LEGEND_ALWAYS
         */
    public void setShowLegend(int legend)
    {
        showLegend = legend;
        dataManager.showLegend();
    }
        /**
         * Get the current setting of the showLegend property
         * @return The current settign of showLegend
         * @see #setShowLegend(int)
         */
    public int getShowLegend()
    {
        return showLegend;
    }
        /**
         * Add a DataSource to the plot.
         * @param ds The DataSource to add
         * @throws DataManagerException If the subclass of DataSource is unrecognized or if the new DataSource is incompatible with previously added DataSources.
         */
    public JASHistData addData(DataSource ds) throws DataManagerException
    {
        if (ds instanceof Rebinnable1DHistogramData) return add1DData((Rebinnable1DHistogramData) ds);
        if (ds instanceof XYDataSource             ) return addXYData((XYDataSource) ds);
        if (ds instanceof Rebinnable2DHistogramData) return add2DData((Rebinnable2DHistogramData) ds);
        if (ds instanceof ScatterPlotSource        ) return addScatterData((ScatterPlotSource) ds);
        if (ds instanceof FunctionData             ) return addFunctionData((FunctionData) ds);
        throw new DataManagerException("Unknown DataSource subclass: "+ds);
    }
        /**
         * Add a Rebinnable1DHistogramData source to the plot
         * @param ds The data source to be added
         */
    private JASHistData add1DData(Rebinnable1DHistogramData ds) throws DataManagerException
    {
        if (dataManager instanceof DefaultDataManager)
        {
            int type = ds.getAxisType();
            if      (type == ds.DOUBLE ) dataManager = new DoubleDataManager(this,dataArea,legend,stats,50);
            else if (type == ds.INTEGER) dataManager = new IntegerDataManager(this,dataArea,legend,stats,50);
            else if (type == ds.STRING ) dataManager = new StringDataManager(this,dataArea,legend,stats);
            else if (type == ds.DATE   ) dataManager = new DateDataManager(this,dataArea,legend,stats,50);
            else throw new DataManagerException("Unsupported axis type");
            
            if (isInit) dataManager.init(); // Only necessary if addNotify already called
        }
        JASHistData data = dataManager.add(ds);
        setChanged();
        return data;
    }
        /**
         * Add a Rebinnable1DHistogramData source to the plot
         * @param ds The data source to be added
         */
    private JASHistData addXYData(XYDataSource ds) throws DataManagerException
    {
        if (dataManager instanceof DefaultDataManager)
        {
            int type = ds.getAxisType();
            if      (type == ds.DOUBLE ) dataManager = new DoubleDataManager(this,dataArea,legend,stats,50);
            else if (type == ds.INTEGER) dataManager = new IntegerDataManager(this,dataArea,legend,stats,50);
            else if (type == ds.DATE   ) dataManager = new DateDataManager(this,dataArea,legend,stats,50);
            else throw new DataManagerException("Unsupported axis type");
            
            if (isInit) dataManager.init(); // Only necessary if addNotify already called
        }
        JASHistData data = dataManager.add(ds);
        setChanged();
        return data;
    }
        /**
         * Add a ScatterPlotSource to the plot
         * @param ds The data source to be added
         */
    private JASHistData addScatterData(ScatterPlotSource ds) throws DataManagerException
    {
        if (dataManager instanceof DefaultDataManager)
        {
            int typeX = ds.getXAxisType();
            int typeY = ds.getYAxisType();
            if (typeY != ds.DOUBLE) throw new DataManagerException("Scatterplot Y Axis must be of type DOUBLE");
            
            if      (typeX == ds.DOUBLE) dataManager = new DoubleScatterDataManager(this, dataArea, legend, stats);
            else if (typeX == ds.DATE  ) dataManager = new DateScatterDataManager(this,dataArea, legend, stats);
            
            if (isInit) dataManager.init(); // Only necessary if addNotify already called
        }
        final JASHistData data = dataManager.add(ds);
        setChanged();
        return data;
    }
        /**
         * Add a 2D Histogram data source to the histogram
         * @param ds The data source to be added
         */
    private JASHistData add2DData(Rebinnable2DHistogramData ds) throws DataManagerException
    {
        if (dataManager instanceof DefaultDataManager)
        {
            int typeX = ds.getXAxisType();
            int typeY = ds.getYAxisType();
            if      (typeX == ds.DOUBLE) dataManager = new DoubleScatterDataManager(this, dataArea, legend, stats);
            else if (typeX == ds.DATE  ) dataManager = new DateScatterDataManager(this,dataArea, legend, stats);
            else throw new DataManagerException("Unsupported X Axis type for ScatterPlot");
            
            if (isInit) dataManager.init(); // Only necessary if addNotify already called
        }
        final JASHistData data = dataManager.add(ds);
        setChanged();
        return data;
    }
        /**
         * Add a function to be overlayed on a 1D histogram
         * @param ds The function to be added
         */
    private JASHistData addFunctionData(FunctionData ds) throws DataManagerException
    {
        if ( dataManager instanceof SupportsFunctions )
        {
           JASHistData data = ((SupportsFunctions) dataManager).addFunction((Basic1DFunction) ds);
           setChanged();
           return data;           
        }
        else throw new UnsupportedOperationException("Cannot add function."); 
    }
    int numberOfDataSets() // used by property dialog
    {
        return dataManager.numberOfDataSources();
    }
    int numberOfFunctions()
    {
        if (dataManager instanceof SupportsFunctions)
            return ((SupportsFunctions) dataManager).numberOfFunctions();
        return 0;
    }
        /**
         * Get the set of data sources currently attached to the plot.
         * Note that despite the name of this routine it does not return
         * an Enumeration of DataSources, but rather an Enumeration of
         * JASHistData objects, from which the DataSource can be obtained,
         * for example:
         * 
         * Enumeration  e = hPlot.getDataSources();
         * while (e.hasMoreElements())
         * {
         *    JASHistData data = (JASHistData) e.nextElement();
         *    DataSource source = data.getDataSource();
         * }
         * 
* @return An Enumeration of the JASHistData objects * @see JASHistData * @see JASHistData#getDataSource() */ public Enumeration getDataSources() { return dataManager.getDataSources(); } /** * Get the number of data sources attached to the plot */ public int getNumberOfDataSources() { return dataManager.numberOfDataSources(); } /** * Get the set of Functions currently attached to the plot * @return An Enumeration of the Functions, or null if the current DataSources do not support functions */ public Enumeration get1DFunctions() { if (dataManager instanceof SupportsFunctions) return ((SupportsFunctions) dataManager).getFunctions(); return null; } /** * Removes and detaches all data from the plot, but doesn't set up * the plot for further use. Call this method if you aren't going to * be using the plot object any more. Call removeAllData() * to remove all data and set up the plot for further use. */ public void destroy() { dataManager.destroy(); contentPane.removeAll(); // should detach itself as a listener to all of its children } /** * Removes and detaches all data and sets up the plot for further use. * The method destroy() is less expensive and * should be used if the plot will not be used any more. */ public void removeAllData() { dataManager.destroy(); // this call detached data, now we set up the plot for further use dataManager = new DefaultDataManager(this,dataArea); setChanged(); } private void resetAxis(JASHistAxis a) { a.setRangeAutomatic(true); a.setAllowSuppressedZero(true); a.setLogarithmic(false); setChanged(); } /** * Fills the appropriate Function menu items into a user * provided menu. This routine can be used to make a function * fitting menu available to an external application without having * to rewrite a bunch of code already contained in JASHist * * @param menu The menu to which the items will be added */ public void fillFunctionMenu(JMenu menu) { if (dataManager instanceof SupportsFunctions) { ((SupportsFunctions) dataManager).fillFunctionMenu(menu); menu.setEnabled(true); } else menu.setEnabled(false); } /** * Fills the appriate Slice/Projection menu items into a user * provided menu. This routine can be used to make a function * fitting menu available to an external application without having * to rewrite a bunch of code already contained in JASHist * * @param menu The menu to which the items will be added */ public void fillSliceMenu(JMenu menu) { if (dataManager instanceof TwoDDataManager) { ((TwoDDataManager) dataManager).fillSliceMenu(menu); menu.setEnabled(true); } else menu.setEnabled(false); } /** * Get the text of the title. * @return A String containing the text of the title, or null if there is no title at present * @see #getTitleObject() */ public String getTitle() { if (title == null) return null; return title.getText(); } /** * Actually get the Title object. * @return The current title, or null if there is no title */ public Title getTitleObject() { return title; } /** * Actually set the Title object. * @param newTitle The new title object */ public void setTitleObject(Title newTitle) { if (title != null) { contentPane.remove(title); } title = newTitle; contentPane.add(title); contentPane.invalidate(); setChanged(); } /** * Set the text of the title * @param newValue pass null to remove the title */ public void setTitle(String newValue) { if (newValue != null && newValue.length() != 0) { if (title == null) { title = new Title(newValue); contentPane.add(title); contentPane.invalidate(); validate(); } else title.setText(newValue); setChanged(); } else if (title != null) { contentPane.remove(title); contentPane.invalidate(); validate(); title = null; } } public boolean isChanged() { return changed; } private void setChanged() { changed = true; validate(); repaint(); // does this cause double repaint if the component was invalidated? } /** * Add a FitListener that will receive notifications about the * status of fits being performed by the plot * @param fitListener The FitListener to add */ //TODO: Dont we need a removeFitListener? public static void addFitListener(final FitListener fitListener) { if (fitListeners == null) fitListeners = new Vector(1, 1); fitListeners.addElement(fitListener); } static void notifyFitListeners(final Fitter fitter) { if (fitListeners != null) { final Enumeration e = fitListeners.elements(); while (e.hasMoreElements()) ((FitListener) e.nextElement()).fitStarted(fitter); } } public void mouseEventNotify(final MouseEvent e) { processMouseEvent(e); } public void deselected() { contentPane.deselected(); } public StatisticsBlock getStats() { return stats; } public void setStats(StatisticsBlock newStats) { stats = newStats; } public Legend getLegend() { return legend; } public void setLegend(Legend newLegend) { legend = newLegend; } /** * Pops up a dialog asking the user to choose a file/format to save the plot. */ public void saveAs() { SaveAsDialog dlg = new SaveAsDialog(this); // dlg.pack(); // dlg.doModal(); } /** * Copies the plot to the clipboard */ public void copy() { VectorGraphicsTransferable t = new VectorGraphicsTransferable(this); getToolkit().getSystemClipboard().setContents(t,t); } static private Vector fitListeners; private Title title; // The plot title private StatisticsBlock stats; private boolean isInit = false; private boolean changed = false; private int showLegend = JASHist.LEGEND_AUTOMATIC; private DataArea dataArea; private DataManager dataManager; private Legend legend; private PlotPanel contentPane = new JASHistPlotPanel(); static final long serialVersionUID = 4433180397297758071L; final private class JASHistPlotPanel extends PlotPanel implements HasPopupItems { void showWarning(Component parent, String text) { javax.swing.JOptionPane.showMessageDialog(parent, text, "Warning: Deprecated", javax.swing.JOptionPane.WARNING_MESSAGE);; } public void print(Graphics g) { jas2.plot.PrintHelper ph = jas2.plot.PrintHelper.instance(); Thread t = ph.printingThread(); if ( ! ph.isPrinting() ) ph.setPrintingThread(Thread.currentThread()); super.print(g); ph.setPrintingThread(t); } public void modifyPopupMenu(final JPopupMenu menu, final Component source) { if (menu.getComponentCount() > 0) menu.addSeparator(); JCheckBoxMenuItem dl = new JCheckBoxMenuItem("Default Plot Layout") { final protected void fireActionPerformed(final ActionEvent e) { contentPane.restoreDefaultLayout(); } }; boolean def = contentPane.hasDefaultLayout(); dl.setSelected(def); dl.setEnabled(!def); menu.add(dl); menu.add(new JMenuItem("Plot Properties...") { final protected void fireActionPerformed(final ActionEvent e) { // String text = "\"Plot Properties...\" is deprecated,\n"; // text += "Please try using \"Edit AIDA Style for\" instead"; // showWarning(this, text); showProperties(); } }); menu.add(new JMenuItem("Copy Plot to Clipboard...") { final protected void fireActionPerformed(final ActionEvent e) { copy(); } }); menu.add(new JMenuItem("Save Plot As...") { final protected void fireActionPerformed(final ActionEvent e) { saveAs(); } }); menu.add(new JMenuItem("Print Plot...") { final protected void fireActionPerformed(final ActionEvent e) { try { PrintHelper ph = PrintHelper.instance(); ph.printTarget(JASHistPlotPanel.this); } catch (Exception x) { x.printStackTrace(); // TODO: Something better } } }); } } private final class SetTitleMenuItem extends JCheckBoxMenuItem { SetTitleMenuItem() { super("Show Title"); setSelected(title != null && title.isVisible()); } protected void fireActionPerformed(final ActionEvent e) { if (title == null || title.getText().length() == 0) { setTitle("Title"); title.edit(); } else { title.setVisible(!title.isVisible()); } } } private class JASHistDataArea extends DataArea { JASHistDataArea(Axis xAxis, Axis yAxis) { super(xAxis,yAxis); } public void modifyPopupMenu(final JPopupMenu menu, final Component source) { if (source == this || !(source instanceof HasPopupItems)) // Not the axis for example { // Begin the main histogram popup menu, with bunches of Show.... menu.add(new SetTitleMenuItem()); JCheckBoxMenuItem ss = new JCheckBoxMenuItem("Show Statistics") { public void fireActionPerformed(ActionEvent e) { setShowStatistics(!getShowStatistics()); } }; ss.setSelected(getShowStatistics()); menu.add(ss); dataManager.modifyPopupMenu(menu, source); super.modifyPopupMenu(menu,source); } } } } class JASEvent extends AWTEvent { JASEvent(Component target) { super(target,AWTEvent.RESERVED_ID_MAX+5000); } }

Ads help maintain this website.