001package jmri.jmrit.simpleclock; 002 003import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 004 005import java.awt.Container; 006import java.awt.event.ActionEvent; 007import java.awt.event.FocusAdapter; 008import java.awt.event.FocusEvent; 009import java.awt.event.WindowEvent; 010import java.beans.PropertyChangeEvent; 011import java.beans.PropertyChangeListener; 012import java.text.DecimalFormat; 013import java.util.Calendar; 014import java.util.Date; 015 016import javax.annotation.CheckForNull; 017import javax.swing.*; 018import jmri.InstanceManager; 019import jmri.Timebase; 020import jmri.TimebaseRateException; 021import jmri.util.JmriJFrame; 022import jmri.util.swing.JmriJOptionPane; 023 024/** 025 * Frame for user configuration of Simple Timebase. 026 * <p> 027 * The current implementation (2007) handles the internal clock and one hardware 028 * clock. 029 * 030 * @author Dave Duchamp Copyright (C) 2004, 2007 031 */ 032public class SimpleClockFrame extends JmriJFrame implements PropertyChangeListener { 033 034 private Timebase clock; 035 private String hardwareName = null; 036 private boolean changed = false; 037 protected boolean showTime = false; 038 private final DecimalFormat threeDigits = new DecimalFormat("0.000"); // 3 digit precision for speedup factor 039 private boolean checkEnabled = jmri.InstanceManager.getDefault(jmri.configurexml.ShutdownPreferences.class).isStoreCheckEnabled(); 040 041 protected JComboBox<String> timeSourceBox = null; 042 protected JComboBox<String> clockStartBox = null; 043 protected JComboBox<String> startRunBox = null; 044 // These are the indexes into the start run box. 045 private final static int START_RUNNING = 0; 046 private final static int START_STOPPED = 1; 047 private final static int START_NORUNCHANGE = 2; 048 049 protected JCheckBox synchronizeCheckBox = null; 050 protected JCheckBox correctCheckBox = null; 051 protected JCheckBox displayCheckBox = null; 052 protected JCheckBox startSetTimeCheckBox = null; 053 protected JCheckBox startSetRateCheckBox = null; 054 protected JCheckBox displayStartStopButton = null; 055 056 protected JTextField factorField = new JTextField(5); 057 protected JTextField startFactorField = new JTextField(5); 058 protected JTextField hoursField = new JTextField(2); 059 protected JTextField minutesField = new JTextField(2); 060 protected JTextField startHoursField = new JTextField(2); 061 protected JTextField startMinutesField = new JTextField(2); 062 063 protected JButton setRateButton = new JButton(Bundle.getMessage("ButtonSet")); 064 protected JButton setTimeButton = new JButton(Bundle.getMessage("ButtonSet")); 065 protected JButton startButton = new JButton(Bundle.getMessage("ButtonStart")); 066 protected JButton stopButton = new JButton(Bundle.getMessage("ButtonStop")); 067 protected JButton doneButton = new JButton(Bundle.getMessage("ButtonDone")); 068 069 protected JLabel clockStatus = new JLabel(); 070 protected JLabel timeLabel = new JLabel(); 071 072 private final int internalSourceIndex = 0; 073 private final int hardwareSourceIndex = 1; 074 075 private final int startNone = 0; 076 private final int startNixieClock = 1; 077 private final int startAnalogClock = 2; 078 private final int startLcdClock = 3; 079 private final int startPragotronClock = 4 ; 080 081 /** 082 * Constructor method. 083 */ 084 public SimpleClockFrame() { 085 super(); 086 } 087 088 /** 089 * Initialize the Clock config window. 090 */ 091 @Override 092 public void initComponents() { 093 setTitle(Bundle.getMessage("SimpleClockWindowTitle")); 094 095 // Determine current state of the clock 096 clock = InstanceManager.getNullableDefault(jmri.Timebase.class); 097 if (clock == null) { 098 // could not initialize clock 099 log.error("Could not obtain a Timebase instance."); 100 setVisible(false); 101 dispose(); 102 } 103 if (!clock.getIsInitialized()) { 104 // if clocks have not been initialized at start up, do so now 105 clock.initializeHardwareClock(); 106 } 107 108 Container contentPane = getContentPane(); 109 contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS)); 110 111 contentPane.add(getClockStatePanel()); 112 113 JPanel saveContainerPanel = new JPanel(); 114 saveContainerPanel.setBorder( BorderFactory.createRaisedBevelBorder() ); 115 saveContainerPanel.setLayout(new BoxLayout(saveContainerPanel, BoxLayout.Y_AXIS)); 116 117 saveContainerPanel.add(getSourcePane()); 118 saveContainerPanel.add(getStartupOptionsPane()); 119 120 // add Done button 121 JPanel panel4 = new JPanel(); 122 panel4.setLayout(new BoxLayout(panel4, BoxLayout.X_AXIS)); 123 panel4.add(doneButton); 124 doneButton.addActionListener(this::doneButtonActionPerformed); 125 saveContainerPanel.add(panel4); 126 127 128 contentPane.add(saveContainerPanel); 129 130 // update contents for current status 131 updateRunningButton(); 132 133 // add save menu item 134 JMenuBar menuBar = new JMenuBar(); 135 JMenu fileMenu = new JMenu(Bundle.getMessage("MenuFile")); 136 menuBar.add(fileMenu); 137 fileMenu.add(new jmri.configurexml.StoreMenu()); 138 139 setJMenuBar(menuBar); 140 // add help menu to window 141 addHelpMenu("package.jmri.jmrit.simpleclock.SimpleClockFrame", true); 142 143 // pack for display 144 pack(); 145 146 // listen for changes to the timebase parameters 147 clock.addPropertyChangeListener(this); 148 } 149 150 private JPanel getClockStatePanel() { 151 152 // Set up clock information panel 153 JPanel clockStatePanel = new JPanel(); 154 clockStatePanel.setLayout(new BoxLayout(clockStatePanel, BoxLayout.Y_AXIS)); 155 clockStatePanel.setBorder(BorderFactory.createTitledBorder( 156 Bundle.getMessage("BoxLabelClockState"))); 157 158 JPanel panel31 = new JPanel(); 159 panel31.add(clockStatus); 160 161 JPanel panel32 = new JPanel(); 162 panel32.add(new JLabel(Bundle.getMessage("CurrentTime") + " ")); 163 setTimeLabel(); 164 panel32.add(timeLabel); 165 clockStatePanel.add(panel32); 166 167 // Set up Start and Stop buttons 168 startButton.setToolTipText(Bundle.getMessage("TipStartButton")); 169 startButton.addActionListener(this::startButtonActionPerformed); 170 panel31.add(startButton); 171 stopButton.setToolTipText(Bundle.getMessage("TipStopButton")); 172 stopButton.addActionListener(this::stopButtonActionPerformed); 173 panel31.add(stopButton); 174 clockStatePanel.add(panel31); 175 176 // Set up time setup information 177 JPanel panel2 = new JPanel(); 178 panel2.add(new JLabel(Bundle.getMessage("NewTime") + " ")); 179 panel2.add(hoursField); 180 hoursField.setText("00"); 181 hoursField.setToolTipText(Bundle.getMessage("TipHoursField")); 182 panel2.add(new JLabel(":")); 183 panel2.add(minutesField); 184 minutesField.setText("00"); 185 minutesField.setToolTipText(Bundle.getMessage("TipMinutesField")); 186 setTimeButton.setToolTipText(Bundle.getMessage("TipSetTimeButton")); 187 setTimeButton.addActionListener(this::setTimeButtonActionPerformed); 188 panel2.add(setTimeButton); 189 clockStatePanel.add(panel2); 190 191 // Set up speed up factor 192 JPanel panel12 = new JPanel(); 193 panel12.add(new JLabel(Bundle.getMessage("SpeedUpFactor") + " ")); 194 panel12.add(factorField); 195 factorField.setText(threeDigits.format(clock.userGetRate())); 196 factorField.setToolTipText(Bundle.getMessage("TipFactorField")); 197 panel12.add(new JLabel(":1 ")); 198 setRateButton.setToolTipText(Bundle.getMessage("TipSetRateButton")); 199 setRateButton.addActionListener(this::setRateButtonActionPerformed); 200 panel12.add(setRateButton); 201 clockStatePanel.add(panel12); 202 203 JPanel clockStatePanelContainer = new JPanel(); 204 clockStatePanelContainer.setBorder( BorderFactory.createRaisedBevelBorder() ); 205 clockStatePanelContainer.setLayout(new BoxLayout(clockStatePanelContainer, BoxLayout.X_AXIS)); 206 clockStatePanelContainer.add(clockStatePanel); 207 return clockStatePanelContainer; 208 209 } 210 211 private JPanel getSourcePane(){ 212 213 JPanel sourcePanel = new JPanel(); 214 sourcePanel.setBorder( BorderFactory.createTitledBorder( Bundle.getMessage("TimeSource"))); 215 sourcePanel.setLayout(new BoxLayout(sourcePanel, BoxLayout.Y_AXIS)); 216 217 // Set up time source choice 218 JPanel panel11 = new JPanel(); 219 // panel11.add(new JLabel(Bundle.getMessage("TimeSource") + " ")); 220 timeSourceBox = new JComboBox<>(); 221 panel11.add(timeSourceBox); 222 timeSourceBox.addItem(Bundle.getMessage("ComputerClock")); 223 hardwareName = InstanceManager.getDefault(jmri.ClockControl.class).getHardwareClockName(); 224 if (hardwareName != null) { 225 timeSourceBox.addItem(hardwareName); 226 } 227 timeSourceBox.setToolTipText(Bundle.getMessage("TipTimeSource")); 228 timeSourceBox.addActionListener(this::setTimeSourceChanged); 229 sourcePanel.add(panel11); 230 231 if (hardwareName != null) { 232 timeSourceBox.setSelectedIndex(clock.getInternalMaster() ? internalSourceIndex : hardwareSourceIndex); 233 JPanel panel11x = new JPanel(); 234 synchronizeCheckBox = new JCheckBox(Bundle.getMessage("Synchronize") + " " 235 + hardwareName); 236 synchronizeCheckBox.setToolTipText(Bundle.getMessage("TipSynchronize")); 237 synchronizeCheckBox.setSelected(clock.getSynchronize()); 238 synchronizeCheckBox.addActionListener(this::synchronizeChanged); 239 panel11x.add(synchronizeCheckBox); 240 sourcePanel.add(panel11x); 241 if (InstanceManager.getDefault(jmri.ClockControl.class).canCorrectHardwareClock()) { 242 JPanel panel11y = new JPanel(); 243 correctCheckBox = new JCheckBox(Bundle.getMessage("Correct")); 244 correctCheckBox.setToolTipText(Bundle.getMessage("TipCorrect")); 245 correctCheckBox.setSelected(clock.getCorrectHardware()); 246 correctCheckBox.addActionListener(this::correctChanged); 247 panel11y.add(correctCheckBox); 248 sourcePanel.add(panel11y); 249 } 250 if (InstanceManager.getDefault(jmri.ClockControl.class).canSet12Or24HourClock()) { 251 JPanel panel11z = new JPanel(); 252 displayCheckBox = new JCheckBox(Bundle.getMessage("Display12Hour")); 253 displayCheckBox.setToolTipText(Bundle.getMessage("TipDisplay")); 254 displayCheckBox.setSelected(clock.use12HourDisplay()); 255 displayCheckBox.addActionListener(this::displayChanged); 256 panel11z.add(displayCheckBox); 257 sourcePanel.add(panel11z); 258 } 259 } 260 261 return sourcePanel; 262 263 } 264 265 private JPanel getStartupOptionsPane(){ 266 267 // Set up startup options panel 268 JPanel startupOptionsPane = new JPanel(); 269 startupOptionsPane.setLayout(new BoxLayout(startupOptionsPane, BoxLayout.Y_AXIS)); 270 JPanel panel61 = new JPanel(); 271 panel61.add(new JLabel(Bundle.getMessage("StartBoxLabel") + " ")); 272 startRunBox = new JComboBox<>(); 273 startRunBox.addItem(Bundle.getMessage("StartSelectRunning")); 274 startRunBox.addItem(Bundle.getMessage("StartSelectStopped")); 275 startRunBox.addItem(Bundle.getMessage("StartSelectNoChange")); 276 startRunBox.setToolTipText(Bundle.getMessage("TipStartRunSelect")); 277 switch (clock.getClockInitialRunState()) { 278 case DO_STOP: 279 startRunBox.setSelectedIndex(START_STOPPED); 280 break; 281 case DO_START: 282 startRunBox.setSelectedIndex(START_RUNNING); 283 break; 284 case DO_NOTHING: 285 startRunBox.setSelectedIndex(START_NORUNCHANGE); 286 break; 287 default: 288 jmri.util.LoggingUtil.warnOnce(log, "Unexpected initial run state = {}", clock.getClockInitialRunState()); 289 break; 290 } 291 startRunBox.addActionListener(this::startRunBoxChanged); 292 panel61.add(startRunBox); 293 startupOptionsPane.add(panel61); 294 295 JPanel panel62 = new JPanel(); 296 startSetTimeCheckBox = new JCheckBox(Bundle.getMessage("StartSetTime")); 297 startSetTimeCheckBox.setToolTipText(Bundle.getMessage("TipStartSetTime")); 298 startSetTimeCheckBox.setSelected(clock.getStartSetTime()); 299 startSetTimeCheckBox.addActionListener(this::startSetTimeChanged); 300 panel62.add(startSetTimeCheckBox); 301 Calendar cal = Calendar.getInstance(); 302 cal.setTime(clock.getStartTime()); 303 startHoursField.setText("" + cal.get(Calendar.HOUR_OF_DAY)); 304 startHoursField.setToolTipText(Bundle.getMessage("TipStartHours")); 305 panel62.add(startHoursField); 306 panel62.add(new JLabel(":")); 307 startMinutesField.setText("" + cal.get(Calendar.MINUTE)); 308 startMinutesField.setToolTipText(Bundle.getMessage("TipStartMinutes")); 309 panel62.add(startMinutesField); 310 311 startMinutesField.addFocusListener(getStartUpSetTimeChangedAdapter()); 312 startHoursField.addFocusListener(getStartUpSetTimeChangedAdapter()); 313 startupOptionsPane.add(panel62); 314 315 JPanel panelStartSetRate = new JPanel(); 316 startSetRateCheckBox = new JCheckBox(Bundle.getMessage("StartSetSpeedUpFactor") + " "); 317 startSetRateCheckBox.setToolTipText(Bundle.getMessage("TipStartSetRate")); 318 startSetRateCheckBox.setSelected(clock.getSetRateAtStart()); 319 startSetRateCheckBox.addActionListener(this::startSetRateChanged); 320 panelStartSetRate.add(startSetRateCheckBox); 321 panelStartSetRate.add(startFactorField); 322 startFactorField.setText(threeDigits.format(clock.getStartRate())); 323 startFactorField.setToolTipText(Bundle.getMessage("TipFactorField")); 324 startFactorField.addActionListener(this::startFactorFieldChanged); 325 startFactorField.addFocusListener(new FocusAdapter() { 326 @Override 327 public void focusLost(FocusEvent focusEvent) { 328 if (!focusEvent.isTemporary()) { 329 startFactorFieldChanged(null); 330 } 331 super.focusLost(focusEvent); 332 } 333 }); 334 panelStartSetRate.add(new JLabel(":1 ")); 335 startupOptionsPane.add(panelStartSetRate); 336 337 JPanel panel63 = new JPanel(); 338 panel63.add(new JLabel(Bundle.getMessage("StartClock") + " ")); 339 clockStartBox = new JComboBox<>(); 340 panel63.add(clockStartBox); 341 clockStartBox.addItem(Bundle.getMessage("None")); 342 clockStartBox.addItem(Bundle.getMessage("MenuItemNixieClock")); 343 clockStartBox.addItem(Bundle.getMessage("MenuItemAnalogClock")); 344 clockStartBox.addItem(Bundle.getMessage("MenuItemLcdClock")); 345 clockStartBox.addItem(Bundle.getMessage("MenuItemPragotronClock")); 346 clockStartBox.setSelectedIndex(startNone); 347 if (clock.getStartClockOption() == Timebase.NIXIE_CLOCK) { 348 clockStartBox.setSelectedIndex(startNixieClock); 349 } else { 350 if (clock.getStartClockOption() == Timebase.ANALOG_CLOCK) { 351 clockStartBox.setSelectedIndex(startAnalogClock); 352 } else { 353 if (clock.getStartClockOption() == Timebase.LCD_CLOCK) { 354 clockStartBox.setSelectedIndex(startLcdClock); 355 } else { 356 if (clock.getStartClockOption() == Timebase.PRAGOTRON_CLOCK) { 357 clockStartBox.setSelectedIndex(startPragotronClock); 358 } 359 } 360 } 361 } 362 clockStartBox.setToolTipText(Bundle.getMessage("TipClockStartOption")); 363 clockStartBox.addActionListener(this::setClockStartChanged); 364 startupOptionsPane.add(panel63); 365 JPanel panel64 = new JPanel(); 366 displayStartStopButton= new JCheckBox(Bundle.getMessage("DisplayOnOff")); 367 displayStartStopButton.setSelected(clock.getShowStopButton()); 368 displayStartStopButton.addActionListener(this::showStopButtonChanged); 369 panel64.add(displayStartStopButton); 370 startupOptionsPane.add(panel64); 371 372 startupOptionsPane.setBorder(BorderFactory.createTitledBorder( 373 Bundle.getMessage("BoxLabelStartUp"))); 374 375 return startupOptionsPane; 376 377 } 378 379 private FocusAdapter getStartUpSetTimeChangedAdapter(){ 380 return new FocusAdapter() { 381 @Override 382 public void focusLost(FocusEvent focusEvent) { 383 if (!focusEvent.isTemporary()) { 384 startSetTimeChanged(null); 385 } 386 super.focusLost(focusEvent); 387 } 388 }; 389 } 390 391 private void startFactorFieldChanged(ActionEvent e) { 392 Double v = parseRate(startFactorField.getText()); 393 if (v != null && !v.equals(clock.getStartRate())) { 394 clock.setStartRate(v); 395 changed = true; 396 } 397 startFactorField.setText(threeDigits.format(clock.getStartRate())); 398 } 399 400 private void startSetRateChanged(ActionEvent e) { 401 clock.setSetRateAtStart(startSetRateCheckBox.isSelected()); 402 changed = true; 403 } 404 405 /** 406 * Adjust to running state changes 407 */ 408 void updateRunningButton() { 409 boolean running = clock.getRun(); 410 if (running) { 411 clockStatus.setText(Bundle.getMessage("ClockRunning")); 412 startButton.setVisible(false); 413 stopButton.setVisible(true); 414 } else { 415 clockStatus.setText(Bundle.getMessage("ClockStopped")); 416 startButton.setVisible(true); 417 stopButton.setVisible(false); 418 } 419 clockStatus.setVisible(true); 420 } 421 422 /** 423 * Converts a user-entered rate to a double, possibly throwing up warning dialogs. 424 * @param fieldEntry value from text field where the user entered a rate. 425 * @return null if the rate could not be parsed, negative, or an unsupported fraction. 426 * Otherwise the fraction value. 427 */ 428 @CheckForNull 429 Double parseRate(String fieldEntry) { 430 double rate; 431 try { 432 char decimalSeparator = threeDigits.getDecimalFormatSymbols().getDecimalSeparator() ; 433 if (decimalSeparator != '.') { 434 fieldEntry = fieldEntry.replace(decimalSeparator, '.') ; 435 } 436 rate = Double.parseDouble(fieldEntry); 437 } catch (NumberFormatException e) { 438 JmriJOptionPane.showMessageDialog(this, (Bundle.getMessage("ParseRateError") + "\n" + e), 439 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 440 log.error("Exception when parsing user-entered rate", e); 441 return null; 442 } 443 if (rate < 0.0) { 444 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("NegativeRateError"), 445 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 446 return null; 447 } 448 if (InstanceManager.getDefault(jmri.ClockControl.class).requiresIntegerRate() && !clock.getInternalMaster()) { 449 double frac = rate - (int) rate; 450 if (frac > 0.001) { 451 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("NonIntegerError"), 452 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 453 return null; 454 } 455 } 456 return rate; 457 } 458 459 /** 460 * Handle Set Rate button. 461 * @param ev unused 462 */ 463 public void setRateButtonActionPerformed(ActionEvent ev) { 464 Double parsedRate = parseRate(factorField.getText()); 465 if (parsedRate == null) { 466 factorField.setText(threeDigits.format(clock.userGetRate())); 467 return; 468 } 469 try { 470 clock.userSetRate(parsedRate); 471 } catch (TimebaseRateException e) { 472 JmriJOptionPane.showMessageDialog(this, (Bundle.getMessage("SetRateError") + "\n" + e.getLocalizedMessage()), 473 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 474 // log.error is already called by clock.userSetRate 475 } 476 changed = true; 477 } 478 479 /** 480 * Handle time source change 481 * 482 * Only changes the time source if the rate is OK (typically: Integer) for new source 483 */ 484 private void setTimeSourceChanged(ActionEvent e) { 485 int index = timeSourceBox.getSelectedIndex(); 486 int oldIndex = internalSourceIndex; 487 if (!clock.getInternalMaster()) { 488 oldIndex = hardwareSourceIndex; 489 } 490 // return if nothing changed 491 if (oldIndex == index) { 492 return; 493 } 494 // change the time source master 495 if (index == internalSourceIndex) { 496 clock.setInternalMaster(true, true); 497 } else { 498 // only change if new source is okay with current rate 499 if (InstanceManager.getDefault(jmri.ClockControl.class).requiresIntegerRate()) { 500 double rate = clock.userGetRate(); 501 double frac = rate - (int) rate; 502 if (frac > 0.001) { 503 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("NonIntegerErrorCantChangeSource"), 504 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 505 timeSourceBox.setSelectedIndex(internalSourceIndex); 506 return; 507 } 508 } 509 clock.setInternalMaster(false, true); 510 } 511 changed = true; 512 } 513 514 /** 515 * Handle synchronize check box change 516 */ 517 private void synchronizeChanged(ActionEvent e) { 518 clock.setSynchronize(synchronizeCheckBox.isSelected(), true); 519 changed = true; 520 } 521 522 /** 523 * Handle correct check box change 524 */ 525 private void correctChanged(ActionEvent e) { 526 clock.setCorrectHardware(correctCheckBox.isSelected(), true); 527 changed = true; 528 } 529 530 /** 531 * Handle 12-hour display check box change 532 */ 533 private void displayChanged(ActionEvent e) { 534 clock.set12HourDisplay(displayCheckBox.isSelected(), true); 535 changed = true; 536 } 537 538 /** 539 * Handle Set Time button. 540 * @param ex unused 541 */ 542 public void setTimeButtonActionPerformed(ActionEvent ex) { 543 int hours; 544 int minutes; 545 // get hours, reporting errors if any 546 try { 547 hours = Integer.parseInt(hoursField.getText()); 548 } catch (NumberFormatException e) { 549 JmriJOptionPane.showMessageDialog(this, (Bundle.getMessage("HoursError") + "\n" + e), 550 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 551 log.error("Exception when parsing hours Field", e); 552 return; 553 } 554 if ((hours < 0) || (hours > 23)) { 555 JmriJOptionPane.showMessageDialog(this, (Bundle.getMessage("HoursRangeError")), 556 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 557 return; 558 } 559 // get minutes, reporting errors if any 560 try { 561 minutes = Integer.parseInt(minutesField.getText()); 562 } catch (NumberFormatException e) { 563 JmriJOptionPane.showMessageDialog(this, (Bundle.getMessage("MinutesError") + "\n" + e), 564 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 565 log.error("Exception when parsing Minutes Field", e); 566 return; 567 } 568 if ((minutes < 0) || (minutes > 59)) { 569 JmriJOptionPane.showMessageDialog(this, (Bundle.getMessage("MinutesRangeError")), 570 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 571 return; 572 } 573 // set time of the fast clock 574 long mSecPerHour = 3600000; 575 long mSecPerMinute = 60000; 576 Calendar cal = Calendar.getInstance(); 577 cal.setTime(clock.getTime()); 578 int cHours = cal.get(Calendar.HOUR_OF_DAY); 579 long cNumMSec = cal.getTime().getTime(); 580 long nNumMSec = ((cNumMSec / mSecPerHour) * mSecPerHour) - (cHours * mSecPerHour) 581 + (hours * mSecPerHour) + (minutes * mSecPerMinute); 582 clock.userSetTime(new Date(nNumMSec)); 583 showTime = true; 584 updateTime(); 585 } 586 587 /** 588 * Handle start run combo box change 589 */ 590 private void startRunBoxChanged(ActionEvent e) { 591 switch (startRunBox.getSelectedIndex()) { 592 case START_STOPPED: 593 clock.setClockInitialRunState(Timebase.ClockInitialRunState.DO_STOP); 594 break; 595 case START_RUNNING: 596 clock.setClockInitialRunState(Timebase.ClockInitialRunState.DO_START); 597 break; 598 default: 599 case START_NORUNCHANGE: 600 clock.setClockInitialRunState(Timebase.ClockInitialRunState.DO_NOTHING); 601 break; 602 } 603 changed = true; 604 } 605 606 /** 607 * Handle Show on/off button check box change 608 */ 609 private void showStopButtonChanged(ActionEvent e) { 610 clock.setShowStopButton(displayStartStopButton.isSelected()); 611 changed = true; 612 } 613 614 /** 615 * Handle start set time check box change 616 */ 617 private void startSetTimeChanged(ActionEvent ev) { 618 int hours; 619 int minutes; 620 // get hours, reporting errors if any 621 try { 622 hours = Integer.parseInt(startHoursField.getText()); 623 } catch (NumberFormatException e) { 624 JmriJOptionPane.showMessageDialog(this, (Bundle.getMessage("HoursError") + "\n" + e), 625 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 626 log.error("Exception when parsing hours Field", e); 627 return; 628 } 629 if ((hours < 0) || (hours > 23)) { 630 JmriJOptionPane.showMessageDialog(this, (Bundle.getMessage("HoursRangeError")), 631 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 632 return; 633 } 634 // get minutes, reporting errors if any 635 try { 636 minutes = Integer.parseInt(startMinutesField.getText()); 637 } catch (NumberFormatException e) { 638 JmriJOptionPane.showMessageDialog(this, (Bundle.getMessage("MinutesError") + "\n" + e), 639 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 640 log.error("Exception when parsing Minutes Field", e); 641 return; 642 } 643 if ((minutes < 0) || (minutes > 59)) { 644 JmriJOptionPane.showMessageDialog(this, (Bundle.getMessage("MinutesRangeError")), 645 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 646 return; 647 } 648 // set time of the fast clock 649 long mSecPerHour = 3600000; 650 long mSecPerMinute = 60000; 651 Calendar cal = Calendar.getInstance(); 652 int cHours = cal.get(Calendar.HOUR_OF_DAY); 653 long cNumMSec = cal.getTime().getTime(); 654 long nNumMSec = ((cNumMSec / mSecPerHour) * mSecPerHour) - (cHours * mSecPerHour) 655 + (hours * mSecPerHour) + (minutes * mSecPerMinute); 656 clock.setStartSetTime(startSetTimeCheckBox.isSelected(), new Date(nNumMSec)); 657 changed = true; 658 } 659 660 /** 661 * Handle start clock combo box change 662 */ 663 private void setClockStartChanged(ActionEvent e) { 664 int sel = Timebase.NONE; 665 switch (clockStartBox.getSelectedIndex()) { 666 case startNixieClock: 667 sel = Timebase.NIXIE_CLOCK; 668 break; 669 case startAnalogClock: 670 sel = Timebase.ANALOG_CLOCK; 671 break; 672 case startLcdClock: 673 sel = Timebase.LCD_CLOCK; 674 break; 675 case startPragotronClock: 676 sel = Timebase.PRAGOTRON_CLOCK; 677 break; 678 default: 679 break; 680 } 681 clock.setStartClockOption(sel); 682 changed = true; 683 } 684 685 /** 686 * Handle Start Clock button 687 * @param e unused 688 */ 689 public void startButtonActionPerformed(ActionEvent e) { 690 clock.setRun(true); 691 } 692 693 /** 694 * Handle Stop Clock button. 695 * @param e unused 696 */ 697 public void stopButtonActionPerformed(ActionEvent e) { 698 clock.setRun(false); 699 } 700 701 /** 702 * Update clock state information 703 */ 704 void updateTime() { 705 if (clock.getRun() || showTime) { 706 showTime = false; 707 setTimeLabel(); 708 timeLabel.setVisible(true); 709 } 710 } 711 712 /** 713 * Set the current Timebase time into timeLabel 714 */ 715 void setTimeLabel() { 716 // Get time 717 Calendar cal = Calendar.getInstance(); 718 cal.setTime(clock.getTime()); 719 int hours = cal.get(Calendar.HOUR_OF_DAY); 720 int minutes = cal.get(Calendar.MINUTE); 721 // Format and display the time 722 timeLabel.setText(" " + (hours / 10) + (hours - (hours / 10) * 10) + ":" 723 + (minutes / 10) + (minutes - (minutes / 10) * 10)); 724 timeLabel.setToolTipText(clock.getTime().toString()); 725 } 726 727 /** 728 * Handle a change to clock properties. 729 * {@inheritDoc} 730 */ 731 @Override 732 public void propertyChange(PropertyChangeEvent event) { 733 log.trace("propertyChange({})",event.getPropertyName()); 734 switch (event.getPropertyName()) { 735 case "run": 736 updateRunningButton(); 737 break; 738 case "rate": 739 factorField.setText(threeDigits.format(clock.userGetRate())); 740 break; 741 case "time": 742 updateTime(); 743 break; 744 case "config": //indicates that something in the clock config has changed, so update all 745 synchronizeCheckBox.setSelected(clock.getSynchronize()); 746 timeSourceBox.setSelectedIndex(clock.getInternalMaster() ? internalSourceIndex : hardwareSourceIndex); 747 switch (clock.getClockInitialRunState()) { 748 case DO_STOP: 749 startRunBox.setSelectedIndex(START_STOPPED); 750 break; 751 case DO_START: 752 startRunBox.setSelectedIndex(START_RUNNING); 753 break; 754 case DO_NOTHING: 755 startRunBox.setSelectedIndex(START_NORUNCHANGE); 756 break; 757 default: 758 jmri.util.LoggingUtil.warnOnce(log, "Unexpected initial run state = {}", clock.getClockInitialRunState()); 759 break; 760 } 761 startSetTimeCheckBox.setSelected(clock.getStartSetTime()); 762 displayStartStopButton.setSelected(clock.getShowStopButton()); 763 startSetRateCheckBox.setSelected(clock.getSetRateAtStart()); 764 Calendar cal = Calendar.getInstance(); 765 cal.setTime(clock.getStartTime()); 766 startHoursField.setText("" + cal.get(Calendar.HOUR_OF_DAY)); 767 startMinutesField.setText("" + cal.get(Calendar.MINUTE)); 768 startSetRateCheckBox.setSelected(clock.getSetRateAtStart()); 769 startFactorField.setText(threeDigits.format(clock.getStartRate())); 770 if (clock.getStartClockOption() == startNone) { 771 clockStartBox.setSelectedIndex(startNone); 772 } else if (clock.getStartClockOption() == Timebase.NIXIE_CLOCK) { 773 clockStartBox.setSelectedIndex(startNixieClock); 774 } else if (clock.getStartClockOption() == Timebase.ANALOG_CLOCK) { 775 clockStartBox.setSelectedIndex(startAnalogClock); 776 } else if (clock.getStartClockOption() == Timebase.LCD_CLOCK) { 777 clockStartBox.setSelectedIndex(startLcdClock); 778 } else if (clock.getStartClockOption() == Timebase.PRAGOTRON_CLOCK) { 779 clockStartBox.setSelectedIndex(startPragotronClock); 780 } 781 if (correctCheckBox != null) { 782 correctCheckBox.setSelected(clock.getCorrectHardware()); 783 } 784 break; 785 default: 786 // ignore all other properties 787 } 788 } 789 790 @Override 791 protected void handleModified() { 792 // ignore super routine 793 } 794 795 /** 796 * Handle Done button. 797 * @param e null if a save reminder, not null then from Done button action. 798 */ 799 public void doneButtonActionPerformed(ActionEvent e) { 800 if (changed && !checkEnabled) { 801 if (InstanceManager.getNullableDefault(jmri.UserPreferencesManager.class) != null) { 802 InstanceManager.getDefault(jmri.UserPreferencesManager.class). 803 showInfoMessage(Bundle.getMessage("ReminderTitle"), // NOI18N 804 Bundle.getMessage("ReminderSaveString", Bundle.getMessage("SimpleClockWindowTitle")), // NOI18N 805 getClassName(), 806 "remindSaveClock"); // NOI18N 807 } 808 } 809 changed = false; 810 setVisible(false); 811 super.windowClosing(null); 812 dispose(); 813 } 814 815 /** 816 * Get the class description for the UserMessagePreferencesPane. 817 * @return The class description 818 */ 819 public String getClassDescription() { 820 return "Fast Clock"; 821 } 822 823 /** 824 * Set the item details for the UserMessagePreferencesPane. 825 */ 826 public void setMessagePreferencesDetails() { 827 InstanceManager.getDefault(jmri.UserPreferencesManager.class). 828 setPreferenceItemDetails(getClassName(), "remindSaveClock", "HideSaveReminder"); // NOI18N 829 } 830 831 protected String getClassName() { 832 // The class that is returned must have a default constructor, 833 // a constructor with no parameters. 834 return this.getClass().getName(); 835 } 836 837 /** 838 * If data changed, prompt to store. 839 * {@inheritDoc} 840 */ 841 @Override 842 @SuppressFBWarnings(value = "OVERRIDING_METHODS_MUST_INVOKE_SUPER", 843 justification = "This calls doneButtonActionPerformed which calls super.windowClosing()") 844 public void windowClosing(WindowEvent e) { 845 doneButtonActionPerformed(null); 846 } 847 848 @Override 849 public void dispose() { 850 if ( clock != null ) { 851 clock.removePropertyChangeListener(this); 852 } 853 super.dispose(); 854 } 855 856 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(SimpleClockFrame.class); 857 858}