001package jmri.jmrit.beantable.routetable; 002 003import jmri.*; 004import jmri.swing.NamedBeanComboBox; 005import jmri.swing.RowSorterUtil; 006import jmri.util.AlphanumComparator; 007import jmri.util.FileUtil; 008import jmri.util.JmriJFrame; 009import jmri.util.StringUtil; 010import jmri.script.swing.ScriptFileChooser; 011import jmri.util.swing.JComboBoxUtil; 012 013import javax.annotation.Nonnull; 014import javax.swing.*; 015import javax.swing.border.Border; 016import javax.swing.table.TableColumn; 017import javax.swing.table.TableColumnModel; 018import javax.swing.table.TableRowSorter; 019import java.awt.*; 020import java.awt.event.ActionEvent; 021import java.util.ArrayList; 022import java.util.List; 023 024/** 025 * Base class for Add/Edit frame for the Route Table. 026 * 027 * Split from {@link jmri.jmrit.beantable.RouteTableAction} 028 * 029 * @author Dave Duchamp Copyright (C) 2004 030 * @author Bob Jacobsen Copyright (C) 2007 031 * @author Simon Reader Copyright (C) 2008 032 * @author Pete Cressman Copyright (C) 2009 033 * @author Egbert Broerse Copyright (C) 2016 034 * @author Paul Bender Copyright (C) 2020 035 */ 036public abstract class AbstractRouteAddEditFrame extends JmriJFrame { 037 038 protected final RouteManager routeManager; 039 040 static final String[] COLUMN_NAMES = {Bundle.getMessage("ColumnSystemName"), 041 Bundle.getMessage("ColumnUserName"), 042 Bundle.getMessage("Include"), 043 Bundle.getMessage("ColumnLabelSetState")}; 044 private static final String SET_TO_ACTIVE = Bundle.getMessage("Set") + " " + Bundle.getMessage("SensorStateActive"); 045 private static final String SET_TO_INACTIVE = Bundle.getMessage("Set") + " " + Bundle.getMessage("SensorStateInactive"); 046 static final String SET_TO_TOGGLE = Bundle.getMessage("Set") + " " + Bundle.getMessage("Toggle"); 047 private static final String[] sensorInputModes = new String[]{ 048 Bundle.getMessage("OnCondition") + " " + Bundle.getMessage("SensorStateActive"), 049 Bundle.getMessage("OnCondition") + " " + Bundle.getMessage("SensorStateInactive"), 050 Bundle.getMessage("OnConditionChange"), 051 "Veto " + Bundle.getMessage("WhenCondition") + " " + Bundle.getMessage("SensorStateActive"), 052 "Veto " + Bundle.getMessage("WhenCondition") + " " + Bundle.getMessage("SensorStateInactive") 053 }; 054 private static final int[] sensorInputModeValues = new int[]{Route.ONACTIVE, Route.ONINACTIVE, Route.ONCHANGE, 055 Route.VETOACTIVE, Route.VETOINACTIVE}; 056 057 // safe methods to set the above 4 static field values 058 private static final int[] turnoutInputModeValues = new int[]{Route.ONCLOSED, Route.ONTHROWN, Route.ONCHANGE, 059 Route.VETOCLOSED, Route.VETOTHROWN}; 060 061 private static int ROW_HEIGHT; 062 // This group will get runtime updates to system-specific contents at 063 // the start of buildModel() above. This is done to prevent 064 // invoking the TurnoutManager at class construction time, 065 // when it hasn't been configured yet 066 067 // used in RouteTurnout 068 static String SET_TO_CLOSED = Bundle.getMessage("Set") + " " 069 + Bundle.getMessage("TurnoutStateClosed"); 070 // used in RouteTurnout 071 static String SET_TO_THROWN = Bundle.getMessage("Set") + " " 072 + Bundle.getMessage("TurnoutStateThrown"); 073 private static String[] turnoutInputModes = new String[]{ 074 Bundle.getMessage("OnCondition") + " " + Bundle.getMessage("TurnoutStateClosed"), 075 Bundle.getMessage("OnCondition") + " " + Bundle.getMessage("TurnoutStateThrown"), 076 Bundle.getMessage("OnConditionChange"), 077 "Veto " + Bundle.getMessage("WhenCondition") + " " + Bundle.getMessage("TurnoutStateClosed"), 078 "Veto " + Bundle.getMessage("WhenCondition") + " " + Bundle.getMessage("TurnoutStateThrown") 079 }; 080 private static final String[] turnoutFeedbackModes = new String[]{Bundle.getMessage("TurnoutFeedbackKnown"), 081 Bundle.getMessage("TurnoutFeedbackCommanded")}; 082 083 private static String[] lockTurnoutInputModes = new String[]{ 084 Bundle.getMessage("OnCondition") + " " + Bundle.getMessage("TurnoutStateClosed"), 085 Bundle.getMessage("OnCondition") + " " + Bundle.getMessage("TurnoutStateThrown"), 086 Bundle.getMessage("OnConditionChange") 087 }; 088 final JTextField _systemName = new JTextField(10); 089 final JTextField _userName = new JTextField(22); 090 final JCheckBox _autoSystemName = new JCheckBox(Bundle.getMessage("LabelAutoSysName")); 091 final JTextField soundFile = new JTextField(20); 092 final JTextField scriptFile = new JTextField(20); 093 final JComboBox<String> sensor1mode = new JComboBox<>(sensorInputModes); 094 final JComboBox<String> sensor2mode = new JComboBox<>(sensorInputModes); 095 final JComboBox<String> sensor3mode = new JComboBox<>(sensorInputModes); 096 final JSpinner timeDelay = new JSpinner(); 097 final JComboBox<String> cTurnoutStateBox = new JComboBox<>(turnoutInputModes); 098 final JComboBox<String> cTurnoutFeedbackBox = new JComboBox<>(turnoutFeedbackModes); 099 final JComboBox<String> cLockTurnoutStateBox = new JComboBox<>(lockTurnoutInputModes); 100 final JLabel nameLabel = new JLabel(Bundle.getMessage("LabelSystemName")); 101 final JLabel userLabel = new JLabel(Bundle.getMessage("LabelUserName")); 102 final JLabel status1 = new JLabel(); 103 final JLabel status2 = new JLabel(); 104 105 protected final String systemNameAuto = this.getClass().getName() + ".AutoSystemName"; 106 107 private ArrayList<RouteTurnout> _turnoutList; // array of all Turnouts 108 private ArrayList<RouteSensor> _sensorList; // array of all Sensors 109 private RouteTurnoutModel _routeTurnoutModel; 110 private JScrollPane _routeTurnoutScrollPane; 111 private RouteSensorModel _routeSensorModel; 112 private JScrollPane _routeSensorScrollPane; 113 private NamedBeanComboBox<Sensor> turnoutsAlignedSensor; 114 private NamedBeanComboBox<Sensor> sensor1; 115 private NamedBeanComboBox<Sensor> sensor2; 116 private NamedBeanComboBox<Sensor> sensor3; 117 private NamedBeanComboBox<Turnout> cTurnout; 118 private NamedBeanComboBox<Turnout> cLockTurnout; 119 Route curRoute = null; 120 boolean editMode = false; 121 protected ArrayList<RouteTurnout> _includedTurnoutList; 122 protected ArrayList<RouteSensor> _includedSensorList; 123 protected UserPreferencesManager pref; 124 private JRadioButton allButton = null; 125 protected boolean routeDirty = false; // true to fire reminder to save work 126 private boolean showAll = true; // false indicates show only included Turnouts 127 private JFileChooser soundChooser = null; 128 private ScriptFileChooser scriptChooser = null; 129 private boolean checkEnabled = InstanceManager.getDefault(jmri.configurexml.ShutdownPreferences.class).isStoreCheckEnabled(); 130 131 public AbstractRouteAddEditFrame(String name, boolean saveSize, boolean savePosition) { 132 super(name, saveSize, savePosition); 133 134 setClosedString(Bundle.getMessage("Set") + " " 135 + InstanceManager.turnoutManagerInstance().getClosedText()); 136 setThrownString(Bundle.getMessage("Set") + " " 137 + InstanceManager.turnoutManagerInstance().getThrownText()); 138 setTurnoutInputModes(new String[]{ 139 Bundle.getMessage("OnCondition") + " " + InstanceManager.turnoutManagerInstance().getClosedText(), 140 Bundle.getMessage("OnCondition") + " " + InstanceManager.turnoutManagerInstance().getThrownText(), 141 Bundle.getMessage("OnConditionChange"), 142 "Veto " + Bundle.getMessage("WhenCondition") + " " + Bundle.getMessage("TurnoutStateClosed"), 143 "Veto " + Bundle.getMessage("WhenCondition") + " " + Bundle.getMessage("TurnoutStateThrown") 144 }); 145 setLockTurnoutModes(new String[]{ 146 Bundle.getMessage("OnCondition") + " " + InstanceManager.turnoutManagerInstance().getClosedText(), 147 Bundle.getMessage("OnCondition") + " " + InstanceManager.turnoutManagerInstance().getThrownText(), 148 Bundle.getMessage("OnConditionChange") 149 }); 150 151 routeManager = InstanceManager.getDefault(RouteManager.class); 152 153 } 154 155 protected static void setClosedString(@Nonnull String newVal) { 156 SET_TO_CLOSED = newVal; 157 } 158 159 protected static void setThrownString(@Nonnull String newVal) { 160 SET_TO_THROWN = newVal; 161 } 162 163 protected static void setTurnoutInputModes(@Nonnull String[] newArray) { 164 turnoutInputModes = newArray; 165 } 166 167 protected static void setLockTurnoutModes(@Nonnull String[] newArray) { 168 lockTurnoutInputModes = newArray; 169 } 170 171 private static synchronized void setRowHeight(int newVal) { 172 ROW_HEIGHT = newVal; 173 } 174 175 @Override 176 public void initComponents() { 177 super.initComponents(); 178 179 pref = InstanceManager.getDefault(UserPreferencesManager.class); 180 if (editMode) { 181 cancelEdit(); 182 } 183 TurnoutManager tm = InstanceManager.turnoutManagerInstance(); 184 _turnoutList = new ArrayList<>(); 185 for (Turnout t : tm.getNamedBeanSet()) { 186 String systemName = t.getSystemName(); 187 String userName = t.getUserName(); 188 _turnoutList.add(new RouteTurnout(systemName, userName)); 189 } 190 191 SensorManager sm = InstanceManager.sensorManagerInstance(); 192 _sensorList = new ArrayList<>(); 193 for (Sensor s : sm.getNamedBeanSet()) { 194 String systemName = s.getSystemName(); 195 String userName = s.getUserName(); 196 _sensorList.add(new RouteSensor(systemName, userName)); 197 } 198 initializeIncludedList(); 199 200 turnoutsAlignedSensor = new NamedBeanComboBox<>(InstanceManager.sensorManagerInstance()); 201 sensor1 = new NamedBeanComboBox<>(InstanceManager.sensorManagerInstance()); 202 sensor2 = new NamedBeanComboBox<>(InstanceManager.sensorManagerInstance()); 203 sensor3 = new NamedBeanComboBox<>(InstanceManager.sensorManagerInstance()); 204 cTurnout = new NamedBeanComboBox<>(InstanceManager.turnoutManagerInstance()); 205 cLockTurnout = new NamedBeanComboBox<>(InstanceManager.turnoutManagerInstance()); 206 207 // Set combo max rows 208 JComboBoxUtil.setupComboBoxMaxRows(turnoutsAlignedSensor); 209 JComboBoxUtil.setupComboBoxMaxRows(sensor1); 210 JComboBoxUtil.setupComboBoxMaxRows(sensor2); 211 JComboBoxUtil.setupComboBoxMaxRows(sensor3); 212 JComboBoxUtil.setupComboBoxMaxRows(cTurnout); 213 JComboBoxUtil.setupComboBoxMaxRows(cLockTurnout); 214 215 addHelpMenu("package.jmri.jmrit.beantable.RouteAddEdit", true); 216 setLocation(100, 30); 217 218 JPanel contentPanel = new JPanel(); 219 contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS)); 220 // add system name 221 JPanel ps = new JPanel(); 222 ps.setLayout(new FlowLayout()); 223 ps.add(nameLabel); 224 nameLabel.setLabelFor(_systemName); 225 ps.add(_systemName); 226 ps.add(_autoSystemName); 227 _autoSystemName.addActionListener((ActionEvent e1) -> autoSystemName()); 228 if (pref.getSimplePreferenceState(systemNameAuto)) { 229 _autoSystemName.setSelected(true); 230 _systemName.setEnabled(false); 231 } 232 _systemName.setToolTipText(Bundle.getMessage("TooltipRouteSystemName")); 233 contentPanel.add(ps); 234 // add user name 235 JPanel p = new JPanel(); 236 p.setLayout(new FlowLayout()); 237 p.add(userLabel); 238 userLabel.setLabelFor(_userName); 239 p.add(_userName); 240 _userName.setToolTipText(Bundle.getMessage("TooltipRouteUserName")); 241 contentPanel.add(p); 242 // add Turnout Display Choice 243 JPanel py = new JPanel(); 244 py.add(new JLabel(Bundle.getMessage("Show") + ":")); 245 ButtonGroup selGroup = new ButtonGroup(); 246 allButton = new JRadioButton(Bundle.getMessage("All"), true); 247 selGroup.add(allButton); 248 py.add(allButton); 249 allButton.addActionListener((ActionEvent e1) -> { 250 // Setup for display of all Turnouts, if needed 251 if (!showAll) { 252 showAll = true; 253 _routeTurnoutModel.fireTableDataChanged(); 254 _routeSensorModel.fireTableDataChanged(); 255 } 256 }); 257 JRadioButton includedButton = new JRadioButton(Bundle.getMessage("Included"), false); 258 selGroup.add(includedButton); 259 py.add(includedButton); 260 includedButton.addActionListener((ActionEvent e1) -> { 261 // Setup for display of included Turnouts only, if needed 262 if (showAll) { 263 showAll = false; 264 initializeIncludedList(); 265 _routeTurnoutModel.fireTableDataChanged(); 266 _routeSensorModel.fireTableDataChanged(); 267 } 268 }); 269 py.add(new JLabel(Bundle.getMessage("_and_", Bundle.getMessage("Turnouts"), Bundle.getMessage("Sensors")))); 270 // keys are in jmri.jmrit.Bundle 271 contentPanel.add(py); 272 273 contentPanel.add(getTurnoutPanel()); 274 contentPanel.add(getSensorPanel()); 275 contentPanel.add(getFileNamesPanel()); 276 contentPanel.add(getAlignedSensorPanel()); 277 contentPanel.add(getControlsPanel()); 278 contentPanel.add(getLockPanel()); 279 contentPanel.add(getNotesPanel()); 280 281 getContentPane().add(new JScrollPane(contentPanel), BorderLayout.CENTER); 282 getContentPane().add(getButtonPanel(), BorderLayout.SOUTH); 283 284 pack(); 285 286 // set listener for window closing 287 addWindowListener(new java.awt.event.WindowAdapter() { 288 @Override 289 public void windowClosing(java.awt.event.WindowEvent e) { 290 closeFrame(); 291 } 292 }); 293 } 294 295 protected abstract JPanel getButtonPanel(); 296 297 private JPanel getNotesPanel() { 298 // add notes panel 299 JPanel pa = new JPanel(); 300 pa.setLayout(new BoxLayout(pa, BoxLayout.Y_AXIS)); 301 JPanel p1 = new JPanel(); 302 p1.setLayout(new FlowLayout()); 303 status1.setText(Bundle.getMessage("RouteAddStatusInitial1", Bundle.getMessage("ButtonCreate"))); // I18N to include original button name in help string 304 status1.setFont(status1.getFont().deriveFont(0.9f * nameLabel.getFont().getSize())); // a bit smaller 305 status1.setForeground(Color.gray); 306 p1.add(status1); 307 JPanel p2 = new JPanel(); 308 p2.setLayout(new FlowLayout()); 309 status2.setText(Bundle.getMessage("RouteAddStatusInitial5", Bundle.getMessage("ButtonCancel",""))); 310 status2.setFont(status1.getFont().deriveFont(0.9f * nameLabel.getFont().getSize())); // a bit smaller 311 status2.setForeground(Color.gray); 312 p2.add(status2); 313 pa.add(p1); 314 pa.add(p2); 315 Border pBorder = BorderFactory.createEtchedBorder(); 316 pa.setBorder(pBorder); 317 return pa; 318 } 319 320 private JPanel getLockPanel() { 321 // add lock control table 322 JPanel p4 = new JPanel(); 323 p4.setLayout(new BoxLayout(p4, BoxLayout.Y_AXIS)); 324 // add lock control turnout 325 JPanel p43 = new JPanel(); 326 p43.add(new JLabel(Bundle.getMessage("LabelLockTurnout"))); 327 p4.add(p43); 328 JPanel p44 = new JPanel(); 329 p44.add(new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("BeanNameTurnout")))); 330 p44.add(cLockTurnout); 331 cLockTurnout.setAllowNull(true); 332 cLockTurnout.setSelectedItem(null); 333 cLockTurnout.setToolTipText(Bundle.getMessage("TooltipEnterTurnout")); 334 p44.add(new JLabel(" " + Bundle.getMessage("MakeLabel", Bundle.getMessage("LabelCondition")))); 335 cLockTurnoutStateBox.setToolTipText(Bundle.getMessage("TooltipLockTurnout")); 336 p44.add(cLockTurnoutStateBox); 337 p4.add(p44); 338 // complete this panel 339 Border p4Border = BorderFactory.createEtchedBorder(); 340 p4.setBorder(p4Border); 341 return p4; 342 } 343 344 private JPanel getControlsPanel() { 345 // add Control Sensor table 346 JPanel p3 = new JPanel(); 347 p3.setLayout(new BoxLayout(p3, BoxLayout.Y_AXIS)); 348 JPanel p31 = new JPanel(); 349 p31.add(new JLabel(Bundle.getMessage("LabelEnterSensors"))); 350 p3.add(p31); 351 JPanel p32 = new JPanel(); 352 //Sensor 1 353 JPanel pS = new JPanel(); 354 pS.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("BeanNameSensor") + " 1")); 355 pS.add(sensor1); 356 pS.add(sensor1mode); 357 p32.add(pS); 358 //Sensor 2 359 pS = new JPanel(); 360 pS.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("BeanNameSensor") + " 2")); 361 pS.add(sensor2); 362 pS.add(sensor2mode); 363 p32.add(pS); 364 //Sensor 3 365 pS = new JPanel(); 366 pS.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("BeanNameSensor") + " 3")); 367 pS.add(sensor3); 368 pS.add(sensor3mode); 369 p32.add(pS); 370 371 sensor1.setAllowNull(true); 372 sensor2.setAllowNull(true); 373 sensor3.setAllowNull(true); 374 sensor1.setSelectedItem(null); 375 sensor2.setSelectedItem(null); 376 sensor3.setSelectedItem(null); 377 String sensorHint = Bundle.getMessage("TooltipEnterSensors"); 378 sensor1.setToolTipText(sensorHint); 379 sensor2.setToolTipText(sensorHint); 380 sensor3.setToolTipText(sensorHint); 381 p3.add(p32); 382 // add control turnout 383 JPanel p33 = new JPanel(); 384 p33.add(new JLabel(Bundle.getMessage("LabelEnterTurnout"))); 385 p3.add(p33); 386 JPanel p34 = new JPanel(); 387 p34.add(new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("BeanNameTurnout")))); 388 p34.add(cTurnout); 389 cTurnout.setAllowNull(true); 390 cTurnout.setSelectedItem(null); 391 cTurnout.setToolTipText(Bundle.getMessage("TooltipEnterTurnout")); 392 p34.add(new JLabel(" " + Bundle.getMessage("MakeLabel", Bundle.getMessage("LabelCondition")))); 393 cTurnoutStateBox.setToolTipText(Bundle.getMessage("TooltipTurnoutCondition")); 394 p34.add(cTurnoutStateBox); 395 p34.add(new JLabel(Bundle.getMessage("Is"))); 396 cTurnoutFeedbackBox.setToolTipText(Bundle.getMessage("TooltipTurnoutFeedback")); 397 p34.add(cTurnoutFeedbackBox); 398 p3.add(p34); 399 // add additional route-specific delay 400 JPanel p36 = new JPanel(); 401 p36.add(new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("LabelTurnoutDelay")))); 402 timeDelay.setModel(new SpinnerNumberModel(0, 0, 1000, 1)); 403 // timeDelay.setValue(0); // reset from possible previous use 404 timeDelay.setPreferredSize(new JTextField(5).getPreferredSize()); 405 p36.add(timeDelay); 406 timeDelay.setToolTipText(Bundle.getMessage("TooltipTurnoutDelay")); 407 p36.add(new JLabel(Bundle.getMessage("LabelMilliseconds"))); 408 p3.add(p36); 409 // complete this panel 410 Border p3Border = BorderFactory.createEtchedBorder(); 411 p3.setBorder(p3Border); 412 return p3; 413 } 414 415 private JPanel getAlignedSensorPanel() { 416 //add turnouts aligned Sensor 417 JPanel p27 = new JPanel(); 418 p27.setLayout(new FlowLayout()); 419 p27.add(new JLabel(Bundle.getMessage("LabelEnterSensorAligned"))); 420 p27.add(turnoutsAlignedSensor); 421 turnoutsAlignedSensor.setAllowNull(true); 422 turnoutsAlignedSensor.setSelectedItem(null); 423 turnoutsAlignedSensor.setToolTipText(Bundle.getMessage("TooltipEnterSensor")); 424 return p27; 425 } 426 427 private JPanel getFileNamesPanel() { 428 // Enter filenames for sound, script 429 JPanel p25 = new JPanel(); 430 p25.setLayout(new FlowLayout()); 431 p25.add(new JLabel(Bundle.getMessage("LabelPlaySound"))); 432 p25.add(soundFile); 433 JButton ss = new JButton("..."); //NO18N 434 ss.addActionListener((ActionEvent e1) -> setSoundPressed()); 435 ss.setToolTipText(Bundle.getMessage("TooltipOpenFile", Bundle.getMessage("BeanNameAudio"))); 436 p25.add(ss); 437 p25.add(new JLabel(Bundle.getMessage("LabelRunScript"))); 438 p25.add(scriptFile); 439 ss = new JButton("..."); //NO18N 440 ss.addActionListener((ActionEvent e1) -> setScriptPressed()); 441 ss.setToolTipText(Bundle.getMessage("TooltipOpenFile", Bundle.getMessage("Script"))); 442 p25.add(ss); 443 return p25; 444 } 445 446 private JPanel getTurnoutPanel(){ 447 // add Turnout table 448 // Turnout list table 449 JPanel p2xt = new JPanel(); 450 JPanel p2xtSpace = new JPanel(); 451 p2xtSpace.setLayout(new BoxLayout(p2xtSpace, BoxLayout.Y_AXIS)); 452 p2xtSpace.add(Box.createRigidArea(new Dimension(30,0))); 453 p2xt.add(p2xtSpace); 454 455 JPanel p21t = new JPanel(); 456 p21t.setLayout(new BoxLayout(p21t, BoxLayout.Y_AXIS)); 457 p21t.add(new JLabel(Bundle.getMessage("SelectInRoute", Bundle.getMessage("Turnouts")))); 458 p2xt.add(p21t); 459 _routeTurnoutModel = new RouteTurnoutModel(this); 460 JTable routeTurnoutTable = new JTable(_routeTurnoutModel); 461 TableRowSorter<RouteTurnoutModel> rtSorter = new TableRowSorter<>(_routeTurnoutModel); 462 463 // Use AlphanumComparator for SNAME and UNAME columns. Start with SNAME sort. 464 rtSorter.setComparator(RouteOutputModel.SNAME_COLUMN, new AlphanumComparator()); 465 rtSorter.setComparator(RouteOutputModel.UNAME_COLUMN, new AlphanumComparator()); 466 RowSorterUtil.setSortOrder(rtSorter, RouteOutputModel.SNAME_COLUMN, SortOrder.ASCENDING); 467 468 routeTurnoutTable.setRowSorter(rtSorter); 469 routeTurnoutTable.setRowSelectionAllowed(false); 470 routeTurnoutTable.setPreferredScrollableViewportSize( 471 new Dimension(400 + (int)cTurnout.getMinimumSize().getWidth(), 80)); 472 473 setRowHeight(routeTurnoutTable.getRowHeight()); 474 JComboBox<String> stateTCombo = new JComboBox<>(); 475 stateTCombo.addItem(SET_TO_CLOSED); 476 stateTCombo.addItem(SET_TO_THROWN); 477 stateTCombo.addItem(SET_TO_TOGGLE); 478 TableColumnModel routeTurnoutColumnModel = routeTurnoutTable.getColumnModel(); 479 TableColumn includeColumnT = routeTurnoutColumnModel. 480 getColumn(RouteOutputModel.INCLUDE_COLUMN); 481 includeColumnT.setResizable(false); 482 includeColumnT.setMinWidth(50); 483 includeColumnT.setMaxWidth(60); 484 TableColumn sNameColumnT = routeTurnoutColumnModel. 485 getColumn(RouteOutputModel.SNAME_COLUMN); 486 sNameColumnT.setResizable(true); 487 sNameColumnT.setMinWidth(95); 488 489 TableColumn uNameColumnT = routeTurnoutColumnModel. 490 getColumn(RouteOutputModel.UNAME_COLUMN); 491 uNameColumnT.setResizable(true); 492 uNameColumnT.setMinWidth(210); 493 494 TableColumn stateColumnT = routeTurnoutColumnModel. 495 getColumn(RouteOutputModel.STATE_COLUMN); 496 stateColumnT.setCellEditor(new DefaultCellEditor(stateTCombo)); 497 stateColumnT.setResizable(false); 498 stateColumnT.setMinWidth(90); 499 stateColumnT.setMaxWidth(100); 500 _routeTurnoutScrollPane = new JScrollPane(routeTurnoutTable); 501 p2xt.add(_routeTurnoutScrollPane, BorderLayout.CENTER); 502 p2xt.setVisible(true); 503 return p2xt; 504 } 505 506 private JPanel getSensorPanel(){ 507 // add Sensor table 508 // Sensor list table 509 JPanel p2xs = new JPanel(); 510 JPanel p2xsSpace = new JPanel(); 511 p2xsSpace.setLayout(new BoxLayout(p2xsSpace, BoxLayout.Y_AXIS)); 512 p2xsSpace.add(Box.createRigidArea(new Dimension(30,0))); 513 p2xs.add(p2xsSpace); 514 515 JPanel p21s = new JPanel(); 516 p21s.setLayout(new BoxLayout(p21s, BoxLayout.Y_AXIS)); 517 p21s.add(new JLabel(Bundle.getMessage("SelectInRoute", Bundle.getMessage("Sensors")))); 518 p2xs.add(p21s); 519 _routeSensorModel = new RouteSensorModel(this); 520 JTable routeSensorTable = new JTable(_routeSensorModel); 521 TableRowSorter<RouteSensorModel> rsSorter = new TableRowSorter<>(_routeSensorModel); 522 523 // Use AlphanumComparator for SNAME and UNAME columns. Start with SNAME sort. 524 rsSorter.setComparator(RouteOutputModel.SNAME_COLUMN, new AlphanumComparator()); 525 rsSorter.setComparator(RouteOutputModel.UNAME_COLUMN, new AlphanumComparator()); 526 RowSorterUtil.setSortOrder(rsSorter, RouteOutputModel.SNAME_COLUMN, SortOrder.ASCENDING); 527 routeSensorTable.setRowSorter(rsSorter); 528 routeSensorTable.setRowSelectionAllowed(false); 529 routeSensorTable.setPreferredScrollableViewportSize( 530 new Dimension(400 + (int)sensor1.getMinimumSize().getWidth(), 80)); 531 532 JComboBox<String> stateSCombo = new JComboBox<>(); 533 stateSCombo.addItem(SET_TO_ACTIVE); 534 stateSCombo.addItem(SET_TO_INACTIVE); 535 stateSCombo.addItem(SET_TO_TOGGLE); 536 TableColumnModel routeSensorColumnModel = routeSensorTable.getColumnModel(); 537 TableColumn includeColumnS = routeSensorColumnModel. 538 getColumn(RouteOutputModel.INCLUDE_COLUMN); 539 includeColumnS.setResizable(false); 540 includeColumnS.setMinWidth(50); 541 includeColumnS.setMaxWidth(60); 542 TableColumn sNameColumnS = routeSensorColumnModel. 543 getColumn(RouteOutputModel.SNAME_COLUMN); 544 sNameColumnS.setResizable(true); 545 sNameColumnS.setMinWidth(95); 546 547 TableColumn uNameColumnS = routeSensorColumnModel. 548 getColumn(RouteOutputModel.UNAME_COLUMN); 549 uNameColumnS.setResizable(true); 550 uNameColumnS.setMinWidth(210); 551 552 TableColumn stateColumnS = routeSensorColumnModel. 553 getColumn(RouteOutputModel.STATE_COLUMN); 554 stateColumnS.setCellEditor(new DefaultCellEditor(stateSCombo)); 555 stateColumnS.setResizable(false); 556 stateColumnS.setMinWidth(90); 557 stateColumnS.setMaxWidth(100); 558 _routeSensorScrollPane = new JScrollPane(routeSensorTable); 559 p2xs.add(_routeSensorScrollPane, BorderLayout.CENTER); 560 p2xs.setVisible(true); 561 return p2xs; 562 } 563 564 /** 565 * Initialize list of included turnout positions. 566 */ 567 protected void initializeIncludedList() { 568 _includedTurnoutList = new ArrayList<>(); 569 for (RouteTurnout routeTurnout : _turnoutList) { 570 if (routeTurnout.isIncluded()) { 571 _includedTurnoutList.add(routeTurnout); 572 } 573 } 574 _includedSensorList = new ArrayList<>(); 575 for (RouteSensor routeSensor : _sensorList) { 576 if (routeSensor.isIncluded()) { 577 _includedSensorList.add(routeSensor); 578 } 579 } 580 } 581 582 private void autoSystemName() { 583 if (_autoSystemName.isSelected()) { 584 _systemName.setEnabled(false); 585 nameLabel.setEnabled(false); 586 } else { 587 _systemName.setEnabled(true); 588 nameLabel.setEnabled(true); 589 } 590 } 591 592 protected void showReminderMessage() { 593 // Use the RouteTabelAction class to combine messages in Preferences -> Messages 594 if (checkEnabled) { 595 return; 596 } 597 InstanceManager.getDefault(UserPreferencesManager.class). 598 showInfoMessage(this, Bundle.getMessage("ReminderTitle"), // NOI18N 599 Bundle.getMessage("ReminderSaveString", Bundle.getMessage("MenuItemRouteTable")), // NOI18N 600 jmri.jmrit.beantable.RouteTableAction.class.getName(), "remindSaveRoute"); // NOI18N 601 } 602 603 private int sensorModeFromBox(JComboBox<String> box) { 604 String mode = (String) box.getSelectedItem(); 605 return sensorModeFromString(mode); 606 } 607 608 int sensorModeFromString(String mode) { 609 int result = StringUtil.getStateFromName(mode, sensorInputModeValues, sensorInputModes); 610 611 if (result < 0) { 612 log.warn("unexpected mode string in sensorMode: {}", mode); 613 throw new IllegalArgumentException(); 614 } 615 return result; 616 } 617 618 void setSensorModeBox(int mode, JComboBox<String> box) { 619 String result = StringUtil.getNameFromState(mode, sensorInputModeValues, sensorInputModes); 620 box.setSelectedItem(result); 621 } 622 623 private int turnoutModeFromBox(JComboBox<String> box) { 624 String mode = (String) box.getSelectedItem(); 625 int result = StringUtil.getStateFromName(mode, turnoutInputModeValues, turnoutInputModes); 626 627 if (result < 0) { 628 log.warn("unexpected mode string in turnoutMode: {}", mode); 629 throw new IllegalArgumentException(); 630 } 631 return result; 632 } 633 634 void setTurnoutModeBox(int mode, JComboBox<String> box) { 635 String result = StringUtil.getNameFromState(mode, turnoutInputModeValues, turnoutInputModes); 636 box.setSelectedItem(result); 637 } 638 639 /** 640 * Set the Turnout information for adding or editing. 641 * 642 * @param g the route to add the turnout to 643 */ 644 protected void setTurnoutInformation(Route g) { 645 for (RouteTurnout t : _includedTurnoutList) { 646 g.addOutputTurnout(t.getDisplayName(), t.getState()); 647 } 648 } 649 650 /** 651 * Sets the Sensor information for adding or editing. 652 * 653 * @param g the route to add the sensor to 654 */ 655 protected void setSensorInformation(Route g) { 656 for (RouteSensor s : _includedSensorList) { 657 g.addOutputSensor(s.getDisplayName(), s.getState()); 658 } 659 } 660 661 /** 662 * Set the Sensor, Turnout, and delay control information for adding or editing. 663 * 664 * @param g the route to configure 665 */ 666 protected void setControlInformation(Route g) { 667 // Get sensor control information if any 668 Sensor sensor = sensor1.getSelectedItem(); 669 if (sensor != null) { 670 if ((!g.addSensorToRoute(sensor.getSystemName(), sensorModeFromBox(sensor1mode)))) { 671 log.error("Unexpected failure to add Sensor '{}' to route '{}'.", sensor.getSystemName(), g.getSystemName()); 672 } 673 } 674 675 if (sensor2.getSelectedItem() != null) { 676 if ((!g.addSensorToRoute(sensor2.getSelectedItemDisplayName(), sensorModeFromBox(sensor2mode)))) { 677 log.error("Unexpected failure to add Sensor '{}' to Route '{}'.", sensor2.getSelectedItemDisplayName(), g.getSystemName()); 678 } 679 } 680 681 if (sensor3.getSelectedItem() != null) { 682 if ((!g.addSensorToRoute(sensor3.getSelectedItemDisplayName(), sensorModeFromBox(sensor3mode)))) { 683 log.error("Unexpected failure to add Sensor '{}' to Route '{}'.", sensor3.getSelectedItemDisplayName(), g.getSystemName()); 684 } 685 } 686 687 //Turnouts Aligned sensor 688 if (turnoutsAlignedSensor.getSelectedItem() != null) { 689 g.setTurnoutsAlignedSensor(turnoutsAlignedSensor.getSelectedItemDisplayName()); 690 } else { 691 g.setTurnoutsAlignedSensor(""); 692 } 693 694 // Set turnout information if there is any 695 if (cTurnout.getSelectedItem() != null) { 696 g.setControlTurnout(cTurnout.getSelectedItemDisplayName()); 697 // set up Control Turnout state 698 g.setControlTurnoutState(turnoutModeFromBox(cTurnoutStateBox)); 699 g.setControlTurnoutFeedback(cTurnoutFeedbackBox.getSelectedIndex() == 1); 700 701 } else { 702 // No Control Turnout was entered 703 g.setControlTurnout(""); 704 } 705 // set route specific Delay information, see jmri.implementation.DefaultRoute#SetRouteThread() 706 int addDelay = (Integer) timeDelay.getValue(); // from a JSpinner with 0 set as minimum 707 g.setRouteCommandDelay(addDelay); 708 709 // Set Lock Turnout information if there is any 710 if (cLockTurnout.getSelectedItem() != null) { 711 g.setLockControlTurnout(cLockTurnout.getSelectedItemDisplayName()); 712 // set up control turnout state 713 g.setLockControlTurnoutState(turnoutModeFromBox(cLockTurnoutStateBox)); 714 } else { 715 // No Lock Turnout was entered 716 g.setLockControlTurnout(""); 717 } 718 } 719 720 /** 721 * Set the sound file. 722 */ 723 private void setSoundPressed() { 724 if (soundChooser == null) { 725 soundChooser = new jmri.util.swing.JmriJFileChooser(FileUtil.getUserFilesPath()); 726 soundChooser.setFileFilter(new jmri.util.NoArchiveFileFilter()); 727 } 728 soundChooser.rescanCurrentDirectory(); 729 int retVal = soundChooser.showOpenDialog(null); 730 // handle selection or cancel 731 if (retVal == JFileChooser.APPROVE_OPTION) { 732 try { 733 soundFile.setText(soundChooser.getSelectedFile().getCanonicalPath()); 734 } catch (java.io.IOException e) { 735 log.error("exception setting sound file: ", e); 736 } 737 } 738 } 739 740 /** 741 * Set the script file. 742 */ 743 private void setScriptPressed() { 744 if (scriptChooser == null) { 745 scriptChooser = new ScriptFileChooser(); 746 } 747 scriptChooser.rescanCurrentDirectory(); 748 int retVal = scriptChooser.showOpenDialog(null); 749 // handle selection or cancel 750 if (retVal == JFileChooser.APPROVE_OPTION) { 751 try { 752 scriptFile.setText(scriptChooser.getSelectedFile().getCanonicalPath()); 753 } catch (java.io.IOException e) { 754 log.error("exception setting script file: ", e); 755 } 756 } 757 } 758 759 760 protected void finishUpdate() { 761 // move to show all Turnouts if not there 762 cancelIncludedOnly(); 763 // Provide feedback to user 764 // switch GUI back to selection mode 765 //status2.setText(Bundle.getMessage("RouteAddStatusInitial2", Bundle.getMessage("ButtonEdit"))); 766 status2.setVisible(true); 767 autoSystemName(); 768 setTitle(Bundle.getMessage("TitleAddRoute")); 769 clearPage(); 770 // reactivate the Route 771 routeDirty = true; 772 // get out of edit mode 773 editMode = false; 774 if (curRoute != null) { 775 curRoute.activateRoute(); 776 } 777 } 778 779 /** 780 * Populate the page fields. The route names are not included since they are handled 781 * by the Edit or Add actions. 782 * <p> 783 * The route is either the route being edited or a source route for doing a copy during 784 * the add action. 785 * 786 * @param route The route that contains the content. 787 */ 788 protected void setPageContent(Route route) { 789 // set up Turnout list for this route 790 int setRow = 0; 791 for (int i = _turnoutList.size() - 1; i >= 0; i--) { 792 RouteTurnout turnout = _turnoutList.get(i); 793 String tSysName = turnout.getSysName(); 794 if (route.isOutputTurnoutIncluded(tSysName)) { 795 turnout.setIncluded(true); 796 turnout.setState(route.getOutputTurnoutSetState(tSysName)); 797 setRow = i; 798 } else { 799 turnout.setIncluded(false); 800 turnout.setState(Turnout.CLOSED); 801 } 802 } 803 setRow -= 1; 804 if (setRow < 0) { 805 setRow = 0; 806 } 807 _routeTurnoutScrollPane.getVerticalScrollBar().setValue(setRow * ROW_HEIGHT); 808 _routeTurnoutModel.fireTableDataChanged(); 809 810 // set up Sensor list for this route 811 for (int i = _sensorList.size() - 1; i >= 0; i--) { 812 RouteSensor sensor = _sensorList.get(i); 813 String tSysName = sensor.getSysName(); 814 if (route.isOutputSensorIncluded(tSysName)) { 815 sensor.setIncluded(true); 816 sensor.setState(route.getOutputSensorSetState(tSysName)); 817 setRow = i; 818 } else { 819 sensor.setIncluded(false); 820 sensor.setState(Sensor.INACTIVE); 821 } 822 } 823 setRow -= 1; 824 if (setRow < 0) { 825 setRow = 0; 826 } 827 _routeSensorScrollPane.getVerticalScrollBar().setValue(setRow * ROW_HEIGHT); 828 _routeSensorModel.fireTableDataChanged(); 829 830 // get Sound and Script file names 831 scriptFile.setText(route.getOutputScriptName()); 832 soundFile.setText(route.getOutputSoundName()); 833 834 // get Turnout Aligned sensor 835 turnoutsAlignedSensor.setSelectedItem(route.getTurnoutsAlgdSensor()); 836 837 // set up Control Sensors if there are any 838 Sensor[] temNames = new Sensor[Route.MAX_CONTROL_SENSORS]; 839 int[] temModes = new int[Route.MAX_CONTROL_SENSORS]; 840 for (int k = 0; k < Route.MAX_CONTROL_SENSORS; k++) { 841 temNames[k] = route.getRouteSensor(k); 842 temModes[k] = route.getRouteSensorMode(k); 843 } 844 sensor1.setSelectedItem(temNames[0]); 845 setSensorModeBox(temModes[0], sensor1mode); 846 847 sensor2.setSelectedItem(temNames[1]); 848 setSensorModeBox(temModes[1], sensor2mode); 849 850 sensor3.setSelectedItem(temNames[2]); 851 setSensorModeBox(temModes[2], sensor3mode); 852 853 // set up Control Turnout if there is one 854 cTurnout.setSelectedItem(route.getCtlTurnout()); 855 856 setTurnoutModeBox(route.getControlTurnoutState(), cTurnoutStateBox); 857 858 if (route.getControlTurnoutFeedback()) { 859 cTurnoutFeedbackBox.setSelectedIndex(1); // Known 860 } else { 861 cTurnoutFeedbackBox.setSelectedIndex(0); // Commanded 862 } 863 864 // set up Lock Control Turnout if there is one 865 cLockTurnout.setSelectedItem(route.getLockCtlTurnout()); 866 867 setTurnoutModeBox(route.getLockControlTurnoutState(), cLockTurnoutStateBox); 868 869 // set up additional route specific Delay 870 timeDelay.setValue(route.getRouteCommandDelay()); 871 } 872 873 private void clearPage() { 874 _systemName.setText(""); 875 _userName.setText(""); 876 sensor1.setSelectedItem(null); 877 sensor2.setSelectedItem(null); 878 sensor3.setSelectedItem(null); 879 cTurnout.setSelectedItem(null); 880 cLockTurnout.setSelectedItem(null); 881 turnoutsAlignedSensor.setSelectedItem(null); 882 soundFile.setText(""); 883 scriptFile.setText(""); 884 for (int i = _turnoutList.size() - 1; i >= 0; i--) { 885 _turnoutList.get(i).setIncluded(false); 886 } 887 for (int i = _sensorList.size() - 1; i >= 0; i--) { 888 _sensorList.get(i).setIncluded(false); 889 } 890 } 891 892 893 /** 894 * Cancel included Turnouts only option 895 */ 896 private void cancelIncludedOnly() { 897 if (!showAll) { 898 allButton.doClick(); 899 } 900 } 901 902 List<RouteTurnout> get_turnoutList() { 903 return _turnoutList; 904 } 905 906 List<RouteTurnout> get_includedTurnoutList() { 907 return _includedTurnoutList; 908 } 909 910 List<RouteSensor> get_sensorList() { 911 return _sensorList; 912 } 913 914 List<RouteSensor> get_includedSensorList() { 915 return _includedSensorList; 916 } 917 918 public boolean isShowAll() { 919 return showAll; 920 } 921 922 /** 923 * Cancels edit mode 924 */ 925 protected void cancelEdit() { 926 if (editMode) { 927 status1.setText(Bundle.getMessage("RouteAddStatusInitial1", Bundle.getMessage("ButtonCreate"))); // I18N to include original button name in help string 928 //status2.setText(Bundle.getMessage("RouteAddStatusInitial2", Bundle.getMessage("ButtonEdit"))); 929 finishUpdate(); 930 // get out of edit mode 931 editMode = false; 932 curRoute = null; 933 } 934 closeFrame(); 935 } 936 937 /** 938 * Respond to the Update button - update to Route Table. 939 * 940 * @param newRoute true if a new route; false otherwise 941 */ 942 protected void updatePressed(boolean newRoute) { 943 // Check if the User Name has been changed 944 String uName = _userName.getText(); 945 Route g = checkNamesOK(); 946 if (g == null) { 947 return; 948 } 949 // User Name is unique, change it 950 g.setUserName(uName); 951 // clear the current Turnout information for this Route 952 g.clearOutputTurnouts(); 953 g.clearOutputSensors(); 954 // clear the current Sensor information for this Route 955 g.clearRouteSensors(); 956 // add those indicated in the panel 957 initializeIncludedList(); 958 setTurnoutInformation(g); 959 setSensorInformation(g); 960 // set the current values of the file names 961 g.setOutputScriptName(scriptFile.getText()); 962 g.setOutputSoundName(soundFile.getText()); 963 // add Control Sensors and a Control Turnout if entered in the panel 964 setControlInformation(g); 965 curRoute = g; 966 finishUpdate(); 967 status1.setForeground(Color.gray); 968 status1.setText((newRoute ? Bundle.getMessage("RouteAddStatusCreated") : 969 Bundle.getMessage("RouteAddStatusUpdated")) + ": \"" + uName + "\" (" + _includedTurnoutList.size() + " " 970 + Bundle.getMessage("Turnouts") + ", " + _includedSensorList.size() + " " + Bundle.getMessage("Sensors") + ")"); 971 972 closeFrame(); 973 } 974 975 /** 976 * Check name and return a new or existing Route object with the name as entered in the _systemName field on the 977 * addFrame pane. 978 * 979 * @return the new/updated Route object 980 */ 981 private Route checkNamesOK() { 982 // Get system name and user name 983 String sName = _systemName.getText(); 984 String uName = _userName.getText(); 985 Route g; 986 if (_autoSystemName.isSelected() && !editMode) { 987 log.debug("checkNamesOK new autogroup"); 988 // create new Route with auto system name 989 g = routeManager.newRoute(uName); 990 } else { 991 if (sName.length() == 0) { 992 status1.setText(Bundle.getMessage("AddBeanStatusEnter")); 993 status1.setForeground(Color.red); 994 return null; 995 } 996 try { 997 sName = routeManager.makeSystemName(sName); 998 g = routeManager.provideRoute(sName, uName); 999 } catch (IllegalArgumentException ex) { 1000 g = null; // for later check: 1001 } 1002 } 1003 if (g == null) { 1004 // should never get here 1005 log.error("Unknown failure to create Route with System Name: {}", sName); // NOI18N 1006 } else { 1007 g.deActivateRoute(); 1008 } 1009 return g; 1010 } 1011 1012 protected void closeFrame(){ 1013 // remind to save, if Route was created or edited 1014 if (routeDirty) { 1015 showReminderMessage(); 1016 routeDirty = false; 1017 } 1018 // hide addFrame 1019 setVisible(false); 1020 1021 // if in Edit, cancel edit mode 1022 if (editMode) { 1023 cancelEdit(); 1024 } 1025 _routeSensorModel.dispose(); 1026 _routeTurnoutModel.dispose(); 1027 this.dispose(); 1028 } 1029 1030 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AbstractRouteAddEditFrame.class); 1031}