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