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