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}