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 contentPanel.add(getButtonPanel()); 281 282 getContentPane().add(new JScrollPane(contentPanel), BorderLayout.CENTER); 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(new Dimension(480, 80)); 471 472 setRowHeight(routeTurnoutTable.getRowHeight()); 473 JComboBox<String> stateTCombo = new JComboBox<>(); 474 stateTCombo.addItem(SET_TO_CLOSED); 475 stateTCombo.addItem(SET_TO_THROWN); 476 stateTCombo.addItem(SET_TO_TOGGLE); 477 TableColumnModel routeTurnoutColumnModel = routeTurnoutTable.getColumnModel(); 478 TableColumn includeColumnT = routeTurnoutColumnModel. 479 getColumn(RouteOutputModel.INCLUDE_COLUMN); 480 includeColumnT.setResizable(false); 481 includeColumnT.setMinWidth(50); 482 includeColumnT.setMaxWidth(60); 483 TableColumn sNameColumnT = routeTurnoutColumnModel. 484 getColumn(RouteOutputModel.SNAME_COLUMN); 485 sNameColumnT.setResizable(true); 486 sNameColumnT.setMinWidth(75); 487 sNameColumnT.setMaxWidth(95); 488 TableColumn uNameColumnT = routeTurnoutColumnModel. 489 getColumn(RouteOutputModel.UNAME_COLUMN); 490 uNameColumnT.setResizable(true); 491 uNameColumnT.setMinWidth(210); 492 uNameColumnT.setMaxWidth(260); 493 TableColumn stateColumnT = routeTurnoutColumnModel. 494 getColumn(RouteOutputModel.STATE_COLUMN); 495 stateColumnT.setCellEditor(new DefaultCellEditor(stateTCombo)); 496 stateColumnT.setResizable(false); 497 stateColumnT.setMinWidth(90); 498 stateColumnT.setMaxWidth(100); 499 _routeTurnoutScrollPane = new JScrollPane(routeTurnoutTable); 500 p2xt.add(_routeTurnoutScrollPane, BorderLayout.CENTER); 501 p2xt.setVisible(true); 502 return p2xt; 503 } 504 505 private JPanel getSensorPanel(){ 506 // add Sensor table 507 // Sensor list table 508 JPanel p2xs = new JPanel(); 509 JPanel p2xsSpace = new JPanel(); 510 p2xsSpace.setLayout(new BoxLayout(p2xsSpace, BoxLayout.Y_AXIS)); 511 p2xsSpace.add(Box.createRigidArea(new Dimension(30,0))); 512 p2xs.add(p2xsSpace); 513 514 JPanel p21s = new JPanel(); 515 p21s.setLayout(new BoxLayout(p21s, BoxLayout.Y_AXIS)); 516 p21s.add(new JLabel(Bundle.getMessage("SelectInRoute", Bundle.getMessage("Sensors")))); 517 p2xs.add(p21s); 518 _routeSensorModel = new RouteSensorModel(this); 519 JTable routeSensorTable = new JTable(_routeSensorModel); 520 TableRowSorter<RouteSensorModel> rsSorter = new TableRowSorter<>(_routeSensorModel); 521 522 // Use AlphanumComparator for SNAME and UNAME columns. Start with SNAME sort. 523 rsSorter.setComparator(RouteOutputModel.SNAME_COLUMN, new AlphanumComparator()); 524 rsSorter.setComparator(RouteOutputModel.UNAME_COLUMN, new AlphanumComparator()); 525 RowSorterUtil.setSortOrder(rsSorter, RouteOutputModel.SNAME_COLUMN, SortOrder.ASCENDING); 526 routeSensorTable.setRowSorter(rsSorter); 527 routeSensorTable.setRowSelectionAllowed(false); 528 routeSensorTable.setPreferredScrollableViewportSize(new Dimension(480, 80)); 529 JComboBox<String> stateSCombo = new JComboBox<>(); 530 stateSCombo.addItem(SET_TO_ACTIVE); 531 stateSCombo.addItem(SET_TO_INACTIVE); 532 stateSCombo.addItem(SET_TO_TOGGLE); 533 TableColumnModel routeSensorColumnModel = routeSensorTable.getColumnModel(); 534 TableColumn includeColumnS = routeSensorColumnModel. 535 getColumn(RouteOutputModel.INCLUDE_COLUMN); 536 includeColumnS.setResizable(false); 537 includeColumnS.setMinWidth(50); 538 includeColumnS.setMaxWidth(60); 539 TableColumn sNameColumnS = routeSensorColumnModel. 540 getColumn(RouteOutputModel.SNAME_COLUMN); 541 sNameColumnS.setResizable(true); 542 sNameColumnS.setMinWidth(75); 543 sNameColumnS.setMaxWidth(95); 544 TableColumn uNameColumnS = routeSensorColumnModel. 545 getColumn(RouteOutputModel.UNAME_COLUMN); 546 uNameColumnS.setResizable(true); 547 uNameColumnS.setMinWidth(210); 548 uNameColumnS.setMaxWidth(260); 549 TableColumn stateColumnS = routeSensorColumnModel. 550 getColumn(RouteOutputModel.STATE_COLUMN); 551 stateColumnS.setCellEditor(new DefaultCellEditor(stateSCombo)); 552 stateColumnS.setResizable(false); 553 stateColumnS.setMinWidth(90); 554 stateColumnS.setMaxWidth(100); 555 _routeSensorScrollPane = new JScrollPane(routeSensorTable); 556 p2xs.add(_routeSensorScrollPane, BorderLayout.CENTER); 557 p2xs.setVisible(true); 558 return p2xs; 559 } 560 561 /** 562 * Initialize list of included turnout positions. 563 */ 564 protected void initializeIncludedList() { 565 _includedTurnoutList = new ArrayList<>(); 566 for (RouteTurnout routeTurnout : _turnoutList) { 567 if (routeTurnout.isIncluded()) { 568 _includedTurnoutList.add(routeTurnout); 569 } 570 } 571 _includedSensorList = new ArrayList<>(); 572 for (RouteSensor routeSensor : _sensorList) { 573 if (routeSensor.isIncluded()) { 574 _includedSensorList.add(routeSensor); 575 } 576 } 577 } 578 579 private void autoSystemName() { 580 if (_autoSystemName.isSelected()) { 581 _systemName.setEnabled(false); 582 nameLabel.setEnabled(false); 583 } else { 584 _systemName.setEnabled(true); 585 nameLabel.setEnabled(true); 586 } 587 } 588 589 protected void showReminderMessage() { 590 // Use the RouteTabelAction class to combine messages in Preferences -> Messages 591 if (checkEnabled) { 592 return; 593 } 594 InstanceManager.getDefault(UserPreferencesManager.class). 595 showInfoMessage(this, Bundle.getMessage("ReminderTitle"), // NOI18N 596 Bundle.getMessage("ReminderSaveString", Bundle.getMessage("MenuItemRouteTable")), // NOI18N 597 jmri.jmrit.beantable.RouteTableAction.class.getName(), "remindSaveRoute"); // NOI18N 598 } 599 600 private int sensorModeFromBox(JComboBox<String> box) { 601 String mode = (String) box.getSelectedItem(); 602 return sensorModeFromString(mode); 603 } 604 605 int sensorModeFromString(String mode) { 606 int result = StringUtil.getStateFromName(mode, sensorInputModeValues, sensorInputModes); 607 608 if (result < 0) { 609 log.warn("unexpected mode string in sensorMode: {}", mode); 610 throw new IllegalArgumentException(); 611 } 612 return result; 613 } 614 615 void setSensorModeBox(int mode, JComboBox<String> box) { 616 String result = StringUtil.getNameFromState(mode, sensorInputModeValues, sensorInputModes); 617 box.setSelectedItem(result); 618 } 619 620 private int turnoutModeFromBox(JComboBox<String> box) { 621 String mode = (String) box.getSelectedItem(); 622 int result = StringUtil.getStateFromName(mode, turnoutInputModeValues, turnoutInputModes); 623 624 if (result < 0) { 625 log.warn("unexpected mode string in turnoutMode: {}", mode); 626 throw new IllegalArgumentException(); 627 } 628 return result; 629 } 630 631 void setTurnoutModeBox(int mode, JComboBox<String> box) { 632 String result = StringUtil.getNameFromState(mode, turnoutInputModeValues, turnoutInputModes); 633 box.setSelectedItem(result); 634 } 635 636 /** 637 * Set the Turnout information for adding or editing. 638 * 639 * @param g the route to add the turnout to 640 */ 641 protected void setTurnoutInformation(Route g) { 642 for (RouteTurnout t : _includedTurnoutList) { 643 g.addOutputTurnout(t.getDisplayName(), t.getState()); 644 } 645 } 646 647 /** 648 * Sets the Sensor information for adding or editing. 649 * 650 * @param g the route to add the sensor to 651 */ 652 protected void setSensorInformation(Route g) { 653 for (RouteSensor s : _includedSensorList) { 654 g.addOutputSensor(s.getDisplayName(), s.getState()); 655 } 656 } 657 658 /** 659 * Set the Sensor, Turnout, and delay control information for adding or editing. 660 * 661 * @param g the route to configure 662 */ 663 protected void setControlInformation(Route g) { 664 // Get sensor control information if any 665 Sensor sensor = sensor1.getSelectedItem(); 666 if (sensor != null) { 667 if ((!g.addSensorToRoute(sensor.getSystemName(), sensorModeFromBox(sensor1mode)))) { 668 log.error("Unexpected failure to add Sensor '{}' to route '{}'.", sensor.getSystemName(), g.getSystemName()); 669 } 670 } 671 672 if (sensor2.getSelectedItem() != null) { 673 if ((!g.addSensorToRoute(sensor2.getSelectedItemDisplayName(), sensorModeFromBox(sensor2mode)))) { 674 log.error("Unexpected failure to add Sensor '{}' to Route '{}'.", sensor2.getSelectedItemDisplayName(), g.getSystemName()); 675 } 676 } 677 678 if (sensor3.getSelectedItem() != null) { 679 if ((!g.addSensorToRoute(sensor3.getSelectedItemDisplayName(), sensorModeFromBox(sensor3mode)))) { 680 log.error("Unexpected failure to add Sensor '{}' to Route '{}'.", sensor3.getSelectedItemDisplayName(), g.getSystemName()); 681 } 682 } 683 684 //Turnouts Aligned sensor 685 if (turnoutsAlignedSensor.getSelectedItem() != null) { 686 g.setTurnoutsAlignedSensor(turnoutsAlignedSensor.getSelectedItemDisplayName()); 687 } else { 688 g.setTurnoutsAlignedSensor(""); 689 } 690 691 // Set turnout information if there is any 692 if (cTurnout.getSelectedItem() != null) { 693 g.setControlTurnout(cTurnout.getSelectedItemDisplayName()); 694 // set up Control Turnout state 695 g.setControlTurnoutState(turnoutModeFromBox(cTurnoutStateBox)); 696 g.setControlTurnoutFeedback(cTurnoutFeedbackBox.getSelectedIndex() == 1); 697 698 } else { 699 // No Control Turnout was entered 700 g.setControlTurnout(""); 701 } 702 // set route specific Delay information, see jmri.implementation.DefaultRoute#SetRouteThread() 703 int addDelay = (Integer) timeDelay.getValue(); // from a JSpinner with 0 set as minimum 704 g.setRouteCommandDelay(addDelay); 705 706 // Set Lock Turnout information if there is any 707 if (cLockTurnout.getSelectedItem() != null) { 708 g.setLockControlTurnout(cLockTurnout.getSelectedItemDisplayName()); 709 // set up control turnout state 710 g.setLockControlTurnoutState(turnoutModeFromBox(cLockTurnoutStateBox)); 711 } else { 712 // No Lock Turnout was entered 713 g.setLockControlTurnout(""); 714 } 715 } 716 717 /** 718 * Set the sound file. 719 */ 720 private void setSoundPressed() { 721 if (soundChooser == null) { 722 soundChooser = new jmri.util.swing.JmriJFileChooser(FileUtil.getUserFilesPath()); 723 soundChooser.setFileFilter(new jmri.util.NoArchiveFileFilter()); 724 } 725 soundChooser.rescanCurrentDirectory(); 726 int retVal = soundChooser.showOpenDialog(null); 727 // handle selection or cancel 728 if (retVal == JFileChooser.APPROVE_OPTION) { 729 try { 730 soundFile.setText(soundChooser.getSelectedFile().getCanonicalPath()); 731 } catch (java.io.IOException e) { 732 log.error("exception setting sound file: ", e); 733 } 734 } 735 } 736 737 /** 738 * Set the script file. 739 */ 740 private void setScriptPressed() { 741 if (scriptChooser == null) { 742 scriptChooser = new ScriptFileChooser(); 743 } 744 scriptChooser.rescanCurrentDirectory(); 745 int retVal = scriptChooser.showOpenDialog(null); 746 // handle selection or cancel 747 if (retVal == JFileChooser.APPROVE_OPTION) { 748 try { 749 scriptFile.setText(scriptChooser.getSelectedFile().getCanonicalPath()); 750 } catch (java.io.IOException e) { 751 log.error("exception setting script file: ", e); 752 } 753 } 754 } 755 756 757 protected void finishUpdate() { 758 // move to show all Turnouts if not there 759 cancelIncludedOnly(); 760 // Provide feedback to user 761 // switch GUI back to selection mode 762 //status2.setText(Bundle.getMessage("RouteAddStatusInitial2", Bundle.getMessage("ButtonEdit"))); 763 status2.setVisible(true); 764 autoSystemName(); 765 setTitle(Bundle.getMessage("TitleAddRoute")); 766 clearPage(); 767 // reactivate the Route 768 routeDirty = true; 769 // get out of edit mode 770 editMode = false; 771 if (curRoute != null) { 772 curRoute.activateRoute(); 773 } 774 } 775 776 /** 777 * Populate the page fields. The route names are not included since they are handled 778 * by the Edit or Add actions. 779 * <p> 780 * The route is either the route being edited or a source route for doing a copy during 781 * the add action. 782 * 783 * @param route The route that contains the content. 784 */ 785 protected void setPageContent(Route route) { 786 // set up Turnout list for this route 787 int setRow = 0; 788 for (int i = _turnoutList.size() - 1; i >= 0; i--) { 789 RouteTurnout turnout = _turnoutList.get(i); 790 String tSysName = turnout.getSysName(); 791 if (route.isOutputTurnoutIncluded(tSysName)) { 792 turnout.setIncluded(true); 793 turnout.setState(route.getOutputTurnoutSetState(tSysName)); 794 setRow = i; 795 } else { 796 turnout.setIncluded(false); 797 turnout.setState(Turnout.CLOSED); 798 } 799 } 800 setRow -= 1; 801 if (setRow < 0) { 802 setRow = 0; 803 } 804 _routeTurnoutScrollPane.getVerticalScrollBar().setValue(setRow * ROW_HEIGHT); 805 _routeTurnoutModel.fireTableDataChanged(); 806 807 // set up Sensor list for this route 808 for (int i = _sensorList.size() - 1; i >= 0; i--) { 809 RouteSensor sensor = _sensorList.get(i); 810 String tSysName = sensor.getSysName(); 811 if (route.isOutputSensorIncluded(tSysName)) { 812 sensor.setIncluded(true); 813 sensor.setState(route.getOutputSensorSetState(tSysName)); 814 setRow = i; 815 } else { 816 sensor.setIncluded(false); 817 sensor.setState(Sensor.INACTIVE); 818 } 819 } 820 setRow -= 1; 821 if (setRow < 0) { 822 setRow = 0; 823 } 824 _routeSensorScrollPane.getVerticalScrollBar().setValue(setRow * ROW_HEIGHT); 825 _routeSensorModel.fireTableDataChanged(); 826 827 // get Sound and Script file names 828 scriptFile.setText(route.getOutputScriptName()); 829 soundFile.setText(route.getOutputSoundName()); 830 831 // get Turnout Aligned sensor 832 turnoutsAlignedSensor.setSelectedItem(route.getTurnoutsAlgdSensor()); 833 834 // set up Control Sensors if there are any 835 Sensor[] temNames = new Sensor[Route.MAX_CONTROL_SENSORS]; 836 int[] temModes = new int[Route.MAX_CONTROL_SENSORS]; 837 for (int k = 0; k < Route.MAX_CONTROL_SENSORS; k++) { 838 temNames[k] = route.getRouteSensor(k); 839 temModes[k] = route.getRouteSensorMode(k); 840 } 841 sensor1.setSelectedItem(temNames[0]); 842 setSensorModeBox(temModes[0], sensor1mode); 843 844 sensor2.setSelectedItem(temNames[1]); 845 setSensorModeBox(temModes[1], sensor2mode); 846 847 sensor3.setSelectedItem(temNames[2]); 848 setSensorModeBox(temModes[2], sensor3mode); 849 850 // set up Control Turnout if there is one 851 cTurnout.setSelectedItem(route.getCtlTurnout()); 852 853 setTurnoutModeBox(route.getControlTurnoutState(), cTurnoutStateBox); 854 855 if (route.getControlTurnoutFeedback()) { 856 cTurnoutFeedbackBox.setSelectedIndex(1); // Known 857 } else { 858 cTurnoutFeedbackBox.setSelectedIndex(0); // Commanded 859 } 860 861 // set up Lock Control Turnout if there is one 862 cLockTurnout.setSelectedItem(route.getLockCtlTurnout()); 863 864 setTurnoutModeBox(route.getLockControlTurnoutState(), cLockTurnoutStateBox); 865 866 // set up additional route specific Delay 867 timeDelay.setValue(route.getRouteCommandDelay()); 868 } 869 870 private void clearPage() { 871 _systemName.setText(""); 872 _userName.setText(""); 873 sensor1.setSelectedItem(null); 874 sensor2.setSelectedItem(null); 875 sensor3.setSelectedItem(null); 876 cTurnout.setSelectedItem(null); 877 cLockTurnout.setSelectedItem(null); 878 turnoutsAlignedSensor.setSelectedItem(null); 879 soundFile.setText(""); 880 scriptFile.setText(""); 881 for (int i = _turnoutList.size() - 1; i >= 0; i--) { 882 _turnoutList.get(i).setIncluded(false); 883 } 884 for (int i = _sensorList.size() - 1; i >= 0; i--) { 885 _sensorList.get(i).setIncluded(false); 886 } 887 } 888 889 890 /** 891 * Cancel included Turnouts only option 892 */ 893 private void cancelIncludedOnly() { 894 if (!showAll) { 895 allButton.doClick(); 896 } 897 } 898 899 List<RouteTurnout> get_turnoutList() { 900 return _turnoutList; 901 } 902 903 List<RouteTurnout> get_includedTurnoutList() { 904 return _includedTurnoutList; 905 } 906 907 List<RouteSensor> get_sensorList() { 908 return _sensorList; 909 } 910 911 List<RouteSensor> get_includedSensorList() { 912 return _includedSensorList; 913 } 914 915 public boolean isShowAll() { 916 return showAll; 917 } 918 919 /** 920 * Cancels edit mode 921 */ 922 protected void cancelEdit() { 923 if (editMode) { 924 status1.setText(Bundle.getMessage("RouteAddStatusInitial1", Bundle.getMessage("ButtonCreate"))); // I18N to include original button name in help string 925 //status2.setText(Bundle.getMessage("RouteAddStatusInitial2", Bundle.getMessage("ButtonEdit"))); 926 finishUpdate(); 927 // get out of edit mode 928 editMode = false; 929 curRoute = null; 930 } 931 closeFrame(); 932 } 933 934 /** 935 * Respond to the Update button - update to Route Table. 936 * 937 * @param newRoute true if a new route; false otherwise 938 */ 939 protected void updatePressed(boolean newRoute) { 940 // Check if the User Name has been changed 941 String uName = _userName.getText(); 942 Route g = checkNamesOK(); 943 if (g == null) { 944 return; 945 } 946 // User Name is unique, change it 947 g.setUserName(uName); 948 // clear the current Turnout information for this Route 949 g.clearOutputTurnouts(); 950 g.clearOutputSensors(); 951 // clear the current Sensor information for this Route 952 g.clearRouteSensors(); 953 // add those indicated in the panel 954 initializeIncludedList(); 955 setTurnoutInformation(g); 956 setSensorInformation(g); 957 // set the current values of the file names 958 g.setOutputScriptName(scriptFile.getText()); 959 g.setOutputSoundName(soundFile.getText()); 960 // add Control Sensors and a Control Turnout if entered in the panel 961 setControlInformation(g); 962 curRoute = g; 963 finishUpdate(); 964 status1.setForeground(Color.gray); 965 status1.setText((newRoute ? Bundle.getMessage("RouteAddStatusCreated") : 966 Bundle.getMessage("RouteAddStatusUpdated")) + ": \"" + uName + "\" (" + _includedTurnoutList.size() + " " 967 + Bundle.getMessage("Turnouts") + ", " + _includedSensorList.size() + " " + Bundle.getMessage("Sensors") + ")"); 968 969 closeFrame(); 970 } 971 972 /** 973 * Check name and return a new or existing Route object with the name as entered in the _systemName field on the 974 * addFrame pane. 975 * 976 * @return the new/updated Route object 977 */ 978 private Route checkNamesOK() { 979 // Get system name and user name 980 String sName = _systemName.getText(); 981 String uName = _userName.getText(); 982 Route g; 983 if (_autoSystemName.isSelected() && !editMode) { 984 log.debug("checkNamesOK new autogroup"); 985 // create new Route with auto system name 986 g = routeManager.newRoute(uName); 987 } else { 988 if (sName.length() == 0) { 989 status1.setText(Bundle.getMessage("AddBeanStatusEnter")); 990 status1.setForeground(Color.red); 991 return null; 992 } 993 try { 994 sName = routeManager.makeSystemName(sName); 995 g = routeManager.provideRoute(sName, uName); 996 } catch (IllegalArgumentException ex) { 997 g = null; // for later check: 998 } 999 } 1000 if (g == null) { 1001 // should never get here 1002 log.error("Unknown failure to create Route with System Name: {}", sName); // NOI18N 1003 } else { 1004 g.deActivateRoute(); 1005 } 1006 return g; 1007 } 1008 1009 protected void closeFrame(){ 1010 // remind to save, if Route was created or edited 1011 if (routeDirty) { 1012 showReminderMessage(); 1013 routeDirty = false; 1014 } 1015 // hide addFrame 1016 setVisible(false); 1017 1018 // if in Edit, cancel edit mode 1019 if (editMode) { 1020 cancelEdit(); 1021 } 1022 _routeSensorModel.dispose(); 1023 _routeTurnoutModel.dispose(); 1024 this.dispose(); 1025 } 1026 1027 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AbstractRouteAddEditFrame.class); 1028}