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