001package apps.gui3.dp3; 002 003import java.awt.BorderLayout; 004import java.awt.Color; 005import java.awt.Toolkit; 006import java.awt.event.ActionEvent; 007import java.awt.event.ItemEvent; 008import java.awt.event.WindowAdapter; 009import java.awt.event.WindowEvent; 010import java.beans.PropertyChangeEvent; 011import java.beans.PropertyChangeListener; 012import java.io.File; 013import java.io.IOException; 014import java.text.MessageFormat; 015import java.util.ArrayList; 016import java.util.List; 017import javax.annotation.Nonnull; 018import javax.swing.AbstractButton; 019import javax.swing.BorderFactory; 020import javax.swing.BoxLayout; 021import javax.swing.Icon; 022import javax.swing.JButton; 023import javax.swing.JFrame; 024import javax.swing.JLabel; 025import javax.swing.JMenu; 026import javax.swing.JMenuBar; 027import javax.swing.JPanel; 028import javax.swing.JSeparator; 029import javax.swing.JTextField; 030import javax.swing.SwingConstants; 031import javax.swing.border.TitledBorder; 032import javax.swing.event.TreeSelectionEvent; 033import javax.swing.tree.TreeNode; 034import jmri.GlobalProgrammerManager; 035import jmri.InstanceManager; 036import jmri.JmriException; 037import jmri.ProgListener; 038import jmri.Programmer; 039import jmri.ProgrammerException; 040import jmri.jmrit.XmlFile; 041import jmri.jmrit.decoderdefn.DecoderFile; 042import jmri.jmrit.decoderdefn.DecoderIndexFile; 043import jmri.jmrit.decoderdefn.PrintDecoderListAction; 044import jmri.jmrit.progsupport.ProgModeSelector; 045import jmri.jmrit.progsupport.ProgServiceModeComboBox; 046import jmri.jmrit.roster.Roster; 047import jmri.jmrit.roster.RosterEntry; 048import jmri.jmrit.roster.swing.RosterMenu; 049import jmri.jmrit.symbolicprog.AbstractValue; 050import jmri.jmrit.symbolicprog.CombinedLocoSelTreePane; 051import jmri.jmrit.symbolicprog.CvTableModel; 052import jmri.jmrit.symbolicprog.DccAddressPanel; 053import jmri.jmrit.symbolicprog.DccAddressVarHandler; 054import jmri.jmrit.symbolicprog.EnumVariableValue; 055import jmri.jmrit.symbolicprog.SymbolicProgBundle; 056import jmri.jmrit.symbolicprog.VariableTableModel; 057import jmri.jmrit.symbolicprog.VariableValue; 058import jmri.jmrit.symbolicprog.tabbedframe.PaneContainer; 059import jmri.jmrit.symbolicprog.tabbedframe.PaneProgFrame; 060import jmri.jmrit.symbolicprog.tabbedframe.PaneProgPane; 061import jmri.jmrit.symbolicprog.tabbedframe.PaneServiceProgFrame; 062import jmri.util.BusyGlassPane; 063import jmri.util.FileUtil; 064import jmri.util.JmriJFrame; 065import jmri.util.jdom.LocaleSelector; 066import jmri.util.swing.*; 067import org.jdom2.Element; 068import org.jdom2.JDOMException; 069 070/** 071 * Swing action to create and register a frame for selecting the information 072 * needed to open a PaneProgFrame in service mode. 073 * <p> 074 * The class name is a historical accident, and probably should have included 075 * "ServiceMode" or something. 076 * 077 * @see jmri.jmrit.symbolicprog.tabbedframe.PaneOpsProgAction 078 * 079 * @author Bob Jacobsen Copyright (C) 2001 080 */ 081public class PaneProgDp3Action extends JmriAbstractAction implements ProgListener, PaneContainer { 082 083 Object o1, o2, o3, o4; 084 JLabel statusLabel; 085 final ProgModeSelector modePane = new ProgServiceModeComboBox(); 086 087 public PaneProgDp3Action(String s, WindowInterface wi) { 088 super(s, wi); 089 init(); 090 } 091 092 public PaneProgDp3Action(String s, Icon i, WindowInterface wi) { 093 super(s, i, wi); 094 init(); 095 } 096 097 public PaneProgDp3Action() { 098 this(Bundle.getMessage("Name")); // NOI18N 099 } 100 101 public PaneProgDp3Action(String s) { 102 super(s); 103 init(); 104 105 } 106 107 void init() { 108 statusLabel = new JLabel(SymbolicProgBundle.getMessage("StateIdle")); // NOI18N 109 } 110 111 JmriJFrame f; 112 CombinedLocoSelTreePane combinedLocoSelTree; 113 114 @Override 115 public void actionPerformed(ActionEvent e) { 116 117 log.debug("Pane programmer requested"); // NOI18N 118 119 if (f == null) { 120 log.debug("found f==null"); 121 // create the initial frame that steers 122 f = new JmriJFrame(apps.gui3.dp3.Bundle.getMessage("FrameProgrammerSetup")); // NOI18N 123 f.getContentPane().setLayout(new BorderLayout()); 124 // ensure status line is cleared on close, so it is normal if re-opened 125 f.addWindowListener(new WindowAdapter() { 126 @Override 127 public void windowClosing(WindowEvent we) { 128 statusLabel.setText(SymbolicProgBundle.getMessage("StateIdle")); // NOI18N 129 f.windowClosing(we); 130 } 131 }); 132 133 // add the Roster menu 134 JMenuBar menuBar = new JMenuBar(); 135 JMenu j = new JMenu(SymbolicProgBundle.getMessage("MenuFile")); // NOI18N 136 j.add(new PrintDecoderListAction(SymbolicProgBundle.getMessage("MenuPrintDecoderDefinitions"), f, false)); // NOI18N 137 j.add(new PrintDecoderListAction(SymbolicProgBundle.getMessage("MenuPrintPreviewDecoderDefinitions"), f, true)); // NOI18N 138 menuBar.add(j); 139 menuBar.add(new RosterMenu(SymbolicProgBundle.getMessage("MenuRoster"), RosterMenu.MAINMENU, f)); // NOI18N 140 f.setJMenuBar(menuBar); 141 final JPanel bottomPanel = new JPanel(new BorderLayout()); 142 // new Loco on programming track 143 combinedLocoSelTree = new CombinedLocoSelTreePane(statusLabel, modePane) { 144 145 @Override 146 protected void startProgrammer(DecoderFile decoderFile, @Nonnull RosterEntry re, 147 @Nonnull String progName) { // progName is ignored here 148 log.debug("startProgrammer"); 149 String title = MessageFormat.format(SymbolicProgBundle.getMessage("FrameServiceProgrammerTitle"), // NOI18N 150 re.getId()); 151 JFrame p; 152 if (!modePane.isSelected() || modePane.getProgrammer() == null) { 153 p = new PaneProgFrame(decoderFile, re, 154 title, "programmers" + File.separator + "Comprehensive.xml", // NOI18N 155 null, false) { 156 @Override 157 protected JPanel getModePane() { 158 return null; 159 } 160 }; 161 } else { 162 p = new PaneServiceProgFrame(decoderFile, re, 163 title, "programmers" + File.separator + "Comprehensive.xml", // NOI18N 164 modePane.getProgrammer()); 165 } 166 p.pack(); 167 p.setVisible(true); 168 } 169 170 @Override 171 protected void openNewLoco() { 172 log.debug("openNewLoco"); 173 // find the decoderFile object 174 DecoderFile decoderFile = InstanceManager.getDefault(DecoderIndexFile.class).fileFromTitle(selectedDecoderType()); 175 log.debug("decoder file: {}", decoderFile.getFileName()); // NOI18N 176 if (rosterIdField.getText().equals(SymbolicProgBundle.getMessage("LabelNewDecoder"))) { // NOI18N 177 re = new RosterEntry(); 178 re.setDecoderFamily(decoderFile.getFamily()); 179 re.setDecoderModel(decoderFile.getModel()); 180 re.setProgrammingModes(decoderFile.getProgrammingModes()); 181 re.setId(SymbolicProgBundle.getMessage("LabelNewDecoder")); // NOI18N 182 // note that we're leaving the filename null 183 // add the new roster entry to the in-memory roster 184 Roster.getDefault().addEntry(re); 185 } else { 186 try { 187 saveRosterEntry(); 188 } catch (JmriException ex) { 189 log.warn("Exception while saving roster entry", ex); // NOI18N 190 return; 191 } 192 } 193 // create a dummy RosterEntry with the decoder info 194 startProgrammer(decoderFile, re, ""); // no programmer name in this case 195 //Set our roster entry back to null so that a fresh roster entry can be created if needed 196 re = null; 197 } 198 199 @Override 200 protected JPanel layoutRosterSelection() { 201 log.debug("layoutRosterSelection"); 202 return null; 203 } 204 205 @Override 206 protected JPanel layoutDecoderSelection() { 207 log.debug("layoutDecoderSelection"); 208 JPanel pan = super.layoutDecoderSelection(); 209 dTree.removeTreeSelectionListener(dListener); 210 dListener = (TreeSelectionEvent e1) -> { 211 if (!dTree.isSelectionEmpty() && dTree.getSelectionPath() != null 212 && // check that this isn't just a model 213 ((TreeNode) dTree.getSelectionPath().getLastPathComponent()).isLeaf() 214 && // can't be just a mfg, has to be at least a family 215 dTree.getSelectionPath().getPathCount() > 2 216 && // can't be a multiple decoder selection 217 dTree.getSelectionCount() < 2) { 218 log.debug("Selection event with {}", dTree.getSelectionPath()); 219 //if (locoBox != null) locoBox.setSelectedIndex(0); 220 go2.setEnabled(true); 221 go2.setRequestFocusEnabled(true); 222 go2.requestFocus(); 223 go2.setToolTipText(SymbolicProgBundle.getMessage("TipClickToOpen")); // NOI18N 224 decoderFile = InstanceManager.getDefault(DecoderIndexFile.class).fileFromTitle(selectedDecoderType()); 225 setUpRosterPanel(); 226 } else { 227 decoderFile = null; 228 // decoder not selected - require one 229 go2.setEnabled(false); 230 go2.setToolTipText(SymbolicProgBundle.getMessage("TipSelectLoco")); // NOI18N 231 setUpRosterPanel(); 232 } 233 }; 234 dTree.addTreeSelectionListener(dListener); 235 return pan; 236 } 237 238 @Override 239 protected void selectDecoder(int mfgID, int modelID, int productID) { 240 log.debug("selectDecoder"); 241 //On selecting a new decoder start a fresh with a new roster entry 242 super.selectDecoder(mfgID, modelID, productID); 243 findDecoderAddress(); 244 } 245 246 @Override 247 protected JPanel createProgrammerSelection() { 248 log.debug("createProgrammerSelection"); 249 250 JPanel pane3a = new JPanel(); 251 pane3a.setLayout(new BoxLayout(pane3a, BoxLayout.Y_AXIS)); 252 253 go2 = new JButton(Bundle.getMessage("OpenProgrammer")); // NOI18N 254 go2.getAccessibleContext().setAccessibleName(Bundle.getMessage("OpenProgrammer")); 255 go2.addActionListener((ActionEvent e1) -> { 256 log.debug("Open programmer pressed"); // NOI18N 257 openButton(); 258 // close this window to prevent having 259 // two windows processing at the same time 260 // 261 // Alternately, could have just cleared out a 262 // bunch of stuff to force starting over, but 263 // that seems to be an even more confusing 264 // user experience. 265 log.debug("Closing f {}", f); 266 WindowEvent wev = new WindowEvent(f, WindowEvent.WINDOW_CLOSING); 267 Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(wev); 268 }); 269 go2.setAlignmentX(JLabel.RIGHT_ALIGNMENT); 270 go2.setEnabled(false); 271 go2.setToolTipText(SymbolicProgBundle.getMessage("TipSelectLoco")); // NOI18N 272 bottomPanel.add(go2, BorderLayout.EAST); 273 274 return pane3a; 275 } 276 }; 277 278 // load primary frame 279 JPanel topPanel = new JPanel(); 280 topPanel.add(modePane); 281 topPanel.add(new JSeparator(SwingConstants.HORIZONTAL)); 282 f.getContentPane().add(topPanel, BorderLayout.NORTH); 283 //f.getContentPane().add(modePane); 284 //f.getContentPane().add(new JSeparator(SwingConstants.HORIZONTAL)); 285 286 combinedLocoSelTree.setAlignmentX(JLabel.CENTER_ALIGNMENT); 287 f.getContentPane().add(combinedLocoSelTree, BorderLayout.CENTER); 288 289 //f.getContentPane().add(new JSeparator(SwingConstants.HORIZONTAL)); 290 //basicRoster.setEnabled(false); 291 statusLabel.setAlignmentX(JLabel.CENTER_ALIGNMENT); 292 bottomPanel.add(statusLabel, BorderLayout.SOUTH); 293 f.getContentPane().add(bottomPanel, BorderLayout.SOUTH); 294 295 f.pack(); 296 log.debug("Tab-Programmer setup created"); // NOI18N 297 } else { 298 re = null; 299 combinedLocoSelTree.resetSelections(); 300 } 301 f.setVisible(true); 302 } 303 304 String lastSelectedProgrammer = this.getClass().getName() + ".SelectedProgrammer"; // NOI18N 305 306 // never invoked, because we overrode actionPerformed above 307 @Override 308 public JmriPanel makePanel() { 309 throw new IllegalArgumentException("Should not be invoked"); // NOI18N 310 } 311 312 JTextField rosterIdField = new JTextField(20); 313 JTextField rosterAddressField = new JTextField(10); 314 315 RosterEntry re; 316 317 int teststatus = 0; 318 319 synchronized void findDecoderAddress() { 320 teststatus = 1; 321 readCV("29"); 322 } 323 324 DecoderFile decoderFile; 325 boolean shortAddr = false; 326 int cv29 = 0; 327 int cv17 = -1; 328 int cv18 = -1; 329 int cv19 = 0; 330 int cv1 = 0; 331 int longAddress; 332 String address = "3"; 333 334 @Override 335 synchronized public void programmingOpReply(int value, int status) { 336 switch (teststatus) { 337 case 1: 338 teststatus = 2; 339 cv29 = value; 340 readCV("1"); 341 break; 342 case 2: 343 teststatus = 3; 344 cv1 = value; 345 readCV("17"); 346 break; 347 case 3: 348 teststatus = 4; 349 cv17 = value; 350 readCV("18"); 351 break; 352 case 4: 353 teststatus = 5; 354 cv18 = value; 355 readCV("19"); 356 break; 357 case 5: 358 cv19 = value; 359 finishRead(); 360 break; 361 default: 362 log.error("unknown test state {}", teststatus); 363 break; 364 } 365 } 366 367 synchronized void finishRead() { 368 if ((cv29 & 0x20) == 0) { 369 shortAddr = true; 370 address = "" + cv1; 371 } 372 if (cv17 != -1 || cv18 != -1) { 373 longAddress = (cv17 & 0x3f) * 256 + cv18; 374 address = "" + longAddress; 375 } 376 if (progPane != null) { 377 progPane.setVariableValue("Short Address", cv1); // NOI18N 378 progPane.setVariableValue("Long Address", longAddress); // NOI18N 379 progPane.setCVValue("29", cv29); 380 progPane.setCVValue("19", cv19); 381 } 382 } 383 384 protected void readCV(String cv) { 385 Programmer p = InstanceManager.getDefault(GlobalProgrammerManager.class).getGlobalProgrammer(); 386 if (p == null) { 387 //statusUpdate("No programmer connected"); 388 } else { 389 try { 390 p.readCV(cv, this); 391 } catch (ProgrammerException ex) { 392 //statusUpdate(""+ex); 393 } 394 } 395 } 396 JPanel rosterPanel = null;//new JPanel(); 397 Programmer mProgrammer; 398 CvTableModel cvModel = null; 399 VariableTableModel variableModel; 400 DccAddressPanel dccAddressPanel; 401 Element modelElem = null; 402 ThisProgPane progPane = null; 403 404 synchronized void setUpRosterPanel() { 405 re = null; 406 if (rosterPanel == null) { 407 rosterPanel = new JPanel(); 408 rosterPanel.setLayout(new BorderLayout()); 409 JPanel p = new JPanel(); 410 p.add(new JLabel(Bundle.getMessage("RosterId"))); // NOI18N 411 p.add(rosterIdField); 412 rosterPanel.add(p, BorderLayout.NORTH); 413 rosterIdField.setText(SymbolicProgBundle.getMessage("LabelNewDecoder")); // NOI18N 414 saveBasicRoster = new JButton(Bundle.getMessage("Save")); // NOI18N 415 saveBasicRoster.addActionListener((ActionEvent e) -> { 416 try { 417 log.debug("saveBasicRoster button pressed, calls saveRosterEntry"); 418 saveRosterEntry(); 419 } catch (JmriException ex) { 420 // user has been informed within saveRosterEntry(), so ignore 421 } 422 }); 423 TitledBorder border = BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.black)); 424 border.setTitle(Bundle.getMessage("CreateBasicRosterEntry")); // NOI18N 425 rosterPanel.setBorder(border); 426 rosterPanel.setVisible(false); 427 f.getContentPane().add(rosterPanel, BorderLayout.EAST); 428 } else { 429 rosterIdField.setText(SymbolicProgBundle.getMessage("LabelNewDecoder")); // NOI18N 430 } 431 if (progPane != null) { 432 progPane.dispose(); 433 rosterPanel.remove(progPane); 434 progPane = null; 435 rosterPanel.revalidate(); 436 f.getContentPane().repaint(); 437 f.repaint(); 438 f.pack(); 439 } 440 if (InstanceManager.getNullableDefault(GlobalProgrammerManager.class) != null 441 && InstanceManager.getDefault(GlobalProgrammerManager.class).isGlobalProgrammerAvailable()) { 442 this.mProgrammer = InstanceManager.getDefault(GlobalProgrammerManager.class).getGlobalProgrammer(); 443 } 444 445 cvModel = new CvTableModel(statusLabel, mProgrammer); 446 447 variableModel = new VariableTableModel(statusLabel, new String[]{"Name", "Value"}, cvModel); 448 if (decoderFile != null) { 449 Element decoderRoot; 450 try { 451 decoderRoot = decoderFile.rootFromName(DecoderFile.fileLocation + decoderFile.getFileName()); 452 } catch (JDOMException | IOException e) { 453 log.error("Exception while loading decoder XML file: {}", decoderFile.getFileName(), e); 454 return; 455 } // NOI18N 456 modelElem = decoderFile.getModelElement(); 457 decoderFile.loadVariableModel(decoderRoot.getChild("decoder"), variableModel); // NOI18N 458 rosterPanel.setVisible(true); 459 } else { 460 rosterPanel.setVisible(false); 461 return; 462 } 463 Element programmerRoot; 464 XmlFile pf = new XmlFile() { 465 }; // XmlFile is abstract 466 467 PropertyChangeListener dccNews = (PropertyChangeEvent e) -> updateDccAddress(); 468 primaryAddr = variableModel.findVar("Short Address"); // NOI18N 469 470 if (primaryAddr == null) { 471 log.debug("DCC Address monitor didn't find a Short Address variable"); // NOI18N 472 } else { 473 primaryAddr.addPropertyChangeListener(dccNews); 474 } 475 extendAddr = variableModel.findVar("Long Address"); // NOI18N 476 if (extendAddr == null) { 477 log.debug("DCC Address monitor didn't find a Long Address variable"); // NOI18N 478 } else { 479 extendAddr.addPropertyChangeListener(dccNews); 480 } 481 addMode = (EnumVariableValue) variableModel.findVar("Address Format"); // NOI18N 482 if (addMode == null) { 483 log.debug("DCC Address monitor didn't find an Address Format variable"); // NOI18N 484 } else { 485 addMode.addPropertyChangeListener(dccNews); 486 } 487 488 try { 489 programmerRoot = pf.rootFromName("programmers" + File.separator + "Basic.xml"); // NOI18N 490 Element base; 491 if ((base = programmerRoot.getChild("programmer")) == null) { // NOI18N 492 log.error("xml file top element is not programmer"); // NOI18N 493 return; 494 } 495 // for all "pane" elements in the programmer 496 List<Element> paneList = base.getChildren("pane"); // NOI18N 497 log.debug("will process {} pane definitions", paneList.size()); // NOI18N 498 String name = LocaleSelector.getAttribute(paneList.get(0), "name"); 499 progPane = new ThisProgPane(this, name, paneList.get(0), cvModel, variableModel, modelElem); 500 501 progPane.setVariableValue("Short Address", cv1); // NOI18N 502 progPane.setVariableValue("Long Address", longAddress); // NOI18N 503 progPane.setCVValue("29", cv29); // NOI18N 504 progPane.setCVValue("19", cv19); // NOI18N 505 rosterPanel.add(progPane, BorderLayout.CENTER); 506 rosterPanel.revalidate(); 507 rosterPanel.setVisible(true); 508 f.getContentPane().repaint(); 509 f.repaint(); 510 f.pack(); 511 } catch (JDOMException | IOException e) { 512 log.error("exception reading programmer file: ", e); // NOI18N 513 } 514 } 515 516 boolean longMode = false; 517 String newAddr = null; 518 519 void updateDccAddress() { 520 521 // wrapped in isDebugEnabled test to prevent overhead of assembling message 522 if (log.isDebugEnabled()) { 523 log.debug("updateDccAddress: short {} long {} mode {}", 524 (primaryAddr == null ? "<null>" : primaryAddr.getValueString()), 525 (extendAddr == null ? "<null>" : extendAddr.getValueString()), 526 (addMode == null ? "<null>" : addMode.getValueString())); 527 } 528 new DccAddressVarHandler(primaryAddr, extendAddr, addMode) { 529 @Override 530 protected void doPrimary() { 531 longMode = false; 532 if (primaryAddr != null && !primaryAddr.getValueString().isEmpty()) { 533 newAddr = primaryAddr.getValueString(); 534 } 535 } 536 537 @Override 538 protected void doExtended() { 539 // long address 540 if (!extendAddr.getValueString().isEmpty()) { 541 longMode = true; 542 newAddr = extendAddr.getValueString(); 543 } 544 } 545 }; 546 // update if needed 547 if (newAddr != null) { 548 synchronized (this) { 549 // store DCC address, type 550 address = newAddr; 551 shortAddr = !longMode; 552 } 553 } 554 } 555 556 JButton saveBasicRoster; 557 558 /** 559 * 560 * @return true if the value in the id JTextField is a duplicate of some 561 * other RosterEntry in the roster 562 */ 563 boolean checkDuplicate() { 564 // check it's not a duplicate 565 List<RosterEntry> l = Roster.getDefault().matchingList(null, null, null, null, null, null, rosterIdField.getText()); 566 boolean oops = false; 567 for (RosterEntry rosterEntry : l) { 568 if (re != rosterEntry) { 569 oops = true; 570 break; 571 } 572 } 573 return oops; 574 } 575 576 void saveRosterEntry() throws JmriException { 577 log.debug("saveRosterEntry"); 578 if (rosterIdField.getText().equals(SymbolicProgBundle.getMessage("LabelNewDecoder"))) { // NOI18N 579 synchronized (this) { 580 JmriJOptionPane.showMessageDialog(progPane, SymbolicProgBundle.getMessage("PromptFillInID")); // NOI18N 581 } 582 throw new JmriException("No Roster ID"); // NOI18N 583 } 584 if (checkDuplicate()) { 585 synchronized (this) { 586 JmriJOptionPane.showMessageDialog(progPane, SymbolicProgBundle.getMessage("ErrorDuplicateID")); // NOI18N 587 } 588 throw new JmriException("Duplicate ID"); // NOI18N 589 } 590 591 if (re == null) { 592 log.debug("re null, creating RosterEntry"); 593 re = new RosterEntry(); 594 re.setDecoderFamily(decoderFile.getFamily()); 595 re.setDecoderModel(decoderFile.getModel()); 596 re.setId(rosterIdField.getText()); 597 re.setDeveloperID(decoderFile.getDeveloperID()); 598 re.setManufacturerID(decoderFile.getManufacturerID()); 599 re.setProductID(decoderFile.getProductID()); 600 re.setProgrammingModes(decoderFile.getProgrammingModes()); 601 Roster.getDefault().addEntry(re); 602 } 603 604 updateDccAddress(); 605 606 // if there isn't a filename, store using the id 607 re.ensureFilenameExists(); 608 String filename = re.getFileName(); 609 610 // create the RosterEntry to its file 611 log.debug("setting DCC address {} {}", address, shortAddr); 612 synchronized (this) { 613 re.setDccAddress(address); // NOI18N 614 re.setLongAddress(!shortAddr); 615 re.writeFile(cvModel, variableModel); 616 617 // mark this as a success 618 variableModel.setFileDirty(false); 619 } 620 // and store an updated roster file 621 FileUtil.createDirectory(FileUtil.getUserFilesPath()); 622 Roster.getDefault().writeRoster(); 623 624 // show OK status 625 statusLabel.setText(MessageFormat.format( 626 SymbolicProgBundle.getMessage("StateSaveOK"), // NOI18N 627 filename)); 628 } 629 630 // hold refs to variables to check dccAddress 631 VariableValue primaryAddr = null; 632 VariableValue extendAddr = null; 633 EnumVariableValue addMode = null; 634 635 @Override 636 public boolean isBusy() { 637 return false; 638 } 639 640 @Override 641 public void paneFinished() { 642 } 643 644 /** 645 * Enable the read/write buttons. 646 * <p> 647 * In addition, if a programming mode pane is present, its "set" button is 648 * enabled. 649 * 650 * @param enable Are reads possible? If false, so not enable the read 651 * buttons. 652 */ 653 @Override 654 public void enableButtons(boolean enable) { 655 } 656 657 @Override 658 public void prepGlassPane(AbstractButton activeButton) { 659 } 660 661 @Override 662 synchronized public BusyGlassPane getBusyGlassPane() { 663 return new BusyGlassPane(new ArrayList<>(), 664 new ArrayList<>(), 665 rosterPanel, f); 666 } 667 668 class ThisProgPane extends PaneProgPane { 669 670 public ThisProgPane(PaneContainer parent, String name, Element pane, CvTableModel cvModel, VariableTableModel varModel, Element modelElem) { 671 super(parent, name, pane, cvModel, varModel, modelElem, re); 672 bottom.remove(readChangesButton); 673 bottom.remove(writeChangesButton); 674 writeAllButton.setText(SymbolicProgBundle.getMessage("ButtonWrite")); // NOI18N 675 readAllButton.setText(SymbolicProgBundle.getMessage("ButtonRead")); // NOI18N 676 bottom.add(saveBasicRoster); 677 bottom.revalidate(); 678 readAllButton.removeItemListener(l2); 679 readAllButton.addItemListener(l2 = (ItemEvent e) -> { 680 if (e.getStateChange() == ItemEvent.SELECTED) { 681 readAllButton.setText(SymbolicProgBundle.getMessage("ButtonStopReadSheet")); // NOI18N 682 if (!container.isBusy()) { 683 prepReadPane(false); 684 prepGlassPane(readAllButton); 685 container.getBusyGlassPane().setVisible(true); 686 readPaneAll(); 687 } 688 } else { 689 stopProgramming(); 690 readAllButton.setText(SymbolicProgBundle.getMessage("ButtonRead")); // NOI18N 691 if (container.isBusy()) { 692 readAllButton.setEnabled(false); 693 } 694 } 695 }); 696 writeAllButton.removeItemListener(l4); 697 writeAllButton.addItemListener(l4 = (ItemEvent e) -> { 698 if (e.getStateChange() == ItemEvent.SELECTED) { 699 writeAllButton.setText(SymbolicProgBundle.getMessage("ButtonStopWriteSheet")); // NOI18N 700 if (!container.isBusy()) { 701 prepWritePane(false); 702 prepGlassPane(writeAllButton); 703 container.getBusyGlassPane().setVisible(true); 704 writePaneAll(); 705 } 706 } else { 707 stopProgramming(); 708 writeAllButton.setText(SymbolicProgBundle.getMessage("ButtonWrite")); // NOI18N 709 if (container.isBusy()) { 710 writeAllButton.setEnabled(false); 711 } 712 } 713 }); 714 if (_cvModel.getProgrammer() == null) { 715 bottom.remove(readAllButton); 716 bottom.remove(writeAllButton); 717 bottom.revalidate(); 718 add(bottom); 719 } 720 } 721 722 public void setCVValue(String cv, int value) { 723 if (_cvModel.getCvByNumber(cv) != null) { 724 (_cvModel.getCvByNumber(cv)).setValue(value); 725 (_cvModel.getCvByNumber(cv)).setState(AbstractValue.ValueState.READ); 726 } 727 } 728 729 public void setVariableValue(String variable, int value) { 730 if (_varModel.findVar(variable) != null) { 731 _varModel.findVar(variable).setIntValue(value); 732 _varModel.findVar(variable).setState(AbstractValue.ValueState.READ); 733 } 734 } 735 736 @Override 737 public void dispose() { 738 bottom.remove(saveBasicRoster); 739 super.dispose(); 740 } 741 742 } 743 744 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(PaneProgDp3Action.class); 745 746}