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