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