001package jmri.util.swing; 002 003import java.awt.FlowLayout; 004import java.awt.event.ActionEvent; 005import java.awt.event.ItemEvent; 006 007import javax.annotation.Nonnull; 008import javax.swing.BoxLayout; 009import javax.swing.ButtonGroup; 010import javax.swing.JPanel; 011import javax.swing.JRadioButton; 012import javax.swing.JTextField; 013import jmri.InstanceManager; 014import jmri.JmriException; 015import jmri.Manager; 016import jmri.NamedBean; 017import jmri.NamedBean.DisplayOptions; 018import jmri.ProvidingManager; 019import jmri.UserPreferencesManager; 020import jmri.ProxyManager; 021import jmri.swing.ManagerComboBox; 022import jmri.swing.NamedBeanComboBox; 023import jmri.swing.SystemNameValidator; 024import org.slf4j.Logger; 025import org.slf4j.LoggerFactory; 026 027public class BeanSelectCreatePanel<E extends NamedBean> extends JPanel { 028 029 //Manager<E> _manager; 030 E _defaultSelect; 031 String _reference = null; 032 JRadioButton existingItem = new JRadioButton(); 033 JRadioButton newItem; 034 ButtonGroup selectcreate = new ButtonGroup(); 035 036 NamedBeanComboBox<E> existingCombo; 037 JTextField hardwareAddress = new JTextField(8); 038 ManagerComboBox<E> prefixBox = new ManagerComboBox<>(); 039 String systemSelectionCombo = this.getClass().getName() + ".SystemSelected"; 040 041 /** 042 * Create a JPanel that provides the option to the user to either select an 043 * already created bean, or to create one on the fly. This only currently 044 * works with Turnouts, Sensors, Memories and Blocks. 045 * 046 * @param manager the bean manager 047 * @param defaultSelect the bean that is selected by default 048 */ 049 public BeanSelectCreatePanel(@Nonnull Manager<E> manager, E defaultSelect) { 050 _defaultSelect = defaultSelect; 051 UserPreferencesManager p = InstanceManager.getDefault(UserPreferencesManager.class); 052 existingItem = new JRadioButton(Bundle.getMessage("UseExisting"), true); 053 newItem = new JRadioButton(Bundle.getMessage("CreateNew")); 054 existingItem.addActionListener((ActionEvent e) -> { 055 update(); 056 }); 057 newItem.addActionListener((ActionEvent e) -> { 058 update(); 059 }); 060 061 selectcreate.add(existingItem); 062 selectcreate.add(newItem); 063 existingCombo = new NamedBeanComboBox<>(manager, defaultSelect, DisplayOptions.USERNAME_SYSTEMNAME); 064 // If the combo list is empty we go straight to creation. 065 if (existingCombo.getItemCount() == 0) { 066 newItem.setSelected(true); 067 } 068 existingCombo.setAllowNull(true); 069 JComboBoxUtil.setupComboBoxMaxRows(existingCombo); 070 071 JPanel radio = new JPanel(); 072 radio.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 0)); 073 JPanel bean = new JPanel(); 074 bean.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 0)); 075 radio.add(existingItem); 076 radio.add(newItem); 077 078 if (manager instanceof ProxyManager) { 079 ProxyManager<E> proxy = (ProxyManager<E>) manager; 080 prefixBox.setManagers(proxy.getManagerList(), proxy.getDefaultManager()); 081 if (p.getComboBoxLastSelection(systemSelectionCombo) != null) { 082 prefixBox.setSelectedItem(p.getComboBoxLastSelection(systemSelectionCombo)); 083 } 084 } else { // not a proxy, just one 085 prefixBox.setManagers(manager); 086 } 087 088 bean.add(existingCombo); 089 bean.add(prefixBox); 090 bean.add(hardwareAddress); 091 hardwareAddress.setToolTipText(Bundle.getMessage("EnterHWaddressAsIntTooltip")); 092 SystemNameValidator validator = new SystemNameValidator(hardwareAddress, prefixBox.getSelectedItem()); 093 prefixBox.addItemListener((ItemEvent e) -> { 094 validator.setManager(prefixBox.getSelectedItem()); 095 }); 096 hardwareAddress.setInputVerifier(validator); 097 super.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); 098 super.add(radio); 099 super.add(bean); 100 BeanSelectCreatePanel.this.update(); 101 } 102 103 void update() { 104 boolean select = true; 105 if (newItem.isSelected()) { 106 select = false; 107 } 108 prefixBox.setVisible(false); 109 hardwareAddress.setVisible(false); 110 existingCombo.setVisible(false); 111 if (select) { 112 existingCombo.setVisible(true); 113 } else { 114 prefixBox.setVisible(true); 115 hardwareAddress.setVisible(true); 116 } 117 } 118 119 @Override 120 public void setEnabled(boolean enabled) { 121 existingItem.setEnabled(enabled); 122 hardwareAddress.setEnabled(enabled); 123 prefixBox.setEnabled(enabled); 124 newItem.setEnabled(enabled); 125 existingCombo.setEnabled(enabled); 126 super.setEnabled(enabled); 127 } 128 129 /** 130 * Get the display name of the bean that has either been selected in the 131 * drop down list or was asked to be created. 132 * 133 * @return the name of the bean 134 */ 135 public String getDisplayName() { 136 if (existingItem.isSelected()) { 137 return existingCombo.getSelectedItemDisplayName(); 138 } else { 139 try { 140 E nBean = createBean(); 141 return nBean.getDisplayName(); 142 } catch (JmriException e) { 143 return ""; 144 } 145 } 146 } 147 148 /** 149 * Is a bean either selected or has the user entered a new name in the combo box? 150 * If either is false, the caller should not try to create a new bean. 151 * @return true if a bean is selected or a name is entered 152 */ 153 public boolean hasBeanOrBeanName() { 154 return existingItem.isSelected() || !hardwareAddress.getText().trim().isEmpty(); 155 } 156 157 /** 158 * Get the named bean that has either been selected in the drop down list or 159 * was asked to be created. 160 * 161 * @return the selected bean or a new bean 162 * @throws JmriException if a bean needs to be created but can't be 163 */ 164 public E getNamedBean() throws JmriException { 165 if (existingItem.isSelected()) { 166 return existingCombo.getSelectedItem(); 167 } 168 try { 169 return createBean(); 170 } catch (JmriException e) { 171 throw e; 172 } 173 } 174 175 private E createBean() throws JmriException { 176 Manager<E> manager = prefixBox.getSelectedItem(); 177 E nBean = null; 178 if (manager instanceof ProvidingManager) { 179 ProvidingManager<E> provider = (ProvidingManager<E>) manager; 180 try { 181 nBean = provider.provide(provider.makeSystemName(hardwareAddress.getText())); 182 } catch (IllegalArgumentException ex) { 183 throw new JmriException(ex); 184 } 185 } 186 if (nBean == null) { 187 throw new JmriException("Unable to create bean"); 188 } 189 updateComment(nBean, _reference); 190 setDefaultNamedBean(nBean); 191 return nBean; 192 } 193 194 /** 195 * Set a reference that can be set against the comment for a bean. 196 * 197 * @param ref the default comment for a bean without a comment 198 */ 199 public void setReference(String ref) { 200 _reference = ref; 201 } 202 203 /** 204 * Set the default selected item in the combo box. After it has been set, 205 * the combo box becomes active and the Add Hardware box details are then 206 * hidden. 207 * 208 * @param nBean the bean that is selected by default 209 */ 210 public void setDefaultNamedBean(E nBean) { 211 _defaultSelect = nBean; 212 existingCombo.setSelectedItem(_defaultSelect); 213 existingItem.setSelected(true); 214 update(); 215 } 216 217 /** 218 * Check that the user selected something in this BeanSelectCreatePanel. 219 * 220 * @return true if not empty 221 */ 222 public boolean isEmpty() { 223 if (existingItem.isSelected() && existingCombo.getSelectedItem() != null) { // use existing 224 log.debug("existingCombo.getSelectedBean() = {}", existingCombo.getSelectedItem().getDisplayName()); 225 return false; 226 } else if (newItem.isSelected() && // create new 227 !hardwareAddress.getText().isEmpty() && hardwareAddress.getText() != null) { 228 log.debug("newBeanEntry = {}", hardwareAddress.getText()); 229 return false; 230 } 231 return true; 232 } 233 234 /** 235 * Update comment on bean if there's content AND there's not already a comment. 236 * 237 * @param nBean the bean to edit 238 * @param content comment to add 239 */ 240 public void updateComment(@Nonnull E nBean, String content) { 241 String comment = nBean.getComment(); 242 log.debug("comment {}", (comment == null || comment.isEmpty()) ? "was empty" : "already filled"); 243 if((content != null && !content.isEmpty()) && (comment ==null || comment.isEmpty())) { 244 log.debug("new comment added to bean {}", nBean.getDisplayName()); 245 nBean.setComment(content); 246 } else { 247 log.debug("empty _reference received"); 248 } 249 } 250 251 public void dispose() { 252 existingCombo.dispose(); 253 } 254 255 //initialize logging 256 private final static Logger log = LoggerFactory.getLogger(BeanSelectCreatePanel.class.getName()); 257 258}