001    package org.LiveGraph.settings;
002    
003    import java.awt.Color;
004    import java.io.FileInputStream;
005    import java.io.FileOutputStream;
006    import java.io.IOException;
007    import java.util.Properties;
008    
009    import org.LiveGraph.LiveGraph;
010    
011    /**
012     * Encapsulates settings concerned with the data graphs and the plot canvas.
013     * 
014     * <p style="font-size:smaller;">This product includes software developed by the
015     *    <strong>LiveGraph</strong> project and its contributors.<br />
016     *    (<a href="http://www.live-graph.org" target="_blank">http://www.live-graph.org</a>)<br />
017     *    Copyright (c) 2007 G. Paperin.<br />
018     *    All rights reserved.
019     * </p>
020     * <p style="font-size:smaller;">File: GraphSettings.java</p> 
021     * <p style="font-size:smaller;">Redistribution and use in source and binary forms, with or
022     *    without modification, are permitted provided that the following terms and conditions are met:
023     * </p>
024     * <p style="font-size:smaller;">1. Redistributions of source code must retain the above
025     *    acknowledgement of the LiveGraph project and its web-site, the above copyright notice,
026     *    this list of conditions and the following disclaimer.<br />
027     *    2. Redistributions in binary form must reproduce the above acknowledgement of the
028     *    LiveGraph project and its web-site, the above copyright notice, this list of conditions
029     *    and the following disclaimer in the documentation and/or other materials provided with
030     *    the distribution.<br />
031     *    3. All advertising materials mentioning features or use of this software or any derived
032     *    software must display the following acknowledgement:<br />
033     *    <em>This product includes software developed by the LiveGraph project and its
034     *    contributors.<br />(http://www.live-graph.org)</em><br />
035     *    4. All advertising materials distributed in form of HTML pages or any other technology
036     *    permitting active hyper-links that mention features or use of this software or any
037     *    derived software must display the acknowledgment specified in condition 3 of this
038     *    agreement, and in addition, include a visible and working hyper-link to the LiveGraph
039     *    homepage (http://www.live-graph.org).
040     * </p>
041     * <p style="font-size:smaller;">THIS SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY
042     *    OF ANY KIND, EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
043     *    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND  NONINFRINGEMENT. IN NO EVENT SHALL
044     *    THE AUTHORS, CONTRIBUTORS OR COPYRIGHT  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
045     *    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING  FROM, OUT OF OR
046     *    IN CONNECTION WITH THE SOFTWARE OR THE USE OR  OTHER DEALINGS IN THE SOFTWARE.
047     * </p>
048     * 
049     * @author Greg Paperin (<a href="http://www.paperin.org" target="_blank">http://www.paperin.org</a>)
050     * @version {@value org.LiveGraph.LiveGraph#version}
051     */
052    public class GraphSettings extends ObservableSettings {
053    
054    /**
055     * Standard file extension.
056     */
057    public static final String preferredFileExtension = ".lggs";
058    
059    /**
060     * Possible types for the vertical grid.
061     */
062    public static enum VGridType { VGrid_None, VGrid_DSNumAligned, VGrid_XAUnitAligned };
063    
064    /**
065     * Possible types for the horizontal grid.
066     */
067    public static enum HGridType { HGrid_None, HGrid_Simple };
068    
069    /**
070     * Possible types for the x-axis.
071     */
072    public static enum XAxisType { XAxis_DSNum, XAxis_DataValSimple, XAxis_DataValTrans0to1, XAxis_DataValScaledSet };
073    
074    /**
075     * Default settings.
076     */
077    private static final Properties defaultValues = new Properties();
078    static {
079            defaultValues.setProperty("MinY", "Auto");
080            defaultValues.setProperty("MaxY", "Auto");
081            defaultValues.setProperty("MinX", "Auto");
082            defaultValues.setProperty("MaxX", "Auto");
083            defaultValues.setProperty("VGridType", "VGrid_None");
084            defaultValues.setProperty("VGridSize", "50");
085            defaultValues.setProperty("VGridColour", "C0C0C0");
086            defaultValues.setProperty("HGridType", "HGrid_None");
087            defaultValues.setProperty("HGridSize", "50");
088            defaultValues.setProperty("HGridColour", "C0C0C0");
089            defaultValues.setProperty("XAxisType", "XAxis_DSNum");
090            defaultValues.setProperty("XAxisSeriesIndex", "0");
091            defaultValues.setProperty("XAxisScaleValue", "100");
092            defaultValues.setProperty("HighlightDataPoints", "1");
093    }
094    
095    /**
096     * Holds the current graph settings.
097     */
098    private Properties values = null;
099    
100    /**
101     * Creates a new graph settings object with the default settings.
102     */
103    public GraphSettings() {
104            super();
105            values = new Properties();
106            values.putAll(defaultValues);
107            checkDisableHighlightingForOldJava();
108    }
109    
110    /**
111     * Creates a new graph settings object and loads the settings from the specified file.
112     * 
113     * @param fileName File to load the settigs from.
114     */
115    public GraphSettings(String fileName) {
116            this();
117            load(fileName);
118    }
119    
120    /**
121     * Loads the settings from the specified file.
122     * 
123     * @param fileName File to load the settigs from.
124     * @return {@code true} if the settings were loaded, {@code false} if an exception occured.
125     */
126    public boolean load(String fileName) {
127            try {
128                    FileInputStream in = new FileInputStream(fileName);
129                    try {
130                            values.loadFromXML(in);
131                            
132                            if (values.containsKey("SavedWithIncompatibleJavaVersion")
133                                            && "1".equals(values.getProperty("SavedWithIncompatibleJavaVersion"))
134                                            && !getHighlightDataPoints()
135                                            && LiveGraph.application().runsCorrectJavaVersion()) {
136                                    values.setProperty("HighlightDataPoints", "1");
137                            }
138                                                    
139                            checkDisableHighlightingForOldJava();
140                            notifyObservers("load");
141                    } finally { in.close(); }               
142                    return true;
143            } catch(IOException e) {
144                    e.printStackTrace();
145                    return false;
146            }
147    }
148    
149    /**
150     * Saves the settings to a specified file.
151     * 
152     * @param fileName The file to save the settings to.
153     * @return {@code true} if the settings were saved, {@code false} if an exception occured. 
154     */
155    public boolean save(String fileName) {
156            
157            if(LiveGraph.application().runsCorrectJavaVersion())
158                    values.remove("SavedWithIncompatibleJavaVersion");
159            else
160                    values.setProperty("SavedWithIncompatibleJavaVersion", "1");
161            
162            try {
163                    FileOutputStream out = new FileOutputStream(fileName);
164                    try { values.storeToXML(out, "LiveGraph version " + LiveGraph.version + ". GraphSettings."); }
165                    finally { out.close(); }
166                    return true;
167            } catch(IOException e) {
168                    e.printStackTrace();
169                    return false;
170            }
171    }
172    
173    /**
174     * Gets the minimum Y value for the plot viewport.
175     * 
176     * @return The minimum value along the Y axis for the graph view
177     * or {@code java.lang.Double.NaN} if the current global minimum of all data series
178     * should be used instead.  
179     */
180    public double getMinY() {
181            if ("Auto".equalsIgnoreCase(values.getProperty("MinY")))
182                    return Double.NaN;
183            else
184                    return Double.parseDouble(values.getProperty("MinY"));
185    }
186    
187    /**
188     * Sets the minimum Y value for the plot viewport.
189     * 
190     * @param v The minimum value along the Y axis for the graph view
191     * or {@code java.lang.Double.NaN} if the global minimum of all data series
192     * should be used at all times.  
193     */
194    public void setMinY(double v) {
195            if (Double.isNaN(v))
196                    values.setProperty("MinY", "Auto");
197            else
198                    values.setProperty("MinY", Double.toString(v)); 
199            notifyObservers("MinY");
200    }
201    
202    /**
203     * Gets the maximum Y value for the plot viewport.
204     * 
205     * @return The maximum value along the Y axis for the graph view
206     * or {@code java.lang.Double.NaN} if the current global maximum of all data series
207     * should be used instead.  
208     */
209    public double getMaxY() {
210            if ("Auto".equalsIgnoreCase(values.getProperty("MaxY")))
211                    return Double.NaN;
212            else
213                    return Double.parseDouble(values.getProperty("MaxY"));
214    }
215    
216    /**
217     * Sets the maximum Y value for the plot viewport.
218     * 
219     * @param v The maximum value along the Y axis for the graph view
220     * or {@code java.lang.Double.NaN} if the global maximum of all data series
221     * should be used at all times.  
222     */
223    public void setMaxY(double v) {
224            if (Double.isNaN(v))
225                    values.setProperty("MaxY", "Auto");
226            else
227                    values.setProperty("MaxY", Double.toString(v));
228            notifyObservers("MaxY");
229    }
230    
231    /**
232     * Gets the minimum X value for the plot viewport.
233     * 
234     * @return The minimum value along the X axis for the graph view
235     * or {@code java.lang.Double.NaN} if the currently smallest X value of all data series
236     * should be used instead.  
237     */
238    public double getMinX() {
239            if ("Auto".equalsIgnoreCase(values.getProperty("MinX")))
240                    return Double.NaN;
241            else
242                    return Double.parseDouble(values.getProperty("MinX"));
243    }
244    
245    /**
246     * Sets the minimum X value for the plot viewport.
247     * 
248     * @param v The minimum value along the X axis for the graph view
249     * or {@code java.lang.Double.NaN} if the smallest X value of all data series
250     * should be used at all times.  
251     */
252    public void setMinX(double v) {
253            if (Double.isNaN(v))
254                    values.setProperty("MinX", "Auto");
255            else
256                    values.setProperty("MinX", Double.toString(v));
257            notifyObservers("MinX");
258    }
259    
260    /**
261     * Gets the maximum X value for the plot viewport.
262     * 
263     * @return The maximum value along the X axis for the graph view
264     * or {@code java.lang.Double.NaN} if the currently largest X value of all data series
265     * should be used instead.  
266     */
267    public double getMaxX() {
268            if ("Auto".equalsIgnoreCase(values.getProperty("MaxX")))
269                    return Double.NaN;
270            else
271                    return Double.parseDouble(values.getProperty("MaxX"));
272    }
273    
274    /**
275     * Sets the maximum X value for the plot viewport.
276     * 
277     * @param v The maximum value along the X axis for the graph view
278     * or {@code java.lang.Double.NaN} if the largest X value of all data series
279     * should be used at all times.  
280     */
281    public void setMaxX(double v) {
282            if (Double.isNaN(v))
283                    values.setProperty("MaxX", "Auto");
284            else
285                    values.setProperty("MaxX", Double.toString(v));
286            notifyObservers("MaxX");
287    }
288    
289    /**
290     * Sets the vertical grid type.
291     * 
292     * @return The vertical grid type.
293     */
294    public VGridType getVGridType() {
295            if ("VGrid_DSNumAligned".equalsIgnoreCase(values.getProperty("VGridType")))
296                    return VGridType.VGrid_DSNumAligned;
297            if ("VGrid_XAUnitAligned".equalsIgnoreCase(values.getProperty("VGridType")))
298                    return VGridType.VGrid_XAUnitAligned;
299            return VGridType.VGrid_None;
300    }
301    
302    /**
303     * Gets the vertical grid type.
304     * 
305     * @param v The vertical grid type.
306     */
307    public void setVGridType(VGridType v) {
308            switch(v) {
309                    case VGrid_DSNumAligned:        values.setProperty("VGridType", "VGrid_DSNumAligned"); break;
310                    case VGrid_XAUnitAligned:       values.setProperty("VGridType", "VGrid_XAUnitAligned"); break;
311                    default:                                        values.setProperty("VGridType", "VGrid_None"); break;
312            }
313            notifyObservers("VGridType");
314    }
315    
316    /**
317     * Gets the interval between the vertical grid bars.
318     * 
319     * @return The interval between the vertical grid bars.
320     */
321    public double getVGridSize() {
322            return Double.parseDouble(values.getProperty("VGridSize"));
323    }
324    
325    /**
326     * Sets the interval between the vertical grid bars.
327     * 
328     * @param val The interval between the vertical grid bars.
329     */
330    public void setVGridSize(double val) {
331            if (Double.isNaN(val) || Double.isInfinite(val) || 0 > val)
332                    val = 0;        
333            values.setProperty("VGridSize", Double.toString(val));
334            notifyObservers("VGridSize");
335    }
336    
337    /**
338     * Gets the colour to use for drawing the vertical grid bars.
339     * 
340     * @return The colour to use for drawing the vertical grid bars.
341     */
342    public Color getVGridColour() {
343            String c = values.getProperty("VGridColour");
344            int r, g, b;
345            try { r = Integer.parseInt(c.substring(0, 2), 16); } catch(NumberFormatException e) { r = 0; }
346            try { g = Integer.parseInt(c.substring(2, 4), 16); } catch(NumberFormatException e) { g = 0; }
347            try { b = Integer.parseInt(c.substring(4, 6), 16); } catch(NumberFormatException e) { b = 0; }
348            return new Color(r, g, b);
349    }
350    
351    /**
352     * Sets the colour to use for drawing the vertical grid bars.
353     * @param c The colour to use for drawing the vertical grid bars.
354     */
355    public void setVGridColour(Color c) {   
356            if (null == c)
357                    values.setProperty("VGridColour", "000000");
358            else
359                    values.setProperty("VGridColour", String.format("%02x%02x%02x", c.getRed(), c.getGreen(), c.getBlue()));
360            notifyObservers("VGridColour");
361    }
362    
363    /**
364     * Gets the horizontal grid type.
365     * @return The horizontal grid type.
366     */
367    public HGridType getHGridType() {       
368            if ("HGrid_Simple".equalsIgnoreCase(values.getProperty("HGridType")))
369                    return HGridType.HGrid_Simple;
370            return HGridType.HGrid_None;
371    }
372    
373    /**
374     * Sets the horizontal grid type.
375     * @param v The horizontal grid type.
376     */
377    public void setHGridType(HGridType v) {
378            switch(v) {
379                    case HGrid_Simple:      values.setProperty("HGridType", "HGrid_Simple"); break;
380                    default:                        values.setProperty("HGridType", "HGrid_None"); break;
381            }
382            notifyObservers("HGridType");
383    }
384    
385    /**
386     * Gets the interval between the horizontal grib bars.
387     * 
388     * @return The interval between the horizontal grib bars.
389     */
390    public double getHGridSize() {
391            return Double.parseDouble(values.getProperty("HGridSize"));
392    }
393    
394    /**
395     * Sets the interval between the horizontal grib bars.
396     * @param val The interval between the horizontal grib bars.
397     */
398    public void setHGridSize(double val) {  
399            if (Double.isNaN(val) || Double.isInfinite(val) || 0 > val)
400                    val = 0;        
401            values.setProperty("HGridSize", Double.toString(val));
402            notifyObservers("HGridSize");   
403    }
404    
405    
406    /**
407     * Gets the colour for drawing the horizontal grid bars.
408     * 
409     * @return The colour for drawing the horizontal grid bars.
410     */
411    public Color getHGridColour() {
412            String c = values.getProperty("HGridColour");
413            int r, g, b;
414            try { r = Integer.parseInt(c.substring(0, 2), 16); } catch(NumberFormatException e) { r = 0; }
415            try { g = Integer.parseInt(c.substring(2, 4), 16); } catch(NumberFormatException e) { g = 0; }
416            try { b = Integer.parseInt(c.substring(4, 6), 16); } catch(NumberFormatException e) { b = 0; }
417            return new Color(r, g, b);
418    }
419    
420    /**
421     * Sets the colour for drawing the horizontal grid bars.
422     * 
423     * @param c The colour for drawing the horizontal grid bars.
424     */
425    public void setHGridColour(Color c) {   
426            if (null == c)
427                    values.setProperty("HGridColour", "000000");
428            else
429                    values.setProperty("HGridColour", String.format("%02x%02x%02x", c.getRed(), c.getGreen(), c.getBlue()));
430            notifyObservers("HGridColour");
431    }
432    
433    /**
434     * Gets the type for the x-axis.
435     * 
436     * @return The type for the x-axis.
437     */
438    public XAxisType getXAxisType() {       
439            if ("XAxis_DataValSimple".equalsIgnoreCase(values.getProperty("XAxisType")))
440                    return XAxisType.XAxis_DataValSimple;
441            if ("XAxis_DataValTrans0to1".equalsIgnoreCase(values.getProperty("XAxisType")))
442                    return XAxisType.XAxis_DataValTrans0to1;
443            if ("XAxis_DataValScaledSet".equalsIgnoreCase(values.getProperty("XAxisType")))
444                    return XAxisType.XAxis_DataValScaledSet;
445            return XAxisType.XAxis_DSNum;
446    
447    }
448    
449    /**
450     * Sets the type for the x-axis.
451     * @param v The type for the x-axis.
452     */
453    public void setXAxisType(XAxisType v) {
454            switch(v) {
455                    case XAxis_DataValSimple:        values.setProperty("XAxisType", "XAxis_DataValSimple"); break;
456                    case XAxis_DataValTrans0to1: values.setProperty("XAxisType", "XAxis_DataValTrans0to1"); break;
457                    case XAxis_DataValScaledSet: values.setProperty("XAxisType", "XAxis_DataValScaledSet"); break;
458                    default:                                         values.setProperty("XAxisType", "XAxis_DSNum"); break;
459            }
460            notifyObservers("XAxisType");
461    }
462    
463    /**
464     * Gets the index of the data series to use as the x-axis.
465     * 
466     * @return The index of the data series to use as the x-axis.
467     */
468    public int getXAxisSeriesIndex() {
469            return Integer.parseInt(values.getProperty("XAxisSeriesIndex"));
470    }
471    
472    /**
473     * Sets the index of the data series to use as the x-axis.
474     * 
475     * @param val The index of the data series to use as the x-axis.
476     */
477    public void setXAxisSeriesIndex(int val) {      
478            if (0 > val)
479                    val = 0;        
480            values.setProperty("XAxisSeriesIndex", Integer.toString(val));
481            notifyObservers("XAxisSeriesIndex");
482    }
483    
484    /**
485     * Gets the scale parameter to use with the type {@code XAxis_DataValScaledSet} for
486     * scaling of X values.
487     * 
488     * @return The scale parameter to use with the type {@code XAxis_DataValScaledSet} for
489     * scaling of X values.
490     */
491    public double getXAxisScaleValue() {
492            double val = Double.parseDouble(values.getProperty("XAxisScaleValue"));
493            if (Double.isNaN(val) || Double.isInfinite(val))
494                    return 1.;
495            return val;
496    }
497    
498    /**
499     * Sets the scale parameter to use with the type {@code XAxis_DataValScaledSet} for
500     * scaling of X values.
501     * 
502     * @param val scale parameter to use with the type {@code XAxis_DataValScaledSet} for
503     * scaling of X values.
504     */
505    public void setXAxisScaleValue(double val) {
506            if (Double.isNaN(val) || Double.isInfinite(val))
507                    val = 1.;       
508            values.setProperty("XAxisScaleValue", Double.toString(val));
509            notifyObservers("XAxisScaleValue");
510    }
511    
512    /**
513     * Gets whether the data points in the vicinity of the mouse cursor should be highlighted.
514     * (Note, this is a hidden setting and cannot be changes via the GUI.)
515     * 
516     * @return {@code true} if the data points in the vicinity of the mouse cursor should be highlighted,
517     * {@code false} otherwise.
518     */
519    public boolean getHighlightDataPoints() { return "1".equals(values.getProperty("HighlightDataPoints")); }
520    
521    /**
522     * Gets whether the data points in the vicinity of the mouse cursor should be highlighted.
523     * (Note, this is a hidden setting and cannot be changes via the GUI.)
524     * 
525     * @param v {@code true} if the data points in the vicinity of the mouse cursor are to be highlighted,
526     * {@code false} otherwise.
527     */
528    public void setHighlightDataPoints(boolean v) {
529            values.setProperty("HighlightDataPoints", v ? "1" : "0");
530            checkDisableHighlightingForOldJava();
531            notifyObservers("HighlightDataPoints");
532    }
533    
534    /**
535     * If the Java version is too old, data point highlighting is disabled.
536     */
537    private void checkDisableHighlightingForOldJava() {
538            if (!LiveGraph.application().runsCorrectJavaVersion())
539                    values.setProperty("HighlightDataPoints", "0");
540    }
541    
542    } // public class GraphSettings