001package jmri.jmrit.throttle; 002 003import java.awt.Dimension; 004import java.beans.PropertyChangeEvent; 005import java.beans.PropertyChangeListener; 006import java.io.File; 007import java.util.ArrayList; 008 009import jmri.jmrit.XmlFile; 010import jmri.util.FileUtil; 011 012import org.jdom2.Document; 013import org.jdom2.Element; 014import org.slf4j.Logger; 015import org.slf4j.LoggerFactory; 016 017/** 018 * A class to store JMRI throttles preferences. 019 * <p> 020 * A singleton instance is provided by a call to 021 * <code>jmri.InstanceManager.getDefault(ThrottlesPreferences.class);</code> or 022 * <code>jmri.InstanceManager.getNullableDefault(ThrottlesPreferences.class)</code>; 023 * @author Lionel Jeanson - 2009-2021 024 * 025 */ 026public class ThrottlesPreferences implements jmri.InstanceManagerAutoDefault { 027 028 private boolean _useExThrottle = true; 029 private boolean _useToolBar = true; 030 private boolean _useFunctionIcon = true; 031 private boolean _useLargeSpeedSlider = true; 032 private boolean _resizeWinImg = false; 033 private boolean _useRosterImage = true; 034 private boolean _enableRosterSearch = true; 035 private boolean _enableAutoLoad = true; 036 private boolean _hideUndefinedFunButton = false; 037 private boolean _hideSpeedStepSelector = false; 038 private boolean _ignoreThrottlePosition = true; 039 private boolean _saveThrottleOnLayoutSave = true; 040 private boolean _isSilentSteal = false; 041 private boolean _isSilentShare = false; 042 private String _defaultThrottleFilePath = null; 043 private ThrottlesPreferencesWindowKeyboardControls _tpwkc = new ThrottlesPreferencesWindowKeyboardControls(); 044 protected boolean dirty = false; 045 046 private Dimension _winDim = new Dimension(800, 600); 047 private String prefFile; 048 private ArrayList<PropertyChangeListener> listeners; 049 050 public ThrottlesPreferences() { 051 String dirname = FileUtil.getUserFilesPath() + "throttle" + File.separator; 052 FileUtil.createDirectory(dirname); 053 prefFile = dirname + "ThrottlesPreferences.xml"; 054 ThrottlesPrefsXml prefs = new ThrottlesPrefsXml(); 055 File file = new File(prefFile); 056 Element root; 057 try { 058 root = prefs.rootFromFile(file); 059 } catch (java.io.FileNotFoundException e2) { 060 log.info("Did not find throttle preferences file. This is normal if you haven't save the preferences before"); 061 root = null; 062 } catch (Exception e) { 063 log.error("Exception while loading throttles preferences", e); 064 root = null; 065 } 066 if (root != null) { 067 load(root.getChild("throttlesPreferences")); 068 } 069 } 070 071 public void load(org.jdom2.Element e) { 072 if (e == null) { 073 return; 074 } 075 org.jdom2.Attribute a; 076 org.jdom2.Attribute b; 077 if ((a = e.getAttribute("isUsingExThrottle")) != null) { 078 setUseExThrottle(a.getValue().compareTo("true") == 0); 079 } 080 if ((a = e.getAttribute("isUsingToolBar")) != null) { 081 setUsingToolBar(a.getValue().compareTo("true") == 0); 082 } 083 if ((a = e.getAttribute("isResizingWindow")) != null) { 084 setResizeWindow(a.getValue().compareTo("true") == 0); 085 } 086 if ((a = e.getAttribute("isUsingFunctionIcon")) != null) { 087 setUsingFunctionIcon(a.getValue().compareTo("true") == 0); 088 } 089 if (((a = e.getAttribute("windowDimensionWidth")) != null) && ((b = e.getAttribute("windowDimensionHeight")) != null)) { 090 setWindowDimension(new Dimension(Integer.parseInt(a.getValue()), Integer.parseInt(b.getValue()))); 091 } 092 if ((a = e.getAttribute("isSavingThrottleOnLayoutSave")) != null) { 093 setSaveThrottleOnLayoutSave(a.getValue().compareTo("true") == 0); 094 } 095 if ((a = e.getAttribute("isUsingRosterImage")) != null) { 096 setUseRosterImage(a.getValue().compareTo("true") == 0); 097 } 098 if ((a = e.getAttribute("isEnablingRosterSearch")) != null) { 099 setEnableRosterSearch(a.getValue().compareTo("true") == 0); 100 } 101 if ((a = e.getAttribute("isAutoLoading")) != null) { 102 setAutoLoad(a.getValue().compareTo("true") == 0); 103 } 104 if ((a = e.getAttribute("isHidingUndefinedFunctionButtons")) != null) { 105 setHideUndefinedFuncButt(a.getValue().compareTo("true") == 0); 106 } 107 if ((a = e.getAttribute("isIgnoringThrottlePosition")) != null) { 108 setIgnoreThrottlePosition(a.getValue().compareTo("true") == 0); 109 } 110 if ((a = e.getAttribute("isSilentSteal")) != null) { 111 setSilentSteal(a.getValue().compareTo("true") == 0); 112 } 113 if ((a = e.getAttribute("isSilentShare")) != null) { 114 setSilentShare(a.getValue().compareTo("true") == 0); 115 } 116 if ((a = e.getAttribute("isUsingLargeSpeedSlider")) != null) { 117 setUseLargeSpeedSlider(a.getValue().compareTo("true") == 0); 118 } 119 if ((a = e.getAttribute("isHidingSpeedStepSelector")) != null) { 120 setHideSpeedStepSelector(a.getValue().compareTo("true") == 0); 121 } 122 if (e.getChild("throttlesControls") != null) { 123 this._tpwkc.load(e.getChild("throttlesControls")); 124 } 125 if ((a = e.getAttribute("defaultThrottleFilePath")) != null) { 126 setDefaultThrottleFilePath(a.getValue()); 127 } 128 129 this.dirty = false; 130 } 131 132 /** 133 * @return true if preferences need to be saved 134 */ 135 public boolean isDirty() { 136 return dirty; 137 } 138 139 /** 140 * An extension of the abstract XmlFile. No changes made to that class. 141 * 142 */ 143 static class ThrottlesPrefsXml extends XmlFile { 144 } 145 146 public Element store() { 147 org.jdom2.Element e = new org.jdom2.Element("throttlesPreferences"); 148 e.setAttribute("isUsingExThrottle", "" + isUsingExThrottle()); 149 e.setAttribute("isUsingToolBar", "" + isUsingToolBar()); 150 e.setAttribute("isUsingFunctionIcon", "" + isUsingFunctionIcon()); 151 e.setAttribute("isResizingWindow", "" + isResizingWindow()); 152 e.setAttribute("windowDimensionWidth", "" + (int) getWindowDimension().getWidth()); 153 e.setAttribute("windowDimensionHeight", "" + (int) getWindowDimension().getHeight()); 154 e.setAttribute("isSavingThrottleOnLayoutSave", "" + isSavingThrottleOnLayoutSave()); 155 e.setAttribute("isUsingRosterImage", "" + isUsingRosterImage()); 156 e.setAttribute("isEnablingRosterSearch", "" + isEnablingRosterSearch()); 157 e.setAttribute("isAutoLoading", "" + isAutoLoading()); 158 e.setAttribute("isHidingUndefinedFunctionButtons", "" + isHidingUndefinedFuncButt()); 159 e.setAttribute("isIgnoringThrottlePosition", "" + isIgnoringThrottlePosition()); 160 e.setAttribute("isSilentSteal", "" + isSilentSteal()); 161 e.setAttribute("isSilentShare", "" + isSilentShare()); 162 e.setAttribute("isHidingSpeedStepSelector", "" + isHidingSpeedStepSelector()); 163 e.setAttribute("isUsingLargeSpeedSlider", "" + isUsingLargeSpeedSlider()); 164 e.setAttribute("defaultThrottleFilePath", "" + getDefaultThrottleFilePath()); 165 java.util.ArrayList<Element> children = new java.util.ArrayList<>(1); 166 children.add(this._tpwkc.store()); 167 e.setContent(children); 168 return e; 169 } 170 171 public void set(ThrottlesPreferences tp) { 172 setWindowDimension(tp.getWindowDimension()); 173 setUseExThrottle(tp.isUsingExThrottle()); 174 setUsingToolBar(tp.isUsingToolBar()); 175 setUsingFunctionIcon(tp.isUsingFunctionIcon()); 176 setResizeWindow(tp.isResizingWindow()); 177 setSaveThrottleOnLayoutSave(tp.isSavingThrottleOnLayoutSave()); 178 setUseRosterImage(tp.isUsingRosterImage()); 179 setEnableRosterSearch(tp.isEnablingRosterSearch()); 180 setAutoLoad(tp.isAutoLoading()); 181 setHideUndefinedFuncButt(tp.isHidingUndefinedFuncButt()); 182 setIgnoreThrottlePosition(tp.isIgnoringThrottlePosition()); 183 setSilentSteal(tp.isSilentSteal()); 184 setSilentShare(tp.isSilentShare()); 185 setUseLargeSpeedSlider(tp.isUsingLargeSpeedSlider()); 186 setThrottlesKeyboardControls(tp.getThrottlesKeyboardControls()); 187 setDefaultThrottleFilePath(tp.getDefaultThrottleFilePath()); 188 setHideSpeedStepSelector(tp.isHidingSpeedStepSelector()); 189 190 if (listeners != null) { 191 for (int i = 0; i < listeners.size(); i++) { 192 PropertyChangeListener l = listeners.get(i); 193 PropertyChangeEvent e = new PropertyChangeEvent(this, "ThrottlePreferences", null, this); 194 l.propertyChange(e); 195 } 196 } 197 } 198 199 public void save() { 200 if (prefFile == null) { 201 return; 202 } 203 XmlFile xf = new XmlFile() { 204 }; // odd syntax is due to XmlFile being abstract 205 xf.makeBackupFile(prefFile); 206 File file = new File(prefFile); 207 try { 208 //The file does not exist, create it before writing 209 File parentDir = file.getParentFile(); 210 if (!parentDir.exists()) { 211 if (!parentDir.mkdir()) // make directory, check result 212 { 213 log.error("failed to make parent directory"); 214 } 215 } 216 if (!file.createNewFile()) // create file, check result 217 { 218 log.error("createNewFile failed"); 219 } 220 } catch (Exception exp) { 221 log.error("Exception while writing the new throttles preferences file, may not be complete", exp); 222 } 223 224 try { 225 Element root = new Element("throttles-preferences"); 226 Document doc = XmlFile.newDocument(root, XmlFile.getDefaultDtdLocation() + "throttles-preferences.dtd"); 227 // add XSLT processing instruction 228 // <?xml-stylesheet type="text/xsl" href="XSLT/throttle.xsl"?> 229/*TODO java.util.Map<String,String> m = new java.util.HashMap<String,String>(); 230 m.put("type", "text/xsl"); 231 m.put("href", jmri.jmrit.XmlFile.xsltLocation+"throttles-preferences.xsl"); 232 ProcessingInstruction p = new ProcessingInstruction("xml-stylesheet", m); 233 doc.addContent(0,p);*/ 234 root.setContent(store()); 235 xf.writeXML(file, doc); 236 } catch (java.io.IOException ex) { 237 log.warn("Exception in storing throttles preferences xml", ex); 238 } 239 this.dirty = false; 240 } 241 242 public Dimension getWindowDimension() { 243 return _winDim; 244 } 245 246 public void setWindowDimension(Dimension d) { 247 _winDim = d; 248 this.dirty = true; 249 } 250 251 public boolean isUsingExThrottle() { 252 return _useExThrottle; 253 } 254 255 public void setUseExThrottle(boolean exThrottle) { 256 _useExThrottle = exThrottle; 257 this.dirty = true; 258 } 259 260 public boolean isUsingToolBar() { 261 return _useToolBar; 262 } 263 264 public void setUsingToolBar(boolean win4all) { 265 _useToolBar = win4all; 266 this.dirty = true; 267 } 268 269 /** 270 * Check if function icons are in use. 271 * 272 * @return user preference to use function icons. 273 */ 274 public boolean isUsingFunctionIcon() { 275 return _useFunctionIcon; 276 } 277 278 public void setUsingFunctionIcon(boolean useFunctionIcon) { 279 _useFunctionIcon = useFunctionIcon; 280 this.dirty = true; 281 } 282 283 public boolean isResizingWindow() { 284 return _resizeWinImg; 285 } 286 287 public void setResizeWindow(boolean winImg) { 288 _resizeWinImg = winImg; 289 this.dirty = true; 290 } 291 292 public boolean isUsingRosterImage() { 293 return _useRosterImage; 294 } 295 296 public void setUseRosterImage(boolean rosterImage) { 297 _useRosterImage = rosterImage; 298 this.dirty = true; 299 } 300 301 public boolean isEnablingRosterSearch() { 302 return _enableRosterSearch; 303 } 304 305 public void setEnableRosterSearch(boolean b) { 306 _enableRosterSearch = b; 307 this.dirty = true; 308 } 309 310 public void setAutoLoad(boolean b) { 311 _enableAutoLoad = b; 312 this.dirty = true; 313 } 314 315 public boolean isAutoLoading() { 316 return _enableAutoLoad; 317 } 318 319 public void setHideUndefinedFuncButt(boolean b) { 320 _hideUndefinedFunButton = b; 321 this.dirty = true; 322 } 323 324 public boolean isHidingUndefinedFuncButt() { 325 return _hideUndefinedFunButton; 326 } 327 328 public void setIgnoreThrottlePosition(boolean b) { 329 _ignoreThrottlePosition = b; 330 this.dirty = true; 331 } 332 333 public boolean isIgnoringThrottlePosition() { 334 return _ignoreThrottlePosition; 335 } 336 337 public void setSaveThrottleOnLayoutSave(boolean b) { 338 _saveThrottleOnLayoutSave = b; 339 this.dirty = true; 340 } 341 342 public boolean isSavingThrottleOnLayoutSave() { 343 return _saveThrottleOnLayoutSave; 344 } 345 346 public boolean isSilentSteal() { 347 return _isSilentSteal; 348 } 349 350 public boolean isSilentShare() { 351 return _isSilentShare; 352 } 353 354 public void setSilentSteal(boolean b) { 355 _isSilentSteal = b; 356 this.dirty = true; 357 } 358 359 public void setSilentShare(boolean b) { 360 _isSilentShare = b; 361 this.dirty = true; 362 } 363 364 365 public void setUseLargeSpeedSlider(boolean b) { 366 _useLargeSpeedSlider = b; 367 this.dirty = true; 368 } 369 370 public boolean isUsingLargeSpeedSlider() { 371 return _useLargeSpeedSlider; 372 } 373 374 public void setDefaultThrottleFilePath(String p) { 375 _defaultThrottleFilePath = p; 376 this.dirty = true; 377 } 378 379 public String getDefaultThrottleFilePath() { 380 return _defaultThrottleFilePath; 381 } 382 383 public boolean isHidingSpeedStepSelector() { 384 return _hideSpeedStepSelector; 385 } 386 387 public void setHideSpeedStepSelector(boolean b) { 388 _hideSpeedStepSelector = b; 389 this.dirty = true; 390 } 391 392 /** 393 * @return the throttles keyboard controls preferences 394 */ 395 public ThrottlesPreferencesWindowKeyboardControls getThrottlesKeyboardControls() { 396 return _tpwkc; 397 } 398 399 /** 400 * Set the throttles keyboard controls preferences 401 * @param tpwkc the new keyboard preferences 402 */ 403 public void setThrottlesKeyboardControls(ThrottlesPreferencesWindowKeyboardControls tpwkc) { 404 _tpwkc = tpwkc; 405 } 406 407 /** 408 * Add an AddressListener. 409 * AddressListeners are notified when the user 410 * selects a new address and when a Throttle is acquired for that address. 411 * @param l listener to add. 412 * 413 */ 414 public void addPropertyChangeListener(PropertyChangeListener l) { 415 if (listeners == null) { 416 listeners = new ArrayList<>(2); 417 } 418 if (!listeners.contains(l)) { 419 listeners.add(l); 420 } 421 } 422 423 /** 424 * Remove an AddressListener. 425 * @param l listener to remove. 426 */ 427 public void removePropertyChangeListener(PropertyChangeListener l) { 428 if (listeners == null) { 429 return; 430 } 431 listeners.remove(l); 432 } 433 434 private final static Logger log = LoggerFactory.getLogger(ThrottlesPreferences.class); 435}