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