001package jmri.jmrit.entryexit; 002 003import java.awt.Color; 004import java.awt.Container; 005import java.awt.GridLayout; 006import java.awt.event.ActionEvent; 007import java.awt.event.ActionListener; 008import java.beans.PropertyChangeEvent; 009import java.beans.PropertyChangeListener; 010import java.util.ArrayList; 011import java.util.List; 012import java.util.SortedSet; 013import java.util.TreeSet; 014 015import javax.swing.*; 016import javax.swing.table.TableCellEditor; 017import javax.swing.table.TableColumn; 018import javax.swing.table.TableRowSorter; 019 020import jmri.InstanceManager; 021import jmri.NamedBean; 022import jmri.jmrit.display.EditorManager; 023import jmri.jmrit.display.layoutEditor.LayoutBlockManager; 024import jmri.jmrit.display.layoutEditor.LayoutEditor; 025import jmri.jmrit.display.layoutEditor.LayoutSlip; 026import jmri.jmrit.display.layoutEditor.LayoutTurnout; 027import jmri.jmrit.display.layoutEditor.LevelXing; 028import jmri.jmrit.display.layoutEditor.PositionablePoint; 029import jmri.swing.NamedBeanComboBox; 030import jmri.util.JmriJFrame; 031import jmri.util.swing.*; 032import jmri.util.table.ButtonEditor; 033import jmri.util.table.ButtonRenderer; 034 035/** 036 * JPanel to create a new EntryExitPair. 037 * 038 * @author Bob Jacobsen Copyright (C) 2009 039 */ 040public class AddEntryExitPairPanel extends jmri.util.swing.JmriPanel { 041 042 JComboBox<String> selectPanel = new JComboBox<>(); 043 JComboBox<String> fromPoint = new JComboBox<>(); 044 JComboBox<String> toPoint = new JComboBox<>(); 045 046 String[] interlockTypes = {Bundle.getMessage("SetTurnoutsOnly"), Bundle.getMessage("SetTurnoutsAndSignalMasts"), Bundle.getMessage("FullInterlock")}; // NOI18N 047 JComboBox<String> typeBox = new JComboBox<>(interlockTypes); 048 049 List<LayoutEditor> panels; 050 051 EntryExitPairs nxPairs = jmri.InstanceManager.getDefault(jmri.jmrit.entryexit.EntryExitPairs.class); 052 053 // signalling.EntryExitBundle via Bundle method 054 055 public AddEntryExitPairPanel(LayoutEditor panel) { 056 057 setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); 058 // combo boxes selection panel 059 JPanel top = new JPanel(); 060 top.setBorder(BorderFactory.createEtchedBorder()); 061 top.setLayout(new GridLayout(4, 2)); 062 top.add(new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("SelectPanel")), SwingConstants.RIGHT)); // NOI18N 063 top.add(selectPanel); 064 selectPanel.removeAllItems(); 065 panels = InstanceManager.getDefault(EditorManager.class).getList(LayoutEditor.class); 066 for (int i = 0; i < panels.size(); i++) { 067 selectPanel.addItem(panels.get(i).getLayoutName()); 068 } 069 if (panel != null) { 070 selectPanel.setSelectedItem(panel.getLayoutName()); 071 } 072 073 top.add(new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("FromLocation")), SwingConstants.RIGHT)); // NOI18N 074 top.add(fromPoint); 075 ActionListener selectPanelListener = (ActionEvent e) -> { 076 selectPointsFromPanel(); 077 nxModel.setPanel(panels.get(selectPanel.getSelectedIndex())); 078 }; 079 selectPointsFromPanel(); 080 selectPanel.addActionListener(selectPanelListener); 081 082 top.add(new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("ToLocation")), SwingConstants.RIGHT)); // NOI18N 083 top.add(toPoint); 084 085 JComboBoxUtil.setupComboBoxMaxRows(fromPoint); 086 JComboBoxUtil.setupComboBoxMaxRows(toPoint); 087 088 top.add(new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("NXType")), SwingConstants.RIGHT)); // NOI18N 089 top.add(typeBox); 090 add(top); 091 092 // button panel 093 JPanel p = new JPanel(); 094 JButton ok = new JButton(Bundle.getMessage("AddPair")); // NOI18N 095 p.add(ok); 096 ok.addActionListener((ActionEvent e) -> { 097 addButton(); 098 }); 099 100 p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS)); 101 JButton auto; 102 p.add(auto = new JButton(Bundle.getMessage("AutoGenerate"))); // NOI18N 103 auto.addActionListener((ActionEvent e) -> { 104 autoDiscovery(); 105 }); 106 p.add(auto); 107 add(p); 108 nxModel = new TableModel(panel); 109 nxDataTable = new JTable(nxModel); 110 nxDataTable.setRowSorter(new TableRowSorter<>(nxModel)); 111 nxDataScroll = new JScrollPane(nxDataTable); 112 nxModel.configureTable(nxDataTable); 113 java.awt.Dimension dataTableSize = nxDataTable.getPreferredSize(); 114 // width is right, but if table is empty, it's not high 115 // enough to reserve much space. 116 dataTableSize.height = Math.max(dataTableSize.height, 400); 117 nxDataScroll.getViewport().setPreferredSize(dataTableSize); 118 add(nxDataScroll); 119 } 120 121 LayoutEditor panel; 122 123 private void addButton() { 124 ValidPoints from = getValidPointFromCombo(fromPoint); 125 ValidPoints to = getValidPointFromCombo(toPoint); 126 if (from == null || to == null) { 127 return; 128 } 129 130 nxPairs.addNXDestination(from.getPoint(), to.getPoint(), panel); 131 nxPairs.setEntryExitType(from.getPoint(), panel, to.getPoint(), typeBox.getSelectedIndex()); 132 133 validateManualPair(from.getPoint(), to.getPoint()); 134 } 135 136 /** 137 * Verify that a route can be set between the source and destination points. 138 * @parm fromPoint The source bean, normally a sensor. 139 * @parm toPoint The destination bean, normally a sensor. 140 */ 141 private void validateManualPair(NamedBean fromPoint, NamedBean toPoint) { 142 var fromDetail = nxPairs.getPointDetails(fromPoint, panel); 143 var toDetail = nxPairs.getPointDetails(toPoint, panel); 144 if (fromDetail == null || toDetail == null) { 145 log.debug("validateManualPair: a point detail was null, skip checks"); 146 return; 147 } 148 149 // Get the facing and protected blocks for each point. 150 var fromFace = fromDetail.getFacing(); 151 var fromProtect = fromDetail.getProtecting(); 152 var toFace = toDetail.getFacing(); 153 var toProtect = toDetail.getProtecting(); 154 if (fromFace == null || fromProtect.isEmpty() || toFace == null || toProtect.isEmpty()) { 155 log.debug("validateManualPair: a facing block or a protected block was not found, skip checks"); 156 return; 157 } 158 159 log.debug("validateManualPair: from: {} :: {}", fromFace.getDisplayName(), fromProtect.get(0).getDisplayName()); 160 log.debug("validateManualPair: to: {} :: {}", toFace.getDisplayName(), toProtect.get(0).getDisplayName()); 161 162 var lbm = InstanceManager.getDefault(LayoutBlockManager.class); 163 var lbc = lbm.getLayoutBlockConnectivityTools(); 164 165 try { 166 if (!lbc.checkValidDest( 167 fromFace, fromProtect.get(0), toFace, toProtect, 168 jmri.jmrit.display.layoutEditor.LayoutBlockConnectivityTools.Routing.SENSORTOSENSOR)) { 169 log.debug("validateManualPair: checkValidDest returned false"); 170 validateDialog(fromPoint, toPoint); 171 } 172 } catch(jmri.JmriException ex) { 173 log.debug("validateManualPair: JmriException: {}", ex.getMessage()); 174 validateDialog(fromPoint, toPoint); 175 } 176 } 177 178 /** 179 * Display the validation failed dialog. Provide an option to delete the bad NX pair that was just added. 180 * @parm fromPoint The source bean, normally a sensor. 181 * @parm toPoint The destination bean, normally a sensor. 182 */ 183 private void validateDialog(NamedBean fromPoint, NamedBean toPoint) { 184 int reply = JmriJOptionPane.showConfirmDialog(this, Bundle.getMessage("ValidateWarning"), Bundle.getMessage("ValidateTitle"), // NOI18N 185 JmriJOptionPane.YES_NO_OPTION, 186 JmriJOptionPane.WARNING_MESSAGE); 187 if (reply == JmriJOptionPane.YES_OPTION) { 188 nxPairs.deleteNxPair(fromPoint, toPoint, panel); 189 } 190 } 191 192 jmri.util.JmriJFrame entryExitFrame = null; 193 JLabel sourceLabel = new JLabel(); 194 195 private void autoDiscovery() { 196 if (!InstanceManager.getDefault(jmri.jmrit.display.layoutEditor.LayoutBlockManager.class).isAdvancedRoutingEnabled()) { 197 int response = JmriJOptionPane.showConfirmDialog(this, Bundle.getMessage("EnableLayoutBlockRouting"), Bundle.getMessage("EnableLayoutBlockRouting"), JmriJOptionPane.YES_NO_OPTION); 198 if ( response == JmriJOptionPane.YES_OPTION ) { 199 InstanceManager.getDefault(jmri.jmrit.display.layoutEditor.LayoutBlockManager.class).enableAdvancedRouting(true); 200 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("LayoutBlockRoutingEnabled")); // NOI18N 201 } 202 } 203 entryExitFrame = new jmri.util.JmriJFrame(Bundle.getMessage("DiscoverEntryExitPairs"), false, false); // NOI18N 204 entryExitFrame.setPreferredSize(null); 205 JPanel panel1 = new JPanel(); 206 sourceLabel = new JLabel(Bundle.getMessage("DiscoveringEntryExitPairs")); // NOI18N 207 /*ImageIcon i; 208 i = new ImageIcon(FileUtil.findURL("resources/icons/misc/gui3/process-working.gif")); 209 JLabel label = new JLabel(); 210 label.setIcon(i); 211 panel1.add(label);*/ 212 panel1.add(sourceLabel); 213 214 entryExitFrame.add(panel1); 215 entryExitFrame.pack(); 216 entryExitFrame.setVisible(true); 217 int retval = JmriJOptionPane.showOptionDialog(this, Bundle.getMessage("AutoGenEntryExitMessage"), Bundle.getMessage("AutoGenEntryExitTitle"), // NOI18N 218 JmriJOptionPane.YES_NO_OPTION, 219 JmriJOptionPane.QUESTION_MESSAGE, null, null, null); 220 if (retval == JmriJOptionPane.YES_OPTION ) { 221 final PropertyChangeListener propertyNXListener = new PropertyChangeListener() { 222 @Override 223 public void propertyChange(PropertyChangeEvent evt) { 224 if (evt.getPropertyName().equals("autoGenerateComplete")) { // NOI18N 225 if (entryExitFrame != null) { 226 entryExitFrame.setVisible(false); 227 entryExitFrame.dispose(); 228 } 229 nxPairs.removePropertyChangeListener(this); 230 JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("AutoGenComplete")); // NOI18N 231 } 232 } 233 }; 234 try { 235 nxPairs.addPropertyChangeListener(propertyNXListener); 236 nxPairs.automaticallyDiscoverEntryExitPairs(panels.get(selectPanel.getSelectedIndex()), typeBox.getSelectedIndex()); 237 } catch (jmri.JmriException e) { 238 nxPairs.removePropertyChangeListener(propertyNXListener); 239 JmriJOptionPane.showMessageDialog(this, e.toString()); 240 entryExitFrame.setVisible(false); 241 } 242 } else { 243 entryExitFrame.setVisible(false); 244 } 245 } 246 247 ValidPoints getValidPointFromCombo(JComboBox<String> box) { 248 String item = (String) box.getSelectedItem(); 249 for (int i = 0; i < validPoints.size(); i++) { 250 if (validPoints.get(i).getDescription().equals(item)) { 251 return validPoints.get(i); 252 } 253 } 254 return null; 255 } 256 257 List<ValidPoints> validPoints = new ArrayList<>(); 258 boolean doFromCombo; 259 SortedSet<String> fromSet = new TreeSet<>(); 260 SortedSet<String> toSet = new TreeSet<>(); 261 262 private void selectPointsFromPanel() { 263 if (selectPanel.getSelectedIndex() == -1) { 264 return; 265 } 266 if (panel == panels.get(selectPanel.getSelectedIndex())) { 267 return; 268 } 269 panel = panels.get(selectPanel.getSelectedIndex()); 270 fromSet.clear(); 271 toSet.clear(); 272 doFromCombo = true; 273 selectPoints(panel); 274 275 // Do other panels if any 276 doFromCombo = false; 277 panels = InstanceManager.getDefault(EditorManager.class).getList(LayoutEditor.class); 278 for (int i = 0; i < panels.size(); i++) { 279 if (panels.get(i) != panel) { 280 selectPoints(panels.get(i)); 281 } 282 } 283 284 // Update the combo boxes 285 fromPoint.removeAllItems(); 286 fromSet.forEach((ent) -> { 287 fromPoint.addItem(ent); 288 }); 289 toPoint.removeAllItems(); 290 toSet.forEach((ent) -> { 291 toPoint.addItem(ent); 292 }); 293 } 294 295 private void selectPoints(LayoutEditor panel) { 296 for (PositionablePoint pp : panel.getPositionablePoints()) { 297 addPointToCombo(pp.getWestBoundSignalMastName(), pp.getWestBoundSensorName()); 298 addPointToCombo(pp.getEastBoundSignalMastName(), pp.getEastBoundSensorName()); 299 } 300 301 for (LayoutTurnout t : panel.getLayoutTurnouts()) { 302 addPointToCombo(t.getSignalAMastName(), t.getSensorAName()); 303 addPointToCombo(t.getSignalBMastName(), t.getSensorBName()); 304 addPointToCombo(t.getSignalCMastName(), t.getSensorCName()); 305 addPointToCombo(t.getSignalDMastName(), t.getSensorDName()); 306 } 307 308 for (LevelXing xing : panel.getLevelXings()) { 309 addPointToCombo(xing.getSignalAMastName(), xing.getSensorAName()); 310 addPointToCombo(xing.getSignalBMastName(), xing.getSensorBName()); 311 addPointToCombo(xing.getSignalCMastName(), xing.getSensorCName()); 312 addPointToCombo(xing.getSignalDMastName(), xing.getSensorDName()); 313 } 314 315 for (LayoutSlip slip : panel.getLayoutSlips()) { 316 addPointToCombo(slip.getSignalAMastName(), slip.getSensorAName()); 317 addPointToCombo(slip.getSignalBMastName(), slip.getSensorBName()); 318 addPointToCombo(slip.getSignalCMastName(), slip.getSensorCName()); 319 addPointToCombo(slip.getSignalDMastName(), slip.getSensorDName()); 320 } 321 } 322 323 void addPointToCombo(String signalMastName, String sensorName) { 324 if (sensorName != null && !sensorName.isEmpty()) { 325 String description = sensorName; 326 NamedBean source = InstanceManager.sensorManagerInstance().getSensor(sensorName); 327 if (signalMastName != null && !signalMastName.isEmpty()) { 328 description = sensorName + " (" + signalMastName + ")"; 329 } 330 validPoints.add(new ValidPoints(source, description)); 331 if (doFromCombo) { 332 fromSet.add(description); 333 } 334 toSet.add(description); 335 } 336 } 337 338 JTable nxDataTable; 339 JScrollPane nxDataScroll; 340 341 TableModel nxModel; 342 343 static final int FROMPOINTCOL = 0; 344 static final int TOPOINTCOL = 1; 345 static final int ACTIVECOL = 2; 346 static final int CLEARCOL = 3; 347 static final int BOTHWAYCOL = 4; 348 static final int DELETECOL = 5; 349 static final int TYPECOL = 6; 350 static final int ENABLEDCOL = 7; 351 352 static final int NUMCOL = ENABLEDCOL + 1; 353 354 class TableModel extends javax.swing.table.AbstractTableModel implements PropertyChangeListener { 355 356 //needs a method to for when panel changes 357 //need a method to delete an item 358 //Possibly also to set a route. 359 // Since 4.17.4 has added functionality a property change listener to catch when paths go active. 360 TableModel(LayoutEditor panel) { 361 setPanel(panel); 362 nxPairs.addPropertyChangeListener(this); 363 source = nxPairs.getNxSource(panel); 364 dest = nxPairs.getNxDestination(); 365 } 366 367 void setPanel(LayoutEditor panel) { 368 if (this.panel == panel) { 369 return; 370 } 371 this.panel = panel; 372 rowCount = nxPairs.getNxPairNumbers(panel); 373 updateNameList(); 374 fireTableDataChanged(); 375 } 376 377 LayoutEditor panel; 378 379 List<Object> source = null; 380 List<Object> dest = null; 381 382 void updateNameList() { 383 source = nxPairs.getNxSource(panel); 384 dest = nxPairs.getNxDestination(); 385 } 386 387 int rowCount = 0; 388 389 @Override 390 public int getRowCount() { 391 return rowCount; 392 } 393 394 public void configureTable(JTable table) { 395 // allow reordering of the columns 396 table.getTableHeader().setReorderingAllowed(true); 397 398 // have to shut off autoResizeMode to get horizontal scroll to work (JavaSwing p 541) 399 table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); 400 401 // resize columns as requested 402 for (int i = 0; i < table.getColumnCount(); i++) { 403 int width = getPreferredWidth(i); 404 table.getColumnModel().getColumn(i).setPreferredWidth(width); 405 } 406 table.sizeColumnsToFit(-1); 407 408 configDeleteColumn(table); 409 410 } 411 412 @Override 413 public Object getValueAt(int row, int col) { 414 // get roster entry for row 415 if (panel == null) { 416 log.debug("no panel selected!"); // NOI18N 417 return Bundle.getMessage("ErrorTitle"); // NOI18N 418 } 419 switch (col) { 420 case FROMPOINTCOL: 421 return nxPairs.getPointAsString((NamedBean) source.get(row), panel); 422 case TOPOINTCOL: 423 return nxPairs.getPointAsString((NamedBean) dest.get(row), panel); 424 case ACTIVECOL: 425 return isPairActive(row); 426 case BOTHWAYCOL: 427 return !nxPairs.isUniDirection(source.get(row), panel, dest.get(row)); 428 case ENABLEDCOL: 429 return !nxPairs.isEnabled(source.get(row), panel, dest.get(row)); 430 case CLEARCOL: 431 return Bundle.getMessage("ButtonClear"); // NOI18N 432 case DELETECOL: 433 return Bundle.getMessage("ButtonDelete"); // NOI18N 434 case TYPECOL: 435 return NXTYPE_NAMES[nxPairs.getEntryExitType(source.get(row), panel, dest.get(row))]; 436 default: 437 return ""; 438 } 439 } 440 441 @Override 442 public void setValueAt(Object value, int row, int col) { 443 if (col == DELETECOL) { 444 // button fired, delete Bean 445 deleteEntryExit(row, col); 446 } 447 if (col == CLEARCOL) { 448 nxPairs.cancelInterlock(source.get(row), panel, dest.get(row)); 449 } 450 if (col == BOTHWAYCOL) { 451 boolean b = !((Boolean) value); 452 nxPairs.setUniDirection(source.get(row), panel, dest.get(row), b); 453 } 454 if (col == ENABLEDCOL) { 455 boolean b = !((Boolean) value); 456 nxPairs.setEnabled(source.get(row), panel, dest.get(row), b); 457 } 458 if (col == TYPECOL) { 459 String val = (String) value; 460 if (val.equals(Bundle.getMessage("SetTurnoutsOnly"))) { // I18N matching needs if-else // NOI18N 461 nxPairs.setEntryExitType(source.get(row), panel, dest.get(row), 0x00); 462 } else if (val.equals(Bundle.getMessage("SetTurnoutsAndSignalMasts"))) { // NOI18N 463 nxPairs.setEntryExitType(source.get(row), panel, dest.get(row), 0x01); 464 } else if (val.equals(Bundle.getMessage("FullInterlock"))) { 465 nxPairs.setEntryExitType(source.get(row), panel, dest.get(row), 0x02); // NOI18N 466 } 467 } 468 } 469 470 public int getPreferredWidth(int col) { 471 switch (col) { 472 case FROMPOINTCOL: 473 case TOPOINTCOL: 474 return new JTextField(15).getPreferredSize().width; 475 case ACTIVECOL: 476 case BOTHWAYCOL: 477 case ENABLEDCOL: 478 return new JTextField(5).getPreferredSize().width; 479 case CLEARCOL: 480 case DELETECOL: // 481 return new JTextField(22).getPreferredSize().width; 482 case TYPECOL: 483 return new JTextField(10).getPreferredSize().width; 484 default: 485 log.warn("Unexpected column in getPreferredWidth: {}", col); // NOI18N 486 return new JTextField(8).getPreferredSize().width; 487 } 488 } 489 490 protected void deleteEntryExit(int row, int col) { 491 NamedBean nbSource = ((NamedBean) source.get(row)); 492 NamedBean nbDest = (NamedBean) dest.get(row); 493 nxPairs.deleteNxPair(nbSource, nbDest, panel); 494 } 495 496 String isPairActive(int row) { 497 if (nxPairs.isPathActive(source.get(row), dest.get(row), panel)) { 498 return (Bundle.getMessage("ButtonYes")); // "Yes" // NOI18N 499 } 500 return (""); 501 } 502 503 @Override 504 public String getColumnName(int col) { 505 switch (col) { 506 case FROMPOINTCOL: 507 return Bundle.getMessage("ColumnFrom"); // NOI18N 508 case TOPOINTCOL: 509 return Bundle.getMessage("ColumnTo"); // NOI18N 510 case ACTIVECOL: 511 return Bundle.getMessage("SensorStateActive"); // "Active" // NOI18N 512 case DELETECOL: 513 return ""; 514 case CLEARCOL: 515 return ""; 516 case BOTHWAYCOL: 517 return Bundle.getMessage("ColumnBoth"); // NOI18N 518 case TYPECOL: 519 return Bundle.getMessage("NXType"); // NOI18N 520 case ENABLEDCOL: 521 return Bundle.getMessage("Disabled"); // NOI18N 522 default: 523 return "<UNKNOWN>"; // NOI18N 524 } 525 } 526 527 @Override 528 public Class<?> getColumnClass(int col) { 529 switch (col) { 530 case FROMPOINTCOL: 531 case TOPOINTCOL: 532 case ACTIVECOL: 533 return String.class; 534 case DELETECOL: 535 case CLEARCOL: 536 return JButton.class; 537 case BOTHWAYCOL: 538 case ENABLEDCOL: 539 return Boolean.class; 540 case TYPECOL: 541 return String.class; 542 default: 543 return null; 544 } 545 } 546 547 @Override 548 public boolean isCellEditable(int row, int col) { 549 switch (col) { 550 case BOTHWAYCOL: 551 Object obj = nxPairs.getEndPointLocation((NamedBean) dest.get(row), panel); 552 if (obj instanceof PositionablePoint) { 553 PositionablePoint point = (PositionablePoint) obj; 554 if (point.getType() == PositionablePoint.PointType.END_BUMPER) { 555 JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("EndBumperPoint")); // NOI18N 556 return false; 557 } 558 } 559 if (!nxPairs.canBeBiDirectional(source.get(row), panel, dest.get(row))) { 560 JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("BothWayTurnoutOnly")); // NOI18N 561 return false; 562 } 563 /*if(nxPairs.getEntryExitType(source.get(row), panel, dest.get(row))!=0x00){ 564 JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("BothWayTurnoutOnly")); 565 return false; 566 }*/ 567 return true; 568 case DELETECOL: 569 case CLEARCOL: 570 case ENABLEDCOL: 571 case TYPECOL: 572 return true; 573 default: 574 return false; 575 } 576 } 577 578 @Override 579 public int getColumnCount() { 580 return NUMCOL; 581 } 582 583 @Override 584 public void propertyChange(java.beans.PropertyChangeEvent e) { 585 if (e.getPropertyName().equals("length") || e.getPropertyName().equals("active")) { // NOI18N 586 rowCount = nxPairs.getNxPairNumbers(panel); 587 updateNameList(); 588 fireTableDataChanged(); 589 } 590 } 591 } 592 593 String[] NXTYPE_NAMES = {Bundle.getMessage("SetTurnoutsOnly"), Bundle.getMessage("SetTurnoutsAndSignalMasts"), Bundle.getMessage("FullInterlock")}; // NOI18N 594 // Picked up in setValueAt() to read back from table 595 596 protected void configDeleteColumn(JTable table) { 597 // have the delete column hold a button 598 setColumnToHoldButton(table, DELETECOL, 599 new JButton(Bundle.getMessage("ButtonDelete"))); // NOI18N 600 601 setColumnToHoldButton(table, CLEARCOL, 602 new JButton(Bundle.getMessage("ButtonClear"))); // NOI18N 603 604 JComboBox<String> typeCombo = new JComboBox<>(NXTYPE_NAMES); 605 606 TableColumn col = table.getColumnModel().getColumn(TYPECOL); 607 col.setCellEditor(new DefaultCellEditor(typeCombo)); 608 } 609 610 /** 611 * Service method to set up a column so that it will hold a button for its 612 * values. 613 * 614 * @param table the table 615 * @param column the column 616 * @param sample Typical button, used for size 617 */ 618 protected void setColumnToHoldButton(JTable table, int column, JButton sample) { 619 //TableColumnModel tcm = table.getColumnModel(); 620 // install a button renderer & editor 621 ButtonRenderer buttonRenderer = new ButtonRenderer(); 622 table.setDefaultRenderer(JButton.class, buttonRenderer); 623 TableCellEditor buttonEditor = new ButtonEditor(new JButton()); 624 table.setDefaultEditor(JButton.class, buttonEditor); 625 // ensure the table rows, columns have enough room for buttons 626 table.setRowHeight(sample.getPreferredSize().height); 627 table.getColumnModel().getColumn(column) 628 .setPreferredWidth((sample.getPreferredSize().width) + 4); 629 } 630 631 static class ValidPoints { 632 633 NamedBean bean; 634 String description; 635 636 ValidPoints(NamedBean bean, String description) { 637 this.bean = bean; 638 this.description = description; 639 } 640 641 NamedBean getPoint() { 642 return bean; 643 } 644 645 String getDescription() { 646 return description; 647 } 648 } 649 650 // Variables for the Options menu item 651 JmriJFrame optionsFrame = null; 652 Container optionsPane = null; 653 654 String[] clearOptions = { 655 Bundle.getMessage("PromptUser"), // NOI18N 656 Bundle.getMessage("ClearRoute"), // NOI18N 657 Bundle.getMessage("CancelRoute"), // NOI18N 658 Bundle.getMessage("StackRoute")}; // NOI18N 659 JComboBox<String> clearEntry = new JComboBox<>(clearOptions); 660 661 String[] overlapOptions = { 662 Bundle.getMessage("PromptUser"), // NOI18N 663 Bundle.getMessage("CancelRoute"), // NOI18N 664 Bundle.getMessage("StackRoute")}; // NOI18N 665 JComboBox<String> overlapEntry = new JComboBox<>(overlapOptions); 666 667 NamedBeanComboBox<jmri.Memory> memoryComboBox = new NamedBeanComboBox<>( 668 InstanceManager.getDefault(jmri.MemoryManager.class)); 669 JSpinner memoryClearDelay = new JSpinner(new SpinnerNumberModel(0, 0, 99, 1)); 670 671 JTextField durationSetting = new JTextField(10); 672 String[] colorText = {"ColorClear", "Black", "DarkGray", "Gray", // NOI18N 673 "LightGray", "White", "Red", "Pink", "Orange", // NOI18N 674 "Yellow", "Green", "Blue", "Magenta", "Cyan"}; // NOI18N 675 Color[] colorCode = {null, Color.black, Color.darkGray, Color.gray, 676 Color.lightGray, Color.white, Color.red, Color.pink, Color.orange, 677 Color.yellow, Color.green, Color.blue, Color.magenta, Color.cyan}; 678 int numColors = 14; // number of entries in the above arrays 679 680 JCheckBox useAbsSignalMode = new JCheckBox(Bundle.getMessage("UseAbsSignalMode")); // NOI18N 681 JCheckBox dispatcherUse = new JCheckBox(Bundle.getMessage("DispatcherInt")); // NOI18N 682 683 JComboBox<String> settingTrackColorBox = new JComboBox<>(); 684 685 private void initializeColorCombo(JComboBox<String> colorCombo) { 686 colorCombo.removeAllItems(); 687 for (int i = 0; i < numColors; i++) { 688 colorCombo.addItem(Bundle.getMessage(colorText[i])); // I18N using Bundle.getMessage from higher level color list 689 } 690 } 691 692 private void setColorCombo(JComboBox<String> colorCombo, Color color) { 693 for (int i = 0; i < numColors; i++) { 694 if (color == colorCode[i]) { 695 colorCombo.setSelectedIndex(i); 696 return; 697 } 698 } 699 } 700 701 private Color getSelectedColor(JComboBox<String> colorCombo) { 702 return (colorCode[colorCombo.getSelectedIndex()]); 703 } 704 705 /** 706 * Build the Options window 707 * @param e the action event 708 */ 709 protected void optionWindow(ActionEvent e) { 710 if (optionsFrame == null) { 711 optionsFrame = new JmriJFrame(Bundle.getMessage("OptionsTitle"), false, true); // NOI18N 712 //optionsFrame.addHelpMenu("package.jmri.jmrit.dispatcher.Options", true); 713 optionsPane = optionsFrame.getContentPane(); 714 optionsPane.setLayout(new BoxLayout(optionsFrame.getContentPane(), BoxLayout.Y_AXIS)); 715 716 clearEntry.setSelectedIndex(nxPairs.getClearDownOption()); 717 JPanel p1 = new JPanel(); 718 //clearEntry.addActionListener(clearEntryListener); 719 clearEntry.setToolTipText(Bundle.getMessage("ReselectionTip")); // NOI18N 720 p1.add(new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("Reselection")))); // NOI18N 721 p1.add(clearEntry); 722 optionsPane.add(p1); 723 724 overlapEntry.setSelectedIndex(nxPairs.getOverlapOption()); 725 JPanel p1a = new JPanel(); 726 overlapEntry.setToolTipText(Bundle.getMessage("OverlapSelectedTip")); // NOI18N 727 p1a.add(new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("OverlapSelected")))); // NOI18N 728 p1a.add(overlapEntry); 729 optionsPane.add(p1a); 730 731 LayoutEditor.setupComboBox(memoryComboBox, false, true, false); 732 memoryComboBox.setSelectedItemByName(nxPairs.getMemoryOption()); 733 JPanel p1b = new JPanel(); 734 memoryComboBox.setToolTipText(Bundle.getMessage("MemoryOptionTip")); // NOI18N 735 p1b.add(new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("MemoryOption")))); // NOI18N 736 p1b.add(memoryComboBox); 737 optionsPane.add(p1b); 738 739 memoryClearDelay.setValue(nxPairs.getMemoryClearDelay()); 740 JPanel p1c = new JPanel(); 741 memoryClearDelay.setToolTipText(Bundle.getMessage("MemoryClearTip")); // NOI18N 742 p1c.add(new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("MemoryClear")))); // NOI18N 743 p1c.add(memoryClearDelay); 744 optionsPane.add(p1c); 745 746 JPanel p2 = new JPanel(); 747 initializeColorCombo(settingTrackColorBox); 748 setColorCombo(settingTrackColorBox, nxPairs.getSettingRouteColor()); 749 ActionListener settingTrackColorListener = (ActionEvent e1) -> { 750 if (getSelectedColor(settingTrackColorBox) != null) { 751 durationSetting.setEnabled(true); 752 } else { 753 durationSetting.setEnabled(false); 754 } 755 }; 756 757 settingTrackColorBox.addActionListener(settingTrackColorListener); 758 p2.add(new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("RouteSetColour")))); // NOI18N 759 p2.add(settingTrackColorBox); 760 optionsPane.add(p2); 761 durationSetting.setText("" + nxPairs.getSettingTimer()); 762 if (nxPairs.useDifferentColorWhenSetting()) { 763 durationSetting.setEnabled(true); 764 } else { 765 durationSetting.setEnabled(false); 766 } 767 JPanel p3 = new JPanel(); 768 p3.add(new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("SettingDuration")))); // NOI18N 769 p3.add(durationSetting); 770 optionsPane.add(p3); 771 772 JPanel p4 = new JPanel(); 773 p4.add(useAbsSignalMode); 774 useAbsSignalMode.setSelected(nxPairs.isAbsSignalMode()); 775 optionsPane.add(p4); 776 777 JPanel p5 = new JPanel(); 778 p5.add(dispatcherUse); 779 dispatcherUse.setSelected(nxPairs.getDispatcherIntegration()); 780 optionsPane.add(p5); 781 782 JButton ok = new JButton(Bundle.getMessage("ButtonOK")); // NOI18N 783 optionsPane.add(ok); 784 ok.addActionListener((ActionEvent e1) -> { 785 optionSaveButton(); 786 }); 787 } 788 optionsFrame.pack(); 789 optionsFrame.setVisible(true); 790 } 791 792 /** 793 * Save the option updates 794 */ 795 void optionSaveButton() { 796 int settingTimer = 2000; // in milliseconds 797 try { 798 settingTimer = Integer.parseInt(durationSetting.getText()); 799 } catch (Exception e) { 800 JmriJOptionPane.showMessageDialog(durationSetting, Bundle.getMessage("ValueBeNumber")); // NOI18N 801 return; 802 } 803 nxPairs.setSettingTimer(settingTimer); 804 nxPairs.setSettingRouteColor(getSelectedColor(settingTrackColorBox)); 805 nxPairs.setClearDownOption(clearEntry.getSelectedIndex()); 806 nxPairs.setOverlapOption(overlapEntry.getSelectedIndex()); 807 nxPairs.setMemoryClearDelay((int) memoryClearDelay.getValue()); 808 nxPairs.setAbsSignalMode(useAbsSignalMode.isSelected()); 809 nxPairs.setDispatcherIntegration(dispatcherUse.isSelected()); 810 811 String memoryName = memoryComboBox.getSelectedItemDisplayName(); 812 nxPairs.setMemoryOption((memoryName == null) ? "" : memoryName); 813 814 optionsFrame.setVisible(false); 815 816 } 817 818 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AddEntryExitPairPanel.class); 819 820}