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.BorderLayout;
034import java.awt.event.ActionEvent;
035
036import javax.swing.BoxLayout;
037import javax.swing.JButton;
038import javax.swing.JPanel;
039import javax.swing.JScrollPane;
040import javax.swing.ScrollPaneConstants;
041import javax.swing.SwingUtilities;
042
043import org.apache.commons.logging.Log;
044import org.apache.commons.logging.LogFactory;
045import org.kepler.plotting.Plot;
046import org.kepler.plotting.PlotEditor;
047import org.kepler.plotting.PlottingControllerFactory;
048
049import ptolemy.actor.gui.TableauFrame;
050import ptolemy.kernel.util.IllegalActionException;
051import ptolemy.kernel.util.NameDuplicationException;
052import ptolemy.kernel.util.NamedObj;
053import ptolemy.vergil.toolbox.FigureAction;
054
055public class PlotsEditorPanel extends JPanel implements TabPane 
056{
057        private static final Log log = LogFactory.getLog(PlotsEditorPanel.class.getName());
058        
059    public PlotsEditorPanel(TableauFrame parent, String title)
060    {
061        super();
062        _title = title;
063        _frame = parent;
064        setBackground(TabManager.BGCOLOR);
065        setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
066                plotsPanel = new JPanel();
067                plotsPanel.setLayout(new BoxLayout(plotsPanel, BoxLayout.PAGE_AXIS));
068                
069                fixGraphics();
070                
071                JScrollPane scrollPane = new JScrollPane(plotsPanel);
072                
073                scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
074                scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
075                this.add(scrollPane, BorderLayout.CENTER);
076                JButton addPlotButton = createAddPlotButton();
077                this.add(addPlotButton, BorderLayout.PAGE_END);
078                
079                // This thread will try to wait until the PlotsPanel is available
080                // before adding a plot to it. If a plot is added before the
081                // applicable tab pane is available, an NPE will be triggered.
082                // The thread will poll for tab pane setup completion every 100 ms,
083                // up to a maximum of 10 seconds (100 iterations). If after 10 seconds
084                // the tab pane is not available, something has probably gone wrong,
085                // and further attempts would do no good.
086                new Thread(new Runnable() {
087                        private void pause(long l) {
088                                try {
089                                        Thread.sleep(l);
090                                }
091                                catch(InterruptedException ignored) {}
092                        }
093                        
094                        public void run() {
095                                int maxIterations = 100;
096                                for (int i = 0; i < maxIterations; i++) {
097                                        log.debug("PlotsEditorPanel thread waiting " +
098                                                "for TabPane to become available...");
099                                        pause(100);
100                                        if (canAddPlot()) {
101                                                log.debug("PlotsEditorPanel thread got TabPane, adding Plot.");
102                                                addPlot();
103                                                return;
104                                        }
105                                }
106                        }
107                }).start();             
108    }
109
110        public void fixGraphics() {
111                SwingUtilities.invokeLater(new Runnable() {
112                        public void run() {
113                                plotsPanel.repaint();
114                                plotsPanel.revalidate();
115                                plotsPanel.repaint();
116                        }
117                });             
118        }
119
120        private JButton createAddPlotButton() {
121                JButton button = new JButton();
122                button.setText("New Plot");
123                final PlotsEditorPanel me = this; 
124                FigureAction action = new FigureAction("add plot") {
125                        @Override
126                        public void actionPerformed(ActionEvent e) {
127                                me.addPlot();
128                        }
129                };
130                button.setAction(action);
131                return button;
132        }
133
134        /** Get the container frame. */
135    public TableauFrame getParentFrame()
136    {
137        return _frame;
138    }
139
140    /** Get the name of the tab. */
141    public String getTabName() {
142                return _title;
143        }
144
145    /** Initialize the contents of the tab. */
146    public void initializeTab() throws Exception {
147                
148    }
149        
150        private boolean canAddPlot() {
151                TabPane pane = TabManager.getInstance().getTab(this.getParentFrame(), "Plot Viewer");
152                return pane != null;
153        }
154        
155        public void addPlot() {
156                TabPane pane = TabManager.getInstance().getTab(this.getParentFrame(), "Plot Viewer");
157                Plot plot = ((PlotsPanel) pane).addGraph();
158
159                addPlotEditor(true, true, plot);
160        }
161        
162        public void addPlotEditor(boolean enabled, boolean refreshGraphics, Plot plot) {
163                PlotEditor editor = new PlotEditor(this, plot);
164                
165                if (plot != null) {
166                        plot.setPlotEditor(editor);
167                }
168                editor.setActive(enabled);
169                plotsPanel.add(editor);
170                
171                if (refreshGraphics) {
172                        fixGraphics();
173                }
174        }
175
176        /** Set the frame for this tab. */
177    public void setParentFrame(TableauFrame parent)
178    {
179        _frame = parent;
180    }
181
182        public void removePlot(final PlotEditor plotEditor) {
183                SwingUtilities.invokeLater(new Runnable() {
184                        public void run() {
185                                plotsPanel.remove(plotEditor);
186                        }
187                });
188        }
189
190        public synchronized int getNextUnusedGraphId() {
191                return graphId++;
192        }
193
194        public static void setPlottingControllerFactory(PlottingControllerFactory plottingControllerFactory) {
195                PlotsEditorPanel.plottingControllerFactory = plottingControllerFactory;
196        }
197
198        public static PlottingControllerFactory getPlottingControllerFactory() {
199                return plottingControllerFactory;
200        }
201
202        public static class Factory extends TabPaneFactory {
203                public Factory(NamedObj container, String name) throws IllegalActionException, NameDuplicationException {
204                        super(container, name);
205                }
206                
207                public TabPane createTabPane(TableauFrame parent) {
208                        return new PlotsEditorPanel(parent, "Plot Designer");
209                }
210        }
211        
212        private JPanel plotsPanel;
213        private static PlottingControllerFactory plottingControllerFactory;
214    private TableauFrame _frame;
215    private String _title;
216        private int graphId = 1;
217    
218}