001package jmri.jmrit.display; 002 003import java.awt.Color; 004import java.awt.event.ActionEvent; 005import java.awt.event.ActionListener; 006import java.util.Collection; 007import java.util.HashMap; 008import java.util.Hashtable; 009import java.util.Map.Entry; 010 011import javax.annotation.Nonnull; 012import javax.swing.AbstractAction; 013import javax.swing.JCheckBoxMenuItem; 014import javax.swing.JComponent; 015import javax.swing.JMenu; 016import javax.swing.JMenuItem; 017import javax.swing.JPopupMenu; 018import javax.swing.Timer; 019 020import jmri.InstanceManager; 021import jmri.NamedBeanHandle; 022import jmri.Sensor; 023import jmri.NamedBean.DisplayOptions; 024import jmri.jmrit.catalog.NamedIcon; 025import jmri.jmrit.display.palette.TableItemPanel; 026import jmri.jmrit.picker.PickListModel; 027import jmri.util.swing.JmriColorChooser; 028import jmri.util.swing.JmriMouseEvent; 029 030import org.slf4j.Logger; 031import org.slf4j.LoggerFactory; 032 033/** 034 * An icon to display a status of a Sensor. 035 * 036 * @author Bob Jacobsen Copyright (C) 2001 037 * @author Pete Cressman Copyright (C) 2010, 2011 038 */ 039public class SensorIcon extends PositionableIcon implements java.beans.PropertyChangeListener { 040 041 static final public int UNKOWN_FONT_COLOR = 0x03; 042 static final public int UNKOWN_BACKGROUND_COLOR = 0x04; 043 static final public int ACTIVE_FONT_COLOR = 0x05; 044 static final public int ACTIVE_BACKGROUND_COLOR = 0x06; 045 static final public int INACTIVE_FONT_COLOR = 0x07; 046 static final public int INACTIVE_BACKGROUND_COLOR = 0x08; 047 static final public int INCONSISTENT_FONT_COLOR = 0x0A; 048 static final public int INCONSISTENT_BACKGROUND_COLOR = 0x0B; 049 050 protected HashMap<String, Integer> _name2stateMap; // name to state 051 protected HashMap<Integer, String> _state2nameMap; // state to name 052 053 public SensorIcon(Editor editor) { 054 // super ctor call to make sure this is an icon label 055 this(new NamedIcon("resources/icons/smallschematics/tracksegments/circuit-error.gif", 056 "resources/icons/smallschematics/tracksegments/circuit-error.gif"), editor); 057 } 058 059 public SensorIcon(NamedIcon s, Editor editor) { 060 // super ctor call to make sure this is an icon label 061 super(s, editor); 062 setOpaque(false); 063 _control = true; 064 setPopupUtility(new SensorPopupUtil(this, this)); 065 } 066 067 public SensorIcon(String s, Editor editor) { 068 super(s, editor); 069 _control = true; 070 originalText = s; 071 setPopupUtility(new SensorPopupUtil(this, this)); 072 displayState(sensorState()); 073 } 074 075 @Override 076 public Positionable deepClone() { 077 SensorIcon pos = new SensorIcon(_editor); 078 return finishClone(pos); 079 } 080 081 protected Positionable finishClone(SensorIcon pos) { 082 pos.setSensor(getNamedSensor().getName()); 083 pos.makeIconMap(); 084 pos._iconMap = cloneMap(_iconMap, pos); 085 pos.setMomentary(getMomentary()); 086 pos.originalText = originalText; 087 pos.setText(getText()); 088 pos.setIcon(null); 089 pos._namedIcon = null; 090 pos.activeText = activeText; 091 pos.inactiveText = inactiveText; 092 pos.inconsistentText = inconsistentText; 093 pos.unknownText = unknownText; 094 pos.textColorInconsistent = textColorInconsistent; 095 pos.textColorUnknown = textColorUnknown; 096 pos.textColorInActive = textColorInActive; 097 pos.textColorActive = textColorActive; 098 pos.backgroundColorInActive = backgroundColorInActive; 099 pos.backgroundColorActive = backgroundColorActive; 100 pos.backgroundColorUnknown = backgroundColorUnknown; 101 pos.backgroundColorInconsistent = backgroundColorInconsistent; 102 return super.finishClone(pos); 103 } 104 105 // the associated Sensor object 106 private NamedBeanHandle<Sensor> namedSensor; 107 108 /** 109 * Attached a named sensor to this display item 110 * 111 * @param pName System/user name to lookup the sensor object 112 */ 113 public void setSensor(String pName) { 114 if (InstanceManager.getNullableDefault(jmri.SensorManager.class) != null) { 115 try { 116 Sensor sensor = InstanceManager.sensorManagerInstance().provideSensor(pName); 117 setSensor(jmri.InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(pName, sensor)); 118 } catch (IllegalArgumentException ex) { 119 log.error("Sensor '{}' not available, icon won't see changes", pName); 120 } 121 } else { 122 log.error("No SensorManager for this protocol, icon won't see changes"); 123 } 124 } 125 126 /** 127 * Attached a named sensor to this display item 128 * 129 * @param s the Sensor 130 */ 131 public void setSensor(NamedBeanHandle<Sensor> s) { 132 if (namedSensor != null) { 133 getSensor().removePropertyChangeListener(this); 134 } 135 136 namedSensor = s; 137 if (namedSensor != null) { 138 if (_iconMap == null) { 139 makeIconMap(); 140 } 141 getSensor().addPropertyChangeListener(this, s.getName(), "SensorIcon on Panel " + _editor.getName()); 142 setName(namedSensor.getName()); // Swing name for e.g. tests 143 } 144 setAttributes(); 145 } 146 147 private void setAttributes() { 148 if (isText()) { 149 if (namedSensor != null) { 150 if (getSensor().getUserName() != null) { 151 String userName = getSensor().getUserName(); 152 if (activeText == null) { 153 activeText = userName; 154 //textColorActive=Color.red; 155 } 156 if (inactiveText == null) { 157 inactiveText = userName; 158 //textColorInActive=Color.yellow; 159 } 160 if (inconsistentText == null) { 161 inconsistentText = userName; 162 //textColorUnknown=Color.black; 163 } 164 if (unknownText == null) { 165 unknownText = userName; 166 //textColorInconsistent=Color.blue; 167 } 168 } 169 } 170 if (activeText == null) { 171 activeText = "<" + Bundle.getMessage("SensorStateActive").toLowerCase() + ">"; // initiate state label string 172 // created from Bundle as lower case, enclosed in < > 173 } 174 if (inactiveText == null) { 175 inactiveText = "<" + Bundle.getMessage("SensorStateInactive").toLowerCase() + ">"; 176 } 177 if (inconsistentText == null) { 178 inconsistentText = "<" + Bundle.getMessage("BeanStateInconsistent").toLowerCase() + ">"; 179 } 180 if (unknownText == null) { 181 unknownText = "<" + Bundle.getMessage("BeanStateUnknown").toLowerCase() + ">"; 182 } 183 if (textColorActive == null) { 184 textColorActive = Color.red; 185 } 186 if (textColorInActive == null) { 187 textColorInActive = Color.yellow; 188 } 189 if (textColorUnknown == null) { 190 textColorUnknown = Color.blue; 191 } 192 if (textColorInconsistent == null) { 193 textColorInconsistent = Color.black; 194 } 195 } else { 196 setOpaque(false); 197 } 198 displayState(sensorState()); 199 if (log.isDebugEnabled()) { // avoid String building unless needed 200 log.debug("setSensor: namedSensor= {} isIcon= {}, isText= {}, activeText= {}", 201 ((namedSensor == null) ? "null" : getNameString()), isIcon(), isText(), activeText); 202 } 203 repaint(); 204 } 205 206 public Sensor getSensor() { 207 if (namedSensor == null) { 208 return null; 209 } 210 return namedSensor.getBean(); 211 } 212 213 @Override 214 public jmri.NamedBean getNamedBean() { 215 return getSensor(); 216 } 217 218 public NamedBeanHandle<Sensor> getNamedSensor() { 219 return namedSensor; 220 } 221 222 void makeIconMap() { 223 _iconMap = new HashMap<>(); 224 _name2stateMap = new HashMap<>(); 225 _name2stateMap.put("BeanStateUnknown", Sensor.UNKNOWN); 226 _name2stateMap.put("BeanStateInconsistent", Sensor.INCONSISTENT); 227 _name2stateMap.put("SensorStateActive", Sensor.ACTIVE); 228 _name2stateMap.put("SensorStateInactive", Sensor.INACTIVE); 229 _state2nameMap = new HashMap<>(); 230 _state2nameMap.put(Sensor.UNKNOWN, "BeanStateUnknown"); 231 _state2nameMap.put(Sensor.INCONSISTENT, "BeanStateInconsistent"); 232 _state2nameMap.put(Sensor.ACTIVE, "SensorStateActive"); 233 _state2nameMap.put(Sensor.INACTIVE, "SensorStateInactive"); 234 } 235 236 @Override 237 public Collection<String> getStateNameCollection() { 238 return _state2nameMap.values(); 239 } 240 241 /** 242 * Place icon by its bean state name key found in the properties file 243 * jmri.NamedBeanBundle.properties by its localized bean state name. 244 * 245 * @param name the icon state name 246 * @param icon the icon to place 247 */ 248 public void setIcon(String name, NamedIcon icon) { 249 log.debug("setIcon for name \"{}\"", name); 250 if (_iconMap == null) { 251 makeIconMap(); 252 } 253 _iconMap.put(name, icon); 254 displayState(sensorState()); 255 } 256 257 /** 258 * Get icon by its localized bean state name. 259 * 260 * @param state the state to get the icon for 261 * @return the icon or null if state not found 262 */ 263 @Override 264 public NamedIcon getIcon(String state) { 265 return _iconMap.get(state); 266 } 267 268 public NamedIcon getIcon(int state) { 269 return _iconMap.get(_state2nameMap.get(state)); 270 } 271 272 @Override 273 public String getFamily() { 274 return _iconFamily; 275 } 276 277 @Override 278 public void setFamily(String family) { 279 _iconFamily = family; 280 } 281 282 /** 283 * Get current state of attached sensor 284 * 285 * @return A state variable from a Sensor, e.g. Sensor.ACTIVE 286 */ 287 int sensorState() { 288 if (namedSensor != null) { 289 return getSensor().getKnownState(); 290 } else { 291 return Sensor.UNKNOWN; 292 } 293 } 294 295 // update icon as state of turnout changes 296 @Override 297 public void propertyChange(java.beans.PropertyChangeEvent e) { 298 log.debug("property change: {}", e); 299 if (e.getPropertyName().equals("KnownState")) { 300 int now = ((Integer) e.getNewValue()); 301 displayState(now); 302 _editor.repaint(); 303 } 304 } 305 306 @Override 307 @Nonnull 308 public String getTypeString() { 309 return Bundle.getMessage("PositionableType_SensorIcon"); 310 } 311 312 @Override 313 @Nonnull 314 public String getNameString() { 315 String name; 316 if (namedSensor == null) { 317 name = Bundle.getMessage("NotConnected"); 318 } else { 319 name = getSensor().getDisplayName(DisplayOptions.USERNAME_SYSTEMNAME); 320 } 321 return name; 322 } 323 324 JCheckBoxMenuItem momentaryItem = new JCheckBoxMenuItem(Bundle.getMessage("Momentary")); 325 326 /** 327 * Pop-up just displays the sensor name. 328 * 329 * @param popup the menu to display 330 * @return always true 331 */ 332 @Override 333 public boolean showPopUp(JPopupMenu popup) { 334 if (isEditable()) { 335 if (isIcon()) { 336 popup.add(new AbstractAction(Bundle.getMessage("ChangeToText")) { 337 @Override 338 public void actionPerformed(ActionEvent e) { 339 changeLayoutSensorType(); 340 } 341 }); 342 } else { 343 popup.add(new AbstractAction(Bundle.getMessage("ChangeToIcon")) { 344 @Override 345 public void actionPerformed(ActionEvent e) { 346 changeLayoutSensorType(); 347 } 348 }); 349 } 350 351 popup.add(momentaryItem); 352 momentaryItem.setSelected(getMomentary()); 353 momentaryItem.addActionListener((java.awt.event.ActionEvent e) -> setMomentary(momentaryItem.isSelected())); 354 } else if (getPopupUtility() != null) { 355 getPopupUtility().setAdditionalViewPopUpMenu(popup); 356 } 357 return true; 358 } 359 360 //////// popup AbstractAction.actionPerformed method overrides //////// 361 362 @Override 363 public boolean setTextEditMenu(JPopupMenu popup) { 364 log.debug("setTextEditMenu isIcon={}, isText={}", isIcon(), isText()); 365 if (isIcon()) { 366 popup.add(CoordinateEdit.getTextEditAction(this, "OverlayText")); 367 } else { 368 popup.add(new AbstractAction(Bundle.getMessage("SetSensorText")) { 369 @Override 370 public void actionPerformed(ActionEvent e) { 371 String name = getNameString(); 372 sensorTextEdit(name); 373 } 374 }); 375 if (isText() && !isIcon()) { 376 JMenu stateColor = new JMenu(Bundle.getMessage("StateColors")); 377 stateColor.add(stateMenu(Bundle.getMessage("BeanStateUnknown"), UNKOWN_FONT_COLOR)); //Unknown 378 stateColor.add(stateMenu(Bundle.getMessage("SensorStateActive"), ACTIVE_FONT_COLOR)); //Active 379 stateColor.add(stateMenu(Bundle.getMessage("SensorStateInactive"), INACTIVE_FONT_COLOR)); //Inactive 380 stateColor.add(stateMenu(Bundle.getMessage("BeanStateInconsistent"), INCONSISTENT_FONT_COLOR)); //Inconsistent 381 popup.add(stateColor); 382 } 383 } 384 return true; 385 } 386 387 public void sensorTextEdit(String name) { 388 log.debug("make text edit menu"); 389 390 SensorTextEdit f = new SensorTextEdit(); 391 f.addHelpMenu("package.jmri.jmrit.display.SensorTextEdit", true); 392 try { 393 f.initComponents(this, name); 394 } catch (Exception ex) { 395 log.error("Exception: {}", ex.toString()); 396 } 397 f.setVisible(true); 398 } 399 400 /** 401 * Drive the current state of the display from the state of the sensor. 402 * 403 * @param state the sensor state 404 */ 405 @Override 406 public void displayState(int state) { 407 if (getNamedSensor() == null) { 408 log.debug("Display state {}, disconnected", state); 409 } else if (isIcon()) { 410 NamedIcon icon = getIcon(state); 411 if (icon != null) { 412 super.setIcon(icon); 413 } 414 } else if (isText()) { 415 switch (state) { 416 case Sensor.UNKNOWN: 417 super.setText(unknownText); 418 getPopupUtility().setBackgroundColor(backgroundColorUnknown); 419 getPopupUtility().setForeground(textColorUnknown); 420 break; 421 case Sensor.ACTIVE: 422 super.setText(activeText); 423 getPopupUtility().setBackgroundColor(backgroundColorActive); 424 getPopupUtility().setForeground(textColorActive); 425 break; 426 case Sensor.INACTIVE: 427 super.setText(inactiveText); 428 getPopupUtility().setBackgroundColor(backgroundColorInActive); 429 getPopupUtility().setForeground(textColorInActive); 430 break; 431 default: 432 super.setText(inconsistentText); 433 getPopupUtility().setBackgroundColor(backgroundColorInconsistent); 434 getPopupUtility().setForeground(textColorInconsistent); 435 break; 436 } 437 } 438 int deg = getDegrees(); 439 rotate(deg); 440 if (deg == 0) { 441 setOpaque(getPopupUtility().hasBackground()); 442 } 443 444 updateSize(); 445 } 446 447 TableItemPanel<Sensor> _itemPanel; 448 449 @Override 450 public boolean setEditItemMenu(JPopupMenu popup) { 451 String txt = java.text.MessageFormat.format(Bundle.getMessage("EditItem"), Bundle.getMessage("BeanNameSensor")); 452 popup.add(new AbstractAction(txt) { 453 @Override 454 public void actionPerformed(ActionEvent e) { 455 editItem(); 456 } 457 }); 458 return true; 459 } 460 461 protected void editItem() { 462 _paletteFrame = makePaletteFrame(java.text.MessageFormat.format(Bundle.getMessage("EditItem"), Bundle.getMessage("BeanNameSensor"))); 463 _itemPanel = new TableItemPanel<>(_paletteFrame, "Sensor", _iconFamily, 464 PickListModel.sensorPickModelInstance()); // NOI18N 465 ActionListener updateAction = (ActionEvent a) -> updateItem(); 466 // duplicate _iconMap map with unscaled and unrotated icons 467 HashMap<String, NamedIcon> map = new HashMap<>(); 468 for (Entry<String, NamedIcon> entry : _iconMap.entrySet()) { 469 NamedIcon oldIcon = entry.getValue(); 470 NamedIcon newIcon = cloneIcon(oldIcon, this); 471 newIcon.rotate(0, this); 472 newIcon.scale(1.0, this); 473 newIcon.setRotation(4, this); 474 map.put(entry.getKey(), newIcon); 475 } 476 _itemPanel.init(updateAction, map); 477 _itemPanel.setSelection(getSensor()); 478 initPaletteFrame(_paletteFrame, _itemPanel); 479 } 480 481 void updateItem() { 482 HashMap<String, NamedIcon> oldMap = cloneMap(_iconMap, this); 483 setSensor(_itemPanel.getTableSelection().getSystemName()); 484 _iconFamily = _itemPanel.getFamilyName(); 485 HashMap<String, NamedIcon> iconMap = _itemPanel.getIconMap(); 486 if (iconMap != null) { 487 for (Entry<String, NamedIcon> entry : iconMap.entrySet()) { 488 if (log.isDebugEnabled()) { 489 log.debug("key= {}", entry.getKey()); 490 } 491 NamedIcon newIcon = entry.getValue(); 492 NamedIcon oldIcon = oldMap.get(entry.getKey()); 493 newIcon.setLoad(oldIcon.getDegrees(), oldIcon.getScale(), this); 494 newIcon.setRotation(oldIcon.getRotation(), this); 495 setIcon(entry.getKey(), newIcon); 496 } 497 } // otherwise retain current map 498 finishItemUpdate(_paletteFrame, _itemPanel); 499 } 500 501 @Override 502 public boolean setEditIconMenu(JPopupMenu popup) { 503 String txt = java.text.MessageFormat.format(Bundle.getMessage("EditItem"), Bundle.getMessage("BeanNameSensor")); 504 popup.add(new AbstractAction(txt) { 505 @Override 506 public void actionPerformed(ActionEvent e) { 507 edit(); 508 } 509 }); 510 return true; 511 } 512 513 @Override 514 protected void edit() { 515 makeIconEditorFrame(this, "Sensor", true, null); 516 _iconEditor.setPickList(jmri.jmrit.picker.PickListModel.sensorPickModelInstance()); 517 int i = 0; 518 for (Entry<String, NamedIcon> entry : _iconMap.entrySet()) { 519 _iconEditor.setIcon(i++, entry.getKey(), entry.getValue()); 520 } 521 _iconEditor.makeIconPanel(false); 522 523 // set default icons, then override with this turnout's icons 524 ActionListener addIconAction = (ActionEvent a) -> updateSensor(); 525 _iconEditor.complete(addIconAction, true, true, true); 526 _iconEditor.setSelection(getSensor()); 527 } 528 529 void updateSensor() { 530 HashMap<String, NamedIcon> oldMap = cloneMap(_iconMap, this); 531 setSensor(_iconEditor.getTableSelection().getDisplayName()); 532 Hashtable<String, NamedIcon> iconMap = _iconEditor.getIconMap(); 533 534 for (Entry<String, NamedIcon> entry : iconMap.entrySet()) { 535 log.debug("key= {}", entry.getKey()); 536 NamedIcon newIcon = entry.getValue(); 537 NamedIcon oldIcon = oldMap.get(entry.getKey()); 538 newIcon.setLoad(oldIcon.getDegrees(), oldIcon.getScale(), this); 539 newIcon.setRotation(oldIcon.getRotation(), this); 540 setIcon(entry.getKey(), newIcon); 541 } 542 _iconEditorFrame.dispose(); 543 _iconEditorFrame = null; 544 _iconEditor = null; 545 invalidate(); 546 } 547 548 // Original text is used when changing between icon and text, this allows for a undo when reverting back. 549 String originalText; 550 551 public void setOriginalText(String s) { 552 originalText = s; 553 displayState(sensorState()); 554 } 555 556 public String getOriginalText() { 557 return originalText; 558 } 559 560 @Override 561 public void setText(String s) { 562 setOpaque(false); 563 if (super._rotateText && !_icon) { 564 return; 565 } 566 _text = (s != null && s.length() > 0); 567 super.setText(s); 568 updateSize(); 569 } 570 571 boolean momentary = false; 572 573 public boolean getMomentary() { 574 return momentary; 575 } 576 577 public void setMomentary(boolean m) { 578 momentary = m; 579 } 580 581 public boolean buttonLive() { 582 if (namedSensor == null) { // no sensor connected for this protocol 583 log.error("No sensor connection, can't process click"); 584 return false; 585 } 586 return _editor.getFlag(Editor.OPTION_CONTROLS, isControlling()); 587 } 588 589 @Override 590 public void doMousePressed(JmriMouseEvent e) { 591 log.debug("doMousePressed buttonLive={}, getMomentary={}", buttonLive(), getMomentary()); 592 if (getMomentary() && buttonLive() && !e.isMetaDown() && !e.isAltDown()) { 593 // this is a momentary button press 594 try { 595 getSensor().setKnownState(jmri.Sensor.ACTIVE); 596 } catch (jmri.JmriException reason) { 597 log.warn("Exception setting momentary sensor", reason); 598 } 599 } 600 super.doMousePressed(e); 601 } 602 603 @Override 604 public void doMouseReleased(JmriMouseEvent e) { 605 if (getMomentary() && buttonLive() && !e.isMetaDown() && !e.isAltDown()) { 606 // this is a momentary button release 607 try { 608 getSensor().setKnownState(jmri.Sensor.INACTIVE); 609 } catch (jmri.JmriException reason) { 610 log.warn("Exception setting momentary sensor", reason); 611 } 612 } 613 super.doMouseReleased(e); 614 } 615 616 @Override 617 public void doMouseClicked(JmriMouseEvent e) { 618 if (buttonLive() && !getMomentary()) { 619 // this button responds to clicks 620 if (!e.isMetaDown() && !e.isAltDown()) { 621 try { 622 if (getSensor().getKnownState() == jmri.Sensor.INACTIVE) { 623 getSensor().setKnownState(jmri.Sensor.ACTIVE); 624 } else { 625 getSensor().setKnownState(jmri.Sensor.INACTIVE); 626 } 627 } catch (jmri.JmriException reason) { 628 log.warn("Exception flipping sensor", reason); 629 } 630 } 631 } 632 super.doMouseClicked(e); 633 } 634 635 @Override 636 public void dispose() { 637 if (namedSensor != null) { 638 getSensor().removePropertyChangeListener(this); 639 } 640 namedSensor = null; 641 _iconMap = null; 642 _name2stateMap = null; 643 _state2nameMap = null; 644 645 super.dispose(); 646 } 647 648 protected HashMap<Integer, NamedIcon> cloneMap(HashMap<Integer, NamedIcon> map, 649 SensorIcon pos) { 650 HashMap<Integer, NamedIcon> clone = new HashMap<>(); 651 if (map != null) { 652 for (Entry<Integer, NamedIcon> entry : map.entrySet()) { 653 clone.put(entry.getKey(), cloneIcon(entry.getValue(), pos)); 654 if (pos != null) { 655 pos.setIcon(pos._state2nameMap.get(entry.getKey()), _iconMap.get(entry.getKey().toString())); 656 } 657 } 658 } 659 return clone; 660 } 661 // The code below here is from the layoutsensoricon. 662 663 Color textColorActive = Color.red; 664 665 public void setTextActive(Color color) { 666 textColorActive = color; 667 displayState(sensorState()); 668 JmriColorChooser.addRecentColor(color); 669 } 670 671 public Color getTextActive() { 672 return textColorActive; 673 } 674 675 Color textColorInActive = Color.yellow; 676 677 public void setTextInActive(Color color) { 678 textColorInActive = color; 679 displayState(sensorState()); 680 JmriColorChooser.addRecentColor(color); 681 } 682 683 public Color getTextInActive() { 684 return textColorInActive; 685 } 686 687 Color textColorUnknown = Color.blue; 688 689 public void setTextUnknown(Color color) { 690 textColorUnknown = color; 691 displayState(sensorState()); 692 JmriColorChooser.addRecentColor(color); 693 } 694 695 public Color getTextUnknown() { 696 return textColorUnknown; 697 } 698 699 Color textColorInconsistent = Color.black; 700 701 public void setTextInconsistent(Color color) { 702 textColorInconsistent = color; 703 displayState(sensorState()); 704 JmriColorChooser.addRecentColor(color); 705 } 706 707 public Color getTextInconsistent() { 708 return textColorInconsistent; 709 } 710 711 Color backgroundColorActive = null; 712 713 public void setBackgroundActive(Color color) { 714 backgroundColorActive = color; 715 displayState(sensorState()); 716 JmriColorChooser.addRecentColor(color); 717 } 718 719 public Color getBackgroundActive() { 720 return backgroundColorActive; 721 } 722 723 Color backgroundColorInActive = null; 724 725 public void setBackgroundInActive(Color color) { 726 backgroundColorInActive = color; 727 displayState(sensorState()); 728 JmriColorChooser.addRecentColor(color); 729 } 730 731 public Color getBackgroundInActive() { 732 return backgroundColorInActive; 733 } 734 735 Color backgroundColorUnknown = null; 736 737 public void setBackgroundUnknown(Color color) { 738 backgroundColorUnknown = color; 739 displayState(sensorState()); 740 JmriColorChooser.addRecentColor(color); 741 } 742 743 public Color getBackgroundUnknown() { 744 return backgroundColorUnknown; 745 } 746 747 Color backgroundColorInconsistent = null; 748 749 public void setBackgroundInconsistent(Color color) { 750 backgroundColorInconsistent = color; 751 displayState(sensorState()); 752 JmriColorChooser.addRecentColor(color); 753 } 754 755 public Color getBackgroundInconsistent() { 756 return backgroundColorInconsistent; 757 } 758 759 private String activeText; 760 private String inactiveText; 761 private String inconsistentText; 762 private String unknownText; 763 764 public String getActiveText() { 765 return activeText; 766 } 767 768 public void setActiveText(String i) { 769 activeText = i; 770 displayState(sensorState()); 771 } 772 773 public String getInactiveText() { 774 return inactiveText; 775 } 776 777 public void setInactiveText(String i) { 778 inactiveText = i; 779 displayState(sensorState()); 780 } 781 782 public String getInconsistentText() { 783 return inconsistentText; 784 } 785 786 public void setInconsistentText(String i) { 787 inconsistentText = i; 788 displayState(sensorState()); 789 } 790 791 public String getUnknownText() { 792 return unknownText; 793 } 794 795 public void setUnknownText(String i) { 796 unknownText = i; 797 displayState(sensorState()); 798 } 799 800 JMenu stateMenu(final String name, int state) { 801 JMenu menu = new JMenu(name); 802 JMenuItem colorMenu = new JMenuItem(Bundle.getMessage("FontColor")); 803 colorMenu.addActionListener((ActionEvent event) -> { 804 Color desiredColor = JmriColorChooser.showDialog(this, 805 Bundle.getMessage("FontColor"), 806 getColor(state)); 807 if (desiredColor!=null ) { 808 setColor(desiredColor,state); 809 } 810 }); 811 menu.add(colorMenu); 812 colorMenu = new JMenuItem(Bundle.getMessage("FontBackgroundColor")); 813 colorMenu.addActionListener((ActionEvent event) -> { 814 Color desiredColor = JmriColorChooser.showDialog(this, 815 Bundle.getMessage("FontBackgroundColor"), 816 getColor(state+1)); 817 if (desiredColor!=null ) { 818 setColor(desiredColor,state+1); 819 } 820 }); 821 menu.add(colorMenu); 822 return menu; 823 } 824 825 private void setColor(Color desiredColor, int state) { 826 PositionablePopupUtil pop = getPopupUtility(); 827 if (pop instanceof SensorPopupUtil) { 828 SensorPopupUtil util = (SensorPopupUtil) pop; 829 switch (state) { 830 case PositionablePopupUtil.FONT_COLOR: 831 util.setForeground(desiredColor); 832 break; 833 case PositionablePopupUtil.BACKGROUND_COLOR: 834 util.setBackgroundColor(desiredColor); 835 break; 836 case PositionablePopupUtil.BORDER_COLOR: 837 util.setBorderColor(desiredColor); 838 break; 839 case UNKOWN_FONT_COLOR: 840 setTextUnknown(desiredColor); 841 break; 842 case UNKOWN_BACKGROUND_COLOR: 843 util.setHasBackground(desiredColor != null); 844 setBackgroundUnknown(desiredColor); 845 break; 846 case ACTIVE_FONT_COLOR: 847 setTextActive(desiredColor); 848 break; 849 case ACTIVE_BACKGROUND_COLOR: 850 util.setHasBackground(desiredColor != null); 851 setBackgroundActive(desiredColor); 852 break; 853 case INACTIVE_FONT_COLOR: 854 setTextInActive(desiredColor); 855 break; 856 case INACTIVE_BACKGROUND_COLOR: 857 util.setHasBackground(desiredColor != null); 858 setBackgroundInActive(desiredColor); 859 break; 860 case INCONSISTENT_FONT_COLOR: 861 setTextInconsistent(desiredColor); 862 break; 863 case INCONSISTENT_BACKGROUND_COLOR: 864 util.setHasBackground(desiredColor != null); 865 setBackgroundInconsistent(desiredColor); 866 break; 867 default: 868 break; 869 } 870 } 871 } 872 873 private Color getColor(int state){ 874 PositionablePopupUtil pop = getPopupUtility(); 875 if (pop instanceof SensorPopupUtil) { 876 SensorPopupUtil util = (SensorPopupUtil) pop; 877 switch (state) { 878 case PositionablePopupUtil.FONT_COLOR: 879 return util.getForeground(); 880 case PositionablePopupUtil.BACKGROUND_COLOR: 881 return util.getBackground(); 882 case PositionablePopupUtil.BORDER_COLOR: 883 return util.getBorderColor(); 884 case UNKOWN_FONT_COLOR: 885 return getTextUnknown(); 886 case UNKOWN_BACKGROUND_COLOR: 887 return getBackgroundUnknown(); 888 case ACTIVE_FONT_COLOR: 889 return getTextActive(); 890 case ACTIVE_BACKGROUND_COLOR: 891 return getBackgroundActive(); 892 case INACTIVE_FONT_COLOR: 893 return getTextInActive(); 894 case INACTIVE_BACKGROUND_COLOR: 895 return getBackgroundInActive(); 896 case INCONSISTENT_FONT_COLOR: 897 return getTextInconsistent(); 898 case INCONSISTENT_BACKGROUND_COLOR: 899 return getBackgroundInconsistent(); 900 default: 901 return null; 902 } 903 } 904 return null; 905 } 906 907 void changeLayoutSensorType() { 908// NamedBeanHandle <Sensor> handle = getNamedSensor(); 909 if (isIcon()) { 910 _icon = false; 911 _text = true; 912 setIcon(null); 913// setOriginalText(getUnRotatedText()); 914 setSuperText(null); 915 setOpaque(true); 916 } else if (isText()) { 917 _icon = true; 918 _text = (originalText != null && originalText.length() > 0); 919 setUnRotatedText(getOriginalText()); 920 setOpaque(false); 921 } 922 _namedIcon = null; 923 setAttributes(); 924 displayState(sensorState()); 925// setSensor(handle); 926 int deg = getDegrees(); 927 rotate(deg); 928 if (deg != 0 && _text && !_icon) { 929 setSuperText(null); 930 } 931 } 932 933 private int flashStateOn = -1; 934 private int flashStateOff = -1; 935 private boolean flashon = false; 936 private ActionListener taskPerformer; 937 private Timer flashTimer; 938 939 synchronized public void flashSensor(int tps, int state1, int state2) { 940 if ((flashTimer != null) && flashTimer.isRunning()) { 941 return; 942 } 943 //Set the maximum number of state changes to 10 per second 944 if (tps > 10) { 945 tps = 10; 946 } else if (tps <= 0) { 947 return; 948 } 949 if ((_state2nameMap.get(state1) == null) || _state2nameMap.get(state2) == null) { 950 log.error("one or other of the states passed for flash is null"); 951 return; 952 } else if (state1 == state2) { 953 log.debug("Both states to flash between are the same, therefore no flashing will occur"); 954 return; 955 } 956 int interval = (1000 / tps) / 2; 957 flashStateOn = state1; 958 flashStateOff = state2; 959 if (taskPerformer == null) { 960 taskPerformer = (ActionEvent evt) -> { 961 if (flashon) { 962 flashon = false; 963 displayState(flashStateOn); 964 } else { 965 flashon = true; 966 displayState(flashStateOff); 967 } 968 }; 969 } 970 flashTimer = new Timer(interval, taskPerformer); 971 flashTimer.start(); 972 } 973 974 synchronized public void stopFlash() { 975 if (flashTimer != null) { 976 flashTimer.stop(); 977 } 978 displayState(sensorState()); 979 } 980 981 class SensorPopupUtil extends PositionablePopupUtil { 982 983 SensorPopupUtil(Positionable parent, javax.swing.JComponent textComp) { 984 super(parent, textComp); 985 } 986 987 @Override 988 public SensorPopupUtil clone(Positionable parent, JComponent textComp) { 989 SensorPopupUtil util = new SensorPopupUtil(parent, textComp); 990 util.setJustification(getJustification()); 991 util.setHorizontalAlignment(getJustification()); 992 util.setFixedWidth(getFixedWidth()); 993 util.setFixedHeight(getFixedHeight()); 994 util.setMargin(getMargin()); 995 util.setBorderSize(getBorderSize()); 996 util.setBorderColor(getBorderColor()); 997 util.setFont(util.getFont().deriveFont(getFontStyle())); 998 util.setFontSize(getFontSize()); 999 util.setOrientation(getOrientation()); 1000 util.setBackgroundColor(getBackground()); 1001 util.setForeground(getForeground()); 1002 util.setHasBackground(hasBackground()); // must do this AFTER setBackgroundColor 1003 return util; 1004 } 1005 1006 @Override 1007 public void setTextJustificationMenu(JPopupMenu popup) { 1008 if (isText()) { 1009 super.setTextJustificationMenu(popup); 1010 } 1011 } 1012 1013 @Override 1014 public void setTextOrientationMenu(JPopupMenu popup) { 1015 if (isText()) { 1016 super.setTextOrientationMenu(popup); 1017 } 1018 } 1019 1020 @Override 1021 public void setFixedTextMenu(JPopupMenu popup) { 1022 if (isText()) { 1023 super.setFixedTextMenu(popup); 1024 } 1025 } 1026 1027 @Override 1028 public void setTextMarginMenu(JPopupMenu popup) { 1029 if (isText()) { 1030 super.setTextMarginMenu(popup); 1031 } 1032 } 1033 1034 @Override 1035 public void setTextBorderMenu(JPopupMenu popup) { 1036 if (isText()) { 1037 super.setTextBorderMenu(popup); 1038 } 1039 } 1040 1041 @Override 1042 public void setTextFontMenu(JPopupMenu popup) { 1043 if (isText()) { 1044 super.setTextFontMenu(popup); 1045 } 1046 } 1047 1048 @Override 1049 public void setBackgroundMenu(JPopupMenu popup) { 1050 if (isIcon()) { 1051 super.setBackgroundMenu(popup); 1052 } 1053 } 1054 } 1055 1056 private final static Logger log = LoggerFactory.getLogger(SensorIcon.class); 1057 1058}