001package jmri.jmrit.logix; 002 003import java.awt.BorderLayout; 004import java.awt.Color; 005import java.awt.Component; 006import java.awt.Dimension; 007import java.awt.event.ActionEvent; 008import java.awt.event.ActionListener; 009import java.awt.event.FocusEvent; 010import java.awt.event.FocusListener; 011import java.awt.event.MouseEvent; 012import java.beans.PropertyChangeListener; 013import java.util.ArrayList; 014import java.util.List; 015import java.util.Map; 016 017import javax.annotation.CheckForNull; 018import javax.annotation.Nonnull; 019 020import javax.swing.AbstractButton; 021import javax.swing.BorderFactory; 022import javax.swing.Box; 023import javax.swing.BoxLayout; 024import javax.swing.ButtonGroup; 025import javax.swing.JButton; 026import javax.swing.JComboBox; 027import javax.swing.JComponent; 028import javax.swing.JDialog; 029import javax.swing.JFrame; 030import javax.swing.JLabel; 031import javax.swing.JPanel; 032import javax.swing.JRadioButton; 033import javax.swing.JScrollPane; 034import javax.swing.JTable; 035import javax.swing.JTextField; 036import javax.swing.table.AbstractTableModel; 037import javax.swing.tree.DefaultMutableTreeNode; 038import javax.swing.tree.DefaultTreeModel; 039import javax.swing.tree.TreeNode; 040 041import jmri.InstanceManager; 042import jmri.Path; 043import jmri.implementation.SignalSpeedMap; 044import jmri.jmrit.picker.PickListModel; 045import jmri.jmrit.roster.Roster; 046import jmri.jmrit.roster.RosterEntry; 047import jmri.jmrit.roster.RosterSpeedProfile; 048import jmri.util.JmriJFrame; 049import jmri.util.swing.JmriJOptionPane; 050 051/** 052 * Make panels for WarrantFrame and NXFrame windows that create and edit 053 * Warrants. 054 * <p> 055 * Input panels for defining a train's route from an eNtry OBlock to an eXit 056 * OBlock. Routes are defined by choosing the originating block, the path on 057 * which the train start and the exit Portal through which it will leave the 058 * block. Also it is required that a Destination block is chosen and the path 059 * and Portal through which the train will arrive. The Portal selections 060 * establish the direction information. Optionally, additional blocks can be 061 * specified requiring the train to pass through or avoid entering. 062 * <p> 063 * Input panels to describe the train. accesses the roster for some info. 064 * 065 * @author Peter Cressman 066 * 067 */ 068abstract class WarrantRoute extends jmri.util.JmriJFrame implements ActionListener, PropertyChangeListener { 069 070 enum Location { 071 ORIGIN, DEST, VIA, AVOID 072 } 073 074 enum Display { 075 MPH("mph"), KPH("kph"), MMPS("mmps"), INPS("inps"), IN("in"), CM("cm"); 076 String _bundleKey; 077 Display(String bundleName) { 078 _bundleKey = bundleName; 079 } 080 @Override 081 public String toString() { 082 return Bundle.getMessage(_bundleKey); 083 } 084 } 085 086 static class DisplayButton extends JButton { 087 private Display pref; 088 DisplayButton(Display p) { 089 super(); 090 DisplayButton.this.setDisplayPref(p); 091 } 092 void setDisplayPref(Display p) { 093 pref = p; 094 setText(p.toString()); 095 } 096 Display getDisplyPref() { 097 return pref; 098 } 099 } 100 101 protected RouteLocation _origin = new RouteLocation(Location.ORIGIN); 102 protected RouteLocation _destination = new RouteLocation(Location.DEST); 103 protected RouteLocation _via = new RouteLocation(Location.VIA); 104 protected RouteLocation _avoid = new RouteLocation(Location.AVOID); 105 protected RouteLocation _focusedField; 106 107 protected SpeedUtil _speedUtil; 108 protected Display _displayPref; // speed units preference 109 protected Display _units; // distance units preference 110 protected float _scale = 87.1f; 111 112 static int STRUT_SIZE = 10; 113 private int _depth = 20; 114 115 static String PAD = " "; 116 private JDialog _pickRouteDialog; 117 private final RouteTableModel _routeModel; 118 protected ArrayList<BlockOrder> _orders; 119 private JFrame _debugFrame; 120 private RouteFinder _routeFinder; 121 private final JTextField _searchDepth = new JTextField(5); 122 private JButton _calculateButton = new JButton(Bundle.getMessage("Calculate")); 123 124 private final JComboBox<String> _rosterBox = new JComboBox<>(); 125 private final AddressTextField _dccNumBox = new AddressTextField(); 126 private final JTextField _trainNameBox = new JTextField(6); 127 private final JButton _viewProfile = new JButton(Bundle.getMessage("ViewProfile")); 128 private JmriJFrame _spTable = null; 129 private JmriJFrame _pickListFrame; 130 131 132 /** 133 * Only subclasses can create this 134 */ 135 protected WarrantRoute() { 136 super(false, true); 137 log.debug("newInstance"); 138 _searchDepth.setText(Integer.toString(_depth)); 139 _routeModel = new RouteTableModel(); 140 _speedUtil = new SpeedUtil(); 141 142 int interpretation = SignalSpeedMap.SPEED_KMPH; 143 WarrantPreferences wp = WarrantPreferences.getDefault(); 144 if (wp != null) { 145 interpretation = WarrantPreferences.getDefault().getInterpretation(); 146 _scale = wp.getLayoutScale(); 147 } 148 switch (interpretation) { 149 case SignalSpeedMap.SPEED_MPH: 150 _displayPref = Display.MPH; 151 _units = Display.IN; 152 break; 153 case SignalSpeedMap.SPEED_KMPH: 154 _displayPref = Display.KPH; 155 _units = Display.CM; 156 break; 157 default: 158 _displayPref = Display.INPS; 159 _units = Display.IN; 160 break; 161 } 162 setupRoster(); 163 } 164 165 protected abstract void selectedRoute(ArrayList<BlockOrder> orders); 166 protected abstract void maxThrottleEventAction(); 167 168 @Override 169 public abstract void propertyChange(java.beans.PropertyChangeEvent e); 170 171 protected void setSpeedUtil(SpeedUtil sp) { 172 _speedUtil = sp; 173 } 174 175 static class AddressTextField extends JTextField implements FocusListener { 176 public AddressTextField() { 177 super(); 178 addFocusListener(AddressTextField.this); 179 } 180 @Override 181 public void focusGained(FocusEvent e) { 182 183 } 184 @Override 185 public void focusLost(FocusEvent e) { 186 fireActionPerformed(); 187 } 188 } 189 190 /* ************************* Panel for Route search depth **********************/ 191 /** 192 * @return How many nodes deep the tree search should be 193 */ 194 private int getDepth() { 195 try { 196 int i = Integer.parseInt(_searchDepth.getText()); 197 if (i > 2 ) { 198 _depth = i; 199 } 200 } catch (NumberFormatException nfe) { 201 // ignore 202 } 203 return _depth; 204 } 205 206 protected JPanel searchDepthPanel(boolean vertical) { 207 _searchDepth.setText(Integer.toString(_depth)); 208 JPanel p = new JPanel(); 209 p.add(Box.createHorizontalGlue()); 210 p.add(makeTextBoxPanel(vertical, _searchDepth, "SearchDepth", "ToolTipSearchDepth")); 211 _searchDepth.setColumns(5); 212 p.add(Box.createHorizontalGlue()); 213 return p; 214 } 215 216 protected JPanel calculatePanel(boolean vertical) { 217 _calculateButton.setMaximumSize(_calculateButton.getPreferredSize()); 218 _calculateButton.addActionListener(new ActionListener() { 219 @Override 220 public void actionPerformed(ActionEvent e) { 221 clearTempWarrant(); 222 calculate(); 223 } 224 }); 225 226 JButton stopButton = new JButton(Bundle.getMessage("Stop")); 227 stopButton.addActionListener(new ActionListener() { 228 @Override 229 public void actionPerformed(ActionEvent e) { 230 stopRouteFinder(); 231 } 232 }); 233 234 JPanel panel = new JPanel(); 235 panel.add(makeTextBoxPanel(vertical, _calculateButton, "CalculateRoute", null)); 236 panel.add(makeTextBoxPanel(vertical, stopButton, "StopSearch", null)); 237 return panel; 238 } 239 public JPanel makePickListPanel() { 240 JButton button = new JButton(Bundle.getMessage("MenuBlockPicker")); 241 button.setMaximumSize(_calculateButton.getPreferredSize()); 242 button.addActionListener(new ActionListener() { 243 @Override 244 public void actionPerformed(ActionEvent e) { 245 if (_pickListFrame !=null) { 246 _pickListFrame.dispose(); 247 } 248 _pickListFrame = new JmriJFrame(); 249 PickListModel<OBlock> model = PickListModel.oBlockPickModelInstance(); 250 _pickListFrame.add(new JScrollPane(model.makePickTable())); 251 _pickListFrame.pack(); 252 _pickListFrame.setVisible(true); 253 } 254 }); 255 JPanel p = new JPanel(); 256 p.add(button); 257 return p; 258 } 259 260 261 /* ************************* Train ID info: Loco Address, etc **********************/ 262 /** 263 * Make panel containing TextFields for Train name and address and ComboBox 264 * for Roster entries. called from: 265 * WarrantFrame.makeBorderedTrainPanel() at init of WarrantFrame 266 * NXFrame.makeAutoRunPanel() at init of NXFrame 267 * 268 * 269 * @param comp optional panel to add 270 * @return panel 271 */ 272 protected JPanel makeTrainIdPanel(JPanel comp) { 273 JPanel trainPanel = new JPanel(); 274 trainPanel.setLayout(new BoxLayout(trainPanel, BoxLayout.LINE_AXIS)); 275 trainPanel.add(Box.createHorizontalStrut(STRUT_SIZE)); 276 277 JPanel panel = new JPanel(); 278 panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS)); 279 panel.add(makeTextBoxPanel(false, _trainNameBox, "TrainName", "noTrainName")); 280 panel.add(makeTextBoxPanel(false, _rosterBox, "Roster", null)); 281 panel.add(Box.createVerticalStrut(2)); 282 panel.add(makeTextBoxPanel(false, _dccNumBox, "DccAddress", null)); 283 _dccNumBox.addActionListener((ActionEvent e) -> { 284 checkAddress(); 285 }); 286 287 JPanel p = new JPanel(); 288 p.setLayout(new BoxLayout(p, BoxLayout.LINE_AXIS)); 289 p.add(_viewProfile); 290 _viewProfile.addActionListener((ActionEvent e) -> { 291 showProfile(); 292 }); 293 panel.add(p); 294 if (comp != null) { 295 panel.add(comp); 296 } 297 trainPanel.add(panel); 298 trainPanel.add(Box.createHorizontalStrut(STRUT_SIZE)); 299 300 return trainPanel; 301 } 302 303 private void setupRoster() { 304 List<RosterEntry> list = Roster.getDefault().matchingList(null, null, null, null, null, null, null); 305 _rosterBox.setRenderer(new jmri.jmrit.roster.swing.RosterEntryListCellRenderer()); 306 _rosterBox.addItem(" "); 307 _rosterBox.addItem(Bundle.getMessage("noSuchAddress")); 308 for (int i = 0; i < list.size(); i++) { 309 RosterEntry r = list.get(i); 310 _rosterBox.addItem(r.titleString()); 311 } 312 _rosterBox.setMaximumSize(_rosterBox.getPreferredSize()); 313 _rosterBox.addActionListener((ActionEvent e) -> { 314 checkAddress(); 315 }); 316 } 317 318 private void showProfile() { 319 closeProfileTable(); 320 321 String id = _speedUtil.getRosterId(); 322 if (id == null || id.isEmpty()) { 323 return; 324 } 325 if (Roster.getDefault().getEntryForId(id) == null) { 326 String rosterId = JmriJOptionPane.showInputDialog(this, 327 Bundle.getMessage("makeRosterEntry", _speedUtil.getAddress()), 328 Bundle.getMessage("QuestionTitle"), 329 JmriJOptionPane.QUESTION_MESSAGE); 330 log.debug("Create roster entry {}", rosterId); 331 if (rosterId == null || rosterId.isEmpty()) { 332 rosterId = id; 333 } 334 RosterEntry rosterEntry = _speedUtil.makeRosterEntry(rosterId); 335 if (rosterEntry == null) { 336 return; 337 } 338 Roster.getDefault().addEntry(rosterEntry); 339 WarrantManager mgr = InstanceManager.getDefault(WarrantManager.class); 340 RosterSpeedProfile mergeProfile = _speedUtil.getMergeProfile(); 341 mgr.setMergeProfile(rosterId, mergeProfile); 342 mgr.getMergeProfiles().remove(id); 343 _speedUtil.setRosterId(rosterId); 344 id = rosterId; 345 } 346 347 JPanel viewPanel = makeViewPanel(id); 348 if (viewPanel == null) { 349 return; 350 } 351 _spTable = new JmriJFrame(false, true); 352 JPanel framePanel = new JPanel(); 353 framePanel.setLayout(new BoxLayout(framePanel, BoxLayout.PAGE_AXIS)); 354 framePanel.add(Box.createGlue()); 355 356 framePanel.add(viewPanel); 357 _spTable.getContentPane().add(framePanel); 358 _spTable.pack(); 359 _spTable.setVisible(true); 360 } 361 362 private JPanel makeViewPanel(String id) { 363 RosterSpeedProfile speedProfile = _speedUtil.getMergeProfile(); 364 RosterEntry re = Roster.getDefault().getEntryForId(id); 365 RosterSpeedProfile rosterSpeedProfile; 366 if (re != null) { 367 rosterSpeedProfile = re.getSpeedProfile(); 368 if (rosterSpeedProfile == null) { 369 rosterSpeedProfile = new RosterSpeedProfile(re); 370 re.setSpeedProfile(rosterSpeedProfile); 371 } 372 } else { 373 rosterSpeedProfile = null; 374 } 375 if ((speedProfile == null || speedProfile.getProfileSize() == 0) && 376 (rosterSpeedProfile == null || rosterSpeedProfile.getProfileSize() == 0)) { 377 _viewProfile.setEnabled(false); 378 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("NoSpeedProfile", id), 379 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 380 return null; 381 } else { 382 _viewProfile.setEnabled(true); 383 } 384 JPanel viewPanel = new JPanel(); 385 viewPanel.setLayout(new BoxLayout(viewPanel, BoxLayout.PAGE_AXIS)); 386 viewPanel.add(Box.createGlue()); 387 JPanel panel = new JPanel(); 388 panel.add(MergePrompt.makeEditInfoPanel(re)); 389 viewPanel.add(panel); 390 391 JPanel spPanel = new JPanel(); 392 spPanel.setLayout(new BoxLayout(spPanel, BoxLayout.LINE_AXIS)); 393 spPanel.add(Box.createGlue()); 394 395 if (rosterSpeedProfile != null ) { 396 Map<Integer, Boolean> anomilies = MergePrompt.validateSpeedProfile(rosterSpeedProfile); 397 spPanel.add(MergePrompt.makeSpeedProfilePanel("rosterSpeedProfile", rosterSpeedProfile, false, anomilies)); 398 spPanel.add(Box.createGlue()); 399 } 400 if (speedProfile != null) { 401 Map<Integer, Boolean> anomaly = MergePrompt.validateSpeedProfile(speedProfile); 402 spPanel.add(MergePrompt.makeSpeedProfilePanel("mergedSpeedProfile", speedProfile, true, anomaly)); 403 spPanel.add(Box.createGlue()); 404 } 405 406 viewPanel.add(spPanel); 407 return viewPanel; 408 } 409 410 411 protected void closeProfileTable() { 412 if (_spTable != null) { 413 String id = _speedUtil.getRosterId(); 414 if (id != null) { 415 RosterSpeedProfile speedProfile = _speedUtil.getMergeProfile(); 416 InstanceManager.getDefault(WarrantManager.class).setMergeProfile(id, speedProfile); 417 } 418 _spTable.dispose(); 419 _spTable = null; 420 } 421 } 422 423 // called by WarrantFrame.setup() 424 protected String setTrainInfo(String name) { 425 if (log.isDebugEnabled()) { 426 log.debug("setTrainInfo for: {}", name); 427 } 428 setTrainName(name); 429 _dccNumBox.setText(_speedUtil.getAddress()); 430 setRosterBox(); 431 if (name == null) { 432 RosterEntry re = _speedUtil.getRosterEntry(); 433 if (re != null) { 434 setTrainName(re.getRoadNumber()); 435 setRosterBox(); 436 } else { 437 setTrainName(_speedUtil.getAddress()); 438 } 439 } 440 return null; 441 } 442 443 private void setRosterBox() { 444 String id = _speedUtil.getRosterId(); 445 if (id != null && id.equals(_rosterBox.getSelectedItem())) { 446 return; 447 } 448 if (id != null && id.charAt(0) != '$' && id.charAt(id.length()-1) !='$') { 449 _rosterBox.setSelectedItem(id); 450 } else { 451 _rosterBox.setSelectedItem(Bundle.getMessage("noSuchAddress")); 452 } 453 } 454 455 protected void setTrainName(String name) { 456 _trainNameBox.setText(name); 457 } 458 459 protected String getTrainName() { 460 String trainName = _trainNameBox.getText(); 461 if (trainName == null || trainName.length() == 0) { 462 trainName = _dccNumBox.getText(); 463 } 464 return trainName; 465 } 466 467 private void checkAddress() { 468 String msg = setAddress(); 469 if (msg != null) { 470 JmriJOptionPane.showMessageDialog(this, msg, 471 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 472 } 473 } 474 475 /** 476 * Called to make final consistency check on loco address before running warrant 477 * @return error message 478 */ 479 protected String setAddress() { 480 String msg = null; 481 String suAddr = _speedUtil.getAddress(); 482 String addrText = _dccNumBox.getText(); 483 String suId = _speedUtil.getRosterId(); 484 boolean textChange = false; 485 if ( !addrText.equals(suAddr) || suId == null) { 486 textChange = true; 487 if (!_speedUtil.setAddress(_dccNumBox.getText())) { 488 msg = Bundle.getMessage("BadDccAddress", _dccNumBox.getText()); 489 } else { // else address OK. 490 suAddr = _speedUtil.getAddress(); 491 _dccNumBox.setText(suAddr); // add protocol string 492 suId = _speedUtil.getRosterId(); 493 maxThrottleEventAction(); 494 if (suId != null && !(suId.charAt(0) == '$' && suId.charAt(suId.length()-1) =='$')) { 495 _rosterBox.setSelectedItem(suId); 496 } else { 497 _rosterBox.setSelectedItem(Bundle.getMessage("noSuchAddress")); 498 return null; 499 } 500 } 501 } 502 503 String id = (String)_rosterBox.getSelectedItem(); 504 RosterEntry re = Roster.getDefault().getEntryForId(id); 505 boolean isRoster = (re != null); 506 suId = _speedUtil.getRosterId(); 507 if (suId != null && suId.charAt(0) == '$' && suId.charAt(suId.length()-1) =='$') { 508 isRoster = true; 509 } 510 if (!textChange && !isRoster) { 511 _dccNumBox.setText(null); 512 return null; 513 } 514 if (re != null) { 515 if (!re.getDccLocoAddress().equals(_speedUtil.getDccAddress())) { 516 _speedUtil.setRosterId(id); 517 } 518 _dccNumBox.setText(re.getDccLocoAddress().toString()); 519 maxThrottleEventAction(); 520 msg = null; 521 } else if (msg == null) { 522 _rosterBox.setSelectedItem(Bundle.getMessage("noSuchAddress")); 523 } 524 return msg; 525 } 526 527 protected String getAddress() { 528 return _dccNumBox.getText(); 529 } 530 531 protected String checkLocoAddress() { 532 if (_speedUtil.getDccAddress() == null) { 533 return Bundle.getMessage("BadDccAddress", _dccNumBox.getText()); 534 } 535 return null; 536 } 537 538 protected void calculate() { 539 String msg = findRoute(); 540 if (msg != null) { 541 JmriJOptionPane.showMessageDialog(this, msg, 542 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 543 } 544 } 545 546 /* ****************************** route info *******************/ 547 /** 548 * Does the action on each of the 4 RouteLocation panels 549 * 550 * @param e the action event 551 */ 552 @Override 553 public void actionPerformed(ActionEvent e) { 554 Object obj = e.getSource(); 555 if (log.isTraceEnabled()) { 556 log.trace("actionPerformed: source {} id= {}, ActionCommand= {}", 557 ((Component) obj).getName(), e.getID(), e.getActionCommand()); 558 } 559 doAction(obj); 560 } 561 562 @SuppressWarnings("unchecked") // parameter can be any of several types, including JComboBox<String> 563 void doAction(Object obj) { 564 if (obj instanceof JTextField) { 565 JTextField box = (JTextField) obj; 566 if ( !_origin.checkBlockBox(box) && !_destination.checkBlockBox(box) && !_via.checkBlockBox(box) ) { 567 _avoid.checkBlockBox(box); 568 } 569 } else { 570 JComboBox<String> box = (JComboBox<String>) obj; 571 if (!_origin.checkPathBox(box) && !_destination.checkPathBox(box) && !_via.checkPathBox(box)) { 572 if (!_avoid.checkPathBox(box)) { 573 if (_origin.checkPortalBox(box)) { 574 _origin.setOrderExitPortal(); 575 } 576 if (_destination.checkPortalBox(box)) { 577 _destination.setOrderEntryPortal(); 578 } 579 } 580 } 581 } 582 } 583 584 protected JPanel makeBlockPanels(boolean add) { 585 JPanel panel = new JPanel(); 586 panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS)); 587 588 JPanel oPanel = _origin.makePanel("OriginBlock", "OriginToolTip", "PathName", "ExitPortalName", this); 589 panel.add(oPanel); 590 591 oPanel = _destination.makePanel("DestBlock", "DestToolTip", "PathName", "EntryPortalName", this); 592 panel.add(oPanel); 593 594 oPanel = _via.makePanel("ViaBlock", "ViaToolTip", "PathName", null, this); 595 596 JPanel aPanel = _avoid.makePanel("AvoidBlock", "AvoidToolTip", "PathName", null, this); 597 598 if (add) { 599 JPanel pLeft = new JPanel(); 600 pLeft.setLayout(new BoxLayout(pLeft, BoxLayout.PAGE_AXIS)); 601 pLeft.add(oPanel); 602 pLeft.add(aPanel); 603 604 JPanel pRight = new JPanel(); 605 pRight.setLayout(new BoxLayout(pRight, BoxLayout.PAGE_AXIS)); 606 pRight.add(searchDepthPanel(true)); 607 pRight.add(makePickListPanel()); 608 pRight.add(calculatePanel(true)); 609 610 JPanel p = new JPanel(); 611 p.setLayout(new BoxLayout(p, BoxLayout.LINE_AXIS)); 612 p.add(pLeft); 613 p.add(pRight); 614 panel.add(p); 615 } else { 616 panel.add(oPanel); 617 panel.add(aPanel); 618 } 619 return panel; 620 } 621 622 private JPanel makeLabelCombo(String title, JComboBox<String> box, String tooltip) { 623 624 JPanel p = new JPanel(); 625 p.setLayout(new BorderLayout()); 626 p.setToolTipText(Bundle.getMessage(tooltip)); 627 box.setToolTipText(Bundle.getMessage(tooltip)); 628 JLabel l = new JLabel(PAD + Bundle.getMessage(title) + PAD); 629 p.add(l, BorderLayout.NORTH); 630 l.setLabelFor(box); 631 p.add(box, BorderLayout.CENTER); 632 box.setBackground(Color.white); 633 box.addActionListener(this); 634 box.setAlignmentX(JComponent.CENTER_ALIGNMENT); 635 return p; 636 } 637 638 private boolean setOriginBlock() { 639 return _origin.setBlock(); 640 } 641 642 private boolean setDestinationBlock() { 643 return _destination.setBlock(); 644 } 645 646 private boolean setViaBlock() { 647 return _via.setBlock(); 648 } 649 650 private boolean setAvoidBlock() { 651 return _avoid.setBlock(); 652 } 653 654 /* ********** route blocks **************************/ 655 protected class RouteLocation extends java.awt.event.MouseAdapter { 656 657 Location location; 658 private BlockOrder order; 659 JTextField blockBox = new JTextField(); 660 private final JComboBox<String> pathBox = new JComboBox<>(); 661 JComboBox<String> portalBox; 662 663 RouteLocation(Location loc) { 664 location = loc; 665 if (location == Location.ORIGIN || location == Location.DEST) { 666 portalBox = new JComboBox<>(); 667 } 668 } 669 670 private JPanel makePanel(String title, String tooltip, String box1Name, String box2Name, WarrantRoute parent) { 671 JPanel oPanel = new JPanel(); 672 oPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(java.awt.Color.BLACK), 673 Bundle.getMessage(title), 674 javax.swing.border.TitledBorder.CENTER, 675 javax.swing.border.TitledBorder.TOP)); 676 JPanel hPanel = new JPanel(); 677 hPanel.setLayout(new BoxLayout(hPanel, BoxLayout.LINE_AXIS)); 678 hPanel.add(Box.createHorizontalStrut(STRUT_SIZE)); 679 hPanel.add(makeBlockBox(tooltip)); 680 hPanel.add(Box.createHorizontalStrut(STRUT_SIZE)); 681 JPanel pPanel = new JPanel(); 682 pPanel.setLayout(new BoxLayout(pPanel, BoxLayout.LINE_AXIS)); 683 pPanel.add(makeLabelCombo(box1Name, pathBox, tooltip)); 684 pPanel.add(Box.createHorizontalStrut(STRUT_SIZE)); 685 686 if (box2Name != null) { 687 pPanel.add(makeLabelCombo(box2Name, portalBox, tooltip)); 688 pPanel.add(Box.createHorizontalStrut(STRUT_SIZE)); 689 } 690 hPanel.add(pPanel); 691 oPanel.add(hPanel); 692 pPanel.setToolTipText(Bundle.getMessage(tooltip)); 693 hPanel.setToolTipText(Bundle.getMessage(tooltip)); 694 oPanel.setToolTipText(Bundle.getMessage(tooltip)); 695 696 blockBox.addActionListener(parent); 697 blockBox.addPropertyChangeListener(parent); 698 blockBox.addMouseListener(this); 699 700 return oPanel; 701 } 702 703 private JPanel makeBlockBox(String tooltip) { 704 blockBox.setDragEnabled(true); 705 blockBox.setTransferHandler(new jmri.util.DnDStringImportHandler()); 706 blockBox.setColumns(20); 707 blockBox.setAlignmentX(Component.CENTER_ALIGNMENT); 708 JPanel p = new JPanel(); 709 p.setLayout(new BorderLayout()); 710 p.setToolTipText(Bundle.getMessage(tooltip)); 711 blockBox.setToolTipText(Bundle.getMessage(tooltip)); 712 JLabel l = new JLabel(Bundle.getMessage("BlockName")); 713 p.add(l, BorderLayout.NORTH); 714 l.setLabelFor(blockBox); 715 p.add(blockBox, BorderLayout.CENTER); 716 return p; 717 } 718 719 private void clearFields() { 720 setBlock(null); 721 } 722 723 private boolean checkBlockBox(JTextField box) { 724 if (box == blockBox) { 725 setBlock(getEndPointBlock()); 726 return true; 727 } 728 return false; 729 } 730 731 private boolean checkPathBox(JComboBox<String> box) { 732 if (box == pathBox) { 733 if (portalBox != null) { 734 setPortalBox(order); 735 } 736 return true; 737 } 738 return false; 739 } 740 741 private boolean checkPortalBox(JComboBox<String> box) { 742 return (box == portalBox); 743 } 744 745 private void setOrderEntryPortal() { 746 if (order != null) { 747 order.setEntryName((String) portalBox.getSelectedItem()); 748 } 749 } 750 751 private void setOrderExitPortal() { 752 if (order != null) { 753 order.setExitName((String) portalBox.getSelectedItem()); 754 } 755 } 756 757 protected void setOrder(BlockOrder o) { 758 if (o != null) { 759 // setting blockBox text triggers doAction, so allow that to finish 760 order = new BlockOrder(o); 761 OBlock block = o.getBlock(); 762 blockBox.setText(block.getDisplayName()); 763 setPathBox(block); 764 setPathName(o.getPathName()); 765 setPortalBox(o); 766 if (location == Location.DEST) { 767 setPortalName(o.getEntryName()); 768 } else if (location == Location.ORIGIN) { 769 setPortalName(o.getExitName()); 770 } 771 } 772 } 773 774 protected BlockOrder getOrder() { 775 return order; 776 } 777 778 private void setPortalName(String name) { 779 portalBox.setSelectedItem(name); 780 } 781 782 private void setPathName(String name) { 783 pathBox.setSelectedItem(name); 784 } 785 786 protected String getBlockName() { 787 return blockBox.getText(); 788 } 789 790 private OBlock getEndPointBlock() { 791 String text = blockBox.getText(); 792 int idx = text.indexOf(java.awt.event.KeyEvent.VK_TAB); 793 if (idx > 0) { 794 if (idx + 1 < text.length()) { 795 text = text.substring(idx + 1); 796 } else { 797 text = text.substring(0, idx); 798 } 799 } 800 blockBox.setText(text); 801 OBlock block = InstanceManager.getDefault(OBlockManager.class).getOBlock(text); 802 if (block == null && text.length() > 0) { 803 JmriJOptionPane.showMessageDialog(blockBox.getParent(), Bundle.getMessage("BlockNotFound", text), 804 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 805 } 806 return block; 807 } 808 809 private boolean setBlock() { 810 return setBlock(getEndPointBlock()); 811 } 812 813 private boolean setBlock(OBlock block) { 814 boolean result = true; 815 if (block == null) { 816 result = false; 817 order = null; 818 } else { 819 if (order != null && block == order.getBlock() 820 && pathIsValid(block, order.getPathName()) == null) { 821 result = true; 822 } else { 823 if (pathsAreValid(block)) { 824 order = new BlockOrder(block); 825 if (!setPathBox(block)) { 826 result = false; 827 } else { 828 setPortalBox(order); 829 } 830 } else { 831 result = false; 832 } 833 } 834 } 835 if (result) { 836 // block cannot be null here. it is protected by result==true 837 if (block != null) { 838 blockBox.setText(block.getDisplayName()); 839 } 840 order.setPathName((String) pathBox.getSelectedItem()); 841 if (location == Location.DEST) { 842 order.setEntryName((String) portalBox.getSelectedItem()); 843 } else if (location == Location.ORIGIN) { 844 order.setExitName((String) portalBox.getSelectedItem()); 845 } 846 setNextLocation(); 847 } else { 848 blockBox.setText(null); 849 pathBox.removeAllItems(); 850 if (portalBox != null) { 851 portalBox.removeAllItems(); 852 } 853 } 854 return result; 855 } 856 857 private boolean setPathBox(OBlock block) { 858 pathBox.removeAllItems(); 859 if (portalBox != null) { 860 portalBox.removeAllItems(); 861 } 862 if (block == null) { 863 return false; 864 } 865 List<Path> list = block.getPaths(); 866 if (list.isEmpty()) { 867 JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("NoPaths", block.getDisplayName()), 868 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 869 return false; 870 } 871 for (int i = 0; i < list.size(); i++) { 872 pathBox.addItem(((OPath) list.get(i)).getName()); 873 } 874 return true; 875 } 876 877 private void setPortalBox(@CheckForNull BlockOrder order) { 878 if (portalBox == null) { 879 return; 880 } 881 portalBox.removeAllItems(); 882 if (order == null) { 883 return; 884 } 885 String pathName = (String) pathBox.getSelectedItem(); 886 order.setPathName(pathName); 887 OPath path = order.getPath(); 888 if (path != null) { 889 Portal portal = path.getFromPortal(); 890 if (portal != null) { 891 String name = portal.getName(); 892 if (name != null) { 893 portalBox.addItem(name); 894 } 895 } 896 portal = path.getToPortal(); 897 if (portal != null) { 898 String name = portal.getName(); 899 if (name != null) { 900 portalBox.addItem(name); 901 } 902 } 903 if (log.isTraceEnabled()) { 904 log.debug("setPortalBox: Path {} set in block {}", path.getName(), order.getBlock().getDisplayName()); 905 } 906 } else { 907 if (log.isDebugEnabled()) { 908 log.debug("setPortalBox: Path {} not found in block {}", pathName, order.getBlock().getDisplayName()); 909 } 910 order.setPathName(null); 911 } 912 } 913 914 private void setNextLocation() { 915 switch (location) { 916 case ORIGIN: 917 _focusedField = _destination; 918 break; 919 case DEST: 920 _focusedField = _via; 921 break; 922 case VIA: 923 _focusedField = _avoid; 924 break; 925 case AVOID: 926 _focusedField = _origin; 927 break; 928 default: 929 log.warn("Unhandled next location code: {}", location); 930 break; 931 } 932 } 933 934 @Override 935 public void mouseClicked(MouseEvent e) { 936 _focusedField = this; 937 } 938 @Override 939 public void mousePressed(MouseEvent e) { 940 _focusedField = this; 941 } 942 } // end RouteLocation 943 944 protected void mouseClickedOnBlock(OBlock block) { 945 if (_focusedField != null) { 946 _focusedField.setBlock(block); 947 } else { 948 _origin.setBlock(block); 949 } 950 } 951 952 boolean pathsAreValid(@Nonnull OBlock block) { 953 List<Path> list = block.getPaths(); 954 if (list.isEmpty()) { 955 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("NoPaths", block.getDisplayName()), 956 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 957 return false; 958 } 959 for (int i = 0; i < list.size(); i++) { 960 OPath path = (OPath) list.get(i); 961 if (path.getFromPortal() == null && path.getToPortal() == null) { 962 JmriJOptionPane.showMessageDialog(this, 963 Bundle.getMessage("PathNeedsPortal", path.getName(), block.getDisplayName()), 964 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 965 return false; 966 } 967 } 968 return true; 969 } 970 971 /* ****************************** Finding the route ********************************/ 972 /** 973 * Gather parameters to search for a route 974 * 975 * @return Error message, if any 976 */ 977 private String findRoute() { 978 // read and verify origin and destination blocks/paths/portals 979 String msg; 980 BlockOrder order; 981 String pathName; 982 if (setOriginBlock()) { 983 order = _origin.getOrder(); 984 pathName = order.getPathName(); 985 if (pathName != null) { 986 if (order.getExitName() == null) { 987 msg = Bundle.getMessage("SetExitPortal", Bundle.getMessage("OriginBlock")); 988 } else { 989 msg = pathIsValid(order.getBlock(), pathName); 990 } 991 } else { 992 msg = Bundle.getMessage("SetPath", Bundle.getMessage("OriginBlock")); 993 } 994 } else { 995 msg = Bundle.getMessage("SetEndPoint", Bundle.getMessage("OriginBlock")); 996 } 997 if (msg == null) { 998 if (setDestinationBlock()) { 999 order = _destination.getOrder(); 1000 pathName = order.getPathName(); 1001 if (pathName != null) { 1002 if (order.getEntryName() == null) { 1003 msg = Bundle.getMessage("SetEntryPortal", Bundle.getMessage("DestBlock")); 1004 } else { 1005 msg = pathIsValid(order.getBlock(), pathName); 1006 } 1007 } else { 1008 msg = Bundle.getMessage("SetPath", Bundle.getMessage("DestBlock")); 1009 } 1010 } else { 1011 msg = Bundle.getMessage("SetEndPoint", Bundle.getMessage("DestBlock")); 1012 } 1013 } 1014 if (msg == null) { 1015 if (setViaBlock()) { 1016 order = _via.getOrder(); 1017 if (order != null && order.getPathName() == null) { 1018 msg = Bundle.getMessage("SetPath", Bundle.getMessage("ViaBlock")); 1019 } 1020 } 1021 } 1022 if (msg == null) { 1023 if (setAvoidBlock()) { 1024 order = _avoid.getOrder(); 1025 if (order != null && order.getPathName() == null) { 1026 msg = Bundle.getMessage("SetPath", Bundle.getMessage("AvoidBlock")); 1027 } 1028 } 1029 } 1030 if (msg == null) { 1031 log.debug("Params OK. findRoute() is creating a RouteFinder"); 1032 _routeFinder = new RouteFinder(this, _origin.getOrder(), _destination.getOrder(), 1033 _via.getOrder(), _avoid.getOrder(), getDepth()); 1034 jmri.util.ThreadingUtil.newThread(_routeFinder).start(); 1035 } 1036 return msg; 1037 } 1038 1039 protected void stopRouteFinder() { 1040 if (_routeFinder != null) { 1041 _routeFinder.quit(); 1042 _routeFinder = null; 1043 } 1044 } 1045 1046 /* *********************************** Route Selection **************************************/ 1047 protected List<BlockOrder> getOrders() { 1048 return _orders; 1049 } 1050 1051 protected BlockOrder getViaBlockOrder() { 1052 return _via.getOrder(); 1053 } 1054 1055 protected BlockOrder getAvoidBlockOrder() { 1056 return _avoid.getOrder(); 1057 } 1058 1059 private Warrant _tempWarrant; // only used in pickRoute() method 1060 1061 protected void clearTempWarrant() { 1062 if (_tempWarrant != null) { 1063 _tempWarrant.deAllocate(); 1064 } 1065 } 1066 1067 private void showTempWarrant(ArrayList<BlockOrder> orders) { 1068 String s = ("" + Math.random()).substring(4); 1069 if (_tempWarrant == null) { 1070 _tempWarrant = new Warrant("IW" + s + "TEMP", null); 1071 _tempWarrant.setBlockOrders(orders); 1072 } 1073 _tempWarrant.setRoute(true, orders); 1074 // Don't clutter with message - this is a temp display 1075 } 1076 1077 /** 1078 * Callback from RouteFinder - several routes found 1079 * 1080 * @param destNodes the destination blocks 1081 * @param routeTree the routes 1082 */ 1083 protected void pickRoute(@Nonnull List<DefaultMutableTreeNode> destNodes, DefaultTreeModel routeTree) { 1084 if (destNodes.size() == 1) { 1085 showRoute(destNodes.get(0), routeTree); 1086 selectedRoute(_orders); 1087 showTempWarrant(_orders); 1088 return; 1089 } 1090 _pickRouteDialog = new JDialog(this, Bundle.getMessage("DialogTitle"), false); 1091 _pickRouteDialog.addWindowListener(new java.awt.event.WindowAdapter() { 1092 @Override 1093 public void windowClosing(java.awt.event.WindowEvent e) { 1094 clearTempWarrant(); 1095 } 1096 }); 1097 _tempWarrant = null; 1098 JPanel mainPanel = new JPanel(); 1099 mainPanel.setLayout(new BorderLayout(5, 5)); 1100 JPanel panel = new JPanel(); 1101 panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); 1102 panel.add(new JLabel(Bundle.getMessage("NumberRoutes1", destNodes.size()))); 1103 panel.add(new JLabel(Bundle.getMessage("NumberRoutes2"))); 1104 JPanel wrapper = new JPanel(); 1105 wrapper.add(panel); 1106 mainPanel.add(wrapper, BorderLayout.NORTH); 1107 ButtonGroup buttons = new ButtonGroup(); 1108 1109 panel = new JPanel(); 1110 panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); 1111 for (int i = 0; i < destNodes.size(); i++) { 1112 JRadioButton button = new JRadioButton(Bundle.getMessage("RouteSize", i + 1, 1113 destNodes.get(i).getLevel() + 1)); 1114 button.setActionCommand("" + i); 1115 button.addActionListener((ActionEvent e) -> clearTempWarrant() ); 1116 buttons.add(button); 1117 panel.add(button); 1118 if (destNodes.size() == 1) { 1119 button.setSelected(true); 1120 } 1121 } 1122 JScrollPane scrollPane = new JScrollPane(panel); 1123 javax.swing.JViewport vp = scrollPane.getViewport(); 1124 JRadioButton button = new JRadioButton(Bundle.getMessage("RouteSize", 000, 000)); 1125 vp.setPreferredSize(new Dimension(button.getWidth(), _depth*button.getHeight())); 1126 mainPanel.add(scrollPane, BorderLayout.CENTER); 1127 1128 JButton ok = new JButton(Bundle.getMessage("ButtonSelect")); 1129 ok.addActionListener(new ActionListener() { 1130 ButtonGroup buts; 1131 JDialog dialog; 1132 List<DefaultMutableTreeNode> dNodes; 1133 DefaultTreeModel tree; 1134 1135 @Override 1136 public void actionPerformed(ActionEvent e) { 1137 if (buts.getSelection() != null) { 1138 clearTempWarrant(); 1139 int i = Integer.parseInt(buttons.getSelection().getActionCommand()); 1140 showRoute(dNodes.get(i), tree); 1141 selectedRoute(_orders); 1142 showTempWarrant(_orders); 1143 dialog.dispose(); 1144 } else { 1145 showWarning(Bundle.getMessage("SelectRoute")); 1146 } 1147 } 1148 1149 ActionListener init(ButtonGroup bg, JDialog d, List<DefaultMutableTreeNode> dn, 1150 DefaultTreeModel t) { 1151 buts = bg; 1152 dialog = d; 1153 dNodes = dn; 1154 tree = t; 1155 return this; 1156 } 1157 }.init(buttons, _pickRouteDialog, destNodes, routeTree)); 1158 ok.setMaximumSize(ok.getPreferredSize()); 1159 JButton show = new JButton(Bundle.getMessage("ButtonReview")); 1160 show.addActionListener(new ActionListener() { 1161 ButtonGroup buts; 1162 List<DefaultMutableTreeNode> destinationNodes; 1163 DefaultTreeModel tree; 1164 1165 @Override 1166 public void actionPerformed(ActionEvent e) { 1167 if (buts.getSelection() != null) { 1168 clearTempWarrant(); 1169 int i = Integer.parseInt(buttons.getSelection().getActionCommand()); 1170 showRoute(destinationNodes.get(i), tree); 1171 showTempWarrant(_orders); 1172 } else { 1173 showWarning(Bundle.getMessage("SelectRoute")); 1174 } 1175 } 1176 1177 ActionListener init(ButtonGroup bg, List<DefaultMutableTreeNode> dn, 1178 DefaultTreeModel t) { 1179 buts = bg; 1180 destinationNodes = dn; 1181 tree = t; 1182 return this; 1183 } 1184 }.init(buttons, destNodes, routeTree)); 1185 show.setMaximumSize(show.getPreferredSize()); 1186 panel = new JPanel(); 1187 panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); 1188 panel.add(Box.createHorizontalGlue()); 1189 panel.add(show); 1190 panel.add(Box.createHorizontalStrut(STRUT_SIZE)); 1191 panel.add(ok); 1192 panel.add(Box.createHorizontalGlue()); 1193 wrapper = new JPanel(); 1194 wrapper.add(panel); 1195 mainPanel.add(wrapper, BorderLayout.SOUTH); 1196 1197 panel = new JPanel(); 1198 panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); 1199 panel.add(Box.createHorizontalStrut(STRUT_SIZE)); 1200 panel.add(makeRouteTablePanel()); 1201 panel.add(Box.createHorizontalStrut(STRUT_SIZE)); 1202 panel.add(mainPanel); 1203 panel.add(Box.createHorizontalStrut(STRUT_SIZE)); 1204 1205 _pickRouteDialog.getContentPane().add(panel); 1206 _pickRouteDialog.setLocation(getLocation().x - 20, getLocation().y + 150); 1207 _pickRouteDialog.pack(); 1208 _pickRouteDialog.setVisible(true); 1209 } 1210 1211 protected void showWarning(String msg) { 1212 JmriJOptionPane.showMessageDialog(this, msg, 1213 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 1214 } 1215 1216 /** 1217 * Callback from RouteFinder - exactly one route found 1218 * 1219 * @param destNode destination block 1220 * @param tree possible routes 1221 */ 1222 private void showRoute(DefaultMutableTreeNode destNode, DefaultTreeModel tree) { 1223 TreeNode[] nodes = tree.getPathToRoot(destNode); 1224 _orders = new ArrayList<>(); 1225 int count = 0; 1226 for (TreeNode node : nodes) { 1227 BlockOrder bo = (BlockOrder) ((DefaultMutableTreeNode) node).getUserObject(); 1228 bo.setIndex(count++); 1229 _orders.add(bo); 1230 } 1231 _routeModel.fireTableDataChanged(); 1232 log.debug("showRoute: Route has {} orders.", _orders.size()); 1233 } 1234 1235 protected JPanel makeRouteTablePanel() { 1236 JTable routeTable = new JTable(_routeModel); 1237 routeTable.setDefaultEditor(JComboBox.class, new jmri.jmrit.symbolicprog.ValueEditor()); 1238 for (int i = 0; i < _routeModel.getColumnCount(); i++) { 1239 int width = _routeModel.getPreferredWidth(i); 1240 routeTable.getColumnModel().getColumn(i).setPreferredWidth(width); 1241 } 1242 JScrollPane tablePane = new JScrollPane(routeTable); 1243 Dimension dim = routeTable.getPreferredSize(); 1244 dim.height = routeTable.getRowHeight() * 11; 1245 tablePane.getViewport().setPreferredSize(dim); 1246 1247 JPanel routePanel = new JPanel(); 1248 routePanel.setLayout(new BoxLayout(routePanel, BoxLayout.Y_AXIS)); 1249 JLabel title = new JLabel(Bundle.getMessage("RouteTableTitle")); 1250 routePanel.add(title, BorderLayout.NORTH); 1251 routePanel.add(tablePane); 1252 routePanel.add(Box.createVerticalGlue()); 1253 return routePanel; 1254 } 1255 1256 /** 1257 * Callback from RouteFinder - no routes found 1258 * 1259 * @param tree routes 1260 * @param origin starting block 1261 * @param dest ending block 1262 */ 1263 protected void debugRoute(DefaultTreeModel tree, BlockOrder origin, BlockOrder dest) { 1264 if (JmriJOptionPane.YES_OPTION != JmriJOptionPane.showConfirmDialog(this, Bundle.getMessage("NoRoute", 1265 new Object[]{origin.getBlock().getDisplayName(), 1266 origin.getPathName(), origin.getExitName(), dest.getBlock().getDisplayName(), 1267 dest.getEntryName(), dest.getPathName(), getDepth()}), 1268 Bundle.getMessage("WarningTitle"), JmriJOptionPane.YES_NO_OPTION, 1269 JmriJOptionPane.WARNING_MESSAGE)) { 1270 return; 1271 } 1272 if (_debugFrame != null) { 1273 _debugFrame.dispose(); 1274 } 1275 _debugFrame = new JFrame(Bundle.getMessage("DebugRoute")); 1276 javax.swing.JTree dTree = new javax.swing.JTree(tree); 1277 dTree.setShowsRootHandles(true); 1278 dTree.setScrollsOnExpand(true); 1279 dTree.setExpandsSelectedPaths(true); 1280 JScrollPane treePane = new JScrollPane(dTree); 1281 treePane.getViewport().setPreferredSize(new Dimension(900, 300)); 1282 _debugFrame.getContentPane().add(treePane); 1283 _debugFrame.setVisible(true); 1284 _debugFrame.pack(); 1285 } 1286 1287 protected void clearRoute() { 1288 _orders = new ArrayList<>(); 1289 clearFrames(); 1290 clearFields(); 1291 _focusedField = _origin; 1292 _routeModel.fireTableDataChanged(); 1293 } 1294 1295 private void clearFrames() { 1296 1297 if (_debugFrame != null) { 1298 _debugFrame.dispose(); 1299 _debugFrame = null; 1300 } 1301 if (_pickRouteDialog != null) { 1302 _pickRouteDialog.dispose(); 1303 _pickRouteDialog = null; 1304 } 1305 closeProfileTable(); 1306 1307 if (_pickListFrame != null) { 1308 _pickListFrame.dispose(); 1309 _pickListFrame = null; 1310 } 1311 } 1312 1313 private void clearFields() { 1314 _origin.clearFields(); 1315 _destination.clearFields(); 1316 _via.clearFields(); 1317 _avoid.clearFields(); 1318 } 1319 1320 protected String routeIsValid() { 1321 if (_orders == null || _orders.isEmpty()) { 1322 return Bundle.getMessage("noBlockOrders"); 1323 } 1324 if (_orders.size() < 2) { 1325 return Bundle.getMessage("NoRouteSet", _origin.getBlockName(), _destination.getBlockName()); 1326 } 1327 BlockOrder blockOrder = _orders.get(0); 1328 String msg = pathIsValid(blockOrder.getBlock(), blockOrder.getPathName()); 1329 if (msg == null) { 1330 for (int i = 1; i < _orders.size(); i++) { 1331 BlockOrder nextBlockOrder = _orders.get(i); 1332 msg = pathIsValid(nextBlockOrder.getBlock(), nextBlockOrder.getPathName()); 1333 if (msg != null) { 1334 return msg; 1335 } 1336 if (!blockOrder.getExitName().equals(nextBlockOrder.getEntryName())) { 1337 return Bundle.getMessage("disconnectedRoute", 1338 blockOrder.getBlock().getDisplayName(), nextBlockOrder.getBlock().getDisplayName()); 1339 } 1340 blockOrder = nextBlockOrder; 1341 } 1342 } 1343 return msg; 1344 } 1345 1346 static protected String pathIsValid(OBlock block, String pathName) { 1347 if (block == null) { 1348 return Bundle.getMessage("PathInvalid", pathName, "null"); 1349 } 1350 List<Path> list = block.getPaths(); 1351 if (list.isEmpty()) { 1352 return Bundle.getMessage("WarningTitle"); 1353 } 1354 if (pathName != null) { 1355 for (int i = 0; i < list.size(); i++) { 1356 OPath path = (OPath) list.get(i); 1357 //if (log.isDebugEnabled()) log.debug("pathIsValid: pathName= "+pathName+", i= "+i+", path is "+path.getName()); 1358 if (pathName.equals(path.getName())) { 1359 if (path.getFromPortal() == null && path.getToPortal() == null) { 1360 return Bundle.getMessage("PathNeedsPortal", pathName, block.getDisplayName()); 1361 } 1362 return null; 1363 } 1364 } 1365 } 1366 return Bundle.getMessage("PathInvalid", pathName, block.getDisplayName()); 1367 } 1368 1369 @Override 1370 public void dispose() { 1371 clearFrames(); 1372 super.dispose(); 1373 } 1374 1375 /* ************************ Route Table ******************************/ 1376 private class RouteTableModel extends AbstractTableModel { 1377 1378 static final int BLOCK_COLUMN = 0; 1379 static final int ENTER_PORTAL_COL = 1; 1380 static final int PATH_COLUMN = 2; 1381 static final int DEST_PORTAL_COL = 3; 1382 static final int NUMCOLS = 4; 1383 1384 RouteTableModel() { 1385 super(); 1386 } 1387 1388 @Override 1389 public int getColumnCount() { 1390 return NUMCOLS; 1391 } 1392 1393 @Override 1394 public int getRowCount() { 1395 if (_orders==null) { 1396 return 0; 1397 } 1398 return _orders.size(); 1399 } 1400 1401 @Override 1402 public String getColumnName(int col) { 1403 switch (col) { 1404 case BLOCK_COLUMN: 1405 return Bundle.getMessage("BlockCol"); 1406 case ENTER_PORTAL_COL: 1407 return Bundle.getMessage("EnterPortalCol"); 1408 case PATH_COLUMN: 1409 return Bundle.getMessage("PathCol"); 1410 case DEST_PORTAL_COL: 1411 return Bundle.getMessage("DestPortalCol"); 1412 default: 1413 // fall through 1414 break; 1415 } 1416 return ""; 1417 } 1418 1419 @Override 1420 public boolean isCellEditable(int row, int col) { 1421 return false; 1422 } 1423 1424 @Override 1425 public Class<?> getColumnClass(int col) { 1426 return String.class; 1427 } 1428 1429 public int getPreferredWidth(int col) { 1430 return new JTextField(15).getPreferredSize().width; 1431 } 1432 1433 @Override 1434 public Object getValueAt(int row, int col) { 1435 // some error checking 1436 if (_orders==null || row >= _orders.size()) { 1437 return ""; 1438 } 1439 BlockOrder bo = _orders.get(row); 1440 // some error checking 1441 if (bo == null) { 1442 log.error("BlockOrder is null"); 1443 return ""; 1444 } 1445 switch (col) { 1446 case BLOCK_COLUMN: 1447 return bo.getBlock().getDisplayName(); 1448 case ENTER_PORTAL_COL: 1449 return bo.getEntryName(); 1450 case PATH_COLUMN: 1451 return bo.getPathName(); 1452 case DEST_PORTAL_COL: 1453 if (row == _orders.size() - 1) { 1454 return ""; 1455 } 1456 return bo.getExitName(); 1457 default: 1458 // fall through 1459 break; 1460 } 1461 return ""; 1462 } 1463 1464 @Override 1465 public void setValueAt(Object value, int row, int col) { 1466 if (_orders==null) { 1467 return; 1468 } 1469 BlockOrder bo = _orders.get(row); 1470 switch (col) { 1471 case BLOCK_COLUMN: 1472 OBlock block = InstanceManager.getDefault(OBlockManager.class).getOBlock((String) value); 1473 if (block != null) { 1474 bo.setBlock(block); 1475 } 1476 break; 1477 case ENTER_PORTAL_COL: 1478 bo.setEntryName((String) value); 1479 break; 1480 case PATH_COLUMN: 1481 bo.setPathName((String) value); 1482 break; 1483 case DEST_PORTAL_COL: 1484 bo.setExitName((String) value); 1485 break; 1486 default: 1487 // do nothing 1488 } 1489 fireTableRowsUpdated(row, row); 1490 } 1491 } 1492 1493 /** 1494 * Puts label message to the Left 1495 * 1496 * @param vertical Label orientation true = above, false = left 1497 * @param comp Component to put into JPanel 1498 * @param text Bundle keyword for label message 1499 * @param tooltip Bundle keyword for tooltip message 1500 * @return Panel containing Component 1501 */ 1502 protected static JPanel makeTextBoxPanel(boolean vertical, @Nonnull JComponent comp, String text, String tooltip) { 1503 JPanel panel = new JPanel(); 1504 JLabel label = new JLabel(Bundle.getMessage(text)); 1505 if (vertical) { 1506 panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS)); 1507 label.setAlignmentX(Component.CENTER_ALIGNMENT); 1508 comp.setAlignmentX(Component.CENTER_ALIGNMENT); 1509 panel.add(Box.createVerticalStrut(STRUT_SIZE)); 1510 } else { 1511 panel.setLayout(new BoxLayout(panel, BoxLayout.LINE_AXIS)); 1512 label.setAlignmentX(Component.LEFT_ALIGNMENT); 1513 comp.setAlignmentX(Component.RIGHT_ALIGNMENT); 1514 panel.add(Box.createHorizontalStrut(STRUT_SIZE)); 1515 } 1516 panel.add(label); 1517 if (!vertical) { 1518 panel.add(Box.createHorizontalStrut(STRUT_SIZE)); 1519 } 1520 panel.add(comp); 1521 if (vertical) { 1522 panel.add(Box.createVerticalStrut(STRUT_SIZE)); 1523 } else { 1524 panel.add(Box.createHorizontalStrut(STRUT_SIZE)); 1525 } 1526 if (comp instanceof JTextField || comp instanceof JComboBox) { 1527 comp.setBackground(Color.white); 1528 } 1529 if (tooltip != null) { 1530 String tipText = Bundle.getMessage(tooltip); 1531 panel.setToolTipText(tipText); 1532 comp.setToolTipText(tipText); 1533 label.setToolTipText(tipText); 1534 } 1535 panel.setMaximumSize(new Dimension(350, comp.getPreferredSize().height)); 1536 panel.setMinimumSize(new Dimension(80, comp.getPreferredSize().height)); 1537 return panel; 1538 } 1539 1540 /** 1541 * Make a horizontal panel for the input of data 1542 * Puts label message to the Left, 2nd component (button) to the right 1543 * 1544 * @param comp Component for input of data 1545 * @param button 2nd Component for panel, usually a button 1546 * @param label Bundle keyword for label message 1547 * @param tooltip Bundle keyword for tooltip message 1548 * @return Panel containing Components 1549 */ 1550 static protected JPanel makeTextAndButtonPanel(JComponent comp, JComponent button, JLabel label, String tooltip) { 1551 JPanel panel = new JPanel(); 1552 panel.setLayout(new BoxLayout(panel, BoxLayout.LINE_AXIS)); 1553 label.setAlignmentX(JComponent.LEFT_ALIGNMENT); 1554 panel.add(Box.createHorizontalStrut(STRUT_SIZE)); 1555 panel.add(label); 1556 panel.add(Box.createHorizontalStrut(STRUT_SIZE)); 1557 panel.add(Box.createHorizontalGlue()); 1558 1559 comp.setAlignmentX(JComponent.RIGHT_ALIGNMENT); 1560 panel.add(comp); 1561 if (comp instanceof JTextField || comp instanceof JComboBox) { 1562 comp.setBackground(Color.white); 1563 } 1564 panel.add(Box.createHorizontalStrut(STRUT_SIZE)); 1565 button.setAlignmentX(JComponent.RIGHT_ALIGNMENT); 1566 panel.add(button); 1567 panel.add(Box.createHorizontalStrut(STRUT_SIZE)); 1568 1569 if (tooltip != null) { 1570 String tipText = Bundle.getMessage(tooltip); 1571 panel.setToolTipText(tipText); 1572 comp.setToolTipText(tipText); 1573 button.setToolTipText(tipText); 1574 label.setToolTipText(tipText); 1575 } 1576 panel.setMaximumSize(new Dimension(350, comp.getPreferredSize().height)); 1577 panel.setMinimumSize(new Dimension(50, comp.getPreferredSize().height)); 1578 return panel; 1579 } 1580 /** 1581 * Puts label message to the Right 1582 * 1583 * @param comp Component to put into JPanel 1584 * @param label Bundle keyword for label message 1585 * @param tooltip Bundle keyword for tooltip message 1586 * @return Panel containing Component 1587 */ 1588 static protected JPanel makeTextBoxPanel(JComponent comp, String label, String tooltip) { 1589 JPanel panel = new JPanel(); 1590 panel.setLayout(new BoxLayout(panel, BoxLayout.LINE_AXIS)); 1591 panel.add(Box.createHorizontalStrut(STRUT_SIZE)); 1592 comp.setAlignmentX(JComponent.LEFT_ALIGNMENT); 1593 comp.setMaximumSize(new Dimension(300, comp.getPreferredSize().height)); 1594 comp.setMinimumSize(new Dimension(30, comp.getPreferredSize().height)); 1595 panel.add(comp); 1596 if (comp instanceof JTextField || comp instanceof JComboBox) { 1597 comp.setBackground(Color.white); 1598 JLabel l = new JLabel(Bundle.getMessage(label)); 1599 l.setAlignmentX(JComponent.LEFT_ALIGNMENT); 1600 l.setToolTipText(Bundle.getMessage(tooltip)); 1601 panel.add(l); 1602 } else if (comp instanceof AbstractButton) { 1603 ((AbstractButton) comp).setText(Bundle.getMessage(label)); 1604 } 1605 panel.add(Box.createHorizontalStrut(STRUT_SIZE)); 1606 if (tooltip != null) { 1607 String tipText = Bundle.getMessage(tooltip); 1608 panel.setToolTipText(tipText); 1609 comp.setToolTipText(tipText); 1610 } 1611 panel.setMaximumSize(new Dimension(350, comp.getPreferredSize().height)); 1612 panel.setMinimumSize(new Dimension(80, comp.getPreferredSize().height)); 1613 return panel; 1614 } 1615 1616 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(WarrantRoute.class); 1617 1618}