001package jmri.jmrit.throttle;
002
003import java.awt.*;
004import java.beans.PropertyChangeEvent;
005import java.beans.PropertyChangeListener;
006import java.io.File;
007import java.util.ArrayList;
008import java.util.List;
009import java.util.Objects;
010
011import javax.swing.JButton;
012import javax.swing.JComboBox;
013import javax.swing.JFrame;
014import javax.swing.JInternalFrame;
015import javax.swing.JOptionPane;
016import javax.swing.JPanel;
017import javax.swing.WindowConstants;
018
019import jmri.DccLocoAddress;
020import jmri.DccThrottle;
021import jmri.InstanceManager;
022import jmri.LocoAddress;
023import jmri.Programmer;
024import jmri.ThrottleListener;
025import jmri.jmrit.DccLocoAddressSelector;
026import jmri.jmrit.roster.Roster;
027import jmri.jmrit.roster.RosterEntry;
028import jmri.jmrit.roster.swing.RosterEntrySelectorPanel;
029import jmri.jmrit.symbolicprog.ProgDefault;
030import jmri.jmrit.symbolicprog.tabbedframe.PaneOpsProgFrame;
031import jmri.jmrix.nce.consist.NceConsistRoster;
032import jmri.jmrix.nce.consist.NceConsistRosterEntry;
033import jmri.util.swing.WrapLayout;
034
035import org.jdom2.Element;
036import org.slf4j.Logger;
037import org.slf4j.LoggerFactory;
038
039/**
040 * A JInternalFrame that provides a way for the user to enter a decoder address.
041 * This class also store AddressListeners and notifies them when the user enters
042 * a new address.
043 *
044 * @author glen Copyright (C) 2002
045 * @author Daniel Boudreau Copyright (C) 2008 (add consist feature)
046 * @author Lionel Jeanson 2009-2021
047 */
048public class AddressPanel extends JInternalFrame implements ThrottleListener, PropertyChangeListener {
049
050    private DccThrottle throttle;
051    private DccThrottle consistThrottle;
052
053    private final DccLocoAddressSelector addrSelector = new DccLocoAddressSelector();
054    private DccLocoAddress currentAddress;
055    private DccLocoAddress consistAddress;
056    private ArrayList<AddressListener> listeners;
057
058    private JPanel mainPanel;
059
060    private JButton releaseButton;
061    private JButton dispatchButton;
062    private JButton progButton;
063    private JButton setButton;
064    private RosterEntrySelectorPanel rosterBox;
065    private JComboBox<String> conRosterBox;
066
067    private RosterEntry rosterEntry;
068
069    /**
070     * Constructor
071     */
072    public AddressPanel() {
073        if (jmri.InstanceManager.getNullableDefault(ThrottlesPreferences.class) == null) {
074            log.debug("Creating new ThrottlesPreference Instance");
075            jmri.InstanceManager.store(new ThrottlesPreferences(), ThrottlesPreferences.class);
076        }  
077        initGUI();
078        applyPreferences();
079    }
080
081    public void destroy() { // Handle disposing of the throttle
082        if (throttle != null) {
083            DccLocoAddress l = (DccLocoAddress) throttle.getLocoAddress();
084            throttle.removePropertyChangeListener(this);
085            InstanceManager.throttleManagerInstance().cancelThrottleRequest(l, this);
086            InstanceManager.throttleManagerInstance().releaseThrottle(throttle, this);
087            notifyListenersOfThrottleRelease();
088            throttle = null;
089        }
090        if (consistThrottle != null) {
091            InstanceManager.throttleManagerInstance().releaseThrottle(consistThrottle, this);
092            notifyListenersOfThrottleRelease();
093            consistThrottle = null;
094        }
095    }
096
097    /**
098     * Add an AddressListener.
099     * AddressListeners are notified when the user
100     * selects a new address and when a Throttle is acquired for that address
101     * @param l listener to add.
102     *
103     */
104    public void addAddressListener(AddressListener l) {
105        if (listeners == null) {
106            listeners = new ArrayList<>(2);
107        }
108        if (!listeners.contains(l)) {
109            listeners.add(l);
110        }
111    }
112
113    /**
114     * Remove an AddressListener.
115     *
116     * @param l listener to remove.
117     */
118    public void removeAddressListener(AddressListener l) {
119        if (listeners == null) {
120            return;
121        }
122        listeners.remove(l);
123    }
124
125    /**
126     * Gets the selected index of the roster combo box. Implemented to support
127     * xboxThrottle.py
128     *
129     * @return the selected index of the roster combo box
130     */
131    public int getRosterSelectedIndex() {
132        return getRosterEntrySelector().getRosterEntryComboBox().getSelectedIndex();
133    }
134
135    /**
136     * Sets the selected index of the roster combo box. Implemented to support
137     * xboxThrottle.py This method temporarily disables roster box actions so it
138     * can change the selected index without triggering a cascade of events.
139     *
140     * @param index the index to select in the combo box
141     */
142    public void setRosterSelectedIndex(int index) {
143        if (getRosterEntrySelector().isEnabled() && index >= 0 && index < getRosterEntrySelector().getRosterEntryComboBox().getItemCount()) {
144            getRosterEntrySelector().getRosterEntryComboBox().setSelectedIndex(index);
145        }
146        if ((backgroundPanel != null) && (rosterBox.getSelectedRosterEntries().length == 0)) {
147            backgroundPanel.setImagePath(null);
148            String rosterEntryTitle = getRosterEntrySelector().getSelectedRosterEntries()[0].titleString();
149            RosterEntry re = Roster.getDefault().entryFromTitle(rosterEntryTitle);
150            if (re != null) {
151                backgroundPanel.setImagePath(re.getImagePath());
152            }
153        }
154    }
155
156    private BackgroundPanel backgroundPanel;
157
158    public void setBackgroundPanel(BackgroundPanel bp) {
159        backgroundPanel = bp;
160    }
161
162    /**
163     * "Sets" the current roster entry. Equivalent to the user pressing the
164     * "Set" button. Implemented to support xboxThrottle.py
165     */
166    public void selectRosterEntry() {
167        rosterItemSelected();
168    }
169
170    /**
171     * Get notification that a throttle has been found as we requested.
172     *
173     * @param t An instantiation of the DccThrottle with the address requested.
174     */
175    @Override
176    public void notifyThrottleFound(DccThrottle t) {
177        log.debug("Asked for {} got {}", currentAddress.getNumber(), t.getLocoAddress());
178        if (consistAddress != null
179                && t.getLocoAddress().getNumber() == consistAddress.getNumber()) {
180            // notify the listeners that a throttle was found
181            // for the consist address.
182            log.debug("notifying that this is a consist");
183            notifyConsistThrottleFound(t);
184            return;
185        }
186        if (t.getLocoAddress().getNumber() != currentAddress.getNumber()) {
187            log.warn("Not correct address, asked for {} got {}, requesting again...", currentAddress.getNumber(), t.getLocoAddress());
188            boolean requestOK
189                    = InstanceManager.throttleManagerInstance().requestThrottle(currentAddress, this, true);
190            if (!requestOK) {
191                JOptionPane.showMessageDialog(mainPanel, Bundle.getMessage("AddressInUse"));
192            }
193            return;
194        }
195
196        throttle = t;
197        releaseButton.setEnabled(true);
198        currentAddress = (DccLocoAddress) t.getLocoAddress();
199        addrSelector.setAddress(currentAddress);
200        throttle.addPropertyChangeListener(this);
201
202        // can we find a roster entry?
203        if ((rosterEntry == null)
204                && (InstanceManager.getDefault(ThrottlesPreferences.class).isUsingExThrottle())
205                && (InstanceManager.getDefault(ThrottlesPreferences.class).isEnablingRosterSearch())
206                && addrSelector.getAddress() != null) {
207            List<RosterEntry> l = Roster.getDefault().matchingList(null, null, "" + addrSelector.getAddress().getNumber(), null, null, null, null);
208            if (l.size() > 0) {
209                rosterEntry = l.get(0);
210            }
211        }
212
213        // update GUI
214        setButton.setEnabled(false);
215        addrSelector.setEnabled(false);
216        getRosterEntrySelector().setEnabled(false);
217        conRosterBox.setEnabled(false);
218        if (InstanceManager.throttleManagerInstance().hasDispatchFunction()) {
219            dispatchButton.setEnabled(true);
220        }
221        // enable program button if programmer available
222        // for ops-mode programming
223        if ((rosterEntry != null) && (ProgDefault.getDefaultProgFile() != null)
224                && (InstanceManager.getNullableDefault(jmri.AddressedProgrammerManager.class) != null)
225                && (InstanceManager.getDefault(jmri.AddressedProgrammerManager.class).isAddressedModePossible())) {
226            progButton.setEnabled(true);
227        }
228        // send notification of new address
229        listeners.stream().filter((l) -> (currentAddress != null)).forEachOrdered((l) -> {
230            l.notifyAddressThrottleFound(throttle);
231        }); 
232    }
233
234    @Override
235    public void notifyFailedThrottleRequest(LocoAddress address, String reason) {
236        javax.swing.JOptionPane.showMessageDialog(null, reason, Bundle.getMessage("FailedSetupRequestTitle"), javax.swing.JOptionPane.WARNING_MESSAGE);
237    }
238
239    /**
240    * A decision is required for Throttle creation to continue.
241    * <p>
242    * Steal / Cancel, Share / Cancel, or Steal / Share Cancel
243    */
244    @Override
245    public void notifyDecisionRequired(LocoAddress address, DecisionType question) {
246        if ( null != question )  {
247            switch (question) {
248                case STEAL:
249                    if (InstanceManager.getDefault(ThrottlesPreferences.class).isSilentSteal() ){
250                        InstanceManager.throttleManagerInstance().responseThrottleDecision(address, this, DecisionType.STEAL );
251                        return;
252                    }   
253                    jmri.util.ThreadingUtil.runOnGUI(() -> {
254                        if ( javax.swing.JOptionPane.YES_OPTION == javax.swing.JOptionPane.showConfirmDialog(
255                                this, Bundle.getMessage("StealQuestionText",address.toString()),
256                                Bundle.getMessage("StealRequestTitle"), javax.swing.JOptionPane.YES_NO_OPTION)) {
257                            InstanceManager.throttleManagerInstance().responseThrottleDecision(address, this, DecisionType.STEAL );
258                        } else {
259                            InstanceManager.throttleManagerInstance().cancelThrottleRequest(address, this);
260                        }
261                    }); 
262                    break;
263                case SHARE:
264                    if (InstanceManager.getDefault(ThrottlesPreferences.class).isSilentShare() ){
265                        InstanceManager.throttleManagerInstance().responseThrottleDecision(address, this, DecisionType.SHARE );
266                        return;
267                    }   
268                    jmri.util.ThreadingUtil.runOnGUI(() -> {
269                        if ( javax.swing.JOptionPane.YES_OPTION == javax.swing.JOptionPane.showConfirmDialog(
270                                this, Bundle.getMessage("ShareQuestionText",address.toString()),
271                                Bundle.getMessage("ShareRequestTitle"), javax.swing.JOptionPane.YES_NO_OPTION)) {
272                            InstanceManager.throttleManagerInstance().responseThrottleDecision(address, this, DecisionType.SHARE );
273                        } else {
274                            InstanceManager.throttleManagerInstance().cancelThrottleRequest(address, this);
275                        }
276                    }); 
277                    break;
278                case STEAL_OR_SHARE:
279                    if ( InstanceManager.getDefault(ThrottlesPreferences.class).isSilentSteal() ){
280                        InstanceManager.throttleManagerInstance().responseThrottleDecision(address, this, DecisionType.STEAL );
281                        return;
282                    }   
283                    if ( InstanceManager.getDefault(ThrottlesPreferences.class).isSilentShare() ){
284                        InstanceManager.throttleManagerInstance().responseThrottleDecision(address, this, DecisionType.SHARE );
285                        return;
286                    }   
287                    String[] options = new String[] {Bundle.getMessage("StealButton"), Bundle.getMessage("ShareButton"), Bundle.getMessage("CancelButton")};
288                    jmri.util.ThreadingUtil.runOnGUI(() -> {
289                        int response = javax.swing.JOptionPane.showOptionDialog(AddressPanel.this, 
290                                Bundle.getMessage("StealShareQuestionText",address.toString()), Bundle.getMessage("StealShareRequestTitle"), 
291                                javax.swing.JOptionPane.DEFAULT_OPTION, javax.swing.JOptionPane.QUESTION_MESSAGE, null, options, options[1]);
292                        switch (response) {
293                            case 0:
294                                log.debug("steal clicked");
295                                InstanceManager.throttleManagerInstance().responseThrottleDecision(address, AddressPanel.this, DecisionType.STEAL);
296                                break;
297                            case 1:
298                                log.debug("share clicked");
299                                InstanceManager.throttleManagerInstance().responseThrottleDecision(address, AddressPanel.this, DecisionType.SHARE);
300                                break;
301                            default:
302                                log.debug("cancel clicked");
303                                InstanceManager.throttleManagerInstance().cancelThrottleRequest(address, AddressPanel.this);
304                                break;
305                        }
306                    }); 
307                    break;
308                default:
309                    break;
310            }
311        }
312    }
313
314    /**
315     * Get notification that a consist throttle has been found as we requested.
316     *
317     * @param t An instantiation of the DccThrottle with the address requested.
318     */
319    public void notifyConsistThrottleFound(DccThrottle t) {
320        this.consistThrottle = t;
321        listeners.forEach((l) -> {
322            // log.debug("Notify address listener of address change {}", l.getClass());
323            l.notifyConsistAddressThrottleFound(t);
324        });
325    }
326
327    /**
328     * Receive notification that an address has been release or dispatched.
329     */
330    public void notifyThrottleDisposed() {
331        log.debug("notifyThrottleDisposed");
332        dispatchButton.setEnabled(false);
333        releaseButton.setEnabled(false);
334        progButton.setEnabled(false);
335        setButton.setEnabled(true);
336        addrSelector.setEnabled(true);
337        getRosterEntrySelector().setEnabled(true);
338        conRosterBox.setEnabled(true);
339        if (throttle != null) {
340            throttle.removePropertyChangeListener(this);
341        }
342        throttle = null;
343        rosterEntry = null;
344        notifyListenersOfThrottleRelease();
345    }
346
347    /**
348     * Get the RosterEntry if there's one for this throttle.
349     *
350     * @return RosterEntry or null
351     */
352    public RosterEntry getRosterEntry() {
353        return rosterEntry;
354    }
355
356    /**
357     * Set the RosterEntry for this throttle.
358     * @param entry roster entry to set.
359     */
360    public void setRosterEntry(RosterEntry entry) {
361        getRosterEntrySelector().setSelectedRosterEntry(entry);
362        addrSelector.setAddress(entry.getDccLocoAddress());
363        rosterEntry = entry;
364        changeOfAddress();
365    }
366
367    /**
368     * Create, initialize and place the GUI objects.
369     */
370    private void initGUI() {
371        this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
372        mainPanel = new JPanel();
373        mainPanel.setLayout(new BorderLayout());
374        this.setContentPane(mainPanel);
375
376        // center: address input
377        addrSelector.setVariableSize(true);
378        mainPanel.add(addrSelector.getCombinedJPanel(), BorderLayout.CENTER);
379        addrSelector.getTextField().addActionListener(e -> {
380            consistAddress = null;
381            changeOfAddress();
382        });
383        
384        // top : roster and consists selectors
385        JPanel topPanel = new JPanel();
386        topPanel.setLayout(new WrapLayout(FlowLayout.CENTER, 2, 2));
387        
388        rosterBox = new RosterEntrySelectorPanel();
389        getRosterEntrySelector().setNonSelectedItem(Bundle.getMessage("NoLocoSelected"));
390        getRosterEntrySelector().setToolTipText(Bundle.getMessage("SelectLocoFromRosterTT"));
391        getRosterEntrySelector().addPropertyChangeListener("selectedRosterEntries", pce -> rosterItemSelected());
392        getRosterEntrySelector().setLayout(new WrapLayout(FlowLayout.CENTER, 2, 2));
393        topPanel.add(getRosterEntrySelector());
394
395        conRosterBox = InstanceManager.getDefault(NceConsistRoster.class).fullRosterComboBox();
396        if (InstanceManager.getDefault(NceConsistRoster.class).numEntries() > 0) {
397            conRosterBox.insertItemAt(Bundle.getMessage("NoConsistSelected"), 0);  // empty entry
398            conRosterBox.setSelectedIndex(0);
399            conRosterBox.setToolTipText(Bundle.getMessage("SelectConsistFromRosterTT"));
400            conRosterBox.addActionListener(e -> consistRosterSelected());
401            topPanel.add(conRosterBox);
402        }
403        
404        mainPanel.add(topPanel, BorderLayout.NORTH);
405
406        // bottom : buttons
407        JPanel buttonPanel = new JPanel();
408        buttonPanel.setLayout(new WrapLayout(FlowLayout.CENTER, 2, 2));        
409
410        progButton = new JButton(Bundle.getMessage("ButtonProgram"));
411        buttonPanel.add(progButton);
412        progButton.setEnabled(false);
413        progButton.addActionListener(e -> openProgrammer());
414        
415        dispatchButton = new JButton(Bundle.getMessage("ButtonDispatch"));
416        buttonPanel.add(dispatchButton);
417        dispatchButton.setEnabled(false);
418        dispatchButton.addActionListener(e -> dispatchAddress());
419
420        releaseButton = new JButton(Bundle.getMessage("ButtonRelease"));
421        buttonPanel.add(releaseButton);
422        releaseButton.setEnabled(false);
423        releaseButton.addActionListener(e -> releaseAddress());
424
425        setButton = new JButton(Bundle.getMessage("ButtonSet"));
426        setButton.addActionListener(e -> {
427            consistAddress = null;
428            changeOfAddress();
429        });
430        buttonPanel.add(setButton);        
431        
432        mainPanel.add(buttonPanel, BorderLayout.SOUTH);
433
434        pack();
435    }
436
437    private void rosterItemSelected() {
438        if (getRosterEntrySelector().getSelectedRosterEntries().length != 0) {
439            setRosterEntry(getRosterEntrySelector().getSelectedRosterEntries()[0]);
440            consistAddress = null;
441        }
442    }
443
444    private void consistRosterSelected() {
445        if (!(Objects.equals(conRosterBox.getSelectedItem(), Bundle.getMessage("NoConsistSelected")))) {
446            String rosterEntryTitle = Objects.requireNonNull(conRosterBox.getSelectedItem()).toString();
447            NceConsistRosterEntry nceConsistRosterEntry = InstanceManager.getDefault(NceConsistRoster.class)
448                    .entryFromTitle(rosterEntryTitle);
449
450            DccLocoAddress a = new DccLocoAddress(Integer.parseInt(nceConsistRosterEntry
451                    .getLoco1DccAddress()), nceConsistRosterEntry.isLoco1LongAddress());
452            addrSelector.setAddress(a);
453            consistAddress = null;
454            int cA = 0;
455            try {
456                cA = Integer.parseInt(nceConsistRosterEntry.getConsistNumber());
457            } catch (NumberFormatException ignored) {
458
459            }
460            if (0 < cA && cA < 128) {
461                consistAddress = new DccLocoAddress(cA, false);
462            } else {
463                log.warn("consist number missing {}", nceConsistRosterEntry.getLoco1DccAddress());
464                JOptionPane.showMessageDialog(mainPanel,
465                        Bundle.getMessage("ConsistNumberHasNotBeenAssigned"),
466                        Bundle.getMessage("NeedsConsistNumber"),
467                        JOptionPane.ERROR_MESSAGE);
468                return;
469            }
470            if (JOptionPane.showConfirmDialog(mainPanel,
471                    Bundle.getMessage("SendFunctionToLead"), Bundle.getMessage("NCEconsistThrottle"),
472                    JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) {
473                addrSelector.setAddress(consistAddress);
474                consistAddress = null;
475            }
476            changeOfAddress();
477        }
478    }
479
480    /**
481     * The user has selected a new address. Notify all listeners.
482     */
483    private void changeOfAddress() {
484        currentAddress = addrSelector.getAddress();
485        if (currentAddress == null) {
486            return; // no address
487        }  
488        // send notification of new address
489        listeners.forEach((l) -> {
490            l.notifyAddressChosen(currentAddress);
491        });
492        log.debug("Requesting new slot for address {} rosterEntry {}",currentAddress,rosterEntry);
493        boolean requestOK;
494        if (rosterEntry == null) {
495            requestOK = InstanceManager.throttleManagerInstance().requestThrottle(currentAddress, this, true);
496        }
497        else {
498            requestOK = InstanceManager.throttleManagerInstance().requestThrottle(rosterEntry, this, true);
499        }
500        if (!requestOK) {
501            JOptionPane.showMessageDialog(mainPanel, Bundle.getMessage("AddressInUse"));
502        }
503    }
504
505    private void changeOfConsistAddress() {
506        if (consistAddress == null) {
507            return; // no address
508        }  // send notification of new address
509        for (AddressListener l : listeners) {
510            //log.debug("Notify address listener {} of address change ", l.getClass());
511            l.notifyConsistAddressChosen(consistAddress.getNumber(), consistAddress.isLongAddress());
512        }
513
514        boolean requestOK
515                = InstanceManager.throttleManagerInstance().requestThrottle(consistAddress, this, true);
516        if (!requestOK) {
517            JOptionPane.showMessageDialog(mainPanel, Bundle.getMessage("AddressInUse"));
518        }
519    }
520
521    /**
522     * Open a programmer for this address
523     */
524    protected void openProgrammer() {
525        if (rosterEntry == null) {
526            return;
527        }
528
529        java.util.ResourceBundle rbt = java.util.ResourceBundle.getBundle("jmri.jmrit.symbolicprog.SymbolicProgBundle");
530        String ptitle = java.text.MessageFormat.format(rbt.getString("FrameOpsProgrammerTitle"), rosterEntry.getId());
531        // find the ops-mode programmer
532        int address = Integer.parseInt(rosterEntry.getDccAddress());
533        boolean longAddr = true;
534        if (address < 100) {
535            longAddr = false;
536        }
537        Programmer programmer = InstanceManager.getDefault(jmri.AddressedProgrammerManager.class).getAddressedProgrammer(longAddr, address);
538        // and created the frame
539        JFrame p = new PaneOpsProgFrame(null, rosterEntry,
540                ptitle, "programmers" + File.separator + ProgDefault.getDefaultProgFile() + ".xml",
541                programmer);
542        p.pack();
543        p.setVisible(true);
544    }
545
546    /**
547     * Dispatch the current address for use by other throttles
548     */
549    public void dispatchAddress() {
550        if (throttle != null) {
551            int usageCount  = InstanceManager.throttleManagerInstance().getThrottleUsageCount(throttle.getLocoAddress()) - 1;
552
553            if ( usageCount != 0 ) {
554                JOptionPane.showMessageDialog(mainPanel, Bundle.getMessage("CannotDisptach", usageCount));
555                return;
556            }
557            InstanceManager.throttleManagerInstance().dispatchThrottle(throttle, this);
558            if (consistThrottle != null) {
559                InstanceManager.throttleManagerInstance().dispatchThrottle(consistThrottle, this);
560                consistThrottle = null;
561            }
562            notifyThrottleDisposed();
563        }
564    }
565
566    /**
567     * Release the current address.
568     */
569    public void releaseAddress() {
570        InstanceManager.throttleManagerInstance().releaseThrottle(throttle, this);
571        if (consistThrottle != null) {
572            InstanceManager.throttleManagerInstance().releaseThrottle(consistThrottle, this);
573            consistThrottle = null;
574        }
575        notifyThrottleDisposed();
576    }
577
578    private void notifyListenersOfThrottleRelease() {
579        if (listeners != null) {
580            listeners.forEach((l) -> {
581                // log.debug("Notify address listener {} of release", l.getClass());
582                l.notifyAddressReleased(currentAddress);
583            });
584        }
585    }
586
587    /**
588     * Create an Element of this object's preferences.
589     * <ul>
590     * <li> Window Preferences
591     * <li> Address value
592     * </ul>
593     *
594     * @return org.jdom2.Element for this objects preferences. Defined in
595     *         DTD/throttle-config
596     */
597    public Element getXml() {
598        Element me = new Element("AddressPanel");
599        //Element window = new Element("window");
600        java.util.ArrayList<Element> children = new java.util.ArrayList<>(1);
601        children.add(WindowPreferences.getPreferences(this));
602        children.add((new jmri.configurexml.LocoAddressXml())
603                .store(addrSelector.getAddress()));
604        children.add((new jmri.configurexml.LocoAddressXml())
605                .store(consistAddress));
606        me.setContent(children);
607        return me;
608    }
609
610    /**
611     * Use the Element passed to initialize based on user prefs.
612     *
613     * @param e The Element containing prefs as defined in DTD/throttle-config
614     */
615    public void setXml(Element e) {
616        Element window = e.getChild("window");
617        WindowPreferences.setPreferences(this, window);
618
619        Element addressElement = e.getChild("address");
620        if ((addressElement != null) && (this.getRosterEntry() == null)) {
621            String address = addressElement.getAttribute("value").getValue();
622            addrSelector.setAddress(new DccLocoAddress(Integer
623                    .parseInt(address), false)); // guess at the short/long
624            consistAddress = null;
625            changeOfAddress();
626        }
627
628        List<Element> elementList = e.getChildren("locoaddress");
629        if ((elementList.size() > 0) && (getThrottle() == null)) {
630            log.debug("found {} locoaddress(es)", elementList.size() );
631            currentAddress = (DccLocoAddress) (new jmri.configurexml.LocoAddressXml())
632                    .getAddress(elementList.get(0));
633            log.debug("Loaded address {} from xml",currentAddress);
634            addrSelector.setAddress(currentAddress);
635            consistAddress = null;
636            // if there are two locoaddress, the second is the consist address
637            if (elementList.size() > 1) {
638                DccLocoAddress tmpAdd = ((DccLocoAddress) (new jmri.configurexml.LocoAddressXml())
639                        .getAddress(elementList.get(1)));
640                if (tmpAdd !=null && ! currentAddress.equals(tmpAdd)) {                    
641                    log.debug("and consist with {}",tmpAdd);
642                    consistAddress = tmpAdd;
643                }
644            }
645            changeOfAddress();
646        }
647    }
648
649    /**
650     * @return the RosterEntrySelectorPanel
651     */
652    public RosterEntrySelectorPanel getRosterEntrySelector() {
653        return rosterBox;
654    }
655
656    public DccThrottle getThrottle() {
657        return throttle;
658    }
659
660    public DccLocoAddress getCurrentAddress() {
661        return currentAddress;
662    }
663
664    public void setCurrentAddress(DccLocoAddress currentAddress) {
665        if (log.isDebugEnabled()) {
666            log.debug("Setting CurrentAddress to {}", currentAddress);
667        }
668        this.addrSelector.setAddress(currentAddress);
669        changeOfAddress();
670    }
671
672    public void setAddress(int consistNumber, boolean b) {
673        setCurrentAddress(new DccLocoAddress(consistNumber, b));
674    }
675
676    public DccLocoAddress getConsistAddress() {
677        return consistAddress;
678    }
679
680    public void setConsistAddress(DccLocoAddress consistAddress) {
681        if (log.isDebugEnabled()) {
682            log.debug("Setting Consist Address to {}", consistAddress);
683        }
684        this.consistAddress = consistAddress;
685        changeOfConsistAddress();
686
687    }
688
689    @Override
690    public void propertyChange(PropertyChangeEvent evt) {
691        if (evt == null) {
692            return;
693        }
694        if ("ThrottleConnected".compareTo(evt.getPropertyName()) == 0) {
695            if (((Boolean) evt.getOldValue()) && (!((Boolean) evt.getNewValue()))) {
696                log.debug("propertyChange: ThrottleConnected to false");
697                notifyThrottleDisposed();
698            }
699        }
700        
701        if ("DispatchEnabled".compareTo(evt.getPropertyName()) == 0) {
702            log.debug("propertyChange: Dispatch Button Enabled {}" , evt.getNewValue() );
703            dispatchButton.setEnabled( (Boolean) evt.getNewValue() );
704        }
705        
706        if ("ReleaseEnabled".compareTo(evt.getPropertyName()) == 0) {
707            log.debug("propertyChange: release Button Enabled {}" , evt.getNewValue() );
708            releaseButton.setEnabled( (Boolean) evt.getNewValue() );
709        }        
710    }
711    
712    void applyPreferences() {
713        // nothing to do, for now
714    }
715    
716    private final static Logger log = LoggerFactory.getLogger(AddressPanel.class);
717
718}
719