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