001/* A tab pane to display sensor data. 
002 * 
003 * Copyright (c) 2010 The Regents of the University of California.
004 * All rights reserved.
005 *
006 * '$Author: crawl $'
007 * '$Date: 2010-06-03 16:45:10 -0700 (Thu, 03 Jun 2010) $' 
008 * '$Revision: 24730 $'
009 * 
010 * Permission is hereby granted, without written agreement and without
011 * license or royalty fees, to use, copy, modify, and distribute this
012 * software and its documentation for any purpose, provided that the above
013 * copyright notice and the following two paragraphs appear in all copies
014 * of this software.
015 *
016 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
017 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
018 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
019 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
020 * SUCH DAMAGE.
021 *
022 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
023 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
024 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
025 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
026 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
027 * ENHANCEMENTS, OR MODIFICATIONS.
028 *
029 */
030
031package org.kepler.gui;
032
033import java.awt.GridBagConstraints;
034import java.awt.GridBagLayout;
035import java.awt.event.ActionEvent;
036import java.awt.event.ActionListener;
037import java.lang.reflect.Field;
038import java.lang.reflect.InvocationTargetException;
039import java.lang.reflect.Method;
040import java.util.ArrayList;
041import java.util.List;
042
043import javax.swing.BorderFactory;
044import javax.swing.BoxLayout;
045import javax.swing.JButton;
046import javax.swing.JComponent;
047import javax.swing.JOptionPane;
048import javax.swing.JPanel;
049import javax.swing.JScrollPane;
050import javax.swing.SwingUtilities;
051
052import org.jfree.data.time.TimePeriodValues;
053import org.jfree.data.time.TimePeriodValuesCollection;
054import org.kepler.plotting.Plot;
055
056import ptolemy.actor.gui.TableauFrame;
057import ptolemy.kernel.util.IllegalActionException;
058import ptolemy.kernel.util.NameDuplicationException;
059import ptolemy.kernel.util.NamedObj;
060
061/** A tab pane to display sensor data.
062 * 
063 * @author Daniel Crawl
064 * @version $Id: PlotsPanel.java 24730 2010-06-03 23:45:10Z crawl $
065 */
066public class PlotsPanel extends JPanel implements TabPane 
067{
068    /** Construct a new PlotsPanel in a frame with a specific title. */
069    public PlotsPanel(TableauFrame parent, String title)
070    {
071        super();
072        _title = title;
073        _frame = parent;
074        setBackground(TabManager.BGCOLOR);
075        setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
076                panel = new JPanel();
077                panel.setLayout(new GridBagLayout());
078                panel.scrollRectToVisible(getVisibleRect());
079                panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
080                scrollPane = new JScrollPane(panel);
081                scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
082                scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
083        }
084
085        /** Get the container frame. */
086    public TableauFrame getParentFrame()
087    {
088        return _frame;
089    }
090
091    /** Get the name of the tab. */
092    public String getTabName()
093    {
094        return _title;
095    }
096
097    /** Initialize the contents of the tab. */
098    public void initializeTab() throws Exception
099    {
100
101//              System.out.println("initializeTab() PlotsPanel");
102                this.add(scrollPane);
103    }
104        
105        public Plot addGraph() {
106                final JComponent me = panel;
107                final Plot plot = createPlot();
108                SwingUtilities.invokeLater(new Runnable() {
109                        public void run() {
110                                GridBagConstraints gbc = new GridBagConstraints();
111                                gbc.gridx = 0;
112                                gbc.gridy = GridBagConstraints.RELATIVE;
113                                JPanel chart = plot.getPanel();
114                                chart.setBorder(BorderFactory.createEmptyBorder(10, 0, 10, 0));
115                                me.add(chart, gbc);
116                                DoubleCheckButton clearButton = new DoubleCheckButton("Clear", "Are you sure you want to clear this graph?");
117                                clearButton.addActionListener(new ActionListener() {
118                                        public void actionPerformed(ActionEvent e) {
119                                                TimePeriodValuesCollection dataset = plot.getDataset();
120                                                for (int i = 0; i < dataset.getSeriesCount(); i++) {
121                                                        // Evil black magic
122                                                        // This is required to remove accumulated data
123                                                        // while still maintaining the data structures
124                                                        // so future points can be plotted for these
125                                                        // lines.
126                                                        TimePeriodValues series = dataset.getSeries(i);
127                                                        try {
128                                                                Field dataField = series.getClass().getDeclaredField("data");
129                                                                dataField.setAccessible(true);
130                                                                List dataList = (List) dataField.get(series);
131                                                                dataList.clear();
132                                                                Method method = series.getClass().getDeclaredMethod("recalculateBounds");
133                                                                method.setAccessible(true);
134                                                                method.invoke(series);
135                                                        }
136                                                        catch(InvocationTargetException e1) {
137                                                                e1.printStackTrace();
138                                                        }
139                                                        catch(NoSuchMethodException e1) {
140                                                                e1.printStackTrace();
141                                                        }
142                                                        catch(IllegalAccessException e1) {
143                                                                e1.printStackTrace();
144                                                        }
145                                                        catch(NoSuchFieldException e1) {
146                                                                e1.printStackTrace();
147                                                        }
148                                                        series.fireSeriesChanged(); // Can do this normally at least.
149                                                }
150                                        }
151                                });
152                                plot.setClearButton(clearButton);
153                                me.add(clearButton, gbc);
154                                me.repaint();
155                                me.revalidate();
156                        }
157                });
158                return plot;
159        }
160
161        private Plot createPlot() {
162                Plot plot = new Plot(_frame);
163                _plots.add(plot);
164                return plot;
165        }
166
167//      private boolean isEmpty() {
168//              return _plots.isEmpty();
169//      }
170        
171        private final List<Plot> _plots = new ArrayList<Plot>();
172
173        /** Set the frame for this tab. */
174    public void setParentFrame(TableauFrame parent)
175    {
176        _frame = parent;
177    }
178    
179    /** A factory to create PlotsPanels. */
180    public static class ViewerFactory extends TabPaneFactory
181    {
182        /**
183         * Create a factory with the given name and container.
184         * 
185         *@param container
186         *            The container.
187         *@param name
188         *            The name of the entity.
189         *@exception IllegalActionException
190         *                If the container is incompatible with this attribute.
191         *@exception NameDuplicationException
192         *                If the name coincides with an attribute already in the
193         *                container.
194         */
195        public ViewerFactory(NamedObj container, String name)
196                throws IllegalActionException, NameDuplicationException {
197            super(container, name);
198        }
199
200        /** Create a tab pane in a frame. */
201        public TabPane createTabPane(TableauFrame parent) {
202            return new PlotsPanel(parent, "Plot Viewer");
203        }
204    }
205    
206    private TableauFrame _frame;
207    private String _title;
208        private JScrollPane scrollPane;
209        private JPanel panel;
210    
211        private class DoubleCheckButton extends JButton {
212                public DoubleCheckButton(String initialText, String confirmText) {
213                        super(initialText);
214                        this.confirmText = confirmText;
215                }
216
217
218                @Override
219                protected void fireActionPerformed(ActionEvent event) {
220                        int response = JOptionPane.showConfirmDialog(null, confirmText, "Confirm", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
221                        if (response == JOptionPane.YES_OPTION) {
222                                super.fireActionPerformed(event);                               
223                        }
224                }
225                
226                private String confirmText = null;
227        }
228}