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