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