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