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