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.LargePowerManagerButton; 057import jmri.jmrit.throttle.ThrottleFrame; 058import jmri.jmrit.throttle.ThrottleFrameManager; 059import jmri.jmrix.ActiveSystemsMenu; 060import jmri.jmrix.ConnectionConfig; 061import jmri.jmrix.ConnectionConfigManager; 062import jmri.jmrix.ConnectionStatus; 063import jmri.profile.Profile; 064import jmri.profile.ProfileManager; 065import jmri.swing.JTablePersistenceManager; 066import jmri.swing.RowSorterUtil; 067import jmri.util.FileUtil; 068import jmri.util.HelpUtil; 069import jmri.util.WindowMenu; 070import jmri.util.datatransfer.RosterEntrySelection; 071import jmri.util.swing.JmriAbstractAction; 072import jmri.util.swing.JmriJOptionPane; 073import jmri.util.swing.JmriMouseAdapter; 074import jmri.util.swing.JmriMouseEvent; 075import jmri.util.swing.JmriMouseListener; 076import jmri.util.swing.ResizableImagePanel; 077import jmri.util.swing.WindowInterface; 078import jmri.util.swing.multipane.TwoPaneTBWindow; 079 080/** 081 * A window for Roster management. 082 * <p> 083 * TODO: Several methods are copied from PaneProgFrame and should be refactored 084 * No programmer support yet (dummy object below). Color only covering borders. 085 * No reset toolbar support yet. No glass pane support (See DecoderPro3Panes 086 * class and usage below). Special panes (Roster entry, attributes, graphics) 087 * not included. How do you pick a programmer file? (hardcoded) Initialization 088 * needs partial deferral, too for 1st pane to appear. 089 * 090 * @see jmri.jmrit.symbolicprog.tabbedframe.PaneSet 091 * 092 * @author Bob Jacobsen Copyright (C) 2010, 2016 093 * @author Kevin Dickerson Copyright (C) 2011 094 * @author Randall Wood Copyright (C) 2012 095 */ 096public class RosterFrame extends TwoPaneTBWindow implements RosterEntrySelector, RosterGroupSelector { 097 098 static final ArrayList<RosterFrame> frameInstances = new ArrayList<>(); 099 protected boolean allowQuit = true; 100 protected String baseTitle = "Roster"; 101 protected JmriAbstractAction newWindowAction; 102 103 public RosterFrame() { 104 this(Bundle.getMessage("RosterTitle")); 105 } 106 107 public RosterFrame(String name) { 108 this(name, 109 "xml/config/parts/jmri/jmrit/roster/swing/RosterFrameMenu.xml", 110 "xml/config/parts/jmri/jmrit/roster/swing/RosterFrameToolBar.xml"); 111 } 112 113 public RosterFrame(String name, String menubarFile, String toolbarFile) { 114 super(name, menubarFile, toolbarFile); 115 this.allowInFrameServlet = false; 116 this.setBaseTitle(name); 117 this.buildWindow(); 118 this.locoSelected(null); 119 } 120 121 final JRadioButtonMenuItem contextEdit = new JRadioButtonMenuItem(Bundle.getMessage("EditOnly")); 122 final JRadioButtonMenuItem contextOps = new JRadioButtonMenuItem(Bundle.getMessage("ProgrammingOnMain")); 123 final JRadioButtonMenuItem contextService = new JRadioButtonMenuItem(Bundle.getMessage("ProgrammingTrack")); 124 final JTextPane dateUpdated = new JTextPane(); 125 final JTextPane dccAddress = new JTextPane(); 126 final JTextPane decoderFamily = new JTextPane(); 127 final JTextPane decoderModel = new JTextPane(); 128 final JRadioButton edit = new JRadioButton(Bundle.getMessage("EditOnly")); 129 final JTextPane filename = new JTextPane(); 130 JLabel firstHelpLabel; 131 //int firstTimeAddedEntry = 0x00; 132 int groupSplitPaneLocation = 0; 133 RosterGroupsPanel groups; 134 boolean hideGroups = false; 135 boolean hideRosterImage = false; 136 final JTextPane id = new JTextPane(); 137 boolean inStartProgrammer = false; 138 ResizableImagePanel locoImage; 139 JTextPane maxSpeed = new JTextPane(); 140 final JTextPane mfg = new JTextPane(); 141 final ProgModeSelector modePanel = new ProgServiceModeComboBox(); 142 final JTextPane model = new JTextPane(); 143 final JLabel operationsModeProgrammerLabel = new JLabel(); 144 final JRadioButton ops = new JRadioButton(Bundle.getMessage("ProgrammingOnMain")); 145 ConnectionConfig opsModeProCon = null; 146 final JTextPane owner = new JTextPane(); 147 UserPreferencesManager prefsMgr; 148 final JButton prog1Button = new JButton(Bundle.getMessage("Program")); 149 final JButton prog2Button = new JButton(Bundle.getMessage("BasicProgrammer")); 150 ActionListener programModeListener; 151 152 // These are the names of the programmer _files_, not what should be displayed to the user 153 String programmer1 = "Comprehensive"; // NOI18N 154 String programmer2 = "Basic"; // NOI18N 155 156 final java.util.ResourceBundle rb = java.util.ResourceBundle.getBundle("apps.AppsBundle"); 157 //current selected loco 158 transient RosterEntry re; 159 final JTextPane roadName = new JTextPane(); 160 final JTextPane roadNumber = new JTextPane(); 161 final JPanel rosterDetailPanel = new JPanel(); 162 PropertyChangeListener rosterEntryUpdateListener; 163 JSplitPane rosterGroupSplitPane; 164 final JButton rosterMedia = new JButton(Bundle.getMessage("LabelsAndMedia")); 165 RosterTable rtable; 166 ConnectionConfig serModeProCon = null; 167 final JRadioButton service = new JRadioButton(Bundle.getMessage("ProgrammingTrack")); 168 final JLabel serviceModeProgrammerLabel = new JLabel(); 169 final JLabel statusField = new JLabel(); 170 final Dimension summaryPaneDim = new Dimension(0, 170); 171 final JButton throttleLabels = new JButton(Bundle.getMessage("ThrottleLabels")); 172 final JButton throttleLaunch = new JButton(Bundle.getMessage("Throttle")); 173 174 protected void additionsToToolBar() { 175 getToolBar().add(new LargePowerManagerButton(true)); 176 getToolBar().add(Box.createHorizontalGlue()); 177 JPanel p = new JPanel(); 178 p.setAlignmentX(JPanel.RIGHT_ALIGNMENT); 179 p.add(modePanel); 180 getToolBar().add(p); 181 } 182 183 /** 184 * For use when the DP3 window is called from another JMRI instance, set 185 * this to prevent the DP3 from shutting down JMRI when the window is 186 * closed. 187 * 188 * @param quitAllowed true if closing window should quit application; false 189 * otherwise 190 */ 191 protected void allowQuit(boolean quitAllowed) { 192 if (allowQuit != quitAllowed) { 193 newWindowAction = null; 194 allowQuit = quitAllowed; 195 groups.setNewWindowMenuAction(this.getNewWindowAction()); 196 } 197 198 firePropertyChange("quit", "setEnabled", allowQuit); 199 //if we are not allowing quit, ie opened from JMRI classic 200 //then we must at least allow the window to be closed 201 if (!allowQuit) { 202 firePropertyChange("closewindow", "setEnabled", true); 203 } 204 } 205 206 JPanel bottomRight() { 207 JPanel panel = new JPanel(); 208 panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); 209 ButtonGroup progMode = new ButtonGroup(); 210 progMode.add(service); 211 progMode.add(ops); 212 progMode.add(edit); 213 service.setEnabled(false); 214 ops.setEnabled(false); 215 edit.setEnabled(true); 216 firePropertyChange("setprogservice", "setEnabled", false); 217 firePropertyChange("setprogops", "setEnabled", false); 218 firePropertyChange("setprogedit", "setEnabled", true); 219 ops.setOpaque(false); 220 service.setOpaque(false); 221 edit.setOpaque(false); 222 JPanel progModePanel = new JPanel(); 223 GridLayout buttonLayout = new GridLayout(3, 1, 0, 0); 224 progModePanel.setLayout(buttonLayout); 225 progModePanel.add(service); 226 progModePanel.add(ops); 227 progModePanel.add(edit); 228 programModeListener = (ActionEvent e) -> updateProgMode(); 229 service.addActionListener(programModeListener); 230 ops.addActionListener(programModeListener); 231 edit.addActionListener(programModeListener); 232 service.setVisible(false); 233 ops.setVisible(false); 234 panel.add(progModePanel); 235 JPanel buttonHolder = new JPanel(new GridBagLayout()); 236 GridBagConstraints c = new GridBagConstraints(); 237 c.weightx = 1.0; 238 c.fill = GridBagConstraints.HORIZONTAL; 239 c.anchor = GridBagConstraints.NORTH; 240 c.gridx = 0; 241 c.ipady = 20; 242 c.gridwidth = GridBagConstraints.REMAINDER; 243 c.gridy = 0; 244 c.insets = new Insets(2, 2, 2, 2); 245 buttonHolder.add(prog1Button, c); 246 c.weightx = 1; 247 c.fill = GridBagConstraints.NONE; 248 c.gridx = 0; 249 c.gridy = 1; 250 c.gridwidth = 1; 251 c.ipady = 0; 252 buttonHolder.add(rosterMedia, c); 253 c.weightx = 1.0; 254 c.fill = GridBagConstraints.NONE; 255 c.gridx = 1; 256 c.gridy = 1; 257 c.gridwidth = 1; 258 c.ipady = 0; 259 buttonHolder.add(throttleLaunch, c); 260 //buttonHolder.add(throttleLaunch); 261 panel.add(buttonHolder); 262 prog1Button.setEnabled(false); 263 prog1Button.addActionListener((ActionEvent e) -> { 264 log.debug("Open programmer pressed"); 265 startProgrammer(null, re, programmer1); 266 }); 267 268 rosterMedia.setEnabled(false); 269 rosterMedia.addActionListener((ActionEvent e) -> { 270 log.debug("Open Media pressed"); 271 edit.setSelected(true); 272 startProgrammer(null, re, "dp3" + File.separator + "MediaPane"); 273 }); 274 throttleLaunch.setEnabled(false); 275 throttleLaunch.addActionListener((ActionEvent e) -> { 276 log.debug("Launch Throttle pressed"); 277 if (!checkIfEntrySelected()) { 278 return; 279 } 280 ThrottleFrame tf = InstanceManager.getDefault(ThrottleFrameManager.class).createThrottleFrame(); 281 tf.toFront(); 282 tf.getAddressPanel().setRosterEntry(re); 283 }); 284 return panel; 285 } 286 287 protected final void buildWindow() { 288 //Additions to the toolbar need to be added first otherwise when trying to hide bits up during the initialisation they remain on screen 289 additionsToToolBar(); 290 frameInstances.add(this); 291 prefsMgr = InstanceManager.getDefault(UserPreferencesManager.class); 292 getTop().add(createTop()); 293 getBottom().setMinimumSize(summaryPaneDim); 294 getBottom().add(createBottom()); 295 statusBar(); 296 systemsMenu(); 297 helpMenu(getMenu(), this); 298 if ((!prefsMgr.getSimplePreferenceState(this.getClass().getName() + ".hideGroups")) && !Roster.getDefault().getRosterGroupList().isEmpty()) { 299 hideGroupsPane(false); 300 } else { 301 hideGroupsPane(true); 302 } 303 if (prefsMgr.getSimplePreferenceState(this.getClass().getName() + ".hideSummary")) { 304 //We have to set it to display first, then we can hide it. 305 hideBottomPane(false); 306 hideBottomPane(true); 307 } 308 PropertyChangeListener propertyChangeListener = (PropertyChangeEvent changeEvent) -> { 309 JSplitPane sourceSplitPane = (JSplitPane) changeEvent.getSource(); 310 String propertyName = changeEvent.getPropertyName(); 311 if (propertyName.equals(JSplitPane.LAST_DIVIDER_LOCATION_PROPERTY)) { 312 int current = sourceSplitPane.getDividerLocation() + sourceSplitPane.getDividerSize(); 313 int panesize = (int) (sourceSplitPane.getSize().getHeight()); 314 hideBottomPane = panesize - current <= 1; 315 //p.setSimplePreferenceState(DecoderPro3Window.class.getName()+".hideSummary",hideSummary); 316 } 317 }; 318 updateProgrammerStatus(null); 319 ConnectionStatus.instance().addPropertyChangeListener((PropertyChangeEvent e) -> { 320 if ((e.getPropertyName().equals("change")) || (e.getPropertyName().equals("add"))) { 321 log.debug("Received property {} with value {} ", e.getPropertyName(), e.getNewValue()); 322 updateProgrammerStatus(e); 323 } 324 }); 325 InstanceManager.addPropertyChangeListener(InstanceManager.getListPropertyName(AddressedProgrammerManager.class), 326 evt -> { 327 log.debug("Received property {} with value {} ", evt.getPropertyName(), evt.getNewValue()); 328 AddressedProgrammerManager m = (AddressedProgrammerManager) evt.getNewValue(); 329 if (m != null) { 330 m.addPropertyChangeListener(this::updateProgrammerStatus); 331 } 332 updateProgrammerStatus(evt); 333 }); 334 InstanceManager.getList(AddressedProgrammerManager.class).forEach(m -> m.addPropertyChangeListener(this::updateProgrammerStatus)); 335 InstanceManager.addPropertyChangeListener(InstanceManager.getListPropertyName(GlobalProgrammerManager.class), 336 evt -> { 337 log.debug("Received property {} with value {} ", evt.getPropertyName(), evt.getNewValue()); 338 GlobalProgrammerManager m = (GlobalProgrammerManager) evt.getNewValue(); 339 if (m != null) { 340 m.addPropertyChangeListener(this::updateProgrammerStatus); 341 } 342 updateProgrammerStatus(evt); 343 }); 344 InstanceManager.getList(GlobalProgrammerManager.class).forEach(m -> m.addPropertyChangeListener(this::updateProgrammerStatus)); 345 getSplitPane().addPropertyChangeListener(propertyChangeListener); 346 if (this.getProgrammerConfigManager().getDefaultFile() != null) { 347 programmer1 = this.getProgrammerConfigManager().getDefaultFile(); 348 } 349 this.getProgrammerConfigManager().addPropertyChangeListener(ProgrammerConfigManager.DEFAULT_FILE, (PropertyChangeEvent evt) -> { 350 if (this.getProgrammerConfigManager().getDefaultFile() != null) { 351 programmer1 = this.getProgrammerConfigManager().getDefaultFile(); 352 } 353 }); 354 355 String lastProg = (String) prefsMgr.getProperty(getWindowFrameRef(), "selectedProgrammer"); 356 if (lastProg != null) { 357 if (lastProg.equals("service") && service.isEnabled()) { 358 service.setSelected(true); 359 updateProgMode(); 360 } else if (lastProg.equals("ops") && ops.isEnabled()) { 361 ops.setSelected(true); 362 updateProgMode(); 363 } else if (lastProg.equals("edit") && edit.isEnabled()) { 364 edit.setSelected(true); 365 updateProgMode(); 366 } 367 } 368 if (frameInstances.size() > 1) { 369 firePropertyChange("closewindow", "setEnabled", true); 370 allowQuit(frameInstances.get(0).isAllowQuit()); 371 } else { 372 firePropertyChange("closewindow", "setEnabled", false); 373 } 374 } 375 376 boolean checkIfEntrySelected() { 377 return this.checkIfEntrySelected(false); 378 } 379 380 boolean checkIfEntrySelected(boolean allowMultiple) { 381 if ((re == null && !allowMultiple) || (this.getSelectedRosterEntries().length < 1)) { 382 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("ErrorNoSelection")); 383 return false; 384 } 385 return true; 386 } 387 388 //@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 389 void closeWindow(WindowEvent e) { 390 saveWindowDetails(); 391 //Save any changes made in the roster entry details 392 Roster.getDefault().writeRoster(); 393 if (allowQuit && frameInstances.size() == 1 && !InstanceManager.getDefault(ShutDownManager.class).isShuttingDown()) { 394 handleQuit(e); 395 } else { 396 //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 397 frameInstances.remove(this); 398 super.windowClosing(e); 399 if ((frameInstances.size() == 1) && (allowQuit)) { 400 frameInstances.get(0).firePropertyChange("closewindow", "setEnabled", false); 401 } 402 dispose(); 403 } 404 } 405 406 protected void copyLoco() { 407 CopyRosterItem act = new CopyRosterItem("Copy", this, re); 408 act.actionPerformed(null); 409 } 410 411 JComponent createBottom() { 412 locoImage = new ResizableImagePanel(null, 240, 160); 413 locoImage.setBorder(BorderFactory.createLineBorder(Color.blue)); 414 locoImage.setOpaque(true); 415 locoImage.setRespectAspectRatio(true); 416 rosterDetailPanel.setLayout(new BorderLayout()); 417 rosterDetailPanel.add(locoImage, BorderLayout.WEST); 418 rosterDetailPanel.add(rosterDetails(), BorderLayout.CENTER); 419 rosterDetailPanel.add(bottomRight(), BorderLayout.EAST); 420 if (prefsMgr.getSimplePreferenceState(this.getClass().getName() + ".hideRosterImage")) { 421 locoImage.setVisible(false); 422 hideRosterImage = true; 423 } 424 rosterEntryUpdateListener = (PropertyChangeEvent e) -> updateDetails(); 425 return rosterDetailPanel; 426 } 427 428 private boolean isUpdatingSelection = false; 429 430 JComponent createTop() { 431 Object selectedRosterGroup = prefsMgr.getProperty(getWindowFrameRef(), SELECTED_ROSTER_GROUP); 432 groups = new RosterGroupsPanel((selectedRosterGroup != null) ? selectedRosterGroup.toString() : null); 433 groups.setNewWindowMenuAction(this.getNewWindowAction()); 434 setTitle(groups.getSelectedRosterGroup()); 435 final JPanel rosters = new JPanel(); 436 rosters.setLayout(new BorderLayout()); 437 // set up roster table 438 rtable = new RosterTable(true, ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); 439 rtable.setRosterGroup(this.getSelectedRosterGroup()); 440 rtable.setRosterGroupSource(groups); 441 rosters.add(rtable, BorderLayout.CENTER); 442 // add selection listener 443 rtable.getTable().getSelectionModel().addListSelectionListener((ListSelectionEvent e) -> { 444 JTable table = rtable.getTable(); 445 if (!e.getValueIsAdjusting()) { 446 if ((rtable.getSelectedRosterEntries().length == 1 ) && (table.getSelectedRow() >= 0)) { 447 log.debug("Selected row {}", table.getSelectedRow()); 448 locoSelected(rtable.getModel().getValueAt(table.getRowSorter().convertRowIndexToModel(table.getSelectedRow()), RosterTableModel.IDCOL).toString()); 449 } else if (rtable.getSelectedRosterEntries().length > 1) { 450 log.debug("Multiple selection"); 451 locoSelected(null); 452 } else if ( (table.getSelectedRow() < 0) && (!isUpdatingSelection) ) { 453 isUpdatingSelection = true; 454 if (re != null) { // can be null with multiple selection 455 log.debug("Selected roster entry {}", re.getId()); 456 if (!rtable.setSelection(re)) { 457 re = null; //nothng was found 458 } 459 } 460 updateDetails(); 461 rtable.moveTableViewToSelected(); 462 isUpdatingSelection = false; 463 } // leave last selected item visible if no selection 464 } 465 }); 466 467 //Set all the sort and width details of the table first. 468 String rostertableref = getWindowFrameRef() + ":roster"; 469 rtable.getTable().setName(rostertableref); 470 471 // Allow only one column to be sorted at a time - 472 // Java allows multiple column sorting, but to effectively persist that, we 473 // need to be intelligent about which columns can be meaningfully sorted 474 // with other columns; this bypasses the problem by only allowing the 475 // last column sorted to affect sorting 476 RowSorterUtil.addSingleSortableColumnListener(rtable.getTable().getRowSorter()); 477 478 // Reset and then persist the table's ui state 479 JTablePersistenceManager tpm = InstanceManager.getNullableDefault(JTablePersistenceManager.class); 480 if (tpm != null) { 481 tpm.resetState(rtable.getTable()); 482 tpm.persist(rtable.getTable()); 483 } 484 rtable.getTable().setDragEnabled(true); 485 rtable.getTable().setTransferHandler(new TransferHandler() { 486 487 @Override 488 public int getSourceActions(JComponent c) { 489 return TransferHandler.COPY; 490 } 491 492 @Override 493 public Transferable createTransferable(JComponent c) { 494 JTable table = rtable.getTable(); 495 ArrayList<String> Ids = new ArrayList<>(table.getSelectedRowCount()); 496 for (int i = 0; i < table.getSelectedRowCount(); i++) { 497 Ids.add(rtable.getModel().getValueAt(table.getRowSorter().convertRowIndexToModel(table.getSelectedRows()[i]), RosterTableModel.IDCOL).toString()); 498 } 499 return new RosterEntrySelection(Ids); 500 } 501 502 @Override 503 public void exportDone(JComponent c, Transferable t, int action) { 504 // nothing to do 505 } 506 }); 507 JmriMouseListener rosterMouseListener = new RosterPopupListener(); 508 rtable.getTable().addMouseListener(JmriMouseListener.adapt(rosterMouseListener)); 509 510 // assemble roster/groups splitpane 511 rosterGroupSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, groups, rosters); 512 rosterGroupSplitPane.setOneTouchExpandable(true); 513 rosterGroupSplitPane.setResizeWeight(0); // emphasis rosters 514 Object w = prefsMgr.getProperty(getWindowFrameRef(), "rosterGroupPaneDividerLocation"); 515 if (w != null) { 516 groupSplitPaneLocation = (Integer) w; 517 rosterGroupSplitPane.setDividerLocation(groupSplitPaneLocation); 518 } 519 if (!Roster.getDefault().getRosterGroupList().isEmpty()) { 520 if (prefsMgr.getSimplePreferenceState(this.getClass().getName() + ".hideGroups")) { 521 hideGroupsPane(true); 522 } 523 } else { 524 enableRosterGroupMenuItems(false); 525 } 526 PropertyChangeListener propertyChangeListener = (PropertyChangeEvent changeEvent) -> { 527 JSplitPane sourceSplitPane = (JSplitPane) changeEvent.getSource(); 528 String propertyName = changeEvent.getPropertyName(); 529 if (propertyName.equals(JSplitPane.LAST_DIVIDER_LOCATION_PROPERTY)) { 530 int current = sourceSplitPane.getDividerLocation(); 531 hideGroups = current <= 1; 532 Integer last = (Integer) changeEvent.getNewValue(); 533 if (current >= 2) { 534 groupSplitPaneLocation = current; 535 } else if (last >= 2) { 536 groupSplitPaneLocation = last; 537 } 538 } 539 }; 540 groups.addPropertyChangeListener(SELECTED_ROSTER_GROUP, new PropertyChangeListener() { 541 @Override 542 public void propertyChange(PropertyChangeEvent pce) { 543 prefsMgr.setProperty(this.getClass().getName(), SELECTED_ROSTER_GROUP, pce.getNewValue()); 544 setTitle((String) pce.getNewValue()); 545 } 546 }); 547 rosterGroupSplitPane.addPropertyChangeListener(propertyChangeListener); 548 Roster.getDefault().addPropertyChangeListener((PropertyChangeEvent e) -> { 549 if (e.getPropertyName().equals("RosterGroupAdded") && Roster.getDefault().getRosterGroupList().size() == 1) { 550 // if the pane is hidden, show it when 1st group is created 551 hideGroupsPane(false); 552 enableRosterGroupMenuItems(true); 553 } else if (!rtable.isVisible() && (e.getPropertyName().equals("saved"))) { 554 if (firstHelpLabel != null) { 555 firstHelpLabel.setVisible(false); 556 } 557 rtable.setVisible(true); 558 rtable.resetColumnWidths(); 559 } 560 }); 561 if (Roster.getDefault().numEntries() == 0) { 562 try { 563 BufferedImage myPicture = ImageIO.read(FileUtil.findURL(("resources/" + Bundle.getMessage("ThrottleFirstUseImage")), FileUtil.Location.INSTALLED)); 564 //rosters.add(new JLabel(new ImageIcon( myPicture )), BorderLayout.CENTER); 565 firstHelpLabel = new JLabel(new ImageIcon(myPicture)); 566 rtable.setVisible(false); 567 rosters.add(firstHelpLabel, BorderLayout.NORTH); 568 //tableArea.add(firstHelpLabel); 569 rtable.setVisible(false); 570 } catch (IOException ex) { 571 // handle exception... 572 } 573 } 574 return rosterGroupSplitPane; 575 } 576 577 protected void deleteLoco() { 578 DeleteRosterItemAction act = new DeleteRosterItemAction("Delete", (WindowInterface) this); 579 act.actionPerformed(null); 580 } 581 582 void editMediaButton() { 583 //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. 584 boolean serviceSelected = service.isSelected(); 585 boolean opsSelected = ops.isSelected(); 586 edit.setSelected(true); 587 startProgrammer(null, re, "dp3" + File.separator + "MediaPane"); 588 service.setSelected(serviceSelected); 589 ops.setSelected(opsSelected); 590 } 591 592 protected void enableRosterGroupMenuItems(boolean enable) { 593 firePropertyChange("groupspane", "setEnabled", enable); 594 firePropertyChange("grouptable", "setEnabled", enable); 595 firePropertyChange("deletegroup", "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 ThrottleFrame tf = InstanceManager.getDefault(ThrottleFrameManager.class).createThrottleFrame(); 1297 tf.toFront(); 1298 tf.getAddressPanel().getRosterEntrySelector().setSelectedRosterGroup(getSelectedRosterGroup()); 1299 tf.getAddressPanel().setRosterEntry(re); 1300 }); 1301 if (re == null) { 1302 menuItem.setEnabled(false); 1303 } 1304 popupMenu.add(menuItem); 1305 popupMenu.addSeparator(); 1306 1307 menuItem = new JMenuItem(Bundle.getMessage("PrintSelection")); 1308 menuItem.addActionListener((ActionEvent e1) -> printLoco(false)); 1309 if (re == null) { 1310 menuItem.setEnabled(false); 1311 } 1312 popupMenu.add(menuItem); 1313 menuItem = new JMenuItem(Bundle.getMessage("PreviewSelection")); 1314 menuItem.addActionListener((ActionEvent e1) -> printLoco(true)); 1315 if (re == null) { 1316 menuItem.setEnabled(false); 1317 } 1318 popupMenu.add(menuItem); 1319 popupMenu.addSeparator(); 1320 1321 menuItem = new JMenuItem(Bundle.getMessage("Duplicateddd")); 1322 menuItem.addActionListener((ActionEvent e1) -> copyLoco()); 1323 if (re == null) { 1324 menuItem.setEnabled(false); 1325 } 1326 popupMenu.add(menuItem); 1327 boolean deleteFromGroup = this.getSelectedRosterGroup() != null && !this.getSelectedRosterGroup().equals(Roster.NOGROUP); 1328 menuItem = new JMenuItem(deleteFromGroup ? Bundle.getMessage("DeleteFromGroup") : Bundle.getMessage("DeleteFromRoster")); // NOI18N 1329 menuItem.addActionListener((ActionEvent e1) -> deleteLoco()); 1330 popupMenu.add(menuItem); 1331 menuItem.setEnabled(this.getSelectedRosterEntries().length > 0); 1332 1333 popupMenu.show(e.getComponent(), e.getX(), e.getY()); 1334 } 1335 1336 /** 1337 * Start the identify operation after [Identify Loco] button pressed. 1338 * <p> 1339 * This defines what happens when Identify is done. 1340 */ 1341 //taken out of CombinedLocoSelPane 1342 protected void startIdentifyLoco() { 1343 final RosterFrame me = this; 1344 Programmer programmer = null; 1345 if (modePanel.isSelected()) { 1346 programmer = modePanel.getProgrammer(); 1347 } 1348 if (programmer == null) { 1349 GlobalProgrammerManager gpm = InstanceManager.getNullableDefault(GlobalProgrammerManager.class); 1350 if (gpm != null) { 1351 programmer = gpm.getGlobalProgrammer(); 1352 log.warn("Selector did not provide a programmer, attempt to use GlobalProgrammerManager default: {}", programmer); 1353 } else { 1354 log.warn("Selector did not provide a programmer, and no ProgramManager found in InstanceManager"); 1355 } 1356 } 1357 1358 // if failed to get programmer, tell user and stop 1359 if (programmer == null) { 1360 log.error("Identify loco called when no service mode programmer is available; button should have been disabled"); 1361 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("IdentifyError")); 1362 return; 1363 } 1364 1365 // and now do the work 1366 IdentifyLoco ident = new IdentifyLoco(programmer) { 1367 private final RosterFrame who = me; 1368 1369 @Override 1370 protected void done(int dccAddress) { 1371 // if Done, updated the selected decoder 1372 // on the GUI thread, right now 1373 jmri.util.ThreadingUtil.runOnGUI(() -> who.selectLoco(dccAddress, !shortAddr, cv8val, cv7val)); 1374 } 1375 1376 @Override 1377 protected void message(String m) { 1378 // on the GUI thread, right now 1379 jmri.util.ThreadingUtil.runOnGUI(() -> statusField.setText(m)); 1380 } 1381 1382 @Override 1383 protected void error() { 1384 // raise the button again 1385 //idloco.setSelected(false); 1386 } 1387 }; 1388 ident.start(); 1389 } 1390 1391 protected void startProgrammer(DecoderFile decoderFile, RosterEntry re, String filename) { 1392 if (inStartProgrammer) { 1393 log.debug("Call to start programmer has been called twice when the first call hasn't opened"); 1394 return; 1395 } 1396 if (!checkIfEntrySelected()) { 1397 return; 1398 } 1399 try { 1400 setCursor(new Cursor(Cursor.WAIT_CURSOR)); 1401 inStartProgrammer = true; 1402 String title = re.getId(); 1403 JFrame progFrame = null; 1404 if (edit.isSelected()) { 1405 progFrame = new PaneProgFrame(decoderFile, re, title, "programmers" + File.separator + filename + ".xml", null, false) { 1406 @Override 1407 protected JPanel getModePane() { 1408 return null; 1409 } // hide prog mode buttons pane 1410 }; 1411 } else if (service.isSelected()) { 1412 progFrame = new PaneServiceProgFrame(decoderFile, re, title, "programmers" + File.separator + filename + ".xml", modePanel.getProgrammer()); 1413 } else if (ops.isSelected()) { 1414 int address = Integer.parseInt(re.getDccAddress()); 1415 boolean longAddr = re.isLongAddress(); 1416 Programmer pProg = InstanceManager.getDefault(AddressedProgrammerManager.class).getAddressedProgrammer(longAddr, address); 1417 progFrame = new PaneOpsProgFrame(decoderFile, re, title, "programmers" + File.separator + filename + ".xml", pProg); 1418 } 1419 if (progFrame == null) { 1420 return; 1421 } 1422 progFrame.pack(); 1423 progFrame.setVisible(true); 1424 } finally { 1425 setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); 1426 } 1427 inStartProgrammer = false; 1428 } 1429 1430 /** 1431 * Create and display a status bar along the bottom edge of the Roster main 1432 * pane. 1433 * <p> 1434 * TODO This status bar needs sorting out properly 1435 */ 1436 protected void statusBar() { 1437 addToStatusBox(serviceModeProgrammerLabel, null); 1438 addToStatusBox(operationsModeProgrammerLabel, null); 1439 JLabel programmerStatusLabel = new JLabel(Bundle.getMessage("ProgrammerStatus")); 1440 statusField.setText(Bundle.getMessage("StateIdle")); 1441 addToStatusBox(programmerStatusLabel, statusField); 1442 Profile profile = ProfileManager.getDefault().getActiveProfile(); 1443 if (profile != null) { 1444 addToStatusBox(new JLabel(Bundle.getMessage("ActiveProfile", profile.getName())), null); 1445 } 1446 } 1447 1448 protected void systemsMenu() { 1449 ActiveSystemsMenu.addItems(getMenu()); 1450 getMenu().add(new WindowMenu(this)); 1451 } 1452 1453 void updateDetails() { 1454 if (re == null) { 1455 String value = (rtable.getTable().getSelectedRowCount() > 1) ? "Multiple Items Selected" : ""; 1456 filename.setText(value); 1457 dateUpdated.setText(value); 1458 decoderModel.setText(value); 1459 decoderFamily.setText(value); 1460 id.setText(value); 1461 roadName.setText(value); 1462 dccAddress.setText(value); 1463 roadNumber.setText(value); 1464 mfg.setText(value); 1465 model.setText(value); 1466 owner.setText(value); 1467 locoImage.setImagePath(null); 1468 service.setEnabled(false); 1469 ops.setEnabled(false); 1470 edit.setEnabled(false); 1471 prog1Button.setEnabled(false); 1472 prog2Button.setEnabled(false); 1473 throttleLabels.setEnabled(false); 1474 rosterMedia.setEnabled(false); 1475 throttleLaunch.setEnabled(false ); 1476 } else { 1477 filename.setText(re.getFileName()); 1478 dateUpdated.setText((re.getDateModified() != null) 1479 ? DateFormat.getDateTimeInstance().format(re.getDateModified()) 1480 : re.getDateUpdated()); 1481 decoderModel.setText(re.getDecoderModel()); 1482 decoderFamily.setText(re.getDecoderFamily()); 1483 dccAddress.setText(re.getDccAddress()); 1484 id.setText(re.getId()); 1485 roadName.setText(re.getRoadName()); 1486 roadNumber.setText(re.getRoadNumber()); 1487 mfg.setText(re.getMfg()); 1488 model.setText(re.getModel()); 1489 owner.setText(re.getOwner()); 1490 locoImage.setImagePath(re.getImagePath()); 1491 if (hideRosterImage) { 1492 locoImage.setVisible(false); 1493 } else { 1494 locoImage.setVisible(true); 1495 } 1496 service.setEnabled(isProgrammingTrackEnabled()); 1497 ops.setEnabled(isProgrammingOnMainEnabled()); 1498 edit.setEnabled(true); 1499 prog1Button.setEnabled(true); 1500 prog2Button.setEnabled(true); 1501 throttleLabels.setEnabled(true); 1502 rosterMedia.setEnabled(true); 1503 throttleLaunch.setEnabled(true); 1504 updateProgMode(); 1505 } 1506 } 1507 1508 void updateProgMode() { 1509 String progMode; 1510 if (service.isSelected()) { 1511 progMode = "setprogservice"; 1512 } else if (ops.isSelected()) { 1513 progMode = "setprogops"; 1514 } else { 1515 progMode = "setprogedit"; 1516 } 1517 firePropertyChange(progMode, "setSelected", true); 1518 } 1519 1520 /** 1521 * Handle setting up and updating the GUI for the types of programmer 1522 * available. 1523 * 1524 * @param evt the triggering event; if not null and if a removal of a 1525 * ProgrammerManager, care will be taken not to trigger the 1526 * automatic creation of a new ProgrammerManager 1527 */ 1528 protected void updateProgrammerStatus(@CheckForNull PropertyChangeEvent evt) { 1529 log.debug("Updating Programmer Status"); 1530 ConnectionConfig oldServMode = serModeProCon; 1531 ConnectionConfig oldOpsMode = opsModeProCon; 1532 GlobalProgrammerManager gpm = null; 1533 AddressedProgrammerManager apm = null; 1534 1535 // Find the connection that goes with the global programmer 1536 // test that IM has a default GPM, or that event is not the removal of a GPM 1537 if (InstanceManager.containsDefault(GlobalProgrammerManager.class) 1538 || (evt != null 1539 && evt.getPropertyName().equals(InstanceManager.getDefaultsPropertyName(GlobalProgrammerManager.class)) 1540 && evt.getNewValue() == null)) { 1541 gpm = InstanceManager.getNullableDefault(GlobalProgrammerManager.class); 1542 log.trace("found global programming manager {}", gpm); 1543 } 1544 if (gpm != null) { 1545 String serviceModeProgrammerName = gpm.getUserName(); 1546 log.debug("GlobalProgrammerManager found of class {} name {} ", gpm.getClass(), serviceModeProgrammerName); 1547 InstanceManager.getOptionalDefault(ConnectionConfigManager.class).ifPresent((ccm) -> { 1548 for (ConnectionConfig connection : ccm) { 1549 log.debug("Checking connection name {}", connection.getConnectionName()); 1550 if (connection.getConnectionName() != null && connection.getConnectionName().equals(serviceModeProgrammerName)) { 1551 log.debug("Connection found for GlobalProgrammermanager"); 1552 serModeProCon = connection; 1553 } 1554 } 1555 }); 1556 } 1557 1558 // Find the connection that goes with the addressed programmer 1559 // test that IM has a default APM, or that event is not the removal of an APM 1560 if (InstanceManager.containsDefault(AddressedProgrammerManager.class) 1561 || (evt != null 1562 && evt.getPropertyName().equals(InstanceManager.getDefaultsPropertyName(AddressedProgrammerManager.class)) 1563 && evt.getNewValue() == null)) { 1564 apm = InstanceManager.getNullableDefault(AddressedProgrammerManager.class); 1565 log.trace("found addressed programming manager {}", gpm); 1566 } 1567 if (apm != null) { 1568 String opsModeProgrammerName = apm.getUserName(); 1569 log.debug("AddressedProgrammerManager found of class {} name {} ", apm.getClass(), opsModeProgrammerName); 1570 InstanceManager.getOptionalDefault(ConnectionConfigManager.class).ifPresent((ccm) -> { 1571 for (ConnectionConfig connection : ccm) { 1572 log.debug("Checking connection name {}", connection.getConnectionName()); 1573 if (connection.getConnectionName() != null && connection.getConnectionName().equals(opsModeProgrammerName)) { 1574 log.debug("Connection found for AddressedProgrammermanager"); 1575 opsModeProCon = connection; 1576 } 1577 } 1578 }); 1579 } 1580 1581 log.trace("start global check with {}, {}, {}", serModeProCon, gpm, (gpm != null ? gpm.isGlobalProgrammerAvailable() : "<none>")); 1582 if (serModeProCon != null && gpm != null && gpm.isGlobalProgrammerAvailable()) { 1583 if (ConnectionStatus.instance().isConnectionOk(serModeProCon.getConnectionName(), serModeProCon.getInfo())) { 1584 log.debug("GPM Connection online 1"); 1585 serviceModeProgrammerLabel.setText( 1586 Bundle.getMessage("ServiceModeProgOnline", serModeProCon.getConnectionName())); 1587 serviceModeProgrammerLabel.setForeground(new Color(0, 128, 0)); 1588 } else { 1589 log.debug("GPM Connection offline"); 1590 serviceModeProgrammerLabel.setText( 1591 Bundle.getMessage("ServiceModeProgOffline", serModeProCon.getConnectionName())); 1592 serviceModeProgrammerLabel.setForeground(Color.red); 1593 } 1594 if (oldServMode == null) { 1595 log.debug("Re-enable user interface"); 1596 contextService.setEnabled(isProgrammingTrackEnabled()); 1597 contextService.setVisible(true); 1598 service.setEnabled(isProgrammingTrackEnabled()); 1599 service.setVisible(true); 1600 firePropertyChange("setprogservice", "setEnabled", true); 1601 getToolBar().getComponents()[1].setEnabled(true); 1602 } 1603 } else if (gpm != null && gpm.isGlobalProgrammerAvailable()) { 1604 if (ConnectionStatus.instance().isSystemOk(gpm.getUserName())) { 1605 log.debug("GPM Connection online 2"); 1606 serviceModeProgrammerLabel.setText( 1607 Bundle.getMessage("ServiceModeProgOnline", gpm.getUserName())); 1608 serviceModeProgrammerLabel.setForeground(new Color(0, 128, 0)); 1609 } else { 1610 log.debug("GPM Connection onffline"); 1611 serviceModeProgrammerLabel.setText( 1612 Bundle.getMessage("ServiceModeProgOffline", gpm.getUserName())); 1613 serviceModeProgrammerLabel.setForeground(Color.red); 1614 } 1615 if (oldServMode == null) { 1616 log.debug("Re-enable user interface"); 1617 contextService.setEnabled(isProgrammingTrackEnabled()); 1618 contextService.setVisible(true); 1619 service.setEnabled(isProgrammingTrackEnabled()); 1620 service.setVisible(true); 1621 firePropertyChange("setprogservice", "setEnabled", true); 1622 getToolBar().getComponents()[1].setEnabled(true); 1623 } 1624 } else { 1625 // No service programmer available, disable interface sections not available 1626 log.debug("no service programmer"); 1627 serviceModeProgrammerLabel.setText(Bundle.getMessage("NoServiceProgrammerAvailable")); 1628 serviceModeProgrammerLabel.setForeground(Color.red); 1629 if (oldServMode != null) { 1630 contextService.setEnabled(false); 1631 contextService.setVisible(false); 1632 service.setEnabled(false); 1633 service.setVisible(false); 1634 firePropertyChange("setprogservice", "setEnabled", false); 1635 } 1636 // Disable Identify in toolBar 1637 // This relies on it being the 2nd item in the toolbar, as defined in xml//config/parts/jmri/jmrit/roster/swing/RosterFrameToolBar.xml 1638 // Because of I18N, we don't look for a particular Action name here 1639 getToolBar().getComponents()[1].setEnabled(false); 1640 serModeProCon = null; 1641 } 1642 1643 if (opsModeProCon != null && apm != null && apm.isAddressedModePossible()) { 1644 if (ConnectionStatus.instance().isConnectionOk(opsModeProCon.getConnectionName(), opsModeProCon.getInfo())) { 1645 log.debug("Ops Mode Connection online"); 1646 operationsModeProgrammerLabel.setText( 1647 Bundle.getMessage("OpsModeProgOnline", opsModeProCon.getConnectionName())); 1648 operationsModeProgrammerLabel.setForeground(new Color(0, 128, 0)); 1649 } else { 1650 log.debug("Ops Mode Connection offline"); 1651 operationsModeProgrammerLabel.setText( 1652 Bundle.getMessage("OpsModeProgOffline", opsModeProCon.getConnectionName())); 1653 operationsModeProgrammerLabel.setForeground(Color.red); 1654 } 1655 if (oldOpsMode == null) { 1656 contextOps.setEnabled(isProgrammingOnMainEnabled()); 1657 contextOps.setVisible(true); 1658 ops.setEnabled(isProgrammingOnMainEnabled()); 1659 ops.setVisible(true); 1660 firePropertyChange("setprogops", "setEnabled", true); 1661 } 1662 } else if (apm != null && apm.isAddressedModePossible()) { 1663 if (ConnectionStatus.instance().isSystemOk(apm.getUserName())) { 1664 log.debug("Ops Mode Connection online"); 1665 operationsModeProgrammerLabel.setText( 1666 Bundle.getMessage("OpsModeProgOnline", apm.getUserName())); 1667 operationsModeProgrammerLabel.setForeground(new Color(0, 128, 0)); 1668 } else { 1669 log.debug("Ops Mode Connection offline"); 1670 operationsModeProgrammerLabel.setText( 1671 Bundle.getMessage("OpsModeProgOffline", apm.getUserName())); 1672 operationsModeProgrammerLabel.setForeground(Color.red); 1673 } 1674 if (oldOpsMode == null) { 1675 contextOps.setEnabled(isProgrammingOnMainEnabled()); 1676 contextOps.setVisible(true); 1677 ops.setEnabled(isProgrammingOnMainEnabled()); 1678 ops.setVisible(true); 1679 firePropertyChange("setprogops", "setEnabled", true); 1680 } 1681 } else { 1682 // No ops mode programmer available, disable interface sections not available 1683 log.debug("no ops mode programmer"); 1684 operationsModeProgrammerLabel.setText(Bundle.getMessage("NoOpsProgrammerAvailable")); 1685 operationsModeProgrammerLabel.setForeground(Color.red); 1686 if (oldOpsMode != null) { 1687 contextOps.setEnabled(false); 1688 contextOps.setVisible(false); 1689 ops.setEnabled(false); 1690 ops.setVisible(false); 1691 firePropertyChange("setprogops", "setEnabled", false); 1692 } 1693 opsModeProCon = null; 1694 } 1695 String strProgMode; 1696 if (service.isEnabled()) { 1697 contextService.setSelected(true); 1698 service.setSelected(true); 1699 strProgMode = "setprogservice"; 1700 modePanel.setVisible(true); 1701 } else if (ops.isEnabled()) { 1702 contextOps.setSelected(true); 1703 ops.setSelected(true); 1704 strProgMode = "setprogops"; 1705 modePanel.setVisible(false); 1706 } else { 1707 contextEdit.setSelected(true); 1708 edit.setSelected(true); 1709 modePanel.setVisible(false); 1710 strProgMode = "setprogedit"; 1711 } 1712 firePropertyChange(strProgMode, "setSelected", true); 1713 } 1714 1715 private boolean isProgrammingTrackEnabled() { 1716 return InstanceManager.getNullableDefault(ProgrammerConfigManager.class) != null && 1717 ! InstanceManager.getDefault(ProgrammerConfigManager.class).isDisableProgrammingTrack(); 1718 } 1719 1720 private boolean isProgrammingOnMainEnabled() { 1721 return InstanceManager.getNullableDefault(ProgrammerConfigManager.class) != null && 1722 ! InstanceManager.getDefault(ProgrammerConfigManager.class).isDisableProgrammingOnMain(); 1723 } 1724 1725 @Override 1726 public void windowClosing(WindowEvent e) { 1727 closeWindow(e); 1728 super.windowClosing(e); 1729 } 1730 1731 /** 1732 * Displays a context (right-click) menu for a roster entry. 1733 */ 1734 private class RosterPopupListener extends JmriMouseAdapter { 1735 1736 @Override 1737 public void mousePressed(JmriMouseEvent e) { 1738 if (e.isPopupTrigger()) { 1739 showPopup(e); 1740 } 1741 } 1742 1743 @Override 1744 public void mouseReleased(JmriMouseEvent e) { 1745 if (e.isPopupTrigger()) { 1746 showPopup(e); 1747 } 1748 } 1749 1750 @Override 1751 public void mouseClicked(JmriMouseEvent e) { 1752 if (e.isPopupTrigger()) { 1753 showPopup(e); 1754 return; 1755 } 1756 if (e.getClickCount() == 2) { 1757 startProgrammer(null, re, programmer1); 1758 } 1759 } 1760 } 1761 1762 private static class ExportRosterItem extends ExportRosterItemAction { 1763 1764 ExportRosterItem(String pName, Component pWho, RosterEntry re) { 1765 super(pName, pWho); 1766 super.setExistingEntry(re); 1767 } 1768 1769 @Override 1770 protected boolean selectFrom() { 1771 return true; 1772 } 1773 } 1774 1775 private static class CopyRosterItem extends CopyRosterItemAction { 1776 1777 CopyRosterItem(String pName, Component pWho, RosterEntry re) { 1778 super(pName, pWho); 1779 super.setExistingEntry(re); 1780 } 1781 1782 @Override 1783 protected boolean selectFrom() { 1784 return true; 1785 } 1786 } 1787 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(RosterFrame.class); 1788 1789}