001package jmri.jmrit.audio.swing;
002
003import java.awt.FlowLayout;
004import java.awt.event.ActionEvent;
005import java.io.File;
006
007import javax.swing.BorderFactory;
008import javax.swing.BoxLayout;
009import javax.swing.JButton;
010import javax.swing.JCheckBox;
011import javax.swing.JFileChooser;
012import javax.swing.JLabel;
013import javax.swing.JPanel;
014import javax.swing.JSpinner;
015import javax.swing.JTextField;
016import javax.swing.SpinnerNumberModel;
017import javax.swing.event.ChangeEvent;
018import javax.swing.filechooser.FileNameExtensionFilter;
019
020import jmri.Audio;
021import jmri.AudioException;
022import jmri.AudioManager;
023import jmri.InstanceManager;
024import jmri.jmrit.audio.AudioBuffer;
025import jmri.jmrit.beantable.AudioTableAction.AudioTableDataModel;
026import jmri.util.FileUtil;
027import jmri.util.swing.JmriJOptionPane;
028
029/**
030 * Defines a GUI to edit AudioBuffer objects.
031 *
032 * <hr>
033 * This file is part of JMRI.
034 * <p>
035 * JMRI is free software; you can redistribute it and/or modify it under the
036 * terms of version 2 of the GNU General Public License as published by the Free
037 * Software Foundation. See the "COPYING" file for a copy of this license.
038 * <p>
039 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY
040 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
041 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
042 *
043 * @author Matthew Harris copyright (c) 2009
044 */
045public class AudioBufferFrame extends AbstractAudioFrame {
046
047    private static int counter = 1;
048
049    private boolean newBuffer;
050
051    private final Object lock = new Object();
052
053    // UI components for Add/Edit Buffer
054    JLabel urlLabel = new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("LabelURL")));
055    JTextField url = new JTextField(40);
056    JButton buttonBrowse = new JButton("...");
057    JCheckBox stream = new JCheckBox(Bundle.getMessage("LabelStream"));
058    // JLabel formatLabel = new JLabel(Bundle.getMessage("LabelFormat"));
059    // JTextField format = new JTextField(20);
060    JLabel loopStartLabel = new JLabel(Bundle.getMessage("LabelLoopStart"));
061    JSpinner loopStart = new JSpinner();
062    JLabel loopEndLabel = new JLabel(Bundle.getMessage("LabelLoopEnd"));
063    JSpinner loopEnd = new JSpinner();
064    JFileChooser fileChooser;
065    // AudioWaveFormPanel waveForm = new AudioWaveFormPanel();
066
067    private final static String PREFIX = "IAB";
068
069//    @SuppressWarnings("OverridableMethodCallInConstructor")
070    public AudioBufferFrame(String title, AudioTableDataModel model) {
071        super(title, model);
072        layoutFrame();
073
074        // For now, disable editing of loop points
075        // TODO: enable editing of looping points
076        loopStart.setEnabled(false);
077        loopStartLabel.setEnabled(false);
078        loopEnd.setEnabled(false);
079        loopEndLabel.setEnabled(false);
080    }
081
082    @Override
083    public void layoutFrame() {
084        super.layoutFrame();
085        JPanel p;
086
087        JPanel p2;
088        p = new JPanel();
089        p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
090        p.setBorder(BorderFactory.createCompoundBorder(
091                BorderFactory.createTitledBorder(Bundle.getMessage("LabelSample")),
092                BorderFactory.createEmptyBorder(5, 5, 5, 5)));
093        p2 = new JPanel();
094        p2.setLayout(new FlowLayout());
095        p2.add(urlLabel);
096        p2.add(url);
097        buttonBrowse.addActionListener(this::browsePressed);
098        buttonBrowse.setToolTipText(Bundle.getMessage("ToolTipButtonBrowse"));
099        p2.add(buttonBrowse);
100        p.add(p2);
101        p2 = new JPanel();
102        p2.setLayout(new FlowLayout());
103        p2.add(stream);
104        p.add(p2);
105//        p2 = new JPanel(); p2.setLayout(new FlowLayout());
106//        p2.add(formatLabel);
107//        p2.add(format);
108//        p.add(p2);
109        main.add(p);
110
111//        p = new JPanel();
112//        p.setBorder(BorderFactory.createCompoundBorder(
113//                        BorderFactory.createTitledBorder("Waveforms"),
114//                        BorderFactory.createEmptyBorder(5, 5, 5, 5)));
115//
116//        JLabel label = new JLabel("Whole sample");
117//        label.setUI(new VerticalLabelUI());
118//        p.add(label);
119//
120//        waveForm.setPreferredSize(new Dimension(400, 120));
121//        p.add(waveForm);
122//
123//        label = new JLabel("Loop-point detail");
124//        label.setUI(new VerticalLabelUI());
125//        p.add(label);
126//
127//        AudioWaveFormPanel waveFormLoop = new AudioWaveFormPanel();
128//        waveFormLoop.setPreferredSize(new Dimension(80, 120));
129//        p.add(waveFormLoop);
130//
131//        main.add(p);
132//
133        p = new JPanel();
134        p.setLayout(new FlowLayout());
135        p.setBorder(BorderFactory.createCompoundBorder(
136                BorderFactory.createTitledBorder(Bundle.getMessage("LabelLoopPoints")),
137                BorderFactory.createEmptyBorder(5, 5, 5, 5)));
138        p.add(loopStartLabel);
139        loopStart.setPreferredSize(new JTextField(8).getPreferredSize());
140        loopStart.setModel(
141                new SpinnerNumberModel(Long.valueOf(0), Long.valueOf(0), Long.valueOf(Long.MAX_VALUE), Long.valueOf(1)));
142        loopStart.addChangeListener((ChangeEvent e) -> {
143            loopEnd.setValue(
144                    ((Long) loopStart.getValue()
145                            < (Long) loopEnd.getValue())
146                            ? loopEnd.getValue()
147                            : loopStart.getValue());
148        });
149        p.add(loopStart);
150        p.add(loopEndLabel);
151        loopEnd.setPreferredSize(new JTextField(8).getPreferredSize());
152        loopEnd.setModel(
153                new SpinnerNumberModel(Long.valueOf(0), Long.valueOf(0), Long.valueOf(Long.MAX_VALUE), Long.valueOf(1)));
154        loopEnd.addChangeListener((ChangeEvent e) -> {
155            loopStart.setValue(
156                    ((Long) loopEnd.getValue()
157                            < (Long) loopStart.getValue())
158                            ? loopEnd.getValue()
159                            : loopStart.getValue());
160        });
161        p.add(loopEnd);
162        main.add(p);
163
164        p = new JPanel();
165        JButton apply;
166        p.add(apply = new JButton(Bundle.getMessage("ButtonApply")));
167        apply.addActionListener((ActionEvent e) -> {
168            applyPressed(e);
169        });
170        JButton ok;
171        p.add(ok = new JButton(Bundle.getMessage("ButtonOK")));
172        ok.addActionListener((ActionEvent e) -> {
173            if (applyPressed(e)) {
174                frame.dispose();
175            }
176        });
177        JButton cancel;
178        p.add(cancel = new JButton(Bundle.getMessage("ButtonCancel")));
179        cancel.addActionListener((ActionEvent e) -> {
180            frame.dispose();
181        });
182        frame.getContentPane().add(p);
183    }
184
185    /**
186     * Populate the Edit Buffer frame with default values.
187     */
188    @Override
189    public void resetFrame() {
190        synchronized (lock) {
191            sysName.setText(PREFIX + nextCounter()); // NOI18N
192        }
193        userName.setText(null);
194        url.setText(null);
195        // format.setText(null);
196        stream.setSelected(false);
197        stream.setEnabled(false); //(true);
198        loopStart.setValue(Long.valueOf(0));
199        loopEnd.setValue(Long.valueOf(0));
200
201        this.newBuffer = true;
202    }
203
204    /**
205     * Populate the Edit Buffer frame with current values.
206     */
207    @Override
208    public void populateFrame(Audio a) {
209        if (!(a instanceof AudioBuffer)) {
210            throw new IllegalArgumentException(a.getSystemName() + " is not an AudioBuffer object");
211        }
212        super.populateFrame(a);
213        AudioBuffer b = (AudioBuffer) a;
214        url.setText(b.getURL());
215        // format.setText(b.toString());
216        stream.setSelected(b.isStreamed());
217        stream.setEnabled(false); //(!b.isStreamedForced());
218        loopStart.setValue(b.getStartLoopPoint());
219        loopEnd.setValue(b.getEndLoopPoint());
220        loopStart.setEnabled(true);
221        loopStartLabel.setEnabled(true);
222        loopEnd.setEnabled(true);
223        loopEndLabel.setEnabled(true);
224
225        this.newBuffer = false;
226    }
227
228    void browsePressed(ActionEvent e) {
229        if (fileChooser == null) {
230            fileChooser = new jmri.util.swing.JmriJFileChooser("resources" + File.separator + "sounds" + File.separator); // NOI18N
231            fileChooser.setFileFilter(new FileNameExtensionFilter("Audio Files (*.wav)", "wav")); // NOI18N
232        }
233
234        // Show dialog
235        fileChooser.rescanCurrentDirectory();
236        int retValue = fileChooser.showOpenDialog(this);
237
238        // Process selection
239        if (retValue == JFileChooser.APPROVE_OPTION) {
240            File file = fileChooser.getSelectedFile();
241            String fileName = FileUtil.getPortableFilename(file);
242            if (!url.getText().equals(fileName)) {
243                url.setText(fileName);
244//                try {
245//                    WaveFileReader wfr = new WaveFileReader(FileUtil.getExternalFilename(fileName));
246//                    JmriJOptionPane.showMessageDialog(null, wfr.toString(), wfr.toString(), JmriJOptionPane.INFORMATION_MESSAGE);
247//                } catch (AudioException ex) {
248//                    JmriJOptionPane.showMessageDialog(this, ex.getMessage(), rba.getString("TitleReadError"), JmriJOptionPane.ERROR_MESSAGE);
249//                }
250            }
251        }
252    }
253
254    boolean applyPressed(ActionEvent e) {
255        String sName = sysName.getText();
256        if (entryError(sName, PREFIX, "" + counter)) {
257            return false;
258        }
259        String user = userName.getText();
260        if (user.equals("")) {
261            user = null;
262        }
263        AudioBuffer b;
264        try {
265            AudioManager am = InstanceManager.getDefault(jmri.AudioManager.class);
266            if (newBuffer && am.getBySystemName(sName) != null) {
267                throw new AudioException(Bundle.getMessage("DuplicateSystemName"));
268            }
269            try {
270                b = (AudioBuffer) am.provideAudio(sName);
271            } catch (IllegalArgumentException ex) {
272                throw new AudioException(Bundle.getMessage("ProblemCreatingBuffer"));
273            }
274            if ((user != null) && newBuffer && am.getByUserName(user) != null) {
275                am.deregister(b);
276                synchronized (lock) {
277                    prevCounter();
278                }
279                throw new AudioException(Bundle.getMessage("DuplicateUserName"));
280            }
281            b.setUserName(user);
282            b.setStreamed(stream.isSelected());
283            if (newBuffer || !b.getURL().equals(url.getText())) {
284                b.setURL(url.getText());
285                log.debug("After load, end loop point = {}", b.getEndLoopPoint());
286                //b.setStartLoopPoint((Long)loopStart.getValue());
287                //b.setEndLoopPoint((Long)loopEnd.getValue());
288            } else {
289                if (!b.getURL().equals(url.getText())) {
290                    log.debug("Sound changed from: {}", b.getURL());
291                    b.setURL(url.getText());
292                }
293            }
294
295            // Update streaming checkbox if necessary
296            stream.setSelected(b.isStreamed());
297            stream.setEnabled(false); //(!b.isStreamedForced());
298
299            // Notify changes
300            model.fireTableDataChanged();
301        } catch (AudioException ex) {
302            JmriJOptionPane.showMessageDialog(this, ex.getMessage(),
303                Bundle.getMessage("AudioCreateErrorTitle"), JmriJOptionPane.ERROR_MESSAGE);
304            return false;
305        }
306        newBuffer = false;  // If the user presses Apply, the dialog stays visible.
307        return true;
308    }
309
310    private static int nextCounter() {
311        return counter++;
312    }
313
314    private static void prevCounter() {
315        counter--;
316    }
317
318    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AudioBufferFrame.class);
319
320}