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