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.Color;
033import java.awt.Container;
034import java.awt.Dimension;
035import java.awt.datatransfer.DataFlavor;
036import java.awt.datatransfer.Transferable;
037import java.awt.datatransfer.UnsupportedFlavorException;
038import java.awt.event.ActionEvent;
039import java.awt.event.ActionListener;
040import java.io.IOException;
041import java.util.ArrayList;
042import java.util.List;
043
044import javax.swing.DefaultCellEditor;
045import javax.swing.JComboBox;
046import javax.swing.JComponent;
047import javax.swing.JMenuItem;
048import javax.swing.JPopupMenu;
049import javax.swing.JTable;
050import javax.swing.TransferHandler;
051import javax.swing.event.TableModelListener;
052import javax.swing.table.AbstractTableModel;
053import javax.swing.table.TableCellEditor;
054import javax.swing.table.TableCellRenderer;
055
056import org.jfree.data.general.Dataset;
057import org.kepler.gui.PlotsEditorPanel;
058import org.kepler.plotting.table.ColorTableCellEditor;
059import org.kepler.plotting.table.ColorTableCellRenderer;
060
061import ptolemy.actor.TypedIOPort;
062import ptolemy.data.type.ArrayType;
063import ptolemy.data.type.BaseType;
064import ptolemy.data.type.Type;
065import ptolemy.vergil.toolbox.PtolemyTransferable;
066
067/**
068 * Created by IntelliJ IDEA.
069 * User: sean
070 * Date: Jul 7, 2010
071 * Time: 12:31:50 PM
072 */
073
074public class DataTable extends JTable implements ActionListener {
075        
076        private DataTable() {
077                DataTable.Model model = new DataTable.Model();
078                model.setTable(this);
079                this.setModel(model);
080                this.setTransferHandler(new MyTransferHandler());
081                // insertDemoData();
082                
083                this.addMouseListener(new java.awt.event.MouseAdapter() {
084                        // os X
085                        public void mousePressed(java.awt.event.MouseEvent evt) {
086                                jTableShowContextMenu(evt);
087                        }
088                        // Windows
089                        public void mouseReleased(java.awt.event.MouseEvent evt) {
090                                jTableShowContextMenu(evt);
091                        }
092                });
093                popupMenu.add(removeRowMenuItem);
094                removeRowMenuItem.addActionListener(this);
095                removeRowMenuItem.setActionCommand(REMOVE_ROW);
096                
097                // adjust column widths
098                int numCols = getColumnCount();
099                for (int i=0; i<numCols; i++){
100                        getColumnModel().getColumn(i).setPreferredWidth(Row.COLUMN_WIDTHS[i]);
101                }
102        }
103        
104        private void jTableShowContextMenu(java.awt.event.MouseEvent mouseEvent) {
105                DataTable dataTable = getPlotEditor().getTable();
106                int[] selectedRows = dataTable.getSelectedRows();
107                int clickedRow = dataTable.rowAtPoint(mouseEvent.getPoint());
108                
109                boolean clickWasOnASelectedRow = false;
110                for (int i = 0; i < selectedRows.length; i++) {
111                        if (clickedRow == selectedRows[i]) {
112                                clickWasOnASelectedRow = true;
113                                break;
114                        }
115                }
116                
117                if (selectedRows.length > 0 && clickWasOnASelectedRow && 
118                                (mouseEvent.isPopupTrigger() || mouseEvent.isControlDown())){
119                        popupMenu.show(mouseEvent.getComponent(), mouseEvent
120                                        .getX(), mouseEvent.getY());
121                }
122        }
123        
124        public DataTable(PlotEditor plotEditor) {
125                this();
126                setPlotEditor(plotEditor);
127        }
128        
129        public void add(Row row) {
130                ((Model) this.getModel()).addRow(row);
131        }
132        
133        public void removeRow(Row row) {
134                ((Model) this.getModel()).removeRow(row);
135        }
136                
137        public Row getRow(int index) {
138                return ((Model) this.getModel()).getRow(index);
139        }
140        
141        public void update() {
142                ((Model) this.getModel()).fireTableDataChanged();
143        }
144
145        @Override
146        public boolean getScrollableTracksViewportHeight() {
147                Container parent = getParent();
148                if (parent != null) {
149                        return parent.getHeight() > getPreferredSize().height;
150                }
151                else {
152                        return false;
153                }
154        }
155
156        @Override
157        public TableCellRenderer getCellRenderer(int row, int column) {
158                if (this.getColumnClass(column) == Color.class) {
159                        return new ColorTableCellRenderer();
160                }
161                else {
162                        return super.getCellRenderer(row, column);
163                }
164        }
165
166        @Override
167        public TableCellEditor getCellEditor(int row, int column) {
168                if (this.getColumnClass(column) == Color.class) {
169                        return new ColorTableCellEditor();
170                }
171                else if (this.getColumnClass(column) == PointType.class) {
172                        if (pointTypeChoices == null) {
173                                pointTypeChoices = new JComboBox();
174                                for (PointType pointType : PointType.values()) {
175                                        pointTypeChoices.addItem(pointType);
176                                }
177                        }
178                        return new DefaultCellEditor(pointTypeChoices);
179                }
180                else {
181                        return super.getCellEditor(row, column);
182                }
183        }
184        
185        private JComboBox pointTypeChoices = null;
186
187        private void insertDemoData() {
188                Model model = (Model) this.getModel();
189                model.addRow(new Row("sensor0", "time", "sensor0.data", PointType.CIRCLE, Color.BLACK, this.getPlotEditor()));
190                model.addRow(new Row("sensor1", "time", "sensor1.data", PointType.SQUARE, Color.BLUE, this.getPlotEditor()));
191                model.addRow(new Row("sensor2", "time", "sensor2.data", PointType.DIAMOND, Color.GREEN, this.getPlotEditor()));
192        }
193
194        public void setActive(boolean active) {
195                if (!active) {
196                        // insertDemoData();
197                }
198        }
199
200        public PlotEditor getPlotEditor() {
201                return plotEditor;
202        }
203
204        private void setPlotEditor(PlotEditor plotEditor) {
205                this.plotEditor = plotEditor;
206        }
207
208        public void setDataset(Dataset dataset) {
209                ((Model) this.getModel()).setDataset(dataset);
210        }
211
212        public PlottingControllerFactory getPlottingControllerFactory() {
213                return plottingControllerFactory;
214        }
215
216        public PlottingController getPlottingController() {
217                return plottingController;
218        }
219
220        public void actionPerformed(ActionEvent ae) {
221
222                DataTable table = getPlotEditor().getTable();
223                int[] selectedRows = table.getSelectedRows();
224                List<Row> rows = new ArrayList<Row>();
225                for (int selectedRow : selectedRows) {
226                        rows.add(table.getRow(selectedRow));
227                }
228
229                for (Row row : rows) {
230                        row.notifyDeletionListeners();
231                        table.removeRow(row);
232                }
233                table.update();
234        }
235
236        private PlotEditor plotEditor = null;
237        private static PlottingControllerFactory plottingControllerFactory;
238
239        private class Model extends AbstractTableModel {
240
241                public void addRow(Row row) {
242                        rows.add(row);
243                        initializeController();
244                        getPlottingController().initialize(row, getPlotEditor().getPlot());
245                        this.getTable().setSize(new Dimension(2, 2));
246                }
247                
248//              public void removeIndividualRow(Row row) {
249//                      removeRow(row);
250//                      SwingUtilities.invokeLater(new Runnable() {
251//                              public void run() {
252//                                      fireTableDataChanged();
253//                              }
254//                      });
255//              }
256                
257                private void removeRow(Row row) {
258                        rows.remove(row);
259                        this.getTable().setSize(new Dimension(2, 2));
260                }
261                
262                public Row getRow(int index) {
263                        return rows.get(index);
264                }
265                
266                public int getRowCount() {
267                        return rows.size();
268                }
269
270                public int getColumnCount() {
271                        return Row.getColumnCount();
272                }
273
274                public String getColumnName(int columnIndex) {
275                        return Row.getColumnName(columnIndex);
276                }
277
278                public Class<?> getColumnClass(int columnIndex) {
279                        return Row.getColumnClass(columnIndex);
280                }
281
282                public boolean isCellEditable(int rowIndex, int columnIndex) {
283                        return Row.isEditableColumns(columnIndex);
284                }
285
286                public Object getValueAt(int rowIndex, int columnIndex) {
287                        return rows.get(rowIndex).getValueByIndex(columnIndex);
288                }
289
290                public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
291                        
292                        rows.get(rowIndex).setCandidateValue(columnIndex, aValue);
293                        // rows.get(rowIndex).setValueByIndex(columnIndex, aValue);
294                        
295                        UpdateType updateType = null;
296                        if (columnIndex == 0) {
297                                updateType = UpdateType.SENSOR_NAME;
298                        }
299                        else if (columnIndex == 1) {                            
300                                updateType = UpdateType.X_AXIS;
301                        }
302                        else if (columnIndex == 2) {
303                                updateType = UpdateType.Y_AXIS;
304                        }
305                        else if (columnIndex == 3) {
306                                updateType = UpdateType.POINT_TYPE;
307                        }
308                        else if (columnIndex == 4) {
309                                updateType = UpdateType.COLOR;
310                        }
311                        
312                        updateSensor(rows.get(rowIndex), updateType);
313                }
314
315                private void updateSensor(Row row, UpdateType updateType) {
316                        initializeController();
317                        getPlottingController().register(row, updateType, getPlotEditor().getPlot());
318                }
319
320                public void addTableModelListener(TableModelListener l) {
321                        listeners.add(l);
322                }
323
324                public void removeTableModelListener(TableModelListener l) {
325                        listeners.remove(l);
326                }
327
328                public void setDataset(Dataset dataset) {
329                        this.dataset = dataset;
330                }
331
332                public void setTable(DataTable table) {
333                        this.table = table;
334                }
335                
336                public DataTable getTable() {
337                        return table;
338                }
339
340                private List<Row> rows = new ArrayList<Row>();          
341                private List<TableModelListener> listeners = new ArrayList<TableModelListener>();
342                private Dataset dataset = null;
343                private DataTable table;
344        }
345
346        private void initializeController() {
347                if (plottingController == null) {
348                        plottingController = PlotsEditorPanel.getPlottingControllerFactory().
349                                getPlottingController(getPlotEditor().getEditorPanel().getParentFrame());
350                }
351        }
352        
353        private PlottingController plottingController = null;
354
355        private class MyTransferHandler extends TransferHandler {
356                
357                public boolean canImport(JComponent component, DataFlavor[] flavors) {
358                        for (DataFlavor flavor : flavors) {
359                                if (PtolemyTransferable.namedObjFlavor.equals(flavor)) {
360                                        return true;
361                                }
362                        }
363                        return false;
364                }
365                
366                public boolean importData(JComponent component, Transferable tr) {
367
368                        if (canImport(component, tr.getTransferDataFlavors())) {
369                                try {
370                                        List droppedObjects = (List) tr.getTransferData(PtolemyTransferable.namedObjFlavor);
371                                        for (Object droppedObject : droppedObjects) {
372                                                TypedIOPort sensorDataPort = null;
373                                                if (droppedObject instanceof TypedIOPort) {
374                                                        TypedIOPort port = (TypedIOPort) droppedObject;
375                                                        if (isPlottablePort(port)) {
376                                                                sensorDataPort = port;
377                                                        }
378                                                }
379                                                
380                                                if (sensorDataPort != null) {
381                                                        // This is a plottable item
382                                                        String sensorName = sensorDataPort.getFullName();
383                                                        Row row = new Row(sensorName, "time", sensorName, 
384                                                                        PlottingConstants.DEFAULT_PLOT_ITEM_SHAPE, 
385                                                                        PlottingConstants.DEFAULT_PLOT_ITEM_COLOR, getPlotEditor());
386                                                        row.setDataPort(sensorDataPort);
387                                                        add(row);
388                                                        getPlottingController().updatePlotManually(row);
389                                                }
390                                        }
391                                        return true;
392                                }
393                                catch(UnsupportedFlavorException e) {
394                                        e.printStackTrace();
395                                }
396                                catch(IOException e) {
397                                        e.printStackTrace();
398                                }
399                        }
400                        return false;
401                }
402                
403                public int getSourceActions(JComponent component) {
404                        return COPY;
405                }
406                
407                protected void exportDone(JComponent component, Transferable tr, int action) {
408                        // System.out.println("Export done!: " + component);
409                }               
410        }
411                
412        private static boolean isPlottablePort(TypedIOPort port) {
413                Type type = port.getType();
414                return type.isCompatible(PLOTTABLE_TYPE);
415        }
416        
417        private static final Type PLOTTABLE_TYPE = new ArrayType(BaseType.INT, 2);
418        
419        private JPopupMenu popupMenu = new JPopupMenu();
420        private static final String REMOVE_ROW = "Delete";
421        private JMenuItem removeRowMenuItem = new JMenuItem(REMOVE_ROW);
422        
423        
424}