001/*
002 * Copyright (c) 2010-2012 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2012-05-09 11:05:40 -0700 (Wed, 09 May 2012) $' 
007 * '$Revision: 29823 $'
008 * 
009 * Permission is hereby granted, without written agreement and without
010 * license or royalty fees, to use, copy, modify, and distribute this
011 * software and its documentation for any purpose, provided that the above
012 * copyright notice and the following two paragraphs appear in all copies
013 * of this software.
014 *
015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
019 * SUCH DAMAGE.
020 *
021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
026 * ENHANCEMENTS, OR MODIFICATIONS.
027 *
028 */
029
030package org.kepler.plotting;
031
032import java.awt.Component;
033import java.awt.GridBagConstraints;
034import java.awt.GridBagLayout;
035import java.awt.event.ActionEvent;
036import java.awt.event.WindowAdapter;
037import java.awt.event.WindowEvent;
038import java.text.DateFormat;
039import java.text.ParseException;
040import java.text.SimpleDateFormat;
041import java.util.ArrayList;
042import java.util.Date;
043import java.util.HashSet;
044import java.util.List;
045import java.util.Set;
046
047import javax.swing.JButton;
048import javax.swing.JMenu;
049import javax.swing.JMenuItem;
050import javax.swing.JPanel;
051import javax.swing.JScrollPane;
052
053import org.jfree.chart.ChartPanel;
054import org.jfree.chart.JFreeChart;
055import org.jfree.chart.axis.DateAxis;
056import org.jfree.chart.axis.NumberAxis;
057import org.jfree.chart.plot.XYPlot;
058import org.jfree.chart.renderer.xy.DefaultXYItemRenderer;
059import org.jfree.chart.renderer.xy.XYItemRenderer;
060import org.jfree.data.time.SimpleTimePeriod;
061import org.jfree.data.time.TimePeriodValues;
062import org.jfree.data.time.TimePeriodValuesCollection;
063
064import ptolemy.actor.gui.TableauFrame;
065import ptolemy.vergil.toolbox.FigureAction;
066
067/**
068 * Created by IntelliJ IDEA.
069 * User: sean
070 * Date: Jul 6, 2010
071 * Time: 12:17:07 PM
072 */
073
074public class Plot extends JPanel {
075        public static List<Plot> getAllPlots() {
076                return _plots;
077        }
078        
079        public Plot(TableauFrame frame) {
080                GridBagLayout layout = new GridBagLayout();
081                this.setLayout(layout);
082                GridBagConstraints c = new GridBagConstraints();
083                JPanel testGraph = getGraph();
084                this.add(testGraph, c);
085                
086                _plots.add(this);
087                
088                // When the window is closed the plot needs to be
089                // removed from the static list so it can be garbage
090                // collected
091                WindowClosedAdapter adapter = new WindowClosedAdapter();
092                frame.addWindowListener(adapter);
093        }
094
095        private JPanel getGraph() {
096                // series = new XYSeries("Line!");
097                demoSeries = new TimePeriodValues("Demo Series");
098                TimePeriodValuesCollection dataset = new TimePeriodValuesCollection();
099                dataset.setDomainIsPointsInTime(false);
100                this.dataset = dataset;
101                // addDemoSeries();
102                // dataset = new XYSeriesCollection(series);
103                
104                //initializeDemoSeries();
105                createChart();
106
107                // don't forget this initializes the chart
108                setScrolling(false);
109
110                resetToggleMenu();
111                
112                return panel;
113        }
114        
115        public ChartPanel getChartPanel() {
116                return chartPanel;
117        }
118        
119        public void setScrolling(boolean scrolling) {
120                
121                GridBagConstraints constraints = new GridBagConstraints();
122                constraints.gridx = 1;
123                constraints.gridy = 1;
124
125                if (panel == null) {
126                        // initialize panel
127                        panel = new JPanel(new GridBagLayout());
128                }
129                else {
130                        panel.removeAll();
131                }
132                // Now we have an empty panel
133                
134                Component graphComponent;
135                chartPanel = new ChartPanel(chart);
136                // System.out.println("Chart panel has been set: " + System.identityHashCode(chartPanel));
137                if (scrolling) {
138                        JScrollPane scrollPane = new JScrollPane(chartPanel);
139                        scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
140                        scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
141                        graphComponent = scrollPane;                    
142                }
143                else {
144                        graphComponent = chartPanel;
145                }
146                
147                panel.add(graphComponent, constraints);
148        }
149
150        private void initializeToggleMenu() {
151                if (toggleMenu == null) {
152                        toggleMenu = new JMenu("Toggle line");
153                }
154                if (!added) {
155                        chartPanel.getPopupMenu().add(toggleMenu);
156                        added = true;
157                }
158        }
159
160        public void setClearButton(JButton clearButton) {
161                this.clearButton = clearButton;
162        }
163        
164        public JButton getClearButton() {
165                return clearButton;
166        }
167
168        private boolean added = false;
169        
170        private void clearToggleMenu() {
171                toggleMenu.removeAll();
172        }
173        
174        private void populateToggleMenu() {
175                for (int i = 0; i < dataset.getSeriesCount(); i++) {
176                        TimePeriodValues series = dataset.getSeries(i);
177                        addEntry(series);
178                }
179                for (TimePeriodValues series : frozenSeries) {
180                        addEntry(series);
181                }
182        }
183        
184        private JMenu toggleMenu = null;
185
186        private void addEntry(final TimePeriodValues series) {
187                String menuEntryName = "Toggle line '" + series.getKey() + "'";
188                final Plot me = this;
189                toggleMenu.add(new JMenuItem(new FigureAction(menuEntryName) {
190                        @Override
191                        public void actionPerformed(ActionEvent e) {
192                                // Get series number
193                                int seriesIndex = -1;
194                                for (int i = 0; i < dataset.getSeriesCount(); i++) {
195                                        TimePeriodValues currentSeries = dataset.getSeries(i);
196                                        if (series == currentSeries) {
197                                                seriesIndex = i;
198                                                break;
199                                        }
200                                }
201                                if (seriesIndex == -1) {
202                                        return;
203                                }
204
205                                XYItemRenderer renderer = me.getXYPlot().getRendererForDataset(dataset);
206                                Boolean currentVisibility = renderer.getSeriesVisible(seriesIndex);
207                                if (currentVisibility == null) {
208                                        currentVisibility = true;
209                                }
210                                renderer.setSeriesVisible(seriesIndex, !currentVisibility);
211                        }
212                }));            
213        }
214
215        public void resetToggleMenu() {
216                initializeToggleMenu();
217                clearToggleMenu();
218                populateToggleMenu();
219        }
220
221        private final Set<TimePeriodValues> frozenSeries = new HashSet<TimePeriodValues>();
222
223        public void removeDemoSeries() {
224                dataset.removeSeries(demoSeries);
225        }
226        
227        public void addDemoSeries() {
228                dataset.addSeries(demoSeries);
229        }
230
231        private void createChart() {
232                boolean includeLegend = false;
233                boolean tooltips = true;
234                boolean urls = false;
235                
236                // chart = ChartFactory.createXYLineChart("Chart Title", "Domain label", "Value label", dataset, PlotOrientation.VERTICAL, includeLegend, tooltips, urls);
237                plot = new XYPlot(dataset, new DateAxis("X Axis"), new NumberAxis("Y Axis"), new DefaultXYItemRenderer());
238                chart = new JFreeChart("Chart Title", plot);
239        }
240
241        private void initializeDemoSeries() {
242                DateFormat df = new SimpleDateFormat("M/d/yyyy HH:mm:ss.SSS");
243                Date d0 = null, d1 = null, d2 = null, d3 = null, d4 = null;
244                Date d5 = null, d6 = null, d7 = null, d8 = null, d9 = null;
245                Date d10 = null, d11 = null, d12 = null, d13 = null, d14 = null;
246                Date d15 = null;
247                try {
248                        d0 = df.parse("11/5/2003 0:00:00.000");
249                        d1 = df.parse("11/5/2003 0:01:00.000");
250                        d2 = df.parse("11/5/2003 0:02:00.000");
251                        d3 = df.parse("11/5/2003 0:03:00.000");
252                        d4 = df.parse("11/5/2003 0:04:00.000");
253                        d5 = df.parse("11/5/2003 0:05:00.000");
254                        d6 = df.parse("11/5/2003 0:06:00.000");
255                        d7 = df.parse("11/5/2003 0:07:00.000");
256                        d8 = df.parse("11/5/2003 0:08:00.000");
257                        d9 = df.parse("11/5/2003 0:09:00.000");
258                        d10 = df.parse("11/5/2003 0:10:00.000");
259                        d11 = df.parse("11/5/2003 0:11:00.000");
260                        d12 = df.parse("11/5/2003 0:12:00.000");
261                        d13 = df.parse("11/5/2003 0:13:00.000");
262                        d14 = df.parse("11/5/2003 0:14:00.000");
263                        d15 = df.parse("11/5/2003 0:15:00.000");
264                }
265                catch(ParseException ex) {
266                        ex.printStackTrace();
267                }
268                
269                demoSeries.add(new SimpleTimePeriod(d0, d0), 0);
270                demoSeries.add(new SimpleTimePeriod(d1, d1), 40.674);
271                demoSeries.add(new SimpleTimePeriod(d2, d2), 74.314);
272                demoSeries.add(new SimpleTimePeriod(d3, d3), 95.106);
273                demoSeries.add(new SimpleTimePeriod(d4, d4), 99.452);
274                demoSeries.add(new SimpleTimePeriod(d5, d5), 86.603);
275                demoSeries.add(new SimpleTimePeriod(d6, d6), 58.779);
276                demoSeries.add(new SimpleTimePeriod(d7, d7), 20.791);
277                demoSeries.add(new SimpleTimePeriod(d8, d8), -20.791);
278                demoSeries.add(new SimpleTimePeriod(d9, d9), -58.779);
279                demoSeries.add(new SimpleTimePeriod(d10, d10), -86.603);
280                demoSeries.add(new SimpleTimePeriod(d11, d11), -99.452);
281                demoSeries.add(new SimpleTimePeriod(d12, d12), -95.106);
282                demoSeries.add(new SimpleTimePeriod(d13, d13), -74.314);
283                demoSeries.add(new SimpleTimePeriod(d14, d14), -40.674);
284                demoSeries.add(new SimpleTimePeriod(d15, d15), 0);
285        }
286        
287        public TimePeriodValuesCollection getDataset() {
288                return dataset;
289        }
290
291        public PlotEditor getPlotEditor() {
292                return plotEditor;
293        }
294        
295        public void setPlotEditor(PlotEditor plotEditor) {
296                this.plotEditor = plotEditor;
297                // Initialize a connection between the dataset and the table model, so that the table model can be accurately reflected
298                this.getPlotEditor().getTable().setDataset(dataset);
299        }
300
301        public JPanel getPanel() {
302                return panel;
303        }
304
305        public void setGraphName(String graphName) {
306                chart.setTitle(graphName);
307        }
308        
309        public JFreeChart getChart() {
310                return chart;
311        }
312        
313        public XYPlot getXYPlot() {
314                return plot;
315        }
316
317        public void setProperty(GraphProperty label, String value) {
318                if (label == GraphProperty.TITLE) {
319                        chart.setTitle(value);
320                }
321                else if (label == GraphProperty.X_LABEL) {
322                        chart.getXYPlot().getDomainAxis().setLabel(value);
323                }
324                else if (label == GraphProperty.Y_LABEL) {
325                        chart.getXYPlot().getRangeAxis().setLabel(value);
326                }
327        }
328        
329        public String getTitle() {
330                try {
331                        return chart.getTitle().getText();
332                }
333                catch(NullPointerException ex) {
334                        return null;
335                }
336        }
337        
338    /** Listener for windowClosing action. */
339    class WindowClosedAdapter extends WindowAdapter {
340        public void windowClosed(WindowEvent e) {
341                //Window window = e.getWindow();
342            //System.out.println("org.kepler.plotting.Plot$WindowClosedAdapter.windowClosed() : " + window.getName());
343            
344            _plots.remove(Plot.this);
345        }
346    }
347
348        private static List<Plot> _plots = new ArrayList<Plot>();
349        private JFreeChart chart;
350        private XYPlot plot;
351        private TimePeriodValues demoSeries;
352        private TimePeriodValuesCollection dataset;
353        private JPanel panel;
354        private ChartPanel chartPanel;
355        private PlotEditor plotEditor;
356        private JButton clearButton;
357}