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