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}