001package jmri.jmrit.display; 002 003import java.awt.Dimension; 004import java.awt.event.ActionEvent; 005import java.awt.event.ActionListener; 006import java.beans.PropertyChangeListener; 007 008import javax.annotation.Nonnull; 009import javax.swing.AbstractAction; 010import javax.swing.JSpinner; 011import javax.swing.SpinnerNumberModel; 012import javax.swing.event.ChangeEvent; 013import javax.swing.event.ChangeListener; 014 015import jmri.InstanceManager; 016import jmri.NamedBeanHandle; 017import jmri.NamedBean.DisplayOptions; 018import jmri.jmrit.logixng.GlobalVariable; 019import jmri.jmrit.logixng.GlobalVariableManager; 020import jmri.util.swing.*; 021 022import org.slf4j.Logger; 023import org.slf4j.LoggerFactory; 024 025/** 026 * An icon to display a status of a GlobalVariable in a JSpinner. 027 * <p> 028 * Handles the case of either a String or an Integer in the GlobalVariable, preserving 029 * what it finds. 030 * 031 * @author Bob Jacobsen Copyright (c) 2009 032 * @author Daniel Bergqvist Copyright (C) 2022 033 * @since 2.7.2 034 */ 035public class GlobalVariableSpinnerIcon extends PositionableJPanel implements ChangeListener, PropertyChangeListener { 036 037 int _min = 0; 038 int _max = 100; 039 JSpinner spinner = new JSpinner(new SpinnerNumberModel(0, _min, _max, 1)); 040 // the associated GlobalVariable object 041 //GlobalVariable globalVariable = null; 042 private NamedBeanHandle<GlobalVariable> namedGlobalVariable; 043 044 private final java.awt.event.MouseListener _mouseListener = JmriMouseListener.adapt(this); 045 private final java.awt.event.MouseMotionListener _mouseMotionListener = JmriMouseMotionListener.adapt(this); 046 047 public GlobalVariableSpinnerIcon(Editor editor) { 048 super(editor); 049 setDisplayLevel(Editor.LABELS); 050 051 setLayout(new java.awt.GridBagLayout()); 052 add(spinner, new java.awt.GridBagConstraints()); 053 spinner.addChangeListener(this); 054 javax.swing.JTextField textBox = ((JSpinner.DefaultEditor) spinner.getEditor()).getTextField(); 055 textBox.addMouseMotionListener(_mouseMotionListener); 056 textBox.addMouseListener(_mouseListener); 057 setPopupUtility(new PositionablePopupUtil(this, textBox)); 058 } 059 060 @Override 061 public Positionable deepClone() { 062 GlobalVariableSpinnerIcon pos = new GlobalVariableSpinnerIcon(_editor); 063 return finishClone(pos); 064 } 065 066 protected Positionable finishClone(GlobalVariableSpinnerIcon pos) { 067 pos.setGlobalVariable(namedGlobalVariable.getName()); 068 return super.finishClone(pos); 069 } 070 071 @Override 072 public javax.swing.JComponent getTextComponent() { 073 return ((JSpinner.DefaultEditor) spinner.getEditor()).getTextField(); 074 } 075 076 @Override 077 public Dimension getSize() { 078 if (log.isDebugEnabled()) { 079 Dimension d = spinner.getSize(); 080 log.debug("spinner width= {}, height= {}", d.width, d.height); 081 java.awt.Rectangle rect = getBounds(null); 082 log.debug("Bounds rect= ({},{}) width= {}, height= {}", 083 rect.x, rect.y, rect.width, rect.height); 084 d = super.getSize(); 085 log.debug("Panel width= {}, height= {}", d.width, d.height); 086 } 087 return super.getSize(); 088 } 089 090 /** 091 * Attached a named GlobalVariable to this display item 092 * 093 * @param pName Used as a system/user name to lookup the GlobalVariable object 094 */ 095 public void setGlobalVariable(String pName) { 096 log.debug("setGlobalVariable for memory= {}", pName); 097 if (InstanceManager.getNullableDefault(GlobalVariableManager.class) != null) { 098 try { 099 GlobalVariable globalVariable = InstanceManager.getDefault(GlobalVariableManager.class).getGlobalVariable(pName); 100 setGlobalVariable(jmri.InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(pName, globalVariable)); 101 } catch (IllegalArgumentException e) { 102 log.error("GlobalVariable '{}' not available, icon won't see changes", pName); 103 } 104 } else { 105 log.error("No GlobalVariableManager for this protocol, icon won't see changes"); 106 } 107 updateSize(); 108 } 109 110 /** 111 * Attached a named GlobalVariable to this display item 112 * 113 * @param m The GlobalVariable object 114 */ 115 public void setGlobalVariable(NamedBeanHandle<GlobalVariable> m) { 116 if (namedGlobalVariable != null) { 117 getGlobalVariable().removePropertyChangeListener(this); 118 } 119 namedGlobalVariable = m; 120 if (namedGlobalVariable != null) { 121 getGlobalVariable().addPropertyChangeListener(this, namedGlobalVariable.getName(), "GlobalVariable Spinner Icon"); 122 displayState(); 123 setName(namedGlobalVariable.getName()); 124 } 125 } 126 127 public NamedBeanHandle<GlobalVariable> getNamedGlobalVariable() { 128 return namedGlobalVariable; 129 } 130 131 // update icon as state of GlobalVariable changes 132 @Override 133 public void propertyChange(java.beans.PropertyChangeEvent e) { 134 if (e.getPropertyName().equals("value")) { 135 displayState(); 136 } 137 } 138 139 public GlobalVariable getGlobalVariable() { 140 if (namedGlobalVariable == null) { 141 return null; 142 } 143 return namedGlobalVariable.getBean(); 144 } 145 146 @Override 147 public jmri.NamedBean getNamedBean() { 148 return getGlobalVariable(); 149 } 150 151 @Override 152 public void stateChanged(ChangeEvent e) { 153 spinnerUpdated(); 154 } 155 156 @Override 157 @Nonnull 158 public String getTypeString() { 159 return Bundle.getMessage("PositionableType_GlobalVariableSpinnerIcon"); 160 } 161 162 @Override 163 public String getNameString() { 164 String name; 165 if (namedGlobalVariable == null) { 166 name = Bundle.getMessage("NotConnected"); 167 } else { 168 name = getGlobalVariable().getDisplayName(DisplayOptions.USERNAME_SYSTEMNAME); 169 } 170 return name; 171 } 172 173 /* 174 public void setSelectable(boolean b) {selectable = b;} 175 public boolean isSelectable() { return selectable;} 176 boolean selectable = false; 177 */ 178 @Override 179 public boolean setEditIconMenu(javax.swing.JPopupMenu popup) { 180 String txt = java.text.MessageFormat.format(Bundle.getMessage("EditItem"), Bundle.getMessage("BeanNameGlobalVariable")); 181 popup.add(new AbstractAction(txt) { 182 @Override 183 public void actionPerformed(ActionEvent e) { 184 edit(); 185 } 186 }); 187 return true; 188 } 189 190 @Override 191 protected void edit() { 192 makeIconEditorFrame(this, "GlobalVariable", true, null); 193 _iconEditor.setPickList(jmri.jmrit.picker.PickListModel.globalVariablePickModelInstance()); 194 ActionListener addIconAction = a -> editGlobalVariable(); 195 _iconEditor.complete(addIconAction, false, false, true); 196 _iconEditor.setSelection(getGlobalVariable()); 197 } 198 199 void editGlobalVariable() { 200 setGlobalVariable(_iconEditor.getTableSelection().getDisplayName()); 201 setSize(getPreferredSize().width, getPreferredSize().height); 202 _iconEditorFrame.dispose(); 203 _iconEditorFrame = null; 204 _iconEditor = null; 205 invalidate(); 206 } 207 208 /** 209 * Drive the current state of the display from the state of the GlobalVariable. 210 */ 211 public void displayState() { 212 log.debug("displayState"); 213 if (namedGlobalVariable == null) { // leave alone if not connected yet 214 return; 215 } 216 if (getGlobalVariable().getValue() == null) { 217 return; 218 } 219 Integer num = null; 220 if (getGlobalVariable().getValue().getClass() == String.class) { 221 try { 222 num = Integer.valueOf((String) getGlobalVariable().getValue()); 223 } catch (NumberFormatException e) { 224 return; 225 } 226 } else if (getGlobalVariable().getValue().getClass() == Integer.class) { 227 num = ((Number) getGlobalVariable().getValue()).intValue(); 228 } else if (getGlobalVariable().getValue().getClass() == Float.class) { 229 num = Math.round((Float) getGlobalVariable().getValue()); 230 log.debug("num= {}", num); 231 } else { 232 //spinner.setValue(getGlobalVariable().getValue()); 233 return; 234 } 235 int n = num; 236 if (n > _max) { 237 num = _max; 238 } else if (n < _min) { 239 num = _min; 240 } 241 spinner.setValue(num); 242 } 243 244 @Override 245 public void mouseExited(JmriMouseEvent e) { 246 spinnerUpdated(); 247 super.mouseExited(e); 248 } 249 250 protected void spinnerUpdated() { 251 if (namedGlobalVariable == null) { 252 return; 253 } 254 if (getGlobalVariable().getValue() == null) { 255 getGlobalVariable().setValue(spinner.getValue()); 256 return; 257 } 258 // Spinner is always an Integer, but memory can contain Integer or String 259 if (getGlobalVariable().getValue().getClass() == String.class) { 260 String newValue = "" + spinner.getValue(); 261 if (!getGlobalVariable().getValue().equals(newValue)) { 262 getGlobalVariable().setValue(newValue); 263 } 264 } else { 265 getGlobalVariable().setValue(spinner.getValue()); 266 } 267 } 268 269 public String getValue() { 270 return "" + spinner.getValue(); 271 } 272 273 @Override 274 void cleanup() { 275 if (namedGlobalVariable != null) { 276 getGlobalVariable().removePropertyChangeListener(this); 277 } 278 if (spinner != null) { 279 spinner.removeChangeListener(this); 280 ((JSpinner.DefaultEditor) spinner.getEditor()).getTextField().removeMouseMotionListener(_mouseMotionListener); 281 ((JSpinner.DefaultEditor) spinner.getEditor()).getTextField().removeMouseListener(_mouseListener); 282 } 283 namedGlobalVariable = null; 284 } 285 286 private static final Logger log = LoggerFactory.getLogger(GlobalVariableSpinnerIcon.class); 287}