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