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.ArrayList; 008 import java.util.List; 009 import java.util.Properties; 010 011 import org.LiveGraph.LiveGraph; 012 013 014 /** 015 * Ecapsulates the settings concerned with plotting each of the data series. 016 * 017 * <p style="font-size:smaller;">This product includes software developed by the 018 * <strong>LiveGraph</strong> project and its contributors.<br /> 019 * (<a href="http://www.live-graph.org" target="_blank">http://www.live-graph.org</a>)<br /> 020 * Copyright (c) 2007 G. Paperin.<br /> 021 * All rights reserved. 022 * </p> 023 * <p style="font-size:smaller;">File: DataSeriesSettings.java</p> 024 * <p style="font-size:smaller;">Redistribution and use in source and binary forms, with or 025 * without modification, are permitted provided that the following terms and conditions are met: 026 * </p> 027 * <p style="font-size:smaller;">1. Redistributions of source code must retain the above 028 * acknowledgement of the LiveGraph project and its web-site, the above copyright notice, 029 * this list of conditions and the following disclaimer.<br /> 030 * 2. Redistributions in binary form must reproduce the above acknowledgement of the 031 * LiveGraph project and its web-site, the above copyright notice, this list of conditions 032 * and the following disclaimer in the documentation and/or other materials provided with 033 * the distribution.<br /> 034 * 3. All advertising materials mentioning features or use of this software or any derived 035 * software must display the following acknowledgement:<br /> 036 * <em>This product includes software developed by the LiveGraph project and its 037 * contributors.<br />(http://www.live-graph.org)</em><br /> 038 * 4. All advertising materials distributed in form of HTML pages or any other technology 039 * permitting active hyper-links that mention features or use of this software or any 040 * derived software must display the acknowledgment specified in condition 3 of this 041 * agreement, and in addition, include a visible and working hyper-link to the LiveGraph 042 * homepage (http://www.live-graph.org). 043 * </p> 044 * <p style="font-size:smaller;">THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY 045 * OF ANY KIND, EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 046 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 047 * THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 048 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 049 * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 050 * </p> 051 * 052 * @author Greg Paperin (<a href="http://www.paperin.org" target="_blank">http://www.paperin.org</a>) 053 * @version {@value org.LiveGraph.LiveGraph#version} 054 */ 055 public class DataSeriesSettings extends ObservableSettings implements SettingsObserver { 056 057 /** 058 * The default file extension. 059 */ 060 public static final String preferredFileExtension = ".lgdss"; 061 062 /** 063 * The transformation mode to the series values. 064 */ 065 public static enum TransformMode { Transform_None, Transform_In0to1, Transform_SetVal }; 066 067 /** 068 * Holds the current settigs. 069 */ 070 private List<SeriesParameters> settings = null; 071 072 /** 073 * Holds the default colours. 074 */ 075 private List<Color> defaultColours = null; 076 077 078 /** 079 * Creates a new data series settings object and initialises it with default values. 080 */ 081 public DataSeriesSettings() { 082 createDefaultColours(); 083 settings = new ArrayList<SeriesParameters>(); 084 } 085 086 /** 087 * Creates a new data series settings object and loads the settigs from the specified file. 088 * 089 * @param fileName The file name to use. 090 */ 091 public DataSeriesSettings(String fileName) { 092 this(); 093 load(fileName); 094 } 095 096 /** 097 * Creates a set of "nice" default colours for the plot. 098 */ 099 private void createDefaultColours() { 100 final int DEF_COLOURS_COUNT = 14; 101 102 defaultColours = new ArrayList<Color>(DEF_COLOURS_COUNT); 103 104 for (int i = 0; i < DEF_COLOURS_COUNT; i++) { 105 106 float h = (2.f / (float) DEF_COLOURS_COUNT) * ((float) i); 107 float s = (0 == (2 * i / DEF_COLOURS_COUNT) % 2 ? 1.f : .5f); 108 float b = (0 == i % 2 ? .7f : 1.f); 109 110 Color col = Color.getHSBColor(h, s, b); 111 defaultColours.add(col); 112 } 113 } 114 115 // Default values for the series is none other spacified: 116 117 private boolean getDefaultShow(int serInd) { return true; } 118 private Color getDefaultColour(int serInd) { return defaultColours.get(serInd % defaultColours.size()); } 119 private TransformMode getDefaultTransformMode(int serInd) { return TransformMode.Transform_None; } 120 private double getDefaultScaleFactor(int serInd) { return 100.; } 121 122 123 /** 124 * Ensures that this settings container contains at least the settings for the data 125 * series with the specified index and all indices before that. If this settings 126 * object does not yet contain any settings for any of the series with these indices, 127 * new settings data structures will be created and initialised with default values. 128 * 129 * @param maxSeriesIndex It will be ensured that this container contains settings for 130 * at least all data series up to this index. 131 */ 132 private void ensureLength(int maxSeriesIndex) { 133 while (settings.size() < maxSeriesIndex + 1) { 134 int newSerInd = settings.size(); 135 SeriesParameters params = new SeriesParameters(getDefaultShow(newSerInd), 136 getDefaultColour(newSerInd), 137 getDefaultTransformMode(newSerInd), 138 getDefaultScaleFactor(newSerInd)); 139 settings.add(params); 140 notifyObservers("NewDefault." + newSerInd); 141 } 142 } 143 144 /** 145 * Loads the settings from a specified file. 146 * 147 * @param fileName The file to load the settings from. 148 * @return {@code true} if the settings were loaded, {@code false} if an exception occured. 149 */ 150 public boolean load(String fileName) { 151 Properties props = new Properties(); 152 try { 153 FileInputStream in = new FileInputStream(fileName); 154 try { props.loadFromXML(in); } 155 finally { in.close(); } 156 } catch(IOException e) { 157 e.printStackTrace(); 158 return false; 159 } 160 161 int describedSeriesCount = 0; 162 try { describedSeriesCount = Integer.parseInt(props.getProperty("DescribedSeriesCount")); } 163 catch (NumberFormatException e) { return false; } 164 165 settings.clear(); 166 for (int i = 0; i < describedSeriesCount; i++) { 167 try { 168 169 boolean show = "1".equals(props.getProperty("Show."+i)); 170 171 String colS = props.getProperty("Colour."+i); 172 int r = Integer.parseInt(colS.substring(0, 2), 16); 173 int g = Integer.parseInt(colS.substring(2, 4), 16); 174 int b = Integer.parseInt(colS.substring(4, 6), 16); 175 Color col = new Color(r, g, b); 176 177 String scaleS = props.getProperty("TransformMode."+i); 178 TransformMode scale = TransformMode.Transform_None; 179 if (TransformMode.Transform_In0to1.toString().equalsIgnoreCase(scaleS)) 180 scale = TransformMode.Transform_In0to1; 181 else if (TransformMode.Transform_SetVal.toString().equalsIgnoreCase(scaleS)) 182 scale = TransformMode.Transform_SetVal; 183 184 double param = Double.parseDouble(props.getProperty("ScaleFactor."+i)); 185 186 settings.add(new SeriesParameters(show, col, scale, param)); 187 188 } catch (NumberFormatException e) { } 189 } 190 191 notifyObservers("load"); 192 return true; 193 } 194 195 196 /** 197 * Saves the settings to a specified file. 198 * 199 * @param fileName The file to save the settings to. 200 * @return {@code true} if the settings were saved, {@code false} if an exception occured. 201 */ 202 public boolean save(String fileName) { 203 204 Properties props = new Properties(); 205 props.setProperty("DescribedSeriesCount", Integer.toString(settings.size())); 206 for (int i = 0; i < settings.size(); i++) { 207 SeriesParameters series = settings.get(i); 208 props.setProperty("Show."+i, series.show ? "1" : "0"); 209 props.setProperty("Colour."+i, String.format("%02x%02x%02x", series.colour.getRed(), 210 series.colour.getGreen(), 211 series.colour.getBlue())); 212 props.setProperty("TransformMode."+i, series.scale.toString()); 213 props.setProperty("ScaleFactor."+i, Double.toString(series.param)); 214 } 215 216 try { 217 FileOutputStream out = new FileOutputStream(fileName); 218 try { props.storeToXML(out, "LiveGraph version " + LiveGraph.version + ". DataSeriesSettings."); } 219 finally { out.close(); } 220 return true; 221 } catch(IOException e) { 222 e.printStackTrace(); 223 return false; 224 } 225 } 226 227 /** 228 * Sets whether the data series with the specified index should be included in tthe plot. 229 * 230 * @param seriesIndex A data series index (corresponds to the column index in the data file). 231 * @param show {@code true} if the data series with the specified index is to be included in the plot, 232 * {@code false} otherwise. 233 */ 234 public void setShow(int seriesIndex, boolean show) { 235 ensureLength(seriesIndex); 236 settings.get(seriesIndex).show = show; 237 notifyObservers("Show." + seriesIndex); 238 } 239 240 /** 241 * Sets whether the data series between the specified indices should be included in the plot. 242 * 243 * @param from Starting data series index (inclusive). 244 * @param to Finishing data series index (inclusive). 245 * @param show {@code true} if the data series with the specified index is to be included in tthe plot, 246 * {@code false} otherwise. 247 */ 248 public void setShowAll(int from, int to, boolean show) { 249 if (from > to) { 250 int t = from; from = to; to = t; 251 } 252 253 ensureLength(to); 254 for (int i = from; i <= to; i++) 255 settings.get(i).show = show; 256 notifyObservers("Show"); 257 } 258 259 /** 260 * Toggles whether the data series between the specified indices should be included in the plot. 261 * 262 * @param from Starting data series index (inclusive). 263 * @param to Finishing data series index (inclusive). 264 */ 265 public void setShowToggleAll(int from, int to) { 266 if (from > to) { 267 int t = from; from = to; to = t; 268 } 269 270 ensureLength(to); 271 for (int i = from; i <= to; i++) 272 settings.get(i).show = !settings.get(i).show; 273 notifyObservers("Show"); 274 } 275 276 /** 277 * Sets the colour for the plot of the data series with the specified index. 278 * 279 * @param seriesIndex A data series index (corresponds to the column index in the data file). 280 * @param colour The colour for the plot of the data series with the specified index. 281 */ 282 public void setColour(int seriesIndex, Color colour) { 283 if (null == colour) 284 throw new NullPointerException("Null colour is not allowed."); 285 ensureLength(seriesIndex); 286 settings.get(seriesIndex).colour = colour; 287 notifyObservers("Colour." + seriesIndex); 288 } 289 290 /** 291 * Sets the transformation mode for the plotted values of the data series with the specified index. 292 * 293 * @param seriesIndex A data series index (corresponds to the column index in the data file). 294 * @param transformMode The transformation mode for the plotted values of the data series with the specified index. 295 */ 296 public void setTransformMode(int seriesIndex, TransformMode transformMode) { 297 if (null == transformMode) 298 throw new NullPointerException("Null scale mode is not allowed."); 299 ensureLength(seriesIndex); 300 settings.get(seriesIndex).scale = transformMode; 301 notifyObservers("TransformMode." + seriesIndex); 302 } 303 304 /** 305 * Sets the parameter for the transformation of the plotted values of the data series with the specified index; 306 * this parameter is currently required only for the mode {@code Transform_SetVal}; 307 308 * @param seriesIndex A data series index (corresponds to the column index in the data file). 309 * @param parameter The parameter for the transformation of the plotted values of the data series with 310 * the specified index. 311 */ 312 public void setScaleFactor(int seriesIndex, double parameter) { 313 ensureLength(seriesIndex); 314 settings.get(seriesIndex).param = parameter; 315 notifyObservers("ScaleFactor." + seriesIndex); 316 } 317 318 /** 319 * Setts whether the data series with the specified index should be included in tthe plot. 320 * If no setting value has been defined for the specified series, a defalut value will be 321 * returned as specified by {@link #getDefaultShow(int)}. 322 * 323 * @param seriesIndex A data series index (corresponds to the column index in the data file). 324 * @return {@code true} if the data series with the specified index is to be included in tthe plot, 325 * {@code false} otherwise. 326 * @see #getDefaultShow(int) 327 */ 328 public boolean getShow(int seriesIndex) { 329 if (seriesIndex >= settings.size()) 330 return getDefaultShow(seriesIndex); 331 return settings.get(seriesIndex).show; 332 } 333 334 /** 335 * Gets the colour for the plot of the data series with the specified index. 336 * If no setting value has been defined for the specified series, a defalut value will be 337 * returned as specified by {@link #getDefaultColour(int)}. 338 * 339 * @param seriesIndex A data series index (corresponds to the column index in the data file). 340 * @return The colour for the plot of the data series with the specified index. 341 * @see #getDefaultColour(int) 342 */ 343 public Color getColour(int seriesIndex) { 344 if (seriesIndex >= settings.size()) 345 return getDefaultColour(seriesIndex); 346 return settings.get(seriesIndex).colour; 347 } 348 349 /** 350 * Gets the transformation mode for the plotted values of the data series with the specified index. 351 * If no setting value has been defined for the specified series, a defalut value will be 352 * returned as specified by {@link #getDefaultTransformMode(int)}. 353 * 354 * @param seriesIndex A data series index (corresponds to the column index in the data file). 355 * @return The transformation mode for the plotted values of the data series with the specified index. 356 * @see #getDefaultTransformMode(int) 357 */ 358 public TransformMode getTransformMode(int seriesIndex) { 359 if (seriesIndex >= settings.size()) 360 return getDefaultTransformMode(seriesIndex); 361 return settings.get(seriesIndex).scale; 362 } 363 364 /** 365 * Gets the parameter for the transformation of the plotted values of the data series with 366 * the specified index; this parameter is currently required only for the mode {@code Transform_SetVal}. 367 * If no setting value has been defined for the specified series, a defalut value will be 368 * returned as specified by {@link #getDefaultScaleFactor(int)}. 369 * 370 * @param seriesIndex A data series index (corresponds to the column index in the data file). 371 * @return The parameter for the transformation of the plotted values of the data series with 372 * the specified index. 373 * @see #getDefaultScaleFactor(int) 374 */ 375 public double getScaleFactor(int seriesIndex) { 376 if (seriesIndex >= settings.size()) 377 return getDefaultScaleFactor(seriesIndex); 378 return settings.get(seriesIndex).param; 379 } 380 381 /** 382 * Data seties settings objects may listen to other settings objects in order to 383 * react to changed in other settings; currently data series settings react when 384 * a data series is set to be used as the x-axis. 385 * This method calls {@link #setSeriesMarkedAsXAxis(int)} to process this event. 386 * <br /><br /> 387 * The corresponding series is then set to invisible, because practice suggests 388 * that users rarely want to plot the series against which all other series are 389 * plotted. If users want to have the series plotted anyway, they can switch 390 * it back on at any time. 391 * 392 * @see #setSeriesMarkedAsXAxis(int) 393 */ 394 public void settingHasChanged(ObservableSettings settings, Object info) { 395 396 if (null == info || null == settings) 397 return; 398 399 if (! (settings instanceof GraphSettings)) 400 return; 401 402 if (! (info instanceof String)) 403 return; 404 405 if (!((String) info).equals("XAxisType") && !((String) info).equals("XAxisSeriesIndex")) 406 return; 407 408 GraphSettings gs = (GraphSettings) settings; 409 410 if (GraphSettings.XAxisType.XAxis_DSNum == gs.getXAxisType()) 411 return; 412 413 setSeriesMarkedAsXAxis(gs.getXAxisSeriesIndex()); 414 } 415 416 /** 417 * Getts called when this settings container was registered as an observer to a 418 * graph settings container and some data series was set as being used as the 419 * x-axis in the observed graph settings container. 420 * <br /><br /> 421 * The specified data series is set to invisible, because practice suggests 422 * that users rarely want to plot the series against which all other series are 423 * plotted. If users want to have the series plotted anyway, they can switch 424 * it back on at any time. 425 * 426 * @param seriesIndex The index of the data series to be set to invisible 427 * (corresponds to the column index in the data file). 428 * @see #settingHasChanged(ObservableSettings, Object) 429 */ 430 private void setSeriesMarkedAsXAxis(int seriesIndex) { 431 if (getShow(seriesIndex)) 432 setShow(seriesIndex, false); 433 } 434 435 /** 436 * This struct-class is used to group the settings for one data series in a single 437 * data structure. 438 */ 439 public class SeriesParameters { 440 441 /** 442 * Whether this data series should be shown at all. 443 */ 444 public boolean show = false; 445 446 /** 447 * Colour to use for this series. 448 */ 449 public Color colour = null; 450 451 /** 452 * Transformation mode for series values. 453 */ 454 public TransformMode scale = null; 455 456 /** 457 * Parameter for series' values transformation. 458 */ 459 public double param = Double.NaN; 460 461 /** 462 * Creates an uninitialised series settings data structure. 463 */ 464 public SeriesParameters() {} 465 466 /** 467 * Creates an series settings data structure and initialises it with the specified values. 468 * 469 * @param show Display? 470 * @param colour Line colour. 471 * @param scale Values transformation. 472 * @param param Transformation parameter. 473 */ 474 public SeriesParameters(boolean show, Color colour, TransformMode scale, double param) { 475 this.show = show; 476 this.colour = colour; 477 this.scale = scale; 478 this.param = param; 479 } 480 } // private class SeriesParameters 481 482 } // public class DataSeriesSettings