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}