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