001package jmri.jmrit.logix; 002 003 004import java.awt.BorderLayout; 005import java.awt.Color; 006import java.awt.Dimension; 007import java.awt.FlowLayout; 008import java.awt.event.ActionEvent; 009import java.beans.PropertyChangeListener; 010import java.util.ArrayList; 011import java.util.HashMap; 012import java.util.List; 013import java.util.Set; 014 015import javax.annotation.Nonnull; 016import javax.swing.AbstractAction; 017import javax.swing.AbstractListModel; 018import javax.swing.Box; 019import javax.swing.BoxLayout; 020import javax.swing.ButtonGroup; 021import javax.swing.JButton; 022import javax.swing.JDialog; 023import javax.swing.JLabel; 024import javax.swing.JList; 025import javax.swing.JMenu; 026import javax.swing.JMenuBar; 027import javax.swing.JMenuItem; 028import javax.swing.JPanel; 029import javax.swing.JRadioButtonMenuItem; 030import javax.swing.JScrollPane; 031import javax.swing.JTable; 032import javax.swing.JTextField; 033import javax.swing.event.ListSelectionEvent; 034import javax.swing.event.ListSelectionListener; 035import javax.swing.table.AbstractTableModel; 036import javax.swing.table.TableRowSorter; 037 038import jmri.Block; 039import jmri.InstanceInitializer; 040import jmri.InstanceManager; 041import jmri.JmriException; 042import jmri.implementation.AbstractInstanceInitializer; 043import jmri.jmrit.display.LocoIcon; 044import jmri.jmrit.display.palette.ItemPalette; 045import jmri.jmrit.picker.PickListModel; 046import jmri.jmrit.picker.PickPanel; 047import jmri.util.JmriJFrame; 048import jmri.util.swing.JmriJOptionPane; 049import jmri.util.swing.JmriMouseEvent; 050import jmri.util.swing.JmriMouseListener; 051import jmri.util.table.ButtonEditor; 052import jmri.util.table.ButtonRenderer; 053 054import org.openide.util.lookup.ServiceProvider; 055 056/** 057 * This class displays a table of the occupancy detection trackers. It does 058 * the listening of block sensors for all the Trackers and chooses the tracker most 059 * likely to have entered a block becoming active or leaving a block when it 060 * becomes inactive. 061 * 062 * @author Peter Cressman 063 */ 064public class TrackerTableAction extends AbstractAction implements PropertyChangeListener{ 065 066 protected static final int STRUT_SIZE = 10; 067 068 private final ArrayList<Tracker> _trackerList = new ArrayList<>(); 069 private final HashMap<OBlock, ArrayList<Tracker>> _trackerBlocks = new HashMap<>(); 070 protected TableFrame _frame; 071 private ChooseTracker _trackerChooser; 072 private boolean _requirePaths; 073 074 private TrackerTableAction(String menuOption) { 075 super(menuOption); 076 } 077 078 @Override 079 public void actionPerformed(ActionEvent e) { 080 if (_frame != null) { 081 _frame.setVisible(true); 082 } else { 083 _frame = new TableFrame(); 084 } 085 } 086 087 public synchronized boolean mouseClickedOnBlock(OBlock block) { 088 if (_frame != null) { 089 return _frame.mouseClickedOnBlock(block); 090 } 091 return false; 092 } 093 094 /** 095 * Create and register a new Tracker. 096 * @param block starting head block of the Tracker 097 * @param name name of the Tracker 098 * @param marker LocoIcon dropped on the block (optional) 099 * @return true if successfully created. 100 */ 101 public boolean markNewTracker(OBlock block, String name, LocoIcon marker) { 102 if (_frame == null) { 103 _frame = new TableFrame(); 104 } 105 if (name == null && marker != null) { 106 name = marker.getUnRotatedText(); 107 } 108 return makeTracker(block, name, marker); 109 } 110 111 private boolean makeTracker(OBlock block, String name, LocoIcon marker) { 112 String msg = null; 113 114 if ((block.getState() & Block.OCCUPIED) == 0) { 115 msg = Bundle.getMessage("blockUnoccupied", block.getDisplayName()); 116 } else if (name == null || name.length() == 0) { 117 msg = Bundle.getMessage("noTrainName"); 118 } else if (nameInuse(name)) { 119 msg = Bundle.getMessage("duplicateName", name); 120 } else { 121 Tracker t = findTrackerIn(block); 122 if (t != null && !name.equals(block.getValue())) { 123 msg = Bundle.getMessage("blockInUse", t.getTrainName(), block.getDisplayName()); 124 } else { 125 Warrant w = block.getWarrant(); 126 if (w != null) { 127 msg = Bundle.getMessage("AllocatedToWarrant", 128 w.getDisplayName(), block.getDisplayName(), w.getTrainName()); 129 } 130 } 131 } 132 if (msg != null) { 133 JmriJOptionPane.showMessageDialog(_frame, msg, 134 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 135 return false; 136 } 137 block.setValue(name); 138 new Tracker(block, name, marker, this); 139 return true; 140 } 141 142 protected void addTracker(Tracker t) { 143 synchronized(this) { 144 _trackerList.add(t); 145 } 146 addBlockListeners(t); 147 if (_frame == null) { 148 _frame = new TableFrame(); 149 } 150 _frame._model.fireTableDataChanged(); 151 _frame.setStatus(Bundle.getMessage("startTracker", 152 t.getTrainName(), t.getHeadBlock().getDisplayName())); 153 } 154 155 protected boolean checkBlock(OBlock b) { 156 if (findTrackerIn(b) == null && b.getWarrant() == null) { 157 b.setValue(null); 158 return true; 159 } 160 return false; 161 } 162 163 boolean nameInuse(String name) { 164 for (Tracker t : _trackerList) { 165 if (name.equals(t.getTrainName())) { 166 return true; 167 } 168 } 169 return false; 170 } 171 172 /** 173 * Stop a Tracker from tracking and remove from list 174 * @param t Tracker to be stopped 175 * @param b Block Tracker of its last move. Optional, for display purpose only. 176 */ 177 public void stopTracker(Tracker t, OBlock b) { 178 if (_frame == null) { 179 _frame = new TableFrame(); 180 } 181 stopTrain(t, b); 182 } 183 184 protected void setStatus(String msg) { 185 _frame.setStatus(msg); 186 } 187 188 /** 189 * See if any Trackers are occupying a given block. 190 * @param b Block being queried 191 * @return Tracker if found 192 */ 193 public Tracker findTrackerIn(OBlock b) { 194 for (Tracker t : _trackerList) { 195 if (t.getBlocksOccupied().contains(b)) { 196 return t; 197 } 198 } 199 return null; 200 } 201 202 public void updateStatus() { 203 _frame._model.fireTableDataChanged(); 204 205 } 206 /** 207 * Adds listeners to all blocks in the range of a Tracker. Called when a 208 * new tracker is created. 209 * @param tracker Tracker that is about to start 210 */ 211 protected void addBlockListeners(Tracker tracker) { 212 List<OBlock> range = tracker.makeRange(); 213 for (OBlock oBlock : range) { 214 addBlockListener(oBlock, tracker); 215 } 216 } 217 218 /** 219 * Adds listener to a block when a tracker enters. 220 */ 221 private void addBlockListener(OBlock block, Tracker tracker) { 222 ArrayList<Tracker> trackers = _trackerBlocks.get(block); 223 if (trackers == null) { 224 trackers = new ArrayList<>(); 225 trackers.add(tracker); 226 if ((block.getState() & Block.UNDETECTED) == 0) { 227 _trackerBlocks.put(block, trackers); 228 block.addPropertyChangeListener(this); 229 } 230 } else { 231 if (trackers.isEmpty()) { 232 if ((block.getState() & Block.UNDETECTED) == 0) { 233 block.addPropertyChangeListener(this); 234 } 235 } 236 if (!trackers.contains(tracker)) { 237 trackers.add(tracker); 238 } 239 } 240 } 241 242 /** 243 * Do Venn Diagram between the two sets. Keep listeners held in common. 244 * Add new listeners. Remove old. 245 */ 246 private void adjustBlockListeners(List<OBlock> oldRange, List<OBlock> newRange, Tracker tracker) { 247 for (OBlock b : newRange) { 248 if (oldRange.contains(b)) { 249 oldRange.remove(b); 250 continue; // held in common. keep listener 251 } 252 addBlockListener(b, tracker); // new block. Add Listener 253 } 254 // blocks left in oldRange that were not found in newRange. Remove Listeners 255 for (OBlock b :oldRange) { 256 removeBlockListener(b, tracker); 257 } 258 259 } 260 261 protected void removeBlockListeners(Tracker tracker) { 262 for (OBlock block : _trackerBlocks.keySet()) { 263 removeBlockListener(block, tracker); 264 } 265 } 266 267 private void removeBlockListener(OBlock block, Tracker tracker) { 268 List<Tracker> trackers = _trackerBlocks.get(block); 269 if (trackers != null) { 270 trackers.remove(tracker); 271 if (trackers.isEmpty()) { 272 block.removePropertyChangeListener(this); 273 } 274 } 275 } 276 277 @Override 278 public synchronized void propertyChange(java.beans.PropertyChangeEvent evt) { 279 if (evt.getPropertyName().equals("state")) { 280 OBlock block = (OBlock) evt.getSource(); 281 int state = ((Number) evt.getNewValue()).intValue(); 282 int oldState = ((Number) evt.getOldValue()).intValue(); 283 // The "jiggle" (see tracker.showBlockValue() causes some state changes to be duplicated. 284 // The following washes out the extra notifications 285 if ((state & Block.UNOCCUPIED) == (oldState & Block.UNOCCUPIED) 286 && (state & Block.OCCUPIED) == (oldState & Block.OCCUPIED)) { 287 return; 288 } 289 ArrayList<Tracker> trackerListeners = _trackerBlocks.get(block); 290 if (trackerListeners == null || trackerListeners.isEmpty()) { 291 log.error("No Trackers found for block \"{}\" going to state= {}", 292 block.getDisplayName(), state); 293 block.removePropertyChangeListener(this); 294 return; 295 } 296 if ((state & Block.OCCUPIED) != 0) { // going occupied 297 List<Tracker> trackers = getAvailableTrackers(block); 298 if (trackers.isEmpty()) { 299 return; 300 } 301 if (trackers.size() > 1) { // if several trackers listen for this block, user must identify which one. 302 if (_trackerChooser != null) { 303 _trackerChooser.dispose(); 304 } 305 java.awt.Toolkit.getDefaultToolkit().beep(); 306 _trackerChooser = new ChooseTracker(block, trackers, state); 307 return; 308 } 309 310 Tracker tracker = trackers.get(0); 311 if (block.getValue() != null && !block.getValue().equals(tracker.getTrainName())) { 312 log.error("Block \"{} \" going active with value= {} for Tracker {}! Who/What is \"{}\"?", 313 block.getDisplayName(), block.getValue(), tracker.getTrainName(), block.getValue()); 314 return; 315 } else { 316 if (!_requirePaths) { 317 try { 318 tracker.hasPathInto(block); 319 } catch (JmriException je) { 320 log.error("Exception handling {} {}", tracker.getTrainName(), je.getMessage()); 321 return; 322 } 323 } 324 processTrackerStateChange(tracker, block, state); 325 } 326 } else if ((state & Block.UNOCCUPIED) != 0) { 327 if (_trackerChooser != null) { 328 _trackerChooser.checkClose(block); 329 } 330 for (Tracker t : trackerListeners) { 331 if (t.getBlocksOccupied().contains(block)) { 332 processTrackerStateChange(t, block, state); 333 break; 334 } 335 } 336 } 337 } 338 _frame._model.fireTableDataChanged(); 339 } 340 341 private List<Tracker> getAvailableTrackers(OBlock block) { 342 List<Tracker> trackers = new ArrayList<>(); 343 ArrayList<Tracker> trackerListeners = _trackerBlocks.get(block); 344 if (_requirePaths) { 345 ArrayList<Tracker> partials = new ArrayList<>(); 346 // filter for trackers with paths set into block 347 for (Tracker t : trackerListeners) { 348 try { 349 switch (t.hasPathInto(block)) { 350 case SET: 351 trackers.add(t); 352 break; 353 case PARTIAL: 354 partials.add(t); 355 break; 356 default: 357 break; 358 } 359 } catch (JmriException je) { 360 log.error("train: {} {}", t.getTrainName(), je.getMessage()); 361 } 362 } 363 if (trackers.isEmpty()) { // nobody has paths set. 364 // even so, likely to be possible for somebody to get there 365 if (!partials.isEmpty()) { 366 trackers = partials; // OK, maybe not all switches are lined up 367 } else { 368 trackers = trackerListeners; // maybe even this bad. 369 } 370 } 371 } else { 372 trackers = trackerListeners; 373 } 374 return trackers; 375 } 376 /** 377 * Called when a state change has occurred for one the blocks listened 378 * to for this tracker. Tracker.move makes the changes to OBlocks to 379 * indicate the new occupancy positions of the train. Upon return, 380 * update the listeners for the trains next move. 381 */ 382 private synchronized void processTrackerStateChange(Tracker tracker, OBlock block, int state) { 383 List<OBlock> oldRange = tracker.makeRange();// total range in effect when state change was detected 384 if (tracker.move(block, state)) { // new total range has been made after move was done. 385 if (tracker._statusMessage != null) { 386 _frame.setStatus(tracker._statusMessage); 387 } else { 388 block._entryTime = System.currentTimeMillis(); 389 adjustBlockListeners(oldRange, tracker.makeRange(), tracker); 390 _frame.setStatus(Bundle.getMessage("TrackerBlockEnter", 391 tracker.getTrainName(), block.getDisplayName())); 392 } 393 } else { 394 if (tracker._statusMessage != null) { 395 _frame.setStatus(tracker._statusMessage); 396 } else if (_trackerList.contains(tracker)) { 397 adjustBlockListeners(oldRange, tracker.makeRange(), tracker); 398 long et = (System.currentTimeMillis() - block._entryTime) / 1000; 399 _frame.setStatus(Bundle.getMessage("TrackerBlockLeave", tracker.getTrainName(), 400 block.getDisplayName(), et / 60, et % 60)); 401 } 402 } 403 } 404 405 private void stopTrain(Tracker t, OBlock b) { 406 t.stop(); 407 removeBlockListeners(t); 408 synchronized(this) { 409 _trackerList.remove(t); 410 } 411 long et = (System.currentTimeMillis() - t._startTime) / 1000; 412 String location; 413 if (b!= null) { 414 location = b.getDisplayName(); 415 } else { 416 location = Bundle.getMessage("BeanStateUnknown"); 417 } 418 _frame.setStatus(Bundle.getMessage("TrackerStopped", 419 t.getTrainName(), location, et / 60, et % 60)); 420 _frame._model.fireTableDataChanged(); 421 } 422 423 class ChooseTracker extends JDialog implements ListSelectionListener { 424 OBlock block; 425 List<Tracker> trackers; 426 int state; 427 JList<Tracker> _jList; 428 429 ChooseTracker(OBlock b, List<Tracker> ts, int s) { 430 super(_frame); 431 setTitle(Bundle.getMessage("TrackerTitle")); 432 block = b; 433 trackers = ts; 434 state = s; 435 JPanel contentPanel = new JPanel(); 436 contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS)); 437 438 contentPanel.add(Box.createVerticalStrut(STRUT_SIZE)); 439 JPanel panel = new JPanel(); 440 panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); 441 panel.add(new JLabel(Bundle.getMessage("MultipleTrackers", block.getDisplayName()))); 442 panel.add(new JLabel(Bundle.getMessage("ChooseTracker", block.getDisplayName()))); 443 JPanel p = new JPanel(); 444 p.add(panel); 445 contentPanel.add(p); 446 panel = new JPanel(); 447 panel.setBorder(javax.swing.BorderFactory .createLineBorder(Color.black, 2)); 448 _jList = new JList<>(); 449 _jList.setModel(new TrackerListModel()); 450 _jList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); 451 _jList.addListSelectionListener(ChooseTracker.this); 452 panel.add(_jList); 453 p = new JPanel(); 454 p.add(panel); 455 contentPanel.add(p); 456 457 contentPanel.add(Box.createVerticalStrut(STRUT_SIZE)); 458 panel = new JPanel(); 459 JButton cancelButton = new JButton(Bundle.getMessage("ButtonCancel")); 460 cancelButton.addActionListener((ActionEvent a) -> dispose()); 461 panel.add(cancelButton); 462 463 contentPanel.add(panel); 464 setContentPane(contentPanel); 465 pack(); 466 setLocation(_frame.getLocation()); 467 setAlwaysOnTop(true); 468 setVisible(true); 469 } 470 471 @Override 472 public void valueChanged(ListSelectionEvent e) { 473 Tracker tr = _jList.getSelectedValue(); 474 if (tr != null) { 475 processTrackerStateChange(tr, block, state); 476 dispose(); 477 } 478 } 479 480 void checkClose(OBlock b) { 481 if (block.equals(b)) { 482 dispose(); 483 } 484 } 485 486 class TrackerListModel extends AbstractListModel<Tracker> { 487 @Override 488 public int getSize() { 489 return trackers.size(); 490 } 491 492 @Override 493 public Tracker getElementAt(int index) { 494 return trackers.get(index); 495 } 496 } 497 } 498 499 /** 500 * Holds a table of Trackers that follow adjacent occupancy. Needs to be a 501 * singleton to be opened and closed for trackers to report to it. 502 * 503 * @author Peter Cressman 504 */ 505 class TableFrame extends JmriJFrame implements JmriMouseListener { 506 507 private final TrackerTableModel _model; 508 private JmriJFrame _pickFrame; 509 JDialog _dialog; 510 JTextField _trainNameBox = new JTextField(30); 511 JTextField _trainLocationBox = new JTextField(30); 512 JTextField _status = new JTextField(80); 513 ArrayList<String> _statusHistory = new ArrayList<>(); 514 public int _maxHistorySize = 20; 515 516 TableFrame() { 517 super(true, true); 518 setTitle(Bundle.getMessage("TrackerTable")); 519 _model = new TrackerTableModel(); 520 JTable table = new JTable(_model); 521 TableRowSorter<TrackerTableModel> sorter = new TableRowSorter<>(_model); 522 table.setRowSorter(sorter); 523 table.getColumnModel().getColumn(TrackerTableModel.STOP_COL).setCellEditor(new ButtonEditor(new JButton())); 524 table.getColumnModel().getColumn(TrackerTableModel.STOP_COL).setCellRenderer(new ButtonRenderer()); 525 for (int i = 0; i < _model.getColumnCount(); i++) { 526 int width = _model.getPreferredWidth(i); 527 table.getColumnModel().getColumn(i).setPreferredWidth(width); 528 } 529 table.setDragEnabled(true); 530 table.setTransferHandler(new jmri.util.DnDTableExportHandler()); 531 JScrollPane tablePane = new JScrollPane(table); 532 Dimension dim = table.getPreferredSize(); 533 int height = new JButton("STOPIT").getPreferredSize().height; 534 dim.height = height * 2; 535 tablePane.getViewport().setPreferredSize(dim); 536 537 JPanel tablePanel = new JPanel(); 538 tablePanel.setLayout(new BoxLayout(tablePanel, BoxLayout.Y_AXIS)); 539 JLabel title = new JLabel(Bundle.getMessage("TrackerTable")); 540 tablePanel.add(title, BorderLayout.NORTH); 541 tablePanel.add(tablePane, BorderLayout.CENTER); 542 543 JPanel panel = new JPanel(); 544 JPanel p = new JPanel(); 545 p.add(new JLabel(Bundle.getMessage("lastEvent"))); 546 p.add(_status); 547 _status.setEditable(false); 548 _status.setBackground(Color.white); 549 _status.addMouseListener(JmriMouseListener.adapt(TableFrame.this)); 550 panel.add(p); 551 552 tablePanel.add(makeButtonPanel(), BorderLayout.CENTER); 553 tablePanel.add(panel, BorderLayout.CENTER); 554 555 setContentPane(tablePanel); 556 557 JMenuBar menuBar = new JMenuBar(); 558 JMenu optionMenu = new JMenu(Bundle.getMessage("MenuMoreOptions")); 559 optionMenu.add(makePathRequirement()); 560 561 JMenuItem pickerMenu = new JMenuItem(Bundle.getMessage("MenuBlockPicker")); 562 pickerMenu.addActionListener((ActionEvent a) -> openPickList()); 563 optionMenu.add(pickerMenu); 564 565 optionMenu.add(WarrantTableAction.getDefault().makeLogMenu()); 566 menuBar.add(optionMenu); 567 setJMenuBar(menuBar); 568 addHelpMenu("package.jmri.jmrit.logix.Tracker", true); 569 570 addWindowListener(new java.awt.event.WindowAdapter() { 571 @Override 572 public void windowClosing(java.awt.event.WindowEvent e) { 573 setDefaultCloseOperation(javax.swing.WindowConstants.HIDE_ON_CLOSE); 574 if (_pickFrame != null) { 575 _pickFrame.dispose(); 576 } 577 _model.fireTableDataChanged(); 578 } 579 }); 580 setLocation(0, 100); 581 setVisible(true); 582 pack(); 583 } 584 585 private JPanel makeButtonPanel() { 586 JPanel panel = new JPanel(); 587 JButton button = new JButton(Bundle.getMessage("MenuNewTracker", "...")); 588 button.addActionListener((ActionEvent a) -> newTrackerDialog()); 589 panel.add(button); 590 591 button = new JButton(Bundle.getMessage("MenuRefresh")); 592 button.addActionListener((ActionEvent a) -> _model.fireTableDataChanged()); 593 panel.add(button); 594 595 return panel; 596 } 597 598 599 600 private JMenuItem makePathRequirement() { 601 JMenu pathkMenu = new JMenu(Bundle.getMessage("MenuPathRanking")); 602 ButtonGroup pathButtonGroup = new ButtonGroup(); 603 JRadioButtonMenuItem r; 604 r = new JRadioButtonMenuItem(Bundle.getMessage("showAllTrackers")); 605 r.addActionListener((ActionEvent e) -> _requirePaths = false); 606 pathButtonGroup.add(r); 607 r.setSelected(!_requirePaths); 608 pathkMenu.add(r); 609 610 r = new JRadioButtonMenuItem(Bundle.getMessage("showMostLikely")); 611 r.addActionListener((ActionEvent e) -> _requirePaths = true); 612 pathButtonGroup.add(r); 613 r.setSelected(_requirePaths); 614 pathkMenu.add(r); 615 616 return pathkMenu; 617 } 618 619 protected boolean mouseClickedOnBlock(OBlock block) { 620 if (_dialog != null) { 621 if ((block.getState() & Block.OCCUPIED) != 0 && block.getValue() != null) { 622 markNewTracker(block, (String) block.getValue(), null); 623 return true; 624 } 625 _trainLocationBox.setText(block.getDisplayName()); 626 if (block.getValue() != null) { 627 _trainNameBox.setText((String) block.getValue()); 628 } 629 return true; 630 } 631 return false; 632 } 633 634 private void newTrackerDialog() { 635 _dialog = new JDialog(this, Bundle.getMessage("MenuNewTracker", ""), false); 636 JPanel panel = new JPanel(); 637 panel.setLayout(new BorderLayout(10, 10)); 638 JPanel mainPanel = new JPanel(); 639 mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS)); 640 641 mainPanel.add(Box.createVerticalStrut(STRUT_SIZE)); 642 JPanel p = new JPanel(); 643 p.add(new JLabel(Bundle.getMessage("createTracker"))); 644 mainPanel.add(p); 645 646 mainPanel.add(Box.createVerticalStrut(STRUT_SIZE)); 647 mainPanel.add(makeTrackerNamePanel()); 648 mainPanel.add(Box.createVerticalStrut(STRUT_SIZE)); 649 mainPanel.add(makeDoneButtonPanel()); 650 panel.add(mainPanel); 651 _dialog.getContentPane().add(panel); 652 _dialog.setLocation(this.getLocation().x + 100, this.getLocation().y + 100); 653 _dialog.pack(); 654 _dialog.setVisible(true); 655 } 656 657 private JPanel makeTrackerNamePanel() { 658 _trainNameBox.setText(""); 659 _trainLocationBox.setText(""); 660 JPanel namePanel = new JPanel(); 661 namePanel.setLayout(new BoxLayout(namePanel, BoxLayout.Y_AXIS)); 662 JPanel p = new JPanel(); 663 p.setLayout(new java.awt.GridBagLayout()); 664 java.awt.GridBagConstraints c = new java.awt.GridBagConstraints(); 665 c.gridwidth = 1; 666 c.gridheight = 1; 667 c.gridx = 0; 668 c.gridy = 0; 669 c.anchor = java.awt.GridBagConstraints.EAST; 670 p.add(new JLabel(Bundle.getMessage("TrainName")), c); 671 c.gridy = 1; 672 p.add(new JLabel(Bundle.getMessage("TrainLocation")), c); 673 c.gridx = 1; 674 c.gridy = 0; 675 c.anchor = java.awt.GridBagConstraints.WEST; 676 c.weightx = 1.0; 677 c.fill = java.awt.GridBagConstraints.HORIZONTAL; // text field will expand 678 p.add(_trainNameBox, c); 679 _trainNameBox.setToolTipText(Bundle.getMessage("TrackerNameTip")); 680 c.gridy = 1; 681 p.add(_trainLocationBox, c); 682 _trainLocationBox.setToolTipText(Bundle.getMessage("TrackerLocTip")); 683 namePanel.add(p); 684 return namePanel; 685 } 686 687 private JPanel makeDoneButtonPanel() { 688 JPanel buttonPanel = new JPanel(); 689 JPanel panel0 = new JPanel(); 690 panel0.setLayout(new FlowLayout()); 691 JButton doneButton; 692 doneButton = new JButton(Bundle.getMessage("ButtonCreate")); 693 doneButton.addActionListener((ActionEvent a) -> { 694 if (doDoneAction()) { 695 _dialog.dispose(); 696 _dialog = null; 697 } 698 }); 699 panel0.add(doneButton); 700 buttonPanel.add(panel0); 701 return buttonPanel; 702 } 703 704 private boolean doDoneAction() { 705 boolean retOK = false; 706 String blockName = _trainLocationBox.getText(); 707 if (blockName != null) { 708 OBlock block = InstanceManager.getDefault(OBlockManager.class).getOBlock(blockName); 709 if (block == null) { 710 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("BlockNotFound", blockName), 711 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 712 } else { 713 retOK = makeTracker(block, _trainNameBox.getText(), null); 714 } 715 } 716 return retOK; 717 } 718 719 void openPickList() { 720 _pickFrame = new JmriJFrame(); 721 JPanel content = new JPanel(); 722 content.setLayout(new BoxLayout(content, BoxLayout.Y_AXIS)); 723 724 JPanel blurb = new JPanel(); 725 blurb.setLayout(new BoxLayout(blurb, BoxLayout.Y_AXIS)); 726 blurb.add(Box.createVerticalStrut(ItemPalette.STRUT_SIZE)); 727 blurb.add(new JLabel(Bundle.getMessage("DragBlockName"))); 728 blurb.add(Box.createVerticalStrut(ItemPalette.STRUT_SIZE)); 729 JPanel panel = new JPanel(); 730 panel.add(blurb); 731 content.add(panel); 732 733 PickListModel<?>[] models = {PickListModel.oBlockPickModelInstance()}; 734 content.add(new PickPanel(models)); 735 736 _pickFrame.setContentPane(content); 737 _pickFrame.setLocationRelativeTo(this); 738 _pickFrame.toFront(); 739 _pickFrame.setVisible(true); 740 _pickFrame.pack(); 741 } 742 743 @Override 744 public void mouseClicked(JmriMouseEvent event) { 745 javax.swing.JPopupMenu popup = new javax.swing.JPopupMenu(); 746 for (int i = _statusHistory.size() - 1; i >= 0; i--) { 747 popup.add(_statusHistory.get(i)); 748 } 749 popup.show(_status, 0, 0); 750 } 751 752 @Override 753 public void mousePressed(JmriMouseEvent event) { 754 // only handling mouseClicked 755 } 756 757 @Override 758 public void mouseEntered(JmriMouseEvent event) { 759 // only handling mouseClicked 760 } 761 762 @Override 763 public void mouseExited(JmriMouseEvent event) { 764 // only handling mouseClicked 765 } 766 767 @Override 768 public void mouseReleased(JmriMouseEvent event) { 769 // only handling mouseClicked 770 } 771 772 private void setStatus(String msg) { 773 _status.setText(msg); 774 if (msg != null && msg.length() > 0) { 775 WarrantTableAction.getDefault().writetoLog(msg); 776 _statusHistory.add(msg); 777 while (_statusHistory.size() > _maxHistorySize) { 778 _statusHistory.remove(0); 779 } 780 } 781 } 782 783 // For test purposes. The Window close action is set to hide the Frame, not dispose. 784 @Override 785 public void dispose(){ 786 if ( _pickFrame != null) { 787 _pickFrame.dispose(); 788 } 789 if ( _dialog != null) { 790 _dialog.dispose(); 791 } 792 super.dispose(); 793 } 794 795 } 796 797 private class TrackerTableModel extends AbstractTableModel { 798 799 public static final int NAME_COL = 0; 800 public static final int STATUS_COL = 1; 801 public static final int STOP_COL = 2; 802 public static final int NUMCOLS = 3; 803 804 public TrackerTableModel() { 805 super(); 806 } 807 808 @Override 809 public int getColumnCount() { 810 return NUMCOLS; 811 } 812 813 @Override 814 public synchronized int getRowCount() { 815 return _trackerList.size(); 816 } 817 818 @Override 819 public String getColumnName(int col) { 820 switch (col) { 821 case NAME_COL: 822 return Bundle.getMessage("TrainName"); 823 case STATUS_COL: 824 return Bundle.getMessage("status"); 825 default: 826 // fall out 827 break; 828 } 829 return ""; 830 } 831 832 @Override 833 public Object getValueAt(int rowIndex, int columnIndex) { 834 switch (columnIndex) { 835 case NAME_COL: 836 return _trackerList.get(rowIndex).getTrainName(); 837 case STATUS_COL: 838 return _trackerList.get(rowIndex).getStatus(); 839 case STOP_COL: 840 return Bundle.getMessage("Stop"); 841 default: 842 // fall out 843 break; 844 } 845 return ""; 846 } 847 848 @Override 849 public void setValueAt(Object value, int row, int col) { 850 if (col == STOP_COL) { 851 Tracker t = _trackerList.get(row); 852 stopTrain(t, t.getHeadBlock()); 853 fireTableDataChanged(); 854 } 855 } 856 857 @Override 858 public boolean isCellEditable(int row, int col) { 859 return (col == STOP_COL); 860 } 861 862 @Override 863 public Class<?> getColumnClass(int col) { 864 if (col == STOP_COL) { 865 return JButton.class; 866 } 867 return String.class; 868 } 869 870 public int getPreferredWidth(int col) { 871 switch (col) { 872 case NAME_COL: 873 return new JTextField(20).getPreferredSize().width; 874 case STATUS_COL: 875 return new JTextField(60).getPreferredSize().width; 876 case STOP_COL: 877 return new JButton("STOPIT").getPreferredSize().width; 878 default: 879 // fall out 880 break; 881 } 882 return 5; 883 } 884 885 } 886 887 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TrackerTableAction.class); 888 889 @ServiceProvider(service = InstanceInitializer.class) 890 public static class Initializer extends AbstractInstanceInitializer { 891 892 @Override 893 @Nonnull 894 public <T> Object getDefault(Class<T> type) { 895 if (type.equals(TrackerTableAction.class)) { 896 return new TrackerTableAction(Bundle.getMessage("MenuTrackers")); 897 } 898 return super.getDefault(type); 899 } 900 901 @Override 902 @Nonnull 903 public Set<Class<?>> getInitalizes() { 904 Set<Class<?>> set = super.getInitalizes(); 905 set.add(TrackerTableAction.class); 906 return set; 907 } 908 } 909 910}