001package jmri.jmrit.operations.routes.gui;
002
003import java.awt.BorderLayout;
004import java.awt.FlowLayout;
005import java.awt.event.ActionEvent;
006import java.awt.event.ActionListener;
007import java.beans.PropertyChangeEvent;
008import java.beans.PropertyChangeListener;
009import java.util.ArrayList;
010import java.util.List;
011
012import javax.swing.*;
013import javax.swing.colorchooser.AbstractColorChooserPanel;
014import javax.swing.table.DefaultTableCellRenderer;
015import javax.swing.table.TableCellEditor;
016
017import jmri.jmrit.operations.routes.Route;
018import jmri.jmrit.operations.routes.RouteLocation;
019import jmri.jmrit.operations.setup.Control;
020import jmri.jmrit.operations.setup.Setup;
021import jmri.jmrit.symbolicprog.ValueEditor;
022import jmri.jmrit.symbolicprog.ValueRenderer;
023import jmri.util.swing.*;
024import jmri.util.table.ButtonEditor;
025import jmri.util.table.ButtonRenderer;
026
027/**
028 * Table Model for edit of route locations used by operations
029 *
030 * @author Daniel Boudreau Copyright (C) 2008, 2013, 2025
031 */
032public class RouteEditTableModel extends javax.swing.table.AbstractTableModel implements PropertyChangeListener {
033
034    // Defines the columns
035    private static final int ID_COLUMN = 0;
036    private static final int NAME_COLUMN = ID_COLUMN + 1;
037    private static final int TRAIN_DIRECTION_COLUMN = NAME_COLUMN + 1;
038    private static final int MAXMOVES_COLUMN = TRAIN_DIRECTION_COLUMN + 1;
039    private static final int RANDOM_CONTROL_COLUMN = MAXMOVES_COLUMN + 1;
040    private static final int PICKUP_COLUMN = RANDOM_CONTROL_COLUMN + 1;
041    private static final int DROP_COLUMN = PICKUP_COLUMN + 1;
042    private static final int LOCAL_COLUMN = DROP_COLUMN + 1;
043    private static final int DEPARTURE_DAY_COLUMN = LOCAL_COLUMN + 1;
044    private static final int DEPARTURE_TIME_COLUMN = DEPARTURE_DAY_COLUMN + 1;
045    private static final int TRAVEL_COLUMN = DEPARTURE_TIME_COLUMN + 1;
046    private static final int MAXLENGTH_COLUMN = TRAVEL_COLUMN + 1;
047    private static final int GRADE_COLUMN = MAXLENGTH_COLUMN + 1;
048    private static final int TRAINICONX_COLUMN = GRADE_COLUMN + 1;
049    private static final int TRAINICONY_COLUMN = TRAINICONX_COLUMN + 1;
050    private static final int COMMENT_COLUMN = TRAINICONY_COLUMN + 1;
051    private static final int UP_COLUMN = COMMENT_COLUMN + 1;
052    private static final int DOWN_COLUMN = UP_COLUMN + 1;
053    private static final int DELETE_COLUMN = DOWN_COLUMN + 1;
054
055    private static final int HIGHEST_COLUMN = DELETE_COLUMN + 1;
056
057    private JTable _table;
058    private Route _route;
059    private RouteEditFrame _frame;
060    List<RouteLocation> _routeList = new ArrayList<>();
061
062    public RouteEditTableModel() {
063        super();
064    }
065
066    private void updateList() {
067        if (_route == null) {
068            return;
069        }
070        // first, remove listeners from the individual objects
071        removePropertyChangeRouteLocations();
072        _routeList = _route.getLocationsBySequenceList();
073        // and add them back in
074        for (RouteLocation rl : _routeList) {
075            rl.addPropertyChangeListener(this);
076        }
077    }
078
079    protected void initTable(RouteEditFrame frame, JTable table, Route route) {
080        _frame = frame;
081        _table = table;
082        _route = route;
083        if (_route != null) {
084            _route.addPropertyChangeListener(this);
085        }
086        Setup.getDefault().addPropertyChangeListener(this);
087        initTable(table);
088    }
089
090    private void initTable(JTable table) {
091        // Use XTableColumnModel so we can control which columns are visible
092        XTableColumnModel tcm = new XTableColumnModel();
093        _table.setColumnModel(tcm);
094        _table.createDefaultColumnsFromModel();
095        // Install the button handlers
096        ButtonRenderer buttonRenderer = new ButtonRenderer();
097        TableCellEditor buttonEditor = new ButtonEditor(new JButton());
098        tcm.getColumn(COMMENT_COLUMN).setCellRenderer(buttonRenderer);
099        tcm.getColumn(COMMENT_COLUMN).setCellEditor(buttonEditor);
100        tcm.getColumn(UP_COLUMN).setCellRenderer(buttonRenderer);
101        tcm.getColumn(UP_COLUMN).setCellEditor(buttonEditor);
102        tcm.getColumn(DOWN_COLUMN).setCellRenderer(buttonRenderer);
103        tcm.getColumn(DOWN_COLUMN).setCellEditor(buttonEditor);
104        tcm.getColumn(DELETE_COLUMN).setCellRenderer(buttonRenderer);
105        tcm.getColumn(DELETE_COLUMN).setCellEditor(buttonEditor);
106        
107        // for tool tips
108        DefaultTableCellRenderer defaultRenderer = new DefaultTableCellRenderer();
109        tcm.getColumn(NAME_COLUMN).setCellRenderer(defaultRenderer);
110        tcm.getColumn(MAXMOVES_COLUMN).setCellRenderer(defaultRenderer);
111        tcm.getColumn(TRAVEL_COLUMN).setCellRenderer(defaultRenderer);
112        tcm.getColumn(MAXLENGTH_COLUMN).setCellRenderer(defaultRenderer);
113        tcm.getColumn(GRADE_COLUMN).setCellRenderer(defaultRenderer);
114        
115        table.setDefaultRenderer(JComboBox.class, new ValueRenderer());
116        table.setDefaultEditor(JComboBox.class, new ValueEditor());
117
118        // set column preferred widths
119        table.getColumnModel().getColumn(ID_COLUMN).setPreferredWidth(40);
120        table.getColumnModel().getColumn(NAME_COLUMN).setPreferredWidth(150);
121        table.getColumnModel().getColumn(TRAIN_DIRECTION_COLUMN).setPreferredWidth(95);
122        table.getColumnModel().getColumn(MAXMOVES_COLUMN).setPreferredWidth(50);
123        table.getColumnModel().getColumn(RANDOM_CONTROL_COLUMN).setPreferredWidth(65);
124        table.getColumnModel().getColumn(PICKUP_COLUMN).setPreferredWidth(65);
125        table.getColumnModel().getColumn(DROP_COLUMN).setPreferredWidth(65);
126        table.getColumnModel().getColumn(LOCAL_COLUMN).setPreferredWidth(75);
127        table.getColumnModel().getColumn(TRAVEL_COLUMN).setPreferredWidth(65);
128        table.getColumnModel().getColumn(DEPARTURE_DAY_COLUMN).setPreferredWidth(65);
129        table.getColumnModel().getColumn(DEPARTURE_TIME_COLUMN).setPreferredWidth(65);
130        table.getColumnModel().getColumn(MAXLENGTH_COLUMN).setPreferredWidth(75);
131        table.getColumnModel().getColumn(GRADE_COLUMN).setPreferredWidth(50);
132        table.getColumnModel().getColumn(TRAINICONX_COLUMN).setPreferredWidth(35);
133        table.getColumnModel().getColumn(TRAINICONY_COLUMN).setPreferredWidth(35);
134        table.getColumnModel().getColumn(COMMENT_COLUMN).setPreferredWidth(70);
135        table.getColumnModel().getColumn(UP_COLUMN).setPreferredWidth(60);
136        table.getColumnModel().getColumn(DOWN_COLUMN).setPreferredWidth(70);
137        table.getColumnModel().getColumn(DELETE_COLUMN).setPreferredWidth(80);
138
139        _frame.loadTableDetails(table);
140        // does not use a table sorter
141        table.setRowSorter(null);
142
143        // turn on column that on earlier versions was off
144        tcm.setColumnVisible(tcm.getColumnByModelIndex(DEPARTURE_TIME_COLUMN), true);
145
146        updateList();
147    }
148
149    @Override
150    public int getRowCount() {
151        return _routeList.size();
152    }
153
154    @Override
155    public int getColumnCount() {
156        return HIGHEST_COLUMN;
157    }
158
159    @Override
160    public String getColumnName(int col) {
161        switch (col) {
162            case ID_COLUMN:
163                return Bundle.getMessage("Id");
164            case NAME_COLUMN:
165                return Bundle.getMessage("Location");
166            case TRAIN_DIRECTION_COLUMN:
167                return Bundle.getMessage("TrainDirection");
168            case MAXMOVES_COLUMN:
169                return Bundle.getMessage("MaxMoves");
170            case RANDOM_CONTROL_COLUMN:
171                return Bundle.getMessage("Random");
172            case PICKUP_COLUMN:
173                return Bundle.getMessage("Pickups");
174            case DROP_COLUMN:
175                return Bundle.getMessage("Drops");
176            case LOCAL_COLUMN:
177                return Bundle.getMessage("LocalMoves");
178            case TRAVEL_COLUMN:
179                return Bundle.getMessage("Travel");
180            case DEPARTURE_DAY_COLUMN:
181                return Bundle.getMessage("Day");
182            case DEPARTURE_TIME_COLUMN:
183                return Bundle.getMessage("Time");
184            case MAXLENGTH_COLUMN:
185                return Bundle.getMessage("MaxLength");
186            case GRADE_COLUMN:
187                return Bundle.getMessage("Grade");
188            case TRAINICONX_COLUMN:
189                return Bundle.getMessage("X");
190            case TRAINICONY_COLUMN:
191                return Bundle.getMessage("Y");
192            case COMMENT_COLUMN:
193                return Bundle.getMessage("Comment");
194            case UP_COLUMN:
195                return Bundle.getMessage("Up");
196            case DOWN_COLUMN:
197                return Bundle.getMessage("Down");
198            case DELETE_COLUMN:
199                return Bundle.getMessage("ButtonDelete"); // titles above all columns
200            default:
201                return "unknown"; // NOI18N
202        }
203    }
204
205    @Override
206    public Class<?> getColumnClass(int col) {
207        switch (col) {
208            case ID_COLUMN:
209            case NAME_COLUMN:
210                return String.class;
211            case TRAVEL_COLUMN:
212            case MAXLENGTH_COLUMN:
213            case MAXMOVES_COLUMN:
214            case TRAINICONX_COLUMN:
215            case TRAINICONY_COLUMN:
216                return Integer.class;
217            case GRADE_COLUMN:
218                return Double.class;
219            case TRAIN_DIRECTION_COLUMN:
220            case RANDOM_CONTROL_COLUMN:
221            case PICKUP_COLUMN:
222            case DROP_COLUMN:
223            case LOCAL_COLUMN:
224            case DEPARTURE_DAY_COLUMN:
225            case DEPARTURE_TIME_COLUMN:
226                return JComboBox.class;
227            case COMMENT_COLUMN:
228            case UP_COLUMN:
229            case DOWN_COLUMN:
230            case DELETE_COLUMN:
231                return JButton.class;
232            default:
233                return null;
234        }
235    }
236
237    @Override
238    public boolean isCellEditable(int row, int col) {
239        switch (col) {
240            case DELETE_COLUMN:
241            case TRAIN_DIRECTION_COLUMN:
242            case MAXMOVES_COLUMN:
243            case RANDOM_CONTROL_COLUMN:
244            case PICKUP_COLUMN:
245            case DROP_COLUMN:
246            case LOCAL_COLUMN:
247            case TRAVEL_COLUMN:
248            case DEPARTURE_DAY_COLUMN:
249            case DEPARTURE_TIME_COLUMN:
250            case MAXLENGTH_COLUMN:
251            case GRADE_COLUMN:
252            case TRAINICONX_COLUMN:
253            case TRAINICONY_COLUMN:
254            case COMMENT_COLUMN:
255            case UP_COLUMN:
256            case DOWN_COLUMN:
257                return true;
258            default:
259                return false;
260        }
261    }
262
263    @Override
264    public Object getValueAt(int row, int col) {
265        if (row >= getRowCount()) {
266            return "ERROR unknown " + row; // NOI18N
267        }
268        RouteLocation rl = _routeList.get(row);
269        if (rl == null) {
270            return "ERROR unknown route location " + row; // NOI18N
271        }
272        switch (col) {
273            case ID_COLUMN:
274                return rl.getId();
275            case NAME_COLUMN:
276                setLocationToolTip(rl);
277                return rl.getName();
278            case TRAIN_DIRECTION_COLUMN: {
279                JComboBox<String> cb = Setup.getTrainDirectionComboBox();
280                cb.setSelectedItem(rl.getTrainDirectionString());
281                return cb;
282            }
283            case MAXMOVES_COLUMN:
284                setToolTip(Bundle.getMessage("TipColMaxMoves", rl.getName()), col);
285                return rl.getMaxCarMoves();
286            case RANDOM_CONTROL_COLUMN: {
287                JComboBox<String> cb = getRandomControlComboBox();
288                cb.setToolTipText(Bundle.getMessage("TipRandomReduce", rl.getRandomControl()));
289                cb.setSelectedItem(rl.getRandomControl());
290                return cb;
291            }
292            case PICKUP_COLUMN: {
293                JComboBox<String> cb = getYesNoComboBox();
294                cb.setSelectedItem(rl.isPickUpAllowed() ? Bundle.getMessage("yes") : Bundle.getMessage("no"));
295                return cb;
296            }
297            case DROP_COLUMN: {
298                JComboBox<String> cb = getYesNoComboBox();
299                cb.setSelectedItem(rl.isDropAllowed() ? Bundle.getMessage("yes") : Bundle.getMessage("no"));
300                return cb;
301            }
302            case LOCAL_COLUMN: {
303                JComboBox<String> cb = getYesNoComboBox();
304                cb.setSelectedItem(rl.isLocalMovesAllowed() ? Bundle.getMessage("yes") : Bundle.getMessage("no"));
305                return cb;
306            }
307            case TRAVEL_COLUMN: {
308                setToolTip(Bundle.getMessage("TipColTravelTime"), col);
309                return rl.getWait() + Setup.getTravelTime();
310            }
311            case DEPARTURE_DAY_COLUMN: {
312                JComboBox<String> cb = getDayComboBox();
313                cb.setToolTipText(Bundle.getMessage("TipDepartureDay", rl.getName()));
314                cb.setSelectedItem(rl.getDepartureTimeDay());
315                return cb;
316            }
317            case DEPARTURE_TIME_COLUMN: {
318                JComboBox<String> cb = getTimeComboBox();
319                cb.setToolTipText(Bundle.getMessage("TipDepartureTime", rl.getName()));
320                cb.setSelectedItem(rl.getDepartureTimeHourMinutes());
321                return cb;
322            }
323            case MAXLENGTH_COLUMN:
324                setToolTip(Bundle.getMessage("TipColMaxLength", rl.getName(), Setup.getLengthUnit().toLowerCase()), col);
325                return rl.getMaxTrainLength();
326            case GRADE_COLUMN:
327                setToolTip(Bundle.getMessage("TipColGrade", rl.getName()), col);
328                return rl.getGrade();
329            case TRAINICONX_COLUMN:
330                return rl.getTrainIconX();
331            case TRAINICONY_COLUMN:
332                return rl.getTrainIconY();
333            case COMMENT_COLUMN: {
334                if (rl.getComment().equals(RouteLocation.NONE)) {
335                    return Bundle.getMessage("Add");
336                } else {
337                    return Bundle.getMessage("ButtonEdit");
338                }
339            }
340            case UP_COLUMN:
341                return Bundle.getMessage("Up");
342            case DOWN_COLUMN:
343                return Bundle.getMessage("Down");
344            case DELETE_COLUMN:
345                return Bundle.getMessage("ButtonDelete");
346            default:
347                return "unknown " + col; // NOI18N
348        }
349    }
350
351    @Override
352    public void setValueAt(Object value, int row, int col) {
353        if (value == null) {
354            log.debug("Warning route table row {} still in edit", row);
355            return;
356        }
357        RouteLocation rl = _routeList.get(row);
358        if (rl == null) {
359            log.error("ERROR unknown route location for row: {}", row); // NOI18N
360        }
361        switch (col) {
362            case COMMENT_COLUMN:
363                setComment(rl);
364                break;
365            case UP_COLUMN:
366                moveUpRouteLocation(rl);
367                break;
368            case DOWN_COLUMN:
369                moveDownRouteLocation(rl);
370                break;
371            case DELETE_COLUMN:
372                deleteRouteLocation(rl);
373                break;
374            case TRAIN_DIRECTION_COLUMN:
375                setTrainDirection(value, rl);
376                break;
377            case MAXMOVES_COLUMN:
378                setMaxTrainMoves(value, rl);
379                break;
380            case RANDOM_CONTROL_COLUMN:
381                setRandomControlValue(value, rl);
382                break;
383            case PICKUP_COLUMN:
384                setPickup(value, rl);
385                break;
386            case DROP_COLUMN:
387                setDrop(value, rl);
388                break;
389            case LOCAL_COLUMN:
390                setLocal(value, rl);
391                break;
392            case TRAVEL_COLUMN:
393                setTravel(value, rl);
394                break;
395            case DEPARTURE_DAY_COLUMN:
396                setDepartureDay(value, rl);
397                break;
398            case DEPARTURE_TIME_COLUMN:
399                setDepartureTime(value, rl);
400                break;
401            case MAXLENGTH_COLUMN:
402                setMaxTrainLength(value, rl);
403                break;
404            case GRADE_COLUMN:
405                setGrade(value, rl);
406                break;
407            case TRAINICONX_COLUMN:
408                setTrainIconX(value, rl);
409                break;
410            case TRAINICONY_COLUMN:
411                setTrainIconY(value, rl);
412                break;
413            default:
414                break;
415        }
416    }
417
418    private void moveUpRouteLocation(RouteLocation rl) {
419        log.debug("move location up");
420        _route.moveLocationUp(rl);
421    }
422
423    private void moveDownRouteLocation(RouteLocation rl) {
424        log.debug("move location down");
425        _route.moveLocationDown(rl);
426    }
427
428    private void deleteRouteLocation(RouteLocation rl) {
429        log.debug("Delete location");
430        _route.deleteLocation(rl);
431    }
432
433    private int _trainDirection = Setup.getDirectionInt(Setup.getTrainDirectionList().get(0));
434
435    public int getLastTrainDirection() {
436        return _trainDirection;
437    }
438
439    private void setTrainDirection(Object value, RouteLocation rl) {
440        _trainDirection = Setup.getDirectionInt((String) ((JComboBox<?>) value).getSelectedItem());
441        rl.setTrainDirection(_trainDirection);
442        // update train icon
443        rl.setTrainIconCoordinates();
444    }
445
446    private int _maxTrainMoves = Setup.getCarMoves();
447
448    public int getLastMaxTrainMoves() {
449        return _maxTrainMoves;
450    }
451
452    private void setMaxTrainMoves(Object value, RouteLocation rl) {
453        int moves = (int) value;
454        if (moves <= 500) {
455            rl.setMaxCarMoves(moves);
456            _maxTrainMoves = moves;
457        } else {
458            JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("MaximumLocationMoves"), Bundle
459                    .getMessage("CanNotChangeMoves"), JmriJOptionPane.ERROR_MESSAGE);
460        }
461    }
462
463    private void setRandomControlValue(Object value, RouteLocation rl) {
464        rl.setRandomControl((String) ((JComboBox<?>) value).getSelectedItem());
465    }
466
467    private void setDrop(Object value, RouteLocation rl) {
468        rl.setDropAllowed(
469                ((String) ((JComboBox<?>) value).getSelectedItem()).equals(Bundle.getMessage("yes")));
470    }
471
472    private void setPickup(Object value, RouteLocation rl) {
473        rl.setPickUpAllowed(
474                ((String) ((JComboBox<?>) value).getSelectedItem()).equals(Bundle.getMessage("yes")));
475    }
476
477    private void setLocal(Object value, RouteLocation rl) {
478        rl.setLocalMovesAllowed(
479                ((String) ((JComboBox<?>) value).getSelectedItem()).equals(Bundle.getMessage("yes")));
480    }
481
482    private int _maxTrainLength = Setup.getMaxTrainLength();
483
484    public int getLastMaxTrainLength() {
485        return _maxTrainLength;
486    }
487
488    private void setTravel(Object value, RouteLocation rl) {
489        int wait = (int) value;
490        rl.setWait(wait - Setup.getTravelTime());
491    }
492
493    private void setDepartureTime(Object value, RouteLocation rl) {
494        rl.setDepartureTimeHourMinutes((String) ((JComboBox<?>) value).getSelectedItem());
495    }
496    
497    private void setDepartureDay(Object value, RouteLocation rl) {
498        rl.setDepartureTimeDay((String) ((JComboBox<?>) value).getSelectedItem());
499    }
500
501    private void setMaxTrainLength(Object value, RouteLocation rl) {
502        int length = (int) value;
503        if (length < 0) {
504            log.error("Maximum departure length must be a postive number");
505            return;
506        }
507        if (length > Setup.getMaxTrainLength()) {
508            log.error("Maximum departure length can not exceed maximum train length");
509            JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("DepartureLengthNotExceed",
510                    length, Setup.getMaxTrainLength()), Bundle.getMessage("CanNotChangeMaxLength"),
511                    JmriJOptionPane.ERROR_MESSAGE);
512            return;
513        }
514        if (rl != _route.getTerminatesRouteLocation() &&
515                (length < 500 && Setup.getLengthUnit().equals(Setup.FEET) ||
516                        length < 160 && Setup.getLengthUnit().equals(Setup.METER))) {
517            // warn that train length might be too short
518            if (JmriJOptionPane.showConfirmDialog(null, Bundle.getMessage("LimitTrainLength",
519                    length, Setup.getLengthUnit().toLowerCase(), rl.getName()),
520                    Bundle.getMessage("WarningTooShort"),
521                    JmriJOptionPane.OK_CANCEL_OPTION) != JmriJOptionPane.OK_OPTION) {
522                return;
523            }
524        }
525        rl.setMaxTrainLength(length);
526        _maxTrainLength = length;
527    }
528
529    private void setGrade(Object value, RouteLocation rl) {
530        double grade = (Double) value;
531        if (grade <= 6 && grade >= -6) {
532            rl.setGrade(grade);
533        } else {
534            log.error("Maximum grade is 6 percent");
535            JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("MaxGrade"),
536                    Bundle.getMessage("CanNotChangeGrade"),
537                    JmriJOptionPane.ERROR_MESSAGE);
538        }
539    }
540
541    private void setTrainIconX(Object value, RouteLocation rl) {
542        int x = (int) value;
543        rl.setTrainIconX(x);
544    }
545
546    private void setTrainIconY(Object value, RouteLocation rl) {
547        int y = (int) value;
548        rl.setTrainIconY(y);
549    }
550
551    private void setComment(RouteLocation rl) {
552        // Create comment panel
553        final JDialog dialog = new JDialog();
554        dialog.setLayout(new BorderLayout());
555        dialog.setTitle(Bundle.getMessage("Comment") + " " + rl.getName());
556        final JTextArea commentTextArea = new JTextArea(5, 100);
557        JScrollPane commentScroller = new JScrollPane(commentTextArea);
558        dialog.add(commentScroller, BorderLayout.CENTER);
559        commentTextArea.setText(rl.getComment());
560
561        JPanel buttonPane = new JPanel();
562        buttonPane.setLayout(new FlowLayout(FlowLayout.CENTER));
563        dialog.add(buttonPane, BorderLayout.SOUTH);
564
565        // text color chooser
566        JPanel pTextColor = new JPanel();
567        pTextColor.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("TextColor")));
568        JColorChooser commentColorChooser = new JColorChooser(rl.getCommentColor());
569        AbstractColorChooserPanel commentColorPanels[] = {new SplitButtonColorChooserPanel()};
570        commentColorChooser.setChooserPanels(commentColorPanels);
571        commentColorChooser.setPreviewPanel(new JPanel());
572        pTextColor.add(commentColorChooser);
573        buttonPane.add(pTextColor);
574
575        JButton okayButton = new JButton(Bundle.getMessage("ButtonOK"));
576        okayButton.addActionListener(new ActionListener() {
577            @Override
578            public void actionPerformed(ActionEvent arg0) {
579                rl.setComment(commentTextArea.getText());
580                rl.setCommentColor(commentColorChooser.getColor());
581                dialog.dispose();
582                return;
583            }
584        });
585        buttonPane.add(okayButton);
586
587        JButton cancelButton = new JButton(Bundle.getMessage("ButtonCancel"));
588        cancelButton.addActionListener(new ActionListener() {
589            @Override
590            public void actionPerformed(ActionEvent arg0) {
591                dialog.dispose();
592                return;
593            }
594        });
595        buttonPane.add(cancelButton);
596
597        dialog.setModal(true);
598        dialog.pack();
599        dialog.setVisible(true);
600    }
601
602    private JComboBox<String> getYesNoComboBox() {
603        JComboBox<String> cb = new JComboBox<>();
604        cb.addItem(Bundle.getMessage("yes"));
605        cb.addItem(Bundle.getMessage("no"));
606        return cb;
607    }
608
609    private JComboBox<String> getRandomControlComboBox() {
610        JComboBox<String> cb = new JComboBox<>();
611        cb.addItem(RouteLocation.DISABLED);
612        // 10 to 100 by 10
613        for (int i = 10; i < 101; i = i + 10) {
614            cb.addItem(Integer.toString(i));
615        }
616        return cb;
617    }
618
619    protected JComboBox<String> getTimeComboBox() {
620        JComboBox<String> timeBox = new JComboBox<>();
621        timeBox.addItem("");
622        for (int i = 0; i < 24; i++) {
623            String hour = String.format("%02d", i);
624            for (int j = 0; j < 60; j++) {
625                String minute = String.format("%02d", j);
626                timeBox.addItem(hour + ":" + minute);
627            }
628        }
629        return timeBox;
630    }
631    
632    protected JComboBox<String> getDayComboBox() {
633        JComboBox<String> dayBox = new JComboBox<>();
634        for (int i = 0; i < 32; i++) {
635            dayBox.addItem(Integer.toString(i));
636        }
637        return dayBox;
638    }
639
640    private void setLocationToolTip(RouteLocation rl) {
641        String text = Bundle.getMessage("TipTrainDirection", rl.getName(), rl.getTrainDirectionString());
642        if (rl == _route.getTerminatesRouteLocation()) {
643            text = Bundle.getMessage("TipTrainTerminates", rl.getName());
644        }
645        setToolTip(text, NAME_COLUMN);
646    }
647    
648    private void setToolTip(String text, int col) {
649        XTableColumnModel tcm = (XTableColumnModel) _table.getColumnModel();
650        JComponent jC = (JComponent) tcm.getColumnByModelIndex(col).getCellRenderer();
651        if (jC != null) {
652            jC.setToolTipText(text);
653        }
654    }
655
656    // this table listens for changes to a route and it's locations
657    @Override
658    public void propertyChange(PropertyChangeEvent e) {
659        if (Control.SHOW_PROPERTY) {
660            log.debug("Property change: ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(), e
661                    .getNewValue());
662        }
663        if (e.getPropertyName().equals(Route.LISTCHANGE_CHANGED_PROPERTY)) {
664            updateList();
665            fireTableDataChanged();
666        }
667        if (e.getPropertyName().equals(Setup.TRAIN_DIRECTION_PROPERTY_CHANGE) ||
668                e.getPropertyName().equals(Setup.TRAVEL_TIME_PROPERTY_CHANGE)) {
669            fireTableDataChanged();
670        }
671        if (e.getSource().getClass().equals(RouteLocation.class)) {
672            RouteLocation rl = (RouteLocation) e.getSource();
673            int row = _routeList.indexOf(rl);
674            if (Control.SHOW_PROPERTY) {
675                log.debug("Update route table row: {} id: {}", row, rl.getId());
676            }
677            if (row >= 0) {
678                fireTableRowsUpdated(row, row);
679            }
680        }
681    }
682
683    private void removePropertyChangeRouteLocations() {
684        for (RouteLocation rl : _routeList) {
685            rl.removePropertyChangeListener(this);
686        }
687    }
688
689    public void dispose() {
690        removePropertyChangeRouteLocations();
691        if (_route != null) {
692            _route.removePropertyChangeListener(this);
693        }
694        Setup.getDefault().removePropertyChangeListener(this);
695        _routeList.clear();
696        fireTableDataChanged();
697    }
698
699    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(RouteEditTableModel.class);
700}