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