001package jmri.jmrit.display; 002 003import java.awt.Color; 004import java.awt.Rectangle; 005import java.awt.event.ActionEvent; 006import java.awt.event.ActionListener; 007import java.util.Collection; 008import java.util.HashMap; 009import java.util.Hashtable; 010import java.util.Map.Entry; 011 012import javax.annotation.Nonnull; 013import javax.swing.AbstractAction; 014import javax.swing.JCheckBoxMenuItem; 015import javax.swing.JComponent; 016import javax.swing.JMenu; 017import javax.swing.JMenuItem; 018import javax.swing.JPopupMenu; 019import javax.swing.Timer; 020 021import jmri.InstanceManager; 022import jmri.NamedBeanHandle; 023import jmri.Sensor; 024import jmri.NamedBean.DisplayOptions; 025import jmri.jmrit.catalog.NamedIcon; 026import jmri.jmrit.display.palette.TableItemPanel; 027import jmri.jmrit.picker.PickListModel; 028import jmri.util.swing.JmriColorChooser; 029import jmri.util.swing.JmriMouseEvent; 030 031/** 032 * An icon to display a status of a Sensor. 033 * 034 * @author Bob Jacobsen Copyright (C) 2001 035 * @author Pete Cressman Copyright (C) 2010, 2011 036 */ 037public class SensorIcon extends PositionableIcon implements java.beans.PropertyChangeListener { 038 039 public static final int UNKOWN_FONT_COLOR = 0x03; 040 public static final int UNKOWN_BACKGROUND_COLOR = 0x04; 041 public static final int ACTIVE_FONT_COLOR = 0x05; 042 public static final int ACTIVE_BACKGROUND_COLOR = 0x06; 043 public static final int INACTIVE_FONT_COLOR = 0x07; 044 public static final int INACTIVE_BACKGROUND_COLOR = 0x08; 045 public static final int INCONSISTENT_FONT_COLOR = 0x0A; 046 public static final int INCONSISTENT_BACKGROUND_COLOR = 0x0B; 047 048 protected HashMap<String, Integer> _name2stateMap; // name to state 049 protected HashMap<Integer, String> _state2nameMap; // state to name 050 051 public SensorIcon(Editor editor) { 052 // super ctor call to make sure this is an icon label 053 this(new NamedIcon("resources/icons/smallschematics/tracksegments/circuit-error.gif", 054 "resources/icons/smallschematics/tracksegments/circuit-error.gif"), editor); 055 } 056 057 public SensorIcon(NamedIcon s, Editor editor) { 058 // super ctor call to make sure this is an icon label 059 super(s, editor); 060 setOpaque(false); 061 _control = true; 062 setPopupUtility(new SensorPopupUtil(this, this)); 063 } 064 065 public SensorIcon(String s, Editor editor) { 066 super(s, editor); 067 _control = true; 068 originalText = s; 069 setPopupUtility(new SensorPopupUtil(this, this)); 070 displayState(sensorState()); 071 } 072 073 @Override 074 public Positionable deepClone() { 075 SensorIcon pos = new SensorIcon(_editor); 076 return finishClone(pos); 077 } 078 079 protected Positionable finishClone(SensorIcon pos) { 080 pos.setSensor(getNamedSensor().getName()); 081 pos.makeIconMap(); 082 pos._iconMap = cloneMap(_iconMap, pos); 083 pos.setMomentary(getMomentary()); 084 pos.originalText = originalText; 085 pos.setText(getText()); 086 pos.setIcon(null); 087 pos._namedIcon = null; 088 pos.activeText = activeText; 089 pos.inactiveText = inactiveText; 090 pos.inconsistentText = inconsistentText; 091 pos.unknownText = unknownText; 092 pos.textColorInconsistent = textColorInconsistent; 093 pos.textColorUnknown = textColorUnknown; 094 pos.textColorInActive = textColorInActive; 095 pos.textColorActive = textColorActive; 096 pos.backgroundColorInActive = backgroundColorInActive; 097 pos.backgroundColorActive = backgroundColorActive; 098 pos.backgroundColorUnknown = backgroundColorUnknown; 099 pos.backgroundColorInconsistent = backgroundColorInconsistent; 100 return super.finishClone(pos); 101 } 102 103 // the associated Sensor object 104 private NamedBeanHandle<Sensor> namedSensor; 105 106 /** 107 * Attached a named sensor to this display item 108 * 109 * @param pName System/user name to lookup the sensor object 110 */ 111 public void setSensor(String pName) { 112 if (InstanceManager.getNullableDefault(jmri.SensorManager.class) != null) { 113 try { 114 Sensor sensor = InstanceManager.sensorManagerInstance().provideSensor(pName); 115 setSensor(jmri.InstanceManager.getDefault(jmri.NamedBeanHandleManager.class).getNamedBeanHandle(pName, sensor)); 116 } catch (IllegalArgumentException ex) { 117 log.error("Sensor '{}' not available, icon won't see changes", pName); 118 } 119 } else { 120 log.error("No SensorManager for this protocol, icon won't see changes"); 121 } 122 } 123 124 /** 125 * Attached a named sensor to this display item 126 * 127 * @param s the Sensor 128 */ 129 public void setSensor(NamedBeanHandle<Sensor> s) { 130 if (namedSensor != null) { 131 getSensor().removePropertyChangeListener(this); 132 } 133 134 namedSensor = s; 135 if (namedSensor != null) { 136 if (_iconMap == null) { 137 makeIconMap(); 138 } 139 getSensor().addPropertyChangeListener(this, s.getName(), "SensorIcon on Panel " + _editor.getName()); 140 setName(namedSensor.getName()); // Swing name for e.g. tests 141 } 142 setAttributes(); 143 } 144 145 private void setAttributes() { 146 if (isText()) { 147 if (namedSensor != null) { 148 if (getSensor().getUserName() != null) { 149 String userName = getSensor().getUserName(); 150 if (activeText == null) { 151 activeText = userName; 152 //textColorActive=Color.red; 153 } 154 if (inactiveText == null) { 155 inactiveText = userName; 156 //textColorInActive=Color.yellow; 157 } 158 if (inconsistentText == null) { 159 inconsistentText = userName; 160 //textColorUnknown=Color.black; 161 } 162 if (unknownText == null) { 163 unknownText = userName; 164 //textColorInconsistent=Color.blue; 165 } 166 } 167 } 168 if (activeText == null) { 169 activeText = "<" + Bundle.getMessage("SensorStateActive").toLowerCase() + ">"; // initiate state label string 170 // created from Bundle as lower case, enclosed in < > 171 } 172 if (inactiveText == null) { 173 inactiveText = "<" + Bundle.getMessage("SensorStateInactive").toLowerCase() + ">"; 174 } 175 if (inconsistentText == null) { 176 inconsistentText = "<" + Bundle.getMessage("BeanStateInconsistent").toLowerCase() + ">"; 177 } 178 if (unknownText == null) { 179 unknownText = "<" + Bundle.getMessage("BeanStateUnknown").toLowerCase() + ">"; 180 } 181 if (textColorActive == null) { 182 textColorActive = Color.red; 183 } 184 if (textColorInActive == null) { 185 textColorInActive = Color.yellow; 186 } 187 if (textColorUnknown == null) { 188 textColorUnknown = Color.blue; 189 } 190 if (textColorInconsistent == null) { 191 textColorInconsistent = Color.black; 192 } 193 } else { 194 setOpaque(false); 195 } 196 displayState(sensorState()); 197 if (log.isDebugEnabled()) { // avoid String building unless needed 198 log.debug("setSensor: namedSensor= {} isIcon= {}, isText= {}, activeText= {}", 199 ((namedSensor == null) ? "null" : getNameString()), isIcon(), isText(), activeText); 200 } 201 repaint(); 202 } 203 204 public Sensor getSensor() { 205 if (namedSensor == null) { 206 return null; 207 } 208 return namedSensor.getBean(); 209 } 210 211 @Override 212 public jmri.NamedBean getNamedBean() { 213 return getSensor(); 214 } 215 216 public NamedBeanHandle<Sensor> getNamedSensor() { 217 return namedSensor; 218 } 219 220 void makeIconMap() { 221 _iconMap = new HashMap<>(); 222 _name2stateMap = new HashMap<>(); 223 _name2stateMap.put("BeanStateUnknown", Sensor.UNKNOWN); 224 _name2stateMap.put("BeanStateInconsistent", Sensor.INCONSISTENT); 225 _name2stateMap.put("SensorStateActive", Sensor.ACTIVE); 226 _name2stateMap.put("SensorStateInactive", Sensor.INACTIVE); 227 _state2nameMap = new HashMap<>(); 228 _state2nameMap.put(Sensor.UNKNOWN, "BeanStateUnknown"); 229 _state2nameMap.put(Sensor.INCONSISTENT, "BeanStateInconsistent"); 230 _state2nameMap.put(Sensor.ACTIVE, "SensorStateActive"); 231 _state2nameMap.put(Sensor.INACTIVE, "SensorStateInactive"); 232 } 233 234 @Override 235 public Collection<String> getStateNameCollection() { 236 return _state2nameMap.values(); 237 } 238 239 /** 240 * Place icon by its bean state name key found in the properties file 241 * jmri.NamedBeanBundle.properties by its localized bean state name. 242 * 243 * @param name the icon state name 244 * @param icon the icon to place 245 */ 246 public void setIcon(String name, NamedIcon icon) { 247 log.debug("setIcon for name \"{}\"", name); 248 if (_iconMap == null) { 249 makeIconMap(); 250 } 251 _iconMap.put(name, icon); 252 displayState(sensorState()); 253 } 254 255 /** 256 * Get icon by its localized bean state name. 257 * 258 * @param state the state to get the icon for 259 * @return the icon or null if state not found 260 */ 261 @Override 262 public NamedIcon getIcon(String state) { 263 return _iconMap.get(state); 264 } 265 266 public NamedIcon getIcon(int state) { 267 return _iconMap.get(_state2nameMap.get(state)); 268 } 269 270 @Override 271 public String getFamily() { 272 return _iconFamily; 273 } 274 275 @Override 276 public void setFamily(String family) { 277 _iconFamily = family; 278 } 279 280 /** 281 * Get current state of attached sensor 282 * 283 * @return A state variable from a Sensor, e.g. Sensor.ACTIVE 284 */ 285 int sensorState() { 286 if (namedSensor != null) { 287 return getSensor().getKnownState(); 288 } else { 289 return Sensor.UNKNOWN; 290 } 291 } 292 293 // update icon as state of turnout changes 294 @Override 295 public void propertyChange(java.beans.PropertyChangeEvent e) { 296 log.debug("property change: {}", e); 297 if (e.getPropertyName().equals("KnownState")) { 298 int now = ((Integer) e.getNewValue()); 299 Rectangle dirty = getBounds(); 300 displayState(now); 301 dirty.add(getBounds()); // union with the new bounds, in case displayState resized this icon 302 _editor.repaintTargetPanel(dirty); 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 {},{} clicks={}, buttonLive={}, getMomentary={}", 592 e.getX(), e.getY(), e.getClickCount(), 593 buttonLive(), getMomentary()); 594 595 if (getMomentary() && buttonLive() && !e.isMetaDown() && !e.isAltDown()) { 596 // this is a momentary button press 597 try { 598 getSensor().setKnownState(jmri.Sensor.ACTIVE); 599 } catch (jmri.JmriException reason) { 600 log.warn("Exception setting momentary sensor", reason); 601 } 602 } 603 super.doMousePressed(e); 604 } 605 606 @Override 607 public void doMouseReleased(JmriMouseEvent e) { 608 log.debug("doMouseReleased {},{} clicks={}, buttonLive={}, getMomentary={}", 609 e.getX(), e.getY(), e.getClickCount(), 610 buttonLive(), getMomentary()); 611 612 if (getMomentary() && buttonLive() && !e.isMetaDown() && !e.isAltDown()) { 613 // this is a momentary button release 614 try { 615 getSensor().setKnownState(jmri.Sensor.INACTIVE); 616 } catch (jmri.JmriException reason) { 617 log.warn("Exception setting momentary sensor", reason); 618 } 619 } 620 super.doMouseReleased(e); 621 } 622 623 @Override 624 public void doMouseClicked(JmriMouseEvent e) { 625 log.debug("doMouseClicked {},{} clicks={}, buttonLive={}, getMomentary={}", 626 e.getX(), e.getY(), e.getClickCount(), 627 buttonLive(), getMomentary()); 628 629 if (buttonLive() && !getMomentary()) { 630 // this button responds to clicks 631 if (!e.isMetaDown() && !e.isAltDown()) { 632 try { 633 if (getSensor().getKnownState() == jmri.Sensor.INACTIVE) { 634 getSensor().setKnownState(jmri.Sensor.ACTIVE); 635 } else { 636 getSensor().setKnownState(jmri.Sensor.INACTIVE); 637 } 638 } catch (jmri.JmriException reason) { 639 log.warn("Exception flipping sensor", reason); 640 } 641 } 642 } 643 super.doMouseClicked(e); 644 } 645 646 @Override 647 public void dispose() { 648 if (namedSensor != null) { 649 getSensor().removePropertyChangeListener(this); 650 } 651 namedSensor = null; 652 _iconMap = null; 653 _name2stateMap = null; 654 _state2nameMap = null; 655 656 super.dispose(); 657 } 658 659 protected HashMap<Integer, NamedIcon> cloneMap(HashMap<Integer, NamedIcon> map, 660 SensorIcon pos) { 661 HashMap<Integer, NamedIcon> clone = new HashMap<>(); 662 if (map != null) { 663 for (Entry<Integer, NamedIcon> entry : map.entrySet()) { 664 clone.put(entry.getKey(), cloneIcon(entry.getValue(), pos)); 665 if (pos != null) { 666 pos.setIcon(pos._state2nameMap.get(entry.getKey()), _iconMap.get(entry.getKey().toString())); 667 } 668 } 669 } 670 return clone; 671 } 672 // The code below here is from the layoutsensoricon. 673 674 Color textColorActive = Color.red; 675 676 public void setTextActive(Color color) { 677 textColorActive = color; 678 displayState(sensorState()); 679 JmriColorChooser.addRecentColor(color); 680 } 681 682 public Color getTextActive() { 683 return textColorActive; 684 } 685 686 Color textColorInActive = Color.yellow; 687 688 public void setTextInActive(Color color) { 689 textColorInActive = color; 690 displayState(sensorState()); 691 JmriColorChooser.addRecentColor(color); 692 } 693 694 public Color getTextInActive() { 695 return textColorInActive; 696 } 697 698 Color textColorUnknown = Color.blue; 699 700 public void setTextUnknown(Color color) { 701 textColorUnknown = color; 702 displayState(sensorState()); 703 JmriColorChooser.addRecentColor(color); 704 } 705 706 public Color getTextUnknown() { 707 return textColorUnknown; 708 } 709 710 Color textColorInconsistent = Color.black; 711 712 public void setTextInconsistent(Color color) { 713 textColorInconsistent = color; 714 displayState(sensorState()); 715 JmriColorChooser.addRecentColor(color); 716 } 717 718 public Color getTextInconsistent() { 719 return textColorInconsistent; 720 } 721 722 Color backgroundColorActive = null; 723 724 public void setBackgroundActive(Color color) { 725 backgroundColorActive = color; 726 displayState(sensorState()); 727 JmriColorChooser.addRecentColor(color); 728 } 729 730 public Color getBackgroundActive() { 731 return backgroundColorActive; 732 } 733 734 Color backgroundColorInActive = null; 735 736 public void setBackgroundInActive(Color color) { 737 backgroundColorInActive = color; 738 displayState(sensorState()); 739 JmriColorChooser.addRecentColor(color); 740 } 741 742 public Color getBackgroundInActive() { 743 return backgroundColorInActive; 744 } 745 746 Color backgroundColorUnknown = null; 747 748 public void setBackgroundUnknown(Color color) { 749 backgroundColorUnknown = color; 750 displayState(sensorState()); 751 JmriColorChooser.addRecentColor(color); 752 } 753 754 public Color getBackgroundUnknown() { 755 return backgroundColorUnknown; 756 } 757 758 Color backgroundColorInconsistent = null; 759 760 public void setBackgroundInconsistent(Color color) { 761 backgroundColorInconsistent = color; 762 displayState(sensorState()); 763 JmriColorChooser.addRecentColor(color); 764 } 765 766 public Color getBackgroundInconsistent() { 767 return backgroundColorInconsistent; 768 } 769 770 private String activeText; 771 private String inactiveText; 772 private String inconsistentText; 773 private String unknownText; 774 775 public String getActiveText() { 776 return activeText; 777 } 778 779 public void setActiveText(String i) { 780 activeText = i; 781 displayState(sensorState()); 782 } 783 784 public String getInactiveText() { 785 return inactiveText; 786 } 787 788 public void setInactiveText(String i) { 789 inactiveText = i; 790 displayState(sensorState()); 791 } 792 793 public String getInconsistentText() { 794 return inconsistentText; 795 } 796 797 public void setInconsistentText(String i) { 798 inconsistentText = i; 799 displayState(sensorState()); 800 } 801 802 public String getUnknownText() { 803 return unknownText; 804 } 805 806 public void setUnknownText(String i) { 807 unknownText = i; 808 displayState(sensorState()); 809 } 810 811 JMenu stateMenu(final String name, int state) { 812 JMenu menu = new JMenu(name); 813 JMenuItem colorMenu = new JMenuItem(Bundle.getMessage("FontColor")); 814 colorMenu.addActionListener((ActionEvent event) -> { 815 Color desiredColor = JmriColorChooser.showDialog(this, 816 Bundle.getMessage("FontColor"), 817 getColor(state)); 818 if (desiredColor!=null ) { 819 setColor(desiredColor,state); 820 } 821 }); 822 menu.add(colorMenu); 823 colorMenu = new JMenuItem(Bundle.getMessage("FontBackgroundColor")); 824 colorMenu.addActionListener((ActionEvent event) -> { 825 Color desiredColor = JmriColorChooser.showDialog(this, 826 Bundle.getMessage("FontBackgroundColor"), 827 getColor(state+1)); 828 if (desiredColor!=null ) { 829 setColor(desiredColor,state+1); 830 } 831 }); 832 menu.add(colorMenu); 833 return menu; 834 } 835 836 private void setColor(Color desiredColor, int state) { 837 PositionablePopupUtil pop = getPopupUtility(); 838 if (pop instanceof SensorPopupUtil) { 839 SensorPopupUtil util = (SensorPopupUtil) pop; 840 switch (state) { 841 case PositionablePopupUtil.FONT_COLOR: 842 util.setForeground(desiredColor); 843 break; 844 case PositionablePopupUtil.BACKGROUND_COLOR: 845 util.setBackgroundColor(desiredColor); 846 break; 847 case PositionablePopupUtil.BORDER_COLOR: 848 util.setBorderColor(desiredColor); 849 break; 850 case UNKOWN_FONT_COLOR: 851 setTextUnknown(desiredColor); 852 break; 853 case UNKOWN_BACKGROUND_COLOR: 854 util.setHasBackground(desiredColor != null); 855 setBackgroundUnknown(desiredColor); 856 break; 857 case ACTIVE_FONT_COLOR: 858 setTextActive(desiredColor); 859 break; 860 case ACTIVE_BACKGROUND_COLOR: 861 util.setHasBackground(desiredColor != null); 862 setBackgroundActive(desiredColor); 863 break; 864 case INACTIVE_FONT_COLOR: 865 setTextInActive(desiredColor); 866 break; 867 case INACTIVE_BACKGROUND_COLOR: 868 util.setHasBackground(desiredColor != null); 869 setBackgroundInActive(desiredColor); 870 break; 871 case INCONSISTENT_FONT_COLOR: 872 setTextInconsistent(desiredColor); 873 break; 874 case INCONSISTENT_BACKGROUND_COLOR: 875 util.setHasBackground(desiredColor != null); 876 setBackgroundInconsistent(desiredColor); 877 break; 878 default: 879 break; 880 } 881 } 882 } 883 884 private Color getColor(int state){ 885 PositionablePopupUtil pop = getPopupUtility(); 886 if (pop instanceof SensorPopupUtil) { 887 SensorPopupUtil util = (SensorPopupUtil) pop; 888 switch (state) { 889 case PositionablePopupUtil.FONT_COLOR: 890 return util.getForeground(); 891 case PositionablePopupUtil.BACKGROUND_COLOR: 892 return util.getBackground(); 893 case PositionablePopupUtil.BORDER_COLOR: 894 return util.getBorderColor(); 895 case UNKOWN_FONT_COLOR: 896 return getTextUnknown(); 897 case UNKOWN_BACKGROUND_COLOR: 898 return getBackgroundUnknown(); 899 case ACTIVE_FONT_COLOR: 900 return getTextActive(); 901 case ACTIVE_BACKGROUND_COLOR: 902 return getBackgroundActive(); 903 case INACTIVE_FONT_COLOR: 904 return getTextInActive(); 905 case INACTIVE_BACKGROUND_COLOR: 906 return getBackgroundInActive(); 907 case INCONSISTENT_FONT_COLOR: 908 return getTextInconsistent(); 909 case INCONSISTENT_BACKGROUND_COLOR: 910 return getBackgroundInconsistent(); 911 default: 912 return null; 913 } 914 } 915 return null; 916 } 917 918 void changeLayoutSensorType() { 919// NamedBeanHandle <Sensor> handle = getNamedSensor(); 920 if (isIcon()) { 921 _icon = false; 922 _text = true; 923 setIcon(null); 924// setOriginalText(getUnRotatedText()); 925 setSuperText(null); 926 setOpaque(true); 927 } else if (isText()) { 928 _icon = true; 929 _text = (originalText != null && originalText.length() > 0); 930 setUnRotatedText(getOriginalText()); 931 setOpaque(false); 932 } 933 _namedIcon = null; 934 setAttributes(); 935 displayState(sensorState()); 936// setSensor(handle); 937 int deg = getDegrees(); 938 rotate(deg); 939 if (deg != 0 && _text && !_icon) { 940 setSuperText(null); 941 } 942 } 943 944 private int flashStateOn = -1; 945 private int flashStateOff = -1; 946 private boolean flashon = false; 947 private ActionListener taskPerformer; 948 private Timer flashTimer; 949 950 synchronized public void flashSensor(int tps, int state1, int state2) { 951 if ((flashTimer != null) && flashTimer.isRunning()) { 952 return; 953 } 954 //Set the maximum number of state changes to 10 per second 955 if (tps > 10) { 956 tps = 10; 957 } else if (tps <= 0) { 958 return; 959 } 960 if ((_state2nameMap.get(state1) == null) || _state2nameMap.get(state2) == null) { 961 log.error("one or other of the states passed for flash is null"); 962 return; 963 } else if (state1 == state2) { 964 log.debug("Both states to flash between are the same, therefore no flashing will occur"); 965 return; 966 } 967 int interval = (1000 / tps) / 2; 968 flashStateOn = state1; 969 flashStateOff = state2; 970 if (taskPerformer == null) { 971 taskPerformer = (ActionEvent evt) -> { 972 if (flashon) { 973 flashon = false; 974 displayState(flashStateOn); 975 } else { 976 flashon = true; 977 displayState(flashStateOff); 978 } 979 }; 980 } 981 flashTimer = new Timer(interval, taskPerformer); 982 flashTimer.start(); 983 } 984 985 synchronized public void stopFlash() { 986 if (flashTimer != null) { 987 flashTimer.stop(); 988 } 989 displayState(sensorState()); 990 } 991 992 class SensorPopupUtil extends PositionablePopupUtil { 993 994 SensorPopupUtil(Positionable parent, javax.swing.JComponent textComp) { 995 super(parent, textComp); 996 } 997 998 @Override 999 public SensorPopupUtil clone(Positionable parent, JComponent textComp) { 1000 SensorPopupUtil util = new SensorPopupUtil(parent, textComp); 1001 util.setJustification(getJustification()); 1002 util.setHorizontalAlignment(getJustification()); 1003 util.setFixedWidth(getFixedWidth()); 1004 util.setFixedHeight(getFixedHeight()); 1005 util.setMargin(getMargin()); 1006 util.setBorderSize(getBorderSize()); 1007 util.setBorderColor(getBorderColor()); 1008 util.setFont(util.getFont().deriveFont(getFontStyle())); 1009 util.setFontSize(getFontSize()); 1010 util.setOrientation(getOrientation()); 1011 util.setBackgroundColor(getBackground()); 1012 util.setForeground(getForeground()); 1013 util.setHasBackground(hasBackground()); // must do this AFTER setBackgroundColor 1014 return util; 1015 } 1016 1017 @Override 1018 public void setTextJustificationMenu(JPopupMenu popup) { 1019 if (isText()) { 1020 super.setTextJustificationMenu(popup); 1021 } 1022 } 1023 1024 @Override 1025 public void setTextOrientationMenu(JPopupMenu popup) { 1026 if (isText()) { 1027 super.setTextOrientationMenu(popup); 1028 } 1029 } 1030 1031 @Override 1032 public void setFixedTextMenu(JPopupMenu popup) { 1033 if (isText()) { 1034 super.setFixedTextMenu(popup); 1035 } 1036 } 1037 1038 @Override 1039 public void setTextMarginMenu(JPopupMenu popup) { 1040 if (isText()) { 1041 super.setTextMarginMenu(popup); 1042 } 1043 } 1044 1045 @Override 1046 public void setTextBorderMenu(JPopupMenu popup) { 1047 if (isText()) { 1048 super.setTextBorderMenu(popup); 1049 } 1050 } 1051 1052 @Override 1053 public void setTextFontMenu(JPopupMenu popup) { 1054 if (isText()) { 1055 super.setTextFontMenu(popup); 1056 } 1057 } 1058 1059 @Override 1060 public void setBackgroundMenu(JPopupMenu popup) { 1061 if (isIcon()) { 1062 super.setBackgroundMenu(popup); 1063 } 1064 } 1065 } 1066 1067 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(SensorIcon.class); 1068 1069}