001    package org.LiveGraph.gui;
002    
003    import java.awt.BorderLayout;
004    import java.awt.Color;
005    import java.awt.Component;
006    import java.awt.Dimension;
007    import java.awt.FlowLayout;
008    import java.awt.FontMetrics;
009    import java.awt.event.ActionEvent;
010    import java.awt.event.ActionListener;
011    import java.awt.event.WindowAdapter;
012    import java.awt.event.WindowEvent;
013    import java.util.ArrayList;
014    import java.util.List;
015    import java.util.StringTokenizer;
016    
017    import javax.swing.AbstractCellEditor;
018    import javax.swing.BorderFactory;
019    import javax.swing.DefaultCellEditor;
020    import javax.swing.JButton;
021    import javax.swing.JColorChooser;
022    import javax.swing.JComboBox;
023    import javax.swing.JFrame;
024    import javax.swing.JLabel;
025    import javax.swing.JOptionPane;
026    import javax.swing.JPanel;
027    import javax.swing.JScrollPane;
028    import javax.swing.JTable;
029    import javax.swing.JTextField;
030    import javax.swing.ListSelectionModel;
031    import javax.swing.WindowConstants;
032    import javax.swing.table.AbstractTableModel;
033    import javax.swing.table.TableCellEditor;
034    import javax.swing.table.TableCellRenderer;
035    
036    import org.LiveGraph.LiveGraph;
037    import org.LiveGraph.dataCache.CacheObserver;
038    import org.LiveGraph.dataCache.DataCache;
039    import org.LiveGraph.settings.DataSeriesSettings;
040    import org.LiveGraph.settings.GraphSettings;
041    import org.LiveGraph.settings.ObservableSettings;
042    import org.LiveGraph.settings.SettingsObserver;
043    import org.LiveGraph.settings.DataSeriesSettings.TransformMode;
044    
045    import com.softnetConsult.utils.collections.ReadOnlyIterator;
046    import com.softnetConsult.utils.swing.DisEnablingPanel;
047    import com.softnetConsult.utils.swing.SwingTools;
048    
049    
050    /**
051     * The "Series Settings" window of the application.
052     * 
053     * <p style="font-size:smaller;">This product includes software developed by the
054     *    <strong>LiveGraph</strong> project and its contributors.<br />
055     *    (<a href="http://www.live-graph.org" target="_blank">http://www.live-graph.org</a>)<br />
056     *    Copyright (c) 2007 G. Paperin.<br />
057     *    All rights reserved.
058     * </p>
059     * <p style="font-size:smaller;">File: SeriesSettingsWindow.java</p> 
060     * <p style="font-size:smaller;">Redistribution and use in source and binary forms, with or
061     *    without modification, are permitted provided that the following terms and conditions are met:
062     * </p>
063     * <p style="font-size:smaller;">1. Redistributions of source code must retain the above
064     *    acknowledgement of the LiveGraph project and its web-site, the above copyright notice,
065     *    this list of conditions and the following disclaimer.<br />
066     *    2. Redistributions in binary form must reproduce the above acknowledgement of the
067     *    LiveGraph project and its web-site, the above copyright notice, this list of conditions
068     *    and the following disclaimer in the documentation and/or other materials provided with
069     *    the distribution.<br />
070     *    3. All advertising materials mentioning features or use of this software or any derived
071     *    software must display the following acknowledgement:<br />
072     *    <em>This product includes software developed by the LiveGraph project and its
073     *    contributors.<br />(http://www.live-graph.org)</em><br />
074     *    4. All advertising materials distributed in form of HTML pages or any other technology
075     *    permitting active hyper-links that mention features or use of this software or any
076     *    derived software must display the acknowledgment specified in condition 3 of this
077     *    agreement, and in addition, include a visible and working hyper-link to the LiveGraph
078     *    homepage (http://www.live-graph.org).
079     * </p>
080     * <p style="font-size:smaller;">THIS SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY
081     *    OF ANY KIND, EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
082     *    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND  NONINFRINGEMENT. IN NO EVENT SHALL
083     *    THE AUTHORS, CONTRIBUTORS OR COPYRIGHT  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
084     *    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING  FROM, OUT OF OR
085     *    IN CONNECTION WITH THE SOFTWARE OR THE USE OR  OTHER DEALINGS IN THE SOFTWARE.
086     * </p>
087     * 
088     * @author Greg Paperin (<a href="http://www.paperin.org" target="_blank">http://www.paperin.org</a>)
089     * @version {@value org.LiveGraph.LiveGraph#version}
090     */
091    public class SeriesSettingsWindow extends JFrame implements CacheObserver, SettingsObserver,
092                                                                                                                                            SeriesHighlightListener {
093    
094    private static final long HIGHLIGHT_LEN = 1500; 
095    
096    private String[] seriesLabels = null; 
097    private AbstractTableModel tableModel = null;
098    private JTable table = null;
099    private ListSelectionModel selectionModel = null;
100    
101    private JComboBox scaleTypeCombo = null;
102    
103    private DisEnablingPanel topPanel;
104    private JButton advPanelButt = null, advGoButt = null;
105    private JPanel advPanel = null;
106    private int helpCols = 0;
107    private JTextField advFrom = null, advTo = null, advEvery = null;
108    private JComboBox advAction = null;
109    
110    /**
111     * This is the default constructor.
112     */
113    public SeriesSettingsWindow() {
114            super();
115            initialize();
116    }
117    
118    /**
119     * This method initializes the window.
120     */
121    private void initialize() {
122            
123            // Window settings:
124            
125            //final SeriesSettingsWindow SERIESSETTINGS_WIN = this;
126            this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
127            Dimension frameDim = new Dimension(450, 200);
128            this.setPreferredSize(frameDim);
129            this.setBounds(480, 510, frameDim.width, frameDim.height);      
130            this.setTitle("Data series settings (LiveGraph)");
131            
132            // Hide-show listener:
133            
134            this.addWindowListener(new WindowAdapter() {
135                    @Override public void windowClosing(WindowEvent e) {
136                            LiveGraph.application().setDisplaySeriesSettingsWindow(false);
137                    }
138            });
139            
140            // Layout:
141            
142            getContentPane().setLayout(new BorderLayout(0, 0));
143            
144            // Buttons at the top:
145            
146            JPanel panel = null;
147            JButton button = null;
148            JLabel label = null;
149            
150            topPanel = new DisEnablingPanel(new BorderLayout());
151            getContentPane().add(topPanel, BorderLayout.NORTH);
152            
153            panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 1, 1));
154            topPanel.add(panel, BorderLayout.NORTH);
155            
156            button = new JButton("Show all");
157            button.addActionListener(new ActionListener() {
158                    public void actionPerformed(ActionEvent e) {
159                            if (null == tableModel)  return;
160                            DataSeriesSettings dss = LiveGraph.application().getDataSeriesSettings();
161                            dss.setShowAll(0, tableModel.getRowCount() - 1, true);                  
162                            tableModel.fireTableDataChanged();
163            }
164            });
165            panel.add(button);
166            
167            button = new JButton("Hide all");
168            button.addActionListener(new ActionListener() {
169                    public void actionPerformed(ActionEvent e) {
170                            if (null == tableModel)  return;
171                            DataSeriesSettings dss = LiveGraph.application().getDataSeriesSettings();
172                            dss.setShowAll(0, tableModel.getRowCount() - 1, false);                 
173                            tableModel.fireTableDataChanged();
174            }
175            });
176            panel.add(button);
177            
178            button = new JButton("Toggle all");
179            button.addActionListener(new ActionListener() {
180                    public void actionPerformed(ActionEvent e) {
181                            if (null == tableModel)  return;
182                            DataSeriesSettings dss = LiveGraph.application().getDataSeriesSettings();
183                            dss.setShowToggleAll(0, tableModel.getRowCount() - 1);                  
184                            tableModel.fireTableDataChanged();
185            }
186            });
187            panel.add(button);
188            
189            // Advanced selection panel:
190            advPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 1, 1));
191            
192            advPanelButt = new JButton(">>");
193            advPanelButt.addActionListener(new ActionListener() {
194                    public void actionPerformed(ActionEvent e) {
195                            setAdvancedPanelVisibility(!advPanel.isVisible());                      
196            }
197            });
198            panel.add(advPanelButt);
199                    
200            topPanel.add(advPanel, BorderLayout.CENTER);    
201            
202            advPanel.add(label = new JLabel("From:"));
203            label.setFont(SwingTools.getPlainFont(label));
204            advFrom = new JTextField("0", 4);
205            advPanel.add(advFrom);
206            
207            advPanel.add(label = new JLabel("To:"));
208            label.setFont(SwingTools.getPlainFont(label));
209            advTo = new JTextField("10000", 4);
210            advPanel.add(advTo);
211            
212            advPanel.add(label = new JLabel("Every:"));
213            label.setFont(SwingTools.getPlainFont(label));
214            advEvery = new JTextField("10", 8);
215            advPanel.add(advEvery);
216            
217            advAction = new JComboBox(new String[] {"Show", "Hide", "Toggle"});
218            advAction.setFont(SwingTools.getPlainFont(advAction));
219            advPanel.add(advAction);
220            
221            advGoButt = new JButton("Go");
222            advGoButt.addActionListener(new ActionListener() {
223                    public void actionPerformed(ActionEvent e) {
224                            runAdvancedSelector();                  
225            }
226            });
227            advPanel.add(advGoButt);
228            
229            // Main table:
230            
231            panel = new JPanel(new BorderLayout(0, 0));
232            
233            table = createTable();
234            panel.add(new JScrollPane(table), BorderLayout.CENTER);
235            getContentPane().add(panel, BorderLayout.CENTER);
236            
237            setAdvancedPanelVisibility(false);
238            
239    } // private void initialize()
240    
241    /**
242     * Creates and initialises the labels table.
243     * @return The labels table.
244     */
245    private JTable createTable() {
246            
247            final String[] scaleModes = new String[] {"Actual value", "Transformed into [0..1]",
248                                                                                              "Scaled by specified value"};
249            final String[] colNames = new String[] {"Show", "Label", "Colour", "Transformation", "Scale factor"};
250            final String[] helperColumnNames = new String[] {"Index"};
251            
252            seriesLabels = new String[0];
253            
254            tableModel = new AbstractTableModel() {
255                                    
256                    public int getColumnCount() {
257                    return colNames.length + helpCols;
258            }
259    
260                    public int getRowCount() {
261                    return seriesLabels.length;
262            }
263                    
264                    @Override
265                    public String getColumnName(int col) {
266                            if (col < helpCols)
267                                    return helperColumnNames[col];
268                            return colNames[col - helpCols];
269                    }
270    
271                    public Object getValueAt(int row, int col) {
272                            if (1 == helpCols && 0 == col)
273                                    return row;                     
274                            switch (col - helpCols) {
275                                    case 0: return LiveGraph.application().getDataSeriesSettings().getShow(row);
276                                    case 1: return seriesLabels[row];
277                                    case 2: return LiveGraph.application().getDataSeriesSettings().getColour(row);
278                                    case 3: switch(LiveGraph.application().getDataSeriesSettings().getTransformMode(row)) {
279                                                            case Transform_None:   return scaleModes[0];
280                                                            case Transform_In0to1: return scaleModes[1];
281                                                            case Transform_SetVal: return scaleModes[2];
282                                                            default: throw new Error("This cannot happen!");
283                                                    }
284                                    case 4: return LiveGraph.application().getDataSeriesSettings().getScaleFactor(row);
285                                    default: throw new Error("Forgot to provide getValueAt for table column " + col + ".");
286                            }
287            }
288                    
289                    @Override
290                    public void setValueAt(Object val, int row, int col) {
291                            super.setValueAt(val, row, col);                        
292                            switch (col - helpCols) {
293                                    case 0: LiveGraph.application().getDataSeriesSettings().setShow(row, ((Boolean) val).booleanValue());
294                                                    break;
295                              //case 1: same as the default case.
296                                    case 2: LiveGraph.application().getDataSeriesSettings().setColour(row, (Color) val);
297                                                    break;
298                                    case 3: if (scaleModes[0].equals(val))
299                                                            LiveGraph.application().getDataSeriesSettings().setTransformMode(row, TransformMode.Transform_None);
300                                                    else if (scaleModes[1].equals(val))
301                                                            LiveGraph.application().getDataSeriesSettings().setTransformMode(row, TransformMode.Transform_In0to1);
302                                                    else if (scaleModes[2].equals(val))
303                                                            LiveGraph.application().getDataSeriesSettings().setTransformMode(row, TransformMode.Transform_SetVal);
304                                                    else
305                                                            throw new Error("Unexpected scale mode (" + val + ")!");
306                                                    break;
307                                    case 4: LiveGraph.application().getDataSeriesSettings().setScaleFactor(row, ((Double) val).doubleValue());
308                                                    break;
309                                    default: throw new Error("Column " + col + " is not supposed to be editable.");
310                            }
311                    }
312                    
313                    @Override
314                    public boolean isCellEditable(int row, int col) {
315                            return (col >= helpCols && col != 1 + helpCols);                     
316                    }
317                    
318                    @Override
319                    public Class<?> getColumnClass(int col) {
320                            if (1 == helpCols && 0 == col)
321                                    return Integer.class;
322                            switch (col - helpCols) {
323                                    case 0: return Boolean.class;
324                                    case 1: return String.class;
325                                    case 2: return Color.class;
326                                    case 3: return String.class;
327                                    case 4: return Double.class;
328                                    default: throw new Error("Forgot to provide getColumnClass for table column " + col + ".");
329                            }
330                }
331                    
332            }; // AbstractTableModel model = new AbstractTableModel()
333            
334            
335            //JTable table = new JTable(tableModel);
336            
337            JTable table = new JTable(tableModel) {
338                    @Override public void changeSelection(int rowIndex, int columnIndex, boolean toggle, boolean extend) {                  
339                            ; // Prevent the table selection to be changed by user input.
340                    }
341            };
342            
343            
344            selectionModel = table.getSelectionModel();
345            selectionModel.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
346            
347            scaleTypeCombo = new JComboBox(scaleModes);             
348            scaleTypeCombo.setFont(SwingTools.getPlainFont(scaleTypeCombo));
349            
350            table.getColumnModel().getColumn(3 + helpCols).setCellEditor(new DefaultCellEditor(scaleTypeCombo));
351            
352            table.setDefaultEditor(Color.class, new ColourEditor());
353            table.setDefaultRenderer(Color.class, new TableCellRenderer() {
354                    private JLabel label = new JLabel();
355                    public Component getTableCellRendererComponent(JTable table, Object colour,
356                                                                                                               boolean isSelected, boolean hasFocus, int row, int column) {                 
357                            label.setOpaque(true);
358                            Color brdCol = isSelected ? table.getSelectionBackground() : table.getBackground();
359                            label.setBorder(BorderFactory.createLineBorder(brdCol, 3));
360                            label.setBackground((Color) colour);
361                            return label;
362                    }
363            
364            });
365            
366            Component rc = table.getTableHeader().getDefaultRenderer().getTableCellRendererComponent(table, null,
367                                                                                                                                                                                             false, false, 0, 0);
368            FontMetrics fm = rc.getFontMetrics(rc.getFont());
369            int w, sumW;
370            
371            sumW = w = fm.stringWidth(colNames[0 + helpCols]) + 10;
372            table.getColumnModel().getColumn(0 + helpCols).setPreferredWidth(w);
373            
374            sumW += w = fm.stringWidth(colNames[2 + helpCols]) + 10;
375            table.getColumnModel().getColumn(2 + helpCols).setPreferredWidth(w);
376            
377            w = 0;
378            for (String sm : scaleModes) { w = Math.max(w, fm.stringWidth(sm)); }
379            sumW += w = Math.max(w, fm.stringWidth(colNames[3 + helpCols])) + 10;   
380            table.getColumnModel().getColumn(3 + helpCols).setPreferredWidth(w);
381            
382            sumW += w = fm.stringWidth(colNames[4 + helpCols]) + 10;
383            table.getColumnModel().getColumn(4 + helpCols).setPreferredWidth(w);
384            
385            w = table.getPreferredScrollableViewportSize().width - sumW - 20;
386            table.getColumnModel().getColumn(1 + helpCols).setPreferredWidth(w);
387            
388            return table;
389            
390    } // private JTable createTable()
391    
392    
393    /**
394     * Shows and hides the advanced selector panel.
395     * @param show Whether to show the advanced selector panel.
396     */
397    private void setAdvancedPanelVisibility(boolean show) {
398            
399            advPanelButt.setText(show ? "<<" : ">>");
400            advPanel.setVisible(show);
401            
402            if (show) {
403                    helpCols = 1;
404            } else {
405                    helpCols = 0;
406            }
407                    
408            tableModel.fireTableStructureChanged();
409            table.getColumnModel().getColumn(3 + helpCols).setCellEditor(new DefaultCellEditor(scaleTypeCombo));
410    }
411    
412    /**
413     * Executes the advanced selection of data series.
414     */
415    private void runAdvancedSelector() {
416            
417            // Get values:
418            int start, end;
419            List<Integer> mods;
420            int action;
421            try {           
422                    start = Integer.parseInt(advFrom.getText());
423                    end = Integer.parseInt(advTo.getText());                
424                    
425                    StringTokenizer tok = new StringTokenizer(advEvery.getText(), ",; ");
426                    mods = new ArrayList<Integer>();
427                    while(tok.hasMoreTokens()) {
428                            int t = Integer.parseInt(tok.nextToken());
429                            if (t > 0)
430                                    mods.add(t);
431                    }
432                    
433                    Object act = advAction.getSelectedItem();
434                    if ("Show".equals(act))
435                            action = 1;
436                    else if ("Hide".equals(act))
437                            action = 2;
438                    else if ("Toggle".equals(act))
439                            action = 3;
440                    else
441                            action = -1;
442                    
443            } catch(NumberFormatException e) {
444                    JOptionPane.showMessageDialog(this, e.getMessage(), "Invalid number format", JOptionPane.ERROR_MESSAGE);
445                    return;
446            }
447            
448            if (0 > start)
449                    start = 0;
450            if (tableModel.getRowCount() <= start)
451                    start = tableModel.getRowCount() - 1;
452            
453            if (0 > end || tableModel.getRowCount() <= end)
454                    end = tableModel.getRowCount() - 1;
455            
456            if (start > end) {
457                    int t = start; start = end; end = t;
458            }
459            
460            advFrom.setText(Integer.toString(start));
461            advTo.setText(Integer.toString(end));
462            
463            topPanel.setEnabled(false);
464            DataSeriesSettings setts = LiveGraph.application().getDataSeriesSettings();     
465            for (int r = start; r <= end; r++) {
466                    
467                    advGoButt.setText(Integer.toString(r));
468                    
469                    int offs = r - start;
470                    boolean apply = false;
471                    for (int m = 0; !apply && m < mods.size(); m++) {
472                            apply = (0 == offs % mods.get(m));
473                    }
474                    
475                    if (!apply)
476                            continue;
477                    
478                    switch(action) {
479                            case 1: setts.setShow(r, true); break;
480                            case 2: setts.setShow(r, false); break;
481                            case 3: setts.setShow(r, !setts.getShow(r)); break;
482                            default: throw new Error("Bug! This line should never be reached. Have fun debugging.");
483                    }
484            }
485            advGoButt.setText("Go");
486            topPanel.setEnabled(true);      
487    }
488    
489    /**
490     * Locally updates the series-lables when they have been changed in the data cache.
491     */
492    public void cacheEventFired(DataCache cache, CacheEvent event) {
493            
494            switch(event) {
495                    case UpdateLabels:
496                            seriesLabels = new String[cache.countDataSeries()];
497                            ReadOnlyIterator<String> labs = cache.iterateDataSeriesLabels();
498                            int i = 0;
499                            while (labs.hasNext())
500                                    seriesLabels[i++] = labs.next();
501                            tableModel.fireTableDataChanged();
502                            break;
503                    case ChangeMode:
504                    case UpdateData:
505                    case UpdateDataFileInfo:                
506                            break;
507                    default:
508                            throw new Error("This case is impossible!");
509                            
510            }
511    } // public void cacheEventFired(DataCache cache, CacheEvent event)
512    
513    
514    /**
515     * Dispatches the settings change events.
516     * @see #settingHasChanged(DataSeriesSettings, String)
517     * @see #settingHasChanged(GraphSettings, String)
518     */
519    public void settingHasChanged(ObservableSettings settings, Object info) {
520            
521            if (null == info || null == settings)
522                    return;
523            
524            if ((settings instanceof DataSeriesSettings) && (info instanceof String)) {
525                    settingHasChanged((DataSeriesSettings) settings, (String) info);
526                    return;
527            }
528            
529            if ((settings instanceof GraphSettings) && (info instanceof String)) {
530                    settingHasChanged((GraphSettings) settings, (String) info);
531                    return;
532            }
533    } // public void settingHasChanged(ObservableSettings settings, Object info)
534    
535    /**
536     * Updates the table display when series settings were loaded from a file.
537     * @param settings Series settings.
538     * @param info Event info.
539     */
540    public void settingHasChanged(DataSeriesSettings settings, String info) {
541            
542            if (null == info || null == settings)
543                    return;
544            
545            if (info.equals("load")) {
546                    tableModel.fireTableDataChanged();
547                    return;
548            }
549            
550            if (info.startsWith("Show.")) {
551                    int r = Integer.parseInt(info.substring(info.lastIndexOf('.') + 1));
552                    tableModel.fireTableRowsUpdated(r, r);
553                    return;
554            }
555    } // public void settingHasChanged(DataSeriesSettings settings, String info)
556    
557    /**
558     * When a series was selected as x-axis, the corresponding setting is highlighted for a second.
559     * @param settings Graph settings.
560     * @param info Event info.
561     */
562    public void settingHasChanged(GraphSettings settings, String info) {
563            
564            if (null == info || null == settings)
565                    return;
566            
567            if (info.equals("XAxisType") || info.equals("XAxisSeriesIndex")) {
568                    
569                    if (GraphSettings.XAxisType.XAxis_DSNum == settings.getXAxisType())
570                            return;
571            
572                    final int serInd = settings.getXAxisSeriesIndex();
573                    tableModel.fireTableRowsUpdated(serInd, serInd);
574                    selectionModel.setSelectionInterval(serInd, serInd);
575                    (new Thread(new Runnable() {
576                            public void run() {
577                                    try { Thread.sleep(HIGHLIGHT_LEN); } catch (InterruptedException e) {}
578                                    if (!selectionModel.getValueIsAdjusting()
579                                                    && selectionModel.getMinSelectionIndex() == serInd
580                                                    && selectionModel.getMaxSelectionIndex() == serInd) {
581                                            selectionModel.clearSelection();
582                                    }
583                }
584                    }, "SeriesMarkedAsXAxis-TableSelectionController")).start();
585                    return;         
586            }
587            
588    } // public void settingHasChanged(GraphSettings settings, String info)
589    
590    public void highlightSeries(List<Integer> seriesIndices) {
591            
592            if (null == seriesIndices)
593                    return;
594            
595            selectionModel.clearSelection();
596            
597            if (seriesIndices.isEmpty())
598                    return;
599            
600            for (int s : seriesIndices)
601                    selectionModel.addSelectionInterval(s, s);
602            /*
603            (new Thread(new Runnable() {
604                    public void run() {
605                            try { Thread.sleep(HIGHLIGHT_LEN); } catch (InterruptedException e) {}
606                            selectionModel.clearSelection();
607            }
608            }, "HighlightSeries-TableSelectionController")).start();
609            */
610            
611    } // public void highlightSeries(List<Integer> seriesIndices)
612    
613    /**
614     * A colour selection cell editor for the settings table.  
615     */
616    private class ColourEditor extends AbstractCellEditor implements TableCellEditor {
617    
618            private Color selColor;
619            private JButton button;
620            
621            public ColourEditor() {
622                    button = new JButton();
623                    button.addActionListener(new ActionListener() {
624                            public void actionPerformed(ActionEvent e) {
625                                    button.setBackground(selColor);
626                                    Color c = JColorChooser.showDialog(button, "Choose a colour for the data series.", selColor);
627                                    if (null != c) 
628                                            selColor = c;
629                                    fireEditingStopped();
630                }
631                    });     
632            }
633            
634            public Object getCellEditorValue() {
635                    return selColor;
636            }
637    
638            public Component getTableCellEditorComponent(JTable table, Object value,
639                                                                                                     boolean isSelected, int row, int column) {
640                    selColor = (Color) value;
641                    Color brdCol = isSelected ? table.getSelectionBackground() : table.getBackground();
642                    button.setBorder(BorderFactory.createLineBorder(brdCol, 3));
643                    return button;
644            }
645    } //private class ColourEditor
646    
647    } // public class SeriesSettingsWindow