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 if (checkEnabled) return; 588 InstanceManager.getDefault(UserPreferencesManager.class). 589 showInfoMessage(Bundle.getMessage("ReminderTitle"), // NOI18N 590 Bundle.getMessage("ReminderSaveString", Bundle.getMessage("MenuItemRouteTable")), // NOI18N 591 getClassName(), "remindSaveRoute"); // NOI18N 592 } 593 594 private int sensorModeFromBox(JComboBox<String> box) { 595 String mode = (String) box.getSelectedItem(); 596 return sensorModeFromString(mode); 597 } 598 599 int sensorModeFromString(String mode) { 600 int result = StringUtil.getStateFromName(mode, sensorInputModeValues, sensorInputModes); 601 602 if (result < 0) { 603 log.warn("unexpected mode string in sensorMode: {}", mode); 604 throw new IllegalArgumentException(); 605 } 606 return result; 607 } 608 609 void setSensorModeBox(int mode, JComboBox<String> box) { 610 String result = StringUtil.getNameFromState(mode, sensorInputModeValues, sensorInputModes); 611 box.setSelectedItem(result); 612 } 613 614 private int turnoutModeFromBox(JComboBox<String> box) { 615 String mode = (String) box.getSelectedItem(); 616 int result = StringUtil.getStateFromName(mode, turnoutInputModeValues, turnoutInputModes); 617 618 if (result < 0) { 619 log.warn("unexpected mode string in turnoutMode: {}", mode); 620 throw new IllegalArgumentException(); 621 } 622 return result; 623 } 624 625 void setTurnoutModeBox(int mode, JComboBox<String> box) { 626 String result = StringUtil.getNameFromState(mode, turnoutInputModeValues, turnoutInputModes); 627 box.setSelectedItem(result); 628 } 629 630 /** 631 * Set the Turnout information for adding or editing. 632 * 633 * @param g the route to add the turnout to 634 */ 635 protected void setTurnoutInformation(Route g) { 636 for (RouteTurnout t : _includedTurnoutList) { 637 g.addOutputTurnout(t.getDisplayName(), t.getState()); 638 } 639 } 640 641 /** 642 * Sets the Sensor information for adding or editing. 643 * 644 * @param g the route to add the sensor to 645 */ 646 protected void setSensorInformation(Route g) { 647 for (RouteSensor s : _includedSensorList) { 648 g.addOutputSensor(s.getDisplayName(), s.getState()); 649 } 650 } 651 652 /** 653 * Set the Sensor, Turnout, and delay control information for adding or editing. 654 * 655 * @param g the route to configure 656 */ 657 protected void setControlInformation(Route g) { 658 // Get sensor control information if any 659 Sensor sensor = sensor1.getSelectedItem(); 660 if (sensor != null) { 661 if ((!g.addSensorToRoute(sensor.getSystemName(), sensorModeFromBox(sensor1mode)))) { 662 log.error("Unexpected failure to add Sensor '{}' to route '{}'.", sensor.getSystemName(), g.getSystemName()); 663 } 664 } 665 666 if (sensor2.getSelectedItem() != null) { 667 if ((!g.addSensorToRoute(sensor2.getSelectedItemDisplayName(), sensorModeFromBox(sensor2mode)))) { 668 log.error("Unexpected failure to add Sensor '{}' to Route '{}'.", sensor2.getSelectedItemDisplayName(), g.getSystemName()); 669 } 670 } 671 672 if (sensor3.getSelectedItem() != null) { 673 if ((!g.addSensorToRoute(sensor3.getSelectedItemDisplayName(), sensorModeFromBox(sensor3mode)))) { 674 log.error("Unexpected failure to add Sensor '{}' to Route '{}'.", sensor3.getSelectedItemDisplayName(), g.getSystemName()); 675 } 676 } 677 678 //Turnouts Aligned sensor 679 if (turnoutsAlignedSensor.getSelectedItem() != null) { 680 g.setTurnoutsAlignedSensor(turnoutsAlignedSensor.getSelectedItemDisplayName()); 681 } else { 682 g.setTurnoutsAlignedSensor(""); 683 } 684 685 // Set turnout information if there is any 686 if (cTurnout.getSelectedItem() != null) { 687 g.setControlTurnout(cTurnout.getSelectedItemDisplayName()); 688 // set up Control Turnout state 689 g.setControlTurnoutState(turnoutModeFromBox(cTurnoutStateBox)); 690 g.setControlTurnoutFeedback(cTurnoutFeedbackBox.getSelectedIndex() == 1); 691 692 } else { 693 // No Control Turnout was entered 694 g.setControlTurnout(""); 695 } 696 // set route specific Delay information, see jmri.implementation.DefaultRoute#SetRouteThread() 697 int addDelay = (Integer) timeDelay.getValue(); // from a JSpinner with 0 set as minimum 698 g.setRouteCommandDelay(addDelay); 699 700 // Set Lock Turnout information if there is any 701 if (cLockTurnout.getSelectedItem() != null) { 702 g.setLockControlTurnout(cLockTurnout.getSelectedItemDisplayName()); 703 // set up control turnout state 704 g.setLockControlTurnoutState(turnoutModeFromBox(cLockTurnoutStateBox)); 705 } else { 706 // No Lock Turnout was entered 707 g.setLockControlTurnout(""); 708 } 709 } 710 711 /** 712 * Set the sound file. 713 */ 714 private void setSoundPressed() { 715 if (soundChooser == null) { 716 soundChooser = new jmri.util.swing.JmriJFileChooser(FileUtil.getUserFilesPath()); 717 soundChooser.setFileFilter(new jmri.util.NoArchiveFileFilter()); 718 } 719 soundChooser.rescanCurrentDirectory(); 720 int retVal = soundChooser.showOpenDialog(null); 721 // handle selection or cancel 722 if (retVal == JFileChooser.APPROVE_OPTION) { 723 try { 724 soundFile.setText(soundChooser.getSelectedFile().getCanonicalPath()); 725 } catch (java.io.IOException e) { 726 log.error("exception setting sound file: ", e); 727 } 728 } 729 } 730 731 /** 732 * Set the script file. 733 */ 734 private void setScriptPressed() { 735 if (scriptChooser == null) { 736 scriptChooser = new ScriptFileChooser(); 737 } 738 scriptChooser.rescanCurrentDirectory(); 739 int retVal = scriptChooser.showOpenDialog(null); 740 // handle selection or cancel 741 if (retVal == JFileChooser.APPROVE_OPTION) { 742 try { 743 scriptFile.setText(scriptChooser.getSelectedFile().getCanonicalPath()); 744 } catch (java.io.IOException e) { 745 log.error("exception setting script file: ", e); 746 } 747 } 748 } 749 750 751 protected void finishUpdate() { 752 // move to show all Turnouts if not there 753 cancelIncludedOnly(); 754 // Provide feedback to user 755 // switch GUI back to selection mode 756 //status2.setText(Bundle.getMessage("RouteAddStatusInitial2", Bundle.getMessage("ButtonEdit"))); 757 status2.setVisible(true); 758 autoSystemName(); 759 setTitle(Bundle.getMessage("TitleAddRoute")); 760 clearPage(); 761 // reactivate the Route 762 routeDirty = true; 763 // get out of edit mode 764 editMode = false; 765 if (curRoute != null) { 766 curRoute.activateRoute(); 767 } 768 } 769 770 /** 771 * Populate the page fields. The route names are not included since they are handled 772 * by the Edit or Add actions. 773 * <p> 774 * The route is either the route being edited or a source route for doing a copy during 775 * the add action. 776 * 777 * @param route The route that contains the content. 778 */ 779 protected void setPageContent(Route route) { 780 // set up Turnout list for this route 781 int setRow = 0; 782 for (int i = _turnoutList.size() - 1; i >= 0; i--) { 783 RouteTurnout turnout = _turnoutList.get(i); 784 String tSysName = turnout.getSysName(); 785 if (route.isOutputTurnoutIncluded(tSysName)) { 786 turnout.setIncluded(true); 787 turnout.setState(route.getOutputTurnoutSetState(tSysName)); 788 setRow = i; 789 } else { 790 turnout.setIncluded(false); 791 turnout.setState(Turnout.CLOSED); 792 } 793 } 794 setRow -= 1; 795 if (setRow < 0) { 796 setRow = 0; 797 } 798 _routeTurnoutScrollPane.getVerticalScrollBar().setValue(setRow * ROW_HEIGHT); 799 _routeTurnoutModel.fireTableDataChanged(); 800 801 // set up Sensor list for this route 802 for (int i = _sensorList.size() - 1; i >= 0; i--) { 803 RouteSensor sensor = _sensorList.get(i); 804 String tSysName = sensor.getSysName(); 805 if (route.isOutputSensorIncluded(tSysName)) { 806 sensor.setIncluded(true); 807 sensor.setState(route.getOutputSensorSetState(tSysName)); 808 setRow = i; 809 } else { 810 sensor.setIncluded(false); 811 sensor.setState(Sensor.INACTIVE); 812 } 813 } 814 setRow -= 1; 815 if (setRow < 0) { 816 setRow = 0; 817 } 818 _routeSensorScrollPane.getVerticalScrollBar().setValue(setRow * ROW_HEIGHT); 819 _routeSensorModel.fireTableDataChanged(); 820 821 // get Sound and Script file names 822 scriptFile.setText(route.getOutputScriptName()); 823 soundFile.setText(route.getOutputSoundName()); 824 825 // get Turnout Aligned sensor 826 turnoutsAlignedSensor.setSelectedItem(route.getTurnoutsAlgdSensor()); 827 828 // set up Control Sensors if there are any 829 Sensor[] temNames = new Sensor[Route.MAX_CONTROL_SENSORS]; 830 int[] temModes = new int[Route.MAX_CONTROL_SENSORS]; 831 for (int k = 0; k < Route.MAX_CONTROL_SENSORS; k++) { 832 temNames[k] = route.getRouteSensor(k); 833 temModes[k] = route.getRouteSensorMode(k); 834 } 835 sensor1.setSelectedItem(temNames[0]); 836 setSensorModeBox(temModes[0], sensor1mode); 837 838 sensor2.setSelectedItem(temNames[1]); 839 setSensorModeBox(temModes[1], sensor2mode); 840 841 sensor3.setSelectedItem(temNames[2]); 842 setSensorModeBox(temModes[2], sensor3mode); 843 844 // set up Control Turnout if there is one 845 cTurnout.setSelectedItem(route.getCtlTurnout()); 846 847 setTurnoutModeBox(route.getControlTurnoutState(), cTurnoutStateBox); 848 849 if (route.getControlTurnoutFeedback()) { 850 cTurnoutFeedbackBox.setSelectedIndex(1); // Known 851 } else { 852 cTurnoutFeedbackBox.setSelectedIndex(0); // Commanded 853 } 854 855 // set up Lock Control Turnout if there is one 856 cLockTurnout.setSelectedItem(route.getLockCtlTurnout()); 857 858 setTurnoutModeBox(route.getLockControlTurnoutState(), cLockTurnoutStateBox); 859 860 // set up additional route specific Delay 861 timeDelay.setValue(route.getRouteCommandDelay()); 862 } 863 864 private void clearPage() { 865 _systemName.setText(""); 866 _userName.setText(""); 867 sensor1.setSelectedItem(null); 868 sensor2.setSelectedItem(null); 869 sensor3.setSelectedItem(null); 870 cTurnout.setSelectedItem(null); 871 cLockTurnout.setSelectedItem(null); 872 turnoutsAlignedSensor.setSelectedItem(null); 873 soundFile.setText(""); 874 scriptFile.setText(""); 875 for (int i = _turnoutList.size() - 1; i >= 0; i--) { 876 _turnoutList.get(i).setIncluded(false); 877 } 878 for (int i = _sensorList.size() - 1; i >= 0; i--) { 879 _sensorList.get(i).setIncluded(false); 880 } 881 } 882 883 884 /** 885 * Cancel included Turnouts only option 886 */ 887 private void cancelIncludedOnly() { 888 if (!showAll) { 889 allButton.doClick(); 890 } 891 } 892 893 private String getClassName() { 894 return this.getClass().getName(); 895 } 896 897 List<RouteTurnout> get_turnoutList() { 898 return _turnoutList; 899 } 900 901 List<RouteTurnout> get_includedTurnoutList() { 902 return _includedTurnoutList; 903 } 904 905 List<RouteSensor> get_sensorList() { 906 return _sensorList; 907 } 908 909 List<RouteSensor> get_includedSensorList() { 910 return _includedSensorList; 911 } 912 913 public boolean isShowAll() { 914 return showAll; 915 } 916 917 /** 918 * Cancels edit mode 919 */ 920 protected void cancelEdit() { 921 if (editMode) { 922 status1.setText(Bundle.getMessage("RouteAddStatusInitial1", Bundle.getMessage("ButtonCreate"))); // I18N to include original button name in help string 923 //status2.setText(Bundle.getMessage("RouteAddStatusInitial2", Bundle.getMessage("ButtonEdit"))); 924 finishUpdate(); 925 // get out of edit mode 926 editMode = false; 927 curRoute = null; 928 } 929 closeFrame(); 930 } 931 932 /** 933 * Respond to the Update button - update to Route Table. 934 * 935 * @param newRoute true if a new route; false otherwise 936 */ 937 protected void updatePressed(boolean newRoute) { 938 // Check if the User Name has been changed 939 String uName = _userName.getText(); 940 Route g = checkNamesOK(); 941 if (g == null) { 942 return; 943 } 944 // User Name is unique, change it 945 g.setUserName(uName); 946 // clear the current Turnout information for this Route 947 g.clearOutputTurnouts(); 948 g.clearOutputSensors(); 949 // clear the current Sensor information for this Route 950 g.clearRouteSensors(); 951 // add those indicated in the panel 952 initializeIncludedList(); 953 setTurnoutInformation(g); 954 setSensorInformation(g); 955 // set the current values of the file names 956 g.setOutputScriptName(scriptFile.getText()); 957 g.setOutputSoundName(soundFile.getText()); 958 // add Control Sensors and a Control Turnout if entered in the panel 959 setControlInformation(g); 960 curRoute = g; 961 finishUpdate(); 962 status1.setForeground(Color.gray); 963 status1.setText((newRoute ? Bundle.getMessage("RouteAddStatusCreated") : 964 Bundle.getMessage("RouteAddStatusUpdated")) + ": \"" + uName + "\" (" + _includedTurnoutList.size() + " " 965 + Bundle.getMessage("Turnouts") + ", " + _includedSensorList.size() + " " + Bundle.getMessage("Sensors") + ")"); 966 967 closeFrame(); 968 } 969 970 /** 971 * Check name and return a new or existing Route object with the name as entered in the _systemName field on the 972 * addFrame pane. 973 * 974 * @return the new/updated Route object 975 */ 976 private Route checkNamesOK() { 977 // Get system name and user name 978 String sName = _systemName.getText(); 979 String uName = _userName.getText(); 980 Route g; 981 if (_autoSystemName.isSelected() && !editMode) { 982 log.debug("checkNamesOK new autogroup"); 983 // create new Route with auto system name 984 g = routeManager.newRoute(uName); 985 } else { 986 if (sName.length() == 0) { 987 status1.setText(Bundle.getMessage("AddBeanStatusEnter")); 988 status1.setForeground(Color.red); 989 return null; 990 } 991 try { 992 sName = routeManager.makeSystemName(sName); 993 g = routeManager.provideRoute(sName, uName); 994 } catch (IllegalArgumentException ex) { 995 g = null; // for later check: 996 } 997 } 998 if (g == null) { 999 // should never get here 1000 log.error("Unknown failure to create Route with System Name: {}", sName); // NOI18N 1001 } else { 1002 g.deActivateRoute(); 1003 } 1004 return g; 1005 } 1006 1007 protected void closeFrame(){ 1008 // remind to save, if Route was created or edited 1009 if (routeDirty) { 1010 showReminderMessage(); 1011 routeDirty = false; 1012 } 1013 // hide addFrame 1014 setVisible(false); 1015 1016 // if in Edit, cancel edit mode 1017 if (editMode) { 1018 cancelEdit(); 1019 } 1020 _routeSensorModel.dispose(); 1021 _routeTurnoutModel.dispose(); 1022 this.dispose(); 1023 } 1024 1025 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AbstractRouteAddEditFrame.class); 1026}