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 void stateChanged(ChangeEvent e) { 148 spinnerUpdated(); 149 } 150 151 @Override 152 @Nonnull 153 public String getTypeString() { 154 return Bundle.getMessage("PositionableType_GlobalVariableSpinnerIcon"); 155 } 156 157 @Override 158 public String getNameString() { 159 String name; 160 if (namedGlobalVariable == null) { 161 name = Bundle.getMessage("NotConnected"); 162 } else { 163 name = getGlobalVariable().getDisplayName(DisplayOptions.USERNAME_SYSTEMNAME); 164 } 165 return name; 166 } 167 168 /* 169 public void setSelectable(boolean b) {selectable = b;} 170 public boolean isSelectable() { return selectable;} 171 boolean selectable = false; 172 */ 173 @Override 174 public boolean setEditIconMenu(javax.swing.JPopupMenu popup) { 175 String txt = java.text.MessageFormat.format(Bundle.getMessage("EditItem"), Bundle.getMessage("BeanNameGlobalVariable")); 176 popup.add(new AbstractAction(txt) { 177 @Override 178 public void actionPerformed(ActionEvent e) { 179 edit(); 180 } 181 }); 182 return true; 183 } 184 185 @Override 186 protected void edit() { 187 makeIconEditorFrame(this, "GlobalVariable", true, null); 188 _iconEditor.setPickList(jmri.jmrit.picker.PickListModel.globalVariablePickModelInstance()); 189 ActionListener addIconAction = a -> editGlobalVariable(); 190 _iconEditor.complete(addIconAction, false, false, true); 191 _iconEditor.setSelection(getGlobalVariable()); 192 } 193 194 void editGlobalVariable() { 195 setGlobalVariable(_iconEditor.getTableSelection().getDisplayName()); 196 setSize(getPreferredSize().width, getPreferredSize().height); 197 _iconEditorFrame.dispose(); 198 _iconEditorFrame = null; 199 _iconEditor = null; 200 invalidate(); 201 } 202 203 /** 204 * Drive the current state of the display from the state of the GlobalVariable. 205 */ 206 public void displayState() { 207 log.debug("displayState"); 208 if (namedGlobalVariable == null) { // leave alone if not connected yet 209 return; 210 } 211 if (getGlobalVariable().getValue() == null) { 212 return; 213 } 214 Integer num = null; 215 if (getGlobalVariable().getValue().getClass() == String.class) { 216 try { 217 num = Integer.valueOf((String) getGlobalVariable().getValue()); 218 } catch (NumberFormatException e) { 219 return; 220 } 221 } else if (getGlobalVariable().getValue().getClass() == Integer.class) { 222 num = ((Number) getGlobalVariable().getValue()).intValue(); 223 } else if (getGlobalVariable().getValue().getClass() == Float.class) { 224 num = Math.round((Float) getGlobalVariable().getValue()); 225 log.debug("num= {}", num); 226 } else { 227 //spinner.setValue(getGlobalVariable().getValue()); 228 return; 229 } 230 int n = num; 231 if (n > _max) { 232 num = _max; 233 } else if (n < _min) { 234 num = _min; 235 } 236 spinner.setValue(num); 237 } 238 239 @Override 240 public void mouseExited(JmriMouseEvent e) { 241 spinnerUpdated(); 242 super.mouseExited(e); 243 } 244 245 protected void spinnerUpdated() { 246 if (namedGlobalVariable == null) { 247 return; 248 } 249 if (getGlobalVariable().getValue() == null) { 250 getGlobalVariable().setValue(spinner.getValue()); 251 return; 252 } 253 // Spinner is always an Integer, but memory can contain Integer or String 254 if (getGlobalVariable().getValue().getClass() == String.class) { 255 String newValue = "" + spinner.getValue(); 256 if (!getGlobalVariable().getValue().equals(newValue)) { 257 getGlobalVariable().setValue(newValue); 258 } 259 } else { 260 getGlobalVariable().setValue(spinner.getValue()); 261 } 262 } 263 264 public String getValue() { 265 return "" + spinner.getValue(); 266 } 267 268 @Override 269 void cleanup() { 270 if (namedGlobalVariable != null) { 271 getGlobalVariable().removePropertyChangeListener(this); 272 } 273 if (spinner != null) { 274 spinner.removeChangeListener(this); 275 ((JSpinner.DefaultEditor) spinner.getEditor()).getTextField().removeMouseMotionListener(_mouseMotionListener); 276 ((JSpinner.DefaultEditor) spinner.getEditor()).getTextField().removeMouseListener(_mouseListener); 277 } 278 namedGlobalVariable = null; 279 } 280 281 private final static Logger log = LoggerFactory.getLogger(GlobalVariableSpinnerIcon.class); 282}