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 // display icons 088 String offLName = "resources/icons/smallschematics/lights/cross-on.png"; 089 NamedIcon off = new NamedIcon(offLName, offLName); 090 String onLName = "resources/icons/smallschematics/lights/cross-off.png"; 091 NamedIcon on = new NamedIcon(onLName, onLName); 092 String inconsistentLName = "resources/icons/smallschematics/lights/cross-inconsistent.png"; 093 NamedIcon inconsistent = new NamedIcon(inconsistentLName, inconsistentLName); 094 String unknownLName = "resources/icons/smallschematics/lights/cross-unknown.png"; 095 NamedIcon unknown = new NamedIcon(unknownLName, unknownLName); 096 097 public NamedIcon getOffIcon() { 098 return off; 099 } 100 101 public void setOffIcon(NamedIcon i) { 102 off = i; 103 displayState(lightState()); 104 } 105 106 public NamedIcon getOnIcon() { 107 return on; 108 } 109 110 public void setOnIcon(NamedIcon i) { 111 on = i; 112 displayState(lightState()); 113 } 114 115 public NamedIcon getInconsistentIcon() { 116 return inconsistent; 117 } 118 119 public void setInconsistentIcon(NamedIcon i) { 120 inconsistent = i; 121 displayState(lightState()); 122 } 123 124 public NamedIcon getUnknownIcon() { 125 return unknown; 126 } 127 128 public void setUnknownIcon(NamedIcon i) { 129 unknown = i; 130 displayState(lightState()); 131 } 132 133 @Override 134 public int maxHeight() { 135 return Math.max( 136 Math.max((off != null) ? off.getIconHeight() : 0, 137 (on != null) ? on.getIconHeight() : 0), 138 (inconsistent != null) ? inconsistent.getIconHeight() : 0 139 ); 140 } 141 142 @Override 143 public int maxWidth() { 144 return Math.max( 145 Math.max((off != null) ? off.getIconWidth() : 0, 146 (on != null) ? on.getIconWidth() : 0), 147 (inconsistent != null) ? inconsistent.getIconWidth() : 0 148 ); 149 } 150 151 /** 152 * Get current state of attached light 153 * 154 * @return A state variable from a Light, e.g. Turnout.CLOSED 155 */ 156 int lightState() { 157 if (light != null) { 158 return light.getState(); 159 } // This doesn't seem right. (Light.UNKNOWN = Light.ON = 0X01) 160 //else return Light.UNKNOWN; 161 else { 162 return Light.INCONSISTENT; 163 } 164 } 165 166 // update icon as state of light changes 167 @Override 168 public void propertyChange(java.beans.PropertyChangeEvent e) { 169 if (log.isDebugEnabled()) { 170 log.debug("property change: {} {} is now {}", getNameString(), e.getPropertyName(), e.getNewValue()); 171 } 172 173 if (e.getPropertyName().equals("KnownState")) { 174 int now = ((Integer) e.getNewValue()); 175 displayState(now); 176 } 177 } 178 179 @Override 180 @Nonnull 181 public String getTypeString() { 182 return Bundle.getMessage("PositionableType_LightIcon"); 183 } 184 185 @Override 186 public String getNameString() { 187 String name; 188 if (light == null) { 189 name = Bundle.getMessage("NotConnected"); 190 } else { 191 name = light.getDisplayName(DisplayOptions.USERNAME_SYSTEMNAME); 192 } 193 return name; 194 } 195 196 // 197 // ****** popup AbstractAction.actionPerformed method overrides ******** 198 // 199 @Override 200 protected void rotateOrthogonal() { 201 off.setRotation(on.getRotation() + 1, this); 202 on.setRotation(off.getRotation() + 1, this); 203 unknown.setRotation(unknown.getRotation() + 1, this); 204 inconsistent.setRotation(inconsistent.getRotation() + 1, this); 205 displayState(lightState()); 206 //bug fix, must repaint icons that have same width and height 207 repaint(); 208 } 209 210 @Override 211 public void setScale(double s) { 212 off.scale(s, this); 213 on.scale(s, this); 214 unknown.scale(s, this); 215 inconsistent.scale(s, this); 216 displayState(lightState()); 217 } 218 219 @Override 220 public void rotate(int deg) { 221 off.rotate(deg, this); 222 on.rotate(deg, this); 223 unknown.rotate(deg, this); 224 inconsistent.rotate(deg, this); 225 displayState(lightState()); 226 } 227 228 @Override 229 protected void edit() { 230 makeIconEditorFrame(this, "Light", true, null); 231 _iconEditor.setPickList(jmri.jmrit.picker.PickListModel.lightPickModelInstance()); 232 _iconEditor.setIcon(3, "StateOff", off); 233 _iconEditor.setIcon(2, "StateOn", on); 234 _iconEditor.setIcon(0, "BeanStateInconsistent", inconsistent); 235 _iconEditor.setIcon(1, "BeanStateUnknown", unknown); 236 _iconEditor.makeIconPanel(false); 237 238 ActionListener addIconAction = (ActionEvent a) -> updateLight(); 239 _iconEditor.complete(addIconAction, true, true, true); 240 _iconEditor.setSelection(light); 241 } 242 243 void updateLight() { 244 setOffIcon(_iconEditor.getIcon("StateOff")); 245 setOnIcon(_iconEditor.getIcon("StateOn")); 246 setUnknownIcon(_iconEditor.getIcon("BeanStateUnknown")); 247 setInconsistentIcon(_iconEditor.getIcon("BeanStateInconsistent")); 248 setLight((Light) _iconEditor.getTableSelection()); 249 _iconEditorFrame.dispose(); 250 _iconEditorFrame = null; 251 _iconEditor = null; 252 invalidate(); 253 } 254 255 // 256 // *********** end popup action methods *************** 257 // 258 /** 259 * Drive the current state of the display from the state of the light. 260 * 261 * @param state the new state 262 */ 263 void displayState(int state) { 264 log.debug("{} displayState {}", getNameString(), state); 265 updateSize(); 266 switch (state) { 267 case Light.OFF: 268 if (isText()) { 269 super.setText(InstanceManager.turnoutManagerInstance().getClosedText()); 270 } 271 if (isIcon()) { 272 super.setIcon(off); 273 } 274 break; 275 case Light.ON: 276 if (isText()) { 277 super.setText(InstanceManager.turnoutManagerInstance().getThrownText()); 278 } 279 if (isIcon()) { 280 super.setIcon(on); 281 } 282 break; 283 default: 284 if (isText()) { 285 super.setText(Bundle.getMessage("BeanStateInconsistent")); 286 } 287 if (isIcon()) { 288 super.setIcon(inconsistent); 289 } 290 break; 291 } 292 } 293 294 /** 295 * Change the light when the icon is clicked. 296 * 297 * @param e the mouse click 298 */ 299 @Override 300 public void doMouseClicked(JmriMouseEvent e) { 301 if (!_editor.getFlag(Editor.OPTION_CONTROLS, isControlling())) { 302 return; 303 } 304 if (e.isMetaDown() || e.isAltDown()) { 305 return; 306 } 307 if (light == null) { 308 log.error("No light connection, can't process click"); 309 return; 310 } 311 if (log.isDebugEnabled()) { 312 log.debug("doMouseClicked state= {}", light.getState()); 313 } 314 if (light.getState() == jmri.Light.OFF) { 315 light.setState(jmri.Light.ON); 316 } else { 317 light.setState(jmri.Light.OFF); 318 } 319 } 320 321 @Override 322 public void dispose() { 323 if (light != null) { 324 light.removePropertyChangeListener(this); 325 } 326 light = null; 327 328 off = null; 329 on = null; 330 inconsistent = null; 331 unknown = null; 332 333 super.dispose(); 334 } 335 336 private final static Logger log = LoggerFactory.getLogger(LightIcon.class); 337}