001package jmri.jmrit.progsupport;
002
003import java.awt.event.ActionListener;
004import java.beans.PropertyChangeListener;
005import java.util.*;
006
007import javax.annotation.Nonnull;
008import javax.swing.*;
009
010import org.slf4j.Logger;
011import org.slf4j.LoggerFactory;
012
013import jmri.*;
014
015/**
016 * Provide a JPanel to configure the service mode (Global) programmer.
017 * <p>
018 * The using code should get a configured programmer with getProgrammer. Since
019 * there's only one service mode programmer, maybe this isn't critical, but it's
020 * a good idea for the future.
021 * <p>
022 * A ProgModePane may "share" between one of these and a ProgOpsModePane, which
023 * means that there might be _none_ of these buttons selected. When that
024 * happens, the mode of the underlying programmer is left unchanged and no
025 * message is propagated.
026 * <p>
027 * Note that you should call the dispose() method when you're really done, so
028 * that a ProgModePane object can disconnect its listeners.
029 * <hr>
030 * This file is part of JMRI.
031 * <p>
032 * JMRI is free software; you can redistribute it and/or modify it under the
033 * terms of version 2 of the GNU General Public License as published by the Free
034 * Software Foundation. See the "COPYING" file for a copy of this license.
035 * <p>
036 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY
037 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
038 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
039 *
040 * @author Bob Jacobsen      Copyright (C) 2001, 2014
041 * @author Daniel Bergqvist  Copyright (C) 2021
042 */
043public class ProgServiceModePane extends ProgModeSelector implements PropertyChangeListener, ActionListener {
044
045    ButtonGroup modeGroup = new ButtonGroup();
046    HashMap<ProgrammingMode, JRadioButton> buttonMap = new HashMap<>();
047    JComboBox<GlobalProgrammerManager> progBox;
048    ArrayList<JRadioButton> buttonPool = new ArrayList<>();
049
050    /**
051     * Get the selected programmer
052     */
053    @Override
054    public Programmer getProgrammer() {
055        if (progBox.getSelectedItem() == null) {
056            return null;
057        }
058        return ((GlobalProgrammerManager) progBox.getSelectedItem()).getGlobalProgrammer();
059    }
060
061    /**
062     * Are any of the modes selected?
063     *
064     * @return true is any button is selected
065     */
066    @Override
067    public boolean isSelected() {
068        for (JRadioButton button : buttonMap.values()) {
069            if (button.isSelected()) {
070                return true;
071            }
072        }
073        return false;
074    }
075
076    /**
077     * @param direction controls layout, either BoxLayout.X_AXIS or
078     *                  BoxLayout.Y_AXIS
079     */
080    public ProgServiceModePane(int direction) {
081        this(direction, new javax.swing.ButtonGroup());
082    }
083
084    /**
085     * Get the list of global managers.
086     *
087     * @return empty list if none
088     */
089    @Nonnull
090    public List<GlobalProgrammerManager> getMgrList() {
091        return InstanceManager.getList(jmri.GlobalProgrammerManager.class);
092    }
093
094    /**
095     * Create a new Programmer Service Mode Pane.
096     * @param direction controls layout, either BoxLayout.X_AXIS or
097     *                  BoxLayout.Y_AXIS
098     * @param group     mode button group.
099     */
100    public ProgServiceModePane(int direction, javax.swing.ButtonGroup group) {
101        modeGroup = group;
102
103        // general GUI config
104        setLayout(new BoxLayout(this, direction));
105
106        // create the programmer display combo box
107        java.util.List<GlobalProgrammerManager> v = new java.util.ArrayList<>();
108        for (GlobalProgrammerManager pm : getMgrList()) {
109            Programmer progrmr = pm.getGlobalProgrammer();
110            if (progrmr!=null) {
111                v.add(pm);
112                // listen for changes
113                progrmr.addPropertyChangeListener(this);
114            }
115        }
116
117        add(progBox = new JComboBox<>(v.toArray(new GlobalProgrammerManager[0])));
118        // if only one, don't show
119        if (progBox.getItemCount() < 2) {
120            // no choice, so don't display, don't monitor for changes
121            progBox.setVisible(false);
122        } else {
123            log.debug("Set combobox box selection to InstanceManager global default: {}", InstanceManager.getDefault(jmri.GlobalProgrammerManager.class));
124            progBox.setSelectedItem(InstanceManager.getDefault(jmri.GlobalProgrammerManager.class)); // set default
125            progBox.addActionListener((java.awt.event.ActionEvent e) -> {
126                // new programmer selection
127                programmerSelected();
128            });
129        }
130
131        // Horizontal glue is needed since the panel is too narrow otherwise
132        add(javax.swing.Box.createHorizontalGlue());
133
134        // and execute the setup for 1st time
135        programmerSelected();
136
137    }
138
139    /**
140     * reload the interface with the new programmers
141     */
142    void programmerSelected() {
143        log.debug("programmerSelected starts with {} buttons", buttonPool.size());
144        // hide buttons
145        for (JRadioButton button : buttonPool) {
146            button.setVisible(false);
147        }
148
149        // clear map
150        buttonMap.clear();
151
152        // configure buttons
153        int index = 0;
154        if (getProgrammer() == null) {
155            return;
156        }
157        List<ProgrammingMode> modes = getProgrammer().getSupportedModes();
158        log.debug("   has {} modes", modes.size());
159        for (ProgrammingMode mode : modes) {
160            JRadioButton button;
161            // need a new button?
162            if (index >= buttonPool.size()) {
163                log.debug("   add button");
164                button = new JRadioButton();
165                buttonPool.add(button);
166                modeGroup.add(button);
167                button.addActionListener(this);
168                add(button); // add to GUI
169            }
170            // configure next button in pool
171            log.debug("   set for {}", mode.toString());
172            button = buttonPool.get(index++);
173            button.setVisible(true);
174            modeGroup.add(button);
175            button.setText(mode.toString());
176            buttonMap.put(mode, button);
177        }
178        setGuiFromProgrammer();
179    }
180
181    /**
182     * Listen to buttons for mode changes
183     */
184    @Override
185    public void actionPerformed(java.awt.event.ActionEvent e) {
186        // find selected button
187        log.debug("Selected button: {}", e.getActionCommand());
188        for (ProgrammingMode mode : buttonMap.keySet()) {
189            if (mode.toString().equals(e.getActionCommand())) {
190                log.debug("      set mode {} on {}", mode.toString(), getProgrammer());
191                getProgrammer().setMode(mode);
192                return; // 1st match
193            }
194        }
195    }
196
197    /**
198     * Listen to programmer for mode changes
199     */
200    @Override
201    public void propertyChange(java.beans.PropertyChangeEvent e) {
202        if ("Mode".equals(e.getPropertyName()) && getProgrammer().equals(e.getSource())) {
203            // mode changed in programmer, change GUI here if needed
204            log.debug("Mode propertyChange with {}", isSelected());
205            if (isSelected()) {  // only change mode if we have a selected mode, in case some other selector with shared group has the selection
206                setGuiFromProgrammer();
207            }
208        }
209    }
210
211    void setGuiFromProgrammer() {
212        ProgrammingMode mode = getProgrammer().getMode();
213        JRadioButton button = buttonMap.get(mode);
214        log.debug("  setting button for mode {}", mode);
215        if (button == null) {
216            log.debug("   didn't find button, returning");
217            return;
218        }
219        button.setSelected(true);
220    }
221
222    // no longer needed, disconnect if still connected
223    @Override
224    public void dispose() {
225        for (GlobalProgrammerManager pm : getMgrList()) {
226            Programmer gp = pm.getGlobalProgrammer();
227            if (gp != null) {
228                gp.removePropertyChangeListener(this);
229            }
230        }
231    }
232
233    private final static Logger log = LoggerFactory.getLogger(ProgServiceModePane.class);
234}