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