001    package org.LiveGraph.gui;
002    
003    import java.awt.Color;
004    import java.awt.ComponentOrientation;
005    import java.awt.FlowLayout;
006    import java.awt.GridBagConstraints;
007    import java.awt.GridBagLayout;
008    import java.awt.Insets;
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    
014    import javax.swing.Box;
015    import javax.swing.ButtonGroup;
016    import javax.swing.JColorChooser;
017    import javax.swing.JFrame;
018    import javax.swing.JPanel;
019    import javax.swing.JRadioButton;
020    import javax.swing.JSeparator;
021    import javax.swing.JTextField;
022    import javax.swing.SwingConstants;
023    import javax.swing.WindowConstants;
024    import java.awt.Dimension;
025    import javax.swing.JLabel;
026    import javax.swing.JButton;
027    
028    import javax.swing.BorderFactory;
029    import javax.swing.border.EtchedBorder;
030    
031    import org.LiveGraph.LiveGraph;
032    import org.LiveGraph.dataCache.CacheObserver;
033    import org.LiveGraph.dataCache.DataCache;
034    import org.LiveGraph.settings.GraphSettings;
035    import org.LiveGraph.settings.ObservableSettings;
036    import org.LiveGraph.settings.SettingsObserver;
037    
038    import com.softnetConsult.utils.collections.ReadOnlyIterator;
039    import com.softnetConsult.utils.swing.DisEnablingPanel;
040    import com.softnetConsult.utils.swing.ResizablePopupComboBox;
041    import com.softnetConsult.utils.swing.SwingTools;
042    
043    
044    import static org.LiveGraph.settings.GraphSettings.HGridType.*;
045    import static org.LiveGraph.settings.GraphSettings.VGridType.*;
046    import static org.LiveGraph.settings.GraphSettings.XAxisType.*;
047    
048    
049    /**
050     * The "Graph Settings" window of the application.
051     * 
052     * <p style="font-size:smaller;">This product includes software developed by the
053     *    <strong>LiveGraph</strong> project and its contributors.<br />
054     *    (<a href="http://www.live-graph.org" target="_blank">http://www.live-graph.org</a>)<br />
055     *    Copyright (c) 2007 G. Paperin.<br />
056     *    All rights reserved.
057     * </p>
058     * <p style="font-size:smaller;">File: GraphSettingsWindow.java</p> 
059     * <p style="font-size:smaller;">Redistribution and use in source and binary forms, with or
060     *    without modification, are permitted provided that the following terms and conditions are met:
061     * </p>
062     * <p style="font-size:smaller;">1. Redistributions of source code must retain the above
063     *    acknowledgement of the LiveGraph project and its web-site, the above copyright notice,
064     *    this list of conditions and the following disclaimer.<br />
065     *    2. Redistributions in binary form must reproduce the above acknowledgement of the
066     *    LiveGraph project and its web-site, the above copyright notice, this list of conditions
067     *    and the following disclaimer in the documentation and/or other materials provided with
068     *    the distribution.<br />
069     *    3. All advertising materials mentioning features or use of this software or any derived
070     *    software must display the following acknowledgement:<br />
071     *    <em>This product includes software developed by the LiveGraph project and its
072     *    contributors.<br />(http://www.live-graph.org)</em><br />
073     *    4. All advertising materials distributed in form of HTML pages or any other technology
074     *    permitting active hyper-links that mention features or use of this software or any
075     *    derived software must display the acknowledgment specified in condition 3 of this
076     *    agreement, and in addition, include a visible and working hyper-link to the LiveGraph
077     *    homepage (http://www.live-graph.org).
078     * </p>
079     * <p style="font-size:smaller;">THIS SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY
080     *    OF ANY KIND, EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
081     *    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND  NONINFRINGEMENT. IN NO EVENT SHALL
082     *    THE AUTHORS, CONTRIBUTORS OR COPYRIGHT  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
083     *    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING  FROM, OUT OF OR
084     *    IN CONNECTION WITH THE SOFTWARE OR THE USE OR  OTHER DEALINGS IN THE SOFTWARE.
085     * </p>
086     * 
087     * @author Greg Paperin (<a href="http://www.paperin.org" target="_blank">http://www.paperin.org</a>)
088     * @version {@value org.LiveGraph.LiveGraph#version}
089     */
090    public class GraphSettingsWindow extends JFrame implements SettingsObserver, CacheObserver {
091    
092    private JTextField minYField = null, maxYField = null, minXField = null, maxXField = null;
093    private JLabel minYLabelExpl = null, maxYLabelExpl = null, minXLabelExpl = null, maxXLabelExpl = null; 
094    private JLabel minYLabelAuto = null, maxYLabelAuto = null, minXLabelAuto = null, maxXLabelAuto = null;
095    private JRadioButton minYValButton = null, minYAutoButton = null, maxYValButton = null, maxYAutoButton = null;
096    private JRadioButton minXValButton = null, minXAutoButton = null, maxXValButton = null, maxXAutoButton = null;
097    
098    private JTextField vGridSizeField = null, hGridSizeField = null;
099    private JRadioButton vGridNoneButton = null, vGridDSNumAlignedButton = null, vGridXAUnitAlignedButton = null;
100    private JRadioButton hGridNoneButton = null, hGridSimpleButton = null;
101    private JButton vGridColourButton = null;
102    private JButton hGridColourButton = null;
103    private DisEnablingPanel hGridDetsPanel = null, vGridDetsPanel = null;
104    
105    private JTextField xScaleValueField = null;
106    private ResizablePopupComboBox seriesCombo = null;
107    private int preferredXAxisSeriesIndex = 0;
108    private DisEnablingPanel xAxisSeriesPanel = null;
109    
110    private JRadioButton xAxisDSNumButton = null, xAxisDataValSimpleButton = null,
111                                             xAxisDataValTrans01Button = null, xAxisDataValScaledSetButton = null;
112    
113    
114    /**
115     * This is the default constructor.
116     */
117    public GraphSettingsWindow() {
118            super();
119            initialize();
120    }
121    
122    
123    /**
124     * This method initializes the window.
125     */
126    private void initialize() {
127            
128            // Window settings:
129            
130            final GraphSettingsWindow GRAPHSETTINGS_WIN = this;
131            this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
132            Dimension frameDim = new Dimension(470, 400);
133            this.setPreferredSize(frameDim);
134            this.setBounds(5, 310, frameDim.width, frameDim.height);        
135            this.setTitle("Graph settings (LiveGraph)");
136            getContentPane().setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
137            //getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
138            getContentPane().setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
139            
140            // Hide-show listener:
141            
142            this.addWindowListener(new WindowAdapter() {
143                    @Override public void windowClosing(WindowEvent e) {
144                            LiveGraph.application().setDisplayGraphSettingsWindow(false);
145                    }
146            });
147            
148            // Layout:
149            
150            GraphSettings settings = LiveGraph.application().getGraphSettings();
151            if (null == settings)
152                    settings = new GraphSettings();
153            
154            //AbstractButton button = null;
155            JLabel label = null;
156            ButtonGroup bGroup = null;
157            Dimension dim = null;
158            RealNumFieldValueChangeAdaptor fvalAdpt = null;
159            double defVal = Double.NaN;
160            
161            // Viewport settings:
162            JPanel viewportPanel = new JPanel(new GridBagLayout());
163            getContentPane().add(viewportPanel);
164            viewportPanel.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));
165            viewportPanel.add(new JLabel("Viewport:"), Tools.createGridBagConstraints(0, 0, 5, 1));
166            /*
167            viewportPanel.add(new Box.Filler((dim = new Dimension(1, 1)), dim, dim),
168                                              new GridBagConstraints(0, 5, 6, 1, 1, 1,
169                                                                                             GridBagConstraints.WEST,
170                                                                                             GridBagConstraints.BOTH,
171                                                                                             new Insets(0, 0, 0, 0),
172                                                                                             0, 0));
173                                                                                             */     
174            
175            viewportPanel.add(new JSeparator(SwingConstants.VERTICAL),
176                                              new GridBagConstraints(5, 1, 1, 2, 0, 1,
177                                                                                             GridBagConstraints.NORTHWEST,
178                                                                                             GridBagConstraints.BOTH,
179                                                                                             new Insets(5, 5, 5, 5),
180                                                                                             0, 0));
181    
182            
183            // Min Y: 
184            viewportPanel.add((minYLabelExpl = new JLabel("Min Y:")), Tools.createGridBagConstraints(0, 1, 1, 1));
185            minYLabelExpl.setFont(SwingTools.getPlainFont(minYLabelExpl));
186            bGroup = new ButtonGroup();
187            
188            minYValButton = new JRadioButton("", !Double.isNaN(settings.getMinY()));
189            viewportPanel.add(minYValButton, Tools.createGridBagConstraints(1, 1, 1, 1));
190            bGroup.add(minYValButton);
191            defVal = Double.isNaN(settings.getMinY()) ? 0. : settings.getMinY();
192            minYField = new JTextField(Double.toString(defVal), 6);
193            viewportPanel.add(minYField, Tools.createGridBagConstraints(2, 1, 1, 1));       
194            minYField.addFocusListener(fvalAdpt = new RealNumFieldValueChangeAdaptor(defVal) {
195                    @Override public double valueChanged(JTextField field, double newValue) {               
196                    if (field.isEnabled())
197                            LiveGraph.application().getGraphSettings().setMinY(newValue);
198                    else 
199                            LiveGraph.application().getGraphSettings().setMinY(Double.NaN);
200                    return newValue;
201            }
202            });
203            minYField.addKeyListener(fvalAdpt);
204    
205            minYLabelAuto = new JLabel("auto");
206            minYLabelAuto.setFont(SwingTools.getPlainFont(minYLabelAuto));
207            viewportPanel.add(minYLabelAuto, Tools.createGridBagConstraints(4, 1, 1, 1));
208            minYValButton.addActionListener(new ActionListener() {
209                    public void actionPerformed(ActionEvent e) {
210                            LiveGraph.application().getGraphSettings().setMinY(Double.parseDouble(minYField.getText()));
211            }
212            });
213            
214            minYAutoButton = new JRadioButton("", Double.isNaN(settings.getMinY()));
215            viewportPanel.add(minYAutoButton, Tools.createGridBagConstraints(3, 1, 1, 1));
216            bGroup.add(minYAutoButton);
217            minYAutoButton.addActionListener(new ActionListener() {
218                    public void actionPerformed(ActionEvent e) {
219                            LiveGraph.application().getGraphSettings().setMinY(Double.NaN);
220            }
221            });
222            minYField.setEnabled(false);
223            
224            // Max Y: 
225            viewportPanel.add((maxYLabelExpl = new JLabel("Max Y:")), Tools.createGridBagConstraints(6, 1, 1, 1));
226            maxYLabelExpl.setFont(SwingTools.getPlainFont(maxYLabelExpl));
227            bGroup = new ButtonGroup();
228            
229            maxYValButton = new JRadioButton("", !Double.isNaN(settings.getMaxY()));
230            viewportPanel.add(maxYValButton, Tools.createGridBagConstraints(7, 1, 1, 1));
231            bGroup.add(maxYValButton);
232            defVal = Double.isNaN(settings.getMaxY()) ? 100. : settings.getMaxY();
233            maxYField = new JTextField(Double.toString(defVal), 6);
234            viewportPanel.add(maxYField, Tools.createGridBagConstraints(8, 1, 1, 1));
235            maxYField.addFocusListener(fvalAdpt = new RealNumFieldValueChangeAdaptor(defVal) {
236                    @Override public double valueChanged(JTextField field, double newValue) {
237                    if (field.isEnabled())
238                            LiveGraph.application().getGraphSettings().setMaxY(newValue);
239                    else 
240                            LiveGraph.application().getGraphSettings().setMaxY(Double.NaN);
241                    return newValue;
242            }
243            });
244            maxYField.addKeyListener(fvalAdpt);
245    
246            maxYLabelAuto = new JLabel("auto");
247            maxYLabelAuto.setFont(SwingTools.getPlainFont(label));
248            viewportPanel.add(maxYLabelAuto, Tools.createGridBagConstraints(10, 1, 1, 1));
249            maxYValButton.addActionListener(new ActionListener() {
250                    public void actionPerformed(ActionEvent e) {
251                            LiveGraph.application().getGraphSettings().setMaxY(Double.parseDouble(maxYField.getText()));
252            }
253            });
254            
255            maxYAutoButton = new JRadioButton("", Double.isNaN(settings.getMaxY()));
256            viewportPanel.add(maxYAutoButton, Tools.createGridBagConstraints(9, 1, 1, 1));
257            bGroup.add(maxYAutoButton);
258            maxYAutoButton.addActionListener(new ActionListener() {
259                    public void actionPerformed(ActionEvent e) {
260                            LiveGraph.application().getGraphSettings().setMaxY(Double.NaN);
261            }
262            });
263            maxYField.setEnabled(false);
264            
265            // Min X: 
266            viewportPanel.add((minXLabelExpl = new JLabel("Min X:")), Tools.createGridBagConstraints(0, 2, 1, 1));
267            minXLabelExpl.setFont(SwingTools.getPlainFont(minXLabelExpl));
268            bGroup = new ButtonGroup();
269            
270            minXValButton = new JRadioButton("", !Double.isNaN(settings.getMinX()));
271            viewportPanel.add(minXValButton, Tools.createGridBagConstraints(1, 2, 1, 1));
272            bGroup.add(minXValButton);
273            defVal = Double.isNaN(settings.getMinX()) ? -1. : settings.getMinX();
274            minXField = new JTextField(Double.toString(defVal), 6);
275            viewportPanel.add(minXField, Tools.createGridBagConstraints(2, 2, 1, 1));
276            minXField.addFocusListener(fvalAdpt = new RealNumFieldValueChangeAdaptor(defVal) {
277                    @Override public double valueChanged(JTextField field, double newValue) {
278                    if (field.isEnabled())
279                            LiveGraph.application().getGraphSettings().setMinX(newValue);
280                    else 
281                            LiveGraph.application().getGraphSettings().setMinX(Double.NaN);
282                    return newValue;
283            }
284            });
285            minXField.addKeyListener(fvalAdpt);
286    
287            minXLabelAuto = new JLabel("auto");
288            minXLabelAuto.setFont(SwingTools.getPlainFont(minXLabelAuto));
289            viewportPanel.add(minXLabelAuto, Tools.createGridBagConstraints(4, 2, 1, 1));
290            minXValButton.addActionListener(new ActionListener() {
291                    public void actionPerformed(ActionEvent e) {
292                            LiveGraph.application().getGraphSettings().setMinX(Double.parseDouble(minXField.getText()));
293            }
294            });
295            
296            minXAutoButton = new JRadioButton("", Double.isNaN(settings.getMinX()));
297            viewportPanel.add(minXAutoButton, Tools.createGridBagConstraints(3, 2, 1, 1));
298            bGroup.add(minXAutoButton);
299            minXAutoButton.addActionListener(new ActionListener() {
300                    public void actionPerformed(ActionEvent e) {
301                            LiveGraph.application().getGraphSettings().setMinX(Double.NaN);
302            }
303            });
304            minXField.setEnabled(false);
305            
306            // Max X: 
307            viewportPanel.add((maxXLabelExpl = new JLabel("Max X:")), Tools.createGridBagConstraints(6, 2, 1, 1));
308            maxXLabelExpl.setFont(SwingTools.getPlainFont(maxXLabelExpl));
309            bGroup = new ButtonGroup();
310            
311            maxXValButton = new JRadioButton("", !Double.isNaN(settings.getMaxX()));
312            viewportPanel.add(maxXValButton, Tools.createGridBagConstraints(7, 2, 1, 1));
313            bGroup.add(maxXValButton);
314            defVal = Double.isNaN(settings.getMaxX()) ? 10000. : settings.getMaxX();
315            maxXField = new JTextField(Double.toString(defVal), 6);
316            viewportPanel.add(maxXField, Tools.createGridBagConstraints(8, 2, 1, 1));
317            maxXField.addFocusListener(fvalAdpt = new RealNumFieldValueChangeAdaptor(defVal) {
318                    @Override public double valueChanged(JTextField field, double newValue) {
319                    if (field.isEnabled())
320                            LiveGraph.application().getGraphSettings().setMaxX(newValue);
321                    else 
322                            LiveGraph.application().getGraphSettings().setMaxX(Double.NaN);
323                    return newValue;
324            }
325            });
326            maxXField.addKeyListener(fvalAdpt);
327    
328            maxXLabelAuto = new JLabel("auto");
329            maxXLabelAuto.setFont(SwingTools.getPlainFont(maxXLabelAuto));
330            viewportPanel.add(maxXLabelAuto, Tools.createGridBagConstraints(10, 2, 1, 1));
331            maxXValButton.addActionListener(new ActionListener() {
332                    public void actionPerformed(ActionEvent e) {
333                            LiveGraph.application().getGraphSettings().setMaxX(Double.parseDouble(maxXField.getText()));
334            }
335            });
336            
337            maxXAutoButton = new JRadioButton("", Double.isNaN(settings.getMaxX()));
338            viewportPanel.add(maxXAutoButton, Tools.createGridBagConstraints(9, 2, 1, 1));
339            bGroup.add(maxXAutoButton);
340            maxXAutoButton.addActionListener(new ActionListener() {
341                    public void actionPerformed(ActionEvent e) {
342                            LiveGraph.application().getGraphSettings().setMaxX(Double.NaN);
343            }
344            });
345            maxXField.setEnabled(false);
346            
347            // Grid settings:
348            JPanel gridPanel = new JPanel(new GridBagLayout());
349            getContentPane().add(gridPanel);
350            gridPanel.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));
351            
352            gridPanel.add(new JSeparator(SwingConstants.VERTICAL),
353                                      new GridBagConstraints(3, 1, 1, 4, 0, 1,
354                                                                                     GridBagConstraints.NORTHWEST,
355                                                                                     GridBagConstraints.BOTH,
356                                                                                     new Insets(5, 10, 5, 10),
357                                                                                     0, 0));
358            
359            // Vertical grid:
360            gridPanel.add(new JLabel("Vertical grid:"), Tools.createGridBagConstraints(0, 0, 3, 1));        
361            bGroup = new ButtonGroup();
362            
363            vGridDetsPanel = new DisEnablingPanel(new GridBagLayout());
364            gridPanel.add(vGridDetsPanel, Tools.createGridBagConstraints(1, 4, 1, 1));
365            
366            vGridNoneButton = new JRadioButton("Do not display a grid",
367                                                                               settings.getVGridType() == VGrid_None);
368            vGridNoneButton.setFont(SwingTools.getPlainFont(vGridNoneButton));
369            gridPanel.add(vGridNoneButton, Tools.createGridBagConstraints(0, 1, 3, 1));
370            bGroup.add(vGridNoneButton);
371            vGridNoneButton.addActionListener(new ActionListener() {
372                    public void actionPerformed(ActionEvent e) {
373                            LiveGraph.application().getGraphSettings().setVGridType(VGrid_None);
374            }
375            });
376            
377            vGridDSNumAlignedButton = new JRadioButton("Grid aligned on dataset indices",
378                                                                                               settings.getVGridType() == VGrid_DSNumAligned);
379            vGridDSNumAlignedButton.setFont(SwingTools.getPlainFont(vGridDSNumAlignedButton));
380            gridPanel.add(vGridDSNumAlignedButton, Tools.createGridBagConstraints(0, 2, 3, 1));
381            bGroup.add(vGridDSNumAlignedButton);
382            vGridDSNumAlignedButton.addActionListener(new ActionListener() {
383                    public void actionPerformed(ActionEvent e) {
384                            LiveGraph.application().getGraphSettings().setVGridType(VGrid_DSNumAligned);
385            }
386            });
387            
388            vGridXAUnitAlignedButton = new JRadioButton("Grid aligned on X-axis units",
389                                                                                                    settings.getVGridType() == VGrid_XAUnitAligned);
390            vGridXAUnitAlignedButton.setFont(SwingTools.getPlainFont(vGridXAUnitAlignedButton));
391            gridPanel.add(vGridXAUnitAlignedButton, Tools.createGridBagConstraints(0, 3, 3, 1));
392            bGroup.add(vGridXAUnitAlignedButton);
393            vGridXAUnitAlignedButton.addActionListener(new ActionListener() {
394                    public void actionPerformed(ActionEvent e) {
395                            LiveGraph.application().getGraphSettings().setVGridType(VGrid_XAUnitAligned);
396            }
397            });
398            
399            dim = new Dimension(20, 10);
400            gridPanel.add(new Box.Filler(dim, dim, dim), Tools.createGridBagConstraints(0, 4, 1, 1));
401            
402            label = new JLabel("Grid size:");
403            label.setFont(SwingTools.getPlainFont(label));
404            vGridDetsPanel.add(label, Tools.createGridBagConstraints(0, 0, 1, 1));
405            
406            vGridSizeField = new JTextField(Double.toString(settings.getVGridSize()), 4);
407            vGridDetsPanel.add(vGridSizeField, Tools.createGridBagConstraints(1, 0, 1, 1));
408            vGridSizeField.addFocusListener(fvalAdpt = new RealNumFieldValueChangeAdaptor(settings.getVGridSize()) {
409                    @Override public double valueChanged(JTextField field, double newValue) {               
410                    LiveGraph.application().getGraphSettings().setVGridSize(newValue);
411                    return LiveGraph.application().getGraphSettings().getVGridSize();
412            }
413            });
414            vGridSizeField.addKeyListener(fvalAdpt);
415            
416            label = new JLabel("Grid colour:");
417            label.setFont(SwingTools.getPlainFont(label));
418            vGridDetsPanel.add(label, Tools.createGridBagConstraints(0, 1, 1, 1));
419            
420            vGridColourButton = new JButton();
421            vGridColourButton.setBackground(settings.getVGridColour());
422            dim = new Dimension(35, 15);
423            vGridColourButton.setSize(dim);
424            vGridColourButton.setPreferredSize(dim);
425            vGridDetsPanel.add(vGridColourButton, Tools.createGridBagConstraints(1, 1, 1, 1));
426            vGridColourButton.addActionListener(new ActionListener() {
427                    public void actionPerformed(ActionEvent e) {
428                            Color nc = JColorChooser.showDialog(GRAPHSETTINGS_WIN, "Choose a colour for the vertical grid:",
429                                                                                                    LiveGraph.application().getGraphSettings().getHGridColour());
430                            if (null == nc)
431                                    return;
432                            LiveGraph.application().getGraphSettings().setVGridColour(nc);
433                    }
434            });
435            vGridDetsPanel.setEnabled(false);
436            
437            // Horizontal grid:
438            gridPanel.add(new JLabel("Horizontal grid:"), Tools.createGridBagConstraints(4, 0, 3, 1));      
439            bGroup = new ButtonGroup();
440            
441            hGridDetsPanel = new DisEnablingPanel(new GridBagLayout());
442            gridPanel.add(hGridDetsPanel, Tools.createGridBagConstraints(5, 4, 1, 1));
443            
444            hGridNoneButton = new JRadioButton("Do not display a grid",
445                                                                               settings.getHGridType() == HGrid_None);
446            hGridNoneButton.setFont(SwingTools.getPlainFont(hGridNoneButton));
447            gridPanel.add(hGridNoneButton, Tools.createGridBagConstraints(4, 1, 3, 1));
448            bGroup.add(hGridNoneButton);
449            hGridNoneButton.addActionListener(new ActionListener() {
450                    public void actionPerformed(ActionEvent e) {
451                            LiveGraph.application().getGraphSettings().setHGridType(HGrid_None);
452            }
453            });
454            
455            hGridSimpleButton = new JRadioButton("Display a horizontal grid",
456                                                                                     settings.getHGridType() == HGrid_Simple);
457            hGridSimpleButton.setFont(SwingTools.getPlainFont(hGridSimpleButton));
458            gridPanel.add(hGridSimpleButton, Tools.createGridBagConstraints(4, 2, 3, 1));
459            bGroup.add(hGridSimpleButton);
460            hGridSimpleButton.addActionListener(new ActionListener() {
461                    public void actionPerformed(ActionEvent e) {
462                            LiveGraph.application().getGraphSettings().setHGridType(HGrid_Simple);
463            }
464            });
465            
466            dim = new Dimension(20, 10);
467            gridPanel.add(new Box.Filler(dim, dim, dim), Tools.createGridBagConstraints(0, 4, 1, 1));
468            
469            label = new JLabel("Grid size:");
470            label.setFont(SwingTools.getPlainFont(label));
471            hGridDetsPanel.add(label, Tools.createGridBagConstraints(0, 0, 1, 1));
472            
473            hGridSizeField = new JTextField(Double.toString(settings.getHGridSize()), 4);
474            hGridDetsPanel.add(hGridSizeField, Tools.createGridBagConstraints(1, 0, 1, 1));
475            hGridSizeField.addFocusListener(fvalAdpt = new RealNumFieldValueChangeAdaptor(settings.getHGridSize()) {
476                    @Override public double valueChanged(JTextField field, double newValue) {               
477                    LiveGraph.application().getGraphSettings().setHGridSize(newValue);
478                    return LiveGraph.application().getGraphSettings().getHGridSize();
479            }
480            });
481            hGridSizeField.addKeyListener(fvalAdpt);
482            
483            label = new JLabel("Grid colour:");
484            label.setFont(SwingTools.getPlainFont(label));
485            hGridDetsPanel.add(label, Tools.createGridBagConstraints(0, 1, 1, 1));
486            
487            hGridColourButton = new JButton();
488            hGridColourButton.setBackground(Color.LIGHT_GRAY);
489            dim = new Dimension(35, 15);
490            hGridColourButton.setSize(dim);
491            hGridColourButton.setPreferredSize(dim);
492            hGridDetsPanel.add(hGridColourButton, Tools.createGridBagConstraints(1, 1, 1, 1));
493            hGridColourButton.addActionListener(new ActionListener() {
494                    public void actionPerformed(ActionEvent e) {
495                            Color nc = JColorChooser.showDialog(GRAPHSETTINGS_WIN, "Choose a colour for the horizontal grid:",
496                                                                                                    LiveGraph.application().getGraphSettings().getHGridColour());
497                            if (null == nc)
498                                    return;
499                            LiveGraph.application().getGraphSettings().setHGridColour(nc);
500                    }
501            });
502            hGridDetsPanel.setEnabled(false);
503            
504            //      X axis settings:
505            JPanel xAxisPanel = new JPanel(new GridBagLayout());
506            xAxisSeriesPanel = new DisEnablingPanel(new GridBagLayout());
507            getContentPane().add(xAxisPanel);
508            xAxisPanel.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));
509            xAxisPanel.add(new JLabel("X axis:"), Tools.createGridBagConstraints(0, 0, 4, 1));      
510            bGroup = new ButtonGroup();
511            /*
512            xAxisPanel.add(new Box.Filler((dim = new Dimension(1, 1)), dim, dim),
513                                       new GridBagConstraints(0, 6, 4, 1, 1, 1,
514                                                                                      GridBagConstraints.WEST,
515                                                                                      GridBagConstraints.BOTH,
516                                                                                      new Insets(0, 0, 0, 0),
517                                                                                      0, 0));
518                                                                                      */
519            
520            xAxisDSNumButton = new JRadioButton("Use dataset number",
521                                                                                    settings.getXAxisType() == XAxis_DSNum);
522            xAxisDSNumButton.setFont(SwingTools.getPlainFont(xAxisDSNumButton));
523            xAxisPanel.add(xAxisDSNumButton, Tools.createGridBagConstraints(0, 1, 2, 1));
524            bGroup.add(xAxisDSNumButton);
525            xAxisDSNumButton.addActionListener(new ActionListener() {
526                    public void actionPerformed(ActionEvent e) {
527                            LiveGraph.application().getGraphSettings().setXAxisType(XAxis_DSNum);
528            }
529            });
530            
531            xAxisDataValSimpleButton = new JRadioButton("Use data series",
532                                                                                                    settings.getXAxisType() == XAxis_DataValSimple);
533            xAxisDataValSimpleButton.setFont(SwingTools.getPlainFont(xAxisDataValSimpleButton));
534            xAxisPanel.add(xAxisDataValSimpleButton, Tools.createGridBagConstraints(0, 2, 2, 1));
535            bGroup.add(xAxisDataValSimpleButton);
536            xAxisDataValSimpleButton.addActionListener(new ActionListener() {
537                    public void actionPerformed(ActionEvent e) {
538                            LiveGraph.application().getGraphSettings().setXAxisType(XAxis_DataValSimple);
539            }
540            });
541            
542            xAxisDataValTrans01Button = new JRadioButton("Use data series transformed into [0..1]",
543                                                                                                     settings.getXAxisType() == XAxis_DataValTrans0to1);
544            xAxisDataValTrans01Button.setFont(SwingTools.getPlainFont(xAxisDataValTrans01Button));
545            xAxisPanel.add(xAxisDataValTrans01Button, Tools.createGridBagConstraints(2, 1, 2, 1));
546            bGroup.add(xAxisDataValTrans01Button);
547            xAxisDataValTrans01Button.addActionListener(new ActionListener() {
548                    public void actionPerformed(ActionEvent e) {
549                            LiveGraph.application().getGraphSettings().setXAxisType(XAxis_DataValTrans0to1);
550            }
551            });
552            
553            xAxisDataValScaledSetButton = new JRadioButton("Use data series scaled by specified value",
554                                                                                                       settings.getXAxisType() == XAxis_DataValScaledSet);
555            xAxisDataValScaledSetButton.setFont(SwingTools.getPlainFont(xAxisDataValScaledSetButton));
556            xAxisPanel.add(xAxisDataValScaledSetButton, Tools.createGridBagConstraints(2, 2, 2, 1));
557            bGroup.add(xAxisDataValScaledSetButton);
558            xAxisDataValScaledSetButton.addActionListener(new ActionListener() {
559                    public void actionPerformed(ActionEvent e) {
560                            LiveGraph.application().getGraphSettings().setXAxisType(XAxis_DataValScaledSet);
561            }
562            });
563            
564            dim = new Dimension(20, 10);
565            xAxisPanel.add(new Box.Filler(dim, dim, dim), Tools.createGridBagConstraints(0, 3, 1, 1));
566            xAxisPanel.add(xAxisSeriesPanel, Tools.createGridBagConstraints(1, 3, 4, 1));
567            
568            xAxisSeriesPanel.add((label = new JLabel("Series:")), Tools.createGridBagConstraints(0, 0, 1, 1));
569            label.setFont(SwingTools.getPlainFont(label));          
570            seriesCombo = new ResizablePopupComboBox(new String[] {});
571            preferredXAxisSeriesIndex = settings.getXAxisSeriesIndex();     
572            xAxisSeriesPanel.add(seriesCombo, Tools.createGridBagConstraints(1, 0, 1, 1));
573            seriesCombo.addActionListener(new ActionListener() {
574                    public void actionPerformed(ActionEvent e) {
575                            if (0 > seriesCombo.getSelectedIndex())
576                                    return;
577                            LiveGraph.application().getGraphSettings().setXAxisSeriesIndex(seriesCombo.getSelectedIndex());
578            }
579            });
580            
581            xAxisSeriesPanel.add((label = new JLabel("Value:")), Tools.createGridBagConstraints(3, 0, 1, 1));
582            label.setFont(SwingTools.getPlainFont(label));  
583            xScaleValueField = new JTextField(Double.toString(settings.getXAxisScaleValue()), 6);
584            xAxisSeriesPanel.add(xScaleValueField, Tools.createGridBagConstraints(4, 0, 1, 1));
585            xScaleValueField.addFocusListener(fvalAdpt = new RealNumFieldValueChangeAdaptor(settings.getXAxisScaleValue()) {
586                    @Override public double valueChanged(JTextField field, double newValue) {               
587                    LiveGraph.application().getGraphSettings().setXAxisScaleValue(newValue);
588                    return LiveGraph.application().getGraphSettings().getXAxisScaleValue();
589            }
590            });
591            xScaleValueField.addKeyListener(fvalAdpt);
592            xAxisSeriesPanel.setEnabled(false);
593            
594            // Set all panes to the same width:
595            viewportPanel.setPreferredSize(new Dimension(frameDim.width - 20, viewportPanel.getPreferredSize().height));
596            gridPanel.setPreferredSize(new Dimension(frameDim.width - 20, gridPanel.getPreferredSize().height));
597            xAxisPanel.setPreferredSize(new Dimension(frameDim.width - 20, xAxisPanel.getPreferredSize().height));
598            
599            seriesCombo.setPreferredSize(new Dimension(frameDim.width - 250, seriesCombo.getPreferredSize().height));
600            
601    } // private void initialize()
602    
603    
604    /**
605     * Updates the display of the graph settings when they have been changed.
606     * @param settings The settings.
607     * @param info Event info.
608     */
609    public void settingHasChanged(ObservableSettings settings, Object info) {
610            if (null == settings)
611                    return;
612            
613            if (settings instanceof GraphSettings) {
614                    settingHasChanged((GraphSettings) settings, info);
615                    return;
616            }
617    }
618    
619    /**
620     * Updates the display of the graph settings when they have been changed.
621     * @param settings The settings.
622     * @param info Event info.
623     */
624    public void settingHasChanged(GraphSettings settings, Object info) {    
625            
626            if (null == info || !(info instanceof String))
627                    return; 
628            
629            String event = (String) info;
630            
631            if (event.equals("MinY") || event.equals("load")) {
632                    if (Double.isNaN(settings.getMinY())) {
633                            minYAutoButton.setSelected(true);
634                            minYValButton.setSelected(false);
635                            minYField.setEnabled(false);
636                    } else {
637                            minYAutoButton.setSelected(false);
638                            minYValButton.setSelected(true);
639                            minYField.setEnabled(true);
640                            minYField.setText(Double.toString(settings.getMinY()));                 
641                    }
642            }
643            
644            if (event.equals("MaxY") || event.equals("load")) {
645                    if (Double.isNaN(settings.getMaxY())) {
646                            maxYAutoButton.setSelected(true);
647                            maxYValButton.setSelected(false);
648                            maxYField.setEnabled(false);
649                    } else {
650                            maxYAutoButton.setSelected(false);
651                            maxYValButton.setSelected(true);
652                            maxYField.setEnabled(true);
653                            maxYField.setText(Double.toString(settings.getMaxY()));
654                    }
655            }
656            
657            if (event.equals("MinX") || event.equals("load")) {
658                    if (Double.isNaN(settings.getMinX())) {
659                            minXAutoButton.setSelected(true);
660                            minXValButton.setSelected(false);
661                            minXField.setEnabled(false);
662                    } else {        
663                            minXAutoButton.setSelected(false);
664                            minXValButton.setSelected(true);
665                            minXField.setEnabled(true);
666                            minXField.setText(Double.toString(settings.getMinX()));
667                    }
668            }
669            
670            if (event.equals("MaxX") || event.equals("load")) {
671                    if (Double.isNaN(settings.getMaxX())) {
672                            maxXAutoButton.setSelected(true);
673                            maxXValButton.setSelected(false);
674                            maxXField.setEnabled(false);
675                    } else {
676                            maxXAutoButton.setSelected(false);
677                            maxXValButton.setSelected(true);
678                            maxXField.setEnabled(true);
679                            maxXField.setText(Double.toString(settings.getMaxX()));         
680                    }
681            }
682            
683            if (event.equals("VGridType") || event.equals("load")) {
684                    switch (settings.getVGridType()) {
685                            case VGrid_None:                        vGridDetsPanel.setEnabled(false);
686                                                                                    vGridNoneButton.setSelected(true);
687                                                                                    break;
688                            case VGrid_DSNumAligned:        vGridDetsPanel.setEnabled(true);
689                                                                                    vGridDSNumAlignedButton.setSelected(true);
690                                                                                    break;
691                            case VGrid_XAUnitAligned:       vGridDetsPanel.setEnabled(true);
692                                                                                    vGridXAUnitAlignedButton.setSelected(true);
693                                                                                    break;
694                            default: throw new Error("Greg forgot a VGrid option!");
695                    }
696            }
697            
698            if (event.equals("VGridSize") || event.equals("load")) {
699                    vGridSizeField.setText(Double.toString(settings.getVGridSize()));
700            }
701            
702            if (event.equals("VGridColour") || event.equals("load")) {
703                    vGridColourButton.setBackground(settings.getVGridColour());
704            }
705            
706            if (event.equals("HGridType") || event.equals("load")) {
707                    switch (settings.getHGridType()) {
708                            case HGrid_None:        hGridDetsPanel.setEnabled(false);
709                                                                    hGridNoneButton.setSelected(true);
710                                                                    break;
711                            case HGrid_Simple:      hGridDetsPanel.setEnabled(true);
712                                                                    hGridSimpleButton.setSelected(true);
713                                                                    break;
714                            default: throw new Error("Greg forgot a HGrid option!");
715                    }
716            }
717            
718            if (event.equals("HGridSize") || event.equals("load")) {
719                    hGridSizeField.setText(Double.toString(settings.getHGridSize()));
720            }
721            
722            if (event.equals("HGridColour") || event.equals("load")) {
723                    hGridColourButton.setBackground(settings.getHGridColour());
724            }
725            
726            if (event.equals("XAxisType") || event.equals("load")) {
727                    switch (settings.getXAxisType()) {
728                            case XAxis_DSNum:                               xAxisSeriesPanel.setEnabled(false);
729                                                                                            xAxisDSNumButton.setSelected(true);
730                                                                                            break;
731                            case XAxis_DataValSimple:               xAxisSeriesPanel.setEnabled(true);
732                                                                                            xAxisDataValSimpleButton.setSelected(true);
733                                                                                            break;
734                            case XAxis_DataValTrans0to1:    xAxisSeriesPanel.setEnabled(true);
735                                                                                            xAxisDataValTrans01Button.setSelected(true);
736                                                                                            break;
737                            case XAxis_DataValScaledSet:    xAxisSeriesPanel.setEnabled(true);
738                                                                                            xAxisDataValScaledSetButton.setSelected(true);
739                                                                                            break;
740                    }
741            }
742            
743            if (event.equals("XAxisSeriesIndex") || event.equals("load")) {
744                    preferredXAxisSeriesIndex = settings.getXAxisSeriesIndex();
745                    if (preferredXAxisSeriesIndex < 0)
746                            preferredXAxisSeriesIndex = 0;
747                    if (preferredXAxisSeriesIndex < seriesCombo.getItemCount())
748                            seriesCombo.setSelectedIndex(preferredXAxisSeriesIndex);
749                    else
750                            seriesCombo.setSelectedIndex(seriesCombo.getItemCount() - 1);
751            }
752            
753            if (event.equals("XAxisScaleValue") || event.equals("load")) {
754                    xScaleValueField.setText(Double.toString(settings.getXAxisScaleValue()));
755            }
756    }
757    
758    /**
759     * Updates the series selection drop down box with the specified series labels.
760     * @param labels Series labels.
761     */
762    private void setSeriesLabels(ReadOnlyIterator<String> labels) {
763            seriesCombo.removeAllItems();
764            while (labels.hasNext()) {
765                    seriesCombo.addItem(labels.next());
766            }
767            
768            if (preferredXAxisSeriesIndex < seriesCombo.getItemCount())
769                    seriesCombo.setSelectedIndex(preferredXAxisSeriesIndex);
770            else
771                    seriesCombo.setSelectedIndex(seriesCombo.getItemCount() - 1);
772    }
773    
774    /**
775     * Updates the series labels when the cache is updated.
776     */
777    public void cacheEventFired(DataCache cache, CacheEvent event) {
778            
779            switch(event) {
780                    case UpdateLabels:
781                            setSeriesLabels(cache.iterateDataSeriesLabels());
782                            break;
783                    case ChangeMode:
784                    case UpdateData:
785                    case UpdateDataFileInfo:                
786                            break;
787                    default:
788                            throw new Error("This case is impossible!");
789                            
790            }
791    }
792    
793    
794    }