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