001package jmri.jmrit.vsdecoder; 002 003import java.beans.PropertyChangeEvent; 004import java.beans.PropertyChangeListener; 005import java.io.*; 006import java.util.ArrayList; 007import jmri.jmrit.XmlFile; 008import jmri.jmrit.vsdecoder.listener.ListeningSpot; 009import jmri.util.FileUtil; 010import jmri.util.PhysicalLocation; 011import org.jdom2.*; 012import org.slf4j.Logger; 013import org.slf4j.LoggerFactory; 014 015/** 016 * Manage VSDecoder Preferences. 017 * 018 * <hr> 019 * This file is part of JMRI. 020 * <p> 021 * JMRI is free software; you can redistribute it and/or modify it under 022 * the terms of version 2 of the GNU General Public License as published 023 * by the Free Software Foundation. See the "COPYING" file for a copy 024 * of this license. 025 * <p> 026 * JMRI is distributed in the hope that it will be useful, but WITHOUT 027 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 028 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 029 * for more details. 030 * 031 * @author Mark Underwood Copyright (C) 2011 032 */ 033public class VSDecoderPreferences { 034 035 public final static String VSDPreferencesFileName = "VSDecoderPreferences.xml"; 036 037 static public final int DefaultMasterVolume = 80; 038 039 // Private variables to hold preference values 040 private boolean _autoStartEngine = false; // play engine sound w/o waiting for "Engine Start" button pressed. 041 private boolean _autoLoadVSDFile = false; // Automatically load a VSD file. 042 private boolean _use_blocks = true; 043 private String _defaultVSDFilePath = null; 044 private ListeningSpot _listenerPosition; 045 private int _masterVolume; 046 047 // Other internal variables 048 private String prefFile; 049 private ArrayList<PropertyChangeListener> listeners; 050 051 public VSDecoderPreferences(String sfile) { 052 prefFile = sfile; 053 VSDecoderPrefsXml prefs = new VSDecoderPrefsXml(); 054 File file = new File(prefFile); 055 Element root; 056 057 // Set default values 058 _defaultVSDFilePath = FileUtil.getExternalFilename("program:resources/vsdecoder"); 059 _listenerPosition = new ListeningSpot(); // default to (0, 0, 0) Orientation (0,1,0) 060 _masterVolume = DefaultMasterVolume; 061 062 // Try to load preferences from the file 063 try { 064 root = prefs.rootFromFile(file); 065 } catch (IOException e2) { 066 log.info("Did not find VSDecoder preferences file. This is normal if you haven't save the preferences before"); 067 root = null; 068 } catch (JDOMException | RuntimeException e) { 069 log.error("Exception while loading VSDecoder preferences", e); 070 root = null; 071 } 072 if (root != null) { 073 load(root.getChild("VSDecoderPreferences")); 074 } 075 } 076 077 public VSDecoderPreferences() { 078 } 079 080 public void load(org.jdom2.Element e) { 081 if (e == null) { 082 return; 083 } 084 org.jdom2.Attribute a; 085 org.jdom2.Element c; 086 087 a = e.getAttribute("isAutoStartingEngine"); 088 if (a != null) { 089 setAutoStartEngine(a.getValue().equals("true")); 090 } 091 // new attribute name! 092 a = e.getAttribute("isAutoLoadingVSDFile"); 093 if (a != null) { 094 setAutoLoadVSDFile(a.getValue().equals("true")); 095 } else { 096 // try the old name, in case the user has not saved his preferences since the name change; JMRI 5.5.4 097 a = e.getAttribute("isAutoLoadingDefaultVSDFile"); 098 if (a != null) { 099 setAutoLoadVSDFile(a.getValue().equals("true")); 100 } 101 } 102 a = e.getAttribute("useBlocks"); 103 if (a != null) { 104 setUseBlocksSetting(a.getValue().equals("true")); 105 } 106 c = e.getChild("DefaultVSDFilePath"); 107 if (c != null) { 108 setDefaultVSDFilePath(c.getValue()); 109 } 110 c = e.getChild("ListenerPosition"); 111 if (c != null) { 112 _listenerPosition = new ListeningSpot(c); 113 } else { 114 _listenerPosition = new ListeningSpot(); 115 } 116 c = e.getChild("MasterVolume"); 117 if (c != null) { 118 setMasterVolume(Integer.parseInt(c.getValue())); 119 } 120 } 121 122 /** 123 * An extension of the abstract XmlFile. No changes made to that class. 124 * 125 */ 126 static class VSDecoderPrefsXml extends XmlFile { 127 } 128 129 private org.jdom2.Element store() { 130 org.jdom2.Element ec; 131 org.jdom2.Element e = new org.jdom2.Element("VSDecoderPreferences"); 132 e.setAttribute("isAutoStartingEngine", "" + isAutoStartingEngine()); 133 e.setAttribute("isAutoLoadingVSDFile", "" + isAutoLoadingVSDFile()); 134 e.setAttribute("useBlocks", "" + getUseBlocksSetting()); 135 ec = new Element("DefaultVSDFilePath"); 136 ec.setText("" + getDefaultVSDFilePath()); 137 e.addContent(ec); 138 // ListenerPosition generates its own XML 139 e.addContent(_listenerPosition.getXml("ListenerPosition")); 140 ec = new Element("MasterVolume"); 141 ec.setText("" + getMasterVolume()); 142 e.addContent(ec); 143 return e; 144 } 145 146 public void set(VSDecoderPreferences tp) { 147 setAutoStartEngine(tp.isAutoStartingEngine()); 148 setAutoLoadVSDFile(tp.isAutoLoadingVSDFile()); 149 setUseBlocksSetting(tp.getUseBlocksSetting()); 150 setDefaultVSDFilePath(tp.getDefaultVSDFilePath()); 151 setListenerPosition(tp.getListenerPosition()); 152 setMasterVolume(tp.getMasterVolume()); 153 154 if (listeners != null) { 155 for (int i = 0; i < listeners.size(); i++) { 156 PropertyChangeListener l = listeners.get(i); 157 PropertyChangeEvent e = new PropertyChangeEvent(this, "VSDecoderPreferences", null, this); 158 l.propertyChange(e); 159 } 160 } 161 } 162 163 public boolean compareTo(VSDecoderPreferences tp) { 164 return (isAutoStartingEngine() != tp.isAutoStartingEngine() 165 || isAutoLoadingVSDFile() != tp.isAutoLoadingVSDFile() 166 || getUseBlocksSetting() != tp.getUseBlocksSetting() 167 || !(getDefaultVSDFilePath().equals(tp.getDefaultVSDFilePath())) 168 || !(getListenerPosition().equals(tp.getListenerPosition())) 169 || !(getMasterVolume().equals(tp.getMasterVolume()))); 170 } 171 172 public void save() { 173 if (prefFile == null) { 174 return; 175 } 176 XmlFile xf = new XmlFile() { 177 }; // odd syntax is due to XmlFile being abstract 178 xf.makeBackupFile(prefFile); 179 File file = new File(prefFile); 180 try { 181 //The file does not exist, create it before writing 182 File parentDir = file.getParentFile(); 183 if (!parentDir.exists()) { 184 if (!parentDir.mkdir()) { // make directory, check result 185 log.error("failed to make parent directory"); 186 } 187 } 188 if (!file.createNewFile()) { // create file, check result 189 log.error("createNewFile failed"); 190 } 191 } catch (IOException | RuntimeException exp) { 192 log.error("Exception while writing the new VSDecoder preferences file, may not be complete", exp); 193 } 194 195 try { 196 Element root = new Element("vsdecoder-preferences"); 197 //Document doc = XmlFile.newDocument(root, XmlFile.dtdLocation+"vsdecoder-preferences.dtd"); 198 Document doc = XmlFile.newDocument(root); 199 // add XSLT processing instruction 200 // <?xml-stylesheet type="text/xsl" href="XSLT/throttle.xsl"?> 201/*TODO java.util.Map<String,String> m = new java.util.HashMap<String,String>(); 202 m.put("type", "text/xsl"); 203 m.put("href", jmri.jmrit.XmlFile.xsltLocation+"throttles-preferences.xsl"); 204 ProcessingInstruction p = new ProcessingInstruction("xml-stylesheet", m); 205 doc.addContent(0,p);*/ 206 root.setContent(store()); 207 xf.writeXML(file, doc); 208 } catch (IOException | RuntimeException ex) { // TODO fix null value for Attribute 209 log.warn("Exception in storing vsdecoder preferences xml", ex); 210 } 211 } 212 213 public String getDefaultVSDFilePath() { 214 return _defaultVSDFilePath; 215 } 216 217 public void setDefaultVSDFilePath(String s) { 218 _defaultVSDFilePath = s; 219 } 220 221 public boolean isAutoStartingEngine() { 222 return _autoStartEngine; 223 } 224 225 public void setAutoStartEngine(boolean b) { 226 _autoStartEngine = b; 227 } 228 229 public boolean isAutoLoadingVSDFile() { 230 return _autoLoadVSDFile; 231 } 232 233 public void setUseBlocksSetting(boolean b) { 234 _use_blocks = b; 235 } 236 237 public boolean getUseBlocksSetting() { 238 return _use_blocks; 239 } 240 241 public void setAutoLoadVSDFile(boolean b) { 242 _autoLoadVSDFile = b; 243 } 244 245 public ListeningSpot getListenerPosition() { 246 log.debug("getListenerPosition() : {}", _listenerPosition); 247 return _listenerPosition; 248 } 249 250 public void setListenerPosition(ListeningSpot p) { 251 VSDecoderManager vm = VSDecoderManager.instance(); 252 vm.setListenerLocation(vm.getDefaultListenerName(), p); 253 _listenerPosition = p; 254 } 255 // Note: No setListenerPosition(String) for ListeningSpot implementation 256 257 public PhysicalLocation getListenerPhysicalLocation() { 258 return _listenerPosition.getPhysicalLocation(); 259 } 260 261 public void setListenerPosition(PhysicalLocation p) { 262 VSDecoderManager vm = VSDecoderManager.instance(); 263 vm.setListenerLocation(vm.getDefaultListenerName(), new ListeningSpot(p)); 264 //_listenerPosition = new ListeningSpot(); 265 //_listenerPosition.setLocation(p); 266 } 267 268 public void setMasterVolume(int v) { 269 _masterVolume = v; 270 } 271 272 public Integer getMasterVolume() { 273 return _masterVolume; 274 } 275 276 /** 277 * Add an AddressListener. 278 * <p> 279 * AddressListeners are notified when the user 280 * selects a new address and when a Throttle is acquired for that address. 281 * 282 * @param l listener to add. 283 * 284 */ 285 public void addPropertyChangeListener(PropertyChangeListener l) { 286 if (listeners == null) { 287 listeners = new ArrayList<PropertyChangeListener>(2); 288 } 289 if (!listeners.contains(l)) { 290 listeners.add(l); 291 } 292 } 293 294 /** 295 * Remove an AddressListener. 296 * 297 * @param l listener to remove. 298 */ 299 public void removePropertyChangeListener(PropertyChangeListener l) { 300 if (listeners == null) { 301 return; 302 } 303 if (listeners.contains(l)) { 304 listeners.remove(l); 305 } 306 } 307 308 private final static Logger log = LoggerFactory.getLogger(VSDecoderPreferences.class); 309 310}