001package jmri.jmrit.vsdecoder.swing;
002
003import java.awt.GridBagConstraints;
004import java.awt.GridBagLayout;
005import java.awt.Insets;
006import java.beans.PropertyChangeEvent;
007import java.beans.PropertyChangeListener;
008import java.net.URI;
009import java.net.URISyntaxException;
010import javax.swing.BorderFactory;
011import javax.swing.BoxLayout;
012import javax.swing.ButtonGroup;
013import javax.swing.JButton;
014import javax.swing.JFileChooser;
015import javax.swing.JFrame;
016import javax.swing.JLabel;
017import javax.swing.JPanel;
018import javax.swing.JRadioButton;
019import javax.swing.filechooser.FileNameExtensionFilter;
020import jmri.jmrit.vsdecoder.VSDecoderManager;
021import jmri.jmrit.vsdecoder.VSDecoderPreferences;
022import jmri.util.FileUtil;
023import org.slf4j.Logger;
024import org.slf4j.LoggerFactory;
025
026/**
027 * Pane to show VSDecoder Preferences.
028 *
029 * <hr>
030 * This file is part of JMRI.
031 * <p>
032 * JMRI is free software; you can redistribute it and/or modify it under 
033 * the terms of version 2 of the GNU General Public License as published 
034 * by the Free Software Foundation. See the "COPYING" file for a copy
035 * of this license.
036 * <p>
037 * JMRI is distributed in the hope that it will be useful, but WITHOUT 
038 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
039 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License 
040 * for more details.
041 *
042 * @author Mark Underwood Copyright (C) 2011
043 */
044class VSDecoderPreferencesPane extends javax.swing.JPanel implements PropertyChangeListener {
045
046    private javax.swing.JCheckBox cbAutoStartEngine;
047    private javax.swing.JCheckBox cbAutoLoadVSDFile;
048    private javax.swing.JTextField tfDefaultVSDFilePath;
049    private javax.swing.JTextField tfDefaultVSDFileName;
050    private javax.swing.JLabel labelDefaultVSDFilePath;
051    private javax.swing.JLabel labelDefaultVSDFileName;
052    private javax.swing.JRadioButton audioModeRoomButton;
053    private javax.swing.JRadioButton audioModeHeadphoneButton;
054    private javax.swing.ButtonGroup audioModeGroup;
055
056    private javax.swing.JLabel labelApplyWarning;
057    private javax.swing.JButton jbApply;
058    private javax.swing.JButton jbCancel;
059    private javax.swing.JButton jbSave;
060    private JFrame m_container = null;
061
062    /**
063     * Creates new form VSDecoderPreferencesPane
064     * @param tp Preferences information
065     */
066    public VSDecoderPreferencesPane(VSDecoderPreferences tp) {
067        initComponents();
068        setComponents(tp);
069        checkConsistency();
070        tp.addPropertyChangeListener(this);
071    }
072
073    public VSDecoderPreferencesPane() {
074        this(VSDecoderManager.instance().getVSDecoderPreferences());
075    }
076
077    private GridBagConstraints setConstraints(Insets i, int x, int y, int width, int fill) {
078        GridBagConstraints gbc = new GridBagConstraints();
079        gbc.insets = i;
080        gbc.gridx = x;
081        gbc.gridy = y;
082        gbc.gridwidth = width;
083        gbc.anchor = GridBagConstraints.LINE_START;
084        gbc.fill = fill;
085        return gbc;
086    }
087
088    private void initComponents() {
089
090        JPanel prefsPane = new JPanel();
091        JPanel controlPane = new JPanel();
092
093        this.setLayout(new GridBagLayout());
094        this.setBorder(BorderFactory.createEmptyBorder());
095
096        jbCancel = new javax.swing.JButton();
097        jbSave = new javax.swing.JButton();
098        jbApply = new javax.swing.JButton();
099
100        labelApplyWarning = new javax.swing.JLabel();
101        labelDefaultVSDFilePath = new javax.swing.JLabel();
102        labelDefaultVSDFileName = new javax.swing.JLabel();
103
104        cbAutoStartEngine = new javax.swing.JCheckBox();
105        cbAutoLoadVSDFile = new javax.swing.JCheckBox();
106        tfDefaultVSDFilePath = new javax.swing.JTextField(40);
107        tfDefaultVSDFileName = new javax.swing.JTextField(40);
108        JButton jbPathBrowse = new javax.swing.JButton(Bundle.getMessage("Browse"));
109        jbPathBrowse.addActionListener(new java.awt.event.ActionListener() {
110            @Override
111            public void actionPerformed(java.awt.event.ActionEvent evt) {
112                jbPathBrowseActionPerformed(evt);
113            }
114        });
115        JButton jbFileBrowse = new javax.swing.JButton(Bundle.getMessage("Browse"));
116        jbFileBrowse.addActionListener(new java.awt.event.ActionListener() {
117            @Override
118            public void actionPerformed(java.awt.event.ActionEvent evt) {
119                jbFileBrowseActionPerformed(evt);
120            }
121        });
122
123        // Audio Mode
124        audioModeRoomButton = new JRadioButton(Bundle.getMessage("ButtonAudioModeRoom"));
125        audioModeHeadphoneButton = new JRadioButton(Bundle.getMessage("ButtonAudioModeHeadphone"));
126        audioModeGroup = new ButtonGroup();
127        audioModeGroup.add(audioModeRoomButton);
128        audioModeGroup.add(audioModeHeadphoneButton);
129        JPanel amPanel = new JPanel();
130        amPanel.setLayout(new BoxLayout(amPanel, BoxLayout.LINE_AXIS));
131        amPanel.add(new JLabel("Audio Mode:"));
132        amPanel.add(audioModeRoomButton);
133        amPanel.add(audioModeHeadphoneButton);
134
135        // Get label strings from the resource bundle and assign it.
136        cbAutoStartEngine.setText(Bundle.getMessage("AutoStartEngine"));
137        cbAutoLoadVSDFile.setText(Bundle.getMessage("AutoLoadVSDFile"));
138        tfDefaultVSDFilePath.setColumns(30);
139        tfDefaultVSDFilePath.setColumns(30);
140        labelDefaultVSDFilePath.setText(Bundle.getMessage("DefaultVSDFilePath"));
141        labelDefaultVSDFileName.setText(Bundle.getMessage("DefaultVSDFileName"));
142        labelApplyWarning.setText(Bundle.getMessage("ExVSDecoderLabelApplyWarning"));
143
144        // Set action listener to check consistency when the user makes changes.
145        java.awt.event.ActionListener al = new java.awt.event.ActionListener() {
146            @Override
147            public void actionPerformed(java.awt.event.ActionEvent evt) {
148                checkConsistency();
149            }
150        };
151        cbAutoStartEngine.addActionListener(al);
152        cbAutoLoadVSDFile.addActionListener(al);
153        tfDefaultVSDFilePath.addActionListener(al);
154        tfDefaultVSDFileName.addActionListener(al);
155
156        // Set action listeners for save / cancel / reset buttons
157        jbSave.setText(Bundle.getMessage("ButtonSave"));
158        jbSave.addActionListener(new java.awt.event.ActionListener() {
159            @Override
160            public void actionPerformed(java.awt.event.ActionEvent evt) {
161                jbSaveActionPerformed(evt);
162            }
163        });
164        jbSave.setVisible(false);
165
166        jbCancel.setText(Bundle.getMessage("ButtonCancel"));
167        jbCancel.addActionListener(new java.awt.event.ActionListener() {
168            @Override
169            public void actionPerformed(java.awt.event.ActionEvent evt) {
170                jbCancelActionPerformed(evt);
171            }
172        });
173
174        jbApply.setText(Bundle.getMessage("ButtonApply"));
175        jbApply.addActionListener(new java.awt.event.ActionListener() {
176            @Override
177            public void actionPerformed(java.awt.event.ActionEvent evt) {
178                jbApplyActionPerformed(evt);
179            }
180        });
181
182        prefsPane.setLayout(new GridBagLayout());
183        prefsPane.setBorder(BorderFactory.createEmptyBorder());
184        controlPane.setLayout(new GridBagLayout());
185        controlPane.setBorder(BorderFactory.createEmptyBorder());
186
187        prefsPane.add(cbAutoStartEngine, setConstraints(new Insets(2, 10, 2, 2), 0, 0, 2, GridBagConstraints.NONE)); //1
188        prefsPane.add(cbAutoLoadVSDFile, setConstraints(new Insets(2, 10, 2, 2), 0, 1, 2, GridBagConstraints.NONE)); //2
189
190        prefsPane.add(labelDefaultVSDFilePath, setConstraints(new Insets(2, 10, 2, 2), 0, 2, 1, GridBagConstraints.NONE)); //5
191        prefsPane.add(tfDefaultVSDFilePath, setConstraints(new Insets(2, 10, 2, 2), 1, 2, 3, GridBagConstraints.HORIZONTAL)); //3
192        prefsPane.add(jbPathBrowse, setConstraints(new Insets(2, 2, 2, 2), 5, 2, 1, GridBagConstraints.NONE)); //3
193
194        prefsPane.add(labelDefaultVSDFileName, setConstraints(new Insets(2, 10, 2, 2), 0, 3, 1, GridBagConstraints.NONE)); //6
195        prefsPane.add(tfDefaultVSDFileName, setConstraints(new Insets(2, 10, 2, 2), 1, 3, 2, GridBagConstraints.HORIZONTAL)); //4
196        prefsPane.add(jbFileBrowse, setConstraints(new Insets(2, 2, 2, 2), 5, 3, 1, GridBagConstraints.NONE)); //3
197
198        prefsPane.add(amPanel, setConstraints(new Insets(2, 10, 2, 2), 0, 6, 2, GridBagConstraints.HORIZONTAL));
199
200        controlPane.add(jbSave, setConstraints(new Insets(5, 3, 5, 2), 2, 100, 1, GridBagConstraints.NONE)); //7
201        controlPane.add(jbCancel, setConstraints(new Insets(5, 3, 5, 2), 0, 100, 1, GridBagConstraints.NONE)); //8
202        controlPane.add(jbApply, setConstraints(new Insets(5, 3, 5, 5), 1, 100, 1, GridBagConstraints.NONE)); //9
203
204        this.add(prefsPane, setConstraints(new Insets(2, 2, 2, 2), 0, 0, 1, GridBagConstraints.NONE));
205        this.add(controlPane, setConstraints(new Insets(2, 2, 2, 2), 0, 1, 1, GridBagConstraints.NONE));
206
207        this.setVisible(true);
208    }
209
210    private void setComponents(VSDecoderPreferences tp) {
211        if (tp == null) {
212            return;
213        }
214        cbAutoStartEngine.setSelected(tp.isAutoStartingEngine());
215        cbAutoLoadVSDFile.setSelected(tp.isAutoLoadingDefaultVSDFile());
216        tfDefaultVSDFilePath.setText(tp.getDefaultVSDFilePath());
217        tfDefaultVSDFileName.setText(tp.getDefaultVSDFileName());
218        switch (tp.getAudioMode()) {
219            case HEADPHONES:
220                audioModeHeadphoneButton.setSelected(true);
221                break;
222            case ROOM_AMBIENT:
223            default:
224                audioModeRoomButton.setSelected(true);
225                break;
226        }
227    }
228
229    private VSDecoderPreferences getVSDecoderPreferences() {
230        VSDecoderPreferences tp = new VSDecoderPreferences();
231        tp.setAutoStartEngine(cbAutoStartEngine.isSelected());
232        tp.setAutoLoadDefaultVSDFile(cbAutoLoadVSDFile.isSelected());
233        tp.setDefaultVSDFilePath(tfDefaultVSDFilePath.getText());
234        tp.setDefaultVSDFileName(tfDefaultVSDFileName.getText());
235        tp.setListenerPosition(VSDecoderManager.instance().getVSDecoderPreferences().getListenerPosition());
236        if (audioModeRoomButton.isSelected()) {
237            tp.setAudioMode(VSDecoderPreferences.AudioMode.ROOM_AMBIENT);
238        } else if (audioModeHeadphoneButton.isSelected()) {
239            tp.setAudioMode(VSDecoderPreferences.AudioMode.HEADPHONES);
240        }
241        tp.setMasterVolume(VSDecoderManager.instance().getVSDecoderPreferences().getMasterVolume());
242        return tp;
243    }
244
245    private void checkConsistency() {
246        /*
247         cbSaveThrottleOnLayoutSave.setEnabled( cbUseExThrottle.isSelected() );
248         cbUseToolBar.setEnabled( cbUseExThrottle.isSelected() );
249         cbUseFunctionIcon.setEnabled( cbUseExThrottle.isSelected() );
250         cbEnableRosterSearch.setEnabled( cbUseExThrottle.isSelected() );
251         cbEnableAutoLoad.setEnabled( cbUseExThrottle.isSelected() );
252         cbUseRosterImage.setEnabled( cbUseExThrottle.isSelected() );
253         cbResizeWinImg.setEnabled( cbUseExThrottle.isSelected()  &&  cbUseRosterImage.isSelected() );
254         cbHideUndefinedButtons.setEnabled( cbUseExThrottle.isSelected() );
255         cbIgnoreThrottlePosition.setEnabled( cbUseExThrottle.isSelected() && cbEnableAutoLoad.isSelected() );
256         cbCleanOnDispose.setEnabled( cbUseExThrottle.isSelected() );
257         if ( cbUseExThrottle.isSelected() ) {
258         if ( cbUseToolBar.isSelected() ) {
259         cbIgnoreThrottlePosition.setSelected( true );
260         cbIgnoreThrottlePosition.setEnabled( false );
261         }
262         }
263         */
264    }
265
266    private void jbPathBrowseActionPerformed(java.awt.event.ActionEvent evt) {
267        // Browse for a path.  Update the UI 
268        // use the path currently in the window text field, if possible.
269        String path;
270        if (tfDefaultVSDFilePath.getText() != null) {
271            path = tfDefaultVSDFilePath.getText();
272        } else {
273            path = VSDecoderManager.instance().getVSDecoderPreferences().getDefaultVSDFilePath();
274        }
275        final JFileChooser fc = new JFileChooser(path);
276        fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
277        int rv = fc.showOpenDialog(this);
278        if (rv == JFileChooser.APPROVE_OPTION) {
279            try {
280                tfDefaultVSDFilePath.setText(fc.getSelectedFile().getCanonicalPath());
281            } catch (java.io.IOException e) {
282                // do nothing.
283            }
284        }
285    }
286
287    private void jbFileBrowseActionPerformed(java.awt.event.ActionEvent evt) {
288        // Browse for a file. Update the UI
289        String path;
290        if (tfDefaultVSDFilePath.getText() != null) {
291            path = tfDefaultVSDFilePath.getText();
292        } else {
293            path = VSDecoderManager.instance().getVSDecoderPreferences().getDefaultVSDFilePath();
294        }
295        final JFileChooser fc = new JFileChooser(path);
296        fc.setFileFilter(new FileNameExtensionFilter(Bundle.getMessage("LoadVSDFileChooserFilterLabel"), "vsd", "zip")); // NOI18N
297        fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
298        int rv = fc.showOpenDialog(this);
299        if (rv == JFileChooser.APPROVE_OPTION) {
300            try {
301                URI base = null;
302                if (tfDefaultVSDFilePath.getText() != null) {
303                    base = FileUtil.findURL(tfDefaultVSDFilePath.getText()).toURI();
304                } else {
305                    base = FileUtil.findURL(VSDecoderManager.instance().getVSDecoderPreferences().getDefaultVSDFilePath()).toURI();
306                }
307                URI absolute = fc.getSelectedFile().toURI();
308                URI relative = base.relativize(absolute);
309                log.debug("URI absolute = {} relative = {}", absolute.toString(), relative.toString());
310
311                tfDefaultVSDFileName.setText(relative.getPath());
312            } catch (URISyntaxException ex) {
313                log.warn("Unable to get URI for {}", path, ex);
314            }
315        }
316    }
317
318    private void jbApplyActionPerformed(java.awt.event.ActionEvent evt) {
319        VSDecoderManager.instance().getVSDecoderPreferences().set(getVSDecoderPreferences());
320    }
321
322    public void jbSaveActionPerformed(java.awt.event.ActionEvent evt) {
323        VSDecoderManager.instance().getVSDecoderPreferences().set(getVSDecoderPreferences());
324        VSDecoderManager.instance().getVSDecoderPreferences().save();
325        if (m_container != null) {
326            VSDecoderManager.instance().getVSDecoderPreferences().removePropertyChangeListener(this);
327            m_container.setVisible(false); // should do with events...
328            m_container.dispose();
329        }
330    }
331
332    private void jbCancelActionPerformed(java.awt.event.ActionEvent evt) {
333        setComponents(VSDecoderManager.instance().getVSDecoderPreferences());
334        checkConsistency();
335        if (m_container != null) {
336            VSDecoderManager.instance().getVSDecoderPreferences().removePropertyChangeListener(this);
337            m_container.setVisible(false); // should do with events...
338            m_container.dispose();
339        }
340    }
341
342    public void setContainer(JFrame f) {
343        m_container = f;
344        jbSave.setVisible(true);
345        jbCancel.setText(Bundle.getMessage("ButtonCancel"));
346    }
347
348    @Override
349    public void propertyChange(PropertyChangeEvent evt) {
350        if ((evt == null) || (evt.getPropertyName() == null)) {
351            return;
352        }
353        if (evt.getPropertyName().equals("VSDecoderPreferences")) {
354            if ((evt.getNewValue() != null) && (evt.getNewValue() instanceof VSDecoderPreferences)) {
355                setComponents((VSDecoderPreferences) evt.getNewValue());
356                checkConsistency();
357            }
358        }
359    }
360
361    private static final Logger log = LoggerFactory.getLogger(VSDecoderPreferencesPane.class);
362
363}