001package jmri.jmrit.throttle.preferences;
002
003import java.awt.BorderLayout;
004import java.awt.GridBagConstraints;
005import java.awt.GridBagLayout;
006import java.awt.Insets;
007import java.awt.event.ActionEvent;
008import java.awt.event.ActionListener;
009import java.io.File;
010import java.io.IOException;
011
012import javax.swing.AbstractAction;
013import javax.swing.BoxLayout;
014import javax.swing.JButton;
015import javax.swing.JCheckBox;
016import javax.swing.JFileChooser;
017import javax.swing.JLabel;
018import javax.swing.JPanel;
019import javax.swing.JSeparator;
020import javax.swing.JTextField;
021
022import org.jdom2.Element;
023import org.jdom2.JDOMException;
024
025import jmri.InstanceManager;
026import jmri.jmrit.throttle.LoadXmlThrottlesLayoutAction;
027import jmri.jmrit.throttle.implementation.ThrottleUICore;
028import jmri.util.swing.JmriJOptionPane;
029
030/**
031 * A preferences panel to display and edit JMRI throttle preferences
032 * 
033 * @author Lionel Jeanson - 2009 - 2021
034 * 
035 */
036public class ThrottlesPreferencesUISettingsPane extends JPanel {
037
038    private JCheckBox cbUseToolBar;
039    private JCheckBox cbUseFunctionIcon;
040    private JCheckBox cbUseLargeSpeedSlider;
041    private JCheckBox cbResizeWinImg;
042    private JCheckBox cbUseExThrottle;
043    private JCheckBox cbUseRosterImage;
044    private JCheckBox cbEnableRosterSearch;
045    private JCheckBox cbEnableAutoLoad;
046    private JCheckBox cbHideUndefinedButtons;
047    private JCheckBox cbHideSpeedSelector;
048    private JCheckBox cbIgnoreThrottlePosition;
049    private JCheckBox cbSaveThrottleOnLayoutSave;
050    private JCheckBox cbAddressSelectorShowingAllRosterGroup;
051    private JCheckBox cbSilentSteal;
052    private JCheckBox cbSilentShare;
053    private JTextField tfDefaultThrottleLocation;
054    private boolean isDirty = false;
055
056    /**
057     * Creates new form ThrottlesPreferencesPane
058     * @param tp the throttle preferences to init the component
059     */
060    public ThrottlesPreferencesUISettingsPane(ThrottlesPreferences tp) {
061        initComponents();
062        resetComponents(tp);
063        checkConsistancy();
064    }
065
066    private void initComponents() {
067
068        cbUseExThrottle = new JCheckBox();
069        cbUseToolBar = new JCheckBox();
070        cbUseFunctionIcon = new JCheckBox();
071        cbUseLargeSpeedSlider = new JCheckBox();
072        cbUseRosterImage = new JCheckBox();
073        cbResizeWinImg = new JCheckBox();
074        cbEnableRosterSearch = new JCheckBox();
075        cbEnableAutoLoad = new JCheckBox();
076        cbHideUndefinedButtons = new JCheckBox();
077        cbHideSpeedSelector = new JCheckBox();
078        cbAddressSelectorShowingAllRosterGroup = new JCheckBox();
079        cbIgnoreThrottlePosition = new JCheckBox();
080        cbSaveThrottleOnLayoutSave = new JCheckBox();
081        cbSilentSteal = new JCheckBox();
082        cbSilentShare = new JCheckBox();
083        tfDefaultThrottleLocation = new JTextField();
084
085        cbUseExThrottle.setText(Bundle.getMessage("UseExThrottle"));
086        cbResizeWinImg.setText(Bundle.getMessage("ExThrottleForceResize"));
087        cbUseToolBar.setText(Bundle.getMessage("ExThrottleUseToolBar"));
088        cbUseFunctionIcon.setText(Bundle.getMessage("ExThrottleUseFunctionIcons"));
089        cbUseLargeSpeedSlider.setText(Bundle.getMessage("ExThrottleUseLargeSpeedSlider"));
090        cbUseRosterImage.setText(Bundle.getMessage("ExThrottleUseRosterImageBkg"));
091        cbEnableRosterSearch.setText(Bundle.getMessage("ExThrottleEnableRosterSearch"));
092        cbEnableAutoLoad.setText(Bundle.getMessage("ExThrottleEnableAutoSave"));
093        cbHideUndefinedButtons.setText(Bundle.getMessage("ExThrottleHideUndefinedFunctionButtons"));
094        cbHideSpeedSelector.setText(Bundle.getMessage("ExThrottleCheckBoxHideSpeedStepSelector"));
095        cbAddressSelectorShowingAllRosterGroup.setText(Bundle.getMessage("ExThrottleCheckBoxAddressSelectorShowingAllRosterGroup"));
096        cbIgnoreThrottlePosition.setText(Bundle.getMessage("ExThrottleIgnoreThrottlePosition"));
097        cbSaveThrottleOnLayoutSave.setText(Bundle.getMessage("ExThrottleSaveThrottleOnLayoutSave"));
098        cbSilentSteal.setText(Bundle.getMessage("ExThrottleSilentSteal"));
099        cbSilentShare.setText(Bundle.getMessage("ExThrottleSilentShare"));
100
101        ActionListener dirtyAL = (ActionEvent evt) -> {        
102            isDirty = true;
103        };
104        cbUseExThrottle.addActionListener(dirtyAL);
105        cbResizeWinImg.addActionListener(dirtyAL);
106        cbUseToolBar.addActionListener(dirtyAL);
107        cbUseFunctionIcon.addActionListener(dirtyAL);
108        cbUseLargeSpeedSlider.addActionListener(dirtyAL);
109        cbUseRosterImage.addActionListener(dirtyAL);
110        cbEnableRosterSearch.addActionListener(dirtyAL);
111        cbEnableAutoLoad.addActionListener(dirtyAL);
112        cbHideUndefinedButtons.addActionListener(dirtyAL);
113        cbHideSpeedSelector.addActionListener(dirtyAL);
114        cbAddressSelectorShowingAllRosterGroup.addActionListener(dirtyAL);
115        cbIgnoreThrottlePosition.addActionListener(dirtyAL);     
116        cbSaveThrottleOnLayoutSave.addActionListener(dirtyAL);     
117        cbSilentSteal.addActionListener(dirtyAL);     
118        cbSilentShare.addActionListener(dirtyAL);
119        
120        ActionListener al = (ActionEvent evt) -> {
121            checkConsistancy();
122        };
123        cbUseExThrottle.addActionListener(al);
124        cbUseToolBar.addActionListener(al);
125        cbUseRosterImage.addActionListener(al);
126        cbEnableAutoLoad.addActionListener(al);
127        
128        // only the steal checkbox OR the share checkbox should be selected
129        ActionListener stealCheck = (ActionEvent evt) -> {
130            checkStealButtonOk();
131        };
132        ActionListener shareCheck = (ActionEvent evt) -> {
133            checkShareButtonOk();
134        };
135        cbSilentSteal.addActionListener(stealCheck);
136        cbSilentShare.addActionListener(shareCheck);
137
138        ActionListener tal = (ActionEvent evt) -> {
139            checkDefaultThrottleFile();
140        };
141        tfDefaultThrottleLocation.addActionListener(tal);
142        
143        setLayout(new GridBagLayout());
144        
145        GridBagConstraints constraints = new GridBagConstraints();
146        constraints.anchor = GridBagConstraints.WEST;
147        constraints.fill = GridBagConstraints.HORIZONTAL;
148        constraints.gridheight = 1;
149        constraints.gridwidth = 1;
150        constraints.ipadx = 5;
151        constraints.ipady = 5;
152        Insets x0 = new Insets(2, 2, 2, 2);
153        Insets x1 = new Insets(2, 18, 2, 2);
154        Insets x2 = new Insets(2, 32, 2, 2);        
155        constraints.insets = x0;
156        constraints.weightx = 1;
157        constraints.weighty = 1;
158        
159        constraints.gridx = 0;
160        constraints.gridy = 0;
161        this.add(cbUseExThrottle, constraints);
162
163        constraints.gridy++;
164        constraints.insets = x1;
165        this.add(cbSaveThrottleOnLayoutSave, constraints);
166        
167        constraints.gridy++;
168        this.add(cbUseRosterImage, constraints);
169        
170        constraints.gridy++;
171        constraints.insets = x2;
172        this.add(cbResizeWinImg, constraints);
173        
174        constraints.gridy++;
175        constraints.insets = x1;
176        this.add(cbEnableRosterSearch, constraints);
177        
178        constraints.gridy++;
179        this.add(cbEnableAutoLoad, constraints);
180
181        constraints.gridy++;
182        constraints.insets = x2;
183        this.add(cbIgnoreThrottlePosition, constraints);
184        
185        constraints.gridy++;
186        constraints.insets = x1;
187        this.add(cbHideUndefinedButtons, constraints);
188
189        constraints.gridy++;
190        this.add(cbUseToolBar, constraints);
191        
192        constraints.gridy++;
193        this.add(cbHideSpeedSelector, constraints);
194        
195        constraints.gridy++;
196        this.add(cbAddressSelectorShowingAllRosterGroup, constraints);
197
198        constraints.gridy++;
199        this.add(cbUseFunctionIcon, constraints);
200
201        constraints.gridy++;
202        this.add(cbUseLargeSpeedSlider, constraints);
203
204        constraints.gridy++;
205        constraints.insets = x0;
206        this.add(new JSeparator(),constraints );
207        constraints.gridy++;
208        this.add(cbSilentSteal,constraints );
209        
210        constraints.gridy++;
211        this.add(cbSilentShare,constraints );
212        
213        constraints.gridy++;
214        this.add(new JSeparator(),constraints );
215        
216        constraints.gridy++;
217        this.add(defaultThrottleLocationPanel(), constraints);
218                                
219        if (InstanceManager.getNullableDefault(jmri.ThrottleManager.class) != null) {
220            cbSilentSteal.setEnabled(InstanceManager.throttleManagerInstance().enablePrefSilentStealOption());
221            cbSilentShare.setEnabled(InstanceManager.throttleManagerInstance().enablePrefSilentShareOption());
222        }
223        
224    }
225
226    public final void resetComponents(ThrottlesPreferences tp) {
227        if (tp == null) {
228            return;
229        }
230        cbSaveThrottleOnLayoutSave.setSelected(tp.isSavingThrottleOnLayoutSave());
231        cbResizeWinImg.setSelected(tp.isResizingWindow());
232        cbUseToolBar.setSelected(tp.isUsingToolBar());
233        cbUseFunctionIcon.setSelected(tp.isUsingFunctionIcon());
234        cbUseRosterImage.setSelected(tp.isUsingRosterImage());
235        cbUseExThrottle.setSelected(tp.isUsingExThrottle());
236        cbEnableRosterSearch.setSelected(tp.isEnablingRosterSearch());
237        cbEnableAutoLoad.setSelected(tp.isAutoLoading());
238        cbHideUndefinedButtons.setSelected(tp.isHidingUndefinedFuncButt());
239        cbHideSpeedSelector.setSelected(tp.isHidingSpeedStepSelector());
240        cbAddressSelectorShowingAllRosterGroup.setSelected(tp.isAddressSelectorShowingAllRosterGroup());
241        cbIgnoreThrottlePosition.setSelected(tp.isIgnoringThrottlePosition());
242        cbUseLargeSpeedSlider.setSelected(tp.isUsingLargeSpeedSlider());
243        cbSilentSteal.setSelected(tp.isSilentSteal());
244        cbSilentShare.setSelected(tp.isSilentShare());
245        tfDefaultThrottleLocation.setText(tp.getDefaultThrottleFilePath());
246        checkConsistancy();
247        isDirty = false;
248    }
249
250    public ThrottlesPreferences updateThrottlesPreferences(ThrottlesPreferences tp) {
251        tp.setUseExThrottle(cbUseExThrottle.isSelected());
252        tp.setUsingToolBar(cbUseToolBar.isSelected());
253        tp.setUsingFunctionIcon(cbUseFunctionIcon.isSelected());
254        tp.setResizeWindow(cbResizeWinImg.isSelected());
255        tp.setUseRosterImage(cbUseRosterImage.isSelected());
256        tp.setSaveThrottleOnLayoutSave(cbSaveThrottleOnLayoutSave.isSelected());
257        tp.setSilentSteal(cbSilentSteal.isSelected());
258        tp.setSilentShare(cbSilentShare.isSelected());
259        tp.setEnableRosterSearch(cbEnableRosterSearch.isSelected());
260        tp.setAutoLoad(cbEnableAutoLoad.isSelected());
261        tp.setHideUndefinedFuncButt(cbHideUndefinedButtons.isSelected());
262        tp.setHideSpeedStepSelector(cbHideSpeedSelector.isSelected());
263        tp.setAddressSelectorShowingAllRosterGroup(cbAddressSelectorShowingAllRosterGroup.isSelected());
264        tp.setIgnoreThrottlePosition(cbIgnoreThrottlePosition.isSelected());        
265        tp.setUseLargeSpeedSlider(cbUseLargeSpeedSlider.isSelected());
266        tp.setDefaultThrottleFilePath(tfDefaultThrottleLocation.getText());
267        return tp;
268    }
269
270    private void checkConsistancy() {
271        cbSaveThrottleOnLayoutSave.setEnabled(cbUseExThrottle.isSelected());
272        cbUseToolBar.setEnabled(cbUseExThrottle.isSelected());
273        cbUseFunctionIcon.setEnabled(cbUseExThrottle.isSelected());
274        cbEnableRosterSearch.setEnabled(cbUseExThrottle.isSelected());
275        cbEnableAutoLoad.setEnabled(cbUseExThrottle.isSelected());
276        cbUseRosterImage.setEnabled(cbUseExThrottle.isSelected());
277        cbResizeWinImg.setEnabled(cbUseExThrottle.isSelected() && cbUseRosterImage.isSelected());
278        cbHideUndefinedButtons.setEnabled(cbUseExThrottle.isSelected());
279        cbHideSpeedSelector.setEnabled(cbUseExThrottle.isSelected());
280        cbAddressSelectorShowingAllRosterGroup.setEnabled(cbUseExThrottle.isSelected());
281        cbIgnoreThrottlePosition.setEnabled(cbUseExThrottle.isSelected() && cbEnableAutoLoad.isSelected());
282        cbUseLargeSpeedSlider.setEnabled(cbUseExThrottle.isSelected());
283        if (cbUseExThrottle.isSelected()) {
284            if (cbUseToolBar.isSelected()) {
285                cbIgnoreThrottlePosition.setSelected(true);
286                cbIgnoreThrottlePosition.setEnabled(false);
287            }
288        }
289    }
290    
291    private void checkStealButtonOk() {
292        if (cbSilentShare.isSelected()){
293            cbSilentShare.setSelected(false);
294        }
295    }
296    
297    private void checkShareButtonOk() {
298        if (cbSilentSteal.isSelected()){
299            cbSilentSteal.setSelected(false);
300        }
301    }
302    
303    private void checkDefaultThrottleFile() {
304        boolean isBad = false;
305        try {
306            LoadXmlThrottlesLayoutAction.ThrottlePrefs prefs = new LoadXmlThrottlesLayoutAction.ThrottlePrefs();
307            Element root = prefs.rootFromFile(new File (tfDefaultThrottleLocation.getText()));
308            // simply test for root element
309            
310            if (root == null || (root.getChildren("ThrottleFrame").size() != 1)) {
311                isBad = true;
312            }
313        } catch (IOException | JDOMException ex) {            
314            isBad = true;
315        } 
316        if (isBad) {
317            tfDefaultThrottleLocation.setText(null);
318            JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("DefaultThrottleFileNotValid"),
319                Bundle.getMessage("DefaultThrottleFile"), JmriJOptionPane.ERROR_MESSAGE);
320        }
321    }
322    
323    private JPanel defaultThrottleLocationPanel() {        
324        final JFileChooser fileChooser = jmri.jmrit.XmlFile.userFileChooser(Bundle.getMessage("PromptXmlFileTypes"), "xml");
325        fileChooser.setDialogType(JFileChooser.OPEN_DIALOG);
326        fileChooser.setDialogTitle(Bundle.getMessage("MessageSelectDefaultThrottleFile"));
327        
328        JButton bScript = new JButton(Bundle.getMessage("ButtonSetDots"));
329        bScript.addActionListener(new ThrottlesPreferencesUISettingsPane.OpenAction(fileChooser, tfDefaultThrottleLocation));
330
331        JButton jbDefaultThrottleNMRA = new JButton(Bundle.getMessage("DefaultThrottleFileUseNMRA"));        
332        JButton jbDefaultThrottleNEM = new JButton(Bundle.getMessage("DefaultThrottleFileUseNEM"));
333        jbDefaultThrottleNMRA.setToolTipText(Bundle.getMessage("DefaultThrottleFileUseNMRAToolTip"));
334        jbDefaultThrottleNEM.setToolTipText(Bundle.getMessage("DefaultThrottleFileUseNEMToolTip"));
335        jbDefaultThrottleNMRA.addActionListener(e -> tfDefaultThrottleLocation.setText("resources/throttle/NMRA throttle.xml") );
336        jbDefaultThrottleNEM.addActionListener(e -> tfDefaultThrottleLocation.setText("resources/throttle/NEM throttle.xml") );
337        
338        JPanel bp = new JPanel();
339        bp.setLayout(new BoxLayout(bp, BoxLayout.X_AXIS));    
340        bp.add(bScript);
341        bp.add(jbDefaultThrottleNMRA); 
342        bp.add(jbDefaultThrottleNEM);         
343
344        JPanel p = new JPanel(new BorderLayout());        
345        p.add(new JLabel(Bundle.getMessage("DefaultThrottleFile")), BorderLayout.LINE_START);
346        p.add(tfDefaultThrottleLocation, BorderLayout.CENTER);
347        p.add(bp, BorderLayout.LINE_END); 
348
349        return p;
350    }
351
352    boolean isDirty() {
353        return isDirty;        
354    }
355    
356    private class OpenAction extends AbstractAction {
357
358        JFileChooser chooser;
359        JTextField field;
360        private boolean firstOpen = true;
361
362        OpenAction(JFileChooser chooser, JTextField field) {
363            this.chooser = chooser;
364            this.field = field;
365        }
366
367        @Override
368        public void actionPerformed(ActionEvent e) {
369            if ( firstOpen ) {
370                chooser.setCurrentDirectory(new File(ThrottleUICore.getDefaultThrottleFolder()));
371                firstOpen = false;
372            }
373            // get the file
374            int retVal = chooser.showOpenDialog(field);
375            if ( (retVal != JFileChooser.APPROVE_OPTION) || (chooser.getSelectedFile() == null) ) {
376                return; // cancelled
377            }
378            field.setText(chooser.getSelectedFile().toString());
379            checkDefaultThrottleFile();
380        }
381    }
382
383    // private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ThrottlesPreferencesUISettingsPane.class);
384
385}