001package jmri.jmrit.beantable; 002 003import java.awt.BorderLayout; 004import java.awt.Color; 005import java.awt.Container; 006import java.awt.FlowLayout; 007import java.awt.event.ActionEvent; 008import java.beans.PropertyChangeListener; 009import java.util.ArrayList; 010import javax.swing.*; 011import javax.swing.border.Border; 012import javax.swing.table.AbstractTableModel; 013import javax.swing.table.TableColumn; 014import javax.swing.table.TableColumnModel; 015import javax.swing.table.TableRowSorter; 016import jmri.*; 017import jmri.swing.RowSorterUtil; 018import jmri.util.JmriJFrame; 019import jmri.util.StringUtil; 020import org.slf4j.Logger; 021import org.slf4j.LoggerFactory; 022 023/** 024 * Swing action to create and register a SignalGroup - Signal Head Edit Table. 025 * <p> 026 * Based in part on RouteTableAction.java and SignalGroupTableAction.java by Bob Jacobsen 027 * 028 * @author Kevin Dickerson Copyright (C) 2010 029 * @author Egbert Broerse 2017 030 031 */ 032public class SignalGroupSubTableAction { 033 034 /** 035 * Create an action with a specific title. 036 * <p> 037 * Note that the argument is the Action title, not the title of the 038 * resulting frame. Perhaps this should be changed? 039 * 040 * @param s title of the action 041 */ 042 public SignalGroupSubTableAction(String s) { 043 } 044 045 public SignalGroupSubTableAction() { 046 this("Signal Group Head Edit Table"); 047 } // NOI18N is never displayed on screen 048 049 String helpTarget() { 050 return "package.jmri.jmrit.beantable.SignalGroupTable"; 051 } 052 053 /** 054 * Set choice for conditional evaluation. 055 * <p> 056 * Set to AND when you want all conditionals to be met for the Signal Head 057 * to turn On when an included Aspect is shown on the main Mast. 058 * Set to OR when you at least one of the conditionals to be met for the 059 * Signal Head to turn On when an included Aspect is shown. 060 * 061 * See {@link #operFromBox} 062 * 063 * @param mode True for AND 064 * @param box the comboBox object to set 065 */ 066 void setoperBox(boolean mode, JComboBox<String> box) { 067 int _mode = 0; // OR 068 if (mode) { 069 _mode = 1; // AND 070 } 071 String result = StringUtil.getNameFromState(_mode, operValues, oper); 072 box.setSelectedItem(result); 073 } 074 075 /** 076 * Get the user choice for conditional evaluation. 077 * 078 * See {@link #setoperBox} 079 * 080 * @param box the comboBox object containing the user choice 081 * @return True for AND, False for OR 082 */ 083 boolean operFromBox(JComboBox<String> box) { 084 String mode = (String) box.getSelectedItem(); 085 int result = StringUtil.getStateFromName(mode, operValues, oper); 086 087 if (result < 0) { 088 log.warn("unexpected mode string in Signal Head Appearance Mode: {}", mode); 089 throw new IllegalArgumentException(); 090 } 091 return result != 0; 092 } 093 094 private static String[] oper = new String[]{"AND", "OR"}; 095 private static int[] operValues = new int[]{0x00, 0x01}; 096 097 /** 098 * Get the user choice for a Signal Group Signal Head's On and Off Appearance 099 * from a comboBox at the top of the Edit Head sub pane. 100 * 101 * @param box the comboBox object containing the user choice 102 * @return Value for the Appearance (color) set i.e. 0 for DARK 103 */ 104 int headStateFromBox(JComboBox<String> box) throws IllegalArgumentException { 105 SignalHead sig = InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(curHeadName); 106 int result; 107 String mode = (String) box.getSelectedItem(); 108 if (sig != null) { 109 result = StringUtil.getStateFromName(mode, sig.getValidStates(), sig.getValidStateNames()); 110 } else { 111 result = StringUtil.getStateFromName(mode, signalStatesValues, signalStates); 112 } 113 114 if (result < 0) { 115 log.warn("unexpected mode string in signalHeadMode: {}", mode); 116 throw new IllegalArgumentException(); 117 } 118 return result; 119 } 120 121 /** 122 * Set selected item in a Signal Group Signal Head's On and Off Appearance 123 * in a comboBox at the top of the Edit Head sub pane. 124 * 125 * @param mode Value for an Appearance (color) i.e. 0 for DARK 126 * @param box the comboBox object to set 127 */ 128 void setSignalHeadStateBox(int mode, JComboBox<String> box) { 129 SignalHead sig = InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(curHeadName); 130 if (sig != null) { 131 String result = StringUtil.getNameFromState(mode, sig.getValidStates(), sig.getValidStateNames()); 132 box.setSelectedItem(result); 133 } else { 134 log.error("Failed to get signal head {}", curHeadName); 135 } 136 } 137 138 /** 139 * Get the user choice for a Sensor conditional's On state from the comboBox on the Edit Head sub pane. 140 * See {@link #turnoutModeFromBox} 141 * 142 * @param box the comboBox object containing the user choice 143 * @return Value for ACTIVE/INACTIVE 144 */ 145 int sensorModeFromBox(JComboBox<String> box) { 146 String mode = (String) box.getSelectedItem(); 147 int result = StringUtil.getStateFromName(mode, sensorInputModeValues, sensorInputModes); 148 149 if (result < 0) { 150 log.warn("unexpected mode string in Signal Head Appearance: {}", mode); 151 throw new IllegalArgumentException(); 152 } 153 return result; 154 } 155 156 /** 157 * Set selected item for a Sensor conditional's On state in the 158 * comboBox on the Edit Head sub pane. 159 * 160 * See {@link #turnoutModeFromBox} 161 * 162 * @param mode Value for ACTIVE/INACTIVE 163 * @param box the comboBox object to set 164 */ 165 void setSensorModeBox(int mode, JComboBox<String> box) { 166 String result = StringUtil.getNameFromState(mode, sensorInputModeValues, sensorInputModes); 167 box.setSelectedItem(result); 168 } 169 170 /** 171 * Get the user choice for a Control Turnout conditional's On state 172 * from the comboBox on the Edit Head sub pane. 173 * 174 * See {@link #sensorModeFromBox} 175 * 176 * @param box the comboBox object containing the user choice 177 * @return Value for CLOSED/THROWN 178 */ 179 int turnoutModeFromBox(JComboBox<String> box) { 180 String mode = (String) box.getSelectedItem(); 181 int result = StringUtil.getStateFromName(mode, turnoutInputModeValues, turnoutInputModes); 182 183 if (result < 0) { 184 log.warn("unexpected mode string in turnoutMode: {}", mode); 185 throw new IllegalArgumentException(); 186 } 187 return result; 188 } 189 190 /** 191 * Set selected item for a Control Turnout conditional's On state 192 * in the comboBox on the Edit Head sub pane. 193 * 194 * See {@link #turnoutModeFromBox} 195 * 196 * @param mode Value for CLOSED/THROWN 197 * @param box the comboBox object to set 198 */ 199 void setTurnoutModeBox(int mode, JComboBox<String> box) { 200 String result = StringUtil.getNameFromState(mode, turnoutInputModeValues, turnoutInputModes); 201 box.setSelectedItem(result); 202 } 203 204 JLabel _systemName; 205 JComboBox<String> _OnAppearance; 206 JComboBox<String> _OffAppearance; 207 JLabel spacer = new JLabel(" "); // to create space between On and Off Appearance choices 208 JComboBox<String> _SensorTurnoutOper = new JComboBox<>(oper); 209 210 JmriJFrame addSubFrame = null; 211 SignalGroupTurnoutModel _SignalGroupTurnoutModel; 212 JScrollPane _SignalGroupTurnoutScrollPane; 213 SignalGroupSensorModel _SignalGroupSensorModel; 214 JScrollPane _SignalGroupSensorScrollPane; 215 216 ButtonGroup selGroup = null; 217 JRadioButton allButton = null; 218 JRadioButton includedButton = null; 219 220 JLabel nameLabel = new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("BeanNameSignalHead"))); 221 JLabel signalOnStateLabel = new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("OnAppearance"))); 222 JLabel signalOffStateLabel = new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("OffAppearance"))); 223 JLabel userLabel = new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("SelectConditionsOn"))); 224 225 JButton cancelButton = new JButton(Bundle.getMessage("ButtonCancel")); 226 JButton updateSubButton = new JButton(Bundle.getMessage("ButtonApply")); 227 228 static String updateInst = Bundle.getMessage("ClickToApply", Bundle.getMessage("ButtonApply")); 229 230 JLabel status1 = new JLabel(updateInst); 231 232 JPanel p2xt = null; // Turnout list table 233 JPanel p2xs = null; // Sensor list table 234 235 SignalGroup curSignalGroup = null; 236 String curHeadName; 237 SignalHead curSignalHead; 238 239 /** 240 * Open an editor to set the details of a Signal Head as part of a Signal Group. 241 * Called when user clicks the Edit button for a Head in the Add/Edit Signal Group pane. 242 * 243 * @see SignalGroupTableAction#signalHeadEditPressed(int) SignalGroupTableAction.signalHeadEditPressed 244 * @param g Parent Signal Head 245 * @param headName System or User Name of this Signal Head 246 */ 247 void editHead(SignalGroup g, String headName) { 248 curSignalGroup = g; 249 curHeadName = headName; 250 curSignalHead = InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(curHeadName); 251 if (curSignalHead != null) { 252 _OnAppearance = new JComboBox<>(curSignalHead.getValidStateNames()); // shows i18n strings from signal head definition 253 _OffAppearance = new JComboBox<>(curSignalHead.getValidStateNames()); 254 } 255 _systemName = new JLabel(headName); 256 _systemName.setVisible(true); 257 258 TurnoutManager tm = InstanceManager.getDefault(TurnoutManager.class); 259 _turnoutList = new ArrayList<>(tm.getNamedBeanSet().size()); 260 tm.getNamedBeanSet().stream().filter(turn -> (turn != null)).forEachOrdered(turn -> { 261 _turnoutList.add(new SignalGroupTurnout(turn.getSystemName(), turn.getUserName())); 262 }); 263 264 SensorManager sm = InstanceManager.getDefault(SensorManager.class); 265 266 _sensorList = new ArrayList<>(sm.getNamedBeanSet().size()); 267 sm.getNamedBeanSet().stream().filter(sen -> (sen != null)).forEachOrdered(sen -> { 268 _sensorList.add(new SignalGroupSensor(sen.getSystemName(), sen.getUserName())); 269 }); 270 initializeIncludedList(); 271 272 // Set up sub panel for editing of a Signal Group Signal Head item 273 if (addSubFrame == null) { // create one if not yet available 274 addSubFrame = new JmriJFrame((Bundle.getMessage("EditSignalGroup") + " - " + Bundle.getMessage("BeanNameSignalHead")), false, true); 275 addSubFrame.addHelpMenu("package.jmri.jmrit.beantable.SignalGroupAddEdit", true); 276 addSubFrame.setLocation(100, 30); 277 addSubFrame.getContentPane().setLayout(new BoxLayout(addSubFrame.getContentPane(), BoxLayout.Y_AXIS)); 278 Container contentPane = addSubFrame.getContentPane(); 279 // add system name label 280 JPanel ps = new JPanel(); 281 ps.setLayout(new FlowLayout()); 282 ps.add(nameLabel); 283 ps.add(_systemName); 284 contentPane.add(ps); 285 // add user name label 286 JPanel pc = new JPanel(); 287 pc.setLayout(new FlowLayout()); 288 pc.add(signalOnStateLabel); 289 pc.add(_OnAppearance); // comboBox to set On Appearance 290 _OnAppearance.setToolTipText(Bundle.getMessage("StateWhenMetTooltip")); 291 pc.add(spacer); 292 pc.add(signalOffStateLabel); 293 pc.add(_OffAppearance); // comboBox to set On Appearance 294 _OffAppearance.setToolTipText(Bundle.getMessage("StateWhenNotMetTooltip")); 295 contentPane.add(pc); 296 297 JPanel p = new JPanel(); 298 p.setLayout(new FlowLayout()); 299 p.add(userLabel); 300 contentPane.add(p); 301 // fill in info for the Signal Head being configured 302 if (curSignalHead.getClass().getName().contains("SingleTurnoutSignalHead")) { 303 jmri.implementation.SingleTurnoutSignalHead stsh = (jmri.implementation.SingleTurnoutSignalHead) InstanceManager.getDefault(SignalHeadManager.class).getByUserName(curHeadName); 304 // we may use a user name in the editing pane, so look for that first 305 if (stsh == null) { 306 stsh = (jmri.implementation.SingleTurnoutSignalHead) InstanceManager.getDefault(SignalHeadManager.class).getBySystemName(curHeadName); 307 // when user name is empty, get by user name 308 } 309 if (stsh != null) { 310 log.debug("SGsubTA #279 editHead: setting props for signal head {}", curHeadName); 311 if ((g.getHeadOnState(curSignalHead) == 0x00) && (g.getHeadOffState(curSignalHead) == 0x00)) { 312 g.setHeadOnState(curSignalHead, stsh.getOnAppearance()); 313 g.setHeadOffState(curSignalHead, stsh.getOffAppearance()); 314 } 315 } else { 316 // nothing found 317 log.error("Failed to get signal head object named {}", curHeadName); 318 } 319 } 320 setSignalHeadStateBox(g.getHeadOnState(curSignalHead), _OnAppearance); 321 setSignalHeadStateBox(g.getHeadOffState(curSignalHead), _OffAppearance); 322 323 // add Turnout Display Choice 324 JPanel py = new JPanel(); 325 py.add(new JLabel(Bundle.getMessage("Show"))); 326 selGroup = new ButtonGroup(); 327 allButton = new JRadioButton(Bundle.getMessage("All"), true); 328 selGroup.add(allButton); 329 py.add(allButton); 330 allButton.addActionListener((ActionEvent e) -> { 331 // Setup for display of all Turnouts, if needed 332 if (!showAll) { 333 showAll = true; 334 _SignalGroupTurnoutModel.fireTableDataChanged(); 335 _SignalGroupSensorModel.fireTableDataChanged(); 336 } 337 }); 338 includedButton = new JRadioButton(Bundle.getMessage("Included"), false); 339 selGroup.add(includedButton); 340 py.add(includedButton); 341 includedButton.addActionListener((ActionEvent e) -> { 342 // Setup for display of included Turnouts only, if needed 343 if (showAll) { 344 showAll = false; 345 initializeIncludedList(); 346 _SignalGroupTurnoutModel.fireTableDataChanged(); 347 _SignalGroupSensorModel.fireTableDataChanged(); 348 } 349 }); 350 py.add(new JLabel(" " + Bundle.getMessage("_and_", Bundle.getMessage("Turnouts"), Bundle.getMessage("Sensors")))); 351 contentPane.add(py); 352 353 // add turnout table 354 p2xt = new JPanel(); 355 JPanel p2xtSpace = new JPanel(); 356 p2xtSpace.setLayout(new BoxLayout(p2xtSpace, BoxLayout.Y_AXIS)); 357 p2xtSpace.add(new JLabel("XXX")); 358 p2xt.add(p2xtSpace); 359 360 JPanel p21t = new JPanel(); 361 p21t.setLayout(new BoxLayout(p21t, BoxLayout.Y_AXIS)); 362 p21t.add(new JLabel(Bundle.getMessage("SelectInGroup", Bundle.getMessage("Turnouts")))); 363 p2xt.add(p21t); 364 _SignalGroupTurnoutModel = new SignalGroupTurnoutModel(); 365 JTable SignalGroupTurnoutTable = new JTable(_SignalGroupTurnoutModel); 366 TableRowSorter<SignalGroupTurnoutModel> sgtSorter = new TableRowSorter<>(_SignalGroupTurnoutModel); 367 368 // use NamedBean's built-in Comparator interface for sorting the system name column 369 RowSorterUtil.setSortOrder(sgtSorter, SignalGroupTurnoutModel.SNAME_COLUMN, SortOrder.ASCENDING); 370 SignalGroupTurnoutTable.setRowSorter(sgtSorter); 371 SignalGroupTurnoutTable.setRowSelectionAllowed(false); 372 SignalGroupTurnoutTable.setPreferredScrollableViewportSize(new java.awt.Dimension(480, 80)); 373 374 SignalGroupSubTableAction.setRowHeight(SignalGroupTurnoutTable.getRowHeight()); 375 JComboBox<String> stateTCombo = new JComboBox<>(); 376 stateTCombo.addItem(SET_TO_CLOSED); 377 stateTCombo.addItem(SET_TO_THROWN); 378 TableColumnModel SignalGroupTurnoutColumnModel = SignalGroupTurnoutTable.getColumnModel(); 379 TableColumn includeColumnT = SignalGroupTurnoutColumnModel. 380 getColumn(SignalGroupTurnoutModel.INCLUDE_COLUMN); 381 includeColumnT.setResizable(false); 382 includeColumnT.setMinWidth(50); 383 includeColumnT.setMaxWidth(60); 384 TableColumn sNameColumnT = SignalGroupTurnoutColumnModel. 385 getColumn(SignalGroupTurnoutModel.SNAME_COLUMN); 386 sNameColumnT.setResizable(true); 387 sNameColumnT.setMinWidth(75); 388 sNameColumnT.setMaxWidth(95); 389 TableColumn uNameColumnT = SignalGroupTurnoutColumnModel. 390 getColumn(SignalGroupTurnoutModel.UNAME_COLUMN); 391 uNameColumnT.setResizable(true); 392 uNameColumnT.setMinWidth(210); 393 uNameColumnT.setMaxWidth(260); 394 TableColumn stateColumnT = SignalGroupTurnoutColumnModel. 395 getColumn(SignalGroupTurnoutModel.STATE_COLUMN); 396 stateColumnT.setCellEditor(new DefaultCellEditor(stateTCombo)); 397 stateColumnT.setResizable(false); 398 stateColumnT.setMinWidth(90); 399 stateColumnT.setMaxWidth(100); 400 _SignalGroupTurnoutScrollPane = new JScrollPane(SignalGroupTurnoutTable); 401 p2xt.add(_SignalGroupTurnoutScrollPane, BorderLayout.CENTER); 402 contentPane.add(p2xt); 403 p2xt.setVisible(true); 404 405 JPanel po = new JPanel(); 406 po.setLayout(new FlowLayout()); 407 JLabel operLabel = new JLabel(Bundle.getMessage("ChooseOrAnd")); 408 po.add(operLabel); 409 po.add(_SensorTurnoutOper); 410 contentPane.add(po); 411 412 // add sensor table 413 p2xs = new JPanel(); 414 JPanel p2xsSpace = new JPanel(); 415 p2xsSpace.setLayout(new BoxLayout(p2xsSpace, BoxLayout.Y_AXIS)); 416 p2xsSpace.add(new JLabel("XXX")); 417 p2xs.add(p2xsSpace); 418 419 JPanel p21s = new JPanel(); 420 p21s.setLayout(new BoxLayout(p21s, BoxLayout.Y_AXIS)); 421 p21s.add(new JLabel(Bundle.getMessage("SelectInGroup", Bundle.getMessage("Sensors")))); 422 p2xs.add(p21s); 423 _SignalGroupSensorModel = new SignalGroupSensorModel(); 424 JTable SignalGroupSensorTable = new JTable(_SignalGroupSensorModel); 425 TableRowSorter<SignalGroupSensorModel> sgsSorter = new TableRowSorter<>(_SignalGroupSensorModel); 426 427 // use NamedBean's built-in Comparator interface for sorting the system name column 428 RowSorterUtil.setSortOrder(sgsSorter, SignalGroupSensorModel.SNAME_COLUMN, SortOrder.ASCENDING); 429 SignalGroupSensorTable.setRowSorter(sgsSorter); 430 SignalGroupSensorTable.setRowSelectionAllowed(false); 431 SignalGroupSensorTable.setPreferredScrollableViewportSize(new java.awt.Dimension(480, 80)); 432 JComboBox<String> stateSCombo = new JComboBox<>(); 433 stateSCombo.addItem(SET_TO_ACTIVE); 434 stateSCombo.addItem(SET_TO_INACTIVE); 435 TableColumnModel SignalGroupSensorColumnModel = SignalGroupSensorTable.getColumnModel(); 436 TableColumn includeColumnS = SignalGroupSensorColumnModel. 437 getColumn(SignalGroupSensorModel.INCLUDE_COLUMN); 438 includeColumnS.setResizable(false); 439 includeColumnS.setMinWidth(50); 440 includeColumnS.setMaxWidth(60); 441 TableColumn sNameColumnS = SignalGroupSensorColumnModel. 442 getColumn(SignalGroupSensorModel.SNAME_COLUMN); 443 sNameColumnS.setResizable(true); 444 sNameColumnS.setMinWidth(75); 445 sNameColumnS.setMaxWidth(95); 446 TableColumn uNameColumnS = SignalGroupSensorColumnModel. 447 getColumn(SignalGroupSensorModel.UNAME_COLUMN); 448 uNameColumnS.setResizable(true); 449 uNameColumnS.setMinWidth(210); 450 uNameColumnS.setMaxWidth(260); 451 TableColumn stateColumnS = SignalGroupSensorColumnModel. 452 getColumn(SignalGroupSensorModel.STATE_COLUMN); 453 stateColumnS.setCellEditor(new DefaultCellEditor(stateSCombo)); 454 stateColumnS.setResizable(false); 455 stateColumnS.setMinWidth(90); 456 stateColumnS.setMaxWidth(100); 457 _SignalGroupSensorScrollPane = new JScrollPane(SignalGroupSensorTable); 458 p2xs.add(_SignalGroupSensorScrollPane, BorderLayout.CENTER); 459 contentPane.add(p2xs); 460 p2xs.setVisible(true); 461 462 // add notes panel 463 JPanel pa = new JPanel(); 464 pa.setLayout(new BoxLayout(pa, BoxLayout.Y_AXIS)); 465 JPanel p1 = new JPanel(); 466 p1.setLayout(new FlowLayout()); 467 status1.setFont(status1.getFont().deriveFont(0.9f * nameLabel.getFont().getSize())); // a bit smaller 468 status1.setForeground(Color.gray); 469 p1.add(status1); 470 pa.add(p1); 471 Border pBorder = BorderFactory.createEtchedBorder(); 472 pa.setBorder(pBorder); 473 contentPane.add(pa); 474 475 // add buttons - Add SignalGroup button 476 JPanel pb = new JPanel(); 477 pb.setLayout(new FlowLayout(FlowLayout.TRAILING)); 478 // add Cancel button 479 pb.add(cancelButton); 480 cancelButton.addActionListener(this::cancelSubPressed); 481 // add Update SignalGroup button 482 pb.add(updateSubButton); 483 updateSubButton.addActionListener((ActionEvent e) -> { 484 updateSubPressed(e, false); 485 }); 486 updateSubButton.setToolTipText(Bundle.getMessage("TooltipUpdateGroup")); 487 488 p2xtSpace.setVisible(false); 489 p2xsSpace.setVisible(false); 490 updateSubButton.setVisible(true); 491 contentPane.add(pb); 492 addSubFrame.pack(); 493 } 494 // set listener for window closing 495 addSubFrame.addWindowListener(new java.awt.event.WindowAdapter() { 496 @Override 497 public void windowClosing(java.awt.event.WindowEvent e) { 498 addSubFrame.setVisible(false); 499 cancelSubEdit(); 500 _SignalGroupSensorModel.dispose(); 501 _SignalGroupTurnoutModel.dispose(); 502 } 503 }); 504 addSubFrame.setVisible(true); 505 // add AND/OR choice box 506 setoperBox(curSignalGroup.getSensorTurnoutOper(curSignalHead), _SensorTurnoutOper); 507 setSignalHeadStateBox(curSignalGroup.getHeadOnState(curSignalHead), _OnAppearance); 508 setSignalHeadStateBox(curSignalGroup.getHeadOffState(curSignalHead), _OffAppearance); 509 int setRow = 0; 510 for (int i = _turnoutList.size() - 1; i >= 0; i--) { 511 SignalGroupTurnout turnout = _turnoutList.get(i); 512 Turnout tTurnout = turnout.getTurnout(); 513 if (curSignalGroup.isTurnoutIncluded(curSignalHead, tTurnout)) { 514 turnout.setIncluded(true); 515 turnout.setState(curSignalGroup.getTurnoutState(curSignalHead, tTurnout)); 516 setRow = i; 517 } else { 518 turnout.setIncluded(false); 519 turnout.setState(Turnout.CLOSED); 520 } 521 } 522 setRow -= 1; 523 if (setRow < 0) { 524 setRow = 0; 525 } 526 _SignalGroupTurnoutScrollPane.getVerticalScrollBar().setValue(setRow * ROW_HEIGHT); 527 _SignalGroupTurnoutModel.fireTableDataChanged(); 528 529 for (int i = _sensorList.size() - 1; i >= 0; i--) { 530 SignalGroupSensor sensor = _sensorList.get(i); 531 Sensor tSensor = sensor.getSensor(); 532 if (curSignalGroup.isSensorIncluded(curSignalHead, tSensor)) { 533 sensor.setIncluded(true); 534 sensor.setState(curSignalGroup.getSensorState(curSignalHead, tSensor)); 535 } else { 536 sensor.setIncluded(false); 537 sensor.setState(Sensor.INACTIVE); 538 } 539 } 540 541 status1.setText(updateInst); 542 updateSubButton.setVisible(true); 543 } 544 545 /** 546 * Initialize the list of included turnouts and sensors for a 547 * Signal Head item on the sub pane. 548 */ 549 void initializeIncludedList() { 550 _includedTurnoutList = new ArrayList<>(); 551 for (int i = 0; i < _turnoutList.size(); i++) { 552 if (_turnoutList.get(i).isIncluded()) { 553 _includedTurnoutList.add(_turnoutList.get(i)); 554 } 555 } 556 _includedSensorList = new ArrayList<>(); 557 for (int i = 0; i < _sensorList.size(); i++) { 558 if (_sensorList.get(i).isIncluded()) { 559 _includedSensorList.add(_sensorList.get(i)); 560 } 561 } 562 } 563 564 /** 565 * Set the Turnout information for adding or editing. 566 * 567 * @param g The Signal Group being configured 568 * @return total number of turnouts included in group 569 */ 570 int setTurnoutInformation(SignalGroup g) { 571 for (int i = 0; i < _includedTurnoutList.size(); i++) { 572 SignalGroupTurnout t = _includedTurnoutList.get(i); 573 g.setHeadAlignTurnout(curSignalHead, t.getTurnout(), t.getState()); 574 } 575 return _includedTurnoutList.size(); 576 } 577 578 /** 579 * Set the Sensor information for adding or editing. 580 * 581 * @param g The Signal Group being configured 582 * @return total number of sensors included in group 583 */ 584 int setSensorInformation(SignalGroup g) { 585 for (int i = 0; i < _includedSensorList.size(); i++) { 586 SignalGroupSensor s = _includedSensorList.get(i); 587 g.setHeadAlignSensor(curSignalHead, s.getSensor(), s.getState()); 588 } 589 return _includedSensorList.size(); 590 } 591 592 /** 593 * Respond to the Cancel button - clean up. 594 * 595 * @param e the event heard 596 */ 597 void cancelSubPressed(ActionEvent e) { 598 log.debug("Edit Signal Group Head canceled in SGSTA line 569"); 599 cancelSubEdit(); 600 _SignalGroupSensorModel.dispose(); 601 _SignalGroupTurnoutModel.dispose(); 602 } 603 604 /** 605 * Respond to the Update button on the Edit Head sub pane - update to SignalGroup. 606 * 607 * @param e the event heard 608 * @param newSignalGroup True if this is a newly created Signal 609 * Group for which additional actions are required 610 */ 611 void updateSubPressed(ActionEvent e, boolean newSignalGroup) { 612 curSignalGroup.clearHeadTurnout(curSignalHead); 613 curSignalGroup.clearHeadSensor(curSignalHead); 614 // store the new configuration as entered by the user 615 initializeIncludedList(); 616 setTurnoutInformation(curSignalGroup); 617 setSensorInformation(curSignalGroup); 618 curSignalGroup.setHeadOnState(curSignalHead, headStateFromBox(_OnAppearance)); 619 curSignalGroup.setHeadOffState(curSignalHead, headStateFromBox(_OffAppearance)); 620 // store AND/OR operand user choice 621 curSignalGroup.setSensorTurnoutOper(curSignalHead, operFromBox(_SensorTurnoutOper)); 622 // add control Sensors and a control Turnouts if entered in the window 623 finishUpdate(); 624 } 625 626 /** 627 * Clean up the interface and reset Included radio button to All for next use 628 */ 629 void finishUpdate() { 630 // show all turnouts and sensors if not yet set to All 631 cancelIncludedOnly(); 632 updateSubButton.setVisible(false); 633 addSubFrame.setVisible(false); 634 } 635 636 /** 637 * Cancel edit mode 638 */ 639 void cancelSubEdit() { 640 // get out of edit mode 641 curSignalGroup = null; 642 finishUpdate(); 643 } 644 645 /** 646 * Cancel included Turnouts and Sensors only option 647 */ 648 void cancelIncludedOnly() { 649 if (!showAll) { 650 allButton.doClick(); 651 } 652 } 653 654 /** 655 * Base table model for selecting Signal Group Head control Conditionals 656 */ 657 public abstract class SignalGroupOutputModel extends AbstractTableModel implements PropertyChangeListener { 658 659 @Override 660 public Class<?> getColumnClass(int c) { 661 if (c == INCLUDE_COLUMN) { 662 return Boolean.class; 663 } else { 664 return String.class; 665 } 666 } 667 668 @Override 669 public void propertyChange(java.beans.PropertyChangeEvent e) { 670 if (e.getPropertyName().equals("length")) { 671 // a new NamedBean is available in the manager 672 fireTableDataChanged(); 673 } 674 } 675 676 @Override 677 public String getColumnName(int c) { 678 return COLUMN_NAMES[c]; 679 } 680 681 @Override 682 public int getColumnCount() { 683 return 4; 684 } 685 686 @Override 687 public boolean isCellEditable(int r, int c) { 688 return ((c == INCLUDE_COLUMN) || (c == STATE_COLUMN)); 689 } 690 691 public static final int SNAME_COLUMN = 0; 692 public static final int UNAME_COLUMN = 1; 693 public static final int INCLUDE_COLUMN = 2; 694 public static final int STATE_COLUMN = 3; 695 696 public String getDisplayName(int r) { 697 if (((String) getValueAt(r, UNAME_COLUMN) != null) || (!((String) getValueAt(r, UNAME_COLUMN)).isEmpty())) { 698 return (String) getValueAt(r, UNAME_COLUMN); 699 } else { 700 return (String) getValueAt(r, SNAME_COLUMN); 701 } 702 } 703 704 } 705 706 /** 707 * Table model for selecting Turnouts and their On State 708 * as Conditionals for a Signal Group Signal Head member. 709 */ 710 class SignalGroupTurnoutModel extends SignalGroupOutputModel { 711 712 SignalGroupTurnoutModel() { 713 init(); 714 } 715 716 final void init(){ 717 InstanceManager.getDefault(TurnoutManager.class).addPropertyChangeListener(this); 718 } 719 720 public void dispose() { 721 InstanceManager.getDefault(TurnoutManager.class).removePropertyChangeListener(this); 722 } 723 724 @Override 725 public int getRowCount() { 726 return ( showAll ? _turnoutList.size() : _includedTurnoutList.size()); 727 } 728 729 @Override 730 public Object getValueAt(int r, int c) { 731 ArrayList<SignalGroupTurnout> turnoutList = ( showAll ? _turnoutList : _includedTurnoutList ); 732 // some error checking 733 if (r >= turnoutList.size()) { 734 log.debug("SGSTA getValueAt #703: row index is greater than turnout list size"); 735 return null; 736 } 737 switch (c) { 738 case INCLUDE_COLUMN: 739 return turnoutList.get(r).isIncluded(); 740 case SNAME_COLUMN: // slot number 741 return turnoutList.get(r).getSysName(); 742 case UNAME_COLUMN: // 743 return turnoutList.get(r).getUserName(); 744 case STATE_COLUMN: // 745 return turnoutList.get(r).getSetToState(); 746 default: 747 return null; 748 } 749 } 750 751 @Override 752 public void setValueAt(Object type, int r, int c) { 753 ArrayList<SignalGroupTurnout> turnoutList = ( showAll ? _turnoutList : _includedTurnoutList ); 754 switch (c) { 755 case INCLUDE_COLUMN: 756 turnoutList.get(r).setIncluded((Boolean) type); 757 break; 758 case STATE_COLUMN: 759 turnoutList.get(r).setSetToState((String) type); 760 break; 761 default: 762 break; 763 } 764 } 765 } 766 767 /** 768 * Set up a table for selecting Sensors and Sensor On State 769 * as Conditionals for a Signal Group Signal Head member. 770 */ 771 class SignalGroupSensorModel extends SignalGroupOutputModel { 772 773 SignalGroupSensorModel() { 774 init(); 775 } 776 777 final void init(){ 778 InstanceManager.getDefault(SensorManager.class).addPropertyChangeListener(this); 779 } 780 781 public void dispose(){ 782 InstanceManager.getDefault(SensorManager.class).removePropertyChangeListener(this); 783 } 784 785 @Override 786 public int getRowCount() { 787 return (showAll ? _sensorList.size() : _includedSensorList.size() ); 788 } 789 790 @Override 791 public Object getValueAt(int r, int c) { 792 ArrayList<SignalGroupSensor> sensorList = ( showAll ? _sensorList : _includedSensorList); 793 // some error checking 794 if (r >= sensorList.size()) { 795 log.debug("SGSTA getValueAt #766: row is greater than sensor list size"); 796 return null; 797 } 798 switch (c) { 799 case INCLUDE_COLUMN: 800 return sensorList.get(r).isIncluded(); 801 case SNAME_COLUMN: // slot number 802 return sensorList.get(r).getSysName(); 803 case UNAME_COLUMN: // 804 return sensorList.get(r).getUserName(); 805 case STATE_COLUMN: // 806 return sensorList.get(r).getSetToState(); 807 default: 808 return null; 809 } 810 } 811 812 @Override 813 public void setValueAt(Object type, int r, int c) { 814 ArrayList<SignalGroupSensor> sensorList = ( showAll ? _sensorList : _includedSensorList ); 815 switch (c) { 816 case INCLUDE_COLUMN: 817 sensorList.get(r).setIncluded(((Boolean) type)); 818 break; 819 case STATE_COLUMN: 820 sensorList.get(r).setSetToState((String) type); 821 break; 822 default: 823 break; 824 } 825 } 826 } 827 828 private boolean showAll = true; // false indicates: show only included Turnouts and Sensors 829 830 private static int ROW_HEIGHT; 831 832 private static String[] COLUMN_NAMES = {Bundle.getMessage("ColumnSystemName"), 833 Bundle.getMessage("ColumnUserName"), 834 Bundle.getMessage("Include"), 835 Bundle.getMessage("ColumnLabelSetState")}; 836 private static String SET_TO_ACTIVE = Bundle.getMessage("SensorStateActive"); 837 private static String SET_TO_INACTIVE = Bundle.getMessage("SensorStateInactive"); 838 private final static String SET_TO_CLOSED = InstanceManager.getDefault(TurnoutManager.class).getClosedText(); 839 private final static String SET_TO_THROWN = InstanceManager.getDefault(TurnoutManager.class).getThrownText(); 840 841 private static String[] sensorInputModes = new String[]{Bundle.getMessage("SensorStateActive"), Bundle.getMessage("SensorStateInactive")}; 842 private static int[] sensorInputModeValues = new int[]{SignalGroup.ONACTIVE, SignalGroup.ONINACTIVE}; 843 844 private static String[] signalStates = new String[]{Bundle.getMessage("SignalHeadStateDark"), Bundle.getMessage("SignalHeadStateRed"), Bundle.getMessage("SignalHeadStateYellow"), Bundle.getMessage("SignalHeadStateGreen"), Bundle.getMessage("SignalHeadStateLunar")}; 845 private static int[] signalStatesValues = new int[]{SignalHead.DARK, SignalHead.RED, SignalHead.YELLOW, SignalHead.GREEN, SignalHead.LUNAR}; 846 847 private static String[] turnoutInputModes = new String[]{SET_TO_CLOSED,SET_TO_THROWN}; 848 private static int[] turnoutInputModeValues = new int[]{SignalGroup.ONCLOSED, SignalGroup.ONTHROWN}; 849 850 private ArrayList<SignalGroupTurnout> _turnoutList; // array of all Turnouts 851 private ArrayList<SignalGroupTurnout> _includedTurnoutList; 852 853 private ArrayList<SignalGroupSensor> _sensorList; // array of all Sensors 854 private ArrayList<SignalGroupSensor> _includedSensorList; 855 856 private synchronized static void setRowHeight(int newVal) { 857 ROW_HEIGHT = newVal; 858 } 859 860 private abstract class SignalGroupElement { 861 862 String _sysName; 863 String _userName; 864 boolean _included; 865 int _setToState; 866 867 SignalGroupElement(String sysName, String userName) { 868 _sysName = sysName; 869 _userName = userName; 870 _included = false; 871 _setToState = Sensor.INACTIVE; 872 } 873 874 String getSysName() { 875 return _sysName; 876 } 877 878 String getUserName() { 879 return _userName; 880 } 881 882 boolean isIncluded() { 883 return _included; 884 } 885 886 void setIncluded(boolean include) { 887 _included = include; 888 } 889 890 abstract String getSetToState(); 891 892 abstract void setSetToState(String state); 893 894 int getState() { 895 return _setToState; 896 } 897 898 void setState(int state) { 899 _setToState = state; 900 } 901 902 } 903 904 /** 905 * Element containing Signal Group configuration information 906 * for a Control Sensor as Conditional. 907 */ 908 private class SignalGroupSensor extends SignalGroupElement { 909 910 /** 911 * Create a Sensor item for this Signal Head by the name of the Control Sensor 912 * @param sysName system name for new signal group sensor 913 * @param userName user name for new signal group sensor 914 */ 915 SignalGroupSensor(String sysName, String userName) { 916 super(sysName, userName); 917 } 918 919 /** 920 * Get the configured On state for the Control Sensor Conditional to be True. 921 * 922 * @return A string describing the On state for use in the GUI 923 * (read from a Properties file, localizable) 924 */ 925 @Override 926 String getSetToState() { 927 switch (_setToState) { 928 case Sensor.INACTIVE: 929 return SET_TO_INACTIVE; 930 case Sensor.ACTIVE: 931 return SET_TO_ACTIVE; 932 default: 933 // fall through 934 break; 935 } 936 return ""; 937 } 938 939 /** 940 * Store a uniform value for the On state of the Control Sensor Conditional. 941 * <p> 942 * Pairs should correspond with values in getSetToState() 943 * 944 * @param state Choice from the comboBox, localizable i.e. Active 945 */ 946 @Override 947 void setSetToState(String state) { 948 if (SET_TO_INACTIVE.equals(state)) { 949 _setToState = Sensor.INACTIVE; 950 } else if (SET_TO_ACTIVE.equals(state)) { 951 _setToState = Sensor.ACTIVE; 952 } 953 } 954 955 /** 956 * Get the Sensor object. 957 * 958 * @return The Sensor Bean acting as Control Sensor for this Head and Group 959 */ 960 Sensor getSensor() { 961 return InstanceManager.getDefault(SensorManager.class).getSensor(_sysName); 962 } 963 } 964 965 /** 966 * Element containing Signal Group configuration information 967 * for a Control Turnout as Conditional. 968 */ 969 private class SignalGroupTurnout extends SignalGroupElement { 970 971 /** 972 * Create a Turnout item for this Signal Head by the name of the Control Turnout. 973 * 974 * @param sysName system name for new signal group turnout 975 * @param userName user name for new signal group turnout 976 */ 977 SignalGroupTurnout(String sysName, String userName) { 978 super(sysName, userName); 979 } 980 981 /** 982 * Get the configured On state for the Control Turnout Conditional to be True. 983 * 984 * @return A string describing the On state for use in the GUI 985 */ 986 @Override 987 String getSetToState() { 988 switch (_setToState) { 989 case Turnout.CLOSED: 990 return SET_TO_CLOSED; 991 case Turnout.THROWN: 992 return SET_TO_THROWN; 993 default: 994 // fall through 995 break; 996 } 997 return ""; 998 } 999 1000 /** 1001 * Store a uniform value for the On state of the Control Sensor Conditional. 1002 * Pairs should correspond with values in getSetToState(). 1003 * 1004 * @param state Choice from the comboBox, localizable i.e. Thrown. 1005 */ 1006 @Override 1007 void setSetToState(String state) { 1008 if (SET_TO_CLOSED.equals(state)) { 1009 _setToState = Turnout.CLOSED; 1010 } else if (SET_TO_THROWN.equals(state)) { 1011 _setToState = Turnout.THROWN; 1012 } 1013 } 1014 1015 /** 1016 * Get the Turnout object. 1017 * 1018 * @return The Turnout Bean acting as Control Turnout for this Head and Group 1019 */ 1020 Turnout getTurnout() { 1021 return InstanceManager.getDefault(TurnoutManager.class).getTurnout(_sysName); 1022 } 1023 } 1024 1025 private final static Logger log = LoggerFactory.getLogger(SignalGroupSubTableAction.class); 1026 1027}