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