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}