001package jmri.jmrit.display; 002 003import java.awt.event.ActionEvent; 004import java.awt.event.ActionListener; 005 006import jmri.InstanceManager; 007import jmri.Light; 008import jmri.NamedBean.DisplayOptions; 009import jmri.jmrit.catalog.NamedIcon; 010import jmri.util.swing.JmriMouseEvent; 011 012import org.slf4j.Logger; 013import org.slf4j.LoggerFactory; 014 015/** 016 * An icon to display a status of a light. 017 * <p> 018 * A click on the icon will command a state change. Specifically, it will set 019 * the state to the opposite (THROWN vs CLOSED) of the current state. 020 * <p> 021 * The default icons show a crossed lamp symbol. 022 * @see Editor#addLightEditor() 023 * 024 * @author Bob Jacobsen Copyright (c) 2002 025 */ 026public class LightIcon extends PositionableLabel implements java.beans.PropertyChangeListener { 027 028 public LightIcon(Editor editor) { 029 // super ctor call to make sure this is an icon label 030 super(new NamedIcon("resources/icons/smallschematics/lights/cross-on.png", 031 "resources/icons/smallschematics/lights/cross-off.png"), editor); 032 _control = true; 033 displayState(lightState()); 034 setPopupUtility(null); 035 } 036 037 // the associated Light object 038 Light light = null; 039 040 @Override 041 public Positionable deepClone() { 042 LightIcon pos = new LightIcon(_editor); 043 return finishClone(pos); 044 } 045 046 protected Positionable finishClone(LightIcon pos) { 047 pos.setLight(getNameString()); 048 pos.setOffIcon(cloneIcon(getOffIcon(), pos)); 049 pos.setOnIcon(cloneIcon(getOnIcon(), pos)); 050 pos.setInconsistentIcon(cloneIcon(getInconsistentIcon(), pos)); 051 pos.setUnknownIcon(cloneIcon(getUnknownIcon(), pos)); 052 return super.finishClone(pos); 053 } 054 055 /** 056 * Attached a named light to this display item 057 * 058 * @param pName Used as a system/user name to lookup the light object 059 */ 060 public void setLight(String pName) { 061 if (InstanceManager.getNullableDefault(jmri.LightManager.class) != null) { 062 light = InstanceManager.lightManagerInstance(). 063 provideLight(pName); 064 setLight(light); 065 } else { 066 log.error("No LightManager for this protocol, icon won't see changes"); 067 } 068 } 069 070 public void setLight(Light to) { 071 if (light != null) { 072 light.removePropertyChangeListener(this); 073 } 074 light = to; 075 if (light != null) { 076 displayState(lightState()); 077 light.addPropertyChangeListener(this); 078 } 079 } 080 081 public Light getLight() { 082 return light; 083 } 084 085 // display icons 086 String offLName = "resources/icons/smallschematics/lights/cross-on.png"; 087 NamedIcon off = new NamedIcon(offLName, offLName); 088 String onLName = "resources/icons/smallschematics/lights/cross-off.png"; 089 NamedIcon on = new NamedIcon(onLName, onLName); 090 String inconsistentLName = "resources/icons/smallschematics/lights/cross-inconsistent.png"; 091 NamedIcon inconsistent = new NamedIcon(inconsistentLName, inconsistentLName); 092 String unknownLName = "resources/icons/smallschematics/lights/cross-unknown.png"; 093 NamedIcon unknown = new NamedIcon(unknownLName, unknownLName); 094 095 public NamedIcon getOffIcon() { 096 return off; 097 } 098 099 public void setOffIcon(NamedIcon i) { 100 off = i; 101 displayState(lightState()); 102 } 103 104 public NamedIcon getOnIcon() { 105 return on; 106 } 107 108 public void setOnIcon(NamedIcon i) { 109 on = i; 110 displayState(lightState()); 111 } 112 113 public NamedIcon getInconsistentIcon() { 114 return inconsistent; 115 } 116 117 public void setInconsistentIcon(NamedIcon i) { 118 inconsistent = i; 119 displayState(lightState()); 120 } 121 122 public NamedIcon getUnknownIcon() { 123 return unknown; 124 } 125 126 public void setUnknownIcon(NamedIcon i) { 127 unknown = i; 128 displayState(lightState()); 129 } 130 131 @Override 132 public int maxHeight() { 133 return Math.max( 134 Math.max((off != null) ? off.getIconHeight() : 0, 135 (on != null) ? on.getIconHeight() : 0), 136 (inconsistent != null) ? inconsistent.getIconHeight() : 0 137 ); 138 } 139 140 @Override 141 public int maxWidth() { 142 return Math.max( 143 Math.max((off != null) ? off.getIconWidth() : 0, 144 (on != null) ? on.getIconWidth() : 0), 145 (inconsistent != null) ? inconsistent.getIconWidth() : 0 146 ); 147 } 148 149 /** 150 * Get current state of attached light 151 * 152 * @return A state variable from a Light, e.g. Turnout.CLOSED 153 */ 154 int lightState() { 155 if (light != null) { 156 return light.getState(); 157 } // This doesn't seem right. (Light.UNKNOWN = Light.ON = 0X01) 158 //else return Light.UNKNOWN; 159 else { 160 return Light.INCONSISTENT; 161 } 162 } 163 164 // update icon as state of light changes 165 @Override 166 public void propertyChange(java.beans.PropertyChangeEvent e) { 167 if (log.isDebugEnabled()) { 168 log.debug("property change: {} {} is now {}", getNameString(), e.getPropertyName(), e.getNewValue()); 169 } 170 171 if (e.getPropertyName().equals("KnownState")) { 172 int now = ((Integer) e.getNewValue()); 173 displayState(now); 174 } 175 } 176 177 @Override 178 public String getNameString() { 179 String name; 180 if (light == null) { 181 name = Bundle.getMessage("NotConnected"); 182 } else { 183 name = light.getDisplayName(DisplayOptions.USERNAME_SYSTEMNAME); 184 } 185 return name; 186 } 187 188 // 189 // ****** popup AbstractAction.actionPerformed method overrides ******** 190 // 191 @Override 192 protected void rotateOrthogonal() { 193 off.setRotation(on.getRotation() + 1, this); 194 on.setRotation(off.getRotation() + 1, this); 195 unknown.setRotation(unknown.getRotation() + 1, this); 196 inconsistent.setRotation(inconsistent.getRotation() + 1, this); 197 displayState(lightState()); 198 //bug fix, must repaint icons that have same width and height 199 repaint(); 200 } 201 202 @Override 203 public void setScale(double s) { 204 off.scale(s, this); 205 on.scale(s, this); 206 unknown.scale(s, this); 207 inconsistent.scale(s, this); 208 displayState(lightState()); 209 } 210 211 @Override 212 public void rotate(int deg) { 213 off.rotate(deg, this); 214 on.rotate(deg, this); 215 unknown.rotate(deg, this); 216 inconsistent.rotate(deg, this); 217 displayState(lightState()); 218 } 219 220 @Override 221 protected void edit() { 222 makeIconEditorFrame(this, "Light", true, null); 223 _iconEditor.setPickList(jmri.jmrit.picker.PickListModel.lightPickModelInstance()); 224 _iconEditor.setIcon(3, "StateOff", off); 225 _iconEditor.setIcon(2, "StateOn", on); 226 _iconEditor.setIcon(0, "BeanStateInconsistent", inconsistent); 227 _iconEditor.setIcon(1, "BeanStateUnknown", unknown); 228 _iconEditor.makeIconPanel(false); 229 230 ActionListener addIconAction = (ActionEvent a) -> updateLight(); 231 _iconEditor.complete(addIconAction, true, true, true); 232 _iconEditor.setSelection(light); 233 } 234 235 void updateLight() { 236 setOffIcon(_iconEditor.getIcon("StateOff")); 237 setOnIcon(_iconEditor.getIcon("StateOn")); 238 setUnknownIcon(_iconEditor.getIcon("BeanStateUnknown")); 239 setInconsistentIcon(_iconEditor.getIcon("BeanStateInconsistent")); 240 setLight((Light) _iconEditor.getTableSelection()); 241 _iconEditorFrame.dispose(); 242 _iconEditorFrame = null; 243 _iconEditor = null; 244 invalidate(); 245 } 246 247 // 248 // *********** end popup action methods *************** 249 // 250 /** 251 * Drive the current state of the display from the state of the light. 252 * 253 * @param state the new state 254 */ 255 void displayState(int state) { 256 log.debug("{} displayState {}", getNameString(), state); 257 updateSize(); 258 switch (state) { 259 case Light.OFF: 260 if (isText()) { 261 super.setText(InstanceManager.turnoutManagerInstance().getClosedText()); 262 } 263 if (isIcon()) { 264 super.setIcon(off); 265 } 266 break; 267 case Light.ON: 268 if (isText()) { 269 super.setText(InstanceManager.turnoutManagerInstance().getThrownText()); 270 } 271 if (isIcon()) { 272 super.setIcon(on); 273 } 274 break; 275 default: 276 if (isText()) { 277 super.setText(Bundle.getMessage("BeanStateInconsistent")); 278 } 279 if (isIcon()) { 280 super.setIcon(inconsistent); 281 } 282 break; 283 } 284 } 285 286 /** 287 * Change the light when the icon is clicked. 288 * 289 * @param e the mouse click 290 */ 291 @Override 292 public void doMouseClicked(JmriMouseEvent e) { 293 if (!_editor.getFlag(Editor.OPTION_CONTROLS, isControlling())) { 294 return; 295 } 296 if (e.isMetaDown() || e.isAltDown()) { 297 return; 298 } 299 if (light == null) { 300 log.error("No light connection, can't process click"); 301 return; 302 } 303 if (log.isDebugEnabled()) { 304 log.debug("doMouseClicked state= {}", light.getState()); 305 } 306 if (light.getState() == jmri.Light.OFF) { 307 light.setState(jmri.Light.ON); 308 } else { 309 light.setState(jmri.Light.OFF); 310 } 311 } 312 313 @Override 314 public void dispose() { 315 if (light != null) { 316 light.removePropertyChangeListener(this); 317 } 318 light = null; 319 320 off = null; 321 on = null; 322 inconsistent = null; 323 unknown = null; 324 325 super.dispose(); 326 } 327 328 private final static Logger log = LoggerFactory.getLogger(LightIcon.class); 329}