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