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