001package jmri.jmrit.roster.swing; 002 003import java.awt.BorderLayout; 004import java.awt.Color; 005import java.awt.Component; 006import java.awt.Cursor; 007import java.awt.Dimension; 008import java.awt.FlowLayout; 009import java.awt.GridBagConstraints; 010import java.awt.GridBagLayout; 011import java.awt.GridLayout; 012import java.awt.Insets; 013import java.awt.datatransfer.Transferable; 014import java.awt.event.ActionEvent; 015import java.awt.event.ActionListener; 016import java.awt.event.WindowEvent; 017import java.awt.image.BufferedImage; 018 019import java.beans.PropertyChangeEvent; 020import java.beans.PropertyChangeListener; 021import java.io.File; 022import java.io.IOException; 023import java.text.DateFormat; 024import java.util.ArrayList; 025import java.util.Arrays; 026import java.util.List; 027 028import javax.annotation.CheckForNull; 029import javax.imageio.ImageIO; 030import javax.swing.*; 031import javax.swing.event.ListSelectionEvent; 032 033import jmri.AddressedProgrammerManager; 034import jmri.GlobalProgrammerManager; 035import jmri.InstanceManager; 036import jmri.Programmer; 037import jmri.ShutDownManager; 038import jmri.UserPreferencesManager; 039import jmri.jmrit.decoderdefn.DecoderFile; 040import jmri.jmrit.decoderdefn.DecoderIndexFile; 041import jmri.jmrit.progsupport.ProgModeSelector; 042import jmri.jmrit.progsupport.ProgServiceModeComboBox; 043import jmri.jmrit.roster.CopyRosterItemAction; 044import jmri.jmrit.roster.DeleteRosterItemAction; 045import jmri.jmrit.roster.ExportRosterItemAction; 046import jmri.jmrit.roster.IdentifyLoco; 047import jmri.jmrit.roster.PrintRosterEntry; 048import jmri.jmrit.roster.Roster; 049import jmri.jmrit.roster.RosterEntry; 050import jmri.jmrit.roster.RosterEntrySelector; 051import jmri.jmrit.roster.rostergroup.RosterGroupSelector; 052import jmri.jmrit.symbolicprog.ProgrammerConfigManager; 053import jmri.jmrit.symbolicprog.tabbedframe.PaneOpsProgFrame; 054import jmri.jmrit.symbolicprog.tabbedframe.PaneProgFrame; 055import jmri.jmrit.symbolicprog.tabbedframe.PaneServiceProgFrame; 056import jmri.jmrit.throttle.*; 057import jmri.jmrix.ActiveSystemsMenu; 058import jmri.jmrix.ConnectionConfig; 059import jmri.jmrix.ConnectionConfigManager; 060import jmri.jmrix.ConnectionStatus; 061import jmri.profile.Profile; 062import jmri.profile.ProfileManager; 063import jmri.swing.JTablePersistenceManager; 064import jmri.swing.RowSorterUtil; 065import jmri.util.FileUtil; 066import jmri.util.HelpUtil; 067import jmri.util.WindowMenu; 068import jmri.util.datatransfer.RosterEntrySelection; 069import jmri.util.swing.JmriAbstractAction; 070import jmri.util.swing.JmriJOptionPane; 071import jmri.util.swing.JmriMouseAdapter; 072import jmri.util.swing.JmriMouseEvent; 073import jmri.util.swing.JmriMouseListener; 074import jmri.util.swing.ResizableImagePanel; 075import jmri.util.swing.WindowInterface; 076import jmri.util.swing.multipane.TwoPaneTBWindow; 077 078/** 079 * A window for Roster management. 080 * <p> 081 * TODO: Several methods are copied from PaneProgFrame and should be refactored 082 * No programmer support yet (dummy object below). Color only covering borders. 083 * No reset toolbar support yet. No glass pane support (See DecoderPro3Panes 084 * class and usage below). Special panes (Roster entry, attributes, graphics) 085 * not included. How do you pick a programmer file? (hardcoded) Initialization 086 * needs partial deferral, too for 1st pane to appear. 087 * 088 * @see jmri.jmrit.symbolicprog.tabbedframe.PaneSet 089 * 090 * @author Bob Jacobsen Copyright (C) 2010, 2016 091 * @author Kevin Dickerson Copyright (C) 2011 092 * @author Randall Wood Copyright (C) 2012 093 */ 094public class RosterFrame extends TwoPaneTBWindow implements RosterEntrySelector, RosterGroupSelector { 095 096 static final ArrayList<RosterFrame> frameInstances = new ArrayList<>(); 097 protected boolean allowQuit = true; 098 protected String baseTitle = "Roster"; 099 protected JmriAbstractAction newWindowAction; 100 101 public RosterFrame() { 102 this(Bundle.getMessage("RosterTitle")); 103 } 104 105 public RosterFrame(String name) { 106 this(name, 107 "xml/config/parts/jmri/jmrit/roster/swing/RosterFrameMenu.xml", 108 "xml/config/parts/jmri/jmrit/roster/swing/RosterFrameToolBar.xml"); 109 } 110 111 public RosterFrame(String name, String menubarFile, String toolbarFile) { 112 super(name, menubarFile, toolbarFile); 113 this.allowInFrameServlet = false; 114 this.setBaseTitle(name); 115 this.buildWindow(); 116 this.locoSelected(null); 117 } 118 119 final JRadioButtonMenuItem contextEdit = new JRadioButtonMenuItem(Bundle.getMessage("EditOnly")); 120 final JRadioButtonMenuItem contextOps = new JRadioButtonMenuItem(Bundle.getMessage("ProgrammingOnMain")); 121 final JRadioButtonMenuItem contextService = new JRadioButtonMenuItem(Bundle.getMessage("ProgrammingTrack")); 122 final JTextPane dateUpdated = new JTextPane(); 123 final JTextPane dccAddress = new JTextPane(); 124 final JTextPane decoderFamily = new JTextPane(); 125 final JTextPane decoderModel = new JTextPane(); 126 final JRadioButton edit = new JRadioButton(Bundle.getMessage("EditOnly")); 127 final JTextPane filename = new JTextPane(); 128 JLabel firstHelpLabel; 129 //int firstTimeAddedEntry = 0x00; 130 int groupSplitPaneLocation = 0; 131 RosterGroupsPanel groups; 132 boolean hideGroups = false; 133 boolean hideRosterImage = false; 134 final JTextPane id = new JTextPane(); 135 boolean inStartProgrammer = false; 136 ResizableImagePanel locoImage; 137 JTextPane maxSpeed = new JTextPane(); 138 final JTextPane mfg = new JTextPane(); 139 final ProgModeSelector modePanel = new ProgServiceModeComboBox(); 140 final JTextPane model = new JTextPane(); 141 final JLabel operationsModeProgrammerLabel = new JLabel(); 142 final JRadioButton ops = new JRadioButton(Bundle.getMessage("ProgrammingOnMain")); 143 ConnectionConfig opsModeProCon = null; 144 final JTextPane owner = new JTextPane(); 145 UserPreferencesManager prefsMgr; 146 final JButton prog1Button = new JButton(Bundle.getMessage("Program")); 147 final JButton prog2Button = new JButton(Bundle.getMessage("BasicProgrammer")); 148 ActionListener programModeListener; 149 150 // These are the names of the programmer _files_, not what should be displayed to the user 151 String programmer1 = "Comprehensive"; // NOI18N 152 String programmer2 = "Basic"; // NOI18N 153 154 final java.util.ResourceBundle rb = java.util.ResourceBundle.getBundle("apps.AppsBundle"); 155 //current selected loco 156 transient RosterEntry re; 157 final JTextPane roadName = new JTextPane(); 158 final JTextPane roadNumber = new JTextPane(); 159 final JPanel rosterDetailPanel = new JPanel(); 160 PropertyChangeListener rosterEntryUpdateListener; 161 JSplitPane rosterGroupSplitPane; 162 final JButton rosterMedia = new JButton(Bundle.getMessage("LabelsAndMedia")); 163 RosterTable rtable; 164 ConnectionConfig serModeProCon = null; 165 final JRadioButton service = new JRadioButton(Bundle.getMessage("ProgrammingTrack")); 166 final JLabel serviceModeProgrammerLabel = new JLabel(); 167 final JLabel statusField = new JLabel(); 168 final Dimension summaryPaneDim = new Dimension(0, 170); 169 final JButton throttleLabels = new JButton(Bundle.getMessage("ThrottleLabels")); 170 final JButton throttleLaunch = new JButton(Bundle.getMessage("Throttle")); 171 172 protected void additionsToToolBar() { 173 getToolBar().add(new LargePowerManagerButton(true)); 174 getToolBar().add(Box.createHorizontalGlue()); 175 JPanel p = new JPanel(); 176 p.setAlignmentX(JPanel.RIGHT_ALIGNMENT); 177 p.setOpaque(false); 178 p.add(modePanel); 179 getToolBar().add(p); 180 } 181 182 /** 183 * For use when the DP3 window is called from another JMRI instance, set 184 * this to prevent the DP3 from shutting down JMRI when the window is 185 * closed. 186 * 187 * @param quitAllowed true if closing window should quit application; false 188 * otherwise 189 */ 190 protected void allowQuit(boolean quitAllowed) { 191 if (allowQuit != quitAllowed) { 192 newWindowAction = null; 193 allowQuit = quitAllowed; 194 groups.setNewWindowMenuAction(this.getNewWindowAction()); 195 } 196 197 firePropertyChange("quit", "setEnabled", allowQuit); 198 //if we are not allowing quit, ie opened from JMRI classic 199 //then we must at least allow the window to be closed 200 if (!allowQuit) { 201 firePropertyChange("closewindow", "setEnabled", true); 202 } 203 } 204 205 JPanel bottomRight() { 206 JPanel panel = new JPanel(); 207 panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); 208 ButtonGroup progMode = new ButtonGroup(); 209 progMode.add(service); 210 progMode.add(ops); 211 progMode.add(edit); 212 service.setEnabled(false); 213 ops.setEnabled(false); 214 edit.setEnabled(true); 215 firePropertyChange("setprogservice", "setEnabled", false); 216 firePropertyChange("setprogops", "setEnabled", false); 217 firePropertyChange("setprogedit", "setEnabled", true); 218 ops.setOpaque(false); 219 service.setOpaque(false); 220 edit.setOpaque(false); 221 JPanel progModePanel = new JPanel(); 222 GridLayout buttonLayout = new GridLayout(3, 1, 0, 0); 223 progModePanel.setLayout(buttonLayout); 224 progModePanel.add(service); 225 progModePanel.add(ops); 226 progModePanel.add(edit); 227 programModeListener = (ActionEvent e) -> updateProgMode(); 228 service.addActionListener(programModeListener); 229 ops.addActionListener(programModeListener); 230 edit.addActionListener(programModeListener); 231 service.setVisible(false); 232 ops.setVisible(false); 233 panel.add(progModePanel); 234 JPanel buttonHolder = new JPanel(new GridBagLayout()); 235 GridBagConstraints c = new GridBagConstraints(); 236 c.weightx = 1.0; 237 c.fill = GridBagConstraints.HORIZONTAL; 238 c.anchor = GridBagConstraints.NORTH; 239 c.gridx = 0; 240 c.ipady = 20; 241 c.gridwidth = GridBagConstraints.REMAINDER; 242 c.gridy = 0; 243 c.insets = new Insets(2, 2, 2, 2); 244 buttonHolder.add(prog1Button, c); 245 c.weightx = 1; 246 c.fill = GridBagConstraints.NONE; 247 c.gridx = 0; 248 c.gridy = 1; 249 c.gridwidth = 1; 250 c.ipady = 0; 251 buttonHolder.add(rosterMedia, c); 252 c.weightx = 1.0; 253 c.fill = GridBagConstraints.NONE; 254 c.gridx = 1; 255 c.gridy = 1; 256 c.gridwidth = 1; 257 c.ipady = 0; 258 buttonHolder.add(throttleLaunch, c); 259 //buttonHolder.add(throttleLaunch); 260 panel.add(buttonHolder); 261 prog1Button.setEnabled(false); 262 prog1Button.addActionListener((ActionEvent e) -> { 263 log.debug("Open programmer pressed"); 264 startProgrammer(null, re, programmer1); 265 }); 266 267 rosterMedia.setEnabled(false); 268 rosterMedia.addActionListener((ActionEvent e) -> { 269 log.debug("Open Media pressed"); 270 edit.setSelected(true); 271 startProgrammer(null, re, "dp3" + File.separator + "MediaPane"); 272 }); 273 throttleLaunch.setEnabled(false); 274 throttleLaunch.addActionListener((ActionEvent e) -> { 275 log.debug("Launch Throttle pressed"); 276 if (!checkIfEntrySelected()) { 277 return; 278 } 279 ThrottleControllerUI tf = InstanceManager.getDefault(ThrottleFrameManager.class).createThrottleController(); 280 tf.toFront(); 281 tf.setRosterEntry(re); 282 }); 283 return panel; 284 } 285 286 protected final void buildWindow() { 287 //Additions to the toolbar need to be added first otherwise when trying to hide bits up during the initialisation they remain on screen 288 additionsToToolBar(); 289 frameInstances.add(this); 290 prefsMgr = InstanceManager.getDefault(UserPreferencesManager.class); 291 getTop().add(createTop()); 292 getBottom().setMinimumSize(summaryPaneDim); 293 getBottom().add(createBottom()); 294 statusBar(); 295 systemsMenu(); 296 helpMenu(getMenu(), this); 297 if ((!prefsMgr.getSimplePreferenceState(this.getClass().getName() + ".hideGroups")) && !Roster.getDefault().getRosterGroupList().isEmpty()) { 298 hideGroupsPane(false); 299 } else { 300 hideGroupsPane(true); 301 } 302 if (prefsMgr.getSimplePreferenceState(this.getClass().getName() + ".hideSummary")) { 303 //We have to set it to display first, then we can hide it. 304 hideBottomPane(false); 305 hideBottomPane(true); 306 } 307 PropertyChangeListener propertyChangeListener = (PropertyChangeEvent changeEvent) -> { 308 JSplitPane sourceSplitPane = (JSplitPane) changeEvent.getSource(); 309 String propertyName = changeEvent.getPropertyName(); 310 if (propertyName.equals(JSplitPane.LAST_DIVIDER_LOCATION_PROPERTY)) { 311 int current = sourceSplitPane.getDividerLocation() + sourceSplitPane.getDividerSize(); 312 int panesize = (int) (sourceSplitPane.getSize().getHeight()); 313 hideBottomPane = panesize - current <= 1; 314 //p.setSimplePreferenceState(DecoderPro3Window.class.getName()+".hideSummary",hideSummary); 315 } 316 }; 317 updateProgrammerStatus(null); 318 ConnectionStatus.instance().addPropertyChangeListener((PropertyChangeEvent e) -> { 319 if ((e.getPropertyName().equals("change")) || (e.getPropertyName().equals("add"))) { 320 log.debug("Received property {} with value {} ", e.getPropertyName(), e.getNewValue()); 321 updateProgrammerStatus(e); 322 } 323 }); 324 InstanceManager.addPropertyChangeListener(InstanceManager.getListPropertyName(AddressedProgrammerManager.class), 325 evt -> { 326 log.debug("Received property {} with value {} ", evt.getPropertyName(), evt.getNewValue()); 327 AddressedProgrammerManager m = (AddressedProgrammerManager) evt.getNewValue(); 328 if (m != null) { 329 m.addPropertyChangeListener(this::updateProgrammerStatus); 330 } 331 updateProgrammerStatus(evt); 332 }); 333 InstanceManager.getList(AddressedProgrammerManager.class).forEach(m -> m.addPropertyChangeListener(this::updateProgrammerStatus)); 334 InstanceManager.addPropertyChangeListener(InstanceManager.getListPropertyName(GlobalProgrammerManager.class), 335 evt -> { 336 log.debug("Received property {} with value {} ", evt.getPropertyName(), evt.getNewValue()); 337 GlobalProgrammerManager m = (GlobalProgrammerManager) evt.getNewValue(); 338 if (m != null) { 339 m.addPropertyChangeListener(this::updateProgrammerStatus); 340 } 341 updateProgrammerStatus(evt); 342 }); 343 InstanceManager.getList(GlobalProgrammerManager.class).forEach(m -> m.addPropertyChangeListener(this::updateProgrammerStatus)); 344 getSplitPane().addPropertyChangeListener(propertyChangeListener); 345 if (this.getProgrammerConfigManager().getDefaultFile() != null) { 346 programmer1 = this.getProgrammerConfigManager().getDefaultFile(); 347 } 348 this.getProgrammerConfigManager().addPropertyChangeListener(ProgrammerConfigManager.DEFAULT_FILE, (PropertyChangeEvent evt) -> { 349 if (this.getProgrammerConfigManager().getDefaultFile() != null) { 350 programmer1 = this.getProgrammerConfigManager().getDefaultFile(); 351 } 352 }); 353 354 String lastProg = (String) prefsMgr.getProperty(getWindowFrameRef(), "selectedProgrammer"); 355 if (lastProg != null) { 356 if (lastProg.equals("service") && service.isEnabled()) { 357 service.setSelected(true); 358 updateProgMode(); 359 } else if (lastProg.equals("ops") && ops.isEnabled()) { 360 ops.setSelected(true); 361 updateProgMode(); 362 } else if (lastProg.equals("edit") && edit.isEnabled()) { 363 edit.setSelected(true); 364 updateProgMode(); 365 } 366 } 367 if (frameInstances.size() > 1) { 368 firePropertyChange("closewindow", "setEnabled", true); 369 allowQuit(frameInstances.get(0).isAllowQuit()); 370 } else { 371 firePropertyChange("closewindow", "setEnabled", false); 372 } 373 } 374 375 boolean checkIfEntrySelected() { 376 return this.checkIfEntrySelected(false); 377 } 378 379 boolean checkIfEntrySelected(boolean allowMultiple) { 380 if ((re == null && !allowMultiple) || (this.getSelectedRosterEntries().length < 1)) { 381 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("ErrorNoSelection")); 382 return false; 383 } 384 return true; 385 } 386 387 //@TODO The disabling of the closeWindow menu item doesn't quite work as this in only invoked on the closing window, and not the one that is left 388 void closeWindow(WindowEvent e) { 389 saveWindowDetails(); 390 //Save any changes made in the roster entry details 391 Roster.getDefault().writeRoster(); 392 if (allowQuit && frameInstances.size() == 1 && !InstanceManager.getDefault(ShutDownManager.class).isShuttingDown()) { 393 handleQuit(e); 394 } else { 395 //As we are not the last window open or we are not allowed to quit the application then we will just close the current window 396 frameInstances.remove(this); 397 super.windowClosing(e); 398 if ((frameInstances.size() == 1) && (allowQuit)) { 399 frameInstances.get(0).firePropertyChange("closewindow", "setEnabled", false); 400 } 401 dispose(); 402 } 403 } 404 405 protected void copyLoco() { 406 CopyRosterItem act = new CopyRosterItem("Copy", this, re); 407 act.actionPerformed(null); 408 } 409 410 JComponent createBottom() { 411 locoImage = new ResizableImagePanel(null, 240, 160); 412 locoImage.setBorder(BorderFactory.createLineBorder(Color.blue)); 413 locoImage.setOpaque(true); 414 locoImage.setRespectAspectRatio(true); 415 rosterDetailPanel.setLayout(new BorderLayout()); 416 rosterDetailPanel.add(locoImage, BorderLayout.WEST); 417 rosterDetailPanel.add(rosterDetails(), BorderLayout.CENTER); 418 rosterDetailPanel.add(bottomRight(), BorderLayout.EAST); 419 if (prefsMgr.getSimplePreferenceState(this.getClass().getName() + ".hideRosterImage")) { 420 locoImage.setVisible(false); 421 hideRosterImage = true; 422 } 423 rosterEntryUpdateListener = (PropertyChangeEvent e) -> updateDetails(); 424 return rosterDetailPanel; 425 } 426 427 private boolean isUpdatingSelection = false; 428 429 JComponent createTop() { 430 Object selectedRosterGroup = prefsMgr.getProperty(getWindowFrameRef(), SELECTED_ROSTER_GROUP); 431 groups = new RosterGroupsPanel((selectedRosterGroup != null) ? selectedRosterGroup.toString() : null); 432 groups.setNewWindowMenuAction(this.getNewWindowAction()); 433 setTitle(groups.getSelectedRosterGroup()); 434 final JPanel rosters = new JPanel(); 435 rosters.setLayout(new BorderLayout()); 436 // set up roster table 437 rtable = new RosterTable(true, ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); 438 rtable.setRosterGroup(this.getSelectedRosterGroup()); 439 rtable.setRosterGroupSource(groups); 440 rosters.add(rtable, BorderLayout.CENTER); 441 // add selection listener 442 rtable.getTable().getSelectionModel().addListSelectionListener((ListSelectionEvent e) -> { 443 JTable table = rtable.getTable(); 444 if (!e.getValueIsAdjusting()) { 445 if ((rtable.getSelectedRosterEntries().length == 1 ) && (table.getSelectedRow() >= 0)) { 446 log.debug("Selected row {}", table.getSelectedRow()); 447 locoSelected(rtable.getModel().getValueAt(table.getRowSorter().convertRowIndexToModel(table.getSelectedRow()), RosterTableModel.IDCOL).toString()); 448 } else if (rtable.getSelectedRosterEntries().length > 1) { 449 log.debug("Multiple selection"); 450 locoSelected(null); 451 } else if ( (table.getSelectedRow() < 0) && (!isUpdatingSelection) ) { 452 isUpdatingSelection = true; 453 if (re != null) { // can be null with multiple selection 454 log.debug("Selected roster entry {}", re.getId()); 455 if (!rtable.setSelection(re)) { 456 re = null; //nothng was found 457 } 458 } 459 updateDetails(); 460 rtable.moveTableViewToSelected(); 461 isUpdatingSelection = false; 462 } // leave last selected item visible if no selection 463 } 464 }); 465 466 //Set all the sort and width details of the table first. 467 String rostertableref = getWindowFrameRef() + ":roster"; 468 rtable.getTable().setName(rostertableref); 469 470 // Allow only one column to be sorted at a time - 471 // Java allows multiple column sorting, but to effectively persist that, we 472 // need to be intelligent about which columns can be meaningfully sorted 473 // with other columns; this bypasses the problem by only allowing the 474 // last column sorted to affect sorting 475 RowSorterUtil.addSingleSortableColumnListener(rtable.getTable().getRowSorter()); 476 477 // Reset and then persist the table's ui state 478 JTablePersistenceManager tpm = InstanceManager.getNullableDefault(JTablePersistenceManager.class); 479 if (tpm != null) { 480 tpm.resetState(rtable.getTable()); 481 tpm.persist(rtable.getTable()); 482 } 483 rtable.getTable().setDragEnabled(true); 484 rtable.getTable().setTransferHandler(new TransferHandler() { 485 486 @Override 487 public int getSourceActions(JComponent c) { 488 return TransferHandler.COPY; 489 } 490 491 @Override 492 public Transferable createTransferable(JComponent c) { 493 JTable table = rtable.getTable(); 494 ArrayList<String> Ids = new ArrayList<>(table.getSelectedRowCount()); 495 for (int i = 0; i < table.getSelectedRowCount(); i++) { 496 Ids.add(rtable.getModel().getValueAt(table.getRowSorter().convertRowIndexToModel(table.getSelectedRows()[i]), RosterTableModel.IDCOL).toString()); 497 } 498 return new RosterEntrySelection(Ids); 499 } 500 501 @Override 502 public void exportDone(JComponent c, Transferable t, int action) { 503 // nothing to do 504 } 505 }); 506 JmriMouseListener rosterMouseListener = new RosterPopupListener(); 507 rtable.getTable().addMouseListener(JmriMouseListener.adapt(rosterMouseListener)); 508 509 // assemble roster/groups splitpane 510 rosterGroupSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, groups, rosters); 511 rosterGroupSplitPane.setOneTouchExpandable(true); 512 rosterGroupSplitPane.setResizeWeight(0); // emphasis rosters 513 Object w = prefsMgr.getProperty(getWindowFrameRef(), "rosterGroupPaneDividerLocation"); 514 if (w != null) { 515 groupSplitPaneLocation = (Integer) w; 516 rosterGroupSplitPane.setDividerLocation(groupSplitPaneLocation); 517 } 518 if (!Roster.getDefault().getRosterGroupList().isEmpty()) { 519 if (prefsMgr.getSimplePreferenceState(this.getClass().getName() + ".hideGroups")) { 520 hideGroupsPane(true); 521 } 522 } else { 523 enableRosterGroupMenuItems(false); 524 } 525 PropertyChangeListener propertyChangeListener = (PropertyChangeEvent changeEvent) -> { 526 JSplitPane sourceSplitPane = (JSplitPane) changeEvent.getSource(); 527 String propertyName = changeEvent.getPropertyName(); 528 if (propertyName.equals(JSplitPane.LAST_DIVIDER_LOCATION_PROPERTY)) { 529 int current = sourceSplitPane.getDividerLocation(); 530 hideGroups = current <= 1; 531 Integer last = (Integer) changeEvent.getNewValue(); 532 if (current >= 2) { 533 groupSplitPaneLocation = current; 534 } else if (last >= 2) { 535 groupSplitPaneLocation = last; 536 } 537 } 538 }; 539 groups.addPropertyChangeListener(SELECTED_ROSTER_GROUP, new PropertyChangeListener() { 540 @Override 541 public void propertyChange(PropertyChangeEvent pce) { 542 prefsMgr.setProperty(this.getClass().getName(), SELECTED_ROSTER_GROUP, pce.getNewValue()); 543 setTitle((String) pce.getNewValue()); 544 } 545 }); 546 rosterGroupSplitPane.addPropertyChangeListener(propertyChangeListener); 547 Roster.getDefault().addPropertyChangeListener((PropertyChangeEvent e) -> { 548 if (e.getPropertyName().equals("RosterGroupAdded") && Roster.getDefault().getRosterGroupList().size() == 1) { 549 // if the pane is hidden, show it when 1st group is created 550 hideGroupsPane(false); 551 enableRosterGroupMenuItems(true); 552 } else if (!rtable.isVisible() && (e.getPropertyName().equals("saved"))) { 553 if (firstHelpLabel != null) { 554 firstHelpLabel.setVisible(false); 555 } 556 rtable.setVisible(true); 557 rtable.resetColumnWidths(); 558 } 559 }); 560 if (Roster.getDefault().numEntries() == 0) { 561 try { 562 BufferedImage myPicture = ImageIO.read(FileUtil.findURL(("resources/" + Bundle.getMessage("ThrottleFirstUseImage")), FileUtil.Location.INSTALLED)); 563 //rosters.add(new JLabel(new ImageIcon( myPicture )), BorderLayout.CENTER); 564 firstHelpLabel = new JLabel(new ImageIcon(myPicture)); 565 rtable.setVisible(false); 566 rosters.add(firstHelpLabel, BorderLayout.NORTH); 567 //tableArea.add(firstHelpLabel); 568 rtable.setVisible(false); 569 } catch (IOException ex) { 570 // handle exception... 571 } 572 } 573 return rosterGroupSplitPane; 574 } 575 576 protected void deleteLoco() { 577 DeleteRosterItemAction act = new DeleteRosterItemAction("Delete", (WindowInterface) this); 578 act.actionPerformed(null); 579 } 580 581 void editMediaButton() { 582 //Because of the way that programmers work, we need to use edit mode for displaying the media pane, so that the read/write buttons do not appear. 583 boolean serviceSelected = service.isSelected(); 584 boolean opsSelected = ops.isSelected(); 585 edit.setSelected(true); 586 startProgrammer(null, re, "dp3" + File.separator + "MediaPane"); 587 service.setSelected(serviceSelected); 588 ops.setSelected(opsSelected); 589 } 590 591 protected void enableRosterGroupMenuItems(boolean enable) { 592 firePropertyChange("groupspane", "setEnabled", enable); 593 firePropertyChange("grouptable", "setEnabled", enable); 594 firePropertyChange("deletegroup", "setEnabled", enable); 595 firePropertyChange("addgroup", "setEnabled", enable); 596 } 597 598 protected void exportLoco() { 599 ExportRosterItem act = new ExportRosterItem(Bundle.getMessage("Export"), this, re); 600 act.actionPerformed(null); 601 } 602 603 void formatTextAreaAsLabel(JTextPane pane) { 604 pane.setOpaque(false); 605 pane.setEditable(false); 606 pane.setBorder(null); 607 } 608 609 /*=============== Getters and Setters for core properties ===============*/ 610 611 /** 612 * @return Will closing the window quit JMRI? 613 */ 614 public boolean isAllowQuit() { 615 return allowQuit; 616 } 617 618 /** 619 * @param allowQuit Set state to either close JMRI or just the roster window 620 */ 621 public void setAllowQuit(boolean allowQuit) { 622 allowQuit(allowQuit); 623 } 624 625 /** 626 * @return the baseTitle 627 */ 628 protected String getBaseTitle() { 629 return baseTitle; 630 } 631 632 /** 633 * @param baseTitle the baseTitle to set 634 */ 635 protected final void setBaseTitle(String baseTitle) { 636 String title = null; 637 if (this.baseTitle == null) { 638 title = this.getTitle(); 639 } 640 this.baseTitle = baseTitle; 641 if (title != null) { 642 this.setTitle(title); 643 } 644 } 645 646 /** 647 * @return the newWindowAction 648 */ 649 protected JmriAbstractAction getNewWindowAction() { 650 if (newWindowAction == null) { 651 newWindowAction = new RosterFrameAction("newWindow", this, allowQuit); 652 } 653 return newWindowAction; 654 } 655 656 /** 657 * @param newWindowAction the newWindowAction to set 658 */ 659 protected void setNewWindowAction(JmriAbstractAction newWindowAction) { 660 this.newWindowAction = newWindowAction; 661 this.groups.setNewWindowMenuAction(newWindowAction); 662 } 663 664 @Override 665 public void setTitle(String title) { 666 if (title == null || title.isEmpty()) { 667 title = Roster.ALLENTRIES; 668 } 669 if (this.baseTitle != null) { 670 if (!title.equals(this.baseTitle) && !title.startsWith(this.baseTitle)) { 671 super.setTitle(this.baseTitle + ": " + title); 672 } 673 } else { 674 super.setTitle(title); 675 } 676 } 677 678 @Override 679 public Object getProperty(String key) { 680 if (key.equalsIgnoreCase(SELECTED_ROSTER_GROUP)) { 681 return getSelectedRosterGroup(); 682 } else if (key.equalsIgnoreCase("hideSummary")) { 683 return hideBottomPane; 684 } 685 // call parent getProperty method to return any properties defined 686 // in the class hierarchy. 687 return super.getProperty(key); 688 } 689 690 public Object getRemoteObject(String value) { 691 return getProperty(value); 692 } 693 694 @Override 695 public RosterEntry[] getSelectedRosterEntries() { 696 RosterEntry[] entries = rtable.getSelectedRosterEntries(); 697 return Arrays.copyOf(entries, entries.length); 698 } 699 700 public RosterEntry[] getAllRosterEntries() { 701 RosterEntry[] entries = rtable.getSortedRosterEntries(); 702 return Arrays.copyOf(entries, entries.length); 703 } 704 705 @Override 706 public String getSelectedRosterGroup() { 707 return groups.getSelectedRosterGroup(); 708 } 709 710 protected ProgrammerConfigManager getProgrammerConfigManager() { 711 return InstanceManager.getDefault(ProgrammerConfigManager.class); 712 } 713 714 void handleQuit(WindowEvent e) { 715 if (e != null && frameInstances.size() == 1) { 716 final String rememberWindowClose = this.getClass().getName() + ".closeDP3prompt"; 717 if (!prefsMgr.getSimplePreferenceState(rememberWindowClose)) { 718 JPanel message = new JPanel(); 719 JLabel question = new JLabel(rb.getString("MessageLongCloseWarning")); 720 final JCheckBox remember = new JCheckBox(rb.getString("MessageRememberSetting")); 721 remember.setFont(remember.getFont().deriveFont(10.0F)); 722 message.setLayout(new BoxLayout(message, BoxLayout.Y_AXIS)); 723 message.add(question); 724 message.add(remember); 725 int result = JmriJOptionPane.showConfirmDialog(null, 726 message, 727 rb.getString("MessageShortCloseWarning"), 728 JmriJOptionPane.YES_NO_OPTION); 729 if (remember.isSelected()) { 730 prefsMgr.setSimplePreferenceState(rememberWindowClose, true); 731 } 732 if (result == JmriJOptionPane.YES_OPTION) { 733 handleQuit(); 734 } 735 } else { 736 handleQuit(); 737 } 738 } else if (frameInstances.size() > 1) { 739 final String rememberWindowClose = this.getClass().getName() + ".closeMultipleDP3prompt"; 740 if (!prefsMgr.getSimplePreferenceState(rememberWindowClose)) { 741 JPanel message = new JPanel(); 742 JLabel question = new JLabel(rb.getString("MessageLongMultipleCloseWarning")); 743 final JCheckBox remember = new JCheckBox(rb.getString("MessageRememberSetting")); 744 remember.setFont(remember.getFont().deriveFont(10.0F)); 745 message.setLayout(new BoxLayout(message, BoxLayout.Y_AXIS)); 746 message.add(question); 747 message.add(remember); 748 int result = JmriJOptionPane.showConfirmDialog(null, 749 message, 750 rb.getString("MessageShortCloseWarning"), 751 JmriJOptionPane.YES_NO_OPTION); 752 if (remember.isSelected()) { 753 prefsMgr.setSimplePreferenceState(rememberWindowClose, true); 754 } 755 if (result == JmriJOptionPane.YES_OPTION) { 756 handleQuit(); 757 } 758 } else { 759 handleQuit(); 760 } 761 //closeWindow(null); 762 } 763 } 764 765 private void handleQuit(){ 766 try { 767 InstanceManager.getDefault(jmri.ShutDownManager.class).shutdown(); 768 } catch (Exception e) { 769 log.error("Continuing after error in handleQuit", e); 770 } 771 } 772 773 protected void helpMenu(JMenuBar menuBar, final JFrame frame) { 774 // create menu and standard items 775 JMenu helpMenu = HelpUtil.makeHelpMenu("package.apps.gui3.dp3.DecoderPro3", true); 776 // use as main help menu 777 menuBar.add(helpMenu); 778 } 779 780 protected void hideGroups() { 781 boolean boo = !hideGroups; 782 hideGroupsPane(boo); 783 } 784 785 public void hideGroupsPane(boolean hide) { 786 if (hideGroups == hide) { 787 return; 788 } 789 hideGroups = hide; 790 if (hide) { 791 groupSplitPaneLocation = rosterGroupSplitPane.getDividerLocation(); 792 rosterGroupSplitPane.setDividerLocation(1); 793 rosterGroupSplitPane.getLeftComponent().setMinimumSize(new Dimension()); 794 if (Roster.getDefault().getRosterGroupList().isEmpty()) { 795 rosterGroupSplitPane.setOneTouchExpandable(false); 796 rosterGroupSplitPane.setDividerSize(0); 797 } 798 } else { 799 rosterGroupSplitPane.setDividerSize(UIManager.getInt("SplitPane.dividerSize")); 800 rosterGroupSplitPane.setOneTouchExpandable(true); 801 if (groupSplitPaneLocation >= 2) { 802 rosterGroupSplitPane.setDividerLocation(groupSplitPaneLocation); 803 } else { 804 rosterGroupSplitPane.resetToPreferredSizes(); 805 } 806 } 807 } 808 809 protected void hideRosterImage() { 810 hideRosterImage = !hideRosterImage; 811 //p.setSimplePreferenceState(DecoderPro3Window.class.getName()+".hideRosterImage",hideRosterImage); 812 if (hideRosterImage) { 813 locoImage.setVisible(false); 814 } else { 815 locoImage.setVisible(true); 816 } 817 } 818 819 protected void hideSummary() { 820 boolean boo = !hideBottomPane; 821 hideBottomPane(boo); 822 } 823 824 /** 825 * An entry has been selected in the Roster Table, activate the bottom part 826 * of the window. 827 * 828 * @param id ID of the selected roster entry 829 */ 830 final void locoSelected(String id) { 831 if (id != null) { 832 log.debug("locoSelected ID {}", id); 833 if (re != null) { 834 // we remove the propertychangelistener if we had a previously selected entry; 835 re.removePropertyChangeListener(rosterEntryUpdateListener); 836 } 837 // convert to roster entry 838 re = Roster.getDefault().entryFromTitle(id); 839 re.addPropertyChangeListener(rosterEntryUpdateListener); 840 } else { 841 log.debug("Multiple selection"); 842 re = null; 843 } 844 updateDetails(); 845 } 846 847 protected void newWindow() { 848 this.newWindow(this.getNewWindowAction()); 849 } 850 851 protected void newWindow(JmriAbstractAction action) { 852 action.setWindowInterface(this); 853 action.actionPerformed(null); 854 firePropertyChange("closewindow", "setEnabled", true); 855 } 856 857 /** 858 * Prepare a roster entry to be printed, and display a selection list. 859 * 860 * @see jmri.jmrit.roster.PrintRosterEntry#printPanes(boolean) 861 * @param preview true if output should go to a Preview pane on screen, false 862 * to output to a printer (dialog) 863 */ 864 protected void printLoco(boolean preview) { 865 log.debug("Selected entry: {}", re.getDisplayName()); 866 String programmer = "Basic"; 867 if (this.getProgrammerConfigManager().getDefaultFile() != null) { 868 programmer = this.getProgrammerConfigManager().getDefaultFile(); 869 } else { 870 log.error("programmer is NULL"); 871 } 872 PrintRosterEntry pre = new PrintRosterEntry(re, this, "programmers" + File.separator + programmer + ".xml"); 873 // uses programmer set in prefs when printing a selected entry from (this) top Roster frame 874 // compare with: jmri.jmrit.symbolicprog.tabbedframe.PaneProgFrame#printPanes(boolean) 875 // as user expects to see more tabs on printout using Comprehensive or just 1 tab for Basic programmer 876 pre.printPanes(preview); 877 } 878 879 /** 880 * Print the displayed table, as displayed. 881 * 882 */ 883 protected void printCurrentTable() { 884 try { 885 var cal = java.util.Calendar.getInstance(); 886 var sdf = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm"); 887 String time = sdf.format(cal.getTime()); 888 889 var selectedRosterGroup = getSelectedRosterGroup(); 890 String g = (selectedRosterGroup != null) ? selectedRosterGroup : "All Entries"; 891 String group = String.format("%-20s",g); // pad to right to fixed length 892 893 rtable.getTable().print(javax.swing.JTable.PrintMode.FIT_WIDTH, 894 null, // no header 895 new java.text.MessageFormat(group+" - {0} - "+time) // spaces for heuristic formatting, don't change 896 ); 897 } catch (java.awt.print.PrinterException ep) { 898 log.error("While printing",ep); 899 } 900 } 901 902 /** 903 * Match the first argument in the array against a locally-known method. 904 * 905 * @param args Array of arguments, we take with element 0 906 */ 907 @Override 908 public void remoteCalls(String[] args) { 909 args[0] = args[0].toLowerCase(); 910 switch (args[0]) { 911 case "identifyloco": 912 startIdentifyLoco(); 913 break; 914 case "printcurrenttable": 915 printCurrentTable(); 916 break; 917 case "printloco": 918 if (checkIfEntrySelected()) { 919 printLoco(false); 920 } 921 break; 922 case "printpreviewloco": 923 if (checkIfEntrySelected()) { 924 printLoco(true); 925 } 926 break; 927 case "exportloco": 928 if (checkIfEntrySelected()) { 929 exportLoco(); 930 } 931 break; 932 case "basicprogrammer": 933 if (checkIfEntrySelected()) { 934 startProgrammer(null, re, programmer2); 935 } 936 break; 937 case "comprehensiveprogrammer": 938 if (checkIfEntrySelected()) { 939 startProgrammer(null, re, programmer1); 940 } 941 break; 942 case "editthrottlelabels": 943 if (checkIfEntrySelected()) { 944 startProgrammer(null, re, "dp3" + File.separator + "ThrottleLabels"); 945 } 946 break; 947 case "editrostermedia": 948 if (checkIfEntrySelected()) { 949 startProgrammer(null, re, "dp3" + File.separator + "MediaPane"); 950 } 951 break; 952 case "hiderosterimage": 953 hideRosterImage(); 954 break; 955 case "summarypane": 956 hideSummary(); 957 break; 958 case "copyloco": 959 if (checkIfEntrySelected()) { 960 copyLoco(); 961 } 962 break; 963 case "deleteloco": 964 if (checkIfEntrySelected(true)) { 965 deleteLoco(); 966 } 967 break; 968 case "setprogservice": 969 service.setSelected(true); 970 break; 971 case "setprogops": 972 ops.setSelected(true); 973 break; 974 case "setprogedit": 975 edit.setSelected(true); 976 break; 977 case "groupspane": 978 hideGroups(); 979 break; 980 case "quit": 981 saveWindowDetails(); 982 handleQuit(new WindowEvent(this, frameInstances.size())); 983 break; 984 case "closewindow": 985 closeWindow(null); 986 break; 987 case "newwindow": 988 newWindow(); 989 break; 990 case "resettablecolumns": 991 rtable.resetColumnWidths(); 992 break; 993 default: 994 log.error("method {} not found", args[0]); 995 break; 996 } 997 } 998 999 JPanel rosterDetails() { 1000 JPanel panel = new JPanel(); 1001 GridBagLayout gbLayout = new GridBagLayout(); 1002 GridBagConstraints cL = new GridBagConstraints(); 1003 GridBagConstraints cR = new GridBagConstraints(); 1004 Dimension minFieldDim = new Dimension(30, 20); 1005 cL.gridx = 0; 1006 cL.gridy = 0; 1007 cL.ipadx = 3; 1008 cL.anchor = GridBagConstraints.EAST; 1009 cL.insets = new Insets(0, 0, 0, 15); 1010 JLabel row0Label = new JLabel(Bundle.getMessage("FieldID") + ":", JLabel.LEFT); 1011 gbLayout.setConstraints(row0Label, cL); 1012 panel.setLayout(gbLayout); 1013 panel.add(row0Label); 1014 cR.gridx = 1; 1015 cR.gridy = 0; 1016 cR.anchor = GridBagConstraints.WEST; 1017 id.setMinimumSize(minFieldDim); 1018 gbLayout.setConstraints(id, cR); 1019 formatTextAreaAsLabel(id); 1020 panel.add(id); 1021 cL.gridy = 1; 1022 JLabel row1Label = new JLabel(Bundle.getMessage("FieldRoadName") + ":", JLabel.LEFT); 1023 gbLayout.setConstraints(row1Label, cL); 1024 panel.add(row1Label); 1025 cR.gridy = 1; 1026 roadName.setMinimumSize(minFieldDim); 1027 gbLayout.setConstraints(roadName, cR); 1028 formatTextAreaAsLabel(roadName); 1029 panel.add(roadName); 1030 cL.gridy = 2; 1031 JLabel row2Label = new JLabel(Bundle.getMessage("FieldRoadNumber") + ":"); 1032 gbLayout.setConstraints(row2Label, cL); 1033 panel.add(row2Label); 1034 cR.gridy = 2; 1035 roadNumber.setMinimumSize(minFieldDim); 1036 gbLayout.setConstraints(roadNumber, cR); 1037 formatTextAreaAsLabel(roadNumber); 1038 panel.add(roadNumber); 1039 cL.gridy = 3; 1040 JLabel row3Label = new JLabel(Bundle.getMessage("FieldManufacturer") + ":"); 1041 gbLayout.setConstraints(row3Label, cL); 1042 panel.add(row3Label); 1043 cR.gridy = 3; 1044 mfg.setMinimumSize(minFieldDim); 1045 gbLayout.setConstraints(mfg, cR); 1046 formatTextAreaAsLabel(mfg); 1047 panel.add(mfg); 1048 cL.gridy = 4; 1049 JLabel row4Label = new JLabel(Bundle.getMessage("FieldOwner") + ":"); 1050 gbLayout.setConstraints(row4Label, cL); 1051 panel.add(row4Label); 1052 cR.gridy = 4; 1053 owner.setMinimumSize(minFieldDim); 1054 gbLayout.setConstraints(owner, cR); 1055 formatTextAreaAsLabel(owner); 1056 panel.add(owner); 1057 cL.gridy = 5; 1058 JLabel row5Label = new JLabel(Bundle.getMessage("FieldModel") + ":"); 1059 gbLayout.setConstraints(row5Label, cL); 1060 panel.add(row5Label); 1061 cR.gridy = 5; 1062 model.setMinimumSize(minFieldDim); 1063 gbLayout.setConstraints(model, cR); 1064 formatTextAreaAsLabel(model); 1065 panel.add(model); 1066 cL.gridy = 6; 1067 JLabel row6Label = new JLabel(Bundle.getMessage("FieldDCCAddress") + ":"); 1068 gbLayout.setConstraints(row6Label, cL); 1069 panel.add(row6Label); 1070 cR.gridy = 6; 1071 dccAddress.setMinimumSize(minFieldDim); 1072 gbLayout.setConstraints(dccAddress, cR); 1073 formatTextAreaAsLabel(dccAddress); 1074 panel.add(dccAddress); 1075 cL.gridy = 7; 1076 cR.gridy = 7; 1077 cL.gridy = 8; 1078 cR.gridy = 8; 1079 cL.gridy = 9; 1080 JLabel row9Label = new JLabel(Bundle.getMessage("FieldDecoderFamily") + ":"); 1081 gbLayout.setConstraints(row9Label, cL); 1082 panel.add(row9Label); 1083 cR.gridy = 9; 1084 decoderFamily.setMinimumSize(minFieldDim); 1085 gbLayout.setConstraints(decoderFamily, cR); 1086 formatTextAreaAsLabel(decoderFamily); 1087 panel.add(decoderFamily); 1088 cL.gridy = 10; 1089 JLabel row10Label = new JLabel(Bundle.getMessage("FieldDecoderModel") + ":"); 1090 gbLayout.setConstraints(row10Label, cL); 1091 panel.add(row10Label); 1092 cR.gridy = 10; 1093 decoderModel.setMinimumSize(minFieldDim); 1094 gbLayout.setConstraints(decoderModel, cR); 1095 formatTextAreaAsLabel(decoderModel); 1096 panel.add(decoderModel); 1097 cL.gridy = 11; 1098 cR.gridy = 11; 1099 cL.gridy = 12; 1100 JLabel row12Label = new JLabel(Bundle.getMessage("FieldFilename") + ":"); 1101 gbLayout.setConstraints(row12Label, cL); 1102 panel.add(row12Label); 1103 cR.gridy = 12; 1104 filename.setMinimumSize(minFieldDim); 1105 gbLayout.setConstraints(filename, cR); 1106 formatTextAreaAsLabel(filename); 1107 panel.add(filename); 1108 cL.gridy = 13; 1109 /* 1110 * JLabel row13Label = new 1111 * JLabel(Bundle.getMessage("FieldDateUpdated")+":"); 1112 * gbLayout.setConstraints(row13Label,cL); panel.add(row13Label); 1113 */ 1114 cR.gridy = 13; 1115 /* 1116 * filename.setMinimumSize(minFieldDim); 1117 * gbLayout.setConstraints(dateUpdated,cR); panel.add(dateUpdated); 1118 */ 1119 formatTextAreaAsLabel(dateUpdated); 1120 JPanel retval = new JPanel(new FlowLayout(FlowLayout.LEFT)); 1121 retval.add(panel); 1122 return retval; 1123 } 1124 1125 void saveWindowDetails() { 1126 prefsMgr.setSimplePreferenceState(this.getClass().getName() + ".hideSummary", hideBottomPane); 1127 prefsMgr.setSimplePreferenceState(this.getClass().getName() + ".hideGroups", hideGroups); 1128 prefsMgr.setSimplePreferenceState(this.getClass().getName() + ".hideRosterImage", hideRosterImage); 1129 prefsMgr.setProperty(getWindowFrameRef(), SELECTED_ROSTER_GROUP, groups.getSelectedRosterGroup()); 1130 String selectedProgMode = "edit"; 1131 if (service.isSelected()) { 1132 selectedProgMode = "service"; 1133 } 1134 if (ops.isSelected()) { 1135 selectedProgMode = "ops"; 1136 } 1137 prefsMgr.setProperty(getWindowFrameRef(), "selectedProgrammer", selectedProgMode); 1138 1139 if (rosterGroupSplitPane.getDividerLocation() > 2) { 1140 prefsMgr.setProperty(getWindowFrameRef(), "rosterGroupPaneDividerLocation", rosterGroupSplitPane.getDividerLocation()); 1141 } else if (groupSplitPaneLocation > 2) { 1142 prefsMgr.setProperty(getWindowFrameRef(), "rosterGroupPaneDividerLocation", groupSplitPaneLocation); 1143 } 1144 } 1145 1146 /** 1147 * Identify locomotive complete, act on it by setting the GUI. This will 1148 * fire "GUI changed" events which will reset the decoder GUI. 1149 * 1150 * @param dccAddress address of locomotive 1151 * @param isLong true if address is long; false if short 1152 * @param mfgId manufacturer id as in decoder 1153 * @param modelId model id as in decoder 1154 */ 1155 protected void selectLoco(int dccAddress, boolean isLong, int mfgId, int modelId) { 1156 // raise the button again 1157 // idloco.setSelected(false); 1158 // locate that loco 1159 inStartProgrammer = false; 1160 if (re != null) { 1161 //We remove the propertychangelistener if we had a previoulsy selected entry; 1162 re.removePropertyChangeListener(rosterEntryUpdateListener); 1163 } 1164 List<RosterEntry> l = Roster.getDefault().matchingList(null, null, Integer.toString(dccAddress), null, null, null, null); 1165 log.debug("selectLoco found {} matches", l.size()); 1166 if (!l.isEmpty()) { 1167 if (l.size() > 1) { 1168 //More than one possible loco, so check long flag 1169 List<RosterEntry> l2 = new ArrayList<>(); 1170 for (RosterEntry _re : l) { 1171 if (_re.isLongAddress() == isLong) { 1172 l2.add(_re); 1173 } 1174 } 1175 if (l2.size() == 1) { 1176 re = l2.get(0); 1177 } else { 1178 if (l2.isEmpty()) { 1179 l2 = l; 1180 } 1181 // Still more than one possible loco, so check against the decoder family 1182 log.trace("Checking against decoder family with mfg {} model {}", mfgId, modelId); 1183 List<RosterEntry> l3 = new ArrayList<>(); 1184 List<DecoderFile> temp = InstanceManager.getDefault(DecoderIndexFile.class).matchingDecoderList(null, null, "" + mfgId, "" + modelId, null, null); 1185 log.trace("found {}", temp.size()); 1186 ArrayList<String> decoderFam = new ArrayList<>(); 1187 for (DecoderFile f : temp) { 1188 if (!decoderFam.contains(f.getModel())) { 1189 decoderFam.add(f.getModel()); 1190 } 1191 } 1192 log.trace("matched {} times", decoderFam.size()); 1193 1194 for (RosterEntry _re : l2) { 1195 if (decoderFam.contains(_re.getDecoderModel())) { 1196 l3.add(_re); 1197 } 1198 } 1199 if (l3.isEmpty()) { 1200 //Unable to determine the loco against the manufacture therefore will be unable to further identify against decoder. 1201 re = l2.get(0); 1202 } else { 1203 //We have no other options to match against so will return the first one we come across; 1204 re = l3.get(0); 1205 } 1206 } 1207 } else { 1208 re = l.get(0); 1209 } 1210 re.addPropertyChangeListener(rosterEntryUpdateListener); 1211 rtable.setSelection(re); 1212 updateDetails(); 1213 rtable.moveTableViewToSelected(); 1214 } else { 1215 log.warn("Read address {}, but no such loco in roster", dccAddress); //"No roster entry found; changed to promote the number to the front, June 2022, Bill Chown" 1216 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("NotFoundError", dccAddress), Bundle.getMessage("NotFoundErrorTitle", dccAddress), JmriJOptionPane.INFORMATION_MESSAGE); 1217 } 1218 } 1219 1220 /** 1221 * Simple method to change over the programmer buttons. 1222 * <p> 1223 * TODO This should be implemented with the buttons in their own class etc. 1224 * but this will work for now. 1225 * 1226 * @param buttonId 1 or 2; use 1 for basic programmer; 2 for comprehensive 1227 * programmer 1228 * @param programmer name of programmer 1229 * @param buttonText button title 1230 */ 1231 public void setProgrammerLaunch(int buttonId, String programmer, String buttonText) { 1232 if (buttonId == 1) { 1233 programmer1 = programmer; 1234 prog1Button.setText(buttonText); 1235 } else if (buttonId == 2) { 1236 programmer2 = programmer; 1237 prog2Button.setText(buttonText); 1238 } 1239 } 1240 1241 public void setSelectedRosterGroup(String rosterGroup) { 1242 groups.setSelectedRosterGroup(rosterGroup); 1243 } 1244 1245 protected void showPopup(JmriMouseEvent e) { 1246 int row = rtable.getTable().rowAtPoint(e.getPoint()); 1247 if (!rtable.getTable().isRowSelected(row)) { 1248 rtable.getTable().changeSelection(row, 0, false, false); 1249 } 1250 JPopupMenu popupMenu = new JPopupMenu(); 1251 1252 JMenuItem menuItem = new JMenuItem(Bundle.getMessage("Program")); 1253 menuItem.addActionListener((ActionEvent e1) -> startProgrammer(null, re, programmer1)); 1254 if (re == null) { 1255 menuItem.setEnabled(false); 1256 } 1257 popupMenu.add(menuItem); 1258 ButtonGroup group = new ButtonGroup(); 1259 group.add(contextService); 1260 group.add(contextOps); 1261 group.add(contextEdit); 1262 JMenu progMenu = new JMenu(Bundle.getMessage("ProgrammerType")); 1263 contextService.addActionListener((ActionEvent e1) -> { 1264 service.setSelected(true); 1265 updateProgMode(); 1266 }); 1267 progMenu.add(contextService); 1268 contextOps.addActionListener((ActionEvent e1) -> { 1269 ops.setSelected(true); 1270 updateProgMode(); 1271 }); 1272 progMenu.add(contextOps); 1273 contextEdit.addActionListener((ActionEvent e1) -> { 1274 edit.setSelected(true); 1275 updateProgMode(); 1276 }); 1277 if (service.isSelected()) { 1278 contextService.setSelected(true); 1279 } else if (ops.isSelected()) { 1280 contextOps.setSelected(true); 1281 } else { 1282 contextEdit.setSelected(true); 1283 } 1284 progMenu.add(contextEdit); 1285 popupMenu.add(progMenu); 1286 1287 popupMenu.addSeparator(); 1288 menuItem = new JMenuItem(Bundle.getMessage("LabelsAndMedia")); 1289 menuItem.addActionListener((ActionEvent e1) -> editMediaButton()); 1290 if (re == null) { 1291 menuItem.setEnabled(false); 1292 } 1293 popupMenu.add(menuItem); 1294 menuItem = new JMenuItem(Bundle.getMessage("Throttle")); 1295 menuItem.addActionListener((ActionEvent e1) -> { 1296 ThrottleControllersUIContainer tw = null; 1297 for (RosterEntry re : rtable.getSelectedRosterEntries()) { 1298 ThrottleControllerUI tf; 1299 if (tw == null) { 1300 tf = InstanceManager.getDefault(ThrottleFrameManager.class).createThrottleController(); 1301 tw = tf.getThrottleControllersContainer(); 1302 } else { 1303 tf = tw.newThrottleController(); 1304 } 1305 tf.toFront(); 1306 tf.setRosterEntry(re); 1307 } 1308 }); 1309 popupMenu.add(menuItem); 1310 popupMenu.addSeparator(); 1311 1312 menuItem = new JMenuItem(Bundle.getMessage("PrintSelection")); 1313 menuItem.addActionListener((ActionEvent e1) -> printLoco(false)); 1314 if (re == null) { 1315 menuItem.setEnabled(false); 1316 } 1317 popupMenu.add(menuItem); 1318 menuItem = new JMenuItem(Bundle.getMessage("PreviewSelection")); 1319 menuItem.addActionListener((ActionEvent e1) -> printLoco(true)); 1320 if (re == null) { 1321 menuItem.setEnabled(false); 1322 } 1323 popupMenu.add(menuItem); 1324 popupMenu.addSeparator(); 1325 1326 menuItem = new JMenuItem(Bundle.getMessage("Duplicateddd")); 1327 menuItem.addActionListener((ActionEvent e1) -> copyLoco()); 1328 if (re == null) { 1329 menuItem.setEnabled(false); 1330 } 1331 popupMenu.add(menuItem); 1332 boolean deleteFromGroup = this.getSelectedRosterGroup() != null && !this.getSelectedRosterGroup().equals(Roster.NOGROUP); 1333 menuItem = new JMenuItem(deleteFromGroup ? Bundle.getMessage("DeleteFromGroup") : Bundle.getMessage("DeleteFromRoster")); // NOI18N 1334 menuItem.addActionListener((ActionEvent e1) -> deleteLoco()); 1335 popupMenu.add(menuItem); 1336 menuItem.setEnabled(this.getSelectedRosterEntries().length > 0); 1337 1338 menuItem = new JMenuItem(new jmri.jmrit.roster.swing.RosterEntryToGroupAction(Bundle.getMessage("AddToGroup"), re)); 1339 if (re == null) { 1340 menuItem.setEnabled(false); 1341 } 1342 popupMenu.add(menuItem); 1343 1344 popupMenu.show(e.getComponent(), e.getX(), e.getY()); 1345 } 1346 1347 /** 1348 * Start the identify operation after [Identify Loco] button pressed. 1349 * <p> 1350 * This defines what happens when Identify is done. 1351 */ 1352 //taken out of CombinedLocoSelPane 1353 protected void startIdentifyLoco() { 1354 final RosterFrame me = this; 1355 Programmer programmer = null; 1356 if (modePanel.isSelected()) { 1357 programmer = modePanel.getProgrammer(); 1358 } 1359 if (programmer == null) { 1360 GlobalProgrammerManager gpm = InstanceManager.getNullableDefault(GlobalProgrammerManager.class); 1361 if (gpm != null) { 1362 programmer = gpm.getGlobalProgrammer(); 1363 log.warn("Selector did not provide a programmer, attempt to use GlobalProgrammerManager default: {}", programmer); 1364 } else { 1365 log.warn("Selector did not provide a programmer, and no ProgramManager found in InstanceManager"); 1366 } 1367 } 1368 1369 // if failed to get programmer, tell user and stop 1370 if (programmer == null) { 1371 log.error("Identify loco called when no service mode programmer is available; button should have been disabled"); 1372 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("IdentifyError")); 1373 return; 1374 } 1375 1376 // and now do the work 1377 IdentifyLoco ident = new IdentifyLoco(programmer) { 1378 private final RosterFrame who = me; 1379 1380 @Override 1381 protected void done(int dccAddress) { 1382 // if Done, updated the selected decoder 1383 // on the GUI thread, right now 1384 jmri.util.ThreadingUtil.runOnGUI(() -> who.selectLoco(dccAddress, !shortAddr, cv8val, cv7val)); 1385 } 1386 1387 @Override 1388 protected void message(String m) { 1389 // on the GUI thread, right now 1390 jmri.util.ThreadingUtil.runOnGUI(() -> statusField.setText(m)); 1391 } 1392 1393 @Override 1394 protected void error() { 1395 // raise the button again 1396 //idloco.setSelected(false); 1397 } 1398 }; 1399 ident.start(); 1400 } 1401 1402 protected void startProgrammer(DecoderFile decoderFile, RosterEntry re, String filename) { 1403 if (inStartProgrammer) { 1404 log.debug("Call to start programmer has been called twice when the first call hasn't opened"); 1405 return; 1406 } 1407 if (!checkIfEntrySelected()) { 1408 return; 1409 } 1410 try { 1411 setCursor(new Cursor(Cursor.WAIT_CURSOR)); 1412 inStartProgrammer = true; 1413 String title = re.getId(); 1414 JFrame progFrame = null; 1415 if (edit.isSelected()) { 1416 progFrame = new PaneProgFrame(decoderFile, re, title, "programmers" + File.separator + filename + ".xml", null, false) { 1417 @Override 1418 protected JPanel getModePane() { 1419 return null; 1420 } // hide prog mode buttons pane 1421 }; 1422 } else if (service.isSelected()) { 1423 progFrame = new PaneServiceProgFrame(decoderFile, re, title, "programmers" + File.separator + filename + ".xml", modePanel.getProgrammer()); 1424 } else if (ops.isSelected()) { 1425 int address = Integer.parseInt(re.getDccAddress()); 1426 boolean longAddr = re.isLongAddress(); 1427 Programmer pProg = InstanceManager.getDefault(AddressedProgrammerManager.class).getAddressedProgrammer(longAddr, address); 1428 progFrame = new PaneOpsProgFrame(decoderFile, re, title, "programmers" + File.separator + filename + ".xml", pProg); 1429 } 1430 if (progFrame == null) { 1431 return; 1432 } 1433 progFrame.pack(); 1434 progFrame.setVisible(true); 1435 } finally { 1436 setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); 1437 } 1438 inStartProgrammer = false; 1439 } 1440 1441 /** 1442 * Create and display a status bar along the bottom edge of the Roster main 1443 * pane. 1444 * <p> 1445 * TODO This status bar needs sorting out properly 1446 */ 1447 protected void statusBar() { 1448 addToStatusBox(serviceModeProgrammerLabel, null); 1449 addToStatusBox(operationsModeProgrammerLabel, null); 1450 JLabel programmerStatusLabel = new JLabel(Bundle.getMessage("ProgrammerStatus")); 1451 statusField.setText(Bundle.getMessage("StateIdle")); 1452 addToStatusBox(programmerStatusLabel, statusField); 1453 Profile profile = ProfileManager.getDefault().getActiveProfile(); 1454 if (profile != null) { 1455 addToStatusBox(new JLabel(Bundle.getMessage("ActiveProfile", profile.getName())), null); 1456 } 1457 } 1458 1459 protected void systemsMenu() { 1460 ActiveSystemsMenu.addItems(getMenu()); 1461 getMenu().add(new WindowMenu(this)); 1462 } 1463 1464 void updateDetails() { 1465 if (re == null) { 1466 String value = (rtable.getTable().getSelectedRowCount() > 1) ? "Multiple Items Selected" : ""; 1467 filename.setText(value); 1468 dateUpdated.setText(value); 1469 decoderModel.setText(value); 1470 decoderFamily.setText(value); 1471 id.setText(value); 1472 roadName.setText(value); 1473 dccAddress.setText(value); 1474 roadNumber.setText(value); 1475 mfg.setText(value); 1476 model.setText(value); 1477 owner.setText(value); 1478 locoImage.setImagePath(null); 1479 service.setEnabled(false); 1480 ops.setEnabled(false); 1481 edit.setEnabled(false); 1482 prog1Button.setEnabled(false); 1483 prog2Button.setEnabled(false); 1484 throttleLabels.setEnabled(false); 1485 rosterMedia.setEnabled(false); 1486 throttleLaunch.setEnabled(false ); 1487 } else { 1488 filename.setText(re.getFileName()); 1489 dateUpdated.setText((re.getDateModified() != null) 1490 ? DateFormat.getDateTimeInstance().format(re.getDateModified()) 1491 : re.getDateUpdated()); 1492 decoderModel.setText(re.getDecoderModel()); 1493 decoderFamily.setText(re.getDecoderFamily()); 1494 dccAddress.setText(re.getDccAddress()); 1495 id.setText(re.getId()); 1496 roadName.setText(re.getRoadName()); 1497 roadNumber.setText(re.getRoadNumber()); 1498 mfg.setText(re.getMfg()); 1499 model.setText(re.getModel()); 1500 owner.setText(re.getOwner()); 1501 locoImage.setImagePath(re.getImagePath()); 1502 if (hideRosterImage) { 1503 locoImage.setVisible(false); 1504 } else { 1505 locoImage.setVisible(true); 1506 } 1507 service.setEnabled(isProgrammingTrackEnabled()); 1508 ops.setEnabled(isProgrammingOnMainEnabled()); 1509 edit.setEnabled(true); 1510 prog1Button.setEnabled(true); 1511 prog2Button.setEnabled(true); 1512 throttleLabels.setEnabled(true); 1513 rosterMedia.setEnabled(true); 1514 throttleLaunch.setEnabled(true); 1515 updateProgMode(); 1516 } 1517 } 1518 1519 void updateProgMode() { 1520 String progMode; 1521 if (service.isSelected()) { 1522 progMode = "setprogservice"; 1523 } else if (ops.isSelected()) { 1524 progMode = "setprogops"; 1525 } else { 1526 progMode = "setprogedit"; 1527 } 1528 firePropertyChange(progMode, "setSelected", true); 1529 } 1530 1531 /** 1532 * Handle setting up and updating the GUI for the types of programmer 1533 * available. 1534 * 1535 * @param evt the triggering event; if not null and if a removal of a 1536 * ProgrammerManager, care will be taken not to trigger the 1537 * automatic creation of a new ProgrammerManager 1538 */ 1539 protected void updateProgrammerStatus(@CheckForNull PropertyChangeEvent evt) { 1540 log.debug("Updating Programmer Status"); 1541 ConnectionConfig oldServMode = serModeProCon; 1542 ConnectionConfig oldOpsMode = opsModeProCon; 1543 GlobalProgrammerManager gpm = null; 1544 AddressedProgrammerManager apm = null; 1545 1546 // Find the connection that goes with the global programmer 1547 // test that IM has a default GPM, or that event is not the removal of a GPM 1548 if (InstanceManager.containsDefault(GlobalProgrammerManager.class) 1549 || (evt != null 1550 && evt.getPropertyName().equals(InstanceManager.getDefaultsPropertyName(GlobalProgrammerManager.class)) 1551 && evt.getNewValue() == null)) { 1552 gpm = InstanceManager.getNullableDefault(GlobalProgrammerManager.class); 1553 log.trace("found global programming manager {}", gpm); 1554 } 1555 if (gpm != null) { 1556 String serviceModeProgrammerName = gpm.getUserName(); 1557 log.debug("GlobalProgrammerManager found of class {} name {} ", gpm.getClass(), serviceModeProgrammerName); 1558 InstanceManager.getOptionalDefault(ConnectionConfigManager.class).ifPresent((ccm) -> { 1559 for (ConnectionConfig connection : ccm) { 1560 log.debug("Checking connection name {}", connection.getConnectionName()); 1561 if (connection.getConnectionName() != null && connection.getConnectionName().equals(serviceModeProgrammerName)) { 1562 log.debug("Connection found for GlobalProgrammermanager"); 1563 serModeProCon = connection; 1564 } 1565 } 1566 }); 1567 } 1568 1569 // Find the connection that goes with the addressed programmer 1570 // test that IM has a default APM, or that event is not the removal of an APM 1571 if (InstanceManager.containsDefault(AddressedProgrammerManager.class) 1572 || (evt != null 1573 && evt.getPropertyName().equals(InstanceManager.getDefaultsPropertyName(AddressedProgrammerManager.class)) 1574 && evt.getNewValue() == null)) { 1575 apm = InstanceManager.getNullableDefault(AddressedProgrammerManager.class); 1576 log.trace("found addressed programming manager {}", gpm); 1577 } 1578 if (apm != null) { 1579 String opsModeProgrammerName = apm.getUserName(); 1580 log.debug("AddressedProgrammerManager found of class {} name {} ", apm.getClass(), opsModeProgrammerName); 1581 InstanceManager.getOptionalDefault(ConnectionConfigManager.class).ifPresent((ccm) -> { 1582 for (ConnectionConfig connection : ccm) { 1583 log.debug("Checking connection name {}", connection.getConnectionName()); 1584 if (connection.getConnectionName() != null && connection.getConnectionName().equals(opsModeProgrammerName)) { 1585 log.debug("Connection found for AddressedProgrammermanager"); 1586 opsModeProCon = connection; 1587 } 1588 } 1589 }); 1590 } 1591 1592 log.trace("start global check with {}, {}, {}", serModeProCon, gpm, (gpm != null ? gpm.isGlobalProgrammerAvailable() : "<none>")); 1593 if (serModeProCon != null && gpm != null && gpm.isGlobalProgrammerAvailable()) { 1594 if (ConnectionStatus.instance().isConnectionOk(serModeProCon.getConnectionName(), serModeProCon.getInfo())) { 1595 log.debug("GPM Connection online 1"); 1596 serviceModeProgrammerLabel.setText( 1597 Bundle.getMessage("ServiceModeProgOnline", serModeProCon.getConnectionName())); 1598 serviceModeProgrammerLabel.setForeground(new Color(0, 128, 0)); 1599 } else { 1600 log.debug("GPM Connection offline"); 1601 serviceModeProgrammerLabel.setText( 1602 Bundle.getMessage("ServiceModeProgOffline", serModeProCon.getConnectionName())); 1603 serviceModeProgrammerLabel.setForeground(Color.red); 1604 } 1605 if (oldServMode == null) { 1606 log.debug("Re-enable user interface"); 1607 contextService.setEnabled(isProgrammingTrackEnabled()); 1608 contextService.setVisible(true); 1609 service.setEnabled(isProgrammingTrackEnabled()); 1610 service.setVisible(true); 1611 firePropertyChange("setprogservice", "setEnabled", true); 1612 getToolBar().getComponents()[1].setEnabled(true); 1613 } 1614 } else if (gpm != null && gpm.isGlobalProgrammerAvailable()) { 1615 if (ConnectionStatus.instance().isSystemOk(gpm.getUserName())) { 1616 log.debug("GPM Connection online 2"); 1617 serviceModeProgrammerLabel.setText( 1618 Bundle.getMessage("ServiceModeProgOnline", gpm.getUserName())); 1619 serviceModeProgrammerLabel.setForeground(new Color(0, 128, 0)); 1620 } else { 1621 log.debug("GPM Connection onffline"); 1622 serviceModeProgrammerLabel.setText( 1623 Bundle.getMessage("ServiceModeProgOffline", gpm.getUserName())); 1624 serviceModeProgrammerLabel.setForeground(Color.red); 1625 } 1626 if (oldServMode == null) { 1627 log.debug("Re-enable user interface"); 1628 contextService.setEnabled(isProgrammingTrackEnabled()); 1629 contextService.setVisible(true); 1630 service.setEnabled(isProgrammingTrackEnabled()); 1631 service.setVisible(true); 1632 firePropertyChange("setprogservice", "setEnabled", true); 1633 getToolBar().getComponents()[1].setEnabled(true); 1634 } 1635 } else { 1636 // No service programmer available, disable interface sections not available 1637 log.debug("no service programmer"); 1638 serviceModeProgrammerLabel.setText(Bundle.getMessage("NoServiceProgrammerAvailable")); 1639 serviceModeProgrammerLabel.setForeground(Color.red); 1640 if (oldServMode != null) { 1641 contextService.setEnabled(false); 1642 contextService.setVisible(false); 1643 service.setEnabled(false); 1644 service.setVisible(false); 1645 firePropertyChange("setprogservice", "setEnabled", false); 1646 } 1647 // Disable Identify in toolBar 1648 // This relies on it being the 2nd item in the toolbar, as defined in xml//config/parts/jmri/jmrit/roster/swing/RosterFrameToolBar.xml 1649 // Because of I18N, we don't look for a particular Action name here 1650 getToolBar().getComponents()[1].setEnabled(false); 1651 serModeProCon = null; 1652 } 1653 1654 if (opsModeProCon != null && apm != null && apm.isAddressedModePossible()) { 1655 if (ConnectionStatus.instance().isConnectionOk(opsModeProCon.getConnectionName(), opsModeProCon.getInfo())) { 1656 log.debug("Ops Mode Connection online"); 1657 operationsModeProgrammerLabel.setText( 1658 Bundle.getMessage("OpsModeProgOnline", opsModeProCon.getConnectionName())); 1659 operationsModeProgrammerLabel.setForeground(new Color(0, 128, 0)); 1660 } else { 1661 log.debug("Ops Mode Connection offline"); 1662 operationsModeProgrammerLabel.setText( 1663 Bundle.getMessage("OpsModeProgOffline", opsModeProCon.getConnectionName())); 1664 operationsModeProgrammerLabel.setForeground(Color.red); 1665 } 1666 if (oldOpsMode == null) { 1667 contextOps.setEnabled(isProgrammingOnMainEnabled()); 1668 contextOps.setVisible(true); 1669 ops.setEnabled(isProgrammingOnMainEnabled()); 1670 ops.setVisible(true); 1671 firePropertyChange("setprogops", "setEnabled", true); 1672 } 1673 } else if (apm != null && apm.isAddressedModePossible()) { 1674 if (ConnectionStatus.instance().isSystemOk(apm.getUserName())) { 1675 log.debug("Ops Mode Connection online"); 1676 operationsModeProgrammerLabel.setText( 1677 Bundle.getMessage("OpsModeProgOnline", apm.getUserName())); 1678 operationsModeProgrammerLabel.setForeground(new Color(0, 128, 0)); 1679 } else { 1680 log.debug("Ops Mode Connection offline"); 1681 operationsModeProgrammerLabel.setText( 1682 Bundle.getMessage("OpsModeProgOffline", apm.getUserName())); 1683 operationsModeProgrammerLabel.setForeground(Color.red); 1684 } 1685 if (oldOpsMode == null) { 1686 contextOps.setEnabled(isProgrammingOnMainEnabled()); 1687 contextOps.setVisible(true); 1688 ops.setEnabled(isProgrammingOnMainEnabled()); 1689 ops.setVisible(true); 1690 firePropertyChange("setprogops", "setEnabled", true); 1691 } 1692 } else { 1693 // No ops mode programmer available, disable interface sections not available 1694 log.debug("no ops mode programmer"); 1695 operationsModeProgrammerLabel.setText(Bundle.getMessage("NoOpsProgrammerAvailable")); 1696 operationsModeProgrammerLabel.setForeground(Color.red); 1697 if (oldOpsMode != null) { 1698 contextOps.setEnabled(false); 1699 contextOps.setVisible(false); 1700 ops.setEnabled(false); 1701 ops.setVisible(false); 1702 firePropertyChange("setprogops", "setEnabled", false); 1703 } 1704 opsModeProCon = null; 1705 } 1706 String strProgMode; 1707 if (service.isEnabled()) { 1708 contextService.setSelected(true); 1709 service.setSelected(true); 1710 strProgMode = "setprogservice"; 1711 modePanel.setVisible(true); 1712 } else if (ops.isEnabled()) { 1713 contextOps.setSelected(true); 1714 ops.setSelected(true); 1715 strProgMode = "setprogops"; 1716 modePanel.setVisible(false); 1717 } else { 1718 contextEdit.setSelected(true); 1719 edit.setSelected(true); 1720 modePanel.setVisible(false); 1721 strProgMode = "setprogedit"; 1722 } 1723 firePropertyChange(strProgMode, "setSelected", true); 1724 } 1725 1726 private boolean isProgrammingTrackEnabled() { 1727 return InstanceManager.getNullableDefault(ProgrammerConfigManager.class) != null && 1728 ! InstanceManager.getDefault(ProgrammerConfigManager.class).isDisableProgrammingTrack(); 1729 } 1730 1731 private boolean isProgrammingOnMainEnabled() { 1732 return InstanceManager.getNullableDefault(ProgrammerConfigManager.class) != null && 1733 ! InstanceManager.getDefault(ProgrammerConfigManager.class).isDisableProgrammingOnMain(); 1734 } 1735 1736 @Override 1737 public void windowClosing(WindowEvent e) { 1738 closeWindow(e); 1739 super.windowClosing(e); 1740 } 1741 1742 /** 1743 * Displays a context (right-click) menu for a roster entry. 1744 */ 1745 private class RosterPopupListener extends JmriMouseAdapter { 1746 1747 @Override 1748 public void mousePressed(JmriMouseEvent e) { 1749 if (e.isPopupTrigger()) { 1750 showPopup(e); 1751 } 1752 } 1753 1754 @Override 1755 public void mouseReleased(JmriMouseEvent e) { 1756 if (e.isPopupTrigger()) { 1757 showPopup(e); 1758 } 1759 } 1760 1761 @Override 1762 public void mouseClicked(JmriMouseEvent e) { 1763 if (e.isPopupTrigger()) { 1764 showPopup(e); 1765 return; 1766 } 1767 if (e.getClickCount() == 2) { 1768 startProgrammer(null, re, programmer1); 1769 } 1770 } 1771 } 1772 1773 private static class ExportRosterItem extends ExportRosterItemAction { 1774 1775 ExportRosterItem(String pName, Component pWho, RosterEntry re) { 1776 super(pName, pWho); 1777 super.setExistingEntry(re); 1778 } 1779 1780 @Override 1781 protected boolean selectFrom() { 1782 return true; 1783 } 1784 } 1785 1786 private static class CopyRosterItem extends CopyRosterItemAction { 1787 1788 CopyRosterItem(String pName, Component pWho, RosterEntry re) { 1789 super(pName, pWho); 1790 super.setExistingEntry(re); 1791 } 1792 1793 @Override 1794 protected boolean selectFrom() { 1795 return true; 1796 } 1797 } 1798 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(RosterFrame.class); 1799 1800}