001package jmri.jmrit.display; 002 003import java.awt.*; 004import java.awt.datatransfer.DataFlavor; 005import java.awt.event.*; 006import java.awt.geom.Rectangle2D; 007import java.beans.PropertyChangeEvent; 008import java.beans.PropertyVetoException; 009import java.beans.VetoableChangeListener; 010import java.lang.reflect.InvocationTargetException; 011import java.text.MessageFormat; 012import java.util.*; 013import java.util.List; 014 015import javax.annotation.Nonnull; 016import javax.swing.*; 017import javax.swing.Timer; 018import javax.swing.border.Border; 019import javax.swing.border.CompoundBorder; 020import javax.swing.border.LineBorder; 021import javax.swing.event.ListSelectionEvent; 022 023import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 024 025import jmri.*; 026import jmri.jmrit.catalog.CatalogPanel; 027import jmri.jmrit.catalog.DirectorySearcher; 028import jmri.jmrit.catalog.ImageIndexEditor; 029import jmri.jmrit.catalog.NamedIcon; 030import jmri.jmrit.display.controlPanelEditor.shape.PositionableShape; 031import jmri.jmrit.logixng.*; 032import jmri.jmrit.logixng.tools.swing.DeleteBean; 033import jmri.jmrit.logixng.tools.swing.LogixNGEditor; 034import jmri.jmrit.operations.trains.TrainIcon; 035import jmri.jmrit.picker.PickListModel; 036import jmri.jmrit.roster.Roster; 037import jmri.jmrit.roster.RosterEntry; 038import jmri.jmrit.roster.swing.RosterEntrySelectorPanel; 039import jmri.util.DnDStringImportHandler; 040import jmri.util.JmriJFrame; 041import jmri.util.ThreadingUtil; 042import jmri.util.swing.JmriColorChooser; 043import jmri.util.swing.JmriJOptionPane; 044import jmri.util.swing.JmriMouseEvent; 045import jmri.util.swing.JmriMouseListener; 046import jmri.util.swing.JmriMouseMotionListener; 047 048/** 049 * This is the Model and a Controller for panel editor Views. (Panel Editor, 050 * Layout Editor or any subsequent editors) The Model is simply a list of 051 * Positionable objects added to a "target panel". Control of the display 052 * attributes of the Positionable objects is done here. However, control of 053 * mouse events is passed to the editor views, so control is also done by the 054 * editor views. 055 * <p> 056 * The "contents" List keeps track of all the objects added to the target frame 057 * for later manipulation. This class only locates and moves "target panel" 058 * items, and does not control their appearance - that is left for the editor 059 * views. 060 * <p> 061 * The Editor has tri-state "flags" to control the display of Positionable 062 * object attributes globally - i.e. "on" or "off" for all - or as a third 063 * state, permits the display control "locally" by corresponding flags in each 064 * Positionable object 065 * <p> 066 * The title of the target and the editor panel are kept consistent via the 067 * {#setTitle} method. 068 * <p> 069 * Mouse events are initial handled here, rather than in the individual 070 * displayed objects, so that selection boxes for moving multiple objects can be 071 * provided. 072 * <p> 073 * This class also implements an effective ToolTipManager replacement, because 074 * the standard Swing one can't deal with the coordinate changes used to zoom a 075 * panel. It works by controlling the contents of the _tooltip instance 076 * variable, and triggering repaint of the target window when the tooltip 077 * changes. The window painting then explicitly draws the tooltip for the 078 * underlying object. 079 * 080 * @author Bob Jacobsen Copyright: Copyright (c) 2002, 2003, 2007 081 * @author Dennis Miller 2004 082 * @author Howard G. Penny Copyright: Copyright (c) 2005 083 * @author Matthew Harris Copyright: Copyright (c) 2009 084 * @author Pete Cressman Copyright: Copyright (c) 2009, 2010, 2011 085 * 086 */ 087public abstract class Editor extends JmriJFrameWithPermissions 088 implements JmriMouseListener, JmriMouseMotionListener, ActionListener, 089 KeyListener, VetoableChangeListener { 090 091 public static final int BKG = 1; 092 public static final int TEMP = 2; 093 public static final int ICONS = 3; 094 public static final int LABELS = 4; 095 public static final int MEMORIES = 5; 096 public static final int REPORTERS = 5; 097 public static final int SECURITY = 6; 098 public static final int TURNOUTS = 7; 099 public static final int LIGHTS = 8; 100 public static final int SIGNALS = 9; 101 public static final int SENSORS = 10; 102 public static final int CLOCK = 10; 103 public static final int MARKERS = 10; 104 public static final int NUM_LEVELS = 10; 105 106 public static final int SCROLL_NONE = 0; 107 public static final int SCROLL_BOTH = 1; 108 public static final int SCROLL_HORIZONTAL = 2; 109 public static final int SCROLL_VERTICAL = 3; 110 111 public static final Color HIGHLIGHT_COLOR = new Color(204, 207, 88); 112 113 public static final String POSITIONABLE_FLAVOR = DataFlavor.javaJVMLocalObjectMimeType 114 + ";class=jmri.jmrit.display.Positionable"; 115 116 private boolean _loadFailed = false; 117 118 private ArrayList<Positionable> _contents = new ArrayList<>(); 119 private Map<String, Positionable> _idContents = new HashMap<>(); 120 private Map<String, Set<Positionable>> _classContents = new HashMap<>(); 121 protected JLayeredPane _targetPanel; 122 private JFrame _targetFrame; 123 private JScrollPane _panelScrollPane; 124 125 // Option menu items 126 protected int _scrollState = SCROLL_NONE; 127 protected boolean _editable = true; 128 private boolean _positionable = true; 129 private boolean _controlLayout = true; 130 private boolean _showHidden = true; 131 private boolean _showToolTip = true; 132// private boolean _showCoordinates = true; 133 134 final public static int OPTION_POSITION = 1; 135 final public static int OPTION_CONTROLS = 2; 136 final public static int OPTION_HIDDEN = 3; 137 final public static int OPTION_TOOLTIP = 4; 138// final public static int OPTION_COORDS = 5; 139 140 private boolean _globalSetsLocal = true; // pre 2.9.6 behavior 141 private boolean _useGlobalFlag = false; // pre 2.9.6 behavior 142 143 // mouse methods variables 144 protected int _lastX; 145 protected int _lastY; 146 BasicStroke DASHED_LINE = new BasicStroke(1f, BasicStroke.CAP_BUTT, 147 BasicStroke.JOIN_BEVEL, 148 10f, new float[]{10f, 10f}, 0f); 149 150 protected Rectangle _selectRect = null; 151 protected Rectangle _highlightcomponent = null; 152 protected boolean _dragging = false; 153 protected ArrayList<Positionable> _selectionGroup = null; // items gathered inside fence 154 155 protected Positionable _currentSelection; 156 private ToolTip _defaultToolTip; 157 private ToolTip _tooltip = null; 158 159 // Accessible to editor views 160 protected int xLoc = 0; // x coord of selected Positionable 161 protected int yLoc = 0; // y coord of selected Positionable 162 protected int _anchorX; // x coord when mousePressed 163 protected int _anchorY; // y coord when mousePressed 164 165// private boolean delayedPopupTrigger = false; // Used to delay the request of a popup, on a mouse press as this may conflict with a drag event 166 protected double _paintScale = 1.0; // scale for _targetPanel drawing 167 168 protected Color defaultBackgroundColor = Color.lightGray; 169 protected boolean _pastePending = false; 170 171 // map of icon editor frames (incl, icon editor) keyed by name 172 protected HashMap<String, JFrameItem> _iconEditorFrame = new HashMap<>(); 173 174 // store panelMenu state so preference is retained on headless systems 175 private boolean panelMenuIsVisible = true; 176 177 private boolean _inEditInlineLogixNGMode = false; 178 private LogixNGEditor _inlineLogixNGEdit; 179 180 public Editor() { 181 } 182 183 public Editor(String name, boolean saveSize, boolean savePosition) { 184 super(name, saveSize, savePosition); 185 setName(name); 186 _defaultToolTip = new ToolTip(null, 0, 0, null); 187 setVisible(false); 188 InstanceManager.getDefault(SignalHeadManager.class).addVetoableChangeListener(this); 189 InstanceManager.getDefault(SignalMastManager.class).addVetoableChangeListener(this); 190 InstanceManager.turnoutManagerInstance().addVetoableChangeListener(this); 191 InstanceManager.sensorManagerInstance().addVetoableChangeListener(this); 192 InstanceManager.memoryManagerInstance().addVetoableChangeListener(this); 193 InstanceManager.getDefault(BlockManager.class).addVetoableChangeListener(this); 194 InstanceManager.getDefault(EditorManager.class).add(this); 195 } 196 197 public Editor(String name) { 198 this(name, true, true); 199 } 200 201 /** 202 * Set <strong>white</strong> as the default background color for panels created using the <strong>New Panel</strong> menu item. 203 * Overriden by LE to use a different default background color and set other initial defaults. 204 */ 205 public void newPanelDefaults() { 206 setBackgroundColor(Color.WHITE); 207 } 208 209 public void loadFailed() { 210 _loadFailed = true; 211 } 212 213 NamedIcon _newIcon; 214 boolean _ignore = false; 215 boolean _delete; 216 HashMap<String, String> _urlMap = new HashMap<>(); 217 218 public NamedIcon loadFailed(String msg, String url) { 219 log.debug("loadFailed _ignore= {} {}", _ignore, msg); 220 if (_urlMap == null) { 221 _urlMap = new HashMap<>(); 222 } 223 String goodUrl = _urlMap.get(url); 224 if (goodUrl != null) { 225 return NamedIcon.getIconByName(goodUrl); 226 } 227 if (_ignore) { 228 _loadFailed = true; 229 return NamedIcon.getIconByName(url); 230 } 231 _newIcon = null; 232 _delete = false; 233 (new UrlErrorDialog(msg, url)).setVisible(true); 234 235 if (_delete) { 236 return null; 237 } 238 if (_newIcon == null) { 239 _loadFailed = true; 240 _newIcon = NamedIcon.getIconByName(url); 241 } 242 return _newIcon; 243 } 244 245 public class UrlErrorDialog extends JDialog { 246 247 private final JTextField _urlField; 248 private final CatalogPanel _catalog; 249 private final String _badUrl; 250 251 UrlErrorDialog(String msg, String url) { 252 super(_targetFrame, Bundle.getMessage("BadIcon"), true); 253 _badUrl = url; 254 JPanel content = new JPanel(); 255 JPanel panel = new JPanel(); 256 panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); 257 panel.add(Box.createVerticalStrut(10)); 258 panel.add(new JLabel(MessageFormat.format(Bundle.getMessage("IconUrlError"), msg))); 259 panel.add(new JLabel(Bundle.getMessage("UrlErrorPrompt1"))); 260 panel.add(new JLabel(Bundle.getMessage("UrlErrorPrompt1A"))); 261 panel.add(new JLabel(Bundle.getMessage("UrlErrorPrompt1B"))); 262 panel.add(Box.createVerticalStrut(10)); 263 panel.add(new JLabel(Bundle.getMessage("UrlErrorPrompt2", Bundle.getMessage("ButtonContinue")))); 264 panel.add(new JLabel(Bundle.getMessage("UrlErrorPrompt3", Bundle.getMessage("ButtonDelete")))); 265 panel.add(new JLabel(Bundle.getMessage("UrlErrorPrompt3A"))); 266 panel.add(Box.createVerticalStrut(10)); 267 panel.add(new JLabel(Bundle.getMessage("UrlErrorPrompt4", Bundle.getMessage("ButtonIgnore")))); 268 panel.add(Box.createVerticalStrut(10)); 269 _urlField = new JTextField(url); 270 _urlField.setDragEnabled(true); 271 _urlField.setTransferHandler(new DnDStringImportHandler()); 272 panel.add(_urlField); 273 panel.add(makeDoneButtonPanel()); 274 _urlField.setToolTipText(Bundle.getMessage("TooltipFixUrl")); 275 panel.setToolTipText(Bundle.getMessage("TooltipFixUrl")); 276 _catalog = CatalogPanel.makeDefaultCatalog(); 277 _catalog.setToolTipText(Bundle.getMessage("ToolTipDragIconToText")); 278 panel.add(_catalog); 279 content.add(panel); 280 setContentPane(content); 281 setLocation(200, 100); 282 pack(); 283 } 284 285 private JPanel makeDoneButtonPanel() { 286 JPanel result = new JPanel(); 287 result.setLayout(new FlowLayout()); 288 JButton doneButton = new JButton(Bundle.getMessage("ButtonContinue")); 289 doneButton.addActionListener(a -> { 290 _newIcon = NamedIcon.getIconByName(_urlField.getText()); 291 if (_newIcon != null) { 292 _urlMap.put(_badUrl, _urlField.getText()); 293 } 294 UrlErrorDialog.this.dispose(); 295 }); 296 doneButton.setToolTipText(Bundle.getMessage("TooltipContinue")); 297 result.add(doneButton); 298 299 JButton deleteButton = new JButton(Bundle.getMessage("ButtonDelete")); 300 deleteButton.addActionListener(a -> { 301 _delete = true; 302 UrlErrorDialog.this.dispose(); 303 }); 304 result.add(deleteButton); 305 deleteButton.setToolTipText(Bundle.getMessage("TooltipDelete")); 306 307 JButton cancelButton = new JButton(Bundle.getMessage("ButtonIgnore")); 308 cancelButton.addActionListener(a -> { 309 _ignore = true; 310 UrlErrorDialog.this.dispose(); 311 }); 312 result.add(cancelButton); 313 cancelButton.setToolTipText(Bundle.getMessage("TooltipIgnore")); 314 return result; 315 } 316 } 317 318 public void disposeLoadData() { 319 _urlMap = null; 320 } 321 322 public boolean loadOK() { 323 return !_loadFailed; 324 } 325 326 public List<Positionable> getContents() { 327 return Collections.unmodifiableList(_contents); 328 } 329 330 public Map<String, Positionable> getIdContents() { 331 return Collections.unmodifiableMap(_idContents); 332 } 333 334 public Set<String> getClassNames() { 335 return Collections.unmodifiableSet(_classContents.keySet()); 336 } 337 338 public Set<Positionable> getPositionablesByClassName(String className) { 339 Set<Positionable> set = _classContents.get(className); 340 if (set == null) { 341 return null; 342 } 343 return Collections.unmodifiableSet(set); 344 } 345 346 public void setDefaultToolTip(ToolTip dtt) { 347 _defaultToolTip = dtt; 348 } 349 350 // 351 // *************** setting the main panel and frame *************** 352 // 353 /** 354 * Set the target panel. 355 * <p> 356 * An Editor may or may not choose to use 'this' as its frame or the 357 * interior class 'TargetPane' for its targetPanel. 358 * 359 * @param targetPanel the panel to be edited 360 * @param frame the frame to embed the panel in 361 */ 362 protected void setTargetPanel(JLayeredPane targetPanel, JmriJFrame frame) { 363 if (targetPanel == null) { 364 _targetPanel = new TargetPane(); 365 } else { 366 _targetPanel = targetPanel; 367 } 368 // If on a headless system, set heavyweight components to null 369 // and don't attach mouse and keyboard listeners to the panel 370 if (GraphicsEnvironment.isHeadless()) { 371 _panelScrollPane = null; 372 _targetFrame = null; 373 return; 374 } 375 if (frame == null) { 376 _targetFrame = this; 377 } else { 378 _targetFrame = frame; 379 } 380 _targetFrame.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE); 381 _panelScrollPane = new JScrollPane(_targetPanel); 382 Container contentPane = _targetFrame.getContentPane(); 383 contentPane.add(_panelScrollPane); 384 _targetFrame.addWindowListener(new WindowAdapter() { 385 @Override 386 public void windowClosing(WindowEvent e) { 387 targetWindowClosingEvent(e); 388 } 389 }); 390 _targetPanel.addMouseListener(JmriMouseListener.adapt(this)); 391 _targetPanel.addMouseMotionListener(JmriMouseMotionListener.adapt(this)); 392 _targetPanel.setFocusable(true); 393 _targetPanel.addKeyListener(this); 394 //_targetFrame.pack(); 395 } 396 397 protected void setTargetPanelSize(int w, int h) { 398// log.debug("setTargetPanelSize now w={}, h={}", w, h); 399 _targetPanel.setSize(w, h); 400 _targetPanel.invalidate(); 401 } 402 403 protected Dimension getTargetPanelSize() { 404 return _targetPanel.getSize(); 405 } 406 407 /** 408 * Allow public access to the target (content) panel for external 409 * modification, particularly from scripts. 410 * 411 * @return the target panel 412 */ 413 public final JComponent getTargetPanel() { 414 return _targetPanel; 415 } 416 417 /** 418 * Allow public access to the scroll pane for external control of position, 419 * particularly from scripts. 420 * 421 * @return the scroll pane containing the target panel 422 */ 423 public final JScrollPane getPanelScrollPane() { 424 return _panelScrollPane; 425 } 426 427 public final JFrame getTargetFrame() { 428 return _targetFrame; 429 } 430 431 public Color getBackgroundColor() { 432 if (_targetPanel instanceof TargetPane) { 433 TargetPane tmp = (TargetPane) _targetPanel; 434 return tmp.getBackgroundColor(); 435 } else { 436 return null; 437 } 438 } 439 440 public void setBackgroundColor(Color col) { 441 if (_targetPanel instanceof TargetPane) { 442 TargetPane tmp = (TargetPane) _targetPanel; 443 tmp.setBackgroundColor(col); 444 } 445 JmriColorChooser.addRecentColor(col); 446 } 447 448 public void clearBackgroundColor() { 449 if (_targetPanel instanceof TargetPane) { 450 TargetPane tmp = (TargetPane) _targetPanel; 451 tmp.clearBackgroundColor(); 452 } 453 } 454 455 /** 456 * Get scale for TargetPane drawing. 457 * 458 * @return the scale 459 */ 460 public final double getPaintScale() { 461 return _paintScale; 462 } 463 464 protected final void setPaintScale(double newScale) { 465 double ratio = newScale / _paintScale; 466 _paintScale = newScale; 467 setScrollbarScale(ratio); 468 } 469 470 private ToolTipTimer _tooltipTimer; 471 472 protected void setToolTip(ToolTip tt) { 473 if (tt != null) { 474 var pos = tt.getPositionable(); 475 if (pos != null) { // LE turnout tooltips do not have a Positionable 476 if (pos.isHidden() && !isEditable()) { 477 // Skip hidden objects 478 return; 479 } 480 } 481 } 482 483 if (tt == null) { 484 _tooltip = null; 485 if (_tooltipTimer != null) { 486 _tooltipTimer.stop(); 487 _tooltipTimer = null; 488 _targetPanel.repaint(); 489 } 490 491 } else if (_tooltip == null && _tooltipTimer == null) { 492 log.debug("start :: tt = {}, tooltip = {}, timer = {}", tt, _tooltip, _tooltipTimer); 493 _tooltipTimer = new ToolTipTimer(TOOLTIPSHOWDELAY, this, tt); 494 _tooltipTimer.setRepeats(false); 495 _tooltipTimer.start(); 496 } 497 } 498 499 static int TOOLTIPSHOWDELAY = 1000; // msec 500 static int TOOLTIPDISMISSDELAY = 4000; // msec 501 502 /* 503 * Wait TOOLTIPSHOWDELAY then show tooltip. Wait TOOLTIPDISMISSDELAY and 504 * disappear. 505 */ 506 @Override 507 public void actionPerformed(ActionEvent event) { 508 //log.debug("_tooltipTimer actionPerformed: Timer on= {}", (_tooltipTimer!=null)); 509 if (_tooltipTimer != null) { 510 _tooltip = _tooltipTimer.getToolTip(); 511 _tooltipTimer.stop(); 512 } 513 if (_tooltip != null) { 514 _tooltipTimer = new ToolTipTimer(TOOLTIPDISMISSDELAY, this, null); 515 _tooltipTimer.setRepeats(false); 516 _tooltipTimer.start(); 517 } else { 518 _tooltipTimer = null; 519 } 520 _targetPanel.repaint(); 521 } 522 523 static class ToolTipTimer extends Timer { 524 525 private final ToolTip tooltip; 526 527 ToolTipTimer(int delay, ActionListener listener, ToolTip tip) { 528 super(delay, listener); 529 tooltip = tip; 530 } 531 532 ToolTip getToolTip() { 533 return tooltip; 534 } 535 } 536 537 /** 538 * Special internal class to allow drawing of layout to a JLayeredPane. This 539 * is the 'target' pane where the layout is displayed. 540 */ 541 public class TargetPane extends JLayeredPane { 542 543 private int h = 100; 544 private int w = 150; 545 546 public TargetPane() { 547 setLayout(null); 548 } 549 550 @Override 551 public void setSize(int width, int height) { 552// log.debug("size now w={}, h={}", width, height); 553 this.h = height; 554 this.w = width; 555 super.setSize(width, height); 556 } 557 558 @Override 559 public Dimension getSize() { 560 return new Dimension(w, h); 561 } 562 563 @Override 564 public Dimension getPreferredSize() { 565 return new Dimension(w, h); 566 } 567 568 @Override 569 public Dimension getMinimumSize() { 570 return getPreferredSize(); 571 } 572 573 @Override 574 public Dimension getMaximumSize() { 575 return getPreferredSize(); 576 } 577 578 @Override 579 public Component add(@Nonnull Component c, int i) { 580 int hnew = Math.max(this.h, c.getLocation().y + c.getSize().height); 581 int wnew = Math.max(this.w, c.getLocation().x + c.getSize().width); 582 if (hnew > h || wnew > w) { 583// log.debug("size was {},{} - i ={}", w, h, i); 584 setSize(wnew, hnew); 585 } 586 return super.add(c, i); 587 } 588 589 @Override 590 public void add(@Nonnull Component c, Object o) { 591 super.add(c, o); 592 int hnew = Math.max(h, c.getLocation().y + c.getSize().height); 593 int wnew = Math.max(w, c.getLocation().x + c.getSize().width); 594 if (hnew > h || wnew > w) { 595 // log.debug("adding of {} with Object - i=", c.getSize(), o); 596 setSize(wnew, hnew); 597 } 598 } 599 600 private Color _highlightColor = HIGHLIGHT_COLOR; 601 private Color _selectGroupColor = HIGHLIGHT_COLOR; 602 private Color _selectRectColor = Color.red; 603 private transient Stroke _selectRectStroke = DASHED_LINE; 604 605 public void setHighlightColor(Color color) { 606 _highlightColor = color; 607 } 608 609 public Color getHighlightColor() { 610 return _highlightColor; 611 } 612 613 public void setSelectGroupColor(Color color) { 614 _selectGroupColor = color; 615 } 616 617 public void setSelectRectColor(Color color) { 618 _selectRectColor = color; 619 } 620 621 public void setSelectRectStroke(Stroke stroke) { 622 _selectRectStroke = stroke; 623 } 624 625 public void setDefaultColors() { 626 _highlightColor = HIGHLIGHT_COLOR; 627 _selectGroupColor = HIGHLIGHT_COLOR; 628 _selectRectColor = Color.red; 629 _selectRectStroke = DASHED_LINE; 630 } 631 632 @Override 633 public void paint(Graphics g) { 634 Graphics2D g2d = null; 635 if (g instanceof Graphics2D) { 636 g2d = (Graphics2D) g; 637 g2d.scale(_paintScale, _paintScale); 638 } 639 super.paint(g); 640 641 Stroke stroke = new BasicStroke(); 642 if (g2d != null) { 643 stroke = g2d.getStroke(); 644 } 645 Color color = g.getColor(); 646 if (_selectRect != null) { 647 //Draw a rectangle on top of the image. 648 if (g2d != null) { 649 g2d.setStroke(_selectRectStroke); 650 } 651 g.setColor(_selectRectColor); 652 g.drawRect(_selectRect.x, _selectRect.y, _selectRect.width, _selectRect.height); 653 } 654 if (_selectionGroup != null) { 655 g.setColor(_selectGroupColor); 656 if (g2d != null) { 657 g2d.setStroke(new BasicStroke(2.0f)); 658 } 659 for (Positionable p : _selectionGroup) { 660 if (p != null) { 661 if (!(p instanceof PositionableShape)) { 662 g.drawRect(p.getX(), p.getY(), p.maxWidth(), p.maxHeight()); 663 } else { 664 PositionableShape s = (PositionableShape) p; 665 s.drawHandles(); 666 } 667 } 668 } 669 } 670 //Draws a border around the highlighted component 671 if (_highlightcomponent != null) { 672 g.setColor(_highlightColor); 673 if (g2d != null) { 674 g2d.setStroke(new BasicStroke(2.0f)); 675 } 676 g.drawRect(_highlightcomponent.x, _highlightcomponent.y, 677 _highlightcomponent.width, _highlightcomponent.height); 678 } 679 paintTargetPanel(g); 680 681 g.setColor(color); 682 if (g2d != null) { 683 g2d.setStroke(stroke); 684 } 685 if (_tooltip != null) { 686 _tooltip.paint(g2d, _paintScale); 687 } 688 } 689 690 public void setBackgroundColor(Color col) { 691 setBackground(col); 692 setOpaque(true); 693 JmriColorChooser.addRecentColor(col); 694 } 695 696 public void clearBackgroundColor() { 697 setOpaque(false); 698 } 699 700 public Color getBackgroundColor() { 701 if (isOpaque()) { 702 return getBackground(); 703 } 704 return null; 705 } 706 } 707 708 private void setScrollbarScale(double ratio) { 709 //resize the panel to reflect scaling 710 Dimension dim = _targetPanel.getSize(); 711 int tpWidth = (int) ((dim.width) * ratio); 712 int tpHeight = (int) ((dim.height) * ratio); 713 _targetPanel.setSize(tpWidth, tpHeight); 714 log.debug("setScrollbarScale: ratio= {}, tpWidth= {}, tpHeight= {}", ratio, tpWidth, tpHeight); 715 // compute new scroll bar positions to keep upper left same 716 JScrollBar horScroll = _panelScrollPane.getHorizontalScrollBar(); 717 JScrollBar vertScroll = _panelScrollPane.getVerticalScrollBar(); 718 int hScroll = (int) (horScroll.getValue() * ratio); 719 int vScroll = (int) (vertScroll.getValue() * ratio); 720 // set scrollbars maximum range (otherwise setValue may fail); 721 horScroll.setMaximum((int) ((horScroll.getMaximum()) * ratio)); 722 vertScroll.setMaximum((int) ((vertScroll.getMaximum()) * ratio)); 723 // set scroll bar positions 724 horScroll.setValue(hScroll); 725 vertScroll.setValue(vScroll); 726 } 727 728 /* 729 * ********************** Options setup ********************* 730 */ 731 /** 732 * Control whether target panel items are editable. Does this by invoke the 733 * {@link Positionable#setEditable(boolean)} function of each item on the 734 * target panel. This also controls the relevant pop-up menu items (which 735 * are the primary way that items are edited). 736 * 737 * @param state true for editable. 738 */ 739 public void setAllEditable(boolean state) { 740 _editable = state; 741 for (Positionable _content : _contents) { 742 _content.setEditable(state); 743 } 744 if (!_editable) { 745 _highlightcomponent = null; 746 deselectSelectionGroup(); 747 } 748 } 749 750 public void deselectSelectionGroup() { 751 if (_selectionGroup == null) { 752 return; 753 } 754 for (Positionable p : _selectionGroup) { 755 if (p instanceof PositionableShape) { 756 PositionableShape s = (PositionableShape) p; 757 s.removeHandles(); 758 } 759 } 760 _selectionGroup = null; 761 } 762 763 // accessor routines for persistent information 764 public boolean isEditable() { 765 return _editable; 766 } 767 768 /** 769 * Set which flag should be used, global or local for Positioning and 770 * Control of individual items. Items call getFlag() to return the 771 * appropriate flag it should use. 772 * 773 * @param set True if global flags should be used for positioning. 774 */ 775 public void setUseGlobalFlag(boolean set) { 776 _useGlobalFlag = set; 777 } 778 779 public boolean useGlobalFlag() { 780 return _useGlobalFlag; 781 } 782 783 /** 784 * Get the setting for the specified option. 785 * 786 * @param whichOption The option to get 787 * @param localFlag is the current setting of the item 788 * @return The setting for the option 789 */ 790 public boolean getFlag(int whichOption, boolean localFlag) { 791 //log.debug("getFlag Option= {}, _useGlobalFlag={} localFlag={}", whichOption, _useGlobalFlag, localFlag); 792 if (_useGlobalFlag) { 793 switch (whichOption) { 794 case OPTION_POSITION: 795 return _positionable; 796 case OPTION_CONTROLS: 797 return _controlLayout; 798 case OPTION_HIDDEN: 799 return _showHidden; 800 case OPTION_TOOLTIP: 801 return _showToolTip; 802// case OPTION_COORDS: 803// return _showCoordinates; 804 default: 805 log.warn("Unhandled which option code: {}", whichOption); 806 break; 807 } 808 } 809 return localFlag; 810 } 811 812 /** 813 * Set if {@link #setAllControlling(boolean)} and 814 * {@link #setAllPositionable(boolean)} are set for existing as well as new 815 * items. 816 * 817 * @param set true if setAllControlling() and setAllPositionable() are set 818 * for existing items 819 */ 820 public void setGlobalSetsLocalFlag(boolean set) { 821 _globalSetsLocal = set; 822 } 823 824 /** 825 * Control whether panel items can be positioned. Markers can always be 826 * positioned. 827 * 828 * @param state true to set all items positionable; false otherwise 829 */ 830 public void setAllPositionable(boolean state) { 831 _positionable = state; 832 if (_globalSetsLocal) { 833 for (Positionable p : _contents) { 834 // don't allow backgrounds to be set positionable by global flag 835 if (!state || p.getDisplayLevel() != BKG) { 836 p.setPositionable(state); 837 } 838 } 839 } 840 } 841 842 public boolean allPositionable() { 843 return _positionable; 844 } 845 846 /** 847 * Control whether target panel items are controlling layout items. 848 * <p> 849 * Does this by invoking the {@link Positionable#setControlling} function of 850 * each item on the target panel. This also controls the relevant pop-up 851 * menu items. 852 * 853 * @param state true for controlling. 854 */ 855 public void setAllControlling(boolean state) { 856 _controlLayout = state; 857 if (_globalSetsLocal) { 858 for (Positionable _content : _contents) { 859 _content.setControlling(state); 860 } 861 } 862 } 863 864 public boolean allControlling() { 865 return _controlLayout; 866 } 867 868 /** 869 * Control whether target panel hidden items are visible or not. Does this 870 * by invoke the {@link Positionable#setHidden} function of each item on the 871 * target panel. 872 * 873 * @param state true for Visible. 874 */ 875 public void setShowHidden(boolean state) { 876 _showHidden = state; 877 if (_showHidden) { 878 for (Positionable _content : _contents) { 879 _content.setVisible(true); 880 } 881 } else { 882 for (Positionable _content : _contents) { 883 _content.showHidden(); 884 } 885 } 886 } 887 888 public boolean showHidden() { 889 return _showHidden; 890 } 891 892 public void setAllShowToolTip(boolean state) { 893 _showToolTip = state; 894 for (Positionable _content : _contents) { 895 _content.setShowToolTip(state); 896 } 897 } 898 899 public boolean showToolTip() { 900 return _showToolTip; 901 } 902 903 /* 904 * Control whether target panel items will show their coordinates in their 905 * popup menu. 906 * 907 * @param state true for show coordinates. 908 */ 909 /* 910 public void setShowCoordinates(boolean state) { 911 _showCoordinates = state; 912 for (int i = 0; i<_contents.size(); i++) { 913 _contents.get(i).setViewCoordinates(state); 914 } 915 } 916 public boolean showCoordinates() { 917 return _showCoordinates; 918 } 919 */ 920 921 /** 922 * Hide or show menus on the target panel. 923 * 924 * @param state true to show menus; false to hide menus 925 * @since 3.9.5 926 */ 927 public void setPanelMenuVisible(boolean state) { 928 this.panelMenuIsVisible = state; 929 if (!GraphicsEnvironment.isHeadless() && this._targetFrame != null) { 930 _targetFrame.getJMenuBar().setVisible(state); 931 this.revalidate(); 932 } 933 } 934 935 /** 936 * Is the menu on the target panel shown? 937 * 938 * @return true if menu is visible 939 * @since 3.9.5 940 */ 941 public boolean isPanelMenuVisible() { 942 if (!GraphicsEnvironment.isHeadless() && this._targetFrame != null) { 943 this.panelMenuIsVisible = _targetFrame.getJMenuBar().isVisible(); 944 } 945 return this.panelMenuIsVisible; 946 } 947 948 protected void setScroll(int state) { 949 log.debug("setScroll {}", state); 950 switch (state) { 951 case SCROLL_NONE: 952 _panelScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER); 953 _panelScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); 954 break; 955 case SCROLL_BOTH: 956 _panelScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); 957 _panelScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS); 958 break; 959 case SCROLL_HORIZONTAL: 960 _panelScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER); 961 _panelScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS); 962 break; 963 case SCROLL_VERTICAL: 964 _panelScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); 965 _panelScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); 966 break; 967 default: 968 log.warn("Unexpected setScroll state of {}", state); 969 break; 970 } 971 _scrollState = state; 972 } 973 974 public void setScroll(String strState) { 975 int state = SCROLL_BOTH; 976 if (strState.equalsIgnoreCase("none") || strState.equalsIgnoreCase("no")) { 977 state = SCROLL_NONE; 978 } else if (strState.equals("horizontal")) { 979 state = SCROLL_HORIZONTAL; 980 } else if (strState.equals("vertical")) { 981 state = SCROLL_VERTICAL; 982 } 983 log.debug("setScroll: strState= {}, state= {}", strState, state); 984 setScroll(state); 985 } 986 987 public String getScrollable() { 988 String value = ""; 989 switch (_scrollState) { 990 case SCROLL_NONE: 991 value = "none"; 992 break; 993 case SCROLL_BOTH: 994 value = "both"; 995 break; 996 case SCROLL_HORIZONTAL: 997 value = "horizontal"; 998 break; 999 case SCROLL_VERTICAL: 1000 value = "vertical"; 1001 break; 1002 default: 1003 log.warn("Unexpected _scrollState of {}", _scrollState); 1004 break; 1005 } 1006 return value; 1007 } 1008 /* 1009 * *********************** end Options setup ********************** 1010 */ 1011 /* 1012 * Handle closing (actually hiding due to HIDE_ON_CLOSE) the target window. 1013 * <p> 1014 * The target window has been requested to close, don't delete it at this 1015 * time. Deletion must be accomplished via the Delete this panel menu item. 1016 */ 1017 protected void targetWindowClosing() { 1018 String name = _targetFrame.getTitle(); 1019 if (!InstanceManager.getDefault(ShutDownManager.class).isShuttingDown()) { 1020 InstanceManager.getDefault(UserPreferencesManager.class).showInfoMessage( 1021 Bundle.getMessage("PanelHideTitle"), Bundle.getMessage("PanelHideNotice", name), // NOI18N 1022 "jmri.jmrit.display.EditorManager", "skipHideDialog"); // NOI18N 1023 InstanceManager.getDefault(UserPreferencesManager.class).setPreferenceItemDetails( 1024 "jmri.jmrit.display.EditorManager", "skipHideDialog", Bundle.getMessage("PanelHideSkip")); // NOI18N 1025 } 1026 } 1027 1028 protected Editor changeView(String className) { 1029 JFrame frame = getTargetFrame(); 1030 1031 try { 1032 Editor ed = (Editor) Class.forName(className).getDeclaredConstructor().newInstance(); 1033 1034 ed.setName(getName()); 1035 ed.init(getName()); 1036 1037 ed._contents = new ArrayList<>(_contents); 1038 ed._idContents = new HashMap<>(_idContents); 1039 ed._classContents = new HashMap<>(_classContents); 1040 1041 for (Positionable p : _contents) { 1042 p.setEditor(ed); 1043 ed.addToTarget(p); 1044 if (log.isDebugEnabled()) { 1045 log.debug("changeView: {} addToTarget class= {}", p.getNameString(), p.getClass().getName()); 1046 } 1047 } 1048 ed.setAllEditable(isEditable()); 1049 //ed.setAllPositionable(allPositionable()); 1050 //ed.setShowCoordinates(showCoordinates()); 1051 ed.setAllShowToolTip(showToolTip()); 1052 //ed.setAllControlling(allControlling()); 1053 ed.setShowHidden(isVisible()); 1054 ed.setPanelMenuVisible(frame.getJMenuBar().isVisible()); 1055 ed.setScroll(getScrollable()); 1056 ed.setTitle(); 1057 ed.setBackgroundColor(getBackgroundColor()); 1058 ed.getTargetFrame().setLocation(frame.getLocation()); 1059 ed.getTargetFrame().setSize(frame.getSize()); 1060 ed.setSize(getSize()); 1061// ed.pack(); 1062 ed.setVisible(true); 1063 dispose(); 1064 InstanceManager.getDefault(EditorManager.class).add(ed); 1065 return ed; 1066 } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException cnfe) { 1067 log.error("changeView exception {}", cnfe.toString()); 1068 } 1069 return null; 1070 } 1071 1072 /* 1073 * *********************** Popup Item Methods ********************** 1074 * 1075 * These methods are to be called from the editor view's showPopUp method 1076 */ 1077 /** 1078 * Add a checkbox to lock the position of the Positionable item. 1079 * 1080 * @param p the item 1081 * @param popup the menu to add the lock menu item to 1082 */ 1083 public void setPositionableMenu(Positionable p, JPopupMenu popup) { 1084 JCheckBoxMenuItem lockItem = new JCheckBoxMenuItem(Bundle.getMessage("LockPosition")); 1085 lockItem.setSelected(!p.isPositionable()); 1086 lockItem.addActionListener(new ActionListener() { 1087 private Positionable comp; 1088 private JCheckBoxMenuItem checkBox; 1089 1090 @Override 1091 public void actionPerformed(ActionEvent e) { 1092 comp.setPositionable(!checkBox.isSelected()); 1093 setSelectionsPositionable(!checkBox.isSelected(), comp); 1094 } 1095 1096 ActionListener init(Positionable pos, JCheckBoxMenuItem cb) { 1097 comp = pos; 1098 checkBox = cb; 1099 return this; 1100 } 1101 }.init(p, lockItem)); 1102 popup.add(lockItem); 1103 } 1104 1105 /** 1106 * Display the {@literal X & Y} coordinates of the Positionable item and 1107 * provide a dialog menu item to edit them. 1108 * 1109 * @param p The item to add the menu item to 1110 * @param popup The menu item to add the action to 1111 * @return always returns true 1112 */ 1113 public boolean setShowCoordinatesMenu(Positionable p, JPopupMenu popup) { 1114 1115 JMenuItem edit; 1116 if ((p instanceof MemoryOrGVIcon) && (p.getPopupUtility().getFixedWidth() == 0)) { 1117 MemoryOrGVIcon pm = (MemoryOrGVIcon) p; 1118 1119 edit = new JMenuItem(Bundle.getMessage( 1120 "EditLocationXY", pm.getOriginalX(), pm.getOriginalY())); 1121 1122 edit.addActionListener(MemoryIconCoordinateEdit.getCoordinateEditAction(pm)); 1123 } else { 1124 edit = new JMenuItem(Bundle.getMessage( 1125 "EditLocationXY", p.getX(), p.getY())); 1126 edit.addActionListener(CoordinateEdit.getCoordinateEditAction(p)); 1127 } 1128 popup.add(edit); 1129 return true; 1130 } 1131 1132 /** 1133 * Offer actions to align the selected Positionable items either 1134 * Horizontally (at average y coordinates) or Vertically (at average x 1135 * coordinates). 1136 * 1137 * @param p The positionable item 1138 * @param popup The menu to add entries to 1139 * @return true if entries added to menu 1140 */ 1141 public boolean setShowAlignmentMenu(Positionable p, JPopupMenu popup) { 1142 if (showAlignPopup(p)) { 1143 JMenu edit = new JMenu(Bundle.getMessage("EditAlignment")); 1144 edit.add(new AbstractAction(Bundle.getMessage("AlignX")) { 1145 private int _x; 1146 1147 @Override 1148 public void actionPerformed(ActionEvent e) { 1149 if (_selectionGroup == null) { 1150 return; 1151 } 1152 for (Positionable comp : _selectionGroup) { 1153 if (!getFlag(OPTION_POSITION, comp.isPositionable())) { 1154 continue; 1155 } 1156 comp.setLocation(_x, comp.getY()); 1157 } 1158 } 1159 1160 AbstractAction init(int x) { 1161 _x = x; 1162 return this; 1163 } 1164 }.init(p.getX())); 1165 edit.add(new AbstractAction(Bundle.getMessage("AlignMiddleX")) { 1166 private int _x; 1167 1168 @Override 1169 public void actionPerformed(ActionEvent e) { 1170 if (_selectionGroup == null) { 1171 return; 1172 } 1173 for (Positionable comp : _selectionGroup) { 1174 if (!getFlag(OPTION_POSITION, comp.isPositionable())) { 1175 continue; 1176 } 1177 comp.setLocation(_x - comp.getWidth() / 2, comp.getY()); 1178 } 1179 } 1180 1181 AbstractAction init(int x) { 1182 _x = x; 1183 return this; 1184 } 1185 }.init(p.getX() + p.getWidth() / 2)); 1186 edit.add(new AbstractAction(Bundle.getMessage("AlignOtherX")) { 1187 private int _x; 1188 1189 @Override 1190 public void actionPerformed(ActionEvent e) { 1191 if (_selectionGroup == null) { 1192 return; 1193 } 1194 for (Positionable comp : _selectionGroup) { 1195 if (!getFlag(OPTION_POSITION, comp.isPositionable())) { 1196 continue; 1197 } 1198 comp.setLocation(_x - comp.getWidth(), comp.getY()); 1199 } 1200 } 1201 1202 AbstractAction init(int x) { 1203 _x = x; 1204 return this; 1205 } 1206 }.init(p.getX() + p.getWidth())); 1207 edit.add(new AbstractAction(Bundle.getMessage("AlignY")) { 1208 private int _y; 1209 1210 @Override 1211 public void actionPerformed(ActionEvent e) { 1212 if (_selectionGroup == null) { 1213 return; 1214 } 1215 for (Positionable comp : _selectionGroup) { 1216 if (!getFlag(OPTION_POSITION, comp.isPositionable())) { 1217 continue; 1218 } 1219 comp.setLocation(comp.getX(), _y); 1220 } 1221 } 1222 1223 AbstractAction init(int y) { 1224 _y = y; 1225 return this; 1226 } 1227 }.init(p.getY())); 1228 edit.add(new AbstractAction(Bundle.getMessage("AlignMiddleY")) { 1229 private int _y; 1230 1231 @Override 1232 public void actionPerformed(ActionEvent e) { 1233 if (_selectionGroup == null) { 1234 return; 1235 } 1236 for (Positionable comp : _selectionGroup) { 1237 if (!getFlag(OPTION_POSITION, comp.isPositionable())) { 1238 continue; 1239 } 1240 comp.setLocation(comp.getX(), _y - comp.getHeight() / 2); 1241 } 1242 } 1243 1244 AbstractAction init(int y) { 1245 _y = y; 1246 return this; 1247 } 1248 }.init(p.getY() + p.getHeight() / 2)); 1249 edit.add(new AbstractAction(Bundle.getMessage("AlignOtherY")) { 1250 private int _y; 1251 1252 @Override 1253 public void actionPerformed(ActionEvent e) { 1254 if (_selectionGroup == null) { 1255 return; 1256 } 1257 for (Positionable comp : _selectionGroup) { 1258 if (!getFlag(OPTION_POSITION, comp.isPositionable())) { 1259 continue; 1260 } 1261 comp.setLocation(comp.getX(), _y - comp.getHeight()); 1262 } 1263 } 1264 1265 AbstractAction init(int y) { 1266 _y = y; 1267 return this; 1268 } 1269 }.init(p.getY() + p.getHeight())); 1270 edit.add(new AbstractAction(Bundle.getMessage("AlignXFirst")) { 1271 1272 @Override 1273 public void actionPerformed(ActionEvent e) { 1274 if (_selectionGroup == null) { 1275 return; 1276 } 1277 int x = _selectionGroup.get(0).getX(); 1278 for (int i = 1; i < _selectionGroup.size(); i++) { 1279 Positionable comp = _selectionGroup.get(i); 1280 if (!getFlag(OPTION_POSITION, comp.isPositionable())) { 1281 continue; 1282 } 1283 comp.setLocation(x, comp.getY()); 1284 } 1285 } 1286 }); 1287 edit.add(new AbstractAction(Bundle.getMessage("AlignYFirst")) { 1288 1289 @Override 1290 public void actionPerformed(ActionEvent e) { 1291 if (_selectionGroup == null) { 1292 return; 1293 } 1294 int y = _selectionGroup.get(0).getX(); 1295 for (int i = 1; i < _selectionGroup.size(); i++) { 1296 Positionable comp = _selectionGroup.get(i); 1297 if (!getFlag(OPTION_POSITION, comp.isPositionable())) { 1298 continue; 1299 } 1300 comp.setLocation(comp.getX(), y); 1301 } 1302 } 1303 }); 1304 popup.add(edit); 1305 return true; 1306 } 1307 return false; 1308 } 1309 1310 /** 1311 * Display 'z' level of the Positionable item and provide a dialog 1312 * menu item to edit it. 1313 * 1314 * @param p The item 1315 * @param popup the menu to add entries to 1316 */ 1317 public void setDisplayLevelMenu(Positionable p, JPopupMenu popup) { 1318 JMenuItem edit = new JMenuItem(Bundle.getMessage("EditLevel_", p.getDisplayLevel())); 1319 edit.addActionListener(CoordinateEdit.getLevelEditAction(p)); 1320 popup.add(edit); 1321 } 1322 1323 /** 1324 * Add a menu entry to set visibility of the Positionable item 1325 * 1326 * @param p the item 1327 * @param popup the menu to add the entry to 1328 */ 1329 public void setHiddenMenu(Positionable p, JPopupMenu popup) { 1330 if (p.getDisplayLevel() == BKG) { 1331 return; 1332 } 1333 JCheckBoxMenuItem hideItem = new JCheckBoxMenuItem(Bundle.getMessage("SetHidden")); 1334 hideItem.setSelected(p.isHidden()); 1335 hideItem.addActionListener(new ActionListener() { 1336 private Positionable comp; 1337 private JCheckBoxMenuItem checkBox; 1338 1339 @Override 1340 public void actionPerformed(ActionEvent e) { 1341 comp.setHidden(checkBox.isSelected()); 1342 setSelectionsHidden(checkBox.isSelected(), comp); 1343 } 1344 1345 ActionListener init(Positionable pos, JCheckBoxMenuItem cb) { 1346 comp = pos; 1347 checkBox = cb; 1348 return this; 1349 } 1350 }.init(p, hideItem)); 1351 popup.add(hideItem); 1352 } 1353 1354 /** 1355 * Add a menu entry to set visibility of the Positionable item based on the presence of contents. 1356 * If the value is null or empty, the icon is not visible. 1357 * This is applicable to memory, block content and LogixNG global variable labels. 1358 * 1359 * @param p the item 1360 * @param popup the menu to add the entry to 1361 */ 1362 public void setEmptyHiddenMenu(Positionable p, JPopupMenu popup) { 1363 if (p.getDisplayLevel() == BKG) { 1364 return; 1365 } 1366 if (p instanceof BlockContentsIcon || p instanceof MemoryIcon || p instanceof GlobalVariableIcon) { 1367 JCheckBoxMenuItem hideEmptyItem = new JCheckBoxMenuItem(Bundle.getMessage("SetEmptyHidden")); 1368 hideEmptyItem.setSelected(p.isEmptyHidden()); 1369 hideEmptyItem.addActionListener(new ActionListener() { 1370 private Positionable comp; 1371 private JCheckBoxMenuItem checkBox; 1372 1373 @Override 1374 public void actionPerformed(ActionEvent e) { 1375 comp.setEmptyHidden(checkBox.isSelected()); 1376 } 1377 1378 ActionListener init(Positionable pos, JCheckBoxMenuItem cb) { 1379 comp = pos; 1380 checkBox = cb; 1381 return this; 1382 } 1383 }.init(p, hideEmptyItem)); 1384 popup.add(hideEmptyItem); 1385 } 1386 } 1387 1388 /** 1389 * Add a menu entry to disable double click value edits. This applies when not in panel edit mode. 1390 * This is applicable to memory, block content and LogixNG global variable labels. 1391 * 1392 * @param p the item 1393 * @param popup the menu to add the entry to 1394 */ 1395 public void setValueEditDisabledMenu(Positionable p, JPopupMenu popup) { 1396 if (p.getDisplayLevel() == BKG) { 1397 return; 1398 } 1399 if (p instanceof BlockContentsIcon || p instanceof MemoryIcon || p instanceof GlobalVariableIcon) { 1400 JCheckBoxMenuItem valueEditDisableItem = new JCheckBoxMenuItem(Bundle.getMessage("SetValueEditDisabled")); 1401 valueEditDisableItem.setSelected(p.isValueEditDisabled()); 1402 valueEditDisableItem.addActionListener(new ActionListener() { 1403 private Positionable comp; 1404 private JCheckBoxMenuItem checkBox; 1405 1406 @Override 1407 public void actionPerformed(ActionEvent e) { 1408 comp.setValueEditDisabled(checkBox.isSelected()); 1409 } 1410 1411 ActionListener init(Positionable pos, JCheckBoxMenuItem cb) { 1412 comp = pos; 1413 checkBox = cb; 1414 return this; 1415 } 1416 }.init(p, valueEditDisableItem)); 1417 popup.add(valueEditDisableItem); 1418 } 1419 } 1420 1421 /** 1422 * Add a menu entry to edit Id of the Positionable item 1423 * 1424 * @param p the item 1425 * @param popup the menu to add the entry to 1426 */ 1427 public void setEditIdMenu(Positionable p, JPopupMenu popup) { 1428 if (p.getDisplayLevel() == BKG) { 1429 return; 1430 } 1431 1432 popup.add(CoordinateEdit.getIdEditAction(p, "EditId", this)); 1433 } 1434 1435 /** 1436 * Add a menu entry to edit Classes of the Positionable item 1437 * 1438 * @param p the item 1439 * @param popup the menu to add the entry to 1440 */ 1441 public void setEditClassesMenu(Positionable p, JPopupMenu popup) { 1442 if (p.getDisplayLevel() == BKG) { 1443 return; 1444 } 1445 1446 popup.add(CoordinateEdit.getClassesEditAction(p, "EditClasses", this)); 1447 } 1448 1449 /** 1450 * Check if edit of a conditional is in progress. 1451 * 1452 * @return true if this is the case, after showing dialog to user 1453 */ 1454 private boolean checkEditConditionalNG() { 1455 if (_inEditInlineLogixNGMode) { 1456 // Already editing a LogixNG, ask for completion of that edit 1457 JmriJOptionPane.showMessageDialog(null, 1458 Bundle.getMessage("Error_InlineLogixNGInEditMode"), // NOI18N 1459 Bundle.getMessage("ErrorTitle"), // NOI18N 1460 JmriJOptionPane.ERROR_MESSAGE); 1461 _inlineLogixNGEdit.bringToFront(); 1462 return true; 1463 } 1464 return false; 1465 } 1466 1467 /** 1468 * Add a menu entry to edit Id of the Positionable item 1469 * 1470 * @param p the item 1471 * @param popup the menu to add the entry to 1472 */ 1473 public void setLogixNGPositionableMenu(Positionable p, JPopupMenu popup) { 1474 if (p.getDisplayLevel() == BKG) { 1475 return; 1476 } 1477 1478 JMenu logixNG_Menu = new JMenu("LogixNG"); 1479 popup.add(logixNG_Menu); 1480 1481 logixNG_Menu.add(new AbstractAction(Bundle.getMessage("LogixNG_Inline")) { 1482 @Override 1483 public void actionPerformed(ActionEvent e) { 1484 if (checkEditConditionalNG()) { 1485 return; 1486 } 1487 1488 if (p.getLogixNG() == null) { 1489 LogixNG logixNG = InstanceManager.getDefault(LogixNG_Manager.class) 1490 .createLogixNG(null, true); 1491 logixNG.setInlineLogixNG(p); 1492 logixNG.activate(); 1493 logixNG.setEnabled(true); 1494 logixNG.clearStartup(); 1495 p.setLogixNG(logixNG); 1496 } 1497 LogixNGEditor logixNGEditor = new LogixNGEditor(null, p.getLogixNG().getSystemName()); 1498 logixNGEditor.addEditorEventListener((HashMap<String, String> data) -> { 1499 _inEditInlineLogixNGMode = false; 1500 data.forEach((key, value) -> { 1501 if (key.equals("Finish")) { // NOI18N 1502 _inlineLogixNGEdit = null; 1503 _inEditInlineLogixNGMode = false; 1504 } else if (key.equals("Delete")) { // NOI18N 1505 _inEditInlineLogixNGMode = false; 1506 deleteLogixNG(p.getLogixNG()); 1507 } else if (key.equals("chgUname")) { // NOI18N 1508 p.getLogixNG().setUserName(value); 1509 } 1510 }); 1511 if (p.getLogixNG() != null && p.getLogixNG().getNumConditionalNGs() == 0) { 1512 deleteLogixNG_Internal(p.getLogixNG()); 1513 } 1514 }); 1515 logixNGEditor.bringToFront(); 1516 _inEditInlineLogixNGMode = true; 1517 _inlineLogixNGEdit = logixNGEditor; 1518 } 1519 }); 1520 } 1521 1522 private void deleteLogixNG(LogixNG logixNG) { 1523 DeleteBean<LogixNG> deleteBean = new DeleteBean<>( 1524 InstanceManager.getDefault(LogixNG_Manager.class)); 1525 1526 boolean hasChildren = logixNG.getNumConditionalNGs() > 0; 1527 1528 deleteBean.delete(logixNG, hasChildren, t -> deleteLogixNG_Internal(t), 1529 (t,list) -> logixNG.getListenerRefsIncludingChildren(list), 1530 jmri.jmrit.logixng.LogixNG_UserPreferences.class.getName()); 1531 } 1532 1533 private void deleteLogixNG_Internal(LogixNG logixNG) { 1534 logixNG.setEnabled(false); 1535 try { 1536 InstanceManager.getDefault(LogixNG_Manager.class).deleteBean(logixNG, "DoDelete"); 1537 logixNG.getInlineLogixNG().setLogixNG(null); 1538 } catch (PropertyVetoException e) { 1539 //At this stage the DoDelete shouldn't fail, as we have already done a can delete, which would trigger a veto 1540 log.error("{} : Could not Delete.", e.getMessage()); 1541 } 1542 } 1543 1544 /** 1545 * Check if it's possible to change the id of the Positionable to the 1546 * desired string. 1547 * @param p the Positionable 1548 * @param newId the desired new id 1549 * @throws jmri.jmrit.display.Positionable.DuplicateIdException if another 1550 * Positionable in the editor already has this id 1551 */ 1552 public void positionalIdChange(Positionable p, String newId) 1553 throws Positionable.DuplicateIdException { 1554 1555 if (Objects.equals(newId, p.getId())) { 1556 return; 1557 } 1558 1559 if ((newId != null) && (_idContents.containsKey(newId))) { 1560 throw new Positionable.DuplicateIdException(); 1561 } 1562 1563 if (p.getId() != null) { 1564 _idContents.remove(p.getId()); 1565 } 1566 if (newId != null) { 1567 _idContents.put(newId, p); 1568 } 1569 } 1570 1571 /** 1572 * Add a class name to the Positionable 1573 * @param p the Positionable 1574 * @param className the class name 1575 * @throws IllegalArgumentException if the name contains a comma 1576 */ 1577 public void positionalAddClass(Positionable p, String className) { 1578 1579 if (className == null) { 1580 throw new IllegalArgumentException("Class name must not be null"); 1581 } 1582 if (className.isBlank()) { 1583 throw new IllegalArgumentException("Class name must not be blank"); 1584 } 1585 if (className.contains(",")) { 1586 throw new IllegalArgumentException("Class name must not contain a comma"); 1587 } 1588 1589 if (p.getClasses().contains(className)) { 1590 return; 1591 } 1592 1593 _classContents.computeIfAbsent(className, o -> new HashSet<>()).add(p); 1594 } 1595 1596 /** 1597 * Removes a class name from the Positionable 1598 * @param p the Positionable 1599 * @param className the class name 1600 */ 1601 public void positionalRemoveClass(Positionable p, String className) { 1602 1603 if (p.getClasses().contains(className)) { 1604 return; 1605 } 1606 _classContents.get(className).remove(p); 1607 } 1608 1609 /** 1610 * Add a checkbox to display a tooltip for the Positionable item and if 1611 * showable, provide a dialog menu to edit it. 1612 * 1613 * @param p the item to set the menu for 1614 * @param popup the menu to add for p 1615 */ 1616 public void setShowToolTipMenu(Positionable p, JPopupMenu popup) { 1617 if (p.getDisplayLevel() == BKG) { 1618 return; 1619 } 1620 1621 JMenu edit = new JMenu(Bundle.getMessage("EditTooltip")); 1622 1623 JCheckBoxMenuItem showToolTipItem = new JCheckBoxMenuItem(Bundle.getMessage("ShowTooltip")); 1624 showToolTipItem.setSelected(p.showToolTip()); 1625 showToolTipItem.addActionListener(new ActionListener() { 1626 private Positionable comp; 1627 private JCheckBoxMenuItem checkBox; 1628 1629 @Override 1630 public void actionPerformed(ActionEvent e) { 1631 comp.setShowToolTip(checkBox.isSelected()); 1632 } 1633 1634 ActionListener init(Positionable pos, JCheckBoxMenuItem cb) { 1635 comp = pos; 1636 checkBox = cb; 1637 return this; 1638 } 1639 }.init(p, showToolTipItem)); 1640 edit.add(showToolTipItem); 1641 1642 edit.add(CoordinateEdit.getToolTipEditAction(p)); 1643 1644 JCheckBoxMenuItem prependToolTipWithDisplayNameItem = 1645 new JCheckBoxMenuItem(Bundle.getMessage("PrependTooltipWithDisplayName")); 1646 prependToolTipWithDisplayNameItem.setSelected(p.getToolTip().getPrependToolTipWithDisplayName()); 1647 prependToolTipWithDisplayNameItem.addActionListener(new ActionListener() { 1648 private Positionable comp; 1649 private JCheckBoxMenuItem checkBox; 1650 1651 @Override 1652 public void actionPerformed(ActionEvent e) { 1653 comp.getToolTip().setPrependToolTipWithDisplayName(checkBox.isSelected()); 1654 } 1655 1656 ActionListener init(Positionable pos, JCheckBoxMenuItem cb) { 1657 comp = pos; 1658 checkBox = cb; 1659 return this; 1660 } 1661 }.init(p, prependToolTipWithDisplayNameItem)); 1662 edit.add(prependToolTipWithDisplayNameItem); 1663 1664 popup.add(edit); 1665 } 1666 1667 /** 1668 * Add an action to remove the Positionable item. 1669 * 1670 * @param p the item to set the menu for 1671 * @param popup the menu to add for p 1672 */ 1673 public void setRemoveMenu(Positionable p, JPopupMenu popup) { 1674 popup.add(new AbstractAction(Bundle.getMessage("Remove")) { 1675 private Positionable comp; 1676 1677 @Override 1678 public void actionPerformed(ActionEvent e) { 1679 comp.remove(); 1680 removeSelections(comp); 1681 } 1682 1683 AbstractAction init(Positionable pos) { 1684 comp = pos; 1685 return this; 1686 } 1687 }.init(p)); 1688 } 1689 1690 /* 1691 * *********************** End Popup Methods ********************** 1692 */ 1693 /* 1694 * ****************** Marker Menu *************************** 1695 */ 1696 protected void locoMarkerFromRoster() { 1697 final JmriJFrame locoRosterFrame = new JmriJFrame(); 1698 locoRosterFrame.getContentPane().setLayout(new FlowLayout()); 1699 locoRosterFrame.setTitle(Bundle.getMessage("LocoFromRoster")); 1700 JLabel mtext = new JLabel(); 1701 mtext.setText(Bundle.getMessage("SelectLoco") + ":"); 1702 locoRosterFrame.getContentPane().add(mtext); 1703 final RosterEntrySelectorPanel rosterBox = new RosterEntrySelectorPanel(); 1704 rosterBox.addPropertyChangeListener("selectedRosterEntries", pce -> { 1705 if (rosterBox.getSelectedRosterEntries().length != 0) { 1706 selectLoco(rosterBox.getSelectedRosterEntries()[0]); 1707 } 1708 }); 1709 locoRosterFrame.getContentPane().add(rosterBox); 1710 locoRosterFrame.addWindowListener(new WindowAdapter() { 1711 @Override 1712 public void windowClosing(WindowEvent e) { 1713 locoRosterFrame.dispose(); 1714 } 1715 }); 1716 locoRosterFrame.pack(); 1717 locoRosterFrame.setVisible(true); 1718 } 1719 1720 protected LocoIcon selectLoco(String rosterEntryTitle) { 1721 if ("".equals(rosterEntryTitle)) { 1722 return null; 1723 } 1724 return selectLoco(Roster.getDefault().entryFromTitle(rosterEntryTitle)); 1725 } 1726 1727 protected LocoIcon selectLoco(RosterEntry entry) { 1728 LocoIcon l = null; 1729 if (entry == null) { 1730 return null; 1731 } 1732 // try getting road number, else use DCC address 1733 String rn = entry.getRoadNumber(); 1734 if ((rn == null) || rn.equals("")) { 1735 rn = entry.getDccAddress(); 1736 } 1737 if (rn != null) { 1738 l = addLocoIcon(rn); 1739 l.setRosterEntry(entry); 1740 } 1741 return l; 1742 } 1743 1744 protected void locoMarkerFromInput() { 1745 final JmriJFrame locoFrame = new JmriJFrame(); 1746 locoFrame.getContentPane().setLayout(new FlowLayout()); 1747 locoFrame.setTitle(Bundle.getMessage("EnterLocoMarker")); 1748 1749 JLabel textId = new JLabel(); 1750 textId.setText(Bundle.getMessage("LocoID") + ":"); 1751 locoFrame.getContentPane().add(textId); 1752 1753 final JTextField locoId = new JTextField(7); 1754 locoFrame.getContentPane().add(locoId); 1755 locoId.setText(""); 1756 locoId.setToolTipText(Bundle.getMessage("EnterLocoID")); 1757 JButton okay = new JButton(); 1758 okay.setText(Bundle.getMessage("ButtonOK")); 1759 okay.addActionListener(e -> { 1760 String nameID = locoId.getText(); 1761 if ((nameID != null) && !(nameID.trim().equals(""))) { 1762 addLocoIcon(nameID.trim()); 1763 } else { 1764 JmriJOptionPane.showMessageDialog(locoFrame, Bundle.getMessage("ErrorEnterLocoID"), 1765 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 1766 } 1767 }); 1768 locoFrame.getContentPane().add(okay); 1769 locoFrame.addWindowListener(new WindowAdapter() { 1770 @Override 1771 public void windowClosing(WindowEvent e) { 1772 locoFrame.dispose(); 1773 } 1774 }); 1775 locoFrame.pack(); 1776 if (_targetFrame != null) { 1777 locoFrame.setLocation(_targetFrame.getLocation()); 1778 } 1779 locoFrame.setVisible(true); 1780 } 1781 1782 /** 1783 * Remove marker icons from panel 1784 */ 1785 protected void removeMarkers() { 1786 log.debug("Remove markers"); 1787 for (int i = _contents.size() - 1; i >= 0; i--) { 1788 Positionable il = _contents.get(i); 1789 if (il instanceof LocoIcon) { 1790 il.remove(); 1791 if (il.getId() != null) { 1792 _idContents.remove(il.getId()); 1793 } 1794 for (String className : il.getClasses()) { 1795 _classContents.get(className).remove(il); 1796 } 1797 } 1798 } 1799 } 1800 1801 /* 1802 * *********************** End Marker Menu Methods ********************** 1803 */ 1804 /* 1805 * ************ Adding content to the panel ********************** 1806 */ 1807 public PositionableLabel setUpBackground(String name) { 1808 NamedIcon icon = NamedIcon.getIconByName(name); 1809 PositionableLabel l = new PositionableLabel(icon, this); 1810 l.setPopupUtility(null); // no text 1811 l.setPositionable(false); 1812 l.setShowToolTip(false); 1813 l.setSize(icon.getIconWidth(), icon.getIconHeight()); 1814 l.setDisplayLevel(BKG); 1815 l.setLocation(getNextBackgroundLeft(), 0); 1816 try { 1817 putItem(l); 1818 } catch (Positionable.DuplicateIdException e) { 1819 // This should never happen 1820 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 1821 } 1822 return l; 1823 } 1824 1825 protected PositionableLabel addLabel(String text) { 1826 PositionableLabel l = new PositionableLabel(text, this); 1827 l.setSize(l.getPreferredSize().width, l.getPreferredSize().height); 1828 l.setDisplayLevel(LABELS); 1829 setNextLocation(l); 1830 try { 1831 putItem(l); 1832 } catch (Positionable.DuplicateIdException e) { 1833 // This should never happen 1834 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 1835 } 1836 return l; 1837 } 1838 1839 /** 1840 * Determine right side x of furthest right background 1841 */ 1842 private int getNextBackgroundLeft() { 1843 int left = 0; 1844 // place to right of background images, if any 1845 for (Positionable p : _contents) { 1846 if (p instanceof PositionableLabel) { 1847 PositionableLabel l = (PositionableLabel) p; 1848 if (l.isBackground()) { 1849 int test = l.getX() + l.maxWidth(); 1850 if (test > left) { 1851 left = test; 1852 } 1853 } 1854 } 1855 } 1856 return left; 1857 } 1858 1859 /** 1860 * Positionable has set a new level. 1861 * Editor must change it in the target panel. 1862 * @param l the positionable to display. 1863 */ 1864 public void displayLevelChange(Positionable l) { 1865 ThreadingUtil.runOnGUI( () -> { 1866 removeFromTarget(l); 1867 addToTarget(l); 1868 }); 1869 } 1870 1871 public TrainIcon addTrainIcon(String name) { 1872 TrainIcon l = new TrainIcon(this); 1873 putLocoIcon(l, name); 1874 return l; 1875 } 1876 1877 public LocoIcon addLocoIcon(String name) { 1878 LocoIcon l = new LocoIcon(this); 1879 putLocoIcon(l, name); 1880 return l; 1881 } 1882 1883 public void putLocoIcon(LocoIcon l, String name) { 1884 l.setText(name); 1885 l.setHorizontalTextPosition(SwingConstants.CENTER); 1886 l.setSize(l.getPreferredSize().width, l.getPreferredSize().height); 1887 l.setEditable(isEditable()); // match popup mode to editor mode 1888 l.setLocation(75, 75); // fixed location 1889 try { 1890 putItem(l); 1891 } catch (Positionable.DuplicateIdException e) { 1892 // This should never happen 1893 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 1894 } 1895 } 1896 1897 public void putItem(@Nonnull Positionable l) throws Positionable.DuplicateIdException { 1898 ThreadingUtil.runOnGUI( () -> { 1899 l.invalidate(); 1900 l.setPositionable(true); 1901 l.setVisible(true); 1902 if (l.getToolTip() == null) { 1903 l.setToolTip(new ToolTip(_defaultToolTip, l)); 1904 } 1905 addToTarget(l); 1906 }); 1907 if (!_contents.add(l)) { 1908 log.error("Unable to add {} to _contents", l.getNameString()); 1909 } 1910 if (l.getId() != null) { 1911 if (_idContents.containsKey(l.getId())) { 1912 throw new Positionable.DuplicateIdException(); 1913 } 1914 _idContents.put(l.getId(), l); 1915 } 1916 for (String className : l.getClasses()) { 1917 _classContents.get(className).add(l); 1918 } 1919 if (log.isDebugEnabled()) { 1920 log.debug("putItem {} to _contents. level= {}", l.getNameString(), l.getDisplayLevel()); 1921 } 1922 } 1923 1924 protected void addToTarget(Positionable l) { 1925 JComponent c = (JComponent) l; 1926 c.invalidate(); 1927 _targetPanel.remove(c); 1928 _targetPanel.add(c, Integer.valueOf(l.getDisplayLevel())); 1929 _targetPanel.moveToFront(c); 1930 c.repaint(); 1931 _targetPanel.revalidate(); 1932 } 1933 1934 /* 1935 * ************ Icon editors for adding content *********** 1936 */ 1937 static final String[] ICON_EDITORS = {"Sensor", "RightTurnout", "LeftTurnout", 1938 "SlipTOEditor", "SignalHead", "SignalMast", "Memory", "Light", 1939 "Reporter", "Background", "MultiSensor", "Icon", "Text", "Block Contents"}; 1940 1941 /** 1942 * Create editor for a given item type. 1943 * Paths to default icons are fixed in code. Compare to respective icon package, 1944 * eg. {@link #addSensorEditor()} and {@link SensorIcon} 1945 * 1946 * @param name Icon editor's name 1947 * @return a window 1948 */ 1949 public JFrameItem getIconFrame(String name) { 1950 JFrameItem frame = _iconEditorFrame.get(name); 1951 if (frame == null) { 1952 if ("Sensor".equals(name)) { 1953 addSensorEditor(); 1954 } else if ("RightTurnout".equals(name)) { 1955 addRightTOEditor(); 1956 } else if ("LeftTurnout".equals(name)) { 1957 addLeftTOEditor(); 1958 } else if ("SlipTOEditor".equals(name)) { 1959 addSlipTOEditor(); 1960 } else if ("SignalHead".equals(name)) { 1961 addSignalHeadEditor(); 1962 } else if ("SignalMast".equals(name)) { 1963 addSignalMastEditor(); 1964 } else if ("Memory".equals(name)) { 1965 addMemoryEditor(); 1966 } else if ("GlobalVariable".equals(name)) { 1967 addGlobalVariableEditor(); 1968 } else if ("Reporter".equals(name)) { 1969 addReporterEditor(); 1970 } else if ("Light".equals(name)) { 1971 addLightEditor(); 1972 } else if ("Background".equals(name)) { 1973 addBackgroundEditor(); 1974 } else if ("MultiSensor".equals(name)) { 1975 addMultiSensorEditor(); 1976 } else if ("Icon".equals(name)) { 1977 addIconEditor(); 1978 } else if ("Text".equals(name)) { 1979 addTextEditor(); 1980 } else if ("BlockLabel".equals(name)) { 1981 addBlockContentsEditor(); 1982 } else if ("Audio".equals(name)) { 1983 addAudioEditor(); 1984 } else if ("LogixNG".equals(name)) { 1985 addLogixNGEditor(); 1986 } else { 1987 // log.error("No such Icon Editor \"{}\"", name); 1988 return null; 1989 } 1990 // frame added in the above switch 1991 frame = _iconEditorFrame.get(name); 1992 1993 if (frame == null) { // addTextEditor does not create a usable frame 1994 return null; 1995 } 1996 //frame.setLocationRelativeTo(this); 1997 frame.setLocation(frameLocationX, frameLocationY); 1998 frameLocationX += DELTA; 1999 frameLocationY += DELTA; 2000 } 2001 frame.setVisible(true); 2002 return frame; 2003 } 2004 public int frameLocationX = 0; 2005 public int frameLocationY = 0; 2006 static final int DELTA = 20; 2007 2008 public IconAdder getIconEditor(String name) { 2009 return _iconEditorFrame.get(name).getEditor(); 2010 } 2011 2012 /** 2013 * Add a label to the target. 2014 */ 2015 protected void addTextEditor() { 2016 String newLabel = JmriJOptionPane.showInputDialog(this, Bundle.getMessage("PromptNewLabel"),""); 2017 if (newLabel == null) { 2018 return; // canceled 2019 } 2020 PositionableLabel l = addLabel(newLabel); 2021 // always allow new items to be moved 2022 l.setPositionable(true); 2023 } 2024 2025 protected void addRightTOEditor() { 2026 IconAdder editor = new IconAdder("RightTurnout"); 2027 editor.setIcon(3, "TurnoutStateClosed", 2028 "resources/icons/smallschematics/tracksegments/os-righthand-west-closed.gif"); 2029 editor.setIcon(2, "TurnoutStateThrown", 2030 "resources/icons/smallschematics/tracksegments/os-righthand-west-thrown.gif"); 2031 editor.setIcon(0, "BeanStateInconsistent", 2032 "resources/icons/smallschematics/tracksegments/os-righthand-west-error.gif"); 2033 editor.setIcon(1, "BeanStateUnknown", 2034 "resources/icons/smallschematics/tracksegments/os-righthand-west-unknown.gif"); 2035 2036 JFrameItem frame = makeAddIconFrame("RightTurnout", true, true, editor); 2037 _iconEditorFrame.put("RightTurnout", frame); 2038 editor.setPickList(PickListModel.turnoutPickModelInstance()); 2039 2040 ActionListener addIconAction = a -> addTurnoutR(); 2041 editor.makeIconPanel(true); 2042 editor.complete(addIconAction, true, true, false); 2043 frame.addHelpMenu("package.jmri.jmrit.display.IconAdder", true); 2044 } 2045 2046 protected void addLeftTOEditor() { 2047 IconAdder editor = new IconAdder("LeftTurnout"); 2048 editor.setIcon(3, "TurnoutStateClosed", 2049 "resources/icons/smallschematics/tracksegments/os-lefthand-east-closed.gif"); 2050 editor.setIcon(2, "TurnoutStateThrown", 2051 "resources/icons/smallschematics/tracksegments/os-lefthand-east-thrown.gif"); 2052 editor.setIcon(0, "BeanStateInconsistent", 2053 "resources/icons/smallschematics/tracksegments/os-lefthand-east-error.gif"); 2054 editor.setIcon(1, "BeanStateUnknown", 2055 "resources/icons/smallschematics/tracksegments/os-lefthand-east-unknown.gif"); 2056 2057 JFrameItem frame = makeAddIconFrame("LeftTurnout", true, true, editor); 2058 _iconEditorFrame.put("LeftTurnout", frame); 2059 editor.setPickList(PickListModel.turnoutPickModelInstance()); 2060 2061 ActionListener addIconAction = a -> addTurnoutL(); 2062 editor.makeIconPanel(true); 2063 editor.complete(addIconAction, true, true, false); 2064 frame.addHelpMenu("package.jmri.jmrit.display.IconAdder", true); 2065 } 2066 2067 protected void addSlipTOEditor() { 2068 SlipIconAdder editor = new SlipIconAdder("SlipTOEditor"); 2069 editor.setIcon(3, "LowerWestToUpperEast", 2070 "resources/icons/smallschematics/tracksegments/os-slip-lower-west-upper-east.gif"); 2071 editor.setIcon(2, "UpperWestToLowerEast", 2072 "resources/icons/smallschematics/tracksegments/os-slip-upper-west-lower-east.gif"); 2073 editor.setIcon(4, "LowerWestToLowerEast", 2074 "resources/icons/smallschematics/tracksegments/os-slip-lower-west-lower-east.gif"); 2075 editor.setIcon(5, "UpperWestToUpperEast", 2076 "resources/icons/smallschematics/tracksegments/os-slip-upper-west-upper-east.gif"); 2077 editor.setIcon(0, "BeanStateInconsistent", 2078 "resources/icons/smallschematics/tracksegments/os-slip-error-full.gif"); 2079 editor.setIcon(1, "BeanStateUnknown", 2080 "resources/icons/smallschematics/tracksegments/os-slip-unknown-full.gif"); 2081 editor.setTurnoutType(SlipTurnoutIcon.DOUBLESLIP); 2082 JFrameItem frame = makeAddIconFrame("SlipTOEditor", true, true, editor); 2083 _iconEditorFrame.put("SlipTOEditor", frame); 2084 editor.setPickList(PickListModel.turnoutPickModelInstance()); 2085 2086 ActionListener addIconAction = a -> addSlip(); 2087 editor.makeIconPanel(true); 2088 editor.complete(addIconAction, true, true, false); 2089 frame.addHelpMenu("package.jmri.jmrit.display.SlipTurnoutIcon", true); 2090 } 2091 2092 protected void addSensorEditor() { 2093 IconAdder editor = new IconAdder("Sensor"); 2094 editor.setIcon(3, "SensorStateActive", 2095 "resources/icons/smallschematics/tracksegments/circuit-occupied.gif"); 2096 editor.setIcon(2, "SensorStateInactive", 2097 "resources/icons/smallschematics/tracksegments/circuit-empty.gif"); 2098 editor.setIcon(0, "BeanStateInconsistent", 2099 "resources/icons/smallschematics/tracksegments/circuit-error.gif"); 2100 editor.setIcon(1, "BeanStateUnknown", 2101 "resources/icons/smallschematics/tracksegments/circuit-error.gif"); 2102 2103 JFrameItem frame = makeAddIconFrame("Sensor", true, true, editor); 2104 _iconEditorFrame.put("Sensor", frame); 2105 editor.setPickList(PickListModel.sensorPickModelInstance()); 2106 2107 ActionListener addIconAction = a -> putSensor(); 2108 editor.makeIconPanel(true); 2109 editor.complete(addIconAction, true, true, false); 2110 frame.addHelpMenu("package.jmri.jmrit.display.IconAdder", true); 2111 } 2112 2113 protected void addSignalHeadEditor() { 2114 IconAdder editor = getSignalHeadEditor(); 2115 JFrameItem frame = makeAddIconFrame("SignalHead", true, true, editor); 2116 _iconEditorFrame.put("SignalHead", frame); 2117 editor.setPickList(PickListModel.signalHeadPickModelInstance()); 2118 2119 ActionListener addIconAction = a -> putSignalHead(); 2120 editor.makeIconPanel(true); 2121 editor.complete(addIconAction, true, false, false); 2122 frame.addHelpMenu("package.jmri.jmrit.display.IconAdder", true); 2123 } 2124 2125 protected IconAdder getSignalHeadEditor() { 2126 // note that all these icons will be refreshed when user clicks a specific signal head in the table 2127 IconAdder editor = new IconAdder("SignalHead"); 2128 editor.setIcon(0, "SignalHeadStateRed", 2129 "resources/icons/smallschematics/searchlights/left-red-marker.gif"); 2130 editor.setIcon(1, "SignalHeadStateYellow", 2131 "resources/icons/smallschematics/searchlights/left-yellow-marker.gif"); 2132 editor.setIcon(2, "SignalHeadStateGreen", 2133 "resources/icons/smallschematics/searchlights/left-green-marker.gif"); 2134 editor.setIcon(3, "SignalHeadStateDark", 2135 "resources/icons/smallschematics/searchlights/left-dark-marker.gif"); 2136 editor.setIcon(4, "SignalHeadStateHeld", 2137 "resources/icons/smallschematics/searchlights/left-held-marker.gif"); 2138 editor.setIcon(5, "SignalHeadStateLunar", 2139 "resources/icons/smallschematics/searchlights/left-lunar-marker.gif"); 2140 editor.setIcon(6, "SignalHeadStateFlashingRed", 2141 "resources/icons/smallschematics/searchlights/left-flashred-marker.gif"); 2142 editor.setIcon(7, "SignalHeadStateFlashingYellow", 2143 "resources/icons/smallschematics/searchlights/left-flashyellow-marker.gif"); 2144 editor.setIcon(8, "SignalHeadStateFlashingGreen", 2145 "resources/icons/smallschematics/searchlights/left-flashgreen-marker.gif"); 2146 editor.setIcon(9, "SignalHeadStateFlashingLunar", 2147 "resources/icons/smallschematics/searchlights/left-flashlunar-marker.gif"); 2148 return editor; 2149 } 2150 2151 protected void addSignalMastEditor() { 2152 IconAdder editor = new IconAdder("SignalMast"); 2153 2154 JFrameItem frame = makeAddIconFrame("SignalMast", true, true, editor); 2155 _iconEditorFrame.put("SignalMast", frame); 2156 editor.setPickList(PickListModel.signalMastPickModelInstance()); 2157 2158 ActionListener addIconAction = a -> putSignalMast(); 2159 editor.makeIconPanel(true); 2160 editor.complete(addIconAction, true, false, false); 2161 frame.addHelpMenu("package.jmri.jmrit.display.IconAdder", true); 2162 } 2163 2164 private final SpinnerNumberModel _spinCols = new SpinnerNumberModel(3, 1, 100, 1); 2165 2166 protected void addMemoryEditor() { 2167 IconAdder editor = new IconAdder("Memory") { 2168 final JButton bSpin = new JButton(Bundle.getMessage("AddSpinner")); 2169 final JButton bBox = new JButton(Bundle.getMessage("AddInputBox")); 2170 final JSpinner spinner = new JSpinner(_spinCols); 2171 2172 @Override 2173 protected void addAdditionalButtons(JPanel p) { 2174 bSpin.addActionListener(a -> addMemorySpinner()); 2175 JPanel p1 = new JPanel(); 2176 //p1.setLayout(new BoxLayout(p1, BoxLayout.X_AXIS)); 2177 bBox.addActionListener(a -> addMemoryInputBox()); 2178 ((JSpinner.DefaultEditor) spinner.getEditor()).getTextField().setColumns(2); 2179 spinner.setMaximumSize(spinner.getPreferredSize()); 2180 JPanel p2 = new JPanel(); 2181 p2.add(new JLabel(Bundle.getMessage("NumColsLabel"))); 2182 p2.add(spinner); 2183 p1.add(p2); 2184 p1.add(bBox); 2185 p.add(p1); 2186 p1 = new JPanel(); 2187 p1.add(bSpin); 2188 p.add(p1); 2189 } 2190 2191 @Override 2192 public void valueChanged(ListSelectionEvent e) { 2193 super.valueChanged(e); 2194 bSpin.setEnabled(addIconIsEnabled()); 2195 bBox.setEnabled(addIconIsEnabled()); 2196 } 2197 }; 2198 ActionListener addIconAction = a -> putMemory(); 2199 JFrameItem frame = makeAddIconFrame("Memory", true, true, editor); 2200 _iconEditorFrame.put("Memory", frame); 2201 editor.setPickList(PickListModel.memoryPickModelInstance()); 2202 editor.makeIconPanel(true); 2203 editor.complete(addIconAction, false, true, false); 2204 frame.addHelpMenu("package.jmri.jmrit.display.IconAdder", true); 2205 } 2206 2207 protected void addGlobalVariableEditor() { 2208 IconAdder editor = new IconAdder("GlobalVariable") { 2209 final JButton bSpin = new JButton(Bundle.getMessage("AddSpinner")); 2210 final JButton bBox = new JButton(Bundle.getMessage("AddInputBox")); 2211 final JSpinner spinner = new JSpinner(_spinCols); 2212 2213 @Override 2214 protected void addAdditionalButtons(JPanel p) { 2215 bSpin.addActionListener(a -> addGlobalVariableSpinner()); 2216 JPanel p1 = new JPanel(); 2217 //p1.setLayout(new BoxLayout(p1, BoxLayout.X_AXIS)); 2218 bBox.addActionListener(a -> addGlobalVariableInputBox()); 2219 ((JSpinner.DefaultEditor) spinner.getEditor()).getTextField().setColumns(2); 2220 spinner.setMaximumSize(spinner.getPreferredSize()); 2221 JPanel p2 = new JPanel(); 2222 p2.add(new JLabel(Bundle.getMessage("NumColsLabel"))); 2223 p2.add(spinner); 2224 p1.add(p2); 2225 p1.add(bBox); 2226 p.add(p1); 2227 p1 = new JPanel(); 2228 p1.add(bSpin); 2229 p.add(p1); 2230 } 2231 2232 @Override 2233 public void valueChanged(ListSelectionEvent e) { 2234 super.valueChanged(e); 2235 bSpin.setEnabled(addIconIsEnabled()); 2236 bBox.setEnabled(addIconIsEnabled()); 2237 } 2238 }; 2239 ActionListener addIconAction = a -> putGlobalVariable(); 2240 JFrameItem frame = makeAddIconFrame("GlobalVariable", true, true, editor); 2241 _iconEditorFrame.put("GlobalVariable", frame); 2242 editor.setPickList(PickListModel.globalVariablePickModelInstance()); 2243 editor.makeIconPanel(true); 2244 editor.complete(addIconAction, false, false, false); 2245 frame.addHelpMenu("package.jmri.jmrit.display.IconAdder", true); 2246 } 2247 2248 protected void addBlockContentsEditor() { 2249 IconAdder editor = new IconAdder("Block Contents"); 2250 ActionListener addIconAction = a -> putBlockContents(); 2251 JFrameItem frame = makeAddIconFrame("BlockLabel", true, true, editor); 2252 _iconEditorFrame.put("BlockLabel", frame); 2253 editor.setPickList(PickListModel.blockPickModelInstance()); 2254 editor.makeIconPanel(true); 2255 editor.complete(addIconAction, false, true, false); 2256 frame.addHelpMenu("package.jmri.jmrit.display.IconAdder", true); 2257 } 2258 2259 protected void addReporterEditor() { 2260 IconAdder editor = new IconAdder("Reporter"); 2261 ActionListener addIconAction = a -> addReporter(); 2262 JFrameItem frame = makeAddIconFrame("Reporter", true, true, editor); 2263 _iconEditorFrame.put("Reporter", frame); 2264 editor.setPickList(PickListModel.reporterPickModelInstance()); 2265 editor.makeIconPanel(true); 2266 editor.complete(addIconAction, false, true, false); 2267 frame.addHelpMenu("package.jmri.jmrit.display.IconAdder", true); 2268 } 2269 2270 protected void addLightEditor() { 2271 IconAdder editor = new IconAdder("Light"); 2272 editor.setIcon(3, "StateOff", 2273 "resources/icons/smallschematics/lights/cross-on.png"); 2274 editor.setIcon(2, "StateOn", 2275 "resources/icons/smallschematics/lights/cross-off.png"); 2276 editor.setIcon(0, "BeanStateInconsistent", 2277 "resources/icons/smallschematics/lights/cross-inconsistent.png"); 2278 editor.setIcon(1, "BeanStateUnknown", 2279 "resources/icons/smallschematics/lights/cross-unknown.png"); 2280 2281 JFrameItem frame = makeAddIconFrame("Light", true, true, editor); 2282 _iconEditorFrame.put("Light", frame); 2283 editor.setPickList(PickListModel.lightPickModelInstance()); 2284 2285 ActionListener addIconAction = a -> addLight(); 2286 editor.makeIconPanel(true); 2287 editor.complete(addIconAction, true, true, false); 2288 frame.addHelpMenu("package.jmri.jmrit.display.IconAdder", true); 2289 } 2290 2291 protected void addBackgroundEditor() { 2292 IconAdder editor = new IconAdder("Background"); 2293 editor.setIcon(0, "background", "resources/PanelPro.gif"); 2294 2295 JFrameItem frame = makeAddIconFrame("Background", true, false, editor); 2296 _iconEditorFrame.put("Background", frame); 2297 2298 ActionListener addIconAction = a -> putBackground(); 2299 editor.makeIconPanel(true); 2300 editor.complete(addIconAction, true, false, false); 2301 frame.addHelpMenu("package.jmri.jmrit.display.IconAdder", true); 2302 } 2303 2304 protected JFrameItem addMultiSensorEditor() { 2305 MultiSensorIconAdder editor = new MultiSensorIconAdder("MultiSensor"); 2306 editor.setIcon(0, "BeanStateInconsistent", 2307 "resources/icons/USS/plate/levers/l-inconsistent.gif"); 2308 editor.setIcon(1, "BeanStateUnknown", 2309 "resources/icons/USS/plate/levers/l-unknown.gif"); 2310 editor.setIcon(2, "SensorStateInactive", 2311 "resources/icons/USS/plate/levers/l-inactive.gif"); 2312 editor.setIcon(3, "MultiSensorPosition 0", 2313 "resources/icons/USS/plate/levers/l-left.gif"); 2314 editor.setIcon(4, "MultiSensorPosition 1", 2315 "resources/icons/USS/plate/levers/l-vertical.gif"); 2316 editor.setIcon(5, "MultiSensorPosition 2", 2317 "resources/icons/USS/plate/levers/l-right.gif"); 2318 2319 JFrameItem frame = makeAddIconFrame("MultiSensor", true, false, editor); 2320 _iconEditorFrame.put("MultiSensor", frame); 2321 frame.addHelpMenu("package.jmri.jmrit.display.MultiSensorIconAdder", true); 2322 2323 editor.setPickList(PickListModel.sensorPickModelInstance()); 2324 2325 ActionListener addIconAction = a -> addMultiSensor(); 2326 editor.makeIconPanel(true); 2327 editor.complete(addIconAction, true, true, false); 2328 return frame; 2329 } 2330 2331 protected void addIconEditor() { 2332 IconAdder editor = new IconAdder("Icon"); 2333 editor.setIcon(0, "plainIcon", "resources/icons/smallschematics/tracksegments/block.gif"); 2334 JFrameItem frame = makeAddIconFrame("Icon", true, false, editor); 2335 _iconEditorFrame.put("Icon", frame); 2336 2337 ActionListener addIconAction = a -> putIcon(); 2338 editor.makeIconPanel(true); 2339 editor.complete(addIconAction, true, false, false); 2340 frame.addHelpMenu("package.jmri.jmrit.display.IconAdder", true); 2341 } 2342 2343 protected void addAudioEditor() { 2344 IconAdder editor = new IconAdder("Audio"); 2345 editor.setIcon(0, "plainIcon", "resources/icons/audio_icon.gif"); 2346 JFrameItem frame = makeAddIconFrame("Audio", true, false, editor); 2347 _iconEditorFrame.put("Audio", frame); 2348 editor.setPickList(PickListModel.audioPickModelInstance()); 2349 2350 ActionListener addIconAction = a -> putAudio(); 2351 editor.makeIconPanel(true); 2352 editor.complete(addIconAction, true, false, false); 2353 frame.addHelpMenu("package.jmri.jmrit.display.IconAdder", true); 2354 } 2355 2356 protected void addLogixNGEditor() { 2357 IconAdder editor = new IconAdder("LogixNG"); 2358 editor.setIcon(0, "plainIcon", "resources/icons/logixng/logixng_icon.gif"); 2359 JFrameItem frame = makeAddIconFrame("LogixNG", true, false, editor); 2360 _iconEditorFrame.put("LogixNG", frame); 2361 2362 ActionListener addIconAction = a -> putLogixNG(); 2363 editor.makeIconPanel(true); 2364 editor.complete(addIconAction, true, false, false); 2365 frame.addHelpMenu("package.jmri.jmrit.display.IconAdder", true); 2366 } 2367 2368 /* 2369 * ************** add content items from Icon Editors ******************* 2370 */ 2371 /** 2372 * Add a sensor indicator to the target. 2373 * 2374 * @return The sensor that was added to the panel. 2375 */ 2376 protected SensorIcon putSensor() { 2377 SensorIcon result = new SensorIcon(new NamedIcon("resources/icons/smallschematics/tracksegments/circuit-error.gif", 2378 "resources/icons/smallschematics/tracksegments/circuit-error.gif"), this); 2379 IconAdder editor = getIconEditor("Sensor"); 2380 Hashtable<String, NamedIcon> map = editor.getIconMap(); 2381 Enumeration<String> e = map.keys(); 2382 while (e.hasMoreElements()) { 2383 String key = e.nextElement(); 2384 result.setIcon(key, map.get(key)); 2385 } 2386// l.setActiveIcon(editor.getIcon("SensorStateActive")); 2387// l.setInactiveIcon(editor.getIcon("SensorStateInactive")); 2388// l.setInconsistentIcon(editor.getIcon("BeanStateInconsistent")); 2389// l.setUnknownIcon(editor.getIcon("BeanStateUnknown")); 2390 NamedBean b = editor.getTableSelection(); 2391 if (b != null) { 2392 result.setSensor(b.getDisplayName()); 2393 } 2394 result.setDisplayLevel(SENSORS); 2395 setNextLocation(result); 2396 try { 2397 putItem(result); 2398 } catch (Positionable.DuplicateIdException ex) { 2399 // This should never happen 2400 log.error("Editor.putItem() with null id has thrown DuplicateIdException", ex); 2401 } 2402 return result; 2403 } 2404 2405 /** 2406 * Add a turnout indicator to the target 2407 */ 2408 void addTurnoutR() { 2409 IconAdder editor = getIconEditor("RightTurnout"); 2410 addTurnout(editor); 2411 } 2412 2413 void addTurnoutL() { 2414 IconAdder editor = getIconEditor("LeftTurnout"); 2415 addTurnout(editor); 2416 } 2417 2418 protected TurnoutIcon addTurnout(IconAdder editor) { 2419 TurnoutIcon result = new TurnoutIcon(this); 2420 result.setTurnout(editor.getTableSelection().getDisplayName()); 2421 Hashtable<String, NamedIcon> map = editor.getIconMap(); 2422 Enumeration<String> e = map.keys(); 2423 while (e.hasMoreElements()) { 2424 String key = e.nextElement(); 2425 result.setIcon(key, map.get(key)); 2426 } 2427 result.setDisplayLevel(TURNOUTS); 2428 setNextLocation(result); 2429 try { 2430 putItem(result); 2431 } catch (Positionable.DuplicateIdException ex) { 2432 // This should never happen 2433 log.error("Editor.putItem() with null id has thrown DuplicateIdException", ex); 2434 } 2435 return result; 2436 } 2437 2438 @SuppressFBWarnings(value="BC_UNCONFIRMED_CAST_OF_RETURN_VALUE", justification="iconEditor requested as exact type") 2439 SlipTurnoutIcon addSlip() { 2440 SlipTurnoutIcon result = new SlipTurnoutIcon(this); 2441 SlipIconAdder editor = (SlipIconAdder) getIconEditor("SlipTOEditor"); 2442 result.setSingleSlipRoute(editor.getSingleSlipRoute()); 2443 2444 switch (editor.getTurnoutType()) { 2445 case SlipTurnoutIcon.DOUBLESLIP: 2446 result.setLowerWestToUpperEastIcon(editor.getIcon("LowerWestToUpperEast")); 2447 result.setUpperWestToLowerEastIcon(editor.getIcon("UpperWestToLowerEast")); 2448 result.setLowerWestToLowerEastIcon(editor.getIcon("LowerWestToLowerEast")); 2449 result.setUpperWestToUpperEastIcon(editor.getIcon("UpperWestToUpperEast")); 2450 break; 2451 case SlipTurnoutIcon.SINGLESLIP: 2452 result.setLowerWestToUpperEastIcon(editor.getIcon("LowerWestToUpperEast")); 2453 result.setUpperWestToLowerEastIcon(editor.getIcon("UpperWestToLowerEast")); 2454 result.setLowerWestToLowerEastIcon(editor.getIcon("Slip")); 2455 result.setSingleSlipRoute(editor.getSingleSlipRoute()); 2456 break; 2457 case SlipTurnoutIcon.THREEWAY: 2458 result.setLowerWestToUpperEastIcon(editor.getIcon("Upper")); 2459 result.setUpperWestToLowerEastIcon(editor.getIcon("Middle")); 2460 result.setLowerWestToLowerEastIcon(editor.getIcon("Lower")); 2461 result.setSingleSlipRoute(editor.getSingleSlipRoute()); 2462 break; 2463 case SlipTurnoutIcon.SCISSOR: //Scissor is the same as a Double for icon storing. 2464 result.setLowerWestToUpperEastIcon(editor.getIcon("LowerWestToUpperEast")); 2465 result.setUpperWestToLowerEastIcon(editor.getIcon("UpperWestToLowerEast")); 2466 result.setLowerWestToLowerEastIcon(editor.getIcon("LowerWestToLowerEast")); 2467 //l.setUpperWestToUpperEastIcon(editor.getIcon("UpperWestToUpperEast")); 2468 break; 2469 default: 2470 log.warn("Unexpected addSlip editor.getTurnoutType() of {}", editor.getTurnoutType()); 2471 break; 2472 } 2473 2474 if ((editor.getTurnoutType() == SlipTurnoutIcon.SCISSOR) && (!editor.getSingleSlipRoute())) { 2475 result.setTurnout(editor.getTurnout("lowerwest").getName(), SlipTurnoutIcon.LOWERWEST); 2476 result.setTurnout(editor.getTurnout("lowereast").getName(), SlipTurnoutIcon.LOWEREAST); 2477 } 2478 result.setInconsistentIcon(editor.getIcon("BeanStateInconsistent")); 2479 result.setUnknownIcon(editor.getIcon("BeanStateUnknown")); 2480 result.setTurnoutType(editor.getTurnoutType()); 2481 result.setTurnout(editor.getTurnout("west").getName(), SlipTurnoutIcon.WEST); 2482 result.setTurnout(editor.getTurnout("east").getName(), SlipTurnoutIcon.EAST); 2483 result.setDisplayLevel(TURNOUTS); 2484 setNextLocation(result); 2485 try { 2486 putItem(result); 2487 } catch (Positionable.DuplicateIdException e) { 2488 // This should never happen 2489 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 2490 } 2491 return result; 2492 } 2493 2494 /** 2495 * Add a signal head to the target. 2496 * 2497 * @return The signal head that was added to the target. 2498 */ 2499 protected SignalHeadIcon putSignalHead() { 2500 SignalHeadIcon result = new SignalHeadIcon(this); 2501 IconAdder editor = getIconEditor("SignalHead"); 2502 result.setSignalHead(editor.getTableSelection().getDisplayName()); 2503 Hashtable<String, NamedIcon> map = editor.getIconMap(); 2504 Enumeration<String> e = map.keys(); 2505 while (e.hasMoreElements()) { 2506 String key = e.nextElement(); 2507 result.setIcon(key, map.get(key)); 2508 } 2509 result.setDisplayLevel(SIGNALS); 2510 setNextLocation(result); 2511 try { 2512 putItem(result); 2513 } catch (Positionable.DuplicateIdException ex) { 2514 // This should never happen 2515 log.error("Editor.putItem() with null id has thrown DuplicateIdException", ex); 2516 } 2517 return result; 2518 } 2519 2520 /** 2521 * Add a signal mast to the target. 2522 * 2523 * @return The signal mast that was added to the target. 2524 */ 2525 protected SignalMastIcon putSignalMast() { 2526 SignalMastIcon result = new SignalMastIcon(this); 2527 IconAdder editor = _iconEditorFrame.get("SignalMast").getEditor(); 2528 result.setSignalMast(editor.getTableSelection().getDisplayName()); 2529 result.setDisplayLevel(SIGNALS); 2530 setNextLocation(result); 2531 try { 2532 putItem(result); 2533 } catch (Positionable.DuplicateIdException e) { 2534 // This should never happen 2535 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 2536 } 2537 return result; 2538 } 2539 2540 protected MemoryIcon putMemory() { 2541 MemoryIcon result = new MemoryIcon(new NamedIcon("resources/icons/misc/X-red.gif", 2542 "resources/icons/misc/X-red.gif"), this); 2543 IconAdder memoryIconEditor = getIconEditor("Memory"); 2544 result.setMemory(memoryIconEditor.getTableSelection().getDisplayName()); 2545 result.setSize(result.getPreferredSize().width, result.getPreferredSize().height); 2546 result.setDisplayLevel(MEMORIES); 2547 setNextLocation(result); 2548 try { 2549 putItem(result); 2550 } catch (Positionable.DuplicateIdException e) { 2551 // This should never happen 2552 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 2553 } 2554 return result; 2555 } 2556 2557 protected MemorySpinnerIcon addMemorySpinner() { 2558 MemorySpinnerIcon result = new MemorySpinnerIcon(this); 2559 IconAdder memoryIconEditor = getIconEditor("Memory"); 2560 result.setMemory(memoryIconEditor.getTableSelection().getDisplayName()); 2561 result.setSize(result.getPreferredSize().width, result.getPreferredSize().height); 2562 result.setDisplayLevel(MEMORIES); 2563 setNextLocation(result); 2564 try { 2565 putItem(result); 2566 } catch (Positionable.DuplicateIdException e) { 2567 // This should never happen 2568 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 2569 } 2570 return result; 2571 } 2572 2573 protected MemoryInputIcon addMemoryInputBox() { 2574 MemoryInputIcon result = new MemoryInputIcon(_spinCols.getNumber().intValue(), this); 2575 IconAdder memoryIconEditor = getIconEditor("Memory"); 2576 result.setMemory(memoryIconEditor.getTableSelection().getDisplayName()); 2577 result.setSize(result.getPreferredSize().width, result.getPreferredSize().height); 2578 result.setDisplayLevel(MEMORIES); 2579 setNextLocation(result); 2580 try { 2581 putItem(result); 2582 } catch (Positionable.DuplicateIdException e) { 2583 // This should never happen 2584 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 2585 } 2586 return result; 2587 } 2588 2589 protected GlobalVariableIcon putGlobalVariable() { 2590 GlobalVariableIcon result = new GlobalVariableIcon(new NamedIcon("resources/icons/misc/X-red.gif", 2591 "resources/icons/misc/X-red.gif"), this); 2592 IconAdder globalVariableIconEditor = getIconEditor("GlobalVariable"); 2593 result.setGlobalVariable(globalVariableIconEditor.getTableSelection().getDisplayName()); 2594 result.setSize(result.getPreferredSize().width, result.getPreferredSize().height); 2595 result.setDisplayLevel(MEMORIES); 2596 setNextLocation(result); 2597 try { 2598 putItem(result); 2599 } catch (Positionable.DuplicateIdException e) { 2600 // This should never happen 2601 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 2602 } 2603 return result; 2604 } 2605 2606 protected GlobalVariableSpinnerIcon addGlobalVariableSpinner() { 2607 GlobalVariableSpinnerIcon result = new GlobalVariableSpinnerIcon(this); 2608 IconAdder globalVariableIconEditor = getIconEditor("GlobalVariable"); 2609 result.setGlobalVariable(globalVariableIconEditor.getTableSelection().getDisplayName()); 2610 result.setSize(result.getPreferredSize().width, result.getPreferredSize().height); 2611 result.setDisplayLevel(MEMORIES); 2612 setNextLocation(result); 2613 try { 2614 putItem(result); 2615 } catch (Positionable.DuplicateIdException e) { 2616 // This should never happen 2617 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 2618 } 2619 return result; 2620 } 2621 2622 protected GlobalVariableInputIcon addGlobalVariableInputBox() { 2623 GlobalVariableInputIcon result = new GlobalVariableInputIcon(_spinCols.getNumber().intValue(), this); 2624 IconAdder globalVariableIconEditor = getIconEditor("GlobalVariable"); 2625 result.setGlobalVariable(globalVariableIconEditor.getTableSelection().getDisplayName()); 2626 result.setSize(result.getPreferredSize().width, result.getPreferredSize().height); 2627 result.setDisplayLevel(MEMORIES); 2628 setNextLocation(result); 2629 try { 2630 putItem(result); 2631 } catch (Positionable.DuplicateIdException e) { 2632 // This should never happen 2633 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 2634 } 2635 return result; 2636 } 2637 2638 protected BlockContentsIcon putBlockContents() { 2639 BlockContentsIcon result = new BlockContentsIcon(new NamedIcon("resources/icons/misc/X-red.gif", 2640 "resources/icons/misc/X-red.gif"), this); 2641 IconAdder blockIconEditor = getIconEditor("BlockLabel"); 2642 result.setBlock(blockIconEditor.getTableSelection().getDisplayName()); 2643 result.setSize(result.getPreferredSize().width, result.getPreferredSize().height); 2644 result.setDisplayLevel(MEMORIES); 2645 setNextLocation(result); 2646 try { 2647 putItem(result); 2648 } catch (Positionable.DuplicateIdException e) { 2649 // This should never happen 2650 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 2651 } 2652 return result; 2653 } 2654 2655 /** 2656 * Add a Light indicator to the target 2657 * 2658 * @return The light indicator that was added to the target. 2659 */ 2660 protected LightIcon addLight() { 2661 LightIcon result = new LightIcon(this); 2662 IconAdder editor = getIconEditor("Light"); 2663 result.setOffIcon(editor.getIcon("StateOff")); 2664 result.setOnIcon(editor.getIcon("StateOn")); 2665 result.setInconsistentIcon(editor.getIcon("BeanStateInconsistent")); 2666 result.setUnknownIcon(editor.getIcon("BeanStateUnknown")); 2667 result.setLight((Light) editor.getTableSelection()); 2668 result.setDisplayLevel(LIGHTS); 2669 setNextLocation(result); 2670 try { 2671 putItem(result); 2672 } catch (Positionable.DuplicateIdException e) { 2673 // This should never happen 2674 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 2675 } 2676 return result; 2677 } 2678 2679 protected ReporterIcon addReporter() { 2680 ReporterIcon result = new ReporterIcon(this); 2681 IconAdder reporterIconEditor = getIconEditor("Reporter"); 2682 result.setReporter((Reporter) reporterIconEditor.getTableSelection()); 2683 result.setSize(result.getPreferredSize().width, result.getPreferredSize().height); 2684 result.setDisplayLevel(REPORTERS); 2685 setNextLocation(result); 2686 try { 2687 putItem(result); 2688 } catch (Positionable.DuplicateIdException e) { 2689 // This should never happen 2690 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 2691 } 2692 return result; 2693 } 2694 2695 /** 2696 * Button pushed, add a background image. Note that a background image 2697 * differs from a regular icon only in the level at which it's presented. 2698 */ 2699 void putBackground() { 2700 // most likely the image is scaled. get full size from URL 2701 IconAdder bkgrndEditor = getIconEditor("Background"); 2702 String url = bkgrndEditor.getIcon("background").getURL(); 2703 setUpBackground(url); 2704 } 2705 2706 /** 2707 * Add an icon to the target. 2708 * 2709 * @return The icon that was added to the target. 2710 */ 2711 protected Positionable putIcon() { 2712 IconAdder iconEditor = getIconEditor("Icon"); 2713 String url = iconEditor.getIcon("plainIcon").getURL(); 2714 NamedIcon icon = NamedIcon.getIconByName(url); 2715 if (log.isDebugEnabled()) { 2716 log.debug("putIcon: {} url= {}", (icon == null ? "null" : "icon"), url); 2717 } 2718 PositionableLabel result = new PositionableLabel(icon, this); 2719// l.setPopupUtility(null); // no text 2720 result.setDisplayLevel(ICONS); 2721 setNextLocation(result); 2722 try { 2723 putItem(result); 2724 } catch (Positionable.DuplicateIdException e) { 2725 // This should never happen 2726 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 2727 } 2728 result.updateSize(); 2729 return result; 2730 } 2731 2732 /** 2733 * Add a LogixNG icon to the target. 2734 * 2735 * @return The LogixNG icon that was added to the target. 2736 */ 2737 protected Positionable putAudio() { 2738 IconAdder iconEditor = getIconEditor("Audio"); 2739 String url = iconEditor.getIcon("plainIcon").getURL(); 2740 NamedIcon icon = NamedIcon.getIconByName(url); 2741 if (log.isDebugEnabled()) { 2742 log.debug("putAudio: {} url= {}", (icon == null ? "null" : "icon"), url); 2743 } 2744 AudioIcon result = new AudioIcon(icon, this); 2745 NamedBean b = iconEditor.getTableSelection(); 2746 if (b != null) { 2747 result.setAudio(b.getDisplayName()); 2748 } 2749// l.setPopupUtility(null); // no text 2750 result.setDisplayLevel(ICONS); 2751 setNextLocation(result); 2752 try { 2753 putItem(result); 2754 } catch (Positionable.DuplicateIdException e) { 2755 // This should never happen 2756 log.error("Editor.putAudio() with null id has thrown DuplicateIdException", e); 2757 } 2758 result.updateSize(); 2759 return result; 2760 } 2761 2762 /** 2763 * Add a LogixNG icon to the target. 2764 * 2765 * @return The LogixNG icon that was added to the target. 2766 */ 2767 protected Positionable putLogixNG() { 2768 IconAdder iconEditor = getIconEditor("LogixNG"); 2769 String url = iconEditor.getIcon("plainIcon").getURL(); 2770 NamedIcon icon = NamedIcon.getIconByName(url); 2771 if (log.isDebugEnabled()) { 2772 log.debug("putLogixNG: {} url= {}", (icon == null ? "null" : "icon"), url); 2773 } 2774 LogixNGIcon result = new LogixNGIcon(icon, this); 2775// l.setPopupUtility(null); // no text 2776 result.setDisplayLevel(ICONS); 2777 setNextLocation(result); 2778 try { 2779 putItem(result); 2780 } catch (Positionable.DuplicateIdException e) { 2781 // This should never happen 2782 log.error("Editor.putLogixNG() with null id has thrown DuplicateIdException", e); 2783 } 2784 result.updateSize(); 2785 return result; 2786 } 2787 2788 @SuppressFBWarnings(value="BC_UNCONFIRMED_CAST_OF_RETURN_VALUE", justification="iconEditor requested as exact type") 2789 public MultiSensorIcon addMultiSensor() { 2790 MultiSensorIcon result = new MultiSensorIcon(this); 2791 MultiSensorIconAdder editor = (MultiSensorIconAdder) getIconEditor("MultiSensor"); 2792 result.setUnknownIcon(editor.getIcon("BeanStateUnknown")); 2793 result.setInconsistentIcon(editor.getIcon("BeanStateInconsistent")); 2794 result.setInactiveIcon(editor.getIcon("SensorStateInactive")); 2795 int numPositions = editor.getNumIcons(); 2796 for (int i = 3; i < numPositions; i++) { 2797 NamedIcon icon = editor.getIcon(i); 2798 String sensor = editor.getSensor(i).getName(); 2799 result.addEntry(sensor, icon); 2800 } 2801 result.setUpDown(editor.getUpDown()); 2802 result.setDisplayLevel(SENSORS); 2803 setNextLocation(result); 2804 try { 2805 putItem(result); 2806 } catch (Positionable.DuplicateIdException e) { 2807 // This should never happen 2808 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 2809 } 2810 return result; 2811 } 2812 2813 protected AnalogClock2Display addClock() { 2814 AnalogClock2Display result = new AnalogClock2Display(this); 2815 result.setOpaque(false); 2816 result.update(); 2817 result.setDisplayLevel(CLOCK); 2818 setNextLocation(result); 2819 try { 2820 putItem(result); 2821 } catch (Positionable.DuplicateIdException e) { 2822 // This should never happen 2823 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 2824 } 2825 return result; 2826 } 2827 2828 protected RpsPositionIcon addRpsReporter() { 2829 RpsPositionIcon result = new RpsPositionIcon(this); 2830 result.setSize(result.getPreferredSize().width, result.getPreferredSize().height); 2831 result.setDisplayLevel(SENSORS); 2832 setNextLocation(result); 2833 try { 2834 putItem(result); 2835 } catch (Positionable.DuplicateIdException e) { 2836 // This should never happen 2837 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 2838 } 2839 return result; 2840 } 2841 2842 /* 2843 * ****************** end adding content ******************** 2844 */ 2845 /* 2846 * ********************* Icon Editors utils *************************** 2847 */ 2848 public static class JFrameItem extends JmriJFrame { 2849 2850 private final IconAdder _editor; 2851 2852 JFrameItem(String name, IconAdder editor) { 2853 super(name); 2854 _editor = editor; 2855 setName(name); 2856 } 2857 2858 public IconAdder getEditor() { 2859 return _editor; 2860 } 2861 2862 @Override 2863 public String toString() { 2864 return this.getName(); 2865 } 2866 } 2867 2868 public void setTitle() { 2869 String name = _targetFrame.getTitle(); 2870 if (name == null || name.equals("")) { 2871 super.setTitle(Bundle.getMessage("LabelEditor")); 2872 } else { 2873 super.setTitle(name + " " + Bundle.getMessage("LabelEditor")); 2874 } 2875 for (JFrameItem frame : _iconEditorFrame.values()) { 2876 frame.setTitle(frame.getName() + " (" + name + ")"); 2877 } 2878 setName(name); 2879 } 2880 2881 /** 2882 * Create a frame showing all images in the set used for an icon. 2883 * Opened when editItemInPanel button is clicked in the Edit Icon Panel, 2884 * shown after icon's context menu Edit Icon... item is selected. 2885 * 2886 * @param name bean type name 2887 * @param add true when used to add a new item on panel, false when used to edit an item already on the panel 2888 * @param table true for bean types presented as table instead of icons 2889 * @param editor parent frame of the image frame 2890 * @return JFrame connected to the editor, to be filled with icons 2891 */ 2892 protected JFrameItem makeAddIconFrame(String name, boolean add, boolean table, IconAdder editor) { 2893 log.debug("makeAddIconFrame for {}, add= {}, table= {}", name, add, table); 2894 String txt; 2895 String bundleName; 2896 JFrameItem frame = new JFrameItem(name, editor); 2897 // use NamedBeanBundle property for basic beans like "Turnout" I18N 2898 if ("Sensor".equals(name)) { 2899 bundleName = "BeanNameSensor"; 2900 } else if ("SignalHead".equals(name)) { 2901 bundleName = "BeanNameSignalHead"; 2902 } else if ("SignalMast".equals(name)) { 2903 bundleName = "BeanNameSignalMast"; 2904 } else if ("Memory".equals(name)) { 2905 bundleName = "BeanNameMemory"; 2906 } else if ("Reporter".equals(name)) { 2907 bundleName = "BeanNameReporter"; 2908 } else if ("Light".equals(name)) { 2909 bundleName = "BeanNameLight"; 2910 } else if ("Turnout".equals(name)) { 2911 bundleName = "BeanNameTurnout"; // called by RightTurnout and LeftTurnout objects in TurnoutIcon.java edit() method 2912 } else if ("Block".equals(name)) { 2913 bundleName = "BeanNameBlock"; 2914 } else if ("GlobalVariable".equals(name)) { 2915 bundleName = "BeanNameGlobalVariable"; 2916 } else if ("Audio".equals(name)) { 2917 bundleName = "BeanNameAudio"; 2918 } else { 2919 bundleName = name; 2920 } 2921 if (editor != null) { 2922 JPanel p = new JPanel(); 2923 p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS)); 2924 if (add) { 2925 txt = MessageFormat.format(Bundle.getMessage("addItemToPanel"), Bundle.getMessage(bundleName)); 2926 } else { 2927 txt = MessageFormat.format(Bundle.getMessage("editItemInPanel"), Bundle.getMessage(bundleName)); 2928 } 2929 p.add(new JLabel(txt)); 2930 if (table) { 2931 txt = MessageFormat.format(Bundle.getMessage("TableSelect"), Bundle.getMessage(bundleName), 2932 (add ? Bundle.getMessage("ButtonAddIcon") : Bundle.getMessage("ButtonUpdateIcon"))); 2933 } else { 2934 if ("MultiSensor".equals(name)) { 2935 txt = MessageFormat.format(Bundle.getMessage("SelectMultiSensor", Bundle.getMessage("ButtonAddIcon")), 2936 (add ? Bundle.getMessage("ButtonAddIcon") : Bundle.getMessage("ButtonUpdateIcon"))); 2937 } else { 2938 txt = MessageFormat.format(Bundle.getMessage("IconSelect"), Bundle.getMessage(bundleName), 2939 (add ? Bundle.getMessage("ButtonAddIcon") : Bundle.getMessage("ButtonUpdateIcon"))); 2940 } 2941 } 2942 p.add(new JLabel(txt)); 2943 p.add(new JLabel(" ")); // add a bit of space on pane above icons 2944 frame.getContentPane().add(p, BorderLayout.NORTH); 2945 frame.getContentPane().add(editor); 2946 2947 JMenuBar menuBar = new JMenuBar(); 2948 JMenu findIcon = new JMenu(Bundle.getMessage("findIconMenu")); 2949 menuBar.add(findIcon); 2950 2951 JMenuItem editItem = new JMenuItem(Bundle.getMessage("editIndexMenu")); 2952 editItem.addActionListener(e -> { 2953 ImageIndexEditor ii = InstanceManager.getDefault(ImageIndexEditor.class); 2954 ii.pack(); 2955 ii.setVisible(true); 2956 }); 2957 findIcon.add(editItem); 2958 findIcon.addSeparator(); 2959 2960 JMenuItem searchItem = new JMenuItem(Bundle.getMessage("searchFSMenu")); 2961 searchItem.addActionListener(new ActionListener() { 2962 private IconAdder ea; 2963 2964 @Override 2965 public void actionPerformed(ActionEvent e) { 2966 InstanceManager.getDefault(DirectorySearcher.class).searchFS(); 2967 ea.addDirectoryToCatalog(); 2968 } 2969 2970 ActionListener init(IconAdder ed) { 2971 ea = ed; 2972 return this; 2973 } 2974 }.init(editor)); 2975 2976 findIcon.add(searchItem); 2977 frame.setJMenuBar(menuBar); 2978 editor.setParent(frame); 2979 // when this window closes, check for saving 2980 if (add) { 2981 frame.addWindowListener(new WindowAdapter() { 2982 @Override 2983 public void windowClosing(WindowEvent e) { 2984 setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE); 2985 if (log.isDebugEnabled()) { 2986 log.debug("windowClosing: HIDE {}", toString()); 2987 } 2988 } 2989 }); 2990 } 2991 } else { 2992 log.error("No icon editor specified for {}", name); // NOI18N 2993 } 2994 if (add) { 2995 txt = MessageFormat.format(Bundle.getMessage("AddItem"), Bundle.getMessage(bundleName)); 2996 _iconEditorFrame.put(name, frame); 2997 } else { 2998 txt = MessageFormat.format(Bundle.getMessage("EditItem"), Bundle.getMessage(bundleName)); 2999 } 3000 frame.setTitle(txt + " (" + getTitle() + ")"); 3001 frame.pack(); 3002 return frame; 3003 } 3004 3005 /* 3006 * ******************* cleanup ************************ 3007 */ 3008 protected void removeFromTarget(@Nonnull Positionable l) { 3009 Point p = l.getLocation(); 3010 _targetPanel.remove((Component) l); 3011 _highlightcomponent = null; 3012 int w = Math.max( l.maxWidth(), l.getWidth()); 3013 int h = Math.max( l.maxHeight(), l.getHeight()); 3014 _targetPanel.revalidate(); 3015 _targetPanel.repaint(p.x, p.y, w, h); 3016 } 3017 3018 public boolean removeFromContents(@Nonnull Positionable l) { 3019 removeFromTarget(l); 3020 //todo check that parent == _targetPanel 3021 //Container parent = this.getParent(); 3022 // force redisplay 3023 if (l.getId() != null) { 3024 _idContents.remove(l.getId()); 3025 } 3026 for (String className : l.getClasses()) { 3027 _classContents.get(className).remove(l); 3028 } 3029 return _contents.remove(l); 3030 } 3031 3032 /** 3033 * Ask user the user to decide what to do with LogixNGs, whether to delete them 3034 * or convert them to normal LogixNGs. Then respond to user's choice. 3035 */ 3036 private void dispositionLogixNGs() { 3037 ArrayList<LogixNG> logixNGArrayList = new ArrayList<>(); 3038 for (Positionable _content : _contents) { 3039 if (_content.getLogixNG() != null) { 3040 LogixNG logixNG = _content.getLogixNG(); 3041 logixNGArrayList.add(logixNG); 3042 } 3043 } 3044 if (!logixNGArrayList.isEmpty()) { 3045 LogixNGDeleteDialog logixNGDeleteDialog = new LogixNGDeleteDialog(this, getTitle(), logixNGArrayList); 3046 logixNGDeleteDialog.setVisible(true); 3047 List<LogixNG> selectedItems = logixNGDeleteDialog.getSelectedItems(); 3048 for (LogixNG logixNG : selectedItems) { 3049 deleteLogixNG_Internal(logixNG); 3050 logixNGArrayList.remove(logixNG); 3051 } 3052 for (LogixNG logixNG : logixNGArrayList) { 3053 logixNG.setInline(false); 3054 logixNG.setEnabled(!logixNGDeleteDialog.isDisableLogixNG()); 3055 } 3056 } 3057 } 3058 3059 /** 3060 * Ask user if panel should be deleted. The caller should dispose the panel 3061 * to delete it. 3062 * 3063 * @return true if panel should be deleted. 3064 */ 3065 public boolean deletePanel() { 3066 log.debug("deletePanel"); 3067 // verify deletion 3068 int selectedValue = JmriJOptionPane.showOptionDialog(_targetPanel, 3069 Bundle.getMessage("QuestionA") + "\n" + Bundle.getMessage("QuestionA2", 3070 Bundle.getMessage("FileMenuItemStore")), 3071 Bundle.getMessage("DeleteVerifyTitle"), JmriJOptionPane.DEFAULT_OPTION, 3072 JmriJOptionPane.QUESTION_MESSAGE, null, 3073 new Object[]{Bundle.getMessage("ButtonYesDelete"), Bundle.getMessage("ButtonCancel")}, 3074 Bundle.getMessage("ButtonCancel")); 3075 // return without deleting if "Cancel" or Cancel Dialog response 3076 if (selectedValue == 0) { 3077 dispositionLogixNGs(); 3078 } 3079 return (selectedValue == 0 ); // array position 0 = Yes, Delete. 3080 } 3081 3082 /** 3083 * Dispose of the editor. 3084 */ 3085 @Override 3086 public void dispose() { 3087 for (JFrameItem frame : _iconEditorFrame.values()) { 3088 frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); 3089 frame.dispose(); 3090 } 3091 // delete panel - deregister the panel for saving 3092 ConfigureManager cm = InstanceManager.getNullableDefault(ConfigureManager.class); 3093 if (cm != null) { 3094 cm.deregister(this); 3095 } 3096 InstanceManager.getDefault(EditorManager.class).remove(this); 3097 setVisible(false); 3098 _contents.clear(); 3099 _idContents.clear(); 3100 for (var list : _classContents.values()) { 3101 list.clear(); 3102 } 3103 _classContents.clear(); 3104 removeAll(); 3105 super.dispose(); 3106 } 3107 3108 /* 3109 * **************** Mouse Methods ********************** 3110 */ 3111 public void showToolTip(Positionable selection, JmriMouseEvent event) { 3112 ToolTip tip = selection.getToolTip(); 3113 tip.setLocation(selection.getX() + selection.getWidth() / 2, selection.getY() + selection.getHeight()); 3114 setToolTip(tip); 3115 } 3116 3117 protected int getItemX(Positionable p, int deltaX) { 3118 if ((p instanceof MemoryOrGVIcon) && (p.getPopupUtility().getFixedWidth() == 0)) { 3119 MemoryOrGVIcon pm = (MemoryOrGVIcon) p; 3120 return pm.getOriginalX() + (int) Math.round(deltaX / getPaintScale()); 3121 } else { 3122 return p.getX() + (int) Math.round(deltaX / getPaintScale()); 3123 } 3124 } 3125 3126 protected int getItemY(Positionable p, int deltaY) { 3127 if ((p instanceof MemoryOrGVIcon) && (p.getPopupUtility().getFixedWidth() == 0)) { 3128 MemoryOrGVIcon pm = (MemoryOrGVIcon) p; 3129 return pm.getOriginalY() + (int) Math.round(deltaY / getPaintScale()); 3130 } else { 3131 return p.getY() + (int) Math.round(deltaY / getPaintScale()); 3132 } 3133 } 3134 3135 /** 3136 * Provide a method for external code to add items to context menus. 3137 * 3138 * @param nb The namedBean associated with the postionable item. 3139 * @param item The entry to add to the menu. 3140 * @param menu The menu to add the entry to. 3141 */ 3142 public void addToPopUpMenu(NamedBean nb, JMenuItem item, int menu) { 3143 if (nb == null || item == null) { 3144 return; 3145 } 3146 for (Positionable pos : _contents) { 3147 if (pos.getNamedBean() == nb && pos.getPopupUtility() != null) { 3148 addToPopUpMenu( pos, item, menu); 3149 return; 3150 } else if (pos instanceof SlipTurnoutIcon) { 3151 if (pos.getPopupUtility() != null) { 3152 SlipTurnoutIcon sti = (SlipTurnoutIcon) pos; 3153 if ( sti.getTurnout(SlipTurnoutIcon.EAST) == nb || sti.getTurnout(SlipTurnoutIcon.WEST) == nb 3154 || sti.getTurnout(SlipTurnoutIcon.LOWEREAST) == nb 3155 || sti.getTurnout(SlipTurnoutIcon.LOWERWEST) == nb) { 3156 addToPopUpMenu( pos, item, menu); 3157 return; 3158 } 3159 } 3160 } else if ( pos instanceof MultiSensorIcon && pos.getPopupUtility() != null) { 3161 MultiSensorIcon msi = (MultiSensorIcon) pos; 3162 for (int i = 0; i < msi.getNumEntries(); i++) { 3163 if ( msi.getSensorName(i).equals(nb.getUserName()) 3164 || msi.getSensorName(i).equals(nb.getSystemName())) { 3165 addToPopUpMenu( pos, item, menu); 3166 return; 3167 } 3168 } 3169 } 3170 } 3171 } 3172 3173 private void addToPopUpMenu( Positionable pos, JMenuItem item, int menu ) { 3174 switch (menu) { 3175 case VIEWPOPUPONLY: 3176 pos.getPopupUtility().addViewPopUpMenu(item); 3177 break; 3178 case EDITPOPUPONLY: 3179 pos.getPopupUtility().addEditPopUpMenu(item); 3180 break; 3181 default: 3182 pos.getPopupUtility().addEditPopUpMenu(item); 3183 pos.getPopupUtility().addViewPopUpMenu(item); 3184 } 3185 } 3186 3187 public static final int VIEWPOPUPONLY = 0x00; 3188 public static final int EDITPOPUPONLY = 0x01; 3189 public static final int BOTHPOPUPS = 0x02; 3190 3191 /** 3192 * Relocate item. 3193 * <p> 3194 * Note that items can not be moved past the left or top edges of the panel. 3195 * 3196 * @param p The item to move. 3197 * @param deltaX The horizontal displacement. 3198 * @param deltaY The vertical displacement. 3199 */ 3200 public void moveItem(Positionable p, int deltaX, int deltaY) { 3201 //log.debug("moveItem at ({},{}) delta ({},{})", p.getX(), p.getY(), deltaX, deltaY); 3202 if (getFlag(OPTION_POSITION, p.isPositionable())) { 3203 int xObj = getItemX(p, deltaX); 3204 int yObj = getItemY(p, deltaY); 3205 // don't allow negative placement, icon can become unreachable 3206 if (xObj < 0) { 3207 xObj = 0; 3208 } 3209 if (yObj < 0) { 3210 yObj = 0; 3211 } 3212 p.setLocation(xObj, yObj); 3213 // and show! 3214 p.repaint(); 3215 } 3216 } 3217 3218 /** 3219 * Return a List of all items whose bounding rectangle contain the mouse 3220 * position. ordered from top level to bottom 3221 * 3222 * @param event contains the mouse position. 3223 * @return a list of positionable items or an empty list. 3224 */ 3225 protected List<Positionable> getSelectedItems(JmriMouseEvent event) { 3226 Rectangle rect = new Rectangle(); 3227 ArrayList<Positionable> selections = new ArrayList<>(); 3228 for (Positionable p : _contents) { 3229 double x = event.getX(); 3230 double y = event.getY(); 3231 rect = p.getBounds(rect); 3232 if (p instanceof jmri.jmrit.display.controlPanelEditor.shape.PositionableShape 3233 && p.getDegrees() != 0) { 3234 double rad = p.getDegrees() * Math.PI / 180.0; 3235 java.awt.geom.AffineTransform t = java.awt.geom.AffineTransform.getRotateInstance(-rad); 3236 double[] pt = new double[2]; 3237 // bit shift to avoid SpotBugs paranoia 3238 pt[0] = x - rect.x - (rect.width >>> 1); 3239 pt[1] = y - rect.y - (rect.height >>> 1); 3240 t.transform(pt, 0, pt, 0, 1); 3241 x = pt[0] + rect.x + (rect.width >>> 1); 3242 y = pt[1] + rect.y + (rect.height >>> 1); 3243 } 3244 Rectangle2D.Double rect2D = new Rectangle2D.Double(rect.x * _paintScale, 3245 rect.y * _paintScale, 3246 rect.width * _paintScale, 3247 rect.height * _paintScale); 3248 if (rect2D.contains(x, y) && (p.getDisplayLevel() > BKG || event.isControlDown())) { 3249 boolean added = false; 3250 int level = p.getDisplayLevel(); 3251 for (int k = 0; k < selections.size(); k++) { 3252 if (level >= selections.get(k).getDisplayLevel()) { 3253 selections.add(k, p); 3254 added = true; // OK to lie in the case of background icon 3255 break; 3256 } 3257 } 3258 if (!added) { 3259 selections.add(p); 3260 } 3261 } 3262 } 3263 //log.debug("getSelectedItems at ({},{}) {} found,", x, y, selections.size()); 3264 return selections; 3265 } 3266 3267 /* 3268 * Gather all items inside _selectRect 3269 * Keep old group if Control key is down 3270 */ 3271 protected void makeSelectionGroup(JmriMouseEvent event) { 3272 if (!event.isControlDown() || _selectionGroup == null) { 3273 _selectionGroup = new ArrayList<>(); 3274 } 3275 Rectangle test = new Rectangle(); 3276 List<Positionable> list = getContents(); 3277 if (event.isShiftDown()) { 3278 for (Positionable comp : list) { 3279 if (_selectRect.intersects(comp.getBounds(test)) 3280 && (event.isControlDown() || comp.getDisplayLevel() > BKG)) { 3281 _selectionGroup.add(comp); 3282 //log.debug("makeSelectionGroup: selection: {}, class= {}", comp.getNameString(), comp.getClass().getName()); 3283 } 3284 } 3285 } else { 3286 for (Positionable comp : list) { 3287 if (_selectRect.contains(comp.getBounds(test)) 3288 && (event.isControlDown() || comp.getDisplayLevel() > BKG)) { 3289 _selectionGroup.add(comp); 3290 //log.debug("makeSelectionGroup: selection: {}, class= {}", comp.getNameString(), comp.getClass().getName()); 3291 } 3292 } 3293 } 3294 log.debug("makeSelectionGroup: {} selected.", _selectionGroup.size()); 3295 if (_selectionGroup.size() < 1) { 3296 _selectRect = null; 3297 deselectSelectionGroup(); 3298 } 3299 } 3300 3301 /* 3302 * For the param, selection, Add to or delete from _selectionGroup. 3303 * If not there, add. 3304 * If there, delete. 3305 * make new group if Cntl key is not held down 3306 */ 3307 protected void modifySelectionGroup(Positionable selection, JmriMouseEvent event) { 3308 if (!event.isControlDown() || _selectionGroup == null) { 3309 _selectionGroup = new ArrayList<>(); 3310 } 3311 boolean removed = false; 3312 if (event.isControlDown()) { 3313 if (selection.getDisplayLevel() > BKG) { 3314 if (_selectionGroup.contains(selection)) { 3315 removed = _selectionGroup.remove(selection); 3316 } else { 3317 _selectionGroup.add(selection); 3318 } 3319 } else if (event.isShiftDown()) { 3320 if (_selectionGroup.contains(selection)) { 3321 removed = _selectionGroup.remove(selection); 3322 } else { 3323 _selectionGroup.add(selection); 3324 } 3325 } 3326 } 3327 log.debug("modifySelectionGroup: size= {}, selection {}", 3328 _selectionGroup.size(), (removed ? "removed" : "added")); 3329 } 3330 3331 /** 3332 * Set attributes of a Positionable. 3333 * 3334 * @param newUtil helper from which to get attributes 3335 * @param p the item to set attributes of 3336 * 3337 */ 3338 public void setAttributes(PositionablePopupUtil newUtil, Positionable p) { 3339 p.setPopupUtility(newUtil.clone(p, p.getTextComponent())); 3340 int mar = newUtil.getMargin(); 3341 int bor = newUtil.getBorderSize(); 3342 Border outlineBorder; 3343 if (bor == 0) { 3344 outlineBorder = BorderFactory.createEmptyBorder(0, 0, 0, 0); 3345 } else { 3346 outlineBorder = new LineBorder(newUtil.getBorderColor(), bor); 3347 } 3348 Border borderMargin; 3349 if (newUtil.hasBackground()) { 3350 borderMargin = new LineBorder(p.getBackground(), mar); 3351 } else { 3352 borderMargin = BorderFactory.createEmptyBorder(mar, mar, mar, mar); 3353 } 3354 p.setBorder(new CompoundBorder(outlineBorder, borderMargin)); 3355 3356 if (p instanceof PositionableLabel) { 3357 PositionableLabel pos = (PositionableLabel) p; 3358 if (pos.isText()) { 3359 int deg = pos.getDegrees(); 3360 pos.rotate(0); 3361 if (deg == 0) { 3362 p.setOpaque(newUtil.hasBackground()); 3363 } else { 3364 pos.rotate(deg); 3365 } 3366 } 3367 } else if (p instanceof PositionableJPanel) { 3368 p.setOpaque(newUtil.hasBackground()); 3369 p.getTextComponent().setOpaque(newUtil.hasBackground()); 3370 } 3371 p.updateSize(); 3372 p.repaint(); 3373 if (p instanceof PositionableIcon) { 3374 NamedBean bean = p.getNamedBean(); 3375 if (bean != null) { 3376 ((PositionableIcon) p).displayState(bean.getState()); 3377 } 3378 } 3379 } 3380 3381 protected void setSelectionsAttributes(PositionablePopupUtil util, Positionable pos) { 3382 if (_selectionGroup != null && _selectionGroup.contains(pos)) { 3383 for (Positionable p : _selectionGroup) { 3384 if (p instanceof PositionableLabel) { 3385 setAttributes(util, p); 3386 } 3387 } 3388 } 3389 } 3390 3391 protected void setSelectionsHidden(boolean enabled, Positionable p) { 3392 if (_selectionGroup != null && _selectionGroup.contains(p)) { 3393 for (Positionable comp : _selectionGroup) { 3394 comp.setHidden(enabled); 3395 } 3396 } 3397 } 3398 3399 protected boolean setSelectionsPositionable(boolean enabled, Positionable p) { 3400 if (_selectionGroup != null && _selectionGroup.contains(p)) { 3401 for (Positionable comp : _selectionGroup) { 3402 comp.setPositionable(enabled); 3403 } 3404 return true; 3405 } else { 3406 return false; 3407 } 3408 } 3409 3410 protected void removeSelections(Positionable p) { 3411 if (_selectionGroup != null && _selectionGroup.contains(p)) { 3412 for (Positionable comp : _selectionGroup) { 3413 comp.remove(); 3414 } 3415 deselectSelectionGroup(); 3416 } 3417 } 3418 3419 protected void setSelectionsScale(double s, Positionable p) { 3420 if (_selectionGroup != null && _selectionGroup.contains(p)) { 3421 for (Positionable comp : _selectionGroup) { 3422 comp.setScale(s); 3423 } 3424 } else { 3425 p.setScale(s); 3426 } 3427 } 3428 3429 protected void setSelectionsRotation(int k, Positionable p) { 3430 if (_selectionGroup != null && _selectionGroup.contains(p)) { 3431 for (Positionable comp : _selectionGroup) { 3432 comp.rotate(k); 3433 } 3434 } else { 3435 p.rotate(k); 3436 } 3437 } 3438 3439 protected void setSelectionsDisplayLevel(int k, Positionable p) { 3440 if (_selectionGroup != null && _selectionGroup.contains(p)) { 3441 for (Positionable comp : _selectionGroup) { 3442 comp.setDisplayLevel(k); 3443 } 3444 } else { 3445 p.setDisplayLevel(k); 3446 } 3447 } 3448 3449 protected void setSelectionsDockingLocation(Positionable p) { 3450 if (_selectionGroup != null && _selectionGroup.contains(p)) { 3451 for (Positionable pos : _selectionGroup) { 3452 if (pos instanceof LocoIcon) { 3453 ((LocoIcon) pos).setDockingLocation(pos.getX(), pos.getY()); 3454 } 3455 } 3456 } else if (p instanceof LocoIcon) { 3457 ((LocoIcon) p).setDockingLocation(p.getX(), p.getY()); 3458 } 3459 } 3460 3461 protected void dockSelections(Positionable p) { 3462 if (_selectionGroup != null && _selectionGroup.contains(p)) { 3463 for (Positionable pos : _selectionGroup) { 3464 if (pos instanceof LocoIcon) { 3465 ((LocoIcon) pos).dock(); 3466 } 3467 } 3468 } else if (p instanceof LocoIcon) { 3469 ((LocoIcon) p).dock(); 3470 } 3471 } 3472 3473 protected boolean showAlignPopup(Positionable p) { 3474 return _selectionGroup != null && _selectionGroup.contains(p); 3475 } 3476 3477 public Rectangle getSelectRect() { 3478 return _selectRect; 3479 } 3480 3481 public void drawSelectRect(int x, int y) { 3482 int aX = getAnchorX(); 3483 int aY = getAnchorY(); 3484 int w = x - aX; 3485 int h = y - aY; 3486 if (x < aX) { 3487 aX = x; 3488 w = -w; 3489 } 3490 if (y < aY) { 3491 aY = y; 3492 h = -h; 3493 } 3494 _selectRect = new Rectangle((int) Math.round(aX / _paintScale), (int) Math.round(aY / _paintScale), 3495 (int) Math.round(w / _paintScale), (int) Math.round(h / _paintScale)); 3496 } 3497 3498 public final int getAnchorX() { 3499 return _anchorX; 3500 } 3501 3502 public final int getAnchorY() { 3503 return _anchorY; 3504 } 3505 3506 public final int getLastX() { 3507 return _lastX; 3508 } 3509 3510 public final int getLastY() { 3511 return _lastY; 3512 } 3513 3514 @Override 3515 public void keyTyped(KeyEvent e) { 3516 } 3517 3518 @Override 3519 public void keyPressed(KeyEvent e) { 3520 if (_selectionGroup == null) { 3521 return; 3522 } 3523 int x = 0; 3524 int y = 0; 3525 switch (e.getKeyCode()) { 3526 case KeyEvent.VK_UP: 3527 y = -1; 3528 break; 3529 case KeyEvent.VK_DOWN: 3530 y = 1; 3531 break; 3532 case KeyEvent.VK_LEFT: 3533 x = -1; 3534 break; 3535 case KeyEvent.VK_RIGHT: 3536 x = 1; 3537 break; 3538 default: 3539 log.debug("Unexpected e.getKeyCode() of {}", e.getKeyCode()); 3540 break; 3541 } 3542 //A cheat if the shift key isn't pressed then we move 5 pixels at a time. 3543 if (!e.isShiftDown()) { 3544 y *= 5; 3545 x *= 5; 3546 } 3547 for (Positionable comp : _selectionGroup) { 3548 moveItem(comp, x, y); 3549 } 3550 _targetPanel.repaint(); 3551 } 3552 3553 @Override 3554 public void keyReleased(KeyEvent e) { 3555 } 3556 3557 @Override 3558 public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException { 3559 NamedBean nb = (NamedBean) evt.getOldValue(); 3560 if ("CanDelete".equals(evt.getPropertyName())) { // NOI18N 3561 StringBuilder message = new StringBuilder(); 3562 message.append(Bundle.getMessage("VetoInUseEditorHeader", getName())); // NOI18N 3563 message.append("<br>"); 3564 boolean found = false; 3565 int count = 0; 3566 for (Positionable p : _contents) { 3567 if (nb.equals(p.getNamedBean())) { 3568 found = true; 3569 count++; 3570 } 3571 } 3572 if (found) { 3573 message.append(Bundle.getMessage("VetoFoundInPanel", count)); 3574 message.append("<br>"); 3575 message.append(Bundle.getMessage("VetoReferencesWillBeRemoved")); // NOI18N 3576 message.append("<br>"); 3577 throw new PropertyVetoException(message.toString(), evt); 3578 } 3579 } else if ("DoDelete".equals(evt.getPropertyName())) { // NOI18N 3580 ArrayList<Positionable> toDelete = new ArrayList<>(); 3581 for (Positionable p : _contents) { 3582 if (nb.equals(p.getNamedBean())) { 3583 toDelete.add(p); 3584 } 3585 } 3586 for (Positionable p : toDelete) { 3587 removeFromContents(p); 3588 _targetPanel.repaint(); 3589 } 3590 } 3591 } 3592 3593 /* 3594 * ********************* Abstract Methods *********************** 3595 */ 3596 @Override 3597 public abstract void mousePressed(JmriMouseEvent event); 3598 3599 @Override 3600 public abstract void mouseReleased(JmriMouseEvent event); 3601 3602 @Override 3603 public abstract void mouseClicked(JmriMouseEvent event); 3604 3605 @Override 3606 public abstract void mouseDragged(JmriMouseEvent event); 3607 3608 @Override 3609 public abstract void mouseMoved(JmriMouseEvent event); 3610 3611 @Override 3612 public abstract void mouseEntered(JmriMouseEvent event); 3613 3614 @Override 3615 public abstract void mouseExited(JmriMouseEvent event); 3616 3617 /* 3618 * set up target panel, frame etc. 3619 */ 3620 protected abstract void init(String name); 3621 3622 /* 3623 * Closing of Target frame window. 3624 */ 3625 protected abstract void targetWindowClosingEvent(WindowEvent e); 3626 3627 /** 3628 * Called from TargetPanel's paint method for additional drawing by editor 3629 * view. 3630 * 3631 * @param g the context to paint within 3632 */ 3633 protected abstract void paintTargetPanel(Graphics g); 3634 3635 /** 3636 * Set an object's location when it is created. 3637 * 3638 * @param obj the object to locate 3639 */ 3640 protected abstract void setNextLocation(Positionable obj); 3641 3642 /** 3643 * After construction, initialize all the widgets to their saved config 3644 * settings. 3645 */ 3646 protected abstract void initView(); 3647 3648 /** 3649 * Set up item(s) to be copied by paste. 3650 * 3651 * @param p the item to copy 3652 */ 3653 protected abstract void copyItem(Positionable p); 3654 3655 public List<NamedBeanUsageReport> getUsageReport(NamedBean bean) { 3656 List<NamedBeanUsageReport> report = new ArrayList<>(); 3657 if (bean != null) { 3658 getContents().forEach( pos -> { 3659 String data = getUsageData(pos); 3660 if (pos instanceof MultiSensorIcon) { 3661 MultiSensorIcon multi = (MultiSensorIcon) pos; 3662 multi.getSensors().forEach( sensor -> { 3663 if (bean.equals(sensor)) { 3664 report.add(new NamedBeanUsageReport("PositionalIcon", data)); 3665 } 3666 }); 3667 3668 } else if (pos instanceof SlipTurnoutIcon) { 3669 SlipTurnoutIcon slip3Scissor = (SlipTurnoutIcon) pos; 3670 if (bean.equals(slip3Scissor.getTurnout(SlipTurnoutIcon.EAST))) { 3671 report.add(new NamedBeanUsageReport("PositionalIcon", data)); 3672 } 3673 if (bean.equals(slip3Scissor.getTurnout(SlipTurnoutIcon.WEST))) { 3674 report.add(new NamedBeanUsageReport("PositionalIcon", data)); 3675 } 3676 if ( slip3Scissor.getNamedTurnout(SlipTurnoutIcon.LOWEREAST) != null 3677 && bean.equals(slip3Scissor.getTurnout(SlipTurnoutIcon.LOWEREAST))) { 3678 report.add(new NamedBeanUsageReport("PositionalIcon", data)); 3679 } 3680 if ( slip3Scissor.getNamedTurnout(SlipTurnoutIcon.LOWERWEST) != null 3681 && bean.equals(slip3Scissor.getTurnout(SlipTurnoutIcon.LOWERWEST))) { 3682 report.add(new NamedBeanUsageReport("PositionalIcon", data)); 3683 } 3684 3685 } else if (pos instanceof LightIcon) { 3686 LightIcon icon = (LightIcon) pos; 3687 if (bean.equals(icon.getLight())) { 3688 report.add(new NamedBeanUsageReport("PositionalIcon", data)); 3689 } 3690 3691 } else if (pos instanceof ReporterIcon) { 3692 ReporterIcon icon = (ReporterIcon) pos; 3693 if (bean.equals(icon.getReporter())) { 3694 report.add(new NamedBeanUsageReport("PositionalIcon", data)); 3695 } 3696 3697 } else if (pos instanceof AudioIcon) { 3698 AudioIcon icon = (AudioIcon) pos; 3699 if (bean.equals(icon.getAudio())) { 3700 report.add(new NamedBeanUsageReport("PositionalIcon", data)); 3701 } 3702 3703 } else { 3704 if ( bean.equals(pos.getNamedBean())) { 3705 report.add(new NamedBeanUsageReport("PositionalIcon", data)); 3706 } 3707 } 3708 }); 3709 } 3710 return report; 3711 } 3712 3713 String getUsageData(Positionable pos) { 3714 Point point = pos.getLocation(); 3715 return String.format("%s :: x=%d, y=%d", 3716 pos.getClass().getSimpleName(), 3717 Math.round(point.getX()), 3718 Math.round(point.getY())); 3719 } 3720 3721 // initialize logging 3722 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(Editor.class); 3723 3724}