001package jmri.jmrit.vsdecoder.swing;
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.awt.event.KeyEvent;
010import java.beans.PropertyChangeEvent;
011import java.beans.PropertyChangeListener;
012import java.util.ArrayList;
013import java.util.HashMap;
014import java.util.Map;
015import javax.swing.BorderFactory;
016import javax.swing.Box;
017import javax.swing.BoxLayout;
018import javax.swing.JButton;
019import javax.swing.JComponent;
020import javax.swing.JLabel;
021import javax.swing.JPanel;
022import javax.swing.SwingConstants;
023import javax.swing.border.Border;
024import jmri.jmrit.vsdecoder.EngineSoundEvent;
025import jmri.jmrit.vsdecoder.SoundEvent;
026import jmri.jmrit.vsdecoder.VSDConfig;
027
028/**
029 * New GUI pane for a Virtual Sound Decoder (VSDecoder).
030 *
031 * <hr>
032 * This file is part of JMRI.
033 * <p>
034 * JMRI is free software; you can redistribute it and/or modify it under
035 * the terms of version 2 of the GNU General Public License as published
036 * by the Free Software Foundation. See the "COPYING" file for a copy
037 * of this license.
038 * <p>
039 * JMRI is distributed in the hope that it will be useful, but WITHOUT
040 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
041 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
042 * for more details.
043 *
044 * @author Mark Underwood Copyright (C) 2011
045 */
046public class VSDControl extends JPanel {
047
048    public static final String OPTION_CHANGE = "OptionChange"; // NOI18N
049    public static final String DELETE = "DeleteDecoder"; // NOI18N
050
051    private static final int NUMBER_SOUNDS = 8;
052
053    // Map of Mnemonic KeyEvent values to GUI Components
054    private static final Map<String, Integer> Mnemonics = new HashMap<>();
055
056    static {
057        // GUI buttons
058        Mnemonics.put("OptionButton", KeyEvent.VK_O);
059        Mnemonics.put("DeleteButton", KeyEvent.VK_D);
060    }
061
062    String address;
063
064    Border tb;
065    JLabel addressLabel;
066    JButton optionButton;
067    JButton deleteButton;
068
069    JPanel soundsPanel;
070    JPanel configPanel;
071
072    private VSDConfig config;
073
074    /**
075     * Constructor
076     */
077    public VSDControl() {
078        super();
079        initComponents("");
080    }
081
082    /**
083     * Constructor
084     *
085     * @param title (String) : Window title
086     */
087    public VSDControl(String title) {
088        super();
089        address = title;
090        config = new VSDConfig();
091        initComponents(title);
092    }
093
094    public VSDControl(VSDConfig c) {
095        super();
096        config = c;
097        address = config.getLocoAddress().toString();
098        initComponents(address);
099    }
100
101    static public JPanel generateBlank() {
102        VSDControl temp = new VSDControl("");
103        JLabel jl = new JLabel(Bundle.getMessage("BlankVSDControlLabel"));
104        jl.setMinimumSize(temp.getPreferredSize());
105        jl.setPreferredSize(temp.getPreferredSize());
106        jl.setHorizontalAlignment(SwingConstants.CENTER);
107        JPanel jp = new JPanel();
108        jp.setLayout(new BorderLayout());
109        jp.add(jl, BorderLayout.CENTER);
110        jl.setMinimumSize(temp.getPreferredSize());
111        jp.setPreferredSize(temp.getPreferredSize());
112        jp.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createRaisedBevelBorder(),
113                BorderFactory.createLoweredBevelBorder()));
114        return jp;
115    }
116
117    private GridBagConstraints setConstraints(int x, int y) {
118        return setConstraints(x, y, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 2), GridBagConstraints.LINE_START);
119    }
120
121    /*
122     private GridBagConstraints setConstraints(int x, int y, int fill) {
123         return setConstraints(x, y, fill, new Insets(2,2,2,2), GridBagConstraints.LINE_START);
124     }
125     */
126
127    private GridBagConstraints setConstraints(int x, int y, int fill, Insets ins, int anchor) {
128        GridBagConstraints gbc1 = new GridBagConstraints();
129        gbc1.insets = ins;
130        gbc1.gridx = x;
131        gbc1.gridy = y;
132        gbc1.weightx = 100.0;
133        gbc1.weighty = 100.0;
134        gbc1.gridwidth = 1;
135        gbc1.anchor = anchor;
136        gbc1.fill = fill;
137
138        return gbc1;
139    }
140
141    /**
142     * Initialize the GUI components.
143     * @param title future title, not yet coded..
144     */
145    protected void initComponents(String title) {
146        // Create the border.
147        // Could make this a titled border with the loco address as the title...
148        //tb = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED);
149        tb = BorderFactory.createCompoundBorder(BorderFactory.createRaisedBevelBorder(),
150                BorderFactory.createLoweredBevelBorder());
151
152        this.setBorder(tb);
153
154        this.setLayout(new GridBagLayout());
155
156        // Create the buttons and slider
157        soundsPanel = new JPanel();
158        soundsPanel.setLayout(new GridBagLayout());
159        addressLabel = new JLabel(address);
160
161        configPanel = new JPanel();
162        configPanel.setLayout(new BoxLayout(configPanel, BoxLayout.PAGE_AXIS));
163        optionButton = new JButton(Bundle.getMessage("OptionsButtonLabel"));
164        deleteButton = new JButton(Bundle.getMessage("ButtonDelete"));
165        configPanel.add(Box.createHorizontalGlue());
166        configPanel.add(optionButton);
167        optionButton.setToolTipText(Bundle.getMessage("MgrOptionButtonToolTip"));
168        optionButton.setMnemonic(Mnemonics.get("OptionButton"));
169        configPanel.add(Box.createHorizontalGlue());
170        configPanel.add(deleteButton);
171        deleteButton.setToolTipText(Bundle.getMessage("MgrDeleteButtonToolTip"));
172        deleteButton.setMnemonic(Mnemonics.get("DeleteButton"));
173
174        JPanel alPanel = new JPanel();
175        alPanel.setLayout(new BoxLayout(alPanel, BoxLayout.PAGE_AXIS));
176        alPanel.add(addressLabel);
177        alPanel.add(new JLabel(config.getProfileName()));
178
179        // Add them to the panel
180        this.add(alPanel, new GridBagConstraints(0, 0, 1, 2, 100.0, 100.0,
181                GridBagConstraints.LINE_START,
182                GridBagConstraints.NONE,
183                new Insets(2, 2, 2, 2),
184                0, 0));
185        this.add(soundsPanel, setConstraints(2, 0));
186        this.add(configPanel, setConstraints(3, 0));
187
188        optionButton.addActionListener(new ActionListener() {
189            @Override
190            public void actionPerformed(ActionEvent e) {
191                optionButtonPressed(e);
192            }
193        });
194        deleteButton.addActionListener(new ActionListener() {
195            @Override
196            public void actionPerformed(ActionEvent e) {
197                deleteButtonPressed(e);
198            }
199        });
200
201        this.setVisible(true);
202    }
203
204    /**
205     * Add buttons for the selected Profile's defined sounds
206     * @param elist list of sounds to make buttons from.
207     */
208    void addSoundButtons(ArrayList<SoundEvent> elist) {
209        soundsPanel.removeAll();
210        int i = 0;
211        for (SoundEvent e : elist) {
212            if (e.getButton() != null) {
213                log.debug("adding button {}", e.getButton());
214                JComponent jc = e.getButton();
215                GridBagConstraints gbc = new GridBagConstraints();
216                // Force the EngineSoundEvent to the second row.
217                if (e instanceof EngineSoundEvent) {
218                    gbc.gridy = 1;
219                    if (elist.size() > NUMBER_SOUNDS) {
220                        gbc.gridy = 2; // third row
221                    }
222                    gbc.gridwidth = elist.size() - 1;
223                    gbc.fill = GridBagConstraints.NONE;
224                    gbc.anchor = GridBagConstraints.LINE_START;
225                    soundsPanel.add(jc, gbc);
226                } else {
227                    i++;
228                    gbc.gridy = 0;
229                    if (i > NUMBER_SOUNDS) {
230                        gbc.gridy = 1;
231                    }
232                    soundsPanel.add(jc, gbc);
233                }
234            }
235        }
236    }
237
238    /**
239     * Handle "Option" button presses.
240     * @param e unused.
241     */
242    protected void optionButtonPressed(ActionEvent e) {
243        log.debug("({}) Option Button Pressed", address);
244        VSDOptionsDialog d = new VSDOptionsDialog(this, Bundle.getMessage("OptionsDialogTitlePrefix") + " " + this.address);
245        d.addPropertyChangeListener(new PropertyChangeListener() {
246            @Override
247            public void propertyChange(PropertyChangeEvent event) {
248                log.debug("property change name: {}, old: {}, new: {}", event.getPropertyName(), event.getOldValue(), event.getNewValue());
249                optionsDialogPropertyChange(event);
250            }
251        });
252    }
253
254    /**
255     * Handle "Delete" button presses.
256     * @param e unused.
257     */
258    protected void deleteButtonPressed(ActionEvent e) {
259        log.debug("({}) Delete Button Pressed", address);
260        firePropertyChange(DELETE, address, null);
261    }
262
263    /**
264     * Callback for the Option Dialog.
265     * @param event the event to get new value from.
266     */
267    protected void optionsDialogPropertyChange(PropertyChangeEvent event) {
268        log.debug("internal options dialog handler");
269        firePropertyChange(OPTION_CHANGE, null, event.getNewValue());
270    }
271
272    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(VSDControl.class);
273
274}