001package jmri.jmrit.audio.swing; 002 003import java.awt.FlowLayout; 004import java.util.Hashtable; 005import javax.swing.*; 006import javax.swing.event.ChangeEvent; 007import javax.vecmath.Vector3f; 008import jmri.Audio; 009import jmri.implementation.AbstractAudio; 010import jmri.jmrit.beantable.AudioTableAction.AudioTableDataModel; 011import jmri.util.JmriJFrame; 012 013/** 014 * Abstract GUI to edit Audio objects 015 * 016 * <hr> 017 * This file is part of JMRI. 018 * <p> 019 * JMRI is free software; you can redistribute it and/or modify it under the 020 * terms of version 2 of the GNU General Public License as published by the Free 021 * Software Foundation. See the "COPYING" file for a copy of this license. 022 * <p> 023 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY 024 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 025 * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 026 * 027 * @author Matthew Harris copyright (c) 2009 028 */ 029abstract public class AbstractAudioFrame extends JmriJFrame { 030 031 AbstractAudioFrame frame = this; 032 033 JPanel main = new JPanel(); 034 private JScrollPane scroll 035 = new JScrollPane(main, 036 ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, 037 ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); 038 039 AudioTableDataModel model; 040 041 private static final int INT_PRECISION = (int) Math.pow(10, Audio.DECIMAL_PLACES); 042 static final float FLT_PRECISION = 1 / (float) INT_PRECISION; 043 044 // Common UI components for Add/Edit Audio 045 private static final JLabel SYS_NAME_LABEL = new JLabel(Bundle.getMessage("LabelSystemName")); 046 JTextField sysName = new JTextField(5); 047 private static final JLabel USER_NAME_LABEL = new JLabel(Bundle.getMessage("LabelUserName")); 048 JTextField userName = new JTextField(15); 049 050 /** 051 * Standard constructor. 052 * 053 * @param title Title of this AudioFrame 054 * @param model AudioTableDataModel holding Audio data 055 */ 056 public AbstractAudioFrame(String title, AudioTableDataModel model) { 057 super(title); 058 this.model = model; 059 } 060 061 /** 062 * Layout the frame. 063 * <p> 064 * This contains common items. 065 * <p> 066 * Sub-classes will override this method and provide additional GUI items. 067 */ 068 public void layoutFrame() { 069 frame.addHelpMenu("package.jmri.jmrit.beantable.AudioAddEdit", true); 070 frame.getContentPane().setLayout(new BoxLayout(frame.getContentPane(), BoxLayout.Y_AXIS)); 071 main.setLayout(new BoxLayout(main, BoxLayout.Y_AXIS)); 072 073 JPanel p; 074 075 p = new JPanel(); 076 p.setLayout(new FlowLayout()); 077 p.add(SYS_NAME_LABEL); 078 p.add(sysName); 079 frame.getContentPane().add(p); 080 081 p = new JPanel(); 082 p.setLayout(new FlowLayout()); 083 p.add(USER_NAME_LABEL); 084 p.add(userName); 085 frame.getContentPane().add(p); 086 087 frame.add(scroll); 088 } 089 090 /** 091 * Populate the Audio frame with default values. 092 */ 093 abstract public void resetFrame(); 094 095 /** 096 * Populate the Audio frame with current values. 097 * 098 * @param a Audio object to use 099 */ 100 public void populateFrame(Audio a) { 101 sysName.setText(a.getSystemName()); 102 userName.setText(a.getUserName()); 103 } 104 105 /** 106 * Check System Name user input. 107 * 108 * @param entry string retrieved from text field 109 * @param counter index of all similar (Source/Buffer) items 110 * @param prefix (AudioListener/Source/Buffer) system name prefix string to compare entry against 111 * @return true if prefix doesn't match 112 */ 113 protected boolean entryError(String entry, String prefix, String counter) { 114 if (!entry.startsWith(prefix)) { 115 JOptionPane.showMessageDialog(null, Bundle.getMessage("AudioCreateError", prefix), 116 Bundle.getMessage("AudioCreateErrorTitle"), JOptionPane.ERROR_MESSAGE); 117 sysName.setText(prefix + counter); 118 return true; 119 } 120 return false; 121 } 122 123 //private static final Logger log = LoggerFactory.getLogger(AbstractAudioFrame.class); 124 /** 125 * Convenience class to create a JPanel to edit a Vector3f object using 3 126 * separate JSpinner Swing objects. 127 */ 128 protected static class JPanelVector3f extends JPanel { 129 130 JLabel xLabel = new JLabel(Bundle.getMessage("LabelX")); 131 JSpinner xValue = new JSpinner(); 132 JLabel yLabel = new JLabel(Bundle.getMessage("LabelY")); 133 JSpinner yValue = new JSpinner(); 134 JLabel zLabel = new JLabel(Bundle.getMessage("LabelZ")); 135 JSpinner zValue = new JSpinner(); 136 JLabel unitsLabel = new JLabel(); 137 138 JPanelVector3f() { 139 super(); 140 layoutPanel("", ""); 141 } 142 143 JPanelVector3f(String title) { 144 super(); 145 layoutPanel(title, ""); 146 } 147 148 JPanelVector3f(String title, String units) { 149 super(); 150 layoutPanel(title, units); 151 } 152 153 @SuppressWarnings("UnnecessaryBoxing") 154 private void layoutPanel(String title, String units) { 155 this.setLayout(new FlowLayout()); 156 if (title.length() != 0) { 157 this.setBorder(BorderFactory.createCompoundBorder( 158 BorderFactory.createTitledBorder(title), 159 BorderFactory.createEmptyBorder(5, 5, 5, 5))); 160 } 161 this.add(xLabel); 162 xValue.setPreferredSize(new JTextField(8).getPreferredSize()); 163 xValue.setModel( 164 new SpinnerNumberModel(Float.valueOf(0f), Float.valueOf(-Audio.MAX_DISTANCE), Float.valueOf(Audio.MAX_DISTANCE), Float.valueOf(FLT_PRECISION))); 165 xValue.setEditor(new JSpinner.NumberEditor(xValue, "0.00")); 166 this.add(xValue); 167 168 this.add(yLabel); 169 yValue.setPreferredSize(new JTextField(8).getPreferredSize()); 170 yValue.setModel( 171 new SpinnerNumberModel(Float.valueOf(0f), Float.valueOf(-Audio.MAX_DISTANCE), Float.valueOf(Audio.MAX_DISTANCE), Float.valueOf(FLT_PRECISION))); 172 yValue.setEditor(new JSpinner.NumberEditor(yValue, "0.00")); 173 this.add(yValue); 174 175 this.add(zLabel); 176 zValue.setPreferredSize(new JTextField(8).getPreferredSize()); 177 zValue.setModel( 178 new SpinnerNumberModel(Float.valueOf(0f), Float.valueOf(-Audio.MAX_DISTANCE), Float.valueOf(Audio.MAX_DISTANCE), Float.valueOf(FLT_PRECISION))); 179 zValue.setEditor(new JSpinner.NumberEditor(zValue, "0.00")); 180 this.add(zValue); 181 182 if (units.length() != 0) { 183 unitsLabel.setText(units); 184 this.add(unitsLabel); 185 } 186 } 187 188 /** 189 * Set the value of this object. 190 * 191 * @param value value to set 192 */ 193 public void setValue(Vector3f value) { 194 xValue.setValue(value.x); 195 yValue.setValue(value.y); 196 zValue.setValue(value.z); 197 } 198 199 /** 200 * Retrieve the current value of this object 201 * 202 * @return current value 203 */ 204 public Vector3f getValue() { 205 return new Vector3f( 206 AbstractAudio.roundDecimal((Float) xValue.getValue()), 207 AbstractAudio.roundDecimal((Float) yValue.getValue()), 208 AbstractAudio.roundDecimal((Float) zValue.getValue())); 209 } 210 } 211 212 /** 213 * A convenience class to create a JPanel for editing a float value using 214 * combined JSlider and JSPinner Swing objects. 215 */ 216 protected static class JPanelSliderf extends JPanel { 217 218 JSlider slider = new JSlider(); 219 220 JSpinner spinner = new JSpinner(); 221 222 @SuppressWarnings({"UnnecessaryBoxing", "OverridableMethodCallInConstructor"}) 223 JPanelSliderf(String title, Float min, Float max, int majorTicks, int minorTicks) { 224 super(); 225 int iMin = Math.round(min * INT_PRECISION); 226 int iMax = Math.round(max * INT_PRECISION); 227 int iInterval = (iMax - iMin) / majorTicks; 228 229 this.setLayout(new FlowLayout()); 230 this.setBorder(BorderFactory.createCompoundBorder( 231 BorderFactory.createTitledBorder(title), 232 BorderFactory.createEmptyBorder(5, 5, 5, 5))); 233 slider.setMinimum(Math.round(min * INT_PRECISION)); 234 slider.setMaximum(Math.round(max * INT_PRECISION)); 235 slider.setMajorTickSpacing(iInterval); 236 slider.setMinorTickSpacing(iInterval / minorTicks); 237 @SuppressWarnings("UseOfObsoleteCollectionType") 238 // Need to use Hashtable for JSlider labels 239 Hashtable<Integer, JLabel> labelTable = new Hashtable<>(); 240 for (int i = iMin; i <= iMax; i += iInterval) { 241 float f = i; 242 f /= INT_PRECISION; 243 labelTable.put(i, new JLabel(Float.toString(f))); 244 } 245 slider.setLabelTable(labelTable); 246 slider.setPaintTicks(true); 247 slider.setPaintLabels(true); 248 slider.addChangeListener((ChangeEvent e) -> { 249 float f = slider.getValue(); 250 f /= INT_PRECISION; 251 spinner.setValue(f); 252 }); 253 spinner.setPreferredSize(new JTextField(5).getPreferredSize()); 254 spinner.setModel( 255 new SpinnerNumberModel(min, min, max, Float.valueOf(FLT_PRECISION))); 256 spinner.setEditor(new JSpinner.NumberEditor(spinner, "0.00")); 257 spinner.addChangeListener((ChangeEvent e) -> { 258 slider.setValue( 259 Math.round((Float) spinner.getValue() * INT_PRECISION)); 260 }); 261 this.add(slider); 262 this.add(spinner); 263 } 264 265 /** 266 * Set the value of this object. 267 * 268 * @param value value to set 269 */ 270 public void setValue(float value) { 271 spinner.setValue(value); 272 } 273 274 /** 275 * Retrieve the current value of this object. 276 * 277 * @return current value 278 */ 279 public float getValue() { 280 return AbstractAudio.roundDecimal((Float) spinner.getValue()); 281 } 282 } 283 284}