001package jmri.jmrit.throttle;
002
003import java.awt.*;
004import java.awt.event.ActionEvent;
005import javax.swing.*;
006
007import jmri.Throttle;
008import jmri.util.FileUtil;
009import jmri.util.swing.EditableResizableImagePanel;
010import jmri.util.swing.JmriJOptionPane;
011
012/**
013 * A very specific dialog for editing the properties of a FunctionButton object.
014 */
015public final class FunctionButtonPropertyEditor extends JDialog {
016
017    private final FunctionButton button;
018
019    private JTextField textField;
020    private JCheckBox lockableCheckBox;
021    private JTextField idField;
022    private JTextField fontField;
023    private JCheckBox visibleCheckBox;
024    private EditableResizableImagePanel _imageFilePath;
025    private EditableResizableImagePanel _imagePressedFilePath;
026    private JTextField imageSize;
027    final static int BUT_IMG_SIZE = 45;
028
029    /**
030     * Constructor. Create it and pack it.
031     * @param btn the functionButton
032     */
033    public FunctionButtonPropertyEditor(FunctionButton btn) {
034        button = btn;
035        initGUI();
036        resetProperties();
037    }
038
039    /**
040     * Create, initialise, and place the GUI objects.
041     */
042    private void initGUI() {
043        this.setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE);
044        this.setTitle(Bundle.getMessage("ButtonEditFunction"));
045        JPanel mainPanel = new JPanel();
046        this.setContentPane(mainPanel);
047        mainPanel.setLayout(new BorderLayout());
048        
049        JPanel propertyPanel = new JPanel();
050        propertyPanel.setLayout(new GridBagLayout());
051        GridBagConstraints constraints = new GridBagConstraints();
052        constraints.anchor = GridBagConstraints.WEST;
053        constraints.fill = GridBagConstraints.HORIZONTAL;
054        constraints.gridheight = 1;
055        constraints.gridwidth = 1;
056        constraints.ipadx = 0;
057        constraints.ipady = 0;
058        Insets insets = new Insets(2, 2, 2, 2);
059        constraints.insets = insets;
060        constraints.weightx = 1;
061        constraints.weighty = 1;
062        constraints.gridx = 0;
063        constraints.gridy = 0;
064
065        idField = new JTextField();
066        idField.setColumns(1);
067        propertyPanel.add(new JLabel(Bundle.getMessage("LabelFunctionNumber")), constraints);
068
069        constraints.anchor = GridBagConstraints.CENTER;
070        constraints.gridx = 1;
071        propertyPanel.add(idField, constraints);
072
073        constraints.anchor = GridBagConstraints.WEST;
074        constraints.gridx = 0;
075        constraints.gridy ++;
076        textField = new JTextField();
077        textField.setColumns(10);
078        propertyPanel.add(new JLabel(Bundle.getMessage("LabelText")), constraints);
079
080        constraints.anchor = GridBagConstraints.CENTER;
081        constraints.gridx = 1;
082        propertyPanel.add(textField, constraints);
083
084        constraints.anchor = GridBagConstraints.WEST;
085        constraints.gridx = 0;
086        constraints.gridy ++;
087        fontField = new JTextField();
088        fontField.setColumns(10);
089        propertyPanel.add(new JLabel(Bundle.getMessage("LabelFontSize")), constraints);
090
091        constraints.anchor = GridBagConstraints.CENTER;
092        constraints.gridx = 1;
093        propertyPanel.add(fontField, constraints);
094        
095        constraints.anchor = GridBagConstraints.WEST;
096        constraints.gridx = 0;
097        constraints.gridy ++;
098        imageSize = new JTextField();
099        imageSize.setColumns(10);
100        propertyPanel.add(new JLabel(Bundle.getMessage("LabelFunctionImageSize")), constraints);
101
102        constraints.anchor = GridBagConstraints.CENTER;
103        constraints.gridx = 1;
104        propertyPanel.add(imageSize, constraints);
105
106        lockableCheckBox = new JCheckBox(Bundle.getMessage("CheckBoxLockable"));
107        constraints.anchor = GridBagConstraints.CENTER;
108        constraints.gridx = 0;
109        constraints.gridy ++;
110        propertyPanel.add(lockableCheckBox, constraints);
111
112        visibleCheckBox = new JCheckBox(Bundle.getMessage("CheckBoxVisible"));
113        constraints.anchor = GridBagConstraints.CENTER;
114        constraints.gridx = 0;
115        constraints.gridy ++;
116        propertyPanel.add(visibleCheckBox, constraints);
117
118        constraints.gridy ++;
119        constraints.gridx = 0;
120        propertyPanel.add(new JLabel(Bundle.getMessage("OffIcon")), constraints);
121
122        constraints.gridx = 1;
123        propertyPanel.add(new JLabel(Bundle.getMessage("OnIcon")), constraints);
124
125        constraints.gridy ++;
126        constraints.fill = GridBagConstraints.BOTH;
127        constraints.weighty = 100;
128        constraints.gridx = 0;        
129        _imageFilePath = new EditableResizableImagePanel("", BUT_IMG_SIZE, BUT_IMG_SIZE);
130        _imageFilePath.setDropFolder(FileUtil.getUserResourcePath()); // will be updated later on if curent throttle is in roster db
131        _imageFilePath.setBackground(new Color(0, 0, 0, 0));
132        _imageFilePath.setBorder(BorderFactory.createLineBorder(java.awt.Color.blue));
133        _imageFilePath.addMenuItemBrowseFolder(Bundle.getMessage("OpenSystemFileBrowserOnJMRIfnButtonsRessources"), FileUtil.getExternalFilename("resources/icons/functionicons"));
134        _imageFilePath.addComponentListener(_imageFilePath); // listen to itself, will rescale image when need
135        propertyPanel.add(_imageFilePath, constraints);
136
137        constraints.gridx = 1;
138        _imagePressedFilePath = new EditableResizableImagePanel("", BUT_IMG_SIZE, BUT_IMG_SIZE);
139        _imagePressedFilePath.setDropFolder(FileUtil.getUserResourcePath()); // will be updated later on if curent throttle is in roster db
140        _imagePressedFilePath.setBackground(new Color(0, 0, 0, 0));
141        _imagePressedFilePath.setBorder(BorderFactory.createLineBorder(java.awt.Color.blue));
142        _imagePressedFilePath.addMenuItemBrowseFolder(Bundle.getMessage("OpenSystemFileBrowserOnJMRIfnButtonsRessources"), FileUtil.getExternalFilename("resources/icons/functionicons"));
143        _imagePressedFilePath.addComponentListener(_imagePressedFilePath);  // listen to itself, will rescale image when needed
144        propertyPanel.add(_imagePressedFilePath, constraints);
145
146        JPanel buttonPanel = new JPanel();
147        buttonPanel.setLayout(new GridLayout(1, 2, 4, 4));
148
149        JButton applyButton = new JButton(Bundle.getMessage("ButtonApply"));
150        applyButton.addActionListener((ActionEvent e) -> {
151            saveProperties();
152        });
153                
154        JButton resetButton = new JButton(Bundle.getMessage("ButtonReset"));
155        resetButton.addActionListener((ActionEvent e) -> {
156            resetProperties();           
157        });        
158        
159        JButton closeButton = new JButton(Bundle.getMessage("ButtonClose"));
160        closeButton.addActionListener((ActionEvent e) -> {
161            finishEdit();
162        });
163
164        buttonPanel.add(resetButton);
165        buttonPanel.add(closeButton);        
166        buttonPanel.add(applyButton);
167
168        mainPanel.add(propertyPanel, BorderLayout.CENTER);
169        mainPanel.add(buttonPanel, BorderLayout.SOUTH);
170
171        pack();
172    }
173
174    /**
175     * Initialize GUI from button properties.
176     *
177     */
178    public void resetProperties() {
179        textField.setText(button.getButtonLabel());
180        lockableCheckBox.setSelected(button.getIsLockable());
181        idField.setText(String.valueOf(button.getIdentity()));
182        Throttle mThrottle = button.getThrottle();
183        if (mThrottle!=null) {
184            idField.setToolTipText(Bundle.getMessage("MaxFunction",mThrottle.getFunctions().length -1));
185        }
186        fontField.setText(String.valueOf(button.getFont().getSize()));
187        imageSize.setText(String.valueOf(button.getButtonImageSize()));
188        visibleCheckBox.setSelected(button.getDisplay());
189        _imageFilePath.setImagePath(button.getIconPath());
190        _imagePressedFilePath.setImagePath(button.getSelectedIconPath());
191        textField.requestFocus();
192    }
193
194    /**
195     * Save the user-modified properties back to the FunctionButton.
196     */
197    private void saveProperties() {
198        if (isDataValid()) {
199            button.setButtonLabel(textField.getText());
200            button.setIsLockable(lockableCheckBox.isSelected());
201            button.setIdentity(Integer.parseInt(idField.getText()));
202            String name = button.getFont().getName();
203            button.setFont(new Font(name,
204                    button.getFont().getStyle(),
205                    Integer.parseInt(fontField.getText())));
206            button.setButtonImageSize( Integer.parseInt(imageSize.getText()) );
207            button.setDisplay(visibleCheckBox.isSelected());
208            button.setIconPath(_imageFilePath.getImagePath());
209            button.setSelectedIconPath(_imagePressedFilePath.getImagePath());
210            button.setDirty(true);
211            button.updateLnF();
212        }
213    }
214
215    /**
216     * Finish the editing process. Hide the dialog.
217     */
218    private void finishEdit() {
219        this.setVisible(false);
220    }
221
222    /**
223     * Verify the data on the dialog. If invalid, notify user of errors.
224     * @return true if valid, else false.
225     */
226    private boolean isDataValid() {
227        StringBuffer errors = new StringBuffer();
228        int errorNumber = 0;
229        /* ID >=0 && ID <= 28 */
230        
231        Throttle mThrottle = button.getThrottle();
232        if (mThrottle==null) {
233            return false;
234        }
235        
236        try {
237            int id = Integer.parseInt(idField.getText());
238            if ((id < 0) || id >= mThrottle.getFunctions().length) {
239                throw new NumberFormatException("");
240            }
241        } catch (NumberFormatException ex) {
242            errors.append(String.valueOf(++errorNumber)).append(". ");
243            errors.append(Bundle.getMessage("ErrorFunctionKeyRange",
244                mThrottle.getFunctions().length-1)).append("\n");
245        }
246
247        /* font > 0 */
248        try {
249            int size = Integer.parseInt(fontField.getText());
250            if (size < 1) {
251                throw new NumberFormatException("");
252            }
253        } catch (NumberFormatException ex) {
254            errors.append(String.valueOf(++errorNumber)).append(". ");
255            errors.append( Bundle.getMessage("ErrorFontSize"));
256        }
257
258        /* image size > 0 */
259        try {
260            int size = Integer.parseInt(imageSize.getText());
261            if (size < 1) {
262                throw new NumberFormatException("");
263            }
264        } catch (NumberFormatException ex) {
265            errors.append(String.valueOf(++errorNumber)).append(". ");
266            errors.append( Bundle.getMessage("ErrorImageSize"));
267        }        
268
269        if (errorNumber > 0) {
270            JmriJOptionPane.showMessageDialog(this, errors,
271                Bundle.getMessage("ErrorOnPage"), JmriJOptionPane.ERROR_MESSAGE);
272            return false;
273        }
274        return true;
275    }
276
277    /**
278     * Set the folder where droped images in Property panel will be stored
279     * 
280     * @param dropFolder the folder path
281     * 
282     */
283    void setDropFolder(String dropFolder) {
284        _imageFilePath.setDropFolder(dropFolder);
285        _imagePressedFilePath.setDropFolder(dropFolder);
286    }
287
288    void destroy() {
289        if (_imageFilePath != null) {
290            _imageFilePath.removeDnd();
291        }
292        if (_imagePressedFilePath != null) {
293            _imagePressedFilePath.removeDnd();
294        }
295    }
296}