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