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