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.setId(SymbolicProgBundle.getMessage("LabelNewDecoder")); // NOI18N 181 // note that we're leaving the filename null 182 // add the new roster entry to the in-memory roster 183 Roster.getDefault().addEntry(re); 184 } else { 185 try { 186 saveRosterEntry(); 187 } catch (JmriException ex) { 188 log.warn("Exception while saving roster entry", ex); // NOI18N 189 return; 190 } 191 } 192 // create a dummy RosterEntry with the decoder info 193 startProgrammer(decoderFile, re, ""); // no programmer name in this case 194 //Set our roster entry back to null so that a fresh roster entry can be created if needed 195 re = null; 196 } 197 198 @Override 199 protected JPanel layoutRosterSelection() { 200 log.debug("layoutRosterSelection"); 201 return null; 202 } 203 204 @Override 205 protected JPanel layoutDecoderSelection() { 206 log.debug("layoutDecoderSelection"); 207 JPanel pan = super.layoutDecoderSelection(); 208 dTree.removeTreeSelectionListener(dListener); 209 dListener = (TreeSelectionEvent e1) -> { 210 if (!dTree.isSelectionEmpty() && dTree.getSelectionPath() != null 211 && // check that this isn't just a model 212 ((TreeNode) dTree.getSelectionPath().getLastPathComponent()).isLeaf() 213 && // can't be just a mfg, has to be at least a family 214 dTree.getSelectionPath().getPathCount() > 2 215 && // can't be a multiple decoder selection 216 dTree.getSelectionCount() < 2) { 217 log.debug("Selection event with {}", dTree.getSelectionPath()); 218 //if (locoBox != null) locoBox.setSelectedIndex(0); 219 go2.setEnabled(true); 220 go2.setRequestFocusEnabled(true); 221 go2.requestFocus(); 222 go2.setToolTipText(SymbolicProgBundle.getMessage("TipClickToOpen")); // NOI18N 223 decoderFile = InstanceManager.getDefault(DecoderIndexFile.class).fileFromTitle(selectedDecoderType()); 224 setUpRosterPanel(); 225 } else { 226 decoderFile = null; 227 // decoder not selected - require one 228 go2.setEnabled(false); 229 go2.setToolTipText(SymbolicProgBundle.getMessage("TipSelectLoco")); // NOI18N 230 setUpRosterPanel(); 231 } 232 }; 233 dTree.addTreeSelectionListener(dListener); 234 return pan; 235 } 236 237 @Override 238 protected void selectDecoder(int mfgID, int modelID, int productID) { 239 log.debug("selectDecoder"); 240 //On selecting a new decoder start a fresh with a new roster entry 241 super.selectDecoder(mfgID, modelID, productID); 242 findDecoderAddress(); 243 } 244 245 @Override 246 protected JPanel createProgrammerSelection() { 247 log.debug("createProgrammerSelection"); 248 249 JPanel pane3a = new JPanel(); 250 pane3a.setLayout(new BoxLayout(pane3a, BoxLayout.Y_AXIS)); 251 252 go2 = new JButton(Bundle.getMessage("OpenProgrammer")); // NOI18N 253 go2.getAccessibleContext().setAccessibleName(Bundle.getMessage("OpenProgrammer")); 254 go2.addActionListener((ActionEvent e1) -> { 255 log.debug("Open programmer pressed"); // NOI18N 256 openButton(); 257 // close this window to prevent having 258 // two windows processing at the same time 259 // 260 // Alternately, could have just cleared out a 261 // bunch of stuff to force starting over, but 262 // that seems to be an even more confusing 263 // user experience. 264 log.debug("Closing f {}", f); 265 WindowEvent wev = new WindowEvent(f, WindowEvent.WINDOW_CLOSING); 266 Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(wev); 267 }); 268 go2.setAlignmentX(JLabel.RIGHT_ALIGNMENT); 269 go2.setEnabled(false); 270 go2.setToolTipText(SymbolicProgBundle.getMessage("TipSelectLoco")); // NOI18N 271 bottomPanel.add(go2, BorderLayout.EAST); 272 273 return pane3a; 274 } 275 }; 276 277 // load primary frame 278 JPanel topPanel = new JPanel(); 279 topPanel.add(modePane); 280 topPanel.add(new JSeparator(SwingConstants.HORIZONTAL)); 281 f.getContentPane().add(topPanel, BorderLayout.NORTH); 282 //f.getContentPane().add(modePane); 283 //f.getContentPane().add(new JSeparator(SwingConstants.HORIZONTAL)); 284 285 combinedLocoSelTree.setAlignmentX(JLabel.CENTER_ALIGNMENT); 286 f.getContentPane().add(combinedLocoSelTree, BorderLayout.CENTER); 287 288 //f.getContentPane().add(new JSeparator(SwingConstants.HORIZONTAL)); 289 //basicRoster.setEnabled(false); 290 statusLabel.setAlignmentX(JLabel.CENTER_ALIGNMENT); 291 bottomPanel.add(statusLabel, BorderLayout.SOUTH); 292 f.getContentPane().add(bottomPanel, BorderLayout.SOUTH); 293 294 f.pack(); 295 log.debug("Tab-Programmer setup created"); // NOI18N 296 } else { 297 re = null; 298 combinedLocoSelTree.resetSelections(); 299 } 300 f.setVisible(true); 301 } 302 303 String lastSelectedProgrammer = this.getClass().getName() + ".SelectedProgrammer"; // NOI18N 304 305 // never invoked, because we overrode actionPerformed above 306 @Override 307 public JmriPanel makePanel() { 308 throw new IllegalArgumentException("Should not be invoked"); // NOI18N 309 } 310 311 JTextField rosterIdField = new JTextField(20); 312 JTextField rosterAddressField = new JTextField(10); 313 314 RosterEntry re; 315 316 int teststatus = 0; 317 318 synchronized void findDecoderAddress() { 319 teststatus = 1; 320 readCV("29"); 321 } 322 323 DecoderFile decoderFile; 324 boolean shortAddr = false; 325 int cv29 = 0; 326 int cv17 = -1; 327 int cv18 = -1; 328 int cv19 = 0; 329 int cv1 = 0; 330 int longAddress; 331 String address = "3"; 332 333 @Override 334 synchronized public void programmingOpReply(int value, int status) { 335 switch (teststatus) { 336 case 1: 337 teststatus = 2; 338 cv29 = value; 339 readCV("1"); 340 break; 341 case 2: 342 teststatus = 3; 343 cv1 = value; 344 readCV("17"); 345 break; 346 case 3: 347 teststatus = 4; 348 cv17 = value; 349 readCV("18"); 350 break; 351 case 4: 352 teststatus = 5; 353 cv18 = value; 354 readCV("19"); 355 break; 356 case 5: 357 cv19 = value; 358 finishRead(); 359 break; 360 default: 361 log.error("unknown test state {}", teststatus); 362 break; 363 } 364 } 365 366 synchronized void finishRead() { 367 if ((cv29 & 0x20) == 0) { 368 shortAddr = true; 369 address = "" + cv1; 370 } 371 if (cv17 != -1 || cv18 != -1) { 372 longAddress = (cv17 & 0x3f) * 256 + cv18; 373 address = "" + longAddress; 374 } 375 if (progPane != null) { 376 progPane.setVariableValue("Short Address", cv1); // NOI18N 377 progPane.setVariableValue("Long Address", longAddress); // NOI18N 378 progPane.setCVValue("29", cv29); 379 progPane.setCVValue("19", cv19); 380 } 381 } 382 383 protected void readCV(String cv) { 384 Programmer p = InstanceManager.getDefault(GlobalProgrammerManager.class).getGlobalProgrammer(); 385 if (p == null) { 386 //statusUpdate("No programmer connected"); 387 } else { 388 try { 389 p.readCV(cv, this); 390 } catch (ProgrammerException ex) { 391 //statusUpdate(""+ex); 392 } 393 } 394 } 395 JPanel rosterPanel = null;//new JPanel(); 396 Programmer mProgrammer; 397 CvTableModel cvModel = null; 398 VariableTableModel variableModel; 399 DccAddressPanel dccAddressPanel; 400 Element modelElem = null; 401 ThisProgPane progPane = null; 402 403 synchronized void setUpRosterPanel() { 404 re = null; 405 if (rosterPanel == null) { 406 rosterPanel = new JPanel(); 407 rosterPanel.setLayout(new BorderLayout()); 408 JPanel p = new JPanel(); 409 p.add(new JLabel(Bundle.getMessage("RosterId"))); // NOI18N 410 p.add(rosterIdField); 411 rosterPanel.add(p, BorderLayout.NORTH); 412 rosterIdField.setText(SymbolicProgBundle.getMessage("LabelNewDecoder")); // NOI18N 413 saveBasicRoster = new JButton(Bundle.getMessage("Save")); // NOI18N 414 saveBasicRoster.addActionListener((ActionEvent e) -> { 415 try { 416 log.debug("saveBasicRoster button pressed, calls saveRosterEntry"); 417 saveRosterEntry(); 418 } catch (JmriException ex) { 419 // user has been informed within saveRosterEntry(), so ignore 420 } 421 }); 422 TitledBorder border = BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.black)); 423 border.setTitle(Bundle.getMessage("CreateBasicRosterEntry")); // NOI18N 424 rosterPanel.setBorder(border); 425 rosterPanel.setVisible(false); 426 f.getContentPane().add(rosterPanel, BorderLayout.EAST); 427 } else { 428 rosterIdField.setText(SymbolicProgBundle.getMessage("LabelNewDecoder")); // NOI18N 429 } 430 if (progPane != null) { 431 progPane.dispose(); 432 rosterPanel.remove(progPane); 433 progPane = null; 434 rosterPanel.revalidate(); 435 f.getContentPane().repaint(); 436 f.repaint(); 437 f.pack(); 438 } 439 if (InstanceManager.getNullableDefault(GlobalProgrammerManager.class) != null 440 && InstanceManager.getDefault(GlobalProgrammerManager.class).isGlobalProgrammerAvailable()) { 441 this.mProgrammer = InstanceManager.getDefault(GlobalProgrammerManager.class).getGlobalProgrammer(); 442 } 443 444 cvModel = new CvTableModel(statusLabel, mProgrammer); 445 446 variableModel = new VariableTableModel(statusLabel, new String[]{"Name", "Value"}, cvModel); 447 if (decoderFile != null) { 448 Element decoderRoot; 449 try { 450 decoderRoot = decoderFile.rootFromName(DecoderFile.fileLocation + decoderFile.getFileName()); 451 } catch (JDOMException | IOException e) { 452 log.error("Exception while loading decoder XML file: {}", decoderFile.getFileName(), e); 453 return; 454 } // NOI18N 455 modelElem = decoderFile.getModelElement(); 456 decoderFile.loadVariableModel(decoderRoot.getChild("decoder"), variableModel); // NOI18N 457 rosterPanel.setVisible(true); 458 } else { 459 rosterPanel.setVisible(false); 460 return; 461 } 462 Element programmerRoot; 463 XmlFile pf = new XmlFile() { 464 }; // XmlFile is abstract 465 466 PropertyChangeListener dccNews = (PropertyChangeEvent e) -> updateDccAddress(); 467 primaryAddr = variableModel.findVar("Short Address"); // NOI18N 468 469 if (primaryAddr == null) { 470 log.debug("DCC Address monitor didn't find a Short Address variable"); // NOI18N 471 } else { 472 primaryAddr.addPropertyChangeListener(dccNews); 473 } 474 extendAddr = variableModel.findVar("Long Address"); // NOI18N 475 if (extendAddr == null) { 476 log.debug("DCC Address monitor didn't find a Long Address variable"); // NOI18N 477 } else { 478 extendAddr.addPropertyChangeListener(dccNews); 479 } 480 addMode = (EnumVariableValue) variableModel.findVar("Address Format"); // NOI18N 481 if (addMode == null) { 482 log.debug("DCC Address monitor didn't find an Address Format variable"); // NOI18N 483 } else { 484 addMode.addPropertyChangeListener(dccNews); 485 } 486 487 try { 488 programmerRoot = pf.rootFromName("programmers" + File.separator + "Basic.xml"); // NOI18N 489 Element base; 490 if ((base = programmerRoot.getChild("programmer")) == null) { // NOI18N 491 log.error("xml file top element is not programmer"); // NOI18N 492 return; 493 } 494 // for all "pane" elements in the programmer 495 List<Element> paneList = base.getChildren("pane"); // NOI18N 496 log.debug("will process {} pane definitions", paneList.size()); // NOI18N 497 String name = LocaleSelector.getAttribute(paneList.get(0), "name"); 498 progPane = new ThisProgPane(this, name, paneList.get(0), cvModel, variableModel, modelElem); 499 500 progPane.setVariableValue("Short Address", cv1); // NOI18N 501 progPane.setVariableValue("Long Address", longAddress); // NOI18N 502 progPane.setCVValue("29", cv29); // NOI18N 503 progPane.setCVValue("19", cv19); // NOI18N 504 rosterPanel.add(progPane, BorderLayout.CENTER); 505 rosterPanel.revalidate(); 506 rosterPanel.setVisible(true); 507 f.getContentPane().repaint(); 508 f.repaint(); 509 f.pack(); 510 } catch (JDOMException | IOException e) { 511 log.error("exception reading programmer file: ", e); // NOI18N 512 } 513 } 514 515 boolean longMode = false; 516 String newAddr = null; 517 518 void updateDccAddress() { 519 520 // wrapped in isDebugEnabled test to prevent overhead of assembling message 521 if (log.isDebugEnabled()) { 522 log.debug("updateDccAddress: short {} long {} mode {}", 523 (primaryAddr == null ? "<null>" : primaryAddr.getValueString()), 524 (extendAddr == null ? "<null>" : extendAddr.getValueString()), 525 (addMode == null ? "<null>" : addMode.getValueString())); 526 } 527 new DccAddressVarHandler(primaryAddr, extendAddr, addMode) { 528 @Override 529 protected void doPrimary() { 530 longMode = false; 531 if (primaryAddr != null && !primaryAddr.getValueString().equals("")) { 532 newAddr = primaryAddr.getValueString(); 533 } 534 } 535 536 @Override 537 protected void doExtended() { 538 // long address 539 if (!extendAddr.getValueString().equals("")) { 540 longMode = true; 541 newAddr = extendAddr.getValueString(); 542 } 543 } 544 }; 545 // update if needed 546 if (newAddr != null) { 547 synchronized (this) { 548 // store DCC address, type 549 address = newAddr; 550 shortAddr = !longMode; 551 } 552 } 553 } 554 555 JButton saveBasicRoster; 556 557 /** 558 * 559 * @return true if the value in the id JTextField is a duplicate of some 560 * other RosterEntry in the roster 561 */ 562 boolean checkDuplicate() { 563 // check its not a duplicate 564 List<RosterEntry> l = Roster.getDefault().matchingList(null, null, null, null, null, null, rosterIdField.getText()); 565 boolean oops = false; 566 for (RosterEntry rosterEntry : l) { 567 if (re != rosterEntry) { 568 oops = true; 569 break; 570 } 571 } 572 return oops; 573 } 574 575 void saveRosterEntry() throws JmriException { 576 log.debug("saveRosterEntry"); 577 if (rosterIdField.getText().equals(SymbolicProgBundle.getMessage("LabelNewDecoder"))) { // NOI18N 578 synchronized (this) { 579 JmriJOptionPane.showMessageDialog(progPane, SymbolicProgBundle.getMessage("PromptFillInID")); // NOI18N 580 } 581 throw new JmriException("No Roster ID"); // NOI18N 582 } 583 if (checkDuplicate()) { 584 synchronized (this) { 585 JmriJOptionPane.showMessageDialog(progPane, SymbolicProgBundle.getMessage("ErrorDuplicateID")); // NOI18N 586 } 587 throw new JmriException("Duplicate ID"); // NOI18N 588 } 589 590 if (re == null) { 591 log.debug("re null, creating RosterEntry"); 592 re = new RosterEntry(); 593 re.setDecoderFamily(decoderFile.getFamily()); 594 re.setDecoderModel(decoderFile.getModel()); 595 re.setId(rosterIdField.getText()); 596 re.setDeveloperID(decoderFile.getDeveloperID()); 597 re.setManufacturerID(decoderFile.getManufacturerID()); 598 re.setProductID(decoderFile.getProductID()); 599 Roster.getDefault().addEntry(re); 600 } 601 602 updateDccAddress(); 603 604 // if there isn't a filename, store using the id 605 re.ensureFilenameExists(); 606 String filename = re.getFileName(); 607 608 // create the RosterEntry to its file 609 log.debug("setting DCC address {} {}", address, shortAddr); 610 synchronized (this) { 611 re.setDccAddress("" + address); // NOI18N 612 re.setLongAddress(!shortAddr); 613 re.writeFile(cvModel, variableModel); 614 615 // mark this as a success 616 variableModel.setFileDirty(false); 617 } 618 // and store an updated roster file 619 FileUtil.createDirectory(FileUtil.getUserFilesPath()); 620 Roster.getDefault().writeRoster(); 621 622 // show OK status 623 statusLabel.setText(MessageFormat.format( 624 SymbolicProgBundle.getMessage("StateSaveOK"), // NOI18N 625 filename)); 626 } 627 628 // hold refs to variables to check dccAddress 629 VariableValue primaryAddr = null; 630 VariableValue extendAddr = null; 631 EnumVariableValue addMode = null; 632 633 @Override 634 public boolean isBusy() { 635 return false; 636 } 637 638 @Override 639 public void paneFinished() { 640 } 641 642 /** 643 * Enable the read/write buttons. 644 * <p> 645 * In addition, if a programming mode pane is present, its "set" button is 646 * enabled. 647 * 648 * @param enable Are reads possible? If false, so not enable the read 649 * buttons. 650 */ 651 @Override 652 public void enableButtons(boolean enable) { 653 } 654 655 @Override 656 public void prepGlassPane(AbstractButton activeButton) { 657 } 658 659 @Override 660 synchronized public BusyGlassPane getBusyGlassPane() { 661 return new BusyGlassPane(new ArrayList<>(), 662 new ArrayList<>(), 663 rosterPanel, f); 664 } 665 666 class ThisProgPane extends PaneProgPane { 667 668 public ThisProgPane(PaneContainer parent, String name, Element pane, CvTableModel cvModel, VariableTableModel varModel, Element modelElem) { 669 super(parent, name, pane, cvModel, varModel, modelElem, re); 670 bottom.remove(readChangesButton); 671 bottom.remove(writeChangesButton); 672 writeAllButton.setText(SymbolicProgBundle.getMessage("ButtonWrite")); // NOI18N 673 readAllButton.setText(SymbolicProgBundle.getMessage("ButtonRead")); // NOI18N 674 bottom.add(saveBasicRoster); 675 bottom.revalidate(); 676 readAllButton.removeItemListener(l2); 677 readAllButton.addItemListener(l2 = (ItemEvent e) -> { 678 if (e.getStateChange() == ItemEvent.SELECTED) { 679 readAllButton.setText(SymbolicProgBundle.getMessage("ButtonStopReadSheet")); // NOI18N 680 if (!container.isBusy()) { 681 prepReadPane(false); 682 prepGlassPane(readAllButton); 683 container.getBusyGlassPane().setVisible(true); 684 readPaneAll(); 685 } 686 } else { 687 stopProgramming(); 688 readAllButton.setText(SymbolicProgBundle.getMessage("ButtonRead")); // NOI18N 689 if (container.isBusy()) { 690 readAllButton.setEnabled(false); 691 } 692 } 693 }); 694 writeAllButton.removeItemListener(l4); 695 writeAllButton.addItemListener(l4 = (ItemEvent e) -> { 696 if (e.getStateChange() == ItemEvent.SELECTED) { 697 writeAllButton.setText(SymbolicProgBundle.getMessage("ButtonStopWriteSheet")); // NOI18N 698 if (!container.isBusy()) { 699 prepWritePane(false); 700 prepGlassPane(writeAllButton); 701 container.getBusyGlassPane().setVisible(true); 702 writePaneAll(); 703 } 704 } else { 705 stopProgramming(); 706 writeAllButton.setText(SymbolicProgBundle.getMessage("ButtonWrite")); // NOI18N 707 if (container.isBusy()) { 708 writeAllButton.setEnabled(false); 709 } 710 } 711 }); 712 if (_cvModel.getProgrammer() == null) { 713 bottom.remove(readAllButton); 714 bottom.remove(writeAllButton); 715 bottom.revalidate(); 716 add(bottom); 717 } 718 } 719 720 public void setCVValue(String cv, int value) { 721 if (_cvModel.getCvByNumber(cv) != null) { 722 (_cvModel.getCvByNumber(cv)).setValue(value); 723 (_cvModel.getCvByNumber(cv)).setState(AbstractValue.ValueState.READ); 724 } 725 } 726 727 public void setVariableValue(String variable, int value) { 728 if (_varModel.findVar(variable) != null) { 729 _varModel.findVar(variable).setIntValue(value); 730 _varModel.findVar(variable).setState(AbstractValue.ValueState.READ); 731 } 732 } 733 734 @Override 735 public void dispose() { 736 bottom.remove(saveBasicRoster); 737 super.dispose(); 738 } 739 740 } 741 742 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(PaneProgDp3Action.class); 743 744}