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