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