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}