001package jmri.jmrit.conditional; 002 003import java.awt.Component; 004import java.awt.Container; 005import java.awt.event.ActionEvent; 006import java.awt.event.ActionListener; 007import java.util.ArrayList; 008import java.util.EventListener; 009import java.util.HashMap; 010import java.util.List; 011import java.util.TreeSet; 012 013import javax.swing.JFrame; 014import javax.swing.JTabbedPane; 015import javax.swing.JTable; 016import javax.swing.JTextField; 017import javax.swing.event.ListSelectionEvent; 018import javax.swing.event.ListSelectionListener; 019 020import jmri.Audio; 021import jmri.Conditional; 022import jmri.ConditionalManager; 023import jmri.ConditionalVariable; 024import jmri.InstanceManager; 025import jmri.Light; 026import jmri.LightManager; 027import jmri.Logix; 028import jmri.LogixManager; 029import jmri.Memory; 030import jmri.MemoryManager; 031import jmri.NamedBean; 032import jmri.Route; 033import jmri.Sensor; 034import jmri.SensorManager; 035import jmri.SignalHead; 036import jmri.SignalHeadManager; 037import jmri.SignalMast; 038import jmri.SignalMastManager; 039import jmri.Turnout; 040import jmri.TurnoutManager; 041import jmri.NamedBean.DisplayOptions; 042import jmri.jmrit.beantable.LRouteTableAction; 043import jmri.jmrit.entryexit.DestinationPoints; 044import jmri.jmrit.entryexit.EntryExitPairs; 045import jmri.jmrit.logix.OBlock; 046import jmri.jmrit.logix.OBlockManager; 047import jmri.jmrit.logix.Warrant; 048import jmri.jmrit.logix.WarrantManager; 049import jmri.jmrit.picker.PickFrame; 050import jmri.jmrit.picker.PickListModel; 051import jmri.jmrit.picker.PickSinglePanel; 052import jmri.swing.NamedBeanComboBox; 053import jmri.util.JmriJFrame; 054import jmri.util.swing.JComboBoxUtil; 055import jmri.util.swing.JmriJOptionPane; 056 057/** 058 * This is the base class for the Conditional edit view classes. Contains shared 059 * variables and methods. 060 * 061 * @author Dave Sand copyright (c) 2017 062 */ 063public class ConditionalEditBase { 064 065 /** 066 * Set the Logix and Conditional managers and set the selection mode. 067 * 068 * @param sName the Logix system name being edited 069 */ 070 public ConditionalEditBase(String sName) { 071// _logixManager = InstanceManager.getNullableDefault(jmri.LogixManager.class); 072// _conditionalManager = InstanceManager.getNullableDefault(jmri.ConditionalManager.class); 073 _logixManager = InstanceManager.getDefault(jmri.LogixManager.class); 074 _conditionalManager = InstanceManager.getDefault(jmri.ConditionalManager.class); 075 _curLogix = _logixManager.getBySystemName(sName); 076 loadSelectionMode(); 077 } 078 079 public ConditionalEditBase() { 080 } 081 082 // ------------ variable definitions ------------ 083 ConditionalManager _conditionalManager = null; 084 LogixManager _logixManager = null; 085 Logix _curLogix = null; 086 JmriJFrame _editLogixFrame = null; 087 088 boolean _inEditMode = false; 089 090 boolean _showReminder = false; 091 boolean _suppressReminder = false; 092 boolean _suppressIndirectRef = false; 093 private boolean _checkEnabled = jmri.InstanceManager.getDefault(jmri.configurexml.ShutdownPreferences.class).isStoreCheckEnabled(); 094 095 /** 096 * Input selection names. 097 * 098 * @since 4.7.3 099 */ 100 public enum SelectionMode { 101 /** 102 * Use the traditional text field, with the tabbed Pick List available 103 * for drag-n-drop 104 */ 105 USEMULTI, 106 /** 107 * Use the traditional text field, but with a single Pick List that 108 * responds with a click 109 */ 110 USESINGLE, 111 /** 112 * Use combo boxes to select names instead of a text field. 113 */ 114 USECOMBO; 115 } 116 SelectionMode _selectionMode; 117 118 /** 119 * Get the saved mode selection, default to the tranditional tabbed pick 120 * list. 121 * <p> 122 * During the menu build process, the corresponding menu item is set to 123 * selected. 124 * 125 * @since 4.7.3 126 */ 127 void loadSelectionMode() { 128 Object modeName = InstanceManager.getDefault(jmri.UserPreferencesManager.class).getProperty("jmri.jmrit.beantable.LogixTableAction", "Selection Mode"); // NOI18N 129 if (modeName == null) { 130 _selectionMode = SelectionMode.USEMULTI; 131 } else { 132 String currentMode = (String) modeName; 133 switch (currentMode) { 134 case "USEMULTI": // NOI18N 135 _selectionMode = SelectionMode.USEMULTI; 136 break; 137 case "USESINGLE": // NOI18N 138 _selectionMode = SelectionMode.USESINGLE; 139 break; 140 case "USECOMBO": // NOI18N 141 _selectionMode = SelectionMode.USECOMBO; 142 break; 143 default: 144 log.warn("Invalid Logix conditional selection mode value, '{}', returned", currentMode); // NOI18N 145 _selectionMode = SelectionMode.USEMULTI; 146 } 147 } 148 } 149 150 // ------------ PickList components ------------ 151 JTable _pickTable = null; // Current pick table 152 JTabbedPane _pickTabPane = null; // The tabbed panel for the pick table 153 PickFrame _pickTables; 154 155 JFrame _pickSingleFrame = null; 156 PickSingleListener _pickListener = null; 157 158 // ------------ Logix Notifications ------------ 159 // The Conditional views support some direct changes to the parent logix. 160 // This custom event is used to notify the parent Logix that changes are requested. 161 // When the event occurs, the parent Logix can retrieve the necessary information 162 // to carry out the actions. 163 // 164 // 1) Notify the calling Logix that the Logix user name has been changed. 165 // 2) Notify the calling Logix that the conditional view is closing 166 // 3) Notify the calling Logix that it is to be deleted 167 /** 168 * Create a custom listener event. 169 */ 170 public interface LogixEventListener extends EventListener { 171 172 void logixEventOccurred(); 173 } 174 175 /** 176 * Maintain a list of listeners -- normally only one. 177 */ 178 List<LogixEventListener> listenerList = new ArrayList<>(); 179 180 /** 181 * This contains a list of commands to be processed by the listener 182 * recipient. 183 */ 184 public HashMap<String, String> logixData = new HashMap<>(); 185 186 /** 187 * Add a listener. 188 * 189 * @param listener The recipient 190 */ 191 public void addLogixEventListener(LogixEventListener listener) { 192 listenerList.add(listener); 193 } 194 195 /** 196 * Remove a listener -- not used. 197 * 198 * @param listener The recipient 199 */ 200 public void removeLogixEventListener(LogixEventListener listener) { 201 listenerList.remove(listener); 202 } 203 204 /** 205 * Notify the listeners to check for new data. 206 */ 207 void fireLogixEvent() { 208 for (LogixEventListener l : listenerList) { 209 l.logixEventOccurred(); 210 } 211 } 212 213 // ------------ Antecedent Methods ------------ 214 215 /** 216 * Create an antecedent string based on the current variables 217 * <p> 218 * The antecedent consists of all of the variables "in order" 219 * combined with the current operator. 220 * @since 4.11.5 221 * @param variableList The current variable list 222 * @return the resulting antecedent string 223 */ 224 String makeAntecedent(List<ConditionalVariable> variableList) { 225 StringBuilder antecedent = new StringBuilder(64); 226 if (variableList.size() != 0) { 227 String row = "R"; // NOI18N 228 if (variableList.get(0).isNegated()) { 229 antecedent.append("not "); 230 } 231 antecedent.append(row + "1"); 232 for (int i = 1; i < variableList.size(); i++) { 233 ConditionalVariable variable = variableList.get(i); 234 switch (variable.getOpern()) { 235 case AND: 236 antecedent.append(" and "); 237 break; 238 case OR: 239 antecedent.append(" or "); 240 break; 241 default: 242 break; 243 } 244 if (variable.isNegated()) { 245 antecedent = antecedent.append("not "); 246 } 247 antecedent.append(row); 248 antecedent.append(i + 1); 249 } 250 } 251 return antecedent.toString(); 252 } 253 254 /** 255 * Add a variable R# entry to the antecedent string. 256 * If not the first one, include <strong>and</strong> or <strong>or</strong> depending on the logic type 257 * @since 4.11.5 258 * @param logicType The current logic type. 259 * @param varListSize The current size of the variable list. 260 * @param antecedent The current antecedent 261 * @return an extended antecedent 262 */ 263 String appendToAntecedent(Conditional.AntecedentOperator logicType, int varListSize, String antecedent) { 264 if (varListSize > 1) { 265 if (logicType == Conditional.AntecedentOperator.ALL_OR) { 266 antecedent = antecedent + " or "; // NOI18N 267 } else { 268 antecedent = antecedent + " and "; // NOI18N 269 } 270 } 271 return antecedent + "R" + varListSize; // NOI18N 272 } 273 274 /** 275 * Check the antecedent and logic type. 276 * <p> 277 * The antecedent text is translated and verified. A new one is created if necessary. 278 * @since 4.11.5 279 * @param logicType The current logic type. Types other than Mixed are ignored. 280 * @param antecedentText The proposed antecedent string using the local language. 281 * @param variableList The current variable list. 282 * @param curConditional The current conditional. 283 * @return false if antecedent can't be validated 284 */ 285 boolean validateAntecedent(Conditional.AntecedentOperator logicType, String antecedentText, List<ConditionalVariable> variableList, Conditional curConditional) { 286 if (logicType != Conditional.AntecedentOperator.MIXED 287 || LRouteTableAction.getLogixInitializer().equals(_curLogix.getSystemName()) 288 || antecedentText == null 289 || antecedentText.trim().length() == 0) { 290 return true; 291 } 292 293 String antecedent = translateAntecedent(antecedentText, true); 294 if (antecedent.length() > 0) { 295 String message = curConditional.validateAntecedent(antecedent, variableList); 296 if (message != null) { 297 JmriJOptionPane.showMessageDialog(_editLogixFrame, 298 message + Bundle.getMessage("ParseError8"), // NOI18N 299 Bundle.getMessage("ErrorTitle"), // NOI18N 300 JmriJOptionPane.ERROR_MESSAGE); 301 return false; 302 } 303 } 304 return true; 305 } 306 307 /** 308 * Translate an antecedent string between English and the current language 309 * as determined by the Bundle classes. 310 * <p> 311 * The property files have Logic??? keys for translating to the target language. 312 * @since 4.11.5 313 * @param antecedent The antecedent string which can either local or English 314 * @param isLocal True if the antecedent string has local words. 315 * @return the translated antecedent string. 316 */ 317 public static String translateAntecedent(String antecedent, boolean isLocal) { 318 if (antecedent == null) { 319 return null; 320 } 321 String oldAnd, oldOr, oldNot; 322 String newAnd, newOr, newNot; 323 if (isLocal) { 324 // To English 325 oldAnd = Bundle.getMessage("LogicAND").toLowerCase(); // NOI18N 326 oldOr = Bundle.getMessage("LogicOR").toLowerCase(); // NOI18N 327 oldNot = Bundle.getMessage("LogicNOT").toLowerCase(); // NOI18N 328 newAnd = "and"; // NOI18N 329 newOr = "or"; // NOI18N 330 newNot = "not"; // NOI18N 331 } else { 332 // From English 333 oldAnd = "and"; // NOI18N 334 oldOr = "or"; // NOI18N 335 oldNot = "not"; // NOI18N 336 newAnd = Bundle.getMessage("LogicAND").toLowerCase(); // NOI18N 337 newOr = Bundle.getMessage("LogicOR").toLowerCase(); // NOI18N 338 newNot = Bundle.getMessage("LogicNOT").toLowerCase(); // NOI18N 339 } 340 log.debug("translateAntecedent: before {}", antecedent); 341 antecedent = antecedent.replaceAll(oldAnd, newAnd); 342 antecedent = antecedent.replaceAll(oldOr, newOr); 343 antecedent = antecedent.replaceAll(oldNot, newNot); 344 log.debug("translateAntecedent: after {}", antecedent); 345 return antecedent; 346 } 347 348 // ------------ Shared Conditional Methods ------------ 349 350 /** 351 * Verify that the user name is not a duplicate for the selected Logix. 352 * 353 * @param uName is the user name to be checked 354 * @param logix is the Logix that is being updated 355 * @return true if the name is unique 356 */ 357 boolean checkConditionalUserName(String uName, Logix logix) { 358 if (uName != null && uName.length() > 0) { 359 Conditional p = _conditionalManager.getByUserName(logix, uName); 360 if (p != null) { 361 // Conditional with this user name already exists 362 log.error("Failure to update Conditional with Duplicate User Name: {}", uName); 363 JmriJOptionPane.showMessageDialog(_editLogixFrame, 364 Bundle.getMessage("Error10"), // NOI18N 365 Bundle.getMessage("ErrorTitle"), // NOI18N 366 JmriJOptionPane.ERROR_MESSAGE); 367 return false; 368 } 369 } // else return true; 370 return true; 371 } 372 373 /** 374 * Create a combo name box for Variable and Action name selection. 375 * 376 * @param itemType The selected variable or action type 377 * @return nameBox A combo box based on the item type 378 */ 379 NamedBeanComboBox<?> createNameBox(Conditional.ItemType itemType) { 380 NamedBeanComboBox<?> nameBox; 381 switch (itemType) { 382 case SENSOR: // 1 383 nameBox = new NamedBeanComboBox<Sensor>( 384 InstanceManager.getDefault(SensorManager.class), null, DisplayOptions.DISPLAYNAME); 385 break; 386 case TURNOUT: // 2 387 nameBox = new NamedBeanComboBox<Turnout>( 388 InstanceManager.getDefault(TurnoutManager.class), null, DisplayOptions.DISPLAYNAME); 389 break; 390 case LIGHT: // 3 391 nameBox = new NamedBeanComboBox<Light>( 392 InstanceManager.getDefault(LightManager.class), null, DisplayOptions.DISPLAYNAME); 393 break; 394 case SIGNALHEAD: // 4 395 nameBox = new NamedBeanComboBox<SignalHead>( 396 InstanceManager.getDefault(SignalHeadManager.class), null, DisplayOptions.DISPLAYNAME); 397 break; 398 case SIGNALMAST: // 5 399 nameBox = new NamedBeanComboBox<SignalMast>( 400 InstanceManager.getDefault(SignalMastManager.class), null, DisplayOptions.DISPLAYNAME); 401 break; 402 case MEMORY: // 6 403 nameBox = new NamedBeanComboBox<Memory>( 404 InstanceManager.getDefault(MemoryManager.class), null, DisplayOptions.DISPLAYNAME); 405 break; 406 case LOGIX: // 7 407 nameBox = new NamedBeanComboBox<Logix>( 408 InstanceManager.getDefault(LogixManager.class), null, DisplayOptions.DISPLAYNAME); 409 break; 410 case WARRANT: // 8 411 nameBox = new NamedBeanComboBox<Warrant>( 412 InstanceManager.getDefault(WarrantManager.class), null, DisplayOptions.DISPLAYNAME); 413 break; 414 case OBLOCK: // 10 415 nameBox = new NamedBeanComboBox<OBlock>( 416 InstanceManager.getDefault(OBlockManager.class), null, DisplayOptions.DISPLAYNAME); 417 break; 418 case ENTRYEXIT: // 11 419 nameBox = new NamedBeanComboBox<DestinationPoints>( 420 InstanceManager.getDefault(EntryExitPairs.class), null, DisplayOptions.DISPLAYNAME); 421 break; 422 case OTHER: // 14 423 nameBox = new NamedBeanComboBox<Route>( 424 InstanceManager.getDefault(jmri.RouteManager.class), null, DisplayOptions.DISPLAYNAME); 425 break; 426 default: 427 return null; // Skip any other items. 428 } 429 nameBox.setAllowNull(true); 430 JComboBoxUtil.setupComboBoxMaxRows(nameBox); 431 return nameBox; 432 } 433 434 /** 435 * Listen for name combo box selection events. 436 * <p> 437 * When a combo box row is selected, the user/system name is copied to the 438 * Action or Variable name field. 439 * 440 * @since 4.7.3 441 */ 442 static class NameBoxListener implements ActionListener { 443 444 /** 445 * @param textField The target field object when an entry is selected 446 */ 447 public NameBoxListener(JTextField textField) { 448 saveTextField = textField; 449 } 450 451 JTextField saveTextField; 452 453 @Override 454 public void actionPerformed(ActionEvent e) { 455 // Get the combo box and display name 456 Object src = e.getSource(); 457 if (!(src instanceof NamedBeanComboBox)) { 458 return; 459 } 460 NamedBeanComboBox<?> srcBox = (NamedBeanComboBox<?>) src; 461 String newName = srcBox.getSelectedItemDisplayName(); 462 463 if (log.isDebugEnabled()) { 464 log.debug("NameBoxListener: new name = '{}'", newName); // NOI18N 465 } 466 saveTextField.setText(newName); 467 } 468 } 469 470 // ------------ Single Pick List Table Methods ------------ 471 472 /** 473 * Create a single panel picklist JFrame for choosing action and variable 474 * names. 475 * 476 * @since 4.7.3 477 * @param itemType The selected variable or action type 478 * @param listener The listener to be assigned to the picklist 479 * @param actionType True if Action, false if Variable. 480 */ 481 void createSinglePanelPickList(Conditional.ItemType itemType, PickSingleListener listener, boolean actionType) { 482 if (_pickListener != null) { 483 Conditional.ItemType saveType = _pickListener.getItemType(); 484 if (saveType != itemType) { 485 // The type has changed, need to start over 486 closeSinglePanelPickList(); 487 } else { 488 // The pick list has already been created 489 return; 490 } 491 } 492 493 PickSinglePanel<?> _pickSingle; 494 495 switch (itemType) { 496 case SENSOR: // 1 497 _pickSingle = new PickSinglePanel<Sensor>(PickListModel.sensorPickModelInstance()); 498 break; 499 case TURNOUT: // 2 500 _pickSingle = new PickSinglePanel<Turnout>(PickListModel.turnoutPickModelInstance()); 501 break; 502 case LIGHT: // 3 503 _pickSingle = new PickSinglePanel<Light>(PickListModel.lightPickModelInstance()); 504 break; 505 case SIGNALHEAD: // 4 506 _pickSingle = new PickSinglePanel<SignalHead>(PickListModel.signalHeadPickModelInstance()); 507 break; 508 case SIGNALMAST: // 5 509 _pickSingle = new PickSinglePanel<SignalMast>(PickListModel.signalMastPickModelInstance()); 510 break; 511 case MEMORY: // 6 512 _pickSingle = new PickSinglePanel<Memory>(PickListModel.memoryPickModelInstance()); 513 break; 514 case LOGIX: // 7 -- can be either Logix or Conditional 515 if (!actionType) { 516 // State Variable 517 return; 518 } 519 _pickSingle = new PickSinglePanel<Logix>(PickListModel.logixPickModelInstance()); 520 break; 521 case WARRANT: // 8 522 _pickSingle = new PickSinglePanel<Warrant>(PickListModel.warrantPickModelInstance()); 523 break; 524 case OBLOCK: // 10 525 _pickSingle = new PickSinglePanel<OBlock>(PickListModel.oBlockPickModelInstance()); 526 break; 527 case ENTRYEXIT: // 11 528 _pickSingle = new PickSinglePanel<jmri.jmrit.entryexit.DestinationPoints>(PickListModel.entryExitPickModelInstance()); 529 break; 530 default: 531 return; // Skip any other items. 532 } 533 534 // Create the JFrame 535 _pickSingleFrame = new JmriJFrame(Bundle.getMessage("SinglePickFrame")); // NOI18N 536 _pickSingleFrame.setContentPane(_pickSingle); 537 _pickSingleFrame.pack(); 538 _pickSingleFrame.setVisible(true); 539 _pickSingleFrame.toFront(); 540 541 // Set the table selection listener 542 _pickListener = listener; 543 _pickTable = _pickSingle.getTable(); 544 _pickTable.getSelectionModel().addListSelectionListener(_pickListener); 545 } 546 547 /** 548 * Close a single panel picklist JFrame and related items. 549 * 550 * @since 4.7.3 551 */ 552 void closeSinglePanelPickList() { 553 if (_pickSingleFrame != null) { 554 _pickSingleFrame.setVisible(false); 555 _pickSingleFrame.dispose(); 556 _pickSingleFrame = null; 557 _pickListener = null; 558 _pickTable = null; 559 } 560 } 561 562 /** 563 * Listen for Pick Single table click events. 564 * <p> 565 * When a table row is selected, the user/system name is copied to the 566 * Action or Variable name field. 567 * 568 * @since 4.7.3 569 */ 570 class PickSingleListener implements ListSelectionListener { 571 572 /** 573 * @param textField The target field object when an entry is selected 574 * @param itemType The current selected table type number 575 */ 576 public PickSingleListener(JTextField textField, Conditional.ItemType itemType) { 577 saveItemType = itemType; 578 saveTextField = textField; 579 } 580 581 JTextField saveTextField; 582 Conditional.ItemType saveItemType; // Current table type 583 584 @Override 585 public void valueChanged(ListSelectionEvent e) { 586 int selectedRow = _pickTable.getSelectedRow(); 587 if (selectedRow >= 0) { 588 int selectedCol = _pickTable.getSelectedColumn(); 589 String newName = (String) _pickTable.getValueAt(selectedRow, selectedCol); 590 if (log.isDebugEnabled()) { 591 log.debug("Pick single panel row event: row = '{}', column = '{}', selected name = '{}'", // NOI18N 592 selectedRow, selectedCol, newName); 593 } 594 saveTextField.setText(newName); 595 } 596 } 597 598 public Conditional.ItemType getItemType() { 599 return saveItemType; 600 } 601 } 602 603 // ------------ Pick List Table Methods ------------ 604 605 /** 606 * Open a new drag-n-drop Pick List to drag Variable and Action names from 607 * to form Logix Conditionals. 608 */ 609 void openPickListTable() { 610 if (_pickTables == null) { 611 _pickTables = new jmri.jmrit.picker.PickFrame(Bundle.getMessage("TitlePickList")); // NOI18N 612 } else { 613 _pickTables.setVisible(true); 614 } 615 _pickTables.toFront(); 616 } 617 618 /** 619 * Hide the drag-n-drop Pick List if the last detail edit is closing. 620 */ 621 void hidePickListTable() { 622 if (_pickTables != null) { 623 _pickTables.setVisible(false); 624 } 625 } 626 627 /** 628 * Set the pick list tab based on the variable or action type. If there is 629 * not a corresponding tab, hide the picklist. 630 * 631 * @param curType is the current type 632 * @param actionType True if Action, false if Variable. 633 */ 634 void setPickListTab(Conditional.ItemType curType, boolean actionType) { 635 boolean tabSet = true; 636 if (_pickTables == null) { 637 return; 638 } 639 if (_pickTabPane == null) { 640 findPickListTabPane(_pickTables.getComponents(), 1); 641 } 642 if (_pickTabPane != null) { 643 // Convert variable/action type to the corresponding tab index 644 int tabIndex = 0; 645 switch (curType) { 646 case SENSOR: // 1 647 tabIndex = 1; 648 break; 649 case TURNOUT: // 2 650 tabIndex = 0; 651 break; 652 case LIGHT: // 3 653 tabIndex = 6; 654 break; 655 case SIGNALHEAD: // 4 656 tabIndex = 2; 657 break; 658 case SIGNALMAST: // 5 659 tabIndex = 3; 660 break; 661 case MEMORY: // 6 662 tabIndex = 4; 663 break; 664 case LOGIX: // 7 Conditional (Variable) or Logix (Action) 665 if (actionType) { 666 tabIndex = 10; 667 } else { 668 // State Variable 669 tabSet = false; 670 } 671 break; 672 case WARRANT: // 8 673 tabIndex = 7; 674 break; 675 case OBLOCK: // 10 676 tabIndex = 8; 677 break; 678 case ENTRYEXIT: // 11 679 tabIndex = 9; 680 break; 681 default: 682 // No tab found 683 tabSet = false; 684 } 685 if (tabSet) { 686 _pickTabPane.setSelectedIndex(tabIndex); 687 } 688 } 689 _pickTables.setVisible(tabSet); 690 return; 691 } 692 693 /** 694 * Recursive search for the tab panel. 695 * 696 * @param compList The components for the current Level 697 * @param level The current level in the structure 698 */ 699 void findPickListTabPane(Component[] compList, int level) { 700 for (Component compItem : compList) { 701 // Safety catch 702 if (level > 10) { 703 log.warn("findPickListTabPane: safety breaker reached"); // NOI18N 704 return; 705 } 706 707 if (compItem instanceof JTabbedPane) { 708 _pickTabPane = (JTabbedPane) compItem; 709 } else { 710 int nextLevel = level + 1; 711 Container nextItem = (Container) compItem; 712 Component[] nextList = nextItem.getComponents(); 713 findPickListTabPane(nextList, nextLevel); 714 } 715 } 716 return; 717 } 718 719 // ------------ Manage Conditional Reference map ------------ 720 721 /** 722 * Build a tree set from conditional references. 723 * 724 * @since 4.7.4 725 * @param varList The ConditionalVariable list that might contain 726 * conditional references 727 * @param treeSet A tree set to be built from the varList data 728 */ 729 void loadReferenceNames(List<ConditionalVariable> varList, TreeSet<String> treeSet) { 730 treeSet.clear(); 731 for (ConditionalVariable var : varList) { 732 if (var.getType() == Conditional.Type.CONDITIONAL_TRUE 733 || var.getType() == Conditional.Type.CONDITIONAL_FALSE) { 734 treeSet.add(var.getName()); 735 } 736 } 737 } 738 739 /** 740 * Check for conditional references. 741 * 742 * @since 4.7.4 743 * @param logixName The Logix under consideration 744 * @return true if no references 745 */ 746 boolean checkConditionalReferences(String logixName) { 747 Logix x = _logixManager.getLogix(logixName); 748 int numConditionals = x.getNumConditionals(); 749 for (int i = 0; i < numConditionals; i++) { 750 String csName = x.getConditionalByNumberOrder(i); 751 752 // If the conditional is a where used target, check scope 753 ArrayList<String> refList = InstanceManager.getDefault(jmri.ConditionalManager.class).getWhereUsed(csName); 754 if (refList != null) { 755 for (String refName : refList) { 756 Logix xRef = _conditionalManager.getParentLogix(refName); 757 String xsName = xRef.getSystemName(); 758 if (logixName.equals(xsName)) { 759 // Member of the same Logix 760 continue; 761 } 762 763 // External references have to be removed before the Logix can be deleted. 764 Conditional c = x.getConditional(csName); 765 Conditional cRef = xRef.getConditional(refName); 766 Object[] msgs = new Object[]{c.getUserName(), c.getSystemName(), cRef.getUserName(), 767 cRef.getSystemName(), xRef.getUserName(), xRef.getSystemName()}; 768 JmriJOptionPane.showMessageDialog(_editLogixFrame, 769 Bundle.getMessage("Error11", msgs), // NOI18N 770 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); // NOI18N 771 return false; 772 } 773 } 774 } 775 return true; 776 } 777 778 /** 779 * Update the conditional reference where used. 780 * <p> 781 * The difference between the saved target names and new target names is 782 * used to add/remove where used references. 783 * 784 * @since 4.7.4 785 * @param oldTargetNames The conditional target names before updating 786 * @param newTargetNames The conditional target names after updating 787 * @param refName The system name for the referencing conditional 788 */ 789 void updateWhereUsed(TreeSet<String> oldTargetNames, TreeSet<String> newTargetNames, String refName) { 790 TreeSet<String> deleteNames = new TreeSet<>(oldTargetNames); 791 deleteNames.removeAll(newTargetNames); 792 for (String deleteName : deleteNames) { 793 InstanceManager.getDefault(jmri.ConditionalManager.class).removeWhereUsed(deleteName, refName); 794 } 795 796 TreeSet<String> addNames = new TreeSet<>(newTargetNames); 797 addNames.removeAll(oldTargetNames); 798 for (String addName : addNames) { 799 InstanceManager.getDefault(jmri.ConditionalManager.class).addWhereUsed(addName, refName); 800 } 801 } 802 803 // ------------ Utility Methods - Data Validation ------------ 804 /** 805 * Display reminder to save. The class is set to LogixTableAction. 806 */ 807 void showSaveReminder() { 808 if (_showReminder && !_checkEnabled) { 809 if (InstanceManager.getNullableDefault(jmri.UserPreferencesManager.class) != null) { 810 InstanceManager.getDefault(jmri.UserPreferencesManager.class). 811 showInfoMessage(Bundle.getMessage("ReminderTitle"), Bundle.getMessage("ReminderSaveString", // NOI18N 812 Bundle.getMessage("MenuItemLogixTable")), // NOI18N 813 "jmri.jmrit.beantable.LogixTableAction", 814 "remindSaveLogix"); // NOI18N 815 } 816 } 817 } 818 819 /** 820 * Check if String is an integer or references an integer. 821 * 822 * @param actionType Conditional action to check for, i.e. 823 * ACTION_SET_LIGHT_INTENSITY 824 * @param intReference string referencing a decimal for light intensity or 825 * the name of a memory 826 * @return true if either correct decimal format or a memory with the given 827 * name is present 828 */ 829 boolean validateIntensityReference(Conditional.Action actionType, String intReference) { 830 if (intReference == null || intReference.trim().length() == 0) { 831 displayBadNumberReference(actionType); 832 return false; 833 } 834 try { 835 return validateIntensity(Integer.parseInt(intReference)); 836 } catch (NumberFormatException e) { 837 String intRef = intReference; 838 if (intReference.length() > 1 && intReference.charAt(0) == '@') { 839 intRef = intRef.substring(1); 840 } 841 if (!confirmIndirectMemory(intRef)) { 842 return false; 843 } 844 intRef = validateMemoryReference(intRef); 845 if (intRef != null) // memory named 'intReference' exists 846 { 847 Memory m = InstanceManager.memoryManagerInstance().getByUserName(intRef); 848 if (m == null) { 849 m = InstanceManager.memoryManagerInstance().getBySystemName(intRef); 850 } 851 try { 852 if (m == null || m.getValue() == null) { 853 throw new NumberFormatException(); 854 } 855 validateIntensity(Integer.parseInt((String) m.getValue())); 856 } catch (NumberFormatException ex) { 857 JmriJOptionPane.showMessageDialog(_editLogixFrame, 858 Bundle.getMessage("Error24", intReference), 859 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 860 } 861 return true; // above is a warning to set memory correctly 862 } 863 displayBadNumberReference(actionType); 864 } 865 return false; 866 } 867 868 /** 869 * Check if text represents an integer is suitable for percentage w/o 870 * NumberFormatException. 871 * 872 * @param time value to use as light intensity percentage 873 * @return true if time is an integer in range 0 - 100 874 */ 875 boolean validateIntensity(int time) { 876 if (time < 0 || time > 100) { 877 JmriJOptionPane.showMessageDialog(_editLogixFrame, 878 Bundle.getMessage("Error38", time, Bundle.getMessage("Error42")), 879 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 880 return false; 881 } 882 return true; 883 } 884 885 /** 886 * Check if a string is decimal or references a decimal. 887 * 888 * @param actionType enum representing the Conditional action type being 889 * checked, i.e. ACTION_DELAYED_TURNOUT 890 * @param ref entry to check 891 * @return true if ref is itself a decimal or user will provide one from a 892 * Memory at run time 893 */ 894 boolean validateTimeReference(Conditional.Action actionType, String ref) { 895 if (ref == null || ref.trim().length() == 0) { 896 displayBadNumberReference(actionType); 897 return false; 898 } 899 try { 900 return validateTime(actionType, Float.parseFloat(ref)); 901 // return true if ref is decimal within allowed range 902 } catch (NumberFormatException e) { 903 String memRef = ref; 904 if (ref.length() > 1 && ref.charAt(0) == '@') { 905 memRef = ref.substring(1); 906 } 907 if (!confirmIndirectMemory(memRef)) { 908 return false; 909 } 910 memRef = validateMemoryReference(memRef); 911 if (memRef != null) // memory named 'intReference' exists 912 { 913 Memory m = InstanceManager.memoryManagerInstance().getByUserName(memRef); 914 if (m == null) { 915 m = InstanceManager.memoryManagerInstance().getBySystemName(memRef); 916 } 917 try { 918 if (m == null || m.getValue() == null) { 919 throw new NumberFormatException(); 920 } 921 validateTime(actionType, Float.parseFloat((String) m.getValue())); 922 } catch (NumberFormatException ex) { 923 JmriJOptionPane.showMessageDialog(_editLogixFrame, 924 Bundle.getMessage("Error24", memRef), 925 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 926 } 927 return true; // above is a warning to set memory correctly 928 } 929 displayBadNumberReference(actionType); 930 } 931 return false; 932 } 933 934 /** 935 * Range check time entry (assumes seconds). 936 * 937 * @param actionType integer representing the Conditional action type being 938 * checked, i.e. ACTION_DELAYED_TURNOUT 939 * @param time value to be checked 940 * @return false if time > 3600 (seconds) or too small 941 */ 942 boolean validateTime(Conditional.Action actionType, float time) { 943 float maxTime = 3600; // more than 1 hour 944 float minTime = 0.020f; 945 if (time < minTime || time > maxTime) { 946 String errorNum = " "; 947 switch (actionType) { 948 case DELAYED_TURNOUT: 949 errorNum = "Error39"; // NOI18N 950 break; 951 case RESET_DELAYED_TURNOUT: 952 errorNum = "Error41"; // NOI18N 953 break; 954 case DELAYED_SENSOR: 955 errorNum = "Error23"; // NOI18N 956 break; 957 case RESET_DELAYED_SENSOR: 958 errorNum = "Error27"; // NOI18N 959 break; 960 case SET_LIGHT_TRANSITION_TIME: 961 errorNum = "Error29"; // NOI18N 962 break; 963 default: 964 break; 965 } 966 JmriJOptionPane.showMessageDialog(_editLogixFrame, 967 Bundle.getMessage("Error38", time, Bundle.getMessage(errorNum)), 968 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 969 return false; 970 } 971 return true; 972 } 973 974 /** 975 * Display an error message to user when an invalid number is provided in 976 * Conditional setup. 977 * 978 * @param actionType integer representing the Conditional action type being 979 * checked, i.e. ACTION_DELAYED_TURNOUT 980 */ 981 void displayBadNumberReference(Conditional.Action actionType) { 982 String errorNum = " "; 983 switch (actionType) { 984 case DELAYED_TURNOUT: 985 errorNum = "Error39"; // NOI18N 986 break; 987 case RESET_DELAYED_TURNOUT: 988 errorNum = "Error41"; // NOI18N 989 break; 990 case DELAYED_SENSOR: 991 errorNum = "Error23"; // NOI18N 992 break; 993 case RESET_DELAYED_SENSOR: 994 errorNum = "Error27"; // NOI18N 995 break; 996 case SET_LIGHT_INTENSITY: 997 JmriJOptionPane.showMessageDialog(_editLogixFrame, 998 Bundle.getMessage("Error43"), // NOI18N 999 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); // NOI18N 1000 return; 1001 case SET_LIGHT_TRANSITION_TIME: 1002 errorNum = "Error29"; // NOI18N 1003 break; 1004 default: 1005 log.warn("Unexpected action type {} in displayBadNumberReference", actionType); // NOI18N 1006 } 1007 JmriJOptionPane.showMessageDialog(_editLogixFrame, 1008 Bundle.getMessage("Error9", Bundle.getMessage(errorNum)), 1009 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); // NOI18N 1010 } 1011 1012 /** 1013 * Check Memory reference of text. 1014 * <p> 1015 * Show a message if not found. 1016 * 1017 * @param name the name to look for 1018 * @return the system or user name of the corresponding Memory, null if not 1019 * found 1020 */ 1021 String validateMemoryReference(String name) { 1022 Memory m = null; 1023 if (name != null) { 1024 if (name.length() > 0) { 1025 m = InstanceManager.memoryManagerInstance().getByUserName(name); 1026 if (m != null) { 1027 return name; 1028 } 1029 } 1030 m = InstanceManager.memoryManagerInstance().getBySystemName(name); 1031 } 1032 if (m == null) { 1033 messageInvalidActionItemName(name, "Memory"); // NOI18N 1034 return null; 1035 } 1036 return name; 1037 } 1038 1039 /** 1040 * Check if user will provide a valid item name in a Memory variable. 1041 * 1042 * @param memName Memory location to provide item name at run time 1043 * @return false if user replies No 1044 */ 1045 boolean confirmIndirectMemory(String memName) { 1046 if (!_suppressIndirectRef) { 1047 int response = JmriJOptionPane.showConfirmDialog(_editLogixFrame, 1048 Bundle.getMessage("ConfirmIndirectReference", memName, 1049 Bundle.getMessage("ButtonYes"), Bundle.getMessage("ButtonNo"), 1050 Bundle.getMessage("ButtonCancel")), // NOI18N 1051 Bundle.getMessage("QuestionTitle"), JmriJOptionPane.YES_NO_CANCEL_OPTION, // NOI18N 1052 JmriJOptionPane.QUESTION_MESSAGE); 1053 if (response == JmriJOptionPane.NO_OPTION || response == JmriJOptionPane.CLOSED_OPTION ) { 1054 return false; 1055 } else if (response == JmriJOptionPane.CANCEL_OPTION) { 1056 _suppressIndirectRef = true; 1057 } 1058 } 1059 return true; 1060 } 1061 1062 /** 1063 * Check if user OK's the use of an item as both an action and 1064 * a state variable. 1065 * 1066 * @param actionName name of ConditionalAction 1067 * @param variableName name of ConditionalVariable 1068 * @return false if user replies No 1069 */ 1070 boolean confirmActionAsVariable(String actionName, String variableName) { 1071 int response = JmriJOptionPane.showConfirmDialog(_editLogixFrame, 1072 Bundle.getMessage("ConfirmActionAsVariable", actionName, variableName), 1073 Bundle.getMessage("QuestionTitle"), JmriJOptionPane.YES_NO_OPTION, // NOI18N 1074 JmriJOptionPane.QUESTION_MESSAGE); 1075 return ( response == JmriJOptionPane.YES_OPTION ); 1076 } 1077 1078 /** 1079 * Check Turnout reference of text. 1080 * <p> 1081 * Show a message if not found. 1082 * 1083 * @param name the name to look for 1084 * @return the system or user name of the corresponding Turnout, null if not 1085 * found 1086 */ 1087 String validateTurnoutReference(String name) { 1088 Turnout t = null; 1089 if (name != null) { 1090 if (name.length() > 0) { 1091 t = InstanceManager.turnoutManagerInstance().getByUserName(name); 1092 if (t != null) { 1093 return name; 1094 } 1095 } 1096 t = InstanceManager.turnoutManagerInstance().getBySystemName(name); 1097 } 1098 if (t == null) { 1099 messageInvalidActionItemName(name, "Turnout"); // NOI18N 1100 return null; 1101 } 1102 return name; 1103 } 1104 1105 /** 1106 * Check SignalHead reference of text. 1107 * <p> 1108 * Show a message if not found. 1109 * 1110 * @param name the name to look for 1111 * @return the system or user name of the corresponding SignalHead, null if 1112 * not found 1113 */ 1114 String validateSignalHeadReference(String name) { 1115 SignalHead h = null; 1116 if (name != null) { 1117 if (name.length() > 0) { 1118 h = InstanceManager.getDefault(jmri.SignalHeadManager.class).getByUserName(name); 1119 if (h != null) { 1120 return name; 1121 } 1122 } 1123 h = InstanceManager.getDefault(jmri.SignalHeadManager.class).getBySystemName(name); 1124 } 1125 if (h == null) { 1126 messageInvalidActionItemName(name, "SignalHead"); // NOI18N 1127 return null; 1128 } 1129 return name; 1130 } 1131 1132 /** 1133 * Check SignalMast reference of text. 1134 * <p> 1135 * Show a message if not found. 1136 * 1137 * @param name the name to look for 1138 * @return the system or user name of the corresponding Signal Mast, null if 1139 * not found 1140 */ 1141 String validateSignalMastReference(String name) { 1142 SignalMast h = null; 1143 if (name != null) { 1144 if (name.length() > 0) { 1145 h = InstanceManager.getDefault(jmri.SignalMastManager.class).getByUserName(name); 1146 if (h != null) { 1147 return name; 1148 } 1149 } 1150 try { 1151 h = InstanceManager.getDefault(jmri.SignalMastManager.class).provideSignalMast(name); 1152 } catch (IllegalArgumentException ex) { 1153 h = null; // tested below 1154 } 1155 } 1156 if (h == null) { 1157 messageInvalidActionItemName(name, "SignalMast"); // NOI18N 1158 return null; 1159 } 1160 return name; 1161 } 1162 1163 /** 1164 * Check Warrant reference of text. 1165 * <p> 1166 * Show a message if not found. 1167 * 1168 * @param name the name to look for 1169 * @return the system or user name of the corresponding Warrant, null if not 1170 * found 1171 */ 1172 String validateWarrantReference(String name) { 1173 Warrant w = null; 1174 if (name != null) { 1175 if (name.length() > 0) { 1176 w = InstanceManager.getDefault(WarrantManager.class).getByUserName(name); 1177 if (w != null) { 1178 return name; 1179 } 1180 } 1181 w = InstanceManager.getDefault(WarrantManager.class).getBySystemName(name); 1182 } 1183 if (w == null) { 1184 messageInvalidActionItemName(name, "Warrant"); // NOI18N 1185 return null; 1186 } 1187 return name; 1188 } 1189 1190 /** 1191 * Check OBlock reference of text. 1192 * <p> 1193 * Show a message if not found. 1194 * 1195 * @param name the name to look for 1196 * @return the system or user name of the corresponding OBlock, null if not 1197 * found 1198 */ 1199 String validateOBlockReference(String name) { 1200 OBlock b = null; 1201 if (name != null) { 1202 if (name.length() > 0) { 1203 b = InstanceManager.getDefault(jmri.jmrit.logix.OBlockManager.class).getByUserName(name); 1204 if (b != null) { 1205 return name; 1206 } 1207 } 1208 b = InstanceManager.getDefault(jmri.jmrit.logix.OBlockManager.class).getBySystemName(name); 1209 } 1210 if (b == null) { 1211 messageInvalidActionItemName(name, "OBlock"); // NOI18N 1212 return null; 1213 } 1214 return name; 1215 } 1216 1217 /** 1218 * Check Sensor reference of text. 1219 * <p> 1220 * Show a message if not found. 1221 * 1222 * @param name the name to look for 1223 * @return the system or user name of the corresponding Sensor, null if not 1224 * found 1225 */ 1226 String validateSensorReference(String name) { 1227 Sensor s = null; 1228 if (name != null) { 1229 if (name.length() > 0) { 1230 s = InstanceManager.getDefault(jmri.SensorManager.class).getByUserName(name); 1231 if (s != null) { 1232 return name; 1233 } 1234 } 1235 s = InstanceManager.getDefault(jmri.SensorManager.class).getBySystemName(name); 1236 } 1237 if (s == null) { 1238 messageInvalidActionItemName(name, "Sensor"); // NOI18N 1239 return null; 1240 } 1241 return name; 1242 } 1243 1244 /** 1245 * Check Light reference of text. 1246 * <p> 1247 * Show a message if not found. 1248 * 1249 * @param name the name to look for 1250 * @return the system or user name of the corresponding Light, null if not 1251 * found 1252 */ 1253 String validateLightReference(String name) { 1254 Light l = null; 1255 if (name != null) { 1256 if (name.length() > 0) { 1257 l = InstanceManager.lightManagerInstance().getByUserName(name); 1258 if (l != null) { 1259 return name; 1260 } 1261 } 1262 l = InstanceManager.lightManagerInstance().getBySystemName(name); 1263 } 1264 if (l == null) { 1265 messageInvalidActionItemName(name, "Light"); // NOI18N 1266 return null; 1267 } 1268 return name; 1269 } 1270 1271 /** 1272 * Check Conditional reference of text. 1273 * <p> 1274 * Show a message if not found. 1275 * 1276 * @param name the name to look for 1277 * @return the system or user name of the corresponding Conditional, null if 1278 * not found 1279 */ 1280 String validateConditionalReference(String name) { 1281 Conditional c = null; 1282 if (name != null) { 1283 if (name.length() > 0) { 1284 c = _conditionalManager.getByUserName(name); 1285 if (c != null) { 1286 return name; 1287 } 1288 } 1289 c = _conditionalManager.getBySystemName(name); 1290 } 1291 if (c == null) { 1292 messageInvalidActionItemName(name, "Conditional"); // NOI18N 1293 return null; 1294 } 1295 return name; 1296 } 1297 1298 /** 1299 * Check Logix reference of text. 1300 * <p> 1301 * Show a message if not found. 1302 * 1303 * @param name the name to look for 1304 * @return the system or user name of the corresponding Logix, null if not 1305 * found 1306 */ 1307 String validateLogixReference(String name) { 1308 Logix l = null; 1309 if (name != null) { 1310 if (name.length() > 0) { 1311 l = _logixManager.getByUserName(name); 1312 if (l != null) { 1313 return name; 1314 } 1315 } 1316 l = _logixManager.getBySystemName(name); 1317 } 1318 if (l == null) { 1319 messageInvalidActionItemName(name, "Logix"); // NOI18N 1320 return null; 1321 } 1322 return name; 1323 } 1324 1325 /** 1326 * Check Route reference of text. 1327 * <p> 1328 * Show a message if not found. 1329 * 1330 * @param name the name to look for 1331 * @return the system or user name of the corresponding Route, null if not 1332 * found 1333 */ 1334 String validateRouteReference(String name) { 1335 Route r = null; 1336 if (name != null) { 1337 if (name.length() > 0) { 1338 r = InstanceManager.getDefault(jmri.RouteManager.class).getByUserName(name); 1339 if (r != null) { 1340 return name; 1341 } 1342 } 1343 r = InstanceManager.getDefault(jmri.RouteManager.class).getBySystemName(name); 1344 } 1345 if (r == null) { 1346 messageInvalidActionItemName(name, "Route"); // NOI18N 1347 return null; 1348 } 1349 return name; 1350 } 1351 1352 /** 1353 * Check an Audio reference of text. 1354 * <p> 1355 * Show a message if not found. 1356 * 1357 * @param name the name to look for 1358 * @return the system or user name of the corresponding AudioManager, null 1359 * if not found 1360 */ 1361 String validateAudioReference(String name) { 1362 Audio a = null; 1363 if (name != null) { 1364 if (name.length() > 0) { 1365 a = InstanceManager.getDefault(jmri.AudioManager.class).getByUserName(name); 1366 if (a != null) { 1367 return name; 1368 } 1369 } 1370 a = InstanceManager.getDefault(jmri.AudioManager.class).getBySystemName(name); 1371 } 1372 if (a == null || (a.getSubType() != Audio.SOURCE && a.getSubType() != Audio.LISTENER)) { 1373 messageInvalidActionItemName(name, "Audio"); // NOI18N 1374 return null; 1375 } 1376 return name; 1377 } 1378 1379 /** 1380 * Check an EntryExit reference of text. 1381 * <p> 1382 * Show a message if not found. 1383 * 1384 * @param name the name to look for 1385 * @return the system name of the corresponding EntryExit pair, null if not 1386 * found 1387 */ 1388 String validateEntryExitReference(String name) { 1389 NamedBean nb = null; 1390 if (name != null) { 1391 if (name.length() > 0) { 1392 nb = jmri.InstanceManager.getDefault(jmri.jmrit.entryexit.EntryExitPairs.class).getNamedBean(name); 1393 if (nb != null) { 1394 return nb.getSystemName(); 1395 } 1396 } 1397 } 1398 messageInvalidActionItemName(name, "BeanNameEntryExit"); // NOI18N 1399 return null; 1400 } 1401 1402 /** 1403 * Get Light instance. 1404 * <p> 1405 * Show a message if not found. 1406 * 1407 * @param name user or system name of an existing light 1408 * @return the Light object 1409 */ 1410 Light getLight(String name) { 1411 if (name == null) { 1412 return null; 1413 } 1414 Light l = null; 1415 if (name.length() > 0) { 1416 l = InstanceManager.lightManagerInstance().getByUserName(name); 1417 if (l != null) { 1418 return l; 1419 } 1420 l = InstanceManager.lightManagerInstance().getBySystemName(name); 1421 } 1422 if (l == null) { 1423 messageInvalidActionItemName(name, "Light"); // NOI18N 1424 } 1425 return l; 1426 } 1427 1428 int parseTime(String s) { 1429 int nHour = 0; 1430 int nMin = 0; 1431 boolean error = false; 1432 int index = s.indexOf(':'); 1433 String hour = null; 1434 String minute = null; 1435 try { 1436 if (index > 0) { // : after start 1437 hour = s.substring(0, index); 1438 if (index + 1 < s.length()) { // check for : at end 1439 minute = s.substring(index + 1); 1440 } else { 1441 minute = "0"; 1442 } 1443 } else if (index == 0) { // : at start 1444 hour = "0"; 1445 minute = s.substring(index + 1); 1446 } else { 1447 hour = s; 1448 minute = "0"; 1449 } 1450 } catch (IndexOutOfBoundsException ioob) { 1451 error = true; 1452 } 1453 if (!error) { 1454 try { 1455 nHour = Integer.parseInt(hour); 1456 if ((nHour < 0) || (nHour > 24)) { 1457 error = true; 1458 } 1459 nMin = Integer.parseInt(minute); 1460 if ((nMin < 0) || (nMin > 59)) { 1461 error = true; 1462 } 1463 } catch (NumberFormatException e) { 1464 error = true; 1465 } 1466 } 1467 if (error) { 1468 // if unsuccessful, print error message 1469 JmriJOptionPane.showMessageDialog(_editLogixFrame, 1470 Bundle.getMessage("Error26", s), 1471 Bundle.getMessage("ErrorTitle"), // NOI18N 1472 JmriJOptionPane.ERROR_MESSAGE); 1473 return (-1); 1474 } 1475 // here if successful 1476 return ((nHour * 60) + nMin); 1477 } 1478 1479 /** 1480 * Format time to hh:mm given integer hour and minute. 1481 * 1482 * @param hour value for time hours 1483 * @param minute value for time minutes 1484 * @return Formatted time string 1485 */ 1486 public static String formatTime(int hour, int minute) { 1487 String s = ""; 1488 String t = Integer.toString(hour); 1489 if (t.length() == 2) { 1490 s = t + ":"; 1491 } else if (t.length() == 1) { 1492 s = "0" + t + ":"; 1493 } 1494 t = Integer.toString(minute); 1495 if (t.length() == 2) { 1496 s = s + t; 1497 } else if (t.length() == 1) { 1498 s = s + "0" + t; 1499 } 1500 if (s.length() != 5) { 1501 // input error 1502 s = "00:00"; 1503 } 1504 return s; 1505 } 1506 1507 // ------------ Error Dialogs ------------ 1508 1509 /** 1510 * Send an Invalid Conditional SignalHead state message for Edit Logix pane. 1511 * 1512 * @param name proposed appearance description 1513 * @param appearance to compare to 1514 */ 1515 void messageInvalidSignalHeadAppearance(String name, String appearance) { 1516 JmriJOptionPane.showMessageDialog(_editLogixFrame, 1517 Bundle.getMessage("Error21", name, appearance), 1518 Bundle.getMessage("ErrorTitle"), // NOI18N 1519 JmriJOptionPane.ERROR_MESSAGE); 1520 } 1521 1522 /** 1523 * Send an Invalid Conditional Action name message for Edit Logix pane. 1524 * 1525 * @param name user or system name to look up 1526 * @param itemType type of Bean to look for 1527 */ 1528 void messageInvalidActionItemName(String name, String itemType) { 1529 JmriJOptionPane.showMessageDialog(_editLogixFrame, 1530 Bundle.getMessage("Error22", name, Bundle.getMessage("BeanName" + itemType)), 1531 Bundle.getMessage("ErrorTitle"), // NOI18N 1532 JmriJOptionPane.ERROR_MESSAGE); 1533 } 1534 1535 /** 1536 * Send a duplicate Conditional user name message for Edit Logix pane. 1537 * 1538 * @param svName proposed name that duplicates an existing name 1539 */ 1540 void messageDuplicateConditionalUserName(String svName) { 1541 JmriJOptionPane.showMessageDialog(_editLogixFrame, 1542 Bundle.getMessage("Error30", svName), 1543 Bundle.getMessage("ErrorTitle"), // NOI18N 1544 JmriJOptionPane.ERROR_MESSAGE); 1545 } 1546 1547 public void bringToFront() { 1548 _editLogixFrame.toFront(); 1549 } 1550 1551 public void locateAt(Component c) { 1552 _editLogixFrame.setLocationRelativeTo(c); 1553 _editLogixFrame.toFront(); 1554 } 1555 1556 protected String getClassName() { 1557 return ConditionalEditBase.class.getName(); 1558 } 1559 1560 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ConditionalEditBase.class); 1561 1562}