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