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