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 return ed; 1064 } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException cnfe) { 1065 log.error("changeView exception {}", cnfe.toString()); 1066 } 1067 return null; 1068 } 1069 1070 /* 1071 * *********************** Popup Item Methods ********************** 1072 * 1073 * These methods are to be called from the editor view's showPopUp method 1074 */ 1075 /** 1076 * Add a checkbox to lock the position of the Positionable item. 1077 * 1078 * @param p the item 1079 * @param popup the menu to add the lock menu item to 1080 */ 1081 public void setPositionableMenu(Positionable p, JPopupMenu popup) { 1082 JCheckBoxMenuItem lockItem = new JCheckBoxMenuItem(Bundle.getMessage("LockPosition")); 1083 lockItem.setSelected(!p.isPositionable()); 1084 lockItem.addActionListener(new ActionListener() { 1085 private Positionable comp; 1086 private JCheckBoxMenuItem checkBox; 1087 1088 @Override 1089 public void actionPerformed(ActionEvent e) { 1090 comp.setPositionable(!checkBox.isSelected()); 1091 setSelectionsPositionable(!checkBox.isSelected(), comp); 1092 } 1093 1094 ActionListener init(Positionable pos, JCheckBoxMenuItem cb) { 1095 comp = pos; 1096 checkBox = cb; 1097 return this; 1098 } 1099 }.init(p, lockItem)); 1100 popup.add(lockItem); 1101 } 1102 1103 /** 1104 * Display the {@literal X & Y} coordinates of the Positionable item and 1105 * provide a dialog menu item to edit them. 1106 * 1107 * @param p The item to add the menu item to 1108 * @param popup The menu item to add the action to 1109 * @return always returns true 1110 */ 1111 public boolean setShowCoordinatesMenu(Positionable p, JPopupMenu popup) { 1112 1113 JMenuItem edit; 1114 if ((p instanceof MemoryOrGVIcon) && (p.getPopupUtility().getFixedWidth() == 0)) { 1115 MemoryOrGVIcon pm = (MemoryOrGVIcon) p; 1116 1117 edit = new JMenuItem(Bundle.getMessage( 1118 "EditLocationXY", pm.getOriginalX(), pm.getOriginalY())); 1119 1120 edit.addActionListener(MemoryIconCoordinateEdit.getCoordinateEditAction(pm)); 1121 } else { 1122 edit = new JMenuItem(Bundle.getMessage( 1123 "EditLocationXY", p.getX(), p.getY())); 1124 edit.addActionListener(CoordinateEdit.getCoordinateEditAction(p)); 1125 } 1126 popup.add(edit); 1127 return true; 1128 } 1129 1130 /** 1131 * Offer actions to align the selected Positionable items either 1132 * Horizontally (at average y coordinates) or Vertically (at average x 1133 * coordinates). 1134 * 1135 * @param p The positionable item 1136 * @param popup The menu to add entries to 1137 * @return true if entries added to menu 1138 */ 1139 public boolean setShowAlignmentMenu(Positionable p, JPopupMenu popup) { 1140 if (showAlignPopup(p)) { 1141 JMenu edit = new JMenu(Bundle.getMessage("EditAlignment")); 1142 edit.add(new AbstractAction(Bundle.getMessage("AlignX")) { 1143 private int _x; 1144 1145 @Override 1146 public void actionPerformed(ActionEvent e) { 1147 if (_selectionGroup == null) { 1148 return; 1149 } 1150 for (Positionable comp : _selectionGroup) { 1151 if (!getFlag(OPTION_POSITION, comp.isPositionable())) { 1152 continue; 1153 } 1154 comp.setLocation(_x, comp.getY()); 1155 } 1156 } 1157 1158 AbstractAction init(int x) { 1159 _x = x; 1160 return this; 1161 } 1162 }.init(p.getX())); 1163 edit.add(new AbstractAction(Bundle.getMessage("AlignMiddleX")) { 1164 private int _x; 1165 1166 @Override 1167 public void actionPerformed(ActionEvent e) { 1168 if (_selectionGroup == null) { 1169 return; 1170 } 1171 for (Positionable comp : _selectionGroup) { 1172 if (!getFlag(OPTION_POSITION, comp.isPositionable())) { 1173 continue; 1174 } 1175 comp.setLocation(_x - comp.getWidth() / 2, comp.getY()); 1176 } 1177 } 1178 1179 AbstractAction init(int x) { 1180 _x = x; 1181 return this; 1182 } 1183 }.init(p.getX() + p.getWidth() / 2)); 1184 edit.add(new AbstractAction(Bundle.getMessage("AlignOtherX")) { 1185 private int _x; 1186 1187 @Override 1188 public void actionPerformed(ActionEvent e) { 1189 if (_selectionGroup == null) { 1190 return; 1191 } 1192 for (Positionable comp : _selectionGroup) { 1193 if (!getFlag(OPTION_POSITION, comp.isPositionable())) { 1194 continue; 1195 } 1196 comp.setLocation(_x - comp.getWidth(), comp.getY()); 1197 } 1198 } 1199 1200 AbstractAction init(int x) { 1201 _x = x; 1202 return this; 1203 } 1204 }.init(p.getX() + p.getWidth())); 1205 edit.add(new AbstractAction(Bundle.getMessage("AlignY")) { 1206 private int _y; 1207 1208 @Override 1209 public void actionPerformed(ActionEvent e) { 1210 if (_selectionGroup == null) { 1211 return; 1212 } 1213 for (Positionable comp : _selectionGroup) { 1214 if (!getFlag(OPTION_POSITION, comp.isPositionable())) { 1215 continue; 1216 } 1217 comp.setLocation(comp.getX(), _y); 1218 } 1219 } 1220 1221 AbstractAction init(int y) { 1222 _y = y; 1223 return this; 1224 } 1225 }.init(p.getY())); 1226 edit.add(new AbstractAction(Bundle.getMessage("AlignMiddleY")) { 1227 private int _y; 1228 1229 @Override 1230 public void actionPerformed(ActionEvent e) { 1231 if (_selectionGroup == null) { 1232 return; 1233 } 1234 for (Positionable comp : _selectionGroup) { 1235 if (!getFlag(OPTION_POSITION, comp.isPositionable())) { 1236 continue; 1237 } 1238 comp.setLocation(comp.getX(), _y - comp.getHeight() / 2); 1239 } 1240 } 1241 1242 AbstractAction init(int y) { 1243 _y = y; 1244 return this; 1245 } 1246 }.init(p.getY() + p.getHeight() / 2)); 1247 edit.add(new AbstractAction(Bundle.getMessage("AlignOtherY")) { 1248 private int _y; 1249 1250 @Override 1251 public void actionPerformed(ActionEvent e) { 1252 if (_selectionGroup == null) { 1253 return; 1254 } 1255 for (Positionable comp : _selectionGroup) { 1256 if (!getFlag(OPTION_POSITION, comp.isPositionable())) { 1257 continue; 1258 } 1259 comp.setLocation(comp.getX(), _y - comp.getHeight()); 1260 } 1261 } 1262 1263 AbstractAction init(int y) { 1264 _y = y; 1265 return this; 1266 } 1267 }.init(p.getY() + p.getHeight())); 1268 edit.add(new AbstractAction(Bundle.getMessage("AlignXFirst")) { 1269 1270 @Override 1271 public void actionPerformed(ActionEvent e) { 1272 if (_selectionGroup == null) { 1273 return; 1274 } 1275 int x = _selectionGroup.get(0).getX(); 1276 for (int i = 1; i < _selectionGroup.size(); i++) { 1277 Positionable comp = _selectionGroup.get(i); 1278 if (!getFlag(OPTION_POSITION, comp.isPositionable())) { 1279 continue; 1280 } 1281 comp.setLocation(x, comp.getY()); 1282 } 1283 } 1284 }); 1285 edit.add(new AbstractAction(Bundle.getMessage("AlignYFirst")) { 1286 1287 @Override 1288 public void actionPerformed(ActionEvent e) { 1289 if (_selectionGroup == null) { 1290 return; 1291 } 1292 int y = _selectionGroup.get(0).getX(); 1293 for (int i = 1; i < _selectionGroup.size(); i++) { 1294 Positionable comp = _selectionGroup.get(i); 1295 if (!getFlag(OPTION_POSITION, comp.isPositionable())) { 1296 continue; 1297 } 1298 comp.setLocation(comp.getX(), y); 1299 } 1300 } 1301 }); 1302 popup.add(edit); 1303 return true; 1304 } 1305 return false; 1306 } 1307 1308 /** 1309 * Display 'z' level of the Positionable item and provide a dialog 1310 * menu item to edit it. 1311 * 1312 * @param p The item 1313 * @param popup the menu to add entries to 1314 */ 1315 public void setDisplayLevelMenu(Positionable p, JPopupMenu popup) { 1316 JMenuItem edit = new JMenuItem(Bundle.getMessage("EditLevel_", p.getDisplayLevel())); 1317 edit.addActionListener(CoordinateEdit.getLevelEditAction(p)); 1318 popup.add(edit); 1319 } 1320 1321 /** 1322 * Add a menu entry to set visibility of the Positionable item 1323 * 1324 * @param p the item 1325 * @param popup the menu to add the entry to 1326 */ 1327 public void setHiddenMenu(Positionable p, JPopupMenu popup) { 1328 if (p.getDisplayLevel() == BKG) { 1329 return; 1330 } 1331 JCheckBoxMenuItem hideItem = new JCheckBoxMenuItem(Bundle.getMessage("SetHidden")); 1332 hideItem.setSelected(p.isHidden()); 1333 hideItem.addActionListener(new ActionListener() { 1334 private Positionable comp; 1335 private JCheckBoxMenuItem checkBox; 1336 1337 @Override 1338 public void actionPerformed(ActionEvent e) { 1339 comp.setHidden(checkBox.isSelected()); 1340 setSelectionsHidden(checkBox.isSelected(), comp); 1341 } 1342 1343 ActionListener init(Positionable pos, JCheckBoxMenuItem cb) { 1344 comp = pos; 1345 checkBox = cb; 1346 return this; 1347 } 1348 }.init(p, hideItem)); 1349 popup.add(hideItem); 1350 } 1351 1352 /** 1353 * Add a menu entry to set visibility of the Positionable item based on the presence of contents. 1354 * If the value is null or empty, the icon is not visible. 1355 * This is applicable to memory, block content and LogixNG global variable labels. 1356 * 1357 * @param p the item 1358 * @param popup the menu to add the entry to 1359 */ 1360 public void setEmptyHiddenMenu(Positionable p, JPopupMenu popup) { 1361 if (p.getDisplayLevel() == BKG) { 1362 return; 1363 } 1364 if (p instanceof BlockContentsIcon || p instanceof MemoryIcon || p instanceof GlobalVariableIcon) { 1365 JCheckBoxMenuItem hideEmptyItem = new JCheckBoxMenuItem(Bundle.getMessage("SetEmptyHidden")); 1366 hideEmptyItem.setSelected(p.isEmptyHidden()); 1367 hideEmptyItem.addActionListener(new ActionListener() { 1368 private Positionable comp; 1369 private JCheckBoxMenuItem checkBox; 1370 1371 @Override 1372 public void actionPerformed(ActionEvent e) { 1373 comp.setEmptyHidden(checkBox.isSelected()); 1374 } 1375 1376 ActionListener init(Positionable pos, JCheckBoxMenuItem cb) { 1377 comp = pos; 1378 checkBox = cb; 1379 return this; 1380 } 1381 }.init(p, hideEmptyItem)); 1382 popup.add(hideEmptyItem); 1383 } 1384 } 1385 1386 /** 1387 * Add a menu entry to disable double click value edits. This applies when not in panel edit mode. 1388 * This is applicable to memory, block content and LogixNG global variable labels. 1389 * 1390 * @param p the item 1391 * @param popup the menu to add the entry to 1392 */ 1393 public void setValueEditDisabledMenu(Positionable p, JPopupMenu popup) { 1394 if (p.getDisplayLevel() == BKG) { 1395 return; 1396 } 1397 if (p instanceof BlockContentsIcon || p instanceof MemoryIcon || p instanceof GlobalVariableIcon) { 1398 JCheckBoxMenuItem valueEditDisableItem = new JCheckBoxMenuItem(Bundle.getMessage("SetValueEditDisabled")); 1399 valueEditDisableItem.setSelected(p.isValueEditDisabled()); 1400 valueEditDisableItem.addActionListener(new ActionListener() { 1401 private Positionable comp; 1402 private JCheckBoxMenuItem checkBox; 1403 1404 @Override 1405 public void actionPerformed(ActionEvent e) { 1406 comp.setValueEditDisabled(checkBox.isSelected()); 1407 } 1408 1409 ActionListener init(Positionable pos, JCheckBoxMenuItem cb) { 1410 comp = pos; 1411 checkBox = cb; 1412 return this; 1413 } 1414 }.init(p, valueEditDisableItem)); 1415 popup.add(valueEditDisableItem); 1416 } 1417 } 1418 1419 /** 1420 * Add a menu entry to edit Id of the Positionable item 1421 * 1422 * @param p the item 1423 * @param popup the menu to add the entry to 1424 */ 1425 public void setEditIdMenu(Positionable p, JPopupMenu popup) { 1426 if (p.getDisplayLevel() == BKG) { 1427 return; 1428 } 1429 1430 popup.add(CoordinateEdit.getIdEditAction(p, "EditId", this)); 1431 } 1432 1433 /** 1434 * Add a menu entry to edit Classes of the Positionable item 1435 * 1436 * @param p the item 1437 * @param popup the menu to add the entry to 1438 */ 1439 public void setEditClassesMenu(Positionable p, JPopupMenu popup) { 1440 if (p.getDisplayLevel() == BKG) { 1441 return; 1442 } 1443 1444 popup.add(CoordinateEdit.getClassesEditAction(p, "EditClasses", this)); 1445 } 1446 1447 /** 1448 * Check if edit of a conditional is in progress. 1449 * 1450 * @return true if this is the case, after showing dialog to user 1451 */ 1452 private boolean checkEditConditionalNG() { 1453 if (_inEditInlineLogixNGMode) { 1454 // Already editing a LogixNG, ask for completion of that edit 1455 JmriJOptionPane.showMessageDialog(null, 1456 Bundle.getMessage("Error_InlineLogixNGInEditMode"), // NOI18N 1457 Bundle.getMessage("ErrorTitle"), // NOI18N 1458 JmriJOptionPane.ERROR_MESSAGE); 1459 _inlineLogixNGEdit.bringToFront(); 1460 return true; 1461 } 1462 return false; 1463 } 1464 1465 /** 1466 * Add a menu entry to edit Id of the Positionable item 1467 * 1468 * @param p the item 1469 * @param popup the menu to add the entry to 1470 */ 1471 public void setLogixNGPositionableMenu(Positionable p, JPopupMenu popup) { 1472 if (p.getDisplayLevel() == BKG) { 1473 return; 1474 } 1475 1476 JMenu logixNG_Menu = new JMenu("LogixNG"); 1477 popup.add(logixNG_Menu); 1478 1479 logixNG_Menu.add(new AbstractAction(Bundle.getMessage("LogixNG_Inline")) { 1480 @Override 1481 public void actionPerformed(ActionEvent e) { 1482 if (checkEditConditionalNG()) { 1483 return; 1484 } 1485 1486 if (p.getLogixNG() == null) { 1487 LogixNG logixNG = InstanceManager.getDefault(LogixNG_Manager.class) 1488 .createLogixNG(null, true); 1489 logixNG.setInlineLogixNG(p); 1490 logixNG.activate(); 1491 logixNG.setEnabled(true); 1492 logixNG.clearStartup(); 1493 p.setLogixNG(logixNG); 1494 } 1495 LogixNGEditor logixNGEditor = new LogixNGEditor(null, p.getLogixNG().getSystemName()); 1496 logixNGEditor.addEditorEventListener((HashMap<String, String> data) -> { 1497 _inEditInlineLogixNGMode = false; 1498 data.forEach((key, value) -> { 1499 if (key.equals("Finish")) { // NOI18N 1500 _inlineLogixNGEdit = null; 1501 _inEditInlineLogixNGMode = false; 1502 } else if (key.equals("Delete")) { // NOI18N 1503 _inEditInlineLogixNGMode = false; 1504 deleteLogixNG(p.getLogixNG()); 1505 } else if (key.equals("chgUname")) { // NOI18N 1506 p.getLogixNG().setUserName(value); 1507 } 1508 }); 1509 if (p.getLogixNG() != null && p.getLogixNG().getNumConditionalNGs() == 0) { 1510 deleteLogixNG_Internal(p.getLogixNG()); 1511 } 1512 }); 1513 logixNGEditor.bringToFront(); 1514 _inEditInlineLogixNGMode = true; 1515 _inlineLogixNGEdit = logixNGEditor; 1516 } 1517 }); 1518 } 1519 1520 private void deleteLogixNG(LogixNG logixNG) { 1521 DeleteBean<LogixNG> deleteBean = new DeleteBean<>( 1522 InstanceManager.getDefault(LogixNG_Manager.class)); 1523 1524 boolean hasChildren = logixNG.getNumConditionalNGs() > 0; 1525 1526 deleteBean.delete(logixNG, hasChildren, t -> deleteLogixNG_Internal(t), 1527 (t,list) -> logixNG.getListenerRefsIncludingChildren(list), 1528 jmri.jmrit.logixng.LogixNG_UserPreferences.class.getName()); 1529 } 1530 1531 private void deleteLogixNG_Internal(LogixNG logixNG) { 1532 logixNG.setEnabled(false); 1533 try { 1534 InstanceManager.getDefault(LogixNG_Manager.class).deleteBean(logixNG, "DoDelete"); 1535 logixNG.getInlineLogixNG().setLogixNG(null); 1536 } catch (PropertyVetoException e) { 1537 //At this stage the DoDelete shouldn't fail, as we have already done a can delete, which would trigger a veto 1538 log.error("{} : Could not Delete.", e.getMessage()); 1539 } 1540 } 1541 1542 /** 1543 * Check if it's possible to change the id of the Positionable to the 1544 * desired string. 1545 * @param p the Positionable 1546 * @param newId the desired new id 1547 * @throws jmri.jmrit.display.Positionable.DuplicateIdException if another 1548 * Positionable in the editor already has this id 1549 */ 1550 public void positionalIdChange(Positionable p, String newId) 1551 throws Positionable.DuplicateIdException { 1552 1553 if (Objects.equals(newId, p.getId())) { 1554 return; 1555 } 1556 1557 if ((newId != null) && (_idContents.containsKey(newId))) { 1558 throw new Positionable.DuplicateIdException(); 1559 } 1560 1561 if (p.getId() != null) { 1562 _idContents.remove(p.getId()); 1563 } 1564 if (newId != null) { 1565 _idContents.put(newId, p); 1566 } 1567 } 1568 1569 /** 1570 * Add a class name to the Positionable 1571 * @param p the Positionable 1572 * @param className the class name 1573 * @throws IllegalArgumentException if the name contains a comma 1574 */ 1575 public void positionalAddClass(Positionable p, String className) { 1576 1577 if (className == null) { 1578 throw new IllegalArgumentException("Class name must not be null"); 1579 } 1580 if (className.isBlank()) { 1581 throw new IllegalArgumentException("Class name must not be blank"); 1582 } 1583 if (className.contains(",")) { 1584 throw new IllegalArgumentException("Class name must not contain a comma"); 1585 } 1586 1587 if (p.getClasses().contains(className)) { 1588 return; 1589 } 1590 1591 _classContents.computeIfAbsent(className, o -> new HashSet<>()).add(p); 1592 } 1593 1594 /** 1595 * Removes a class name from the Positionable 1596 * @param p the Positionable 1597 * @param className the class name 1598 */ 1599 public void positionalRemoveClass(Positionable p, String className) { 1600 1601 if (p.getClasses().contains(className)) { 1602 return; 1603 } 1604 _classContents.get(className).remove(p); 1605 } 1606 1607 /** 1608 * Add a checkbox to display a tooltip for the Positionable item and if 1609 * showable, provide a dialog menu to edit it. 1610 * 1611 * @param p the item to set the menu for 1612 * @param popup the menu to add for p 1613 */ 1614 public void setShowToolTipMenu(Positionable p, JPopupMenu popup) { 1615 if (p.getDisplayLevel() == BKG) { 1616 return; 1617 } 1618 1619 JMenu edit = new JMenu(Bundle.getMessage("EditTooltip")); 1620 1621 JCheckBoxMenuItem showToolTipItem = new JCheckBoxMenuItem(Bundle.getMessage("ShowTooltip")); 1622 showToolTipItem.setSelected(p.showToolTip()); 1623 showToolTipItem.addActionListener(new ActionListener() { 1624 private Positionable comp; 1625 private JCheckBoxMenuItem checkBox; 1626 1627 @Override 1628 public void actionPerformed(ActionEvent e) { 1629 comp.setShowToolTip(checkBox.isSelected()); 1630 } 1631 1632 ActionListener init(Positionable pos, JCheckBoxMenuItem cb) { 1633 comp = pos; 1634 checkBox = cb; 1635 return this; 1636 } 1637 }.init(p, showToolTipItem)); 1638 edit.add(showToolTipItem); 1639 1640 edit.add(CoordinateEdit.getToolTipEditAction(p)); 1641 1642 JCheckBoxMenuItem prependToolTipWithDisplayNameItem = 1643 new JCheckBoxMenuItem(Bundle.getMessage("PrependTooltipWithDisplayName")); 1644 prependToolTipWithDisplayNameItem.setSelected(p.getToolTip().getPrependToolTipWithDisplayName()); 1645 prependToolTipWithDisplayNameItem.addActionListener(new ActionListener() { 1646 private Positionable comp; 1647 private JCheckBoxMenuItem checkBox; 1648 1649 @Override 1650 public void actionPerformed(ActionEvent e) { 1651 comp.getToolTip().setPrependToolTipWithDisplayName(checkBox.isSelected()); 1652 } 1653 1654 ActionListener init(Positionable pos, JCheckBoxMenuItem cb) { 1655 comp = pos; 1656 checkBox = cb; 1657 return this; 1658 } 1659 }.init(p, prependToolTipWithDisplayNameItem)); 1660 edit.add(prependToolTipWithDisplayNameItem); 1661 1662 popup.add(edit); 1663 } 1664 1665 /** 1666 * Add an action to remove the Positionable item. 1667 * 1668 * @param p the item to set the menu for 1669 * @param popup the menu to add for p 1670 */ 1671 public void setRemoveMenu(Positionable p, JPopupMenu popup) { 1672 popup.add(new AbstractAction(Bundle.getMessage("Remove")) { 1673 private Positionable comp; 1674 1675 @Override 1676 public void actionPerformed(ActionEvent e) { 1677 comp.remove(); 1678 removeSelections(comp); 1679 } 1680 1681 AbstractAction init(Positionable pos) { 1682 comp = pos; 1683 return this; 1684 } 1685 }.init(p)); 1686 } 1687 1688 /* 1689 * *********************** End Popup Methods ********************** 1690 */ 1691 /* 1692 * ****************** Marker Menu *************************** 1693 */ 1694 protected void locoMarkerFromRoster() { 1695 final JmriJFrame locoRosterFrame = new JmriJFrame(); 1696 locoRosterFrame.getContentPane().setLayout(new FlowLayout()); 1697 locoRosterFrame.setTitle(Bundle.getMessage("LocoFromRoster")); 1698 JLabel mtext = new JLabel(); 1699 mtext.setText(Bundle.getMessage("SelectLoco") + ":"); 1700 locoRosterFrame.getContentPane().add(mtext); 1701 final RosterEntrySelectorPanel rosterBox = new RosterEntrySelectorPanel(); 1702 rosterBox.addPropertyChangeListener("selectedRosterEntries", pce -> { 1703 if (rosterBox.getSelectedRosterEntries().length != 0) { 1704 selectLoco(rosterBox.getSelectedRosterEntries()[0]); 1705 } 1706 }); 1707 locoRosterFrame.getContentPane().add(rosterBox); 1708 locoRosterFrame.addWindowListener(new WindowAdapter() { 1709 @Override 1710 public void windowClosing(WindowEvent e) { 1711 locoRosterFrame.dispose(); 1712 } 1713 }); 1714 locoRosterFrame.pack(); 1715 locoRosterFrame.setVisible(true); 1716 } 1717 1718 protected LocoIcon selectLoco(String rosterEntryTitle) { 1719 if ("".equals(rosterEntryTitle)) { 1720 return null; 1721 } 1722 return selectLoco(Roster.getDefault().entryFromTitle(rosterEntryTitle)); 1723 } 1724 1725 protected LocoIcon selectLoco(RosterEntry entry) { 1726 LocoIcon l = null; 1727 if (entry == null) { 1728 return null; 1729 } 1730 // try getting road number, else use DCC address 1731 String rn = entry.getRoadNumber(); 1732 if ((rn == null) || rn.equals("")) { 1733 rn = entry.getDccAddress(); 1734 } 1735 if (rn != null) { 1736 l = addLocoIcon(rn); 1737 l.setRosterEntry(entry); 1738 } 1739 return l; 1740 } 1741 1742 protected void locoMarkerFromInput() { 1743 final JmriJFrame locoFrame = new JmriJFrame(); 1744 locoFrame.getContentPane().setLayout(new FlowLayout()); 1745 locoFrame.setTitle(Bundle.getMessage("EnterLocoMarker")); 1746 1747 JLabel textId = new JLabel(); 1748 textId.setText(Bundle.getMessage("LocoID") + ":"); 1749 locoFrame.getContentPane().add(textId); 1750 1751 final JTextField locoId = new JTextField(7); 1752 locoFrame.getContentPane().add(locoId); 1753 locoId.setText(""); 1754 locoId.setToolTipText(Bundle.getMessage("EnterLocoID")); 1755 JButton okay = new JButton(); 1756 okay.setText(Bundle.getMessage("ButtonOK")); 1757 okay.addActionListener(e -> { 1758 String nameID = locoId.getText(); 1759 if ((nameID != null) && !(nameID.trim().equals(""))) { 1760 addLocoIcon(nameID.trim()); 1761 } else { 1762 JmriJOptionPane.showMessageDialog(locoFrame, Bundle.getMessage("ErrorEnterLocoID"), 1763 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 1764 } 1765 }); 1766 locoFrame.getContentPane().add(okay); 1767 locoFrame.addWindowListener(new WindowAdapter() { 1768 @Override 1769 public void windowClosing(WindowEvent e) { 1770 locoFrame.dispose(); 1771 } 1772 }); 1773 locoFrame.pack(); 1774 if (_targetFrame != null) { 1775 locoFrame.setLocation(_targetFrame.getLocation()); 1776 } 1777 locoFrame.setVisible(true); 1778 } 1779 1780 /** 1781 * Remove marker icons from panel 1782 */ 1783 protected void removeMarkers() { 1784 log.debug("Remove markers"); 1785 for (int i = _contents.size() - 1; i >= 0; i--) { 1786 Positionable il = _contents.get(i); 1787 if (il instanceof LocoIcon) { 1788 il.remove(); 1789 if (il.getId() != null) { 1790 _idContents.remove(il.getId()); 1791 } 1792 for (String className : il.getClasses()) { 1793 _classContents.get(className).remove(il); 1794 } 1795 } 1796 } 1797 } 1798 1799 /* 1800 * *********************** End Marker Menu Methods ********************** 1801 */ 1802 /* 1803 * ************ Adding content to the panel ********************** 1804 */ 1805 public PositionableLabel setUpBackground(String name) { 1806 NamedIcon icon = NamedIcon.getIconByName(name); 1807 PositionableLabel l = new PositionableLabel(icon, this); 1808 l.setPopupUtility(null); // no text 1809 l.setPositionable(false); 1810 l.setShowToolTip(false); 1811 l.setSize(icon.getIconWidth(), icon.getIconHeight()); 1812 l.setDisplayLevel(BKG); 1813 l.setLocation(getNextBackgroundLeft(), 0); 1814 try { 1815 putItem(l); 1816 } catch (Positionable.DuplicateIdException e) { 1817 // This should never happen 1818 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 1819 } 1820 return l; 1821 } 1822 1823 protected PositionableLabel addLabel(String text) { 1824 PositionableLabel l = new PositionableLabel(text, this); 1825 l.setSize(l.getPreferredSize().width, l.getPreferredSize().height); 1826 l.setDisplayLevel(LABELS); 1827 setNextLocation(l); 1828 try { 1829 putItem(l); 1830 } catch (Positionable.DuplicateIdException e) { 1831 // This should never happen 1832 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 1833 } 1834 return l; 1835 } 1836 1837 /** 1838 * Determine right side x of furthest right background 1839 */ 1840 private int getNextBackgroundLeft() { 1841 int left = 0; 1842 // place to right of background images, if any 1843 for (Positionable p : _contents) { 1844 if (p instanceof PositionableLabel) { 1845 PositionableLabel l = (PositionableLabel) p; 1846 if (l.isBackground()) { 1847 int test = l.getX() + l.maxWidth(); 1848 if (test > left) { 1849 left = test; 1850 } 1851 } 1852 } 1853 } 1854 return left; 1855 } 1856 1857 /* Positionable has set a new level. Editor must change it in the target panel. 1858 */ 1859 public void displayLevelChange(Positionable l) { 1860 removeFromTarget(l); 1861 addToTarget(l); 1862 } 1863 1864 public TrainIcon addTrainIcon(String name) { 1865 TrainIcon l = new TrainIcon(this); 1866 putLocoIcon(l, name); 1867 return l; 1868 } 1869 1870 public LocoIcon addLocoIcon(String name) { 1871 LocoIcon l = new LocoIcon(this); 1872 putLocoIcon(l, name); 1873 return l; 1874 } 1875 1876 public void putLocoIcon(LocoIcon l, String name) { 1877 l.setText(name); 1878 l.setHorizontalTextPosition(SwingConstants.CENTER); 1879 l.setSize(l.getPreferredSize().width, l.getPreferredSize().height); 1880 l.setEditable(isEditable()); // match popup mode to editor mode 1881 try { 1882 putItem(l); 1883 } catch (Positionable.DuplicateIdException e) { 1884 // This should never happen 1885 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 1886 } 1887 } 1888 1889 public void putItem(Positionable l) throws Positionable.DuplicateIdException { 1890 l.invalidate(); 1891 l.setPositionable(true); 1892 l.setVisible(true); 1893 if (l.getToolTip() == null) { 1894 l.setToolTip(new ToolTip(_defaultToolTip, l)); 1895 } 1896 addToTarget(l); 1897 if (!_contents.add(l)) { 1898 log.error("Unable to add {} to _contents", l.getNameString()); 1899 } 1900 if (l.getId() != null) { 1901 if (_idContents.containsKey(l.getId())) { 1902 throw new Positionable.DuplicateIdException(); 1903 } 1904 _idContents.put(l.getId(), l); 1905 } 1906 for (String className : l.getClasses()) { 1907 _classContents.get(className).add(l); 1908 } 1909 if (log.isDebugEnabled()) { 1910 log.debug("putItem {} to _contents. level= {}", l.getNameString(), l.getDisplayLevel()); 1911 } 1912 } 1913 1914 protected void addToTarget(Positionable l) { 1915 JComponent c = (JComponent) l; 1916 c.invalidate(); 1917 _targetPanel.remove(c); 1918 _targetPanel.add(c, Integer.valueOf(l.getDisplayLevel())); 1919 _targetPanel.moveToFront(c); 1920 c.repaint(); 1921 _targetPanel.revalidate(); 1922 } 1923 1924 /* 1925 * ************ Icon editors for adding content *********** 1926 */ 1927 static final String[] ICON_EDITORS = {"Sensor", "RightTurnout", "LeftTurnout", 1928 "SlipTOEditor", "SignalHead", "SignalMast", "Memory", "Light", 1929 "Reporter", "Background", "MultiSensor", "Icon", "Text", "Block Contents"}; 1930 1931 /** 1932 * Create editor for a given item type. 1933 * Paths to default icons are fixed in code. Compare to respective icon package, 1934 * eg. {@link #addSensorEditor()} and {@link SensorIcon} 1935 * 1936 * @param name Icon editor's name 1937 * @return a window 1938 */ 1939 public JFrameItem getIconFrame(String name) { 1940 JFrameItem frame = _iconEditorFrame.get(name); 1941 if (frame == null) { 1942 if ("Sensor".equals(name)) { 1943 addSensorEditor(); 1944 } else if ("RightTurnout".equals(name)) { 1945 addRightTOEditor(); 1946 } else if ("LeftTurnout".equals(name)) { 1947 addLeftTOEditor(); 1948 } else if ("SlipTOEditor".equals(name)) { 1949 addSlipTOEditor(); 1950 } else if ("SignalHead".equals(name)) { 1951 addSignalHeadEditor(); 1952 } else if ("SignalMast".equals(name)) { 1953 addSignalMastEditor(); 1954 } else if ("Memory".equals(name)) { 1955 addMemoryEditor(); 1956 } else if ("GlobalVariable".equals(name)) { 1957 addGlobalVariableEditor(); 1958 } else if ("Reporter".equals(name)) { 1959 addReporterEditor(); 1960 } else if ("Light".equals(name)) { 1961 addLightEditor(); 1962 } else if ("Background".equals(name)) { 1963 addBackgroundEditor(); 1964 } else if ("MultiSensor".equals(name)) { 1965 addMultiSensorEditor(); 1966 } else if ("Icon".equals(name)) { 1967 addIconEditor(); 1968 } else if ("Text".equals(name)) { 1969 addTextEditor(); 1970 } else if ("BlockLabel".equals(name)) { 1971 addBlockContentsEditor(); 1972 } else if ("Audio".equals(name)) { 1973 addAudioEditor(); 1974 } else if ("LogixNG".equals(name)) { 1975 addLogixNGEditor(); 1976 } else { 1977 // log.error("No such Icon Editor \"{}\"", name); 1978 return null; 1979 } 1980 // frame added in the above switch 1981 frame = _iconEditorFrame.get(name); 1982 1983 if (frame == null) { // addTextEditor does not create a usable frame 1984 return null; 1985 } 1986 //frame.setLocationRelativeTo(this); 1987 frame.setLocation(frameLocationX, frameLocationY); 1988 frameLocationX += DELTA; 1989 frameLocationY += DELTA; 1990 } 1991 frame.setVisible(true); 1992 return frame; 1993 } 1994 public int frameLocationX = 0; 1995 public int frameLocationY = 0; 1996 static final int DELTA = 20; 1997 1998 public IconAdder getIconEditor(String name) { 1999 return _iconEditorFrame.get(name).getEditor(); 2000 } 2001 2002 /** 2003 * Add a label to the target. 2004 */ 2005 protected void addTextEditor() { 2006 String newLabel = JmriJOptionPane.showInputDialog(this, Bundle.getMessage("PromptNewLabel"),""); 2007 if (newLabel == null) { 2008 return; // canceled 2009 } 2010 PositionableLabel l = addLabel(newLabel); 2011 // always allow new items to be moved 2012 l.setPositionable(true); 2013 } 2014 2015 protected void addRightTOEditor() { 2016 IconAdder editor = new IconAdder("RightTurnout"); 2017 editor.setIcon(3, "TurnoutStateClosed", 2018 "resources/icons/smallschematics/tracksegments/os-righthand-west-closed.gif"); 2019 editor.setIcon(2, "TurnoutStateThrown", 2020 "resources/icons/smallschematics/tracksegments/os-righthand-west-thrown.gif"); 2021 editor.setIcon(0, "BeanStateInconsistent", 2022 "resources/icons/smallschematics/tracksegments/os-righthand-west-error.gif"); 2023 editor.setIcon(1, "BeanStateUnknown", 2024 "resources/icons/smallschematics/tracksegments/os-righthand-west-unknown.gif"); 2025 2026 JFrameItem frame = makeAddIconFrame("RightTurnout", true, true, editor); 2027 _iconEditorFrame.put("RightTurnout", frame); 2028 editor.setPickList(PickListModel.turnoutPickModelInstance()); 2029 2030 ActionListener addIconAction = a -> addTurnoutR(); 2031 editor.makeIconPanel(true); 2032 editor.complete(addIconAction, true, true, false); 2033 frame.addHelpMenu("package.jmri.jmrit.display.IconAdder", true); 2034 } 2035 2036 protected void addLeftTOEditor() { 2037 IconAdder editor = new IconAdder("LeftTurnout"); 2038 editor.setIcon(3, "TurnoutStateClosed", 2039 "resources/icons/smallschematics/tracksegments/os-lefthand-east-closed.gif"); 2040 editor.setIcon(2, "TurnoutStateThrown", 2041 "resources/icons/smallschematics/tracksegments/os-lefthand-east-thrown.gif"); 2042 editor.setIcon(0, "BeanStateInconsistent", 2043 "resources/icons/smallschematics/tracksegments/os-lefthand-east-error.gif"); 2044 editor.setIcon(1, "BeanStateUnknown", 2045 "resources/icons/smallschematics/tracksegments/os-lefthand-east-unknown.gif"); 2046 2047 JFrameItem frame = makeAddIconFrame("LeftTurnout", true, true, editor); 2048 _iconEditorFrame.put("LeftTurnout", frame); 2049 editor.setPickList(PickListModel.turnoutPickModelInstance()); 2050 2051 ActionListener addIconAction = a -> addTurnoutL(); 2052 editor.makeIconPanel(true); 2053 editor.complete(addIconAction, true, true, false); 2054 frame.addHelpMenu("package.jmri.jmrit.display.IconAdder", true); 2055 } 2056 2057 protected void addSlipTOEditor() { 2058 SlipIconAdder editor = new SlipIconAdder("SlipTOEditor"); 2059 editor.setIcon(3, "LowerWestToUpperEast", 2060 "resources/icons/smallschematics/tracksegments/os-slip-lower-west-upper-east.gif"); 2061 editor.setIcon(2, "UpperWestToLowerEast", 2062 "resources/icons/smallschematics/tracksegments/os-slip-upper-west-lower-east.gif"); 2063 editor.setIcon(4, "LowerWestToLowerEast", 2064 "resources/icons/smallschematics/tracksegments/os-slip-lower-west-lower-east.gif"); 2065 editor.setIcon(5, "UpperWestToUpperEast", 2066 "resources/icons/smallschematics/tracksegments/os-slip-upper-west-upper-east.gif"); 2067 editor.setIcon(0, "BeanStateInconsistent", 2068 "resources/icons/smallschematics/tracksegments/os-slip-error-full.gif"); 2069 editor.setIcon(1, "BeanStateUnknown", 2070 "resources/icons/smallschematics/tracksegments/os-slip-unknown-full.gif"); 2071 editor.setTurnoutType(SlipTurnoutIcon.DOUBLESLIP); 2072 JFrameItem frame = makeAddIconFrame("SlipTOEditor", true, true, editor); 2073 _iconEditorFrame.put("SlipTOEditor", frame); 2074 editor.setPickList(PickListModel.turnoutPickModelInstance()); 2075 2076 ActionListener addIconAction = a -> addSlip(); 2077 editor.makeIconPanel(true); 2078 editor.complete(addIconAction, true, true, false); 2079 frame.addHelpMenu("package.jmri.jmrit.display.SlipTurnoutIcon", true); 2080 } 2081 2082 protected void addSensorEditor() { 2083 IconAdder editor = new IconAdder("Sensor"); 2084 editor.setIcon(3, "SensorStateActive", 2085 "resources/icons/smallschematics/tracksegments/circuit-occupied.gif"); 2086 editor.setIcon(2, "SensorStateInactive", 2087 "resources/icons/smallschematics/tracksegments/circuit-empty.gif"); 2088 editor.setIcon(0, "BeanStateInconsistent", 2089 "resources/icons/smallschematics/tracksegments/circuit-error.gif"); 2090 editor.setIcon(1, "BeanStateUnknown", 2091 "resources/icons/smallschematics/tracksegments/circuit-error.gif"); 2092 2093 JFrameItem frame = makeAddIconFrame("Sensor", true, true, editor); 2094 _iconEditorFrame.put("Sensor", frame); 2095 editor.setPickList(PickListModel.sensorPickModelInstance()); 2096 2097 ActionListener addIconAction = a -> putSensor(); 2098 editor.makeIconPanel(true); 2099 editor.complete(addIconAction, true, true, false); 2100 frame.addHelpMenu("package.jmri.jmrit.display.IconAdder", true); 2101 } 2102 2103 protected void addSignalHeadEditor() { 2104 IconAdder editor = getSignalHeadEditor(); 2105 JFrameItem frame = makeAddIconFrame("SignalHead", true, true, editor); 2106 _iconEditorFrame.put("SignalHead", frame); 2107 editor.setPickList(PickListModel.signalHeadPickModelInstance()); 2108 2109 ActionListener addIconAction = a -> putSignalHead(); 2110 editor.makeIconPanel(true); 2111 editor.complete(addIconAction, true, false, false); 2112 frame.addHelpMenu("package.jmri.jmrit.display.IconAdder", true); 2113 } 2114 2115 protected IconAdder getSignalHeadEditor() { 2116 // note that all these icons will be refreshed when user clicks a specific signal head in the table 2117 IconAdder editor = new IconAdder("SignalHead"); 2118 editor.setIcon(0, "SignalHeadStateRed", 2119 "resources/icons/smallschematics/searchlights/left-red-marker.gif"); 2120 editor.setIcon(1, "SignalHeadStateYellow", 2121 "resources/icons/smallschematics/searchlights/left-yellow-marker.gif"); 2122 editor.setIcon(2, "SignalHeadStateGreen", 2123 "resources/icons/smallschematics/searchlights/left-green-marker.gif"); 2124 editor.setIcon(3, "SignalHeadStateDark", 2125 "resources/icons/smallschematics/searchlights/left-dark-marker.gif"); 2126 editor.setIcon(4, "SignalHeadStateHeld", 2127 "resources/icons/smallschematics/searchlights/left-held-marker.gif"); 2128 editor.setIcon(5, "SignalHeadStateLunar", 2129 "resources/icons/smallschematics/searchlights/left-lunar-marker.gif"); 2130 editor.setIcon(6, "SignalHeadStateFlashingRed", 2131 "resources/icons/smallschematics/searchlights/left-flashred-marker.gif"); 2132 editor.setIcon(7, "SignalHeadStateFlashingYellow", 2133 "resources/icons/smallschematics/searchlights/left-flashyellow-marker.gif"); 2134 editor.setIcon(8, "SignalHeadStateFlashingGreen", 2135 "resources/icons/smallschematics/searchlights/left-flashgreen-marker.gif"); 2136 editor.setIcon(9, "SignalHeadStateFlashingLunar", 2137 "resources/icons/smallschematics/searchlights/left-flashlunar-marker.gif"); 2138 return editor; 2139 } 2140 2141 protected void addSignalMastEditor() { 2142 IconAdder editor = new IconAdder("SignalMast"); 2143 2144 JFrameItem frame = makeAddIconFrame("SignalMast", true, true, editor); 2145 _iconEditorFrame.put("SignalMast", frame); 2146 editor.setPickList(PickListModel.signalMastPickModelInstance()); 2147 2148 ActionListener addIconAction = a -> putSignalMast(); 2149 editor.makeIconPanel(true); 2150 editor.complete(addIconAction, true, false, false); 2151 frame.addHelpMenu("package.jmri.jmrit.display.IconAdder", true); 2152 } 2153 2154 private final SpinnerNumberModel _spinCols = new SpinnerNumberModel(3, 1, 100, 1); 2155 2156 protected void addMemoryEditor() { 2157 IconAdder editor = new IconAdder("Memory") { 2158 final JButton bSpin = new JButton(Bundle.getMessage("AddSpinner")); 2159 final JButton bBox = new JButton(Bundle.getMessage("AddInputBox")); 2160 final JSpinner spinner = new JSpinner(_spinCols); 2161 2162 @Override 2163 protected void addAdditionalButtons(JPanel p) { 2164 bSpin.addActionListener(a -> addMemorySpinner()); 2165 JPanel p1 = new JPanel(); 2166 //p1.setLayout(new BoxLayout(p1, BoxLayout.X_AXIS)); 2167 bBox.addActionListener(a -> addMemoryInputBox()); 2168 ((JSpinner.DefaultEditor) spinner.getEditor()).getTextField().setColumns(2); 2169 spinner.setMaximumSize(spinner.getPreferredSize()); 2170 JPanel p2 = new JPanel(); 2171 p2.add(new JLabel(Bundle.getMessage("NumColsLabel"))); 2172 p2.add(spinner); 2173 p1.add(p2); 2174 p1.add(bBox); 2175 p.add(p1); 2176 p1 = new JPanel(); 2177 p1.add(bSpin); 2178 p.add(p1); 2179 } 2180 2181 @Override 2182 public void valueChanged(ListSelectionEvent e) { 2183 super.valueChanged(e); 2184 bSpin.setEnabled(addIconIsEnabled()); 2185 bBox.setEnabled(addIconIsEnabled()); 2186 } 2187 }; 2188 ActionListener addIconAction = a -> putMemory(); 2189 JFrameItem frame = makeAddIconFrame("Memory", true, true, editor); 2190 _iconEditorFrame.put("Memory", frame); 2191 editor.setPickList(PickListModel.memoryPickModelInstance()); 2192 editor.makeIconPanel(true); 2193 editor.complete(addIconAction, false, true, false); 2194 frame.addHelpMenu("package.jmri.jmrit.display.IconAdder", true); 2195 } 2196 2197 protected void addGlobalVariableEditor() { 2198 IconAdder editor = new IconAdder("GlobalVariable") { 2199 final JButton bSpin = new JButton(Bundle.getMessage("AddSpinner")); 2200 final JButton bBox = new JButton(Bundle.getMessage("AddInputBox")); 2201 final JSpinner spinner = new JSpinner(_spinCols); 2202 2203 @Override 2204 protected void addAdditionalButtons(JPanel p) { 2205 bSpin.addActionListener(a -> addGlobalVariableSpinner()); 2206 JPanel p1 = new JPanel(); 2207 //p1.setLayout(new BoxLayout(p1, BoxLayout.X_AXIS)); 2208 bBox.addActionListener(a -> addGlobalVariableInputBox()); 2209 ((JSpinner.DefaultEditor) spinner.getEditor()).getTextField().setColumns(2); 2210 spinner.setMaximumSize(spinner.getPreferredSize()); 2211 JPanel p2 = new JPanel(); 2212 p2.add(new JLabel(Bundle.getMessage("NumColsLabel"))); 2213 p2.add(spinner); 2214 p1.add(p2); 2215 p1.add(bBox); 2216 p.add(p1); 2217 p1 = new JPanel(); 2218 p1.add(bSpin); 2219 p.add(p1); 2220 } 2221 2222 @Override 2223 public void valueChanged(ListSelectionEvent e) { 2224 super.valueChanged(e); 2225 bSpin.setEnabled(addIconIsEnabled()); 2226 bBox.setEnabled(addIconIsEnabled()); 2227 } 2228 }; 2229 ActionListener addIconAction = a -> putGlobalVariable(); 2230 JFrameItem frame = makeAddIconFrame("GlobalVariable", true, true, editor); 2231 _iconEditorFrame.put("GlobalVariable", frame); 2232 editor.setPickList(PickListModel.globalVariablePickModelInstance()); 2233 editor.makeIconPanel(true); 2234 editor.complete(addIconAction, false, false, false); 2235 frame.addHelpMenu("package.jmri.jmrit.display.IconAdder", true); 2236 } 2237 2238 protected void addBlockContentsEditor() { 2239 IconAdder editor = new IconAdder("Block Contents"); 2240 ActionListener addIconAction = a -> putBlockContents(); 2241 JFrameItem frame = makeAddIconFrame("BlockLabel", true, true, editor); 2242 _iconEditorFrame.put("BlockLabel", frame); 2243 editor.setPickList(PickListModel.blockPickModelInstance()); 2244 editor.makeIconPanel(true); 2245 editor.complete(addIconAction, false, true, false); 2246 frame.addHelpMenu("package.jmri.jmrit.display.IconAdder", true); 2247 } 2248 2249 protected void addReporterEditor() { 2250 IconAdder editor = new IconAdder("Reporter"); 2251 ActionListener addIconAction = a -> addReporter(); 2252 JFrameItem frame = makeAddIconFrame("Reporter", true, true, editor); 2253 _iconEditorFrame.put("Reporter", frame); 2254 editor.setPickList(PickListModel.reporterPickModelInstance()); 2255 editor.makeIconPanel(true); 2256 editor.complete(addIconAction, false, true, false); 2257 frame.addHelpMenu("package.jmri.jmrit.display.IconAdder", true); 2258 } 2259 2260 protected void addLightEditor() { 2261 IconAdder editor = new IconAdder("Light"); 2262 editor.setIcon(3, "StateOff", 2263 "resources/icons/smallschematics/lights/cross-on.png"); 2264 editor.setIcon(2, "StateOn", 2265 "resources/icons/smallschematics/lights/cross-off.png"); 2266 editor.setIcon(0, "BeanStateInconsistent", 2267 "resources/icons/smallschematics/lights/cross-inconsistent.png"); 2268 editor.setIcon(1, "BeanStateUnknown", 2269 "resources/icons/smallschematics/lights/cross-unknown.png"); 2270 2271 JFrameItem frame = makeAddIconFrame("Light", true, true, editor); 2272 _iconEditorFrame.put("Light", frame); 2273 editor.setPickList(PickListModel.lightPickModelInstance()); 2274 2275 ActionListener addIconAction = a -> addLight(); 2276 editor.makeIconPanel(true); 2277 editor.complete(addIconAction, true, true, false); 2278 frame.addHelpMenu("package.jmri.jmrit.display.IconAdder", true); 2279 } 2280 2281 protected void addBackgroundEditor() { 2282 IconAdder editor = new IconAdder("Background"); 2283 editor.setIcon(0, "background", "resources/PanelPro.gif"); 2284 2285 JFrameItem frame = makeAddIconFrame("Background", true, false, editor); 2286 _iconEditorFrame.put("Background", frame); 2287 2288 ActionListener addIconAction = a -> putBackground(); 2289 editor.makeIconPanel(true); 2290 editor.complete(addIconAction, true, false, false); 2291 frame.addHelpMenu("package.jmri.jmrit.display.IconAdder", true); 2292 } 2293 2294 protected JFrameItem addMultiSensorEditor() { 2295 MultiSensorIconAdder editor = new MultiSensorIconAdder("MultiSensor"); 2296 editor.setIcon(0, "BeanStateInconsistent", 2297 "resources/icons/USS/plate/levers/l-inconsistent.gif"); 2298 editor.setIcon(1, "BeanStateUnknown", 2299 "resources/icons/USS/plate/levers/l-unknown.gif"); 2300 editor.setIcon(2, "SensorStateInactive", 2301 "resources/icons/USS/plate/levers/l-inactive.gif"); 2302 editor.setIcon(3, "MultiSensorPosition 0", 2303 "resources/icons/USS/plate/levers/l-left.gif"); 2304 editor.setIcon(4, "MultiSensorPosition 1", 2305 "resources/icons/USS/plate/levers/l-vertical.gif"); 2306 editor.setIcon(5, "MultiSensorPosition 2", 2307 "resources/icons/USS/plate/levers/l-right.gif"); 2308 2309 JFrameItem frame = makeAddIconFrame("MultiSensor", true, false, editor); 2310 _iconEditorFrame.put("MultiSensor", frame); 2311 frame.addHelpMenu("package.jmri.jmrit.display.MultiSensorIconAdder", true); 2312 2313 editor.setPickList(PickListModel.sensorPickModelInstance()); 2314 2315 ActionListener addIconAction = a -> addMultiSensor(); 2316 editor.makeIconPanel(true); 2317 editor.complete(addIconAction, true, true, false); 2318 return frame; 2319 } 2320 2321 protected void addIconEditor() { 2322 IconAdder editor = new IconAdder("Icon"); 2323 editor.setIcon(0, "plainIcon", "resources/icons/smallschematics/tracksegments/block.gif"); 2324 JFrameItem frame = makeAddIconFrame("Icon", true, false, editor); 2325 _iconEditorFrame.put("Icon", frame); 2326 2327 ActionListener addIconAction = a -> putIcon(); 2328 editor.makeIconPanel(true); 2329 editor.complete(addIconAction, true, false, false); 2330 frame.addHelpMenu("package.jmri.jmrit.display.IconAdder", true); 2331 } 2332 2333 protected void addAudioEditor() { 2334 IconAdder editor = new IconAdder("Audio"); 2335 editor.setIcon(0, "plainIcon", "resources/icons/audio_icon.gif"); 2336 JFrameItem frame = makeAddIconFrame("Audio", true, false, editor); 2337 _iconEditorFrame.put("Audio", frame); 2338 editor.setPickList(PickListModel.audioPickModelInstance()); 2339 2340 ActionListener addIconAction = a -> putAudio(); 2341 editor.makeIconPanel(true); 2342 editor.complete(addIconAction, true, false, false); 2343 frame.addHelpMenu("package.jmri.jmrit.display.IconAdder", true); 2344 } 2345 2346 protected void addLogixNGEditor() { 2347 IconAdder editor = new IconAdder("LogixNG"); 2348 editor.setIcon(0, "plainIcon", "resources/icons/logixng/logixng_icon.gif"); 2349 JFrameItem frame = makeAddIconFrame("LogixNG", true, false, editor); 2350 _iconEditorFrame.put("LogixNG", frame); 2351 2352 ActionListener addIconAction = a -> putLogixNG(); 2353 editor.makeIconPanel(true); 2354 editor.complete(addIconAction, true, false, false); 2355 frame.addHelpMenu("package.jmri.jmrit.display.IconAdder", true); 2356 } 2357 2358 /* 2359 * ************** add content items from Icon Editors ******************* 2360 */ 2361 /** 2362 * Add a sensor indicator to the target. 2363 * 2364 * @return The sensor that was added to the panel. 2365 */ 2366 protected SensorIcon putSensor() { 2367 SensorIcon result = new SensorIcon(new NamedIcon("resources/icons/smallschematics/tracksegments/circuit-error.gif", 2368 "resources/icons/smallschematics/tracksegments/circuit-error.gif"), this); 2369 IconAdder editor = getIconEditor("Sensor"); 2370 Hashtable<String, NamedIcon> map = editor.getIconMap(); 2371 Enumeration<String> e = map.keys(); 2372 while (e.hasMoreElements()) { 2373 String key = e.nextElement(); 2374 result.setIcon(key, map.get(key)); 2375 } 2376// l.setActiveIcon(editor.getIcon("SensorStateActive")); 2377// l.setInactiveIcon(editor.getIcon("SensorStateInactive")); 2378// l.setInconsistentIcon(editor.getIcon("BeanStateInconsistent")); 2379// l.setUnknownIcon(editor.getIcon("BeanStateUnknown")); 2380 NamedBean b = editor.getTableSelection(); 2381 if (b != null) { 2382 result.setSensor(b.getDisplayName()); 2383 } 2384 result.setDisplayLevel(SENSORS); 2385 setNextLocation(result); 2386 try { 2387 putItem(result); 2388 } catch (Positionable.DuplicateIdException ex) { 2389 // This should never happen 2390 log.error("Editor.putItem() with null id has thrown DuplicateIdException", ex); 2391 } 2392 return result; 2393 } 2394 2395 /** 2396 * Add a turnout indicator to the target 2397 */ 2398 void addTurnoutR() { 2399 IconAdder editor = getIconEditor("RightTurnout"); 2400 addTurnout(editor); 2401 } 2402 2403 void addTurnoutL() { 2404 IconAdder editor = getIconEditor("LeftTurnout"); 2405 addTurnout(editor); 2406 } 2407 2408 protected TurnoutIcon addTurnout(IconAdder editor) { 2409 TurnoutIcon result = new TurnoutIcon(this); 2410 result.setTurnout(editor.getTableSelection().getDisplayName()); 2411 Hashtable<String, NamedIcon> map = editor.getIconMap(); 2412 Enumeration<String> e = map.keys(); 2413 while (e.hasMoreElements()) { 2414 String key = e.nextElement(); 2415 result.setIcon(key, map.get(key)); 2416 } 2417 result.setDisplayLevel(TURNOUTS); 2418 setNextLocation(result); 2419 try { 2420 putItem(result); 2421 } catch (Positionable.DuplicateIdException ex) { 2422 // This should never happen 2423 log.error("Editor.putItem() with null id has thrown DuplicateIdException", ex); 2424 } 2425 return result; 2426 } 2427 2428 @SuppressFBWarnings(value="BC_UNCONFIRMED_CAST_OF_RETURN_VALUE", justification="iconEditor requested as exact type") 2429 SlipTurnoutIcon addSlip() { 2430 SlipTurnoutIcon result = new SlipTurnoutIcon(this); 2431 SlipIconAdder editor = (SlipIconAdder) getIconEditor("SlipTOEditor"); 2432 result.setSingleSlipRoute(editor.getSingleSlipRoute()); 2433 2434 switch (editor.getTurnoutType()) { 2435 case SlipTurnoutIcon.DOUBLESLIP: 2436 result.setLowerWestToUpperEastIcon(editor.getIcon("LowerWestToUpperEast")); 2437 result.setUpperWestToLowerEastIcon(editor.getIcon("UpperWestToLowerEast")); 2438 result.setLowerWestToLowerEastIcon(editor.getIcon("LowerWestToLowerEast")); 2439 result.setUpperWestToUpperEastIcon(editor.getIcon("UpperWestToUpperEast")); 2440 break; 2441 case SlipTurnoutIcon.SINGLESLIP: 2442 result.setLowerWestToUpperEastIcon(editor.getIcon("LowerWestToUpperEast")); 2443 result.setUpperWestToLowerEastIcon(editor.getIcon("UpperWestToLowerEast")); 2444 result.setLowerWestToLowerEastIcon(editor.getIcon("Slip")); 2445 result.setSingleSlipRoute(editor.getSingleSlipRoute()); 2446 break; 2447 case SlipTurnoutIcon.THREEWAY: 2448 result.setLowerWestToUpperEastIcon(editor.getIcon("Upper")); 2449 result.setUpperWestToLowerEastIcon(editor.getIcon("Middle")); 2450 result.setLowerWestToLowerEastIcon(editor.getIcon("Lower")); 2451 result.setSingleSlipRoute(editor.getSingleSlipRoute()); 2452 break; 2453 case SlipTurnoutIcon.SCISSOR: //Scissor is the same as a Double for icon storing. 2454 result.setLowerWestToUpperEastIcon(editor.getIcon("LowerWestToUpperEast")); 2455 result.setUpperWestToLowerEastIcon(editor.getIcon("UpperWestToLowerEast")); 2456 result.setLowerWestToLowerEastIcon(editor.getIcon("LowerWestToLowerEast")); 2457 //l.setUpperWestToUpperEastIcon(editor.getIcon("UpperWestToUpperEast")); 2458 break; 2459 default: 2460 log.warn("Unexpected addSlip editor.getTurnoutType() of {}", editor.getTurnoutType()); 2461 break; 2462 } 2463 2464 if ((editor.getTurnoutType() == SlipTurnoutIcon.SCISSOR) && (!editor.getSingleSlipRoute())) { 2465 result.setTurnout(editor.getTurnout("lowerwest").getName(), SlipTurnoutIcon.LOWERWEST); 2466 result.setTurnout(editor.getTurnout("lowereast").getName(), SlipTurnoutIcon.LOWEREAST); 2467 } 2468 result.setInconsistentIcon(editor.getIcon("BeanStateInconsistent")); 2469 result.setUnknownIcon(editor.getIcon("BeanStateUnknown")); 2470 result.setTurnoutType(editor.getTurnoutType()); 2471 result.setTurnout(editor.getTurnout("west").getName(), SlipTurnoutIcon.WEST); 2472 result.setTurnout(editor.getTurnout("east").getName(), SlipTurnoutIcon.EAST); 2473 result.setDisplayLevel(TURNOUTS); 2474 setNextLocation(result); 2475 try { 2476 putItem(result); 2477 } catch (Positionable.DuplicateIdException e) { 2478 // This should never happen 2479 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 2480 } 2481 return result; 2482 } 2483 2484 /** 2485 * Add a signal head to the target. 2486 * 2487 * @return The signal head that was added to the target. 2488 */ 2489 protected SignalHeadIcon putSignalHead() { 2490 SignalHeadIcon result = new SignalHeadIcon(this); 2491 IconAdder editor = getIconEditor("SignalHead"); 2492 result.setSignalHead(editor.getTableSelection().getDisplayName()); 2493 Hashtable<String, NamedIcon> map = editor.getIconMap(); 2494 Enumeration<String> e = map.keys(); 2495 while (e.hasMoreElements()) { 2496 String key = e.nextElement(); 2497 result.setIcon(key, map.get(key)); 2498 } 2499 result.setDisplayLevel(SIGNALS); 2500 setNextLocation(result); 2501 try { 2502 putItem(result); 2503 } catch (Positionable.DuplicateIdException ex) { 2504 // This should never happen 2505 log.error("Editor.putItem() with null id has thrown DuplicateIdException", ex); 2506 } 2507 return result; 2508 } 2509 2510 /** 2511 * Add a signal mast to the target. 2512 * 2513 * @return The signal mast that was added to the target. 2514 */ 2515 protected SignalMastIcon putSignalMast() { 2516 SignalMastIcon result = new SignalMastIcon(this); 2517 IconAdder editor = _iconEditorFrame.get("SignalMast").getEditor(); 2518 result.setSignalMast(editor.getTableSelection().getDisplayName()); 2519 result.setDisplayLevel(SIGNALS); 2520 setNextLocation(result); 2521 try { 2522 putItem(result); 2523 } catch (Positionable.DuplicateIdException e) { 2524 // This should never happen 2525 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 2526 } 2527 return result; 2528 } 2529 2530 protected MemoryIcon putMemory() { 2531 MemoryIcon result = new MemoryIcon(new NamedIcon("resources/icons/misc/X-red.gif", 2532 "resources/icons/misc/X-red.gif"), this); 2533 IconAdder memoryIconEditor = getIconEditor("Memory"); 2534 result.setMemory(memoryIconEditor.getTableSelection().getDisplayName()); 2535 result.setSize(result.getPreferredSize().width, result.getPreferredSize().height); 2536 result.setDisplayLevel(MEMORIES); 2537 setNextLocation(result); 2538 try { 2539 putItem(result); 2540 } catch (Positionable.DuplicateIdException e) { 2541 // This should never happen 2542 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 2543 } 2544 return result; 2545 } 2546 2547 protected MemorySpinnerIcon addMemorySpinner() { 2548 MemorySpinnerIcon result = new MemorySpinnerIcon(this); 2549 IconAdder memoryIconEditor = getIconEditor("Memory"); 2550 result.setMemory(memoryIconEditor.getTableSelection().getDisplayName()); 2551 result.setSize(result.getPreferredSize().width, result.getPreferredSize().height); 2552 result.setDisplayLevel(MEMORIES); 2553 setNextLocation(result); 2554 try { 2555 putItem(result); 2556 } catch (Positionable.DuplicateIdException e) { 2557 // This should never happen 2558 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 2559 } 2560 return result; 2561 } 2562 2563 protected MemoryInputIcon addMemoryInputBox() { 2564 MemoryInputIcon result = new MemoryInputIcon(_spinCols.getNumber().intValue(), this); 2565 IconAdder memoryIconEditor = getIconEditor("Memory"); 2566 result.setMemory(memoryIconEditor.getTableSelection().getDisplayName()); 2567 result.setSize(result.getPreferredSize().width, result.getPreferredSize().height); 2568 result.setDisplayLevel(MEMORIES); 2569 setNextLocation(result); 2570 try { 2571 putItem(result); 2572 } catch (Positionable.DuplicateIdException e) { 2573 // This should never happen 2574 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 2575 } 2576 return result; 2577 } 2578 2579 protected GlobalVariableIcon putGlobalVariable() { 2580 GlobalVariableIcon result = new GlobalVariableIcon(new NamedIcon("resources/icons/misc/X-red.gif", 2581 "resources/icons/misc/X-red.gif"), this); 2582 IconAdder globalVariableIconEditor = getIconEditor("GlobalVariable"); 2583 result.setGlobalVariable(globalVariableIconEditor.getTableSelection().getDisplayName()); 2584 result.setSize(result.getPreferredSize().width, result.getPreferredSize().height); 2585 result.setDisplayLevel(MEMORIES); 2586 setNextLocation(result); 2587 try { 2588 putItem(result); 2589 } catch (Positionable.DuplicateIdException e) { 2590 // This should never happen 2591 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 2592 } 2593 return result; 2594 } 2595 2596 protected GlobalVariableSpinnerIcon addGlobalVariableSpinner() { 2597 GlobalVariableSpinnerIcon result = new GlobalVariableSpinnerIcon(this); 2598 IconAdder globalVariableIconEditor = getIconEditor("GlobalVariable"); 2599 result.setGlobalVariable(globalVariableIconEditor.getTableSelection().getDisplayName()); 2600 result.setSize(result.getPreferredSize().width, result.getPreferredSize().height); 2601 result.setDisplayLevel(MEMORIES); 2602 setNextLocation(result); 2603 try { 2604 putItem(result); 2605 } catch (Positionable.DuplicateIdException e) { 2606 // This should never happen 2607 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 2608 } 2609 return result; 2610 } 2611 2612 protected GlobalVariableInputIcon addGlobalVariableInputBox() { 2613 GlobalVariableInputIcon result = new GlobalVariableInputIcon(_spinCols.getNumber().intValue(), this); 2614 IconAdder globalVariableIconEditor = getIconEditor("GlobalVariable"); 2615 result.setGlobalVariable(globalVariableIconEditor.getTableSelection().getDisplayName()); 2616 result.setSize(result.getPreferredSize().width, result.getPreferredSize().height); 2617 result.setDisplayLevel(MEMORIES); 2618 setNextLocation(result); 2619 try { 2620 putItem(result); 2621 } catch (Positionable.DuplicateIdException e) { 2622 // This should never happen 2623 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 2624 } 2625 return result; 2626 } 2627 2628 protected BlockContentsIcon putBlockContents() { 2629 BlockContentsIcon result = new BlockContentsIcon(new NamedIcon("resources/icons/misc/X-red.gif", 2630 "resources/icons/misc/X-red.gif"), this); 2631 IconAdder blockIconEditor = getIconEditor("BlockLabel"); 2632 result.setBlock(blockIconEditor.getTableSelection().getDisplayName()); 2633 result.setSize(result.getPreferredSize().width, result.getPreferredSize().height); 2634 result.setDisplayLevel(MEMORIES); 2635 setNextLocation(result); 2636 try { 2637 putItem(result); 2638 } catch (Positionable.DuplicateIdException e) { 2639 // This should never happen 2640 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 2641 } 2642 return result; 2643 } 2644 2645 /** 2646 * Add a Light indicator to the target 2647 * 2648 * @return The light indicator that was added to the target. 2649 */ 2650 protected LightIcon addLight() { 2651 LightIcon result = new LightIcon(this); 2652 IconAdder editor = getIconEditor("Light"); 2653 result.setOffIcon(editor.getIcon("StateOff")); 2654 result.setOnIcon(editor.getIcon("StateOn")); 2655 result.setInconsistentIcon(editor.getIcon("BeanStateInconsistent")); 2656 result.setUnknownIcon(editor.getIcon("BeanStateUnknown")); 2657 result.setLight((Light) editor.getTableSelection()); 2658 result.setDisplayLevel(LIGHTS); 2659 setNextLocation(result); 2660 try { 2661 putItem(result); 2662 } catch (Positionable.DuplicateIdException e) { 2663 // This should never happen 2664 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 2665 } 2666 return result; 2667 } 2668 2669 protected ReporterIcon addReporter() { 2670 ReporterIcon result = new ReporterIcon(this); 2671 IconAdder reporterIconEditor = getIconEditor("Reporter"); 2672 result.setReporter((Reporter) reporterIconEditor.getTableSelection()); 2673 result.setSize(result.getPreferredSize().width, result.getPreferredSize().height); 2674 result.setDisplayLevel(REPORTERS); 2675 setNextLocation(result); 2676 try { 2677 putItem(result); 2678 } catch (Positionable.DuplicateIdException e) { 2679 // This should never happen 2680 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 2681 } 2682 return result; 2683 } 2684 2685 /** 2686 * Button pushed, add a background image. Note that a background image 2687 * differs from a regular icon only in the level at which it's presented. 2688 */ 2689 void putBackground() { 2690 // most likely the image is scaled. get full size from URL 2691 IconAdder bkgrndEditor = getIconEditor("Background"); 2692 String url = bkgrndEditor.getIcon("background").getURL(); 2693 setUpBackground(url); 2694 } 2695 2696 /** 2697 * Add an icon to the target. 2698 * 2699 * @return The icon that was added to the target. 2700 */ 2701 protected Positionable putIcon() { 2702 IconAdder iconEditor = getIconEditor("Icon"); 2703 String url = iconEditor.getIcon("plainIcon").getURL(); 2704 NamedIcon icon = NamedIcon.getIconByName(url); 2705 if (log.isDebugEnabled()) { 2706 log.debug("putIcon: {} url= {}", (icon == null ? "null" : "icon"), url); 2707 } 2708 PositionableLabel result = new PositionableLabel(icon, this); 2709// l.setPopupUtility(null); // no text 2710 result.setDisplayLevel(ICONS); 2711 setNextLocation(result); 2712 try { 2713 putItem(result); 2714 } catch (Positionable.DuplicateIdException e) { 2715 // This should never happen 2716 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 2717 } 2718 result.updateSize(); 2719 return result; 2720 } 2721 2722 /** 2723 * Add a LogixNG icon to the target. 2724 * 2725 * @return The LogixNG icon that was added to the target. 2726 */ 2727 protected Positionable putAudio() { 2728 IconAdder iconEditor = getIconEditor("Audio"); 2729 String url = iconEditor.getIcon("plainIcon").getURL(); 2730 NamedIcon icon = NamedIcon.getIconByName(url); 2731 if (log.isDebugEnabled()) { 2732 log.debug("putAudio: {} url= {}", (icon == null ? "null" : "icon"), url); 2733 } 2734 AudioIcon result = new AudioIcon(icon, this); 2735 NamedBean b = iconEditor.getTableSelection(); 2736 if (b != null) { 2737 result.setAudio(b.getDisplayName()); 2738 } 2739// l.setPopupUtility(null); // no text 2740 result.setDisplayLevel(ICONS); 2741 setNextLocation(result); 2742 try { 2743 putItem(result); 2744 } catch (Positionable.DuplicateIdException e) { 2745 // This should never happen 2746 log.error("Editor.putAudio() with null id has thrown DuplicateIdException", e); 2747 } 2748 result.updateSize(); 2749 return result; 2750 } 2751 2752 /** 2753 * Add a LogixNG icon to the target. 2754 * 2755 * @return The LogixNG icon that was added to the target. 2756 */ 2757 protected Positionable putLogixNG() { 2758 IconAdder iconEditor = getIconEditor("LogixNG"); 2759 String url = iconEditor.getIcon("plainIcon").getURL(); 2760 NamedIcon icon = NamedIcon.getIconByName(url); 2761 if (log.isDebugEnabled()) { 2762 log.debug("putLogixNG: {} url= {}", (icon == null ? "null" : "icon"), url); 2763 } 2764 LogixNGIcon result = new LogixNGIcon(icon, this); 2765// l.setPopupUtility(null); // no text 2766 result.setDisplayLevel(ICONS); 2767 setNextLocation(result); 2768 try { 2769 putItem(result); 2770 } catch (Positionable.DuplicateIdException e) { 2771 // This should never happen 2772 log.error("Editor.putLogixNG() with null id has thrown DuplicateIdException", e); 2773 } 2774 result.updateSize(); 2775 return result; 2776 } 2777 2778 @SuppressFBWarnings(value="BC_UNCONFIRMED_CAST_OF_RETURN_VALUE", justification="iconEditor requested as exact type") 2779 public MultiSensorIcon addMultiSensor() { 2780 MultiSensorIcon result = new MultiSensorIcon(this); 2781 MultiSensorIconAdder editor = (MultiSensorIconAdder) getIconEditor("MultiSensor"); 2782 result.setUnknownIcon(editor.getIcon("BeanStateUnknown")); 2783 result.setInconsistentIcon(editor.getIcon("BeanStateInconsistent")); 2784 result.setInactiveIcon(editor.getIcon("SensorStateInactive")); 2785 int numPositions = editor.getNumIcons(); 2786 for (int i = 3; i < numPositions; i++) { 2787 NamedIcon icon = editor.getIcon(i); 2788 String sensor = editor.getSensor(i).getName(); 2789 result.addEntry(sensor, icon); 2790 } 2791 result.setUpDown(editor.getUpDown()); 2792 result.setDisplayLevel(SENSORS); 2793 setNextLocation(result); 2794 try { 2795 putItem(result); 2796 } catch (Positionable.DuplicateIdException e) { 2797 // This should never happen 2798 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 2799 } 2800 return result; 2801 } 2802 2803 protected AnalogClock2Display addClock() { 2804 AnalogClock2Display result = new AnalogClock2Display(this); 2805 result.setOpaque(false); 2806 result.update(); 2807 result.setDisplayLevel(CLOCK); 2808 setNextLocation(result); 2809 try { 2810 putItem(result); 2811 } catch (Positionable.DuplicateIdException e) { 2812 // This should never happen 2813 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 2814 } 2815 return result; 2816 } 2817 2818 protected RpsPositionIcon addRpsReporter() { 2819 RpsPositionIcon result = new RpsPositionIcon(this); 2820 result.setSize(result.getPreferredSize().width, result.getPreferredSize().height); 2821 result.setDisplayLevel(SENSORS); 2822 setNextLocation(result); 2823 try { 2824 putItem(result); 2825 } catch (Positionable.DuplicateIdException e) { 2826 // This should never happen 2827 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 2828 } 2829 return result; 2830 } 2831 2832 /* 2833 * ****************** end adding content ******************** 2834 */ 2835 /* 2836 * ********************* Icon Editors utils *************************** 2837 */ 2838 public static class JFrameItem extends JmriJFrame { 2839 2840 private final IconAdder _editor; 2841 2842 JFrameItem(String name, IconAdder editor) { 2843 super(name); 2844 _editor = editor; 2845 setName(name); 2846 } 2847 2848 public IconAdder getEditor() { 2849 return _editor; 2850 } 2851 2852 @Override 2853 public String toString() { 2854 return this.getName(); 2855 } 2856 } 2857 2858 public void setTitle() { 2859 String name = _targetFrame.getTitle(); 2860 if (name == null || name.equals("")) { 2861 super.setTitle(Bundle.getMessage("LabelEditor")); 2862 } else { 2863 super.setTitle(name + " " + Bundle.getMessage("LabelEditor")); 2864 } 2865 for (JFrameItem frame : _iconEditorFrame.values()) { 2866 frame.setTitle(frame.getName() + " (" + name + ")"); 2867 } 2868 setName(name); 2869 } 2870 2871 /** 2872 * Create a frame showing all images in the set used for an icon. 2873 * Opened when editItemInPanel button is clicked in the Edit Icon Panel, 2874 * shown after icon's context menu Edit Icon... item is selected. 2875 * 2876 * @param name bean type name 2877 * @param add true when used to add a new item on panel, false when used to edit an item already on the panel 2878 * @param table true for bean types presented as table instead of icons 2879 * @param editor parent frame of the image frame 2880 * @return JFrame connected to the editor, to be filled with icons 2881 */ 2882 protected JFrameItem makeAddIconFrame(String name, boolean add, boolean table, IconAdder editor) { 2883 log.debug("makeAddIconFrame for {}, add= {}, table= {}", name, add, table); 2884 String txt; 2885 String bundleName; 2886 JFrameItem frame = new JFrameItem(name, editor); 2887 // use NamedBeanBundle property for basic beans like "Turnout" I18N 2888 if ("Sensor".equals(name)) { 2889 bundleName = "BeanNameSensor"; 2890 } else if ("SignalHead".equals(name)) { 2891 bundleName = "BeanNameSignalHead"; 2892 } else if ("SignalMast".equals(name)) { 2893 bundleName = "BeanNameSignalMast"; 2894 } else if ("Memory".equals(name)) { 2895 bundleName = "BeanNameMemory"; 2896 } else if ("Reporter".equals(name)) { 2897 bundleName = "BeanNameReporter"; 2898 } else if ("Light".equals(name)) { 2899 bundleName = "BeanNameLight"; 2900 } else if ("Turnout".equals(name)) { 2901 bundleName = "BeanNameTurnout"; // called by RightTurnout and LeftTurnout objects in TurnoutIcon.java edit() method 2902 } else if ("Block".equals(name)) { 2903 bundleName = "BeanNameBlock"; 2904 } else if ("GlobalVariable".equals(name)) { 2905 bundleName = "BeanNameGlobalVariable"; 2906 } else if ("Audio".equals(name)) { 2907 bundleName = "BeanNameAudio"; 2908 } else { 2909 bundleName = name; 2910 } 2911 if (editor != null) { 2912 JPanel p = new JPanel(); 2913 p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS)); 2914 if (add) { 2915 txt = MessageFormat.format(Bundle.getMessage("addItemToPanel"), Bundle.getMessage(bundleName)); 2916 } else { 2917 txt = MessageFormat.format(Bundle.getMessage("editItemInPanel"), Bundle.getMessage(bundleName)); 2918 } 2919 p.add(new JLabel(txt)); 2920 if (table) { 2921 txt = MessageFormat.format(Bundle.getMessage("TableSelect"), Bundle.getMessage(bundleName), 2922 (add ? Bundle.getMessage("ButtonAddIcon") : Bundle.getMessage("ButtonUpdateIcon"))); 2923 } else { 2924 if ("MultiSensor".equals(name)) { 2925 txt = MessageFormat.format(Bundle.getMessage("SelectMultiSensor", Bundle.getMessage("ButtonAddIcon")), 2926 (add ? Bundle.getMessage("ButtonAddIcon") : Bundle.getMessage("ButtonUpdateIcon"))); 2927 } else { 2928 txt = MessageFormat.format(Bundle.getMessage("IconSelect"), Bundle.getMessage(bundleName), 2929 (add ? Bundle.getMessage("ButtonAddIcon") : Bundle.getMessage("ButtonUpdateIcon"))); 2930 } 2931 } 2932 p.add(new JLabel(txt)); 2933 p.add(new JLabel(" ")); // add a bit of space on pane above icons 2934 frame.getContentPane().add(p, BorderLayout.NORTH); 2935 frame.getContentPane().add(editor); 2936 2937 JMenuBar menuBar = new JMenuBar(); 2938 JMenu findIcon = new JMenu(Bundle.getMessage("findIconMenu")); 2939 menuBar.add(findIcon); 2940 2941 JMenuItem editItem = new JMenuItem(Bundle.getMessage("editIndexMenu")); 2942 editItem.addActionListener(e -> { 2943 ImageIndexEditor ii = InstanceManager.getDefault(ImageIndexEditor.class); 2944 ii.pack(); 2945 ii.setVisible(true); 2946 }); 2947 findIcon.add(editItem); 2948 findIcon.addSeparator(); 2949 2950 JMenuItem searchItem = new JMenuItem(Bundle.getMessage("searchFSMenu")); 2951 searchItem.addActionListener(new ActionListener() { 2952 private IconAdder ea; 2953 2954 @Override 2955 public void actionPerformed(ActionEvent e) { 2956 InstanceManager.getDefault(DirectorySearcher.class).searchFS(); 2957 ea.addDirectoryToCatalog(); 2958 } 2959 2960 ActionListener init(IconAdder ed) { 2961 ea = ed; 2962 return this; 2963 } 2964 }.init(editor)); 2965 2966 findIcon.add(searchItem); 2967 frame.setJMenuBar(menuBar); 2968 editor.setParent(frame); 2969 // when this window closes, check for saving 2970 if (add) { 2971 frame.addWindowListener(new WindowAdapter() { 2972 @Override 2973 public void windowClosing(WindowEvent e) { 2974 setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE); 2975 if (log.isDebugEnabled()) { 2976 log.debug("windowClosing: HIDE {}", toString()); 2977 } 2978 } 2979 }); 2980 } 2981 } else { 2982 log.error("No icon editor specified for {}", name); // NOI18N 2983 } 2984 if (add) { 2985 txt = MessageFormat.format(Bundle.getMessage("AddItem"), Bundle.getMessage(bundleName)); 2986 _iconEditorFrame.put(name, frame); 2987 } else { 2988 txt = MessageFormat.format(Bundle.getMessage("EditItem"), Bundle.getMessage(bundleName)); 2989 } 2990 frame.setTitle(txt + " (" + getTitle() + ")"); 2991 frame.pack(); 2992 return frame; 2993 } 2994 2995 /* 2996 * ******************* cleanup ************************ 2997 */ 2998 protected void removeFromTarget(Positionable l) { 2999 _targetPanel.remove((Component) l); 3000 _highlightcomponent = null; 3001 Point p = l.getLocation(); 3002 int w = l.getWidth(); 3003 int h = l.getHeight(); 3004 _targetPanel.revalidate(); 3005 _targetPanel.repaint(p.x, p.y, w, h); 3006 } 3007 3008 public boolean removeFromContents(Positionable l) { 3009 removeFromTarget(l); 3010 //todo check that parent == _targetPanel 3011 //Container parent = this.getParent(); 3012 // force redisplay 3013 if (l.getId() != null) { 3014 _idContents.remove(l.getId()); 3015 } 3016 for (String className : l.getClasses()) { 3017 _classContents.get(className).remove(l); 3018 } 3019 return _contents.remove(l); 3020 } 3021 3022 /** 3023 * Ask user if panel should be deleted. The caller should dispose the panel 3024 * to delete it. 3025 * 3026 * @return true if panel should be deleted. 3027 */ 3028 public boolean deletePanel() { 3029 log.debug("deletePanel"); 3030 // verify deletion 3031 int selectedValue = JmriJOptionPane.showOptionDialog(_targetPanel, 3032 Bundle.getMessage("QuestionA") + "\n" + Bundle.getMessage("QuestionA2", 3033 Bundle.getMessage("FileMenuItemStore")), 3034 Bundle.getMessage("DeleteVerifyTitle"), JmriJOptionPane.DEFAULT_OPTION, 3035 JmriJOptionPane.QUESTION_MESSAGE, null, 3036 new Object[]{Bundle.getMessage("ButtonYesDelete"), Bundle.getMessage("ButtonCancel")}, 3037 Bundle.getMessage("ButtonCancel")); 3038 // return without deleting if "Cancel" or Cancel Dialog response 3039 return (selectedValue == 0 ); // array position 0 = Yes, Delete. 3040 } 3041 3042 /** 3043 * Dispose of the editor. 3044 */ 3045 @Override 3046 public void dispose() { 3047 for (JFrameItem frame : _iconEditorFrame.values()) { 3048 frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); 3049 frame.dispose(); 3050 } 3051 // delete panel - deregister the panel for saving 3052 ConfigureManager cm = InstanceManager.getNullableDefault(ConfigureManager.class); 3053 if (cm != null) { 3054 cm.deregister(this); 3055 } 3056 InstanceManager.getDefault(EditorManager.class).remove(this); 3057 setVisible(false); 3058 _contents.clear(); 3059 _idContents.clear(); 3060 for (var list : _classContents.values()) { 3061 list.clear(); 3062 } 3063 _classContents.clear(); 3064 removeAll(); 3065 super.dispose(); 3066 } 3067 3068 /* 3069 * **************** Mouse Methods ********************** 3070 */ 3071 public void showToolTip(Positionable selection, JmriMouseEvent event) { 3072 ToolTip tip = selection.getToolTip(); 3073 tip.setLocation(selection.getX() + selection.getWidth() / 2, selection.getY() + selection.getHeight()); 3074 setToolTip(tip); 3075 } 3076 3077 protected int getItemX(Positionable p, int deltaX) { 3078 if ((p instanceof MemoryOrGVIcon) && (p.getPopupUtility().getFixedWidth() == 0)) { 3079 MemoryOrGVIcon pm = (MemoryOrGVIcon) p; 3080 return pm.getOriginalX() + (int) Math.round(deltaX / getPaintScale()); 3081 } else { 3082 return p.getX() + (int) Math.round(deltaX / getPaintScale()); 3083 } 3084 } 3085 3086 protected int getItemY(Positionable p, int deltaY) { 3087 if ((p instanceof MemoryOrGVIcon) && (p.getPopupUtility().getFixedWidth() == 0)) { 3088 MemoryOrGVIcon pm = (MemoryOrGVIcon) p; 3089 return pm.getOriginalY() + (int) Math.round(deltaY / getPaintScale()); 3090 } else { 3091 return p.getY() + (int) Math.round(deltaY / getPaintScale()); 3092 } 3093 } 3094 3095 /** 3096 * Provide a method for external code to add items to context menus. 3097 * 3098 * @param nb The namedBean associated with the postionable item. 3099 * @param item The entry to add to the menu. 3100 * @param menu The menu to add the entry to. 3101 */ 3102 public void addToPopUpMenu(NamedBean nb, JMenuItem item, int menu) { 3103 if (nb == null || item == null) { 3104 return; 3105 } 3106 for (Positionable pos : _contents) { 3107 if (pos.getNamedBean() == nb && pos.getPopupUtility() != null) { 3108 addToPopUpMenu( pos, item, menu); 3109 return; 3110 } else if (pos instanceof SlipTurnoutIcon) { 3111 if (pos.getPopupUtility() != null) { 3112 SlipTurnoutIcon sti = (SlipTurnoutIcon) pos; 3113 if ( sti.getTurnout(SlipTurnoutIcon.EAST) == nb || sti.getTurnout(SlipTurnoutIcon.WEST) == nb 3114 || sti.getTurnout(SlipTurnoutIcon.LOWEREAST) == nb 3115 || sti.getTurnout(SlipTurnoutIcon.LOWERWEST) == nb) { 3116 addToPopUpMenu( pos, item, menu); 3117 return; 3118 } 3119 } 3120 } else if ( pos instanceof MultiSensorIcon && pos.getPopupUtility() != null) { 3121 MultiSensorIcon msi = (MultiSensorIcon) pos; 3122 for (int i = 0; i < msi.getNumEntries(); i++) { 3123 if ( msi.getSensorName(i).equals(nb.getUserName()) 3124 || msi.getSensorName(i).equals(nb.getSystemName())) { 3125 addToPopUpMenu( pos, item, menu); 3126 return; 3127 } 3128 } 3129 } 3130 } 3131 } 3132 3133 private void addToPopUpMenu( Positionable pos, JMenuItem item, int menu ) { 3134 switch (menu) { 3135 case VIEWPOPUPONLY: 3136 pos.getPopupUtility().addViewPopUpMenu(item); 3137 break; 3138 case EDITPOPUPONLY: 3139 pos.getPopupUtility().addEditPopUpMenu(item); 3140 break; 3141 default: 3142 pos.getPopupUtility().addEditPopUpMenu(item); 3143 pos.getPopupUtility().addViewPopUpMenu(item); 3144 } 3145 } 3146 3147 public static final int VIEWPOPUPONLY = 0x00; 3148 public static final int EDITPOPUPONLY = 0x01; 3149 public static final int BOTHPOPUPS = 0x02; 3150 3151 /** 3152 * Relocate item. 3153 * <p> 3154 * Note that items can not be moved past the left or top edges of the panel. 3155 * 3156 * @param p The item to move. 3157 * @param deltaX The horizontal displacement. 3158 * @param deltaY The vertical displacement. 3159 */ 3160 public void moveItem(Positionable p, int deltaX, int deltaY) { 3161 //log.debug("moveItem at ({},{}) delta ({},{})", p.getX(), p.getY(), deltaX, deltaY); 3162 if (getFlag(OPTION_POSITION, p.isPositionable())) { 3163 int xObj = getItemX(p, deltaX); 3164 int yObj = getItemY(p, deltaY); 3165 // don't allow negative placement, icon can become unreachable 3166 if (xObj < 0) { 3167 xObj = 0; 3168 } 3169 if (yObj < 0) { 3170 yObj = 0; 3171 } 3172 p.setLocation(xObj, yObj); 3173 // and show! 3174 p.repaint(); 3175 } 3176 } 3177 3178 /** 3179 * Return a List of all items whose bounding rectangle contain the mouse 3180 * position. ordered from top level to bottom 3181 * 3182 * @param event contains the mouse position. 3183 * @return a list of positionable items or an empty list. 3184 */ 3185 protected List<Positionable> getSelectedItems(JmriMouseEvent event) { 3186 Rectangle rect = new Rectangle(); 3187 ArrayList<Positionable> selections = new ArrayList<>(); 3188 for (Positionable p : _contents) { 3189 double x = event.getX(); 3190 double y = event.getY(); 3191 rect = p.getBounds(rect); 3192 if (p instanceof jmri.jmrit.display.controlPanelEditor.shape.PositionableShape 3193 && p.getDegrees() != 0) { 3194 double rad = p.getDegrees() * Math.PI / 180.0; 3195 java.awt.geom.AffineTransform t = java.awt.geom.AffineTransform.getRotateInstance(-rad); 3196 double[] pt = new double[2]; 3197 // bit shift to avoid SpotBugs paranoia 3198 pt[0] = x - rect.x - (rect.width >>> 1); 3199 pt[1] = y - rect.y - (rect.height >>> 1); 3200 t.transform(pt, 0, pt, 0, 1); 3201 x = pt[0] + rect.x + (rect.width >>> 1); 3202 y = pt[1] + rect.y + (rect.height >>> 1); 3203 } 3204 Rectangle2D.Double rect2D = new Rectangle2D.Double(rect.x * _paintScale, 3205 rect.y * _paintScale, 3206 rect.width * _paintScale, 3207 rect.height * _paintScale); 3208 if (rect2D.contains(x, y) && (p.getDisplayLevel() > BKG || event.isControlDown())) { 3209 boolean added = false; 3210 int level = p.getDisplayLevel(); 3211 for (int k = 0; k < selections.size(); k++) { 3212 if (level >= selections.get(k).getDisplayLevel()) { 3213 selections.add(k, p); 3214 added = true; // OK to lie in the case of background icon 3215 break; 3216 } 3217 } 3218 if (!added) { 3219 selections.add(p); 3220 } 3221 } 3222 } 3223 //log.debug("getSelectedItems at ({},{}) {} found,", x, y, selections.size()); 3224 return selections; 3225 } 3226 3227 /* 3228 * Gather all items inside _selectRect 3229 * Keep old group if Control key is down 3230 */ 3231 protected void makeSelectionGroup(JmriMouseEvent event) { 3232 if (!event.isControlDown() || _selectionGroup == null) { 3233 _selectionGroup = new ArrayList<>(); 3234 } 3235 Rectangle test = new Rectangle(); 3236 List<Positionable> list = getContents(); 3237 if (event.isShiftDown()) { 3238 for (Positionable comp : list) { 3239 if (_selectRect.intersects(comp.getBounds(test)) 3240 && (event.isControlDown() || comp.getDisplayLevel() > BKG)) { 3241 _selectionGroup.add(comp); 3242 //log.debug("makeSelectionGroup: selection: {}, class= {}", comp.getNameString(), comp.getClass().getName()); 3243 } 3244 } 3245 } else { 3246 for (Positionable comp : list) { 3247 if (_selectRect.contains(comp.getBounds(test)) 3248 && (event.isControlDown() || comp.getDisplayLevel() > BKG)) { 3249 _selectionGroup.add(comp); 3250 //log.debug("makeSelectionGroup: selection: {}, class= {}", comp.getNameString(), comp.getClass().getName()); 3251 } 3252 } 3253 } 3254 log.debug("makeSelectionGroup: {} selected.", _selectionGroup.size()); 3255 if (_selectionGroup.size() < 1) { 3256 _selectRect = null; 3257 deselectSelectionGroup(); 3258 } 3259 } 3260 3261 /* 3262 * For the param, selection, Add to or delete from _selectionGroup. 3263 * If not there, add. 3264 * If there, delete. 3265 * make new group if Cntl key is not held down 3266 */ 3267 protected void modifySelectionGroup(Positionable selection, JmriMouseEvent event) { 3268 if (!event.isControlDown() || _selectionGroup == null) { 3269 _selectionGroup = new ArrayList<>(); 3270 } 3271 boolean removed = false; 3272 if (event.isControlDown()) { 3273 if (selection.getDisplayLevel() > BKG) { 3274 if (_selectionGroup.contains(selection)) { 3275 removed = _selectionGroup.remove(selection); 3276 } else { 3277 _selectionGroup.add(selection); 3278 } 3279 } else if (event.isShiftDown()) { 3280 if (_selectionGroup.contains(selection)) { 3281 removed = _selectionGroup.remove(selection); 3282 } else { 3283 _selectionGroup.add(selection); 3284 } 3285 } 3286 } 3287 log.debug("modifySelectionGroup: size= {}, selection {}", 3288 _selectionGroup.size(), (removed ? "removed" : "added")); 3289 } 3290 3291 /** 3292 * Set attributes of a Positionable. 3293 * 3294 * @param newUtil helper from which to get attributes 3295 * @param p the item to set attributes of 3296 * 3297 */ 3298 public void setAttributes(PositionablePopupUtil newUtil, Positionable p) { 3299 p.setPopupUtility(newUtil.clone(p, p.getTextComponent())); 3300 int mar = newUtil.getMargin(); 3301 int bor = newUtil.getBorderSize(); 3302 Border outlineBorder; 3303 if (bor == 0) { 3304 outlineBorder = BorderFactory.createEmptyBorder(0, 0, 0, 0); 3305 } else { 3306 outlineBorder = new LineBorder(newUtil.getBorderColor(), bor); 3307 } 3308 Border borderMargin; 3309 if (newUtil.hasBackground()) { 3310 borderMargin = new LineBorder(p.getBackground(), mar); 3311 } else { 3312 borderMargin = BorderFactory.createEmptyBorder(mar, mar, mar, mar); 3313 } 3314 p.setBorder(new CompoundBorder(outlineBorder, borderMargin)); 3315 3316 if (p instanceof PositionableLabel) { 3317 PositionableLabel pos = (PositionableLabel) p; 3318 if (pos.isText()) { 3319 int deg = pos.getDegrees(); 3320 pos.rotate(0); 3321 if (deg == 0) { 3322 p.setOpaque(newUtil.hasBackground()); 3323 } else { 3324 pos.rotate(deg); 3325 } 3326 } 3327 } else if (p instanceof PositionableJPanel) { 3328 p.setOpaque(newUtil.hasBackground()); 3329 p.getTextComponent().setOpaque(newUtil.hasBackground()); 3330 } 3331 p.updateSize(); 3332 p.repaint(); 3333 if (p instanceof PositionableIcon) { 3334 NamedBean bean = p.getNamedBean(); 3335 if (bean != null) { 3336 ((PositionableIcon) p).displayState(bean.getState()); 3337 } 3338 } 3339 } 3340 3341 protected void setSelectionsAttributes(PositionablePopupUtil util, Positionable pos) { 3342 if (_selectionGroup != null && _selectionGroup.contains(pos)) { 3343 for (Positionable p : _selectionGroup) { 3344 if (p instanceof PositionableLabel) { 3345 setAttributes(util, p); 3346 } 3347 } 3348 } 3349 } 3350 3351 protected void setSelectionsHidden(boolean enabled, Positionable p) { 3352 if (_selectionGroup != null && _selectionGroup.contains(p)) { 3353 for (Positionable comp : _selectionGroup) { 3354 comp.setHidden(enabled); 3355 } 3356 } 3357 } 3358 3359 protected boolean setSelectionsPositionable(boolean enabled, Positionable p) { 3360 if (_selectionGroup != null && _selectionGroup.contains(p)) { 3361 for (Positionable comp : _selectionGroup) { 3362 comp.setPositionable(enabled); 3363 } 3364 return true; 3365 } else { 3366 return false; 3367 } 3368 } 3369 3370 protected void removeSelections(Positionable p) { 3371 if (_selectionGroup != null && _selectionGroup.contains(p)) { 3372 for (Positionable comp : _selectionGroup) { 3373 comp.remove(); 3374 } 3375 deselectSelectionGroup(); 3376 } 3377 } 3378 3379 protected void setSelectionsScale(double s, Positionable p) { 3380 if (_selectionGroup != null && _selectionGroup.contains(p)) { 3381 for (Positionable comp : _selectionGroup) { 3382 comp.setScale(s); 3383 } 3384 } else { 3385 p.setScale(s); 3386 } 3387 } 3388 3389 protected void setSelectionsRotation(int k, Positionable p) { 3390 if (_selectionGroup != null && _selectionGroup.contains(p)) { 3391 for (Positionable comp : _selectionGroup) { 3392 comp.rotate(k); 3393 } 3394 } else { 3395 p.rotate(k); 3396 } 3397 } 3398 3399 protected void setSelectionsDisplayLevel(int k, Positionable p) { 3400 if (_selectionGroup != null && _selectionGroup.contains(p)) { 3401 for (Positionable comp : _selectionGroup) { 3402 comp.setDisplayLevel(k); 3403 } 3404 } else { 3405 p.setDisplayLevel(k); 3406 } 3407 } 3408 3409 protected void setSelectionsDockingLocation(Positionable p) { 3410 if (_selectionGroup != null && _selectionGroup.contains(p)) { 3411 for (Positionable pos : _selectionGroup) { 3412 if (pos instanceof LocoIcon) { 3413 ((LocoIcon) pos).setDockingLocation(pos.getX(), pos.getY()); 3414 } 3415 } 3416 } else if (p instanceof LocoIcon) { 3417 ((LocoIcon) p).setDockingLocation(p.getX(), p.getY()); 3418 } 3419 } 3420 3421 protected void dockSelections(Positionable p) { 3422 if (_selectionGroup != null && _selectionGroup.contains(p)) { 3423 for (Positionable pos : _selectionGroup) { 3424 if (pos instanceof LocoIcon) { 3425 ((LocoIcon) pos).dock(); 3426 } 3427 } 3428 } else if (p instanceof LocoIcon) { 3429 ((LocoIcon) p).dock(); 3430 } 3431 } 3432 3433 protected boolean showAlignPopup(Positionable p) { 3434 return _selectionGroup != null && _selectionGroup.contains(p); 3435 } 3436 3437 public Rectangle getSelectRect() { 3438 return _selectRect; 3439 } 3440 3441 public void drawSelectRect(int x, int y) { 3442 int aX = getAnchorX(); 3443 int aY = getAnchorY(); 3444 int w = x - aX; 3445 int h = y - aY; 3446 if (x < aX) { 3447 aX = x; 3448 w = -w; 3449 } 3450 if (y < aY) { 3451 aY = y; 3452 h = -h; 3453 } 3454 _selectRect = new Rectangle((int) Math.round(aX / _paintScale), (int) Math.round(aY / _paintScale), 3455 (int) Math.round(w / _paintScale), (int) Math.round(h / _paintScale)); 3456 } 3457 3458 public final int getAnchorX() { 3459 return _anchorX; 3460 } 3461 3462 public final int getAnchorY() { 3463 return _anchorY; 3464 } 3465 3466 public final int getLastX() { 3467 return _lastX; 3468 } 3469 3470 public final int getLastY() { 3471 return _lastY; 3472 } 3473 3474 @Override 3475 public void keyTyped(KeyEvent e) { 3476 } 3477 3478 @Override 3479 public void keyPressed(KeyEvent e) { 3480 if (_selectionGroup == null) { 3481 return; 3482 } 3483 int x = 0; 3484 int y = 0; 3485 switch (e.getKeyCode()) { 3486 case KeyEvent.VK_UP: 3487 y = -1; 3488 break; 3489 case KeyEvent.VK_DOWN: 3490 y = 1; 3491 break; 3492 case KeyEvent.VK_LEFT: 3493 x = -1; 3494 break; 3495 case KeyEvent.VK_RIGHT: 3496 x = 1; 3497 break; 3498 default: 3499 log.warn("Unexpected e.getKeyCode() of {}", e.getKeyCode()); 3500 break; 3501 } 3502 //A cheat if the shift key isn't pressed then we move 5 pixels at a time. 3503 if (!e.isShiftDown()) { 3504 y *= 5; 3505 x *= 5; 3506 } 3507 for (Positionable comp : _selectionGroup) { 3508 moveItem(comp, x, y); 3509 } 3510 _targetPanel.repaint(); 3511 } 3512 3513 @Override 3514 public void keyReleased(KeyEvent e) { 3515 } 3516 3517 @Override 3518 public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException { 3519 NamedBean nb = (NamedBean) evt.getOldValue(); 3520 if ("CanDelete".equals(evt.getPropertyName())) { // NOI18N 3521 StringBuilder message = new StringBuilder(); 3522 message.append(Bundle.getMessage("VetoInUseEditorHeader", getName())); // NOI18N 3523 message.append("<br>"); 3524 boolean found = false; 3525 int count = 0; 3526 for (Positionable p : _contents) { 3527 if (nb.equals(p.getNamedBean())) { 3528 found = true; 3529 count++; 3530 } 3531 } 3532 if (found) { 3533 message.append(Bundle.getMessage("VetoFoundInPanel", count)); 3534 message.append("<br>"); 3535 message.append(Bundle.getMessage("VetoReferencesWillBeRemoved")); // NOI18N 3536 message.append("<br>"); 3537 throw new PropertyVetoException(message.toString(), evt); 3538 } 3539 } else if ("DoDelete".equals(evt.getPropertyName())) { // NOI18N 3540 ArrayList<Positionable> toDelete = new ArrayList<>(); 3541 for (Positionable p : _contents) { 3542 if (nb.equals(p.getNamedBean())) { 3543 toDelete.add(p); 3544 } 3545 } 3546 for (Positionable p : toDelete) { 3547 removeFromContents(p); 3548 _targetPanel.repaint(); 3549 } 3550 } 3551 } 3552 3553 /* 3554 * ********************* Abstract Methods *********************** 3555 */ 3556 @Override 3557 public abstract void mousePressed(JmriMouseEvent event); 3558 3559 @Override 3560 public abstract void mouseReleased(JmriMouseEvent event); 3561 3562 @Override 3563 public abstract void mouseClicked(JmriMouseEvent event); 3564 3565 @Override 3566 public abstract void mouseDragged(JmriMouseEvent event); 3567 3568 @Override 3569 public abstract void mouseMoved(JmriMouseEvent event); 3570 3571 @Override 3572 public abstract void mouseEntered(JmriMouseEvent event); 3573 3574 @Override 3575 public abstract void mouseExited(JmriMouseEvent event); 3576 3577 /* 3578 * set up target panel, frame etc. 3579 */ 3580 protected abstract void init(String name); 3581 3582 /* 3583 * Closing of Target frame window. 3584 */ 3585 protected abstract void targetWindowClosingEvent(WindowEvent e); 3586 3587 /** 3588 * Called from TargetPanel's paint method for additional drawing by editor 3589 * view. 3590 * 3591 * @param g the context to paint within 3592 */ 3593 protected abstract void paintTargetPanel(Graphics g); 3594 3595 /** 3596 * Set an object's location when it is created. 3597 * 3598 * @param obj the object to locate 3599 */ 3600 protected abstract void setNextLocation(Positionable obj); 3601 3602 /** 3603 * After construction, initialize all the widgets to their saved config 3604 * settings. 3605 */ 3606 protected abstract void initView(); 3607 3608 /** 3609 * Set up item(s) to be copied by paste. 3610 * 3611 * @param p the item to copy 3612 */ 3613 protected abstract void copyItem(Positionable p); 3614 3615 public List<NamedBeanUsageReport> getUsageReport(NamedBean bean) { 3616 List<NamedBeanUsageReport> report = new ArrayList<>(); 3617 if (bean != null) { 3618 getContents().forEach( pos -> { 3619 String data = getUsageData(pos); 3620 if (pos instanceof MultiSensorIcon) { 3621 MultiSensorIcon multi = (MultiSensorIcon) pos; 3622 multi.getSensors().forEach( sensor -> { 3623 if (bean.equals(sensor)) { 3624 report.add(new NamedBeanUsageReport("PositionalIcon", data)); 3625 } 3626 }); 3627 3628 } else if (pos instanceof SlipTurnoutIcon) { 3629 SlipTurnoutIcon slip3Scissor = (SlipTurnoutIcon) pos; 3630 if (bean.equals(slip3Scissor.getTurnout(SlipTurnoutIcon.EAST))) { 3631 report.add(new NamedBeanUsageReport("PositionalIcon", data)); 3632 } 3633 if (bean.equals(slip3Scissor.getTurnout(SlipTurnoutIcon.WEST))) { 3634 report.add(new NamedBeanUsageReport("PositionalIcon", data)); 3635 } 3636 if ( slip3Scissor.getNamedTurnout(SlipTurnoutIcon.LOWEREAST) != null 3637 && bean.equals(slip3Scissor.getTurnout(SlipTurnoutIcon.LOWEREAST))) { 3638 report.add(new NamedBeanUsageReport("PositionalIcon", data)); 3639 } 3640 if ( slip3Scissor.getNamedTurnout(SlipTurnoutIcon.LOWERWEST) != null 3641 && bean.equals(slip3Scissor.getTurnout(SlipTurnoutIcon.LOWERWEST))) { 3642 report.add(new NamedBeanUsageReport("PositionalIcon", data)); 3643 } 3644 3645 } else if (pos instanceof LightIcon) { 3646 LightIcon icon = (LightIcon) pos; 3647 if (bean.equals(icon.getLight())) { 3648 report.add(new NamedBeanUsageReport("PositionalIcon", data)); 3649 } 3650 3651 } else if (pos instanceof ReporterIcon) { 3652 ReporterIcon icon = (ReporterIcon) pos; 3653 if (bean.equals(icon.getReporter())) { 3654 report.add(new NamedBeanUsageReport("PositionalIcon", data)); 3655 } 3656 3657 } else if (pos instanceof AudioIcon) { 3658 AudioIcon icon = (AudioIcon) pos; 3659 if (bean.equals(icon.getAudio())) { 3660 report.add(new NamedBeanUsageReport("PositionalIcon", data)); 3661 } 3662 3663 } else { 3664 if ( bean.equals(pos.getNamedBean())) { 3665 report.add(new NamedBeanUsageReport("PositionalIcon", data)); 3666 } 3667 } 3668 }); 3669 } 3670 return report; 3671 } 3672 3673 String getUsageData(Positionable pos) { 3674 Point point = pos.getLocation(); 3675 return String.format("%s :: x=%d, y=%d", 3676 pos.getClass().getSimpleName(), 3677 Math.round(point.getX()), 3678 Math.round(point.getY())); 3679 } 3680 3681 // initialize logging 3682 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(Editor.class); 3683 3684}