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