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 getNameString() { 309 String name; 310 if (namedSensor == null) { 311 name = Bundle.getMessage("NotConnected"); 312 } else { 313 name = getSensor().getDisplayName(DisplayOptions.USERNAME_SYSTEMNAME); 314 } 315 return name; 316 } 317 318 JCheckBoxMenuItem momentaryItem = new JCheckBoxMenuItem(Bundle.getMessage("Momentary")); 319 320 /** 321 * Pop-up just displays the sensor name. 322 * 323 * @param popup the menu to display 324 * @return always true 325 */ 326 @Override 327 public boolean showPopUp(JPopupMenu popup) { 328 if (isEditable()) { 329 if (isIcon()) { 330 popup.add(new AbstractAction(Bundle.getMessage("ChangeToText")) { 331 @Override 332 public void actionPerformed(ActionEvent e) { 333 changeLayoutSensorType(); 334 } 335 }); 336 } else { 337 popup.add(new AbstractAction(Bundle.getMessage("ChangeToIcon")) { 338 @Override 339 public void actionPerformed(ActionEvent e) { 340 changeLayoutSensorType(); 341 } 342 }); 343 } 344 345 popup.add(momentaryItem); 346 momentaryItem.setSelected(getMomentary()); 347 momentaryItem.addActionListener((java.awt.event.ActionEvent e) -> setMomentary(momentaryItem.isSelected())); 348 } else if (getPopupUtility() != null) { 349 getPopupUtility().setAdditionalViewPopUpMenu(popup); 350 } 351 return true; 352 } 353 354 //////// popup AbstractAction.actionPerformed method overrides //////// 355 356 @Override 357 public boolean setTextEditMenu(JPopupMenu popup) { 358 log.debug("setTextEditMenu isIcon={}, isText={}", isIcon(), isText()); 359 if (isIcon()) { 360 popup.add(CoordinateEdit.getTextEditAction(this, "OverlayText")); 361 } else { 362 popup.add(new AbstractAction(Bundle.getMessage("SetSensorText")) { 363 @Override 364 public void actionPerformed(ActionEvent e) { 365 String name = getNameString(); 366 sensorTextEdit(name); 367 } 368 }); 369 if (isText() && !isIcon()) { 370 JMenu stateColor = new JMenu(Bundle.getMessage("StateColors")); 371 stateColor.add(stateMenu(Bundle.getMessage("BeanStateUnknown"), UNKOWN_FONT_COLOR)); //Unknown 372 stateColor.add(stateMenu(Bundle.getMessage("SensorStateActive"), ACTIVE_FONT_COLOR)); //Active 373 stateColor.add(stateMenu(Bundle.getMessage("SensorStateInactive"), INACTIVE_FONT_COLOR)); //Inactive 374 stateColor.add(stateMenu(Bundle.getMessage("BeanStateInconsistent"), INCONSISTENT_FONT_COLOR)); //Inconsistent 375 popup.add(stateColor); 376 } 377 } 378 return true; 379 } 380 381 public void sensorTextEdit(String name) { 382 log.debug("make text edit menu"); 383 384 SensorTextEdit f = new SensorTextEdit(); 385 f.addHelpMenu("package.jmri.jmrit.display.SensorTextEdit", true); 386 try { 387 f.initComponents(this, name); 388 } catch (Exception ex) { 389 log.error("Exception: {}", ex.toString()); 390 } 391 f.setVisible(true); 392 } 393 394 /** 395 * Drive the current state of the display from the state of the sensor. 396 * 397 * @param state the sensor state 398 */ 399 @Override 400 public void displayState(int state) { 401 if (getNamedSensor() == null) { 402 log.debug("Display state {}, disconnected", state); 403 } else if (isIcon()) { 404 NamedIcon icon = getIcon(state); 405 if (icon != null) { 406 super.setIcon(icon); 407 } 408 } else if (isText()) { 409 switch (state) { 410 case Sensor.UNKNOWN: 411 super.setText(unknownText); 412 getPopupUtility().setBackgroundColor(backgroundColorUnknown); 413 getPopupUtility().setForeground(textColorUnknown); 414 break; 415 case Sensor.ACTIVE: 416 super.setText(activeText); 417 getPopupUtility().setBackgroundColor(backgroundColorActive); 418 getPopupUtility().setForeground(textColorActive); 419 break; 420 case Sensor.INACTIVE: 421 super.setText(inactiveText); 422 getPopupUtility().setBackgroundColor(backgroundColorInActive); 423 getPopupUtility().setForeground(textColorInActive); 424 break; 425 default: 426 super.setText(inconsistentText); 427 getPopupUtility().setBackgroundColor(backgroundColorInconsistent); 428 getPopupUtility().setForeground(textColorInconsistent); 429 break; 430 } 431 } 432 int deg = getDegrees(); 433 rotate(deg); 434 if (deg == 0) { 435 setOpaque(getPopupUtility().hasBackground()); 436 } 437 438 updateSize(); 439 } 440 441 TableItemPanel<Sensor> _itemPanel; 442 443 @Override 444 public boolean setEditItemMenu(JPopupMenu popup) { 445 String txt = java.text.MessageFormat.format(Bundle.getMessage("EditItem"), Bundle.getMessage("BeanNameSensor")); 446 popup.add(new AbstractAction(txt) { 447 @Override 448 public void actionPerformed(ActionEvent e) { 449 editItem(); 450 } 451 }); 452 return true; 453 } 454 455 protected void editItem() { 456 _paletteFrame = makePaletteFrame(java.text.MessageFormat.format(Bundle.getMessage("EditItem"), Bundle.getMessage("BeanNameSensor"))); 457 _itemPanel = new TableItemPanel<>(_paletteFrame, "Sensor", _iconFamily, 458 PickListModel.sensorPickModelInstance()); // NOI18N 459 ActionListener updateAction = (ActionEvent a) -> updateItem(); 460 // duplicate _iconMap map with unscaled and unrotated icons 461 HashMap<String, NamedIcon> map = new HashMap<>(); 462 for (Entry<String, NamedIcon> entry : _iconMap.entrySet()) { 463 NamedIcon oldIcon = entry.getValue(); 464 NamedIcon newIcon = cloneIcon(oldIcon, this); 465 newIcon.rotate(0, this); 466 newIcon.scale(1.0, this); 467 newIcon.setRotation(4, this); 468 map.put(entry.getKey(), newIcon); 469 } 470 _itemPanel.init(updateAction, map); 471 _itemPanel.setSelection(getSensor()); 472 initPaletteFrame(_paletteFrame, _itemPanel); 473 } 474 475 void updateItem() { 476 HashMap<String, NamedIcon> oldMap = cloneMap(_iconMap, this); 477 setSensor(_itemPanel.getTableSelection().getSystemName()); 478 _iconFamily = _itemPanel.getFamilyName(); 479 HashMap<String, NamedIcon> iconMap = _itemPanel.getIconMap(); 480 if (iconMap != null) { 481 for (Entry<String, NamedIcon> entry : iconMap.entrySet()) { 482 if (log.isDebugEnabled()) { 483 log.debug("key= {}", entry.getKey()); 484 } 485 NamedIcon newIcon = entry.getValue(); 486 NamedIcon oldIcon = oldMap.get(entry.getKey()); 487 newIcon.setLoad(oldIcon.getDegrees(), oldIcon.getScale(), this); 488 newIcon.setRotation(oldIcon.getRotation(), this); 489 setIcon(entry.getKey(), newIcon); 490 } 491 } // otherwise retain current map 492 finishItemUpdate(_paletteFrame, _itemPanel); 493 } 494 495 @Override 496 public boolean setEditIconMenu(JPopupMenu popup) { 497 String txt = java.text.MessageFormat.format(Bundle.getMessage("EditItem"), Bundle.getMessage("BeanNameSensor")); 498 popup.add(new AbstractAction(txt) { 499 @Override 500 public void actionPerformed(ActionEvent e) { 501 edit(); 502 } 503 }); 504 return true; 505 } 506 507 @Override 508 protected void edit() { 509 makeIconEditorFrame(this, "Sensor", true, null); 510 _iconEditor.setPickList(jmri.jmrit.picker.PickListModel.sensorPickModelInstance()); 511 int i = 0; 512 for (Entry<String, NamedIcon> entry : _iconMap.entrySet()) { 513 _iconEditor.setIcon(i++, entry.getKey(), entry.getValue()); 514 } 515 _iconEditor.makeIconPanel(false); 516 517 // set default icons, then override with this turnout's icons 518 ActionListener addIconAction = (ActionEvent a) -> updateSensor(); 519 _iconEditor.complete(addIconAction, true, true, true); 520 _iconEditor.setSelection(getSensor()); 521 } 522 523 void updateSensor() { 524 HashMap<String, NamedIcon> oldMap = cloneMap(_iconMap, this); 525 setSensor(_iconEditor.getTableSelection().getDisplayName()); 526 Hashtable<String, NamedIcon> iconMap = _iconEditor.getIconMap(); 527 528 for (Entry<String, NamedIcon> entry : iconMap.entrySet()) { 529 log.debug("key= {}", entry.getKey()); 530 NamedIcon newIcon = entry.getValue(); 531 NamedIcon oldIcon = oldMap.get(entry.getKey()); 532 newIcon.setLoad(oldIcon.getDegrees(), oldIcon.getScale(), this); 533 newIcon.setRotation(oldIcon.getRotation(), this); 534 setIcon(entry.getKey(), newIcon); 535 } 536 _iconEditorFrame.dispose(); 537 _iconEditorFrame = null; 538 _iconEditor = null; 539 invalidate(); 540 } 541 542 // Original text is used when changing between icon and text, this allows for a undo when reverting back. 543 String originalText; 544 545 public void setOriginalText(String s) { 546 originalText = s; 547 displayState(sensorState()); 548 } 549 550 public String getOriginalText() { 551 return originalText; 552 } 553 554 @Override 555 public void setText(String s) { 556 setOpaque(false); 557 if (super._rotateText && !_icon) { 558 return; 559 } 560 _text = (s != null && s.length() > 0); 561 super.setText(s); 562 updateSize(); 563 } 564 565 boolean momentary = false; 566 567 public boolean getMomentary() { 568 return momentary; 569 } 570 571 public void setMomentary(boolean m) { 572 momentary = m; 573 } 574 575 public boolean buttonLive() { 576 if (namedSensor == null) { // no sensor connected for this protocol 577 log.error("No sensor connection, can't process click"); 578 return false; 579 } 580 return _editor.getFlag(Editor.OPTION_CONTROLS, isControlling()); 581 } 582 583 @Override 584 public void doMousePressed(JmriMouseEvent e) { 585 log.debug("doMousePressed buttonLive={}, getMomentary={}", buttonLive(), getMomentary()); 586 if (getMomentary() && buttonLive() && !e.isMetaDown() && !e.isAltDown()) { 587 // this is a momentary button press 588 try { 589 getSensor().setKnownState(jmri.Sensor.ACTIVE); 590 } catch (jmri.JmriException reason) { 591 log.warn("Exception setting momentary sensor", reason); 592 } 593 } 594 super.doMousePressed(e); 595 } 596 597 @Override 598 public void doMouseReleased(JmriMouseEvent e) { 599 if (getMomentary() && buttonLive() && !e.isMetaDown() && !e.isAltDown()) { 600 // this is a momentary button release 601 try { 602 getSensor().setKnownState(jmri.Sensor.INACTIVE); 603 } catch (jmri.JmriException reason) { 604 log.warn("Exception setting momentary sensor", reason); 605 } 606 } 607 super.doMouseReleased(e); 608 } 609 610 @Override 611 public void doMouseClicked(JmriMouseEvent e) { 612 if (buttonLive() && !getMomentary()) { 613 // this button responds to clicks 614 if (!e.isMetaDown() && !e.isAltDown()) { 615 try { 616 if (getSensor().getKnownState() == jmri.Sensor.INACTIVE) { 617 getSensor().setKnownState(jmri.Sensor.ACTIVE); 618 } else { 619 getSensor().setKnownState(jmri.Sensor.INACTIVE); 620 } 621 } catch (jmri.JmriException reason) { 622 log.warn("Exception flipping sensor", reason); 623 } 624 } 625 } 626 super.doMouseClicked(e); 627 } 628 629 @Override 630 public void dispose() { 631 if (namedSensor != null) { 632 getSensor().removePropertyChangeListener(this); 633 } 634 namedSensor = null; 635 _iconMap = null; 636 _name2stateMap = null; 637 _state2nameMap = null; 638 639 super.dispose(); 640 } 641 642 protected HashMap<Integer, NamedIcon> cloneMap(HashMap<Integer, NamedIcon> map, 643 SensorIcon pos) { 644 HashMap<Integer, NamedIcon> clone = new HashMap<>(); 645 if (map != null) { 646 for (Entry<Integer, NamedIcon> entry : map.entrySet()) { 647 clone.put(entry.getKey(), cloneIcon(entry.getValue(), pos)); 648 if (pos != null) { 649 pos.setIcon(pos._state2nameMap.get(entry.getKey()), _iconMap.get(entry.getKey().toString())); 650 } 651 } 652 } 653 return clone; 654 } 655 // The code below here is from the layoutsensoricon. 656 657 Color textColorActive = Color.red; 658 659 public void setTextActive(Color color) { 660 textColorActive = color; 661 displayState(sensorState()); 662 JmriColorChooser.addRecentColor(color); 663 } 664 665 public Color getTextActive() { 666 return textColorActive; 667 } 668 669 Color textColorInActive = Color.yellow; 670 671 public void setTextInActive(Color color) { 672 textColorInActive = color; 673 displayState(sensorState()); 674 JmriColorChooser.addRecentColor(color); 675 } 676 677 public Color getTextInActive() { 678 return textColorInActive; 679 } 680 681 Color textColorUnknown = Color.blue; 682 683 public void setTextUnknown(Color color) { 684 textColorUnknown = color; 685 displayState(sensorState()); 686 JmriColorChooser.addRecentColor(color); 687 } 688 689 public Color getTextUnknown() { 690 return textColorUnknown; 691 } 692 693 Color textColorInconsistent = Color.black; 694 695 public void setTextInconsistent(Color color) { 696 textColorInconsistent = color; 697 displayState(sensorState()); 698 JmriColorChooser.addRecentColor(color); 699 } 700 701 public Color getTextInconsistent() { 702 return textColorInconsistent; 703 } 704 705 Color backgroundColorActive = null; 706 707 public void setBackgroundActive(Color color) { 708 backgroundColorActive = color; 709 displayState(sensorState()); 710 JmriColorChooser.addRecentColor(color); 711 } 712 713 public Color getBackgroundActive() { 714 return backgroundColorActive; 715 } 716 717 Color backgroundColorInActive = null; 718 719 public void setBackgroundInActive(Color color) { 720 backgroundColorInActive = color; 721 displayState(sensorState()); 722 JmriColorChooser.addRecentColor(color); 723 } 724 725 public Color getBackgroundInActive() { 726 return backgroundColorInActive; 727 } 728 729 Color backgroundColorUnknown = null; 730 731 public void setBackgroundUnknown(Color color) { 732 backgroundColorUnknown = color; 733 displayState(sensorState()); 734 JmriColorChooser.addRecentColor(color); 735 } 736 737 public Color getBackgroundUnknown() { 738 return backgroundColorUnknown; 739 } 740 741 Color backgroundColorInconsistent = null; 742 743 public void setBackgroundInconsistent(Color color) { 744 backgroundColorInconsistent = color; 745 displayState(sensorState()); 746 JmriColorChooser.addRecentColor(color); 747 } 748 749 public Color getBackgroundInconsistent() { 750 return backgroundColorInconsistent; 751 } 752 753 private String activeText; 754 private String inactiveText; 755 private String inconsistentText; 756 private String unknownText; 757 758 public String getActiveText() { 759 return activeText; 760 } 761 762 public void setActiveText(String i) { 763 activeText = i; 764 displayState(sensorState()); 765 } 766 767 public String getInactiveText() { 768 return inactiveText; 769 } 770 771 public void setInactiveText(String i) { 772 inactiveText = i; 773 displayState(sensorState()); 774 } 775 776 public String getInconsistentText() { 777 return inconsistentText; 778 } 779 780 public void setInconsistentText(String i) { 781 inconsistentText = i; 782 displayState(sensorState()); 783 } 784 785 public String getUnknownText() { 786 return unknownText; 787 } 788 789 public void setUnknownText(String i) { 790 unknownText = i; 791 displayState(sensorState()); 792 } 793 794 JMenu stateMenu(final String name, int state) { 795 JMenu menu = new JMenu(name); 796 JMenuItem colorMenu = new JMenuItem(Bundle.getMessage("FontColor")); 797 colorMenu.addActionListener((ActionEvent event) -> { 798 Color desiredColor = JmriColorChooser.showDialog(this, 799 Bundle.getMessage("FontColor"), 800 getColor(state)); 801 if (desiredColor!=null ) { 802 setColor(desiredColor,state); 803 } 804 }); 805 menu.add(colorMenu); 806 colorMenu = new JMenuItem(Bundle.getMessage("FontBackgroundColor")); 807 colorMenu.addActionListener((ActionEvent event) -> { 808 Color desiredColor = JmriColorChooser.showDialog(this, 809 Bundle.getMessage("FontBackgroundColor"), 810 getColor(state+1)); 811 if (desiredColor!=null ) { 812 setColor(desiredColor,state+1); 813 } 814 }); 815 menu.add(colorMenu); 816 return menu; 817 } 818 819 private void setColor(Color desiredColor, int state) { 820 PositionablePopupUtil pop = getPopupUtility(); 821 if (pop instanceof SensorPopupUtil) { 822 SensorPopupUtil util = (SensorPopupUtil) pop; 823 switch (state) { 824 case PositionablePopupUtil.FONT_COLOR: 825 util.setForeground(desiredColor); 826 break; 827 case PositionablePopupUtil.BACKGROUND_COLOR: 828 util.setBackgroundColor(desiredColor); 829 break; 830 case PositionablePopupUtil.BORDER_COLOR: 831 util.setBorderColor(desiredColor); 832 break; 833 case UNKOWN_FONT_COLOR: 834 setTextUnknown(desiredColor); 835 break; 836 case UNKOWN_BACKGROUND_COLOR: 837 util.setHasBackground(desiredColor != null); 838 setBackgroundUnknown(desiredColor); 839 break; 840 case ACTIVE_FONT_COLOR: 841 setTextActive(desiredColor); 842 break; 843 case ACTIVE_BACKGROUND_COLOR: 844 util.setHasBackground(desiredColor != null); 845 setBackgroundActive(desiredColor); 846 break; 847 case INACTIVE_FONT_COLOR: 848 setTextInActive(desiredColor); 849 break; 850 case INACTIVE_BACKGROUND_COLOR: 851 util.setHasBackground(desiredColor != null); 852 setBackgroundInActive(desiredColor); 853 break; 854 case INCONSISTENT_FONT_COLOR: 855 setTextInconsistent(desiredColor); 856 break; 857 case INCONSISTENT_BACKGROUND_COLOR: 858 util.setHasBackground(desiredColor != null); 859 setBackgroundInconsistent(desiredColor); 860 break; 861 default: 862 break; 863 } 864 } 865 } 866 867 private Color getColor(int state){ 868 PositionablePopupUtil pop = getPopupUtility(); 869 if (pop instanceof SensorPopupUtil) { 870 SensorPopupUtil util = (SensorPopupUtil) pop; 871 switch (state) { 872 case PositionablePopupUtil.FONT_COLOR: 873 return util.getForeground(); 874 case PositionablePopupUtil.BACKGROUND_COLOR: 875 return util.getBackground(); 876 case PositionablePopupUtil.BORDER_COLOR: 877 return util.getBorderColor(); 878 case UNKOWN_FONT_COLOR: 879 return getTextUnknown(); 880 case UNKOWN_BACKGROUND_COLOR: 881 return getBackgroundUnknown(); 882 case ACTIVE_FONT_COLOR: 883 return getTextActive(); 884 case ACTIVE_BACKGROUND_COLOR: 885 return getBackgroundActive(); 886 case INACTIVE_FONT_COLOR: 887 return getTextInActive(); 888 case INACTIVE_BACKGROUND_COLOR: 889 return getBackgroundInActive(); 890 case INCONSISTENT_FONT_COLOR: 891 return getTextInconsistent(); 892 case INCONSISTENT_BACKGROUND_COLOR: 893 return getBackgroundInconsistent(); 894 default: 895 return null; 896 } 897 } 898 return null; 899 } 900 901 void changeLayoutSensorType() { 902// NamedBeanHandle <Sensor> handle = getNamedSensor(); 903 if (isIcon()) { 904 _icon = false; 905 _text = true; 906 setIcon(null); 907// setOriginalText(getUnRotatedText()); 908 setSuperText(null); 909 setOpaque(true); 910 } else if (isText()) { 911 _icon = true; 912 _text = (originalText != null && originalText.length() > 0); 913 setUnRotatedText(getOriginalText()); 914 setOpaque(false); 915 } 916 _namedIcon = null; 917 setAttributes(); 918 displayState(sensorState()); 919// setSensor(handle); 920 int deg = getDegrees(); 921 rotate(deg); 922 if (deg != 0 && _text && !_icon) { 923 setSuperText(null); 924 } 925 } 926 927 private int flashStateOn = -1; 928 private int flashStateOff = -1; 929 private boolean flashon = false; 930 private ActionListener taskPerformer; 931 private Timer flashTimer; 932 933 synchronized public void flashSensor(int tps, int state1, int state2) { 934 if ((flashTimer != null) && flashTimer.isRunning()) { 935 return; 936 } 937 //Set the maximum number of state changes to 10 per second 938 if (tps > 10) { 939 tps = 10; 940 } else if (tps <= 0) { 941 return; 942 } 943 if ((_state2nameMap.get(state1) == null) || _state2nameMap.get(state2) == null) { 944 log.error("one or other of the states passed for flash is null"); 945 return; 946 } else if (state1 == state2) { 947 log.debug("Both states to flash between are the same, therefore no flashing will occur"); 948 return; 949 } 950 int interval = (1000 / tps) / 2; 951 flashStateOn = state1; 952 flashStateOff = state2; 953 if (taskPerformer == null) { 954 taskPerformer = (ActionEvent evt) -> { 955 if (flashon) { 956 flashon = false; 957 displayState(flashStateOn); 958 } else { 959 flashon = true; 960 displayState(flashStateOff); 961 } 962 }; 963 } 964 flashTimer = new Timer(interval, taskPerformer); 965 flashTimer.start(); 966 } 967 968 synchronized public void stopFlash() { 969 if (flashTimer != null) { 970 flashTimer.stop(); 971 } 972 displayState(sensorState()); 973 } 974 975 class SensorPopupUtil extends PositionablePopupUtil { 976 977 SensorPopupUtil(Positionable parent, javax.swing.JComponent textComp) { 978 super(parent, textComp); 979 } 980 981 @Override 982 public SensorPopupUtil clone(Positionable parent, JComponent textComp) { 983 SensorPopupUtil util = new SensorPopupUtil(parent, textComp); 984 util.setJustification(getJustification()); 985 util.setHorizontalAlignment(getJustification()); 986 util.setFixedWidth(getFixedWidth()); 987 util.setFixedHeight(getFixedHeight()); 988 util.setMargin(getMargin()); 989 util.setBorderSize(getBorderSize()); 990 util.setBorderColor(getBorderColor()); 991 util.setFont(util.getFont().deriveFont(getFontStyle())); 992 util.setFontSize(getFontSize()); 993 util.setOrientation(getOrientation()); 994 util.setBackgroundColor(getBackground()); 995 util.setForeground(getForeground()); 996 util.setHasBackground(hasBackground()); // must do this AFTER setBackgroundColor 997 return util; 998 } 999 1000 @Override 1001 public void setTextJustificationMenu(JPopupMenu popup) { 1002 if (isText()) { 1003 super.setTextJustificationMenu(popup); 1004 } 1005 } 1006 1007 @Override 1008 public void setTextOrientationMenu(JPopupMenu popup) { 1009 if (isText()) { 1010 super.setTextOrientationMenu(popup); 1011 } 1012 } 1013 1014 @Override 1015 public void setFixedTextMenu(JPopupMenu popup) { 1016 if (isText()) { 1017 super.setFixedTextMenu(popup); 1018 } 1019 } 1020 1021 @Override 1022 public void setTextMarginMenu(JPopupMenu popup) { 1023 if (isText()) { 1024 super.setTextMarginMenu(popup); 1025 } 1026 } 1027 1028 @Override 1029 public void setTextBorderMenu(JPopupMenu popup) { 1030 if (isText()) { 1031 super.setTextBorderMenu(popup); 1032 } 1033 } 1034 1035 @Override 1036 public void setTextFontMenu(JPopupMenu popup) { 1037 if (isText()) { 1038 super.setTextFontMenu(popup); 1039 } 1040 } 1041 1042 @Override 1043 public void setBackgroundMenu(JPopupMenu popup) { 1044 if (isIcon()) { 1045 super.setBackgroundMenu(popup); 1046 } 1047 } 1048 } 1049 1050 private final static Logger log = LoggerFactory.getLogger(SensorIcon.class); 1051 1052}