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