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 if (_routeFinder != null) { 1033 _routeFinder.cancel(); // a previous search must not report after this one 1034 } 1035 _routeFinder = new RouteFinder(this, _origin.getOrder(), _destination.getOrder(), 1036 _via.getOrder(), _avoid.getOrder(), getDepth()); 1037 jmri.util.ThreadingUtil.newThread(_routeFinder).start(); 1038 } 1039 return msg; 1040 } 1041 1042 protected void stopRouteFinder() { 1043 if (_routeFinder != null) { 1044 _routeFinder.quit(); 1045 _routeFinder = null; 1046 } 1047 } 1048 1049 /* *********************************** Route Selection **************************************/ 1050 protected List<BlockOrder> getOrders() { 1051 return _orders; 1052 } 1053 1054 protected BlockOrder getViaBlockOrder() { 1055 return _via.getOrder(); 1056 } 1057 1058 protected BlockOrder getAvoidBlockOrder() { 1059 return _avoid.getOrder(); 1060 } 1061 1062 private Warrant _tempWarrant; // only used in pickRoute() method 1063 1064 protected void clearTempWarrant() { 1065 if (_tempWarrant != null) { 1066 _tempWarrant.deAllocate(); 1067 } 1068 } 1069 1070 private void showTempWarrant(ArrayList<BlockOrder> orders) { 1071 String s = ("" + Math.random()).substring(4); 1072 if (_tempWarrant == null) { 1073 _tempWarrant = new Warrant("IW" + s + "TEMP", null); 1074 _tempWarrant.setBlockOrders(orders); 1075 } 1076 _tempWarrant.setRoute(true, orders); 1077 // Don't clutter with message - this is a temp display 1078 } 1079 1080 /** 1081 * Callback from RouteFinder - several routes found 1082 * 1083 * @param destNodes the destination blocks 1084 * @param routeTree the routes 1085 */ 1086 protected void pickRoute(@Nonnull List<DefaultMutableTreeNode> destNodes, DefaultTreeModel routeTree) { 1087 if (destNodes.size() == 1) { 1088 showRoute(destNodes.get(0), routeTree); 1089 selectedRoute(_orders); 1090 showTempWarrant(_orders); 1091 return; 1092 } 1093 _pickRouteDialog = new JDialog(this, Bundle.getMessage("DialogTitle"), false); 1094 _pickRouteDialog.addWindowListener(new java.awt.event.WindowAdapter() { 1095 @Override 1096 public void windowClosing(java.awt.event.WindowEvent e) { 1097 clearTempWarrant(); 1098 } 1099 }); 1100 _tempWarrant = null; 1101 JPanel mainPanel = new JPanel(); 1102 mainPanel.setLayout(new BorderLayout(5, 5)); 1103 JPanel panel = new JPanel(); 1104 panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); 1105 panel.add(new JLabel(Bundle.getMessage("NumberRoutes1", destNodes.size()))); 1106 panel.add(new JLabel(Bundle.getMessage("NumberRoutes2"))); 1107 JPanel wrapper = new JPanel(); 1108 wrapper.add(panel); 1109 mainPanel.add(wrapper, BorderLayout.NORTH); 1110 ButtonGroup buttons = new ButtonGroup(); 1111 1112 panel = new JPanel(); 1113 panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); 1114 for (int i = 0; i < destNodes.size(); i++) { 1115 JRadioButton button = new JRadioButton(Bundle.getMessage("RouteSize", i + 1, 1116 destNodes.get(i).getLevel() + 1)); 1117 button.setActionCommand("" + i); 1118 button.addActionListener((ActionEvent e) -> clearTempWarrant() ); 1119 buttons.add(button); 1120 panel.add(button); 1121 if (destNodes.size() == 1) { 1122 button.setSelected(true); 1123 } 1124 } 1125 JScrollPane scrollPane = new JScrollPane(panel); 1126 javax.swing.JViewport vp = scrollPane.getViewport(); 1127 JRadioButton button = new JRadioButton(Bundle.getMessage("RouteSize", 000, 000)); 1128 vp.setPreferredSize(new Dimension(button.getWidth(), _depth*button.getHeight())); 1129 mainPanel.add(scrollPane, BorderLayout.CENTER); 1130 1131 JButton ok = new JButton(Bundle.getMessage("ButtonSelect")); 1132 ok.addActionListener(new ActionListener() { 1133 ButtonGroup buts; 1134 JDialog dialog; 1135 List<DefaultMutableTreeNode> dNodes; 1136 DefaultTreeModel tree; 1137 1138 @Override 1139 public void actionPerformed(ActionEvent e) { 1140 if (buts.getSelection() != null) { 1141 clearTempWarrant(); 1142 int i = Integer.parseInt(buttons.getSelection().getActionCommand()); 1143 showRoute(dNodes.get(i), tree); 1144 selectedRoute(_orders); 1145 showTempWarrant(_orders); 1146 dialog.dispose(); 1147 } else { 1148 showWarning(Bundle.getMessage("SelectRoute")); 1149 } 1150 } 1151 1152 ActionListener init(ButtonGroup bg, JDialog d, List<DefaultMutableTreeNode> dn, 1153 DefaultTreeModel t) { 1154 buts = bg; 1155 dialog = d; 1156 dNodes = dn; 1157 tree = t; 1158 return this; 1159 } 1160 }.init(buttons, _pickRouteDialog, destNodes, routeTree)); 1161 ok.setMaximumSize(ok.getPreferredSize()); 1162 JButton show = new JButton(Bundle.getMessage("ButtonReview")); 1163 show.addActionListener(new ActionListener() { 1164 ButtonGroup buts; 1165 List<DefaultMutableTreeNode> destinationNodes; 1166 DefaultTreeModel tree; 1167 1168 @Override 1169 public void actionPerformed(ActionEvent e) { 1170 if (buts.getSelection() != null) { 1171 clearTempWarrant(); 1172 int i = Integer.parseInt(buttons.getSelection().getActionCommand()); 1173 showRoute(destinationNodes.get(i), tree); 1174 showTempWarrant(_orders); 1175 } else { 1176 showWarning(Bundle.getMessage("SelectRoute")); 1177 } 1178 } 1179 1180 ActionListener init(ButtonGroup bg, List<DefaultMutableTreeNode> dn, 1181 DefaultTreeModel t) { 1182 buts = bg; 1183 destinationNodes = dn; 1184 tree = t; 1185 return this; 1186 } 1187 }.init(buttons, destNodes, routeTree)); 1188 show.setMaximumSize(show.getPreferredSize()); 1189 panel = new JPanel(); 1190 panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); 1191 panel.add(Box.createHorizontalGlue()); 1192 panel.add(show); 1193 panel.add(Box.createHorizontalStrut(STRUT_SIZE)); 1194 panel.add(ok); 1195 panel.add(Box.createHorizontalGlue()); 1196 wrapper = new JPanel(); 1197 wrapper.add(panel); 1198 mainPanel.add(wrapper, BorderLayout.SOUTH); 1199 1200 panel = new JPanel(); 1201 panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); 1202 panel.add(Box.createHorizontalStrut(STRUT_SIZE)); 1203 panel.add(makeRouteTablePanel()); 1204 panel.add(Box.createHorizontalStrut(STRUT_SIZE)); 1205 panel.add(mainPanel); 1206 panel.add(Box.createHorizontalStrut(STRUT_SIZE)); 1207 1208 _pickRouteDialog.getContentPane().add(panel); 1209 _pickRouteDialog.setLocation(getLocation().x - 20, getLocation().y + 150); 1210 _pickRouteDialog.pack(); 1211 _pickRouteDialog.setVisible(true); 1212 } 1213 1214 protected void showWarning(String msg) { 1215 JmriJOptionPane.showMessageDialog(this, msg, 1216 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 1217 } 1218 1219 /** 1220 * Callback from RouteFinder - exactly one route found 1221 * 1222 * @param destNode destination block 1223 * @param tree possible routes 1224 */ 1225 private void showRoute(DefaultMutableTreeNode destNode, DefaultTreeModel tree) { 1226 TreeNode[] nodes = tree.getPathToRoot(destNode); 1227 _orders = new ArrayList<>(); 1228 int count = 0; 1229 for (TreeNode node : nodes) { 1230 BlockOrder bo = (BlockOrder) ((DefaultMutableTreeNode) node).getUserObject(); 1231 bo.setIndex(count++); 1232 _orders.add(bo); 1233 } 1234 _routeModel.fireTableDataChanged(); 1235 log.debug("showRoute: Route has {} orders.", _orders.size()); 1236 } 1237 1238 protected JPanel makeRouteTablePanel() { 1239 JTable routeTable = new JTable(_routeModel); 1240 routeTable.setDefaultEditor(JComboBox.class, new jmri.jmrit.symbolicprog.ValueEditor()); 1241 for (int i = 0; i < _routeModel.getColumnCount(); i++) { 1242 int width = _routeModel.getPreferredWidth(i); 1243 routeTable.getColumnModel().getColumn(i).setPreferredWidth(width); 1244 } 1245 JScrollPane tablePane = new JScrollPane(routeTable); 1246 Dimension dim = routeTable.getPreferredSize(); 1247 dim.height = routeTable.getRowHeight() * 11; 1248 tablePane.getViewport().setPreferredSize(dim); 1249 1250 JPanel routePanel = new JPanel(); 1251 routePanel.setLayout(new BoxLayout(routePanel, BoxLayout.Y_AXIS)); 1252 JLabel title = new JLabel(Bundle.getMessage("RouteTableTitle")); 1253 routePanel.add(title, BorderLayout.NORTH); 1254 routePanel.add(tablePane); 1255 routePanel.add(Box.createVerticalGlue()); 1256 return routePanel; 1257 } 1258 1259 /** 1260 * Callback from RouteFinder - no routes found 1261 * 1262 * @param tree routes 1263 * @param origin starting block 1264 * @param dest ending block 1265 */ 1266 protected void debugRoute(DefaultTreeModel tree, BlockOrder origin, BlockOrder dest) { 1267 if (JmriJOptionPane.YES_OPTION != JmriJOptionPane.showConfirmDialog(this, Bundle.getMessage("NoRoute", 1268 new Object[]{origin.getBlock().getDisplayName(), 1269 origin.getPathName(), origin.getExitName(), dest.getBlock().getDisplayName(), 1270 dest.getEntryName(), dest.getPathName(), getDepth()}), 1271 Bundle.getMessage("WarningTitle"), JmriJOptionPane.YES_NO_OPTION, 1272 JmriJOptionPane.WARNING_MESSAGE)) { 1273 return; 1274 } 1275 if (_debugFrame != null) { 1276 _debugFrame.dispose(); 1277 } 1278 _debugFrame = new JFrame(Bundle.getMessage("DebugRoute")); 1279 javax.swing.JTree dTree = new javax.swing.JTree(tree); 1280 dTree.setShowsRootHandles(true); 1281 dTree.setScrollsOnExpand(true); 1282 dTree.setExpandsSelectedPaths(true); 1283 JScrollPane treePane = new JScrollPane(dTree); 1284 treePane.getViewport().setPreferredSize(new Dimension(900, 300)); 1285 _debugFrame.getContentPane().add(treePane); 1286 _debugFrame.setVisible(true); 1287 _debugFrame.pack(); 1288 } 1289 1290 protected void clearRoute() { 1291 _orders = new ArrayList<>(); 1292 clearFrames(); 1293 clearFields(); 1294 _focusedField = _origin; 1295 _routeModel.fireTableDataChanged(); 1296 } 1297 1298 private void clearFrames() { 1299 1300 if (_debugFrame != null) { 1301 _debugFrame.dispose(); 1302 _debugFrame = null; 1303 } 1304 if (_pickRouteDialog != null) { 1305 _pickRouteDialog.dispose(); 1306 _pickRouteDialog = null; 1307 } 1308 closeProfileTable(); 1309 1310 if (_pickListFrame != null) { 1311 _pickListFrame.dispose(); 1312 _pickListFrame = null; 1313 } 1314 } 1315 1316 private void clearFields() { 1317 _origin.clearFields(); 1318 _destination.clearFields(); 1319 _via.clearFields(); 1320 _avoid.clearFields(); 1321 } 1322 1323 protected String routeIsValid() { 1324 if (_orders == null || _orders.isEmpty()) { 1325 return Bundle.getMessage("noBlockOrders"); 1326 } 1327 if (_orders.size() < 2) { 1328 return Bundle.getMessage("NoRouteSet", _origin.getBlockName(), _destination.getBlockName()); 1329 } 1330 BlockOrder blockOrder = _orders.get(0); 1331 String msg = pathIsValid(blockOrder.getBlock(), blockOrder.getPathName()); 1332 if (msg == null) { 1333 for (int i = 1; i < _orders.size(); i++) { 1334 BlockOrder nextBlockOrder = _orders.get(i); 1335 msg = pathIsValid(nextBlockOrder.getBlock(), nextBlockOrder.getPathName()); 1336 if (msg != null) { 1337 return msg; 1338 } 1339 if (!blockOrder.getExitName().equals(nextBlockOrder.getEntryName())) { 1340 return Bundle.getMessage("disconnectedRoute", 1341 blockOrder.getBlock().getDisplayName(), nextBlockOrder.getBlock().getDisplayName()); 1342 } 1343 blockOrder = nextBlockOrder; 1344 } 1345 } 1346 return msg; 1347 } 1348 1349 protected static String pathIsValid(OBlock block, String pathName) { 1350 if (block == null) { 1351 return Bundle.getMessage("PathInvalid", pathName, "null"); 1352 } 1353 List<Path> list = block.getPaths(); 1354 if (list.isEmpty()) { 1355 return Bundle.getMessage("WarningTitle"); 1356 } 1357 if (pathName != null) { 1358 for (int i = 0; i < list.size(); i++) { 1359 OPath path = (OPath) list.get(i); 1360 //if (log.isDebugEnabled()) log.debug("pathIsValid: pathName= "+pathName+", i= "+i+", path is "+path.getName()); 1361 if (pathName.equals(path.getName())) { 1362 if (path.getFromPortal() == null && path.getToPortal() == null) { 1363 return Bundle.getMessage("PathNeedsPortal", pathName, block.getDisplayName()); 1364 } 1365 return null; 1366 } 1367 } 1368 } 1369 return Bundle.getMessage("PathInvalid", pathName, block.getDisplayName()); 1370 } 1371 1372 @Override 1373 public void dispose() { 1374 clearFrames(); 1375 super.dispose(); 1376 } 1377 1378 /* ************************ Route Table ******************************/ 1379 private class RouteTableModel extends AbstractTableModel { 1380 1381 static final int BLOCK_COLUMN = 0; 1382 static final int ENTER_PORTAL_COL = 1; 1383 static final int PATH_COLUMN = 2; 1384 static final int DEST_PORTAL_COL = 3; 1385 static final int NUMCOLS = 4; 1386 1387 RouteTableModel() { 1388 super(); 1389 } 1390 1391 @Override 1392 public int getColumnCount() { 1393 return NUMCOLS; 1394 } 1395 1396 @Override 1397 public int getRowCount() { 1398 if (_orders==null) { 1399 return 0; 1400 } 1401 return _orders.size(); 1402 } 1403 1404 @Override 1405 public String getColumnName(int col) { 1406 switch (col) { 1407 case BLOCK_COLUMN: 1408 return Bundle.getMessage("BlockCol"); 1409 case ENTER_PORTAL_COL: 1410 return Bundle.getMessage("EnterPortalCol"); 1411 case PATH_COLUMN: 1412 return Bundle.getMessage("PathCol"); 1413 case DEST_PORTAL_COL: 1414 return Bundle.getMessage("DestPortalCol"); 1415 default: 1416 // fall through 1417 break; 1418 } 1419 return ""; 1420 } 1421 1422 @Override 1423 public boolean isCellEditable(int row, int col) { 1424 return false; 1425 } 1426 1427 @Override 1428 public Class<?> getColumnClass(int col) { 1429 return String.class; 1430 } 1431 1432 public int getPreferredWidth(int col) { 1433 return new JTextField(15).getPreferredSize().width; 1434 } 1435 1436 @Override 1437 public Object getValueAt(int row, int col) { 1438 // some error checking 1439 if (_orders==null || row >= _orders.size()) { 1440 return ""; 1441 } 1442 BlockOrder bo = _orders.get(row); 1443 // some error checking 1444 if (bo == null) { 1445 log.error("BlockOrder is null"); 1446 return ""; 1447 } 1448 switch (col) { 1449 case BLOCK_COLUMN: 1450 return bo.getBlock().getDisplayName(); 1451 case ENTER_PORTAL_COL: 1452 return bo.getEntryName(); 1453 case PATH_COLUMN: 1454 return bo.getPathName(); 1455 case DEST_PORTAL_COL: 1456 if (row == _orders.size() - 1) { 1457 return ""; 1458 } 1459 return bo.getExitName(); 1460 default: 1461 // fall through 1462 break; 1463 } 1464 return ""; 1465 } 1466 1467 @Override 1468 public void setValueAt(Object value, int row, int col) { 1469 if (_orders==null) { 1470 return; 1471 } 1472 BlockOrder bo = _orders.get(row); 1473 switch (col) { 1474 case BLOCK_COLUMN: 1475 OBlock block = InstanceManager.getDefault(OBlockManager.class).getOBlock((String) value); 1476 if (block != null) { 1477 bo.setBlock(block); 1478 } 1479 break; 1480 case ENTER_PORTAL_COL: 1481 bo.setEntryName((String) value); 1482 break; 1483 case PATH_COLUMN: 1484 bo.setPathName((String) value); 1485 break; 1486 case DEST_PORTAL_COL: 1487 bo.setExitName((String) value); 1488 break; 1489 default: 1490 // do nothing 1491 } 1492 fireTableRowsUpdated(row, row); 1493 } 1494 } 1495 1496 /** 1497 * Puts label message to the Left 1498 * 1499 * @param vertical Label orientation true = above, false = left 1500 * @param comp Component to put into JPanel 1501 * @param text Bundle keyword for label message 1502 * @param tooltip Bundle keyword for tooltip message 1503 * @return Panel containing Component 1504 */ 1505 protected static JPanel makeTextBoxPanel(boolean vertical, @Nonnull JComponent comp, String text, String tooltip) { 1506 JPanel panel = new JPanel(); 1507 JLabel label = new JLabel(Bundle.getMessage(text)); 1508 if (vertical) { 1509 panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS)); 1510 label.setAlignmentX(Component.CENTER_ALIGNMENT); 1511 comp.setAlignmentX(Component.CENTER_ALIGNMENT); 1512 panel.add(Box.createVerticalStrut(STRUT_SIZE)); 1513 } else { 1514 panel.setLayout(new BoxLayout(panel, BoxLayout.LINE_AXIS)); 1515 label.setAlignmentX(Component.LEFT_ALIGNMENT); 1516 comp.setAlignmentX(Component.RIGHT_ALIGNMENT); 1517 panel.add(Box.createHorizontalStrut(STRUT_SIZE)); 1518 } 1519 panel.add(label); 1520 if (!vertical) { 1521 panel.add(Box.createHorizontalStrut(STRUT_SIZE)); 1522 } 1523 panel.add(comp); 1524 if (vertical) { 1525 panel.add(Box.createVerticalStrut(STRUT_SIZE)); 1526 } else { 1527 panel.add(Box.createHorizontalStrut(STRUT_SIZE)); 1528 } 1529 if (comp instanceof JTextField || comp instanceof JComboBox) { 1530 comp.setBackground(Color.white); 1531 } 1532 if (tooltip != null) { 1533 String tipText = Bundle.getMessage(tooltip); 1534 panel.setToolTipText(tipText); 1535 comp.setToolTipText(tipText); 1536 label.setToolTipText(tipText); 1537 } 1538 panel.setMaximumSize(new Dimension(350, comp.getPreferredSize().height)); 1539 panel.setMinimumSize(new Dimension(80, comp.getPreferredSize().height)); 1540 return panel; 1541 } 1542 1543 /** 1544 * Make a horizontal panel for the input of data 1545 * Puts label message to the Left, 2nd component (button) to the right 1546 * 1547 * @param comp Component for input of data 1548 * @param button 2nd Component for panel, usually a button 1549 * @param label Bundle keyword for label message 1550 * @param tooltip Bundle keyword for tooltip message 1551 * @return Panel containing Components 1552 */ 1553 protected static JPanel makeTextAndButtonPanel(JComponent comp, JComponent button, JLabel label, String tooltip) { 1554 JPanel panel = new JPanel(); 1555 panel.setLayout(new BoxLayout(panel, BoxLayout.LINE_AXIS)); 1556 label.setAlignmentX(JComponent.LEFT_ALIGNMENT); 1557 panel.add(Box.createHorizontalStrut(STRUT_SIZE)); 1558 panel.add(label); 1559 panel.add(Box.createHorizontalStrut(STRUT_SIZE)); 1560 panel.add(Box.createHorizontalGlue()); 1561 1562 comp.setAlignmentX(JComponent.RIGHT_ALIGNMENT); 1563 panel.add(comp); 1564 if (comp instanceof JTextField || comp instanceof JComboBox) { 1565 comp.setBackground(Color.white); 1566 } 1567 panel.add(Box.createHorizontalStrut(STRUT_SIZE)); 1568 button.setAlignmentX(JComponent.RIGHT_ALIGNMENT); 1569 panel.add(button); 1570 panel.add(Box.createHorizontalStrut(STRUT_SIZE)); 1571 1572 if (tooltip != null) { 1573 String tipText = Bundle.getMessage(tooltip); 1574 panel.setToolTipText(tipText); 1575 comp.setToolTipText(tipText); 1576 button.setToolTipText(tipText); 1577 label.setToolTipText(tipText); 1578 } 1579 panel.setMaximumSize(new Dimension(350, comp.getPreferredSize().height)); 1580 panel.setMinimumSize(new Dimension(50, comp.getPreferredSize().height)); 1581 return panel; 1582 } 1583 /** 1584 * Puts label message to the Right 1585 * 1586 * @param comp Component to put into JPanel 1587 * @param label Bundle keyword for label message 1588 * @param tooltip Bundle keyword for tooltip message 1589 * @return Panel containing Component 1590 */ 1591 protected static JPanel makeTextBoxPanel(JComponent comp, String label, String tooltip) { 1592 JPanel panel = new JPanel(); 1593 panel.setLayout(new BoxLayout(panel, BoxLayout.LINE_AXIS)); 1594 panel.add(Box.createHorizontalStrut(STRUT_SIZE)); 1595 comp.setAlignmentX(JComponent.LEFT_ALIGNMENT); 1596 comp.setMaximumSize(new Dimension(300, comp.getPreferredSize().height)); 1597 comp.setMinimumSize(new Dimension(30, comp.getPreferredSize().height)); 1598 panel.add(comp); 1599 if (comp instanceof JTextField || comp instanceof JComboBox) { 1600 comp.setBackground(Color.white); 1601 JLabel l = new JLabel(Bundle.getMessage(label)); 1602 l.setAlignmentX(JComponent.LEFT_ALIGNMENT); 1603 l.setToolTipText(Bundle.getMessage(tooltip)); 1604 panel.add(l); 1605 } else if (comp instanceof AbstractButton) { 1606 ((AbstractButton) comp).setText(Bundle.getMessage(label)); 1607 } 1608 panel.add(Box.createHorizontalStrut(STRUT_SIZE)); 1609 if (tooltip != null) { 1610 String tipText = Bundle.getMessage(tooltip); 1611 panel.setToolTipText(tipText); 1612 comp.setToolTipText(tipText); 1613 } 1614 panel.setMaximumSize(new Dimension(350, comp.getPreferredSize().height)); 1615 panel.setMinimumSize(new Dimension(80, comp.getPreferredSize().height)); 1616 return panel; 1617 } 1618 1619 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(WarrantRoute.class); 1620 1621}