001package jmri.jmrit.vsdecoder.swing; 002 003import java.awt.Dimension; 004import java.awt.GraphicsEnvironment; 005import java.awt.event.ActionEvent; 006import java.awt.event.ActionListener; 007import java.beans.PropertyChangeEvent; 008 009import javax.swing.AbstractButton; 010import javax.swing.ButtonModel; 011import javax.swing.BorderFactory; 012import javax.swing.JSlider; 013import javax.swing.JSpinner; 014import javax.swing.JToggleButton; 015import javax.swing.SpinnerNumberModel; 016import javax.swing.event.ChangeEvent; 017import javax.swing.event.ChangeListener; 018import javax.swing.Timer; 019 020import jmri.jmrit.vsdecoder.EnginePane; 021import jmri.util.swing.JmriJOptionPane; 022 023/** 024 * Sound control buttons for the new GUI. 025 * 026 * <hr> 027 * This file is part of JMRI. 028 * <p> 029 * JMRI is free software; you can redistribute it and/or modify it under 030 * the terms of version 2 of the GNU General Public License as published 031 * by the Free Software Foundation. See the "COPYING" file for a copy 032 * of this license. 033 * <p> 034 * JMRI is distributed in the hope that it will be useful, but WITHOUT 035 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 036 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 037 * for more details. 038 * 039 * @author Mark Underwood Copyright (C) 2011 040 * @author Klaus Killinger Copyright (C) 2018-2021 041 */ 042public class DieselPane extends EnginePane { 043 044 static final int THROTTLE_MIN = 1; 045 static final int THROTTLE_MAX = 18; 046 static final int THROTTLE_INIT = 1; 047 048 public static final String THROTTLE = "VSDDP:Throttle"; // NOI18N 049 public static final String START = "VSDDP:Start"; // NOI18N 050 public static final String VOLUME = "VSDDP:Volume"; // NOI18N 051 052 JSpinner throttle_spinner; 053 public JSlider volume_slider; 054 JToggleButton start_button; 055 056 int throttle_setting; 057 private boolean engine_is_started; 058 059 private Timer timer; 060 int dtime = 1; 061 float lastSpeed = 0.0f; 062 063 /** 064 * Constructor 065 * 066 * @param n pane title 067 */ 068 public DieselPane(String n) { 069 super(n); 070 initComponents(); 071 throttle_setting = THROTTLE_INIT; 072 engine_is_started = start_button.isSelected(); 073 } 074 075 /** 076 * Null constructor 077 */ 078 public DieselPane() { 079 this(null); 080 } 081 082 /** 083 * Init Context. 084 * @param context unused. 085 */ 086 @Override 087 public void initContext(Object context) { 088 initComponents(); 089 } 090 091 protected Timer newTimer(int time, boolean repeat, ActionListener al) { 092 timer = new Timer(time, al); 093 timer.setRepeats(repeat); 094 return timer; 095 } 096 097 // Lock the start/stop-button until the start/stop-sound has finished 098 // Skip the timer if there is no start/stop-sound 099 void startDelayTimer() { 100 if (dtime > 1) { 101 start_button.setEnabled(false); 102 timer = newTimer(dtime, false, new ActionListener() { 103 @Override 104 public void actionPerformed(ActionEvent e) { 105 start_button.setEnabled(true); 106 } 107 }); 108 timer.start(); 109 } 110 } 111 112 @Override 113 public void setButtonDelay(long t) { 114 // Timer only takes an int ... cap the length at MAXINT 115 // Note: this only works for positive lengths ... 116 if (t > Integer.MAX_VALUE) { 117 t = Integer.MAX_VALUE; // small enough to safely cast 118 } 119 dtime = (int) t; // time in ms 120 } 121 122 /** 123 * Build the GUI components 124 */ 125 @Override 126 public void initComponents() { 127 //Set up the throttle spinner 128 throttle_spinner = new JSpinner(new SpinnerNumberModel(THROTTLE_INIT, THROTTLE_MIN, THROTTLE_MAX, 1)); 129 throttle_spinner.setPreferredSize(new Dimension(40, 30)); 130 throttle_spinner.setToolTipText(Bundle.getMessage("ToolTipDP_ThrottleSpinner")); 131 throttle_spinner.setEnabled(false); 132 133 this.add(throttle_spinner); 134 135 // Setup the start button 136 start_button = new JToggleButton(); 137 start_button.setText(Bundle.getMessage("ButtonEngineStart")); 138 start_button.setPreferredSize(new Dimension(150, 30)); 139 start_button.setToolTipText(Bundle.getMessage("ToolTipDP_StartButton")); 140 start_button.addActionListener(new ActionListener() { 141 @Override 142 public void actionPerformed(ActionEvent e) { 143 startButtonChange(e); 144 } 145 }); 146 147 start_button.addChangeListener(new ChangeListener() { 148 @Override 149 public void stateChanged(ChangeEvent ev) { 150 startButtonStateChange(ev); 151 } 152 }); 153 154 this.add(start_button); 155 156 //Set up the volume slider 157 volume_slider = new JSlider(0, 100); 158 volume_slider.setMinorTickSpacing(10); 159 volume_slider.setPaintTicks(true); 160 volume_slider.setPreferredSize(new Dimension(160, 30)); 161 volume_slider.setToolTipText(Bundle.getMessage("DecoderVolumeToolTip")); 162 volume_slider.addChangeListener(new ChangeListener() { 163 @Override 164 public void stateChanged(ChangeEvent e) { 165 volumeChange(e); // slider in real time 166 } 167 }); 168 this.add(volume_slider); 169 170 this.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); 171 this.setVisible(true); 172 } 173 174 // Respond to a start button press with stateChanged 175 @edu.umd.cs.findbugs.annotations.SuppressFBWarnings( value="SLF4J_FORMAT_SHOULD_BE_CONST", 176 justification="I18N of Headless Info Message") 177 public void startButtonStateChange(ChangeEvent ev) { 178 AbstractButton abstractButton = (AbstractButton) ev.getSource(); 179 ButtonModel buttonModel = abstractButton.getModel(); 180 boolean armed = buttonModel.isArmed(); 181 boolean pressed = buttonModel.isPressed(); 182 boolean selected = buttonModel.isSelected(); 183 if (armed && pressed && selected && (lastSpeed > 0.0f) && (!engine_is_started)) { 184 buttonModel.setArmed(false); 185 buttonModel.setPressed(false); 186 buttonModel.setSelected(false); 187 if (GraphicsEnvironment.isHeadless()) { 188 log.info(Bundle.getMessage("EngineStartSpeedMessage")); 189 } else { 190 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("EngineStartSpeedMessage")); 191 } 192 } 193 194 if (armed && pressed && selected && (lastSpeed > 0.0f) && (engine_is_started) && (getStopOption())) { 195 buttonModel.setArmed(false); 196 buttonModel.setPressed(false); 197 if (GraphicsEnvironment.isHeadless()) { 198 log.info(Bundle.getMessage("EngineStopSpeedMessage")); 199 } else { 200 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("EngineStopSpeedMessage")); 201 } 202 } 203 } 204 205 /** 206 * Respond to a throttle change. 207 * Basically, doesn't do anything. 208 * @param e unused. 209 */ 210 public void throttleChange(ChangeEvent e) { 211 firePropertyChangeEvent(new PropertyChangeEvent(this, THROTTLE, throttle_setting, throttle_spinner.getModel().getValue())); 212 throttle_setting = throttleNotch(); 213 } 214 215 /** 216 * Respond to a start button press. 217 * @param e unused. 218 */ 219 public void startButtonChange(ActionEvent e) { 220 firePropertyChangeEvent(new PropertyChangeEvent(this, START, engine_is_started, start_button.isSelected())); 221 engine_is_started = start_button.isSelected(); 222 if (engine_is_started) { // switch button name to make the panel more responsive 223 start_button.setText(Bundle.getMessage("ButtonEngineStop")); 224 } else { 225 start_button.setText(Bundle.getMessage("ButtonEngineStart")); 226 } 227 startDelayTimer(); 228 } 229 230 protected void volumeChange(ChangeEvent e) { 231 JSlider v = (JSlider) e.getSource(); 232 log.debug("Decoder Volume slider set to value: {}", v.getValue()); 233 firePropertyChangeEvent(new PropertyChangeEvent(this, VOLUME, v.getValue(), null)); 234 } 235 236 @Override 237 public void startButtonClick() { 238 start_button.doClick(); // Animate button and process ChangeEvent 239 } 240 241 /** 242 * Get if Engine is On. 243 * @return true if the start button is "on". 244 */ 245 public boolean engineIsOn() { 246 return start_button.isSelected(); 247 } 248 249 /** 250 * Get Throttle notch. 251 * @return current notch setting of the throttle slider. 252 */ 253 public int throttleNotch() { 254 return (Integer) throttle_spinner.getModel().getValue(); 255 } 256 257 /** 258 * Set the Throttle spinner value. 259 * @param t new value. 260 */ 261 @Override 262 public void setThrottle(int t) { 263 throttle_spinner.setValue(t); 264 } 265 266 @Override 267 public void setSpeed(float s) { 268 lastSpeed = s; 269 } 270 271 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DieselPane.class); 272 273}