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