001package jmri.jmrit.display.panelEditor;
002
003import java.awt.Color;
004import java.awt.Component;
005import java.awt.Dimension;
006import java.awt.FlowLayout;
007import java.awt.Font;
008import java.awt.Graphics;
009import java.awt.Rectangle;
010import java.awt.event.ActionEvent;
011import java.awt.event.ActionListener;
012import java.awt.event.ItemEvent;
013import java.awt.event.ItemListener;
014import java.awt.event.KeyAdapter;
015import java.awt.event.KeyEvent;
016import java.awt.event.WindowAdapter;
017import java.lang.reflect.InvocationTargetException;
018import java.util.ArrayList;
019import java.util.Collections;
020import java.util.HashMap;
021import java.util.List;
022
023import javax.swing.AbstractAction;
024import javax.swing.BoxLayout;
025import javax.swing.JButton;
026import javax.swing.JCheckBox;
027import javax.swing.JCheckBoxMenuItem;
028import javax.swing.JComboBox;
029import javax.swing.JComponent;
030import javax.swing.JDialog;
031import javax.swing.JFrame;
032import javax.swing.JLabel;
033import javax.swing.JMenu;
034import javax.swing.JMenuBar;
035import javax.swing.JMenuItem;
036import javax.swing.JPanel;
037import javax.swing.JPopupMenu;
038import javax.swing.JTextField;
039
040import jmri.CatalogTreeManager;
041import jmri.ConfigureManager;
042import jmri.InstanceManager;
043import jmri.configurexml.ConfigXmlManager;
044import jmri.configurexml.XmlAdapter;
045import jmri.jmrit.catalog.ImageIndexEditor;
046import jmri.jmrit.display.Editor;
047import jmri.jmrit.display.EditorManager;
048import jmri.jmrit.display.Positionable;
049import jmri.jmrit.display.PositionablePopupUtil;
050import jmri.jmrit.display.ToolTip;
051import jmri.util.JmriJFrame;
052import jmri.util.gui.GuiLafPreferencesManager;
053import jmri.util.swing.JmriColorChooser;
054import jmri.util.swing.JmriJOptionPane;
055import jmri.util.swing.JmriMouseEvent;
056
057import org.jdom2.Element;
058
059/**
060 * Provides a simple editor for adding jmri.jmrit.display items to a captive
061 * JFrame.
062 * <p>
063 * GUI is structured as a band of common parameters across the top, then a
064 * series of things you can add.
065 * <p>
066 * All created objects are put specific levels depending on their type (higher
067 * levels are in front):
068 * <ul>
069 *   <li>BKG background
070 *   <li>ICONS icons and other drawing symbols
071 *   <li>LABELS text labels
072 *   <li>TURNOUTS turnouts and other variable track items
073 *   <li>SENSORS sensors and other independently modified objects
074 * </ul>
075 * <p>
076 * The "contents" List keeps track of all the objects added to the target frame
077 * for later manipulation.
078 * <p>
079 * If you close the Editor window, the target is left alone and the editor
080 * window is just hidden, not disposed. If you close the target, the editor and
081 * target are removed, and dispose is run. To make this logic work, the
082 * PanelEditor is descended from a JFrame, not a JPanel. That way it can control
083 * its own visibility.
084 * <p>
085 * The title of the target and the editor panel are kept consistent via the
086 * {#setTitle} method.
087 *
088 * @author Bob Jacobsen Copyright (c) 2002, 2003, 2007
089 * @author Dennis Miller 2004
090 * @author Howard G. Penny Copyright (c) 2005
091 * @author Matthew Harris Copyright (c) 2009
092 * @author Pete Cressman Copyright (c) 2009, 2010
093 */
094public class PanelEditor extends Editor implements ItemListener {
095
096    private static final String SENSOR = "Sensor";
097    private static final String SIGNAL_HEAD = "SignalHead";
098    private static final String SIGNAL_MAST = "SignalMast";
099    private static final String MEMORY = "Memory";
100    private static final String RIGHT_TURNOUT = "RightTurnout";
101    private static final String LEFT_TURNOUT = "LeftTurnout";
102    private static final String SLIP_TO_EDITOR = "SlipTOEditor";
103    private static final String BLOCK_LABEL = "BlockLabel";
104    private static final String REPORTER = "Reporter";
105    private static final String LIGHT = "Light";
106    private static final String BACKGROUND = "Background";
107    private static final String MULTI_SENSOR = "MultiSensor";
108    private static final String RPSREPORTER = "RPSreporter";
109    private static final String FAST_CLOCK = "FastClock";
110    private static final String GLOBAL_VARIABLE = "GlobalVariable";
111    private static final String ICON = "Icon";
112    private static final String AUDIO = "Audio";
113    private static final String LOGIXNG = "LogixNG";
114    private final JTextField nextX = new JTextField("0", 4);
115    private final JTextField nextY = new JTextField("0", 4);
116
117    private final JCheckBox editableBox = new JCheckBox(Bundle.getMessage("CheckBoxEditable"));
118    private final JCheckBox positionableBox = new JCheckBox(Bundle.getMessage("CheckBoxPositionable"));
119    private final JCheckBox controllingBox = new JCheckBox(Bundle.getMessage("CheckBoxControlling"));
120    //private JCheckBox showCoordinatesBox = new JCheckBox(Bundle.getMessage("CheckBoxShowCoordinates"));
121    private final JCheckBox showTooltipBox = new JCheckBox(Bundle.getMessage("CheckBoxShowTooltips"));
122    private final JCheckBox hiddenBox = new JCheckBox(Bundle.getMessage("CheckBoxHidden"));
123    private final JCheckBox menuBox = new JCheckBox(Bundle.getMessage("CheckBoxMenuBar"));
124    private final JLabel scrollableLabel = new JLabel(Bundle.getMessage("ComboBoxScrollable"));
125    private final JComboBox<String> scrollableComboBox = new JComboBox<>();
126
127    private final JButton labelAdd = new JButton(Bundle.getMessage("ButtonAddText"));
128    private final JTextField nextLabel = new JTextField(10);
129
130    private JComboBox<ComboBoxItem> _addIconBox;
131
132    public PanelEditor() {
133    }
134
135    public PanelEditor(String name) {
136        super(name, false, true);
137        init(name);
138    }
139
140    @Override
141    protected void init(String name) {
142        java.awt.Container contentPane = this.getContentPane();
143        contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS));
144        // common items
145        JPanel common = new JPanel();
146        common.setLayout(new FlowLayout());
147        common.add(new JLabel(" x:"));
148        common.add(nextX);
149        common.add(new JLabel(" y:"));
150        common.add(nextY);
151        contentPane.add(common);
152        setAllEditable(true);
153        setShowHidden(true);
154        super.setTargetPanel(null, makeFrame(name));
155        super.setTargetPanelSize(400, 300);
156        super.setDefaultToolTip(new ToolTip(null, 0, 0, new Font("SansSerif", Font.PLAIN, 12),
157                Color.black, new Color(215, 225, 255), Color.black, null));
158        // set scrollbar initial state
159        setScroll(SCROLL_BOTH);
160
161        // add menu - not using PanelMenu, because it now
162        // has other stuff in it?
163        JMenuBar menuBar = new JMenuBar();
164        JMenu fileMenu = new JMenu(Bundle.getMessage("MenuFile"));
165        menuBar.add(fileMenu);
166        fileMenu.add(new jmri.jmrit.display.NewPanelAction(Bundle.getMessage("MenuItemNew")));
167        fileMenu.add(new jmri.configurexml.StoreXmlUserAction(Bundle.getMessage("FileMenuItemStore")));
168        JMenuItem storeIndexItem = new JMenuItem(Bundle.getMessage("MIStoreImageIndex"));
169        fileMenu.add(storeIndexItem);
170        storeIndexItem.addActionListener(event -> InstanceManager.getDefault(CatalogTreeManager.class).storeImageIndex());
171        JMenuItem editItem = new JMenuItem(Bundle.getMessage("editIndexMenu"));
172        editItem.addActionListener(e -> {
173            ImageIndexEditor ii = InstanceManager.getDefault(ImageIndexEditor.class);
174            ii.pack();
175            ii.setVisible(true);
176        });
177        fileMenu.add(editItem);
178
179        editItem = new JMenuItem(Bundle.getMessage("CPEView"));
180        fileMenu.add(editItem);
181        editItem.addActionListener(event -> changeView("jmri.jmrit.display.controlPanelEditor.ControlPanelEditor"));
182
183        fileMenu.addSeparator();
184        JMenuItem deleteItem = new JMenuItem(Bundle.getMessage("DeletePanel"));
185        fileMenu.add(deleteItem);
186        deleteItem.addActionListener(event -> {
187            if (deletePanel()) {
188                getTargetFrame().dispose();
189                dispose();
190            }
191        });
192
193        setJMenuBar(menuBar);
194        addHelpMenu("package.jmri.jmrit.display.PanelEditor", true);
195
196        // allow renaming the panel
197        {
198            JPanel namep = new JPanel();
199            namep.setLayout(new FlowLayout());
200            JButton b = new JButton(Bundle.getMessage("renamePanelMenu", "..."));
201            b.addActionListener(new ActionListener() {
202                PanelEditor editor;
203
204                @Override
205                public void actionPerformed(ActionEvent e) {
206                    Component ancestor = getTargetPanel().getTopLevelAncestor(); // could be null
207                    String oldName = "";
208                    if (ancestor instanceof JFrame) {
209                        oldName = ((JFrame) ancestor).getTitle();
210                    }
211                    // prompt for name
212                    String newName = JmriJOptionPane.showInputDialog(null, Bundle.getMessage("PromptNewName"), oldName);
213                    if ((newName == null) || (oldName.equals(newName))) {
214                        return;  // cancelled
215                    }
216                    if (InstanceManager.getDefault(EditorManager.class).contains(newName)) {
217                        JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("CanNotRename"), Bundle.getMessage("PanelExist"),
218                                JmriJOptionPane.ERROR_MESSAGE);
219                        return;
220                    }
221                    if (ancestor instanceof JFrame) {
222                        ((JFrame) ancestor).setTitle(newName);
223                    }
224                    editor.setTitle();
225                }
226
227                ActionListener init(PanelEditor e) {
228                    editor = e;
229                    return this;
230                }
231            }.init(this));
232            namep.add(b);
233            this.getContentPane().add(namep);
234        }
235        // add a text label
236        {
237            JPanel panel = new JPanel();
238            panel.setLayout(new FlowLayout());
239            panel.add(labelAdd);
240            labelAdd.setEnabled(false);
241            labelAdd.setToolTipText(Bundle.getMessage("ToolTipWillActivate"));
242            panel.add(nextLabel);
243            labelAdd.addActionListener(new ActionListener() {
244                PanelEditor editor;
245
246                @Override
247                public void actionPerformed(ActionEvent a) {
248                    editor.addLabel(nextLabel.getText());
249                }
250
251                ActionListener init(PanelEditor e) {
252                    editor = e;
253                    return this;
254                }
255            }.init(this));
256            nextLabel.addKeyListener(new KeyAdapter() {
257                @Override
258                public void keyReleased(KeyEvent a) {
259                    if (nextLabel.getText().equals("")) {
260                        labelAdd.setEnabled(false);
261                        labelAdd.setToolTipText(Bundle.getMessage("ToolTipWillActivate"));
262                    } else {
263                        labelAdd.setEnabled(true);
264                        labelAdd.setToolTipText(null);
265                    }
266                }
267            });
268            this.getContentPane().add(panel);
269        }
270
271        // Selection of the type of entity for the icon to represent is done from a combobox
272        _addIconBox = new JComboBox<>();
273        _addIconBox.setMinimumSize(new Dimension(75, 75));
274        _addIconBox.setMaximumSize(new Dimension(200, 200));
275        _addIconBox.addItem(new ComboBoxItem(RIGHT_TURNOUT));
276        _addIconBox.addItem(new ComboBoxItem(LEFT_TURNOUT));
277        _addIconBox.addItem(new ComboBoxItem(SLIP_TO_EDITOR));
278        _addIconBox.addItem(new ComboBoxItem(SENSOR)); // NOI18N
279        _addIconBox.addItem(new ComboBoxItem(SIGNAL_HEAD));
280        _addIconBox.addItem(new ComboBoxItem(SIGNAL_MAST));
281        _addIconBox.addItem(new ComboBoxItem(MEMORY));
282        _addIconBox.addItem(new ComboBoxItem(BLOCK_LABEL));
283        _addIconBox.addItem(new ComboBoxItem(REPORTER));
284        _addIconBox.addItem(new ComboBoxItem(LIGHT));
285        _addIconBox.addItem(new ComboBoxItem(BACKGROUND));
286        _addIconBox.addItem(new ComboBoxItem(MULTI_SENSOR));
287        _addIconBox.addItem(new ComboBoxItem(RPSREPORTER));
288        _addIconBox.addItem(new ComboBoxItem(FAST_CLOCK));
289        _addIconBox.addItem(new ComboBoxItem(GLOBAL_VARIABLE));
290        _addIconBox.addItem(new ComboBoxItem(AUDIO));
291        _addIconBox.addItem(new ComboBoxItem(LOGIXNG));
292        _addIconBox.addItem(new ComboBoxItem(ICON));
293        _addIconBox.setSelectedIndex(-1);
294        _addIconBox.addItemListener(this);  // must be AFTER no selection is set
295        JPanel p1 = new JPanel();
296        p1.setLayout(new BoxLayout(p1, BoxLayout.Y_AXIS));
297        JPanel p2 = new JPanel();
298        p2.setLayout(new FlowLayout());
299        p2.add(new JLabel(Bundle.getMessage("selectTypeIcon")));
300        p1.add(p2);
301        p1.add(_addIconBox);
302        contentPane.add(p1);
303
304        // edit, position, control controls
305        {
306            // edit mode item
307            contentPane.add(editableBox);
308            editableBox.addActionListener(event -> {
309                setAllEditable(editableBox.isSelected());
310                hiddenCheckBoxListener();
311            });
312            editableBox.setSelected(isEditable());
313            // positionable item
314            contentPane.add(positionableBox);
315            positionableBox.addActionListener(event -> setAllPositionable(positionableBox.isSelected()));
316            positionableBox.setSelected(allPositionable());
317            // controlable item
318            contentPane.add(controllingBox);
319            controllingBox.addActionListener(event -> setAllControlling(controllingBox.isSelected()));
320            controllingBox.setSelected(allControlling());
321            // hidden item
322            contentPane.add(hiddenBox);
323            hiddenCheckBoxListener();
324            hiddenBox.setSelected(showHidden());
325
326            /*
327             contentPane.add(showCoordinatesBox);
328             showCoordinatesBox.addActionListener(new ActionListener() {
329             public void actionPerformed(ActionEvent e) {
330             setShowCoordinates(showCoordinatesBox.isSelected());
331             }
332             });
333             showCoordinatesBox.setSelected(showCoordinates());
334             */
335            contentPane.add(showTooltipBox);
336            showTooltipBox.addActionListener(e -> setAllShowToolTip(showTooltipBox.isSelected()));
337            showTooltipBox.setSelected(showToolTip());
338
339            contentPane.add(menuBox);
340            menuBox.addActionListener(e -> setPanelMenuVisible(menuBox.isSelected()));
341            menuBox.setSelected(true);
342
343            // Show/Hide Scroll Bars
344            JPanel scrollPanel = new JPanel();
345            scrollPanel.setLayout(new FlowLayout());
346            scrollableLabel.setLabelFor(scrollableComboBox);
347            scrollPanel.add(scrollableLabel);
348            scrollPanel.add(scrollableComboBox);
349            contentPane.add(scrollPanel);
350            scrollableComboBox.addItem(Bundle.getMessage("ScrollNone"));
351            scrollableComboBox.addItem(Bundle.getMessage("ScrollBoth"));
352            scrollableComboBox.addItem(Bundle.getMessage("ScrollHorizontal"));
353            scrollableComboBox.addItem(Bundle.getMessage("ScrollVertical"));
354            scrollableComboBox.setSelectedIndex(SCROLL_BOTH);
355            scrollableComboBox.addActionListener(e -> setScroll(scrollableComboBox.getSelectedIndex()));
356        }
357
358        // register the resulting panel for later configuration
359        ConfigureManager cm = InstanceManager.getNullableDefault(jmri.ConfigureManager.class);
360        if (cm != null) {
361            cm.registerUser(this);
362        }
363
364        // when this window closes, set contents of target uneditable
365        addWindowListener(new java.awt.event.WindowAdapter() {
366
367            HashMap<String, JFrameItem> iconAdderFrames;
368
369            @Override
370            public void windowClosing(java.awt.event.WindowEvent e) {
371                for (JFrameItem frame : iconAdderFrames.values()) {
372                    frame.dispose();
373                }
374            }
375
376            WindowAdapter init(HashMap<String, JFrameItem> f) {
377                iconAdderFrames = f;
378                return this;
379            }
380        }.init(_iconEditorFrame));
381
382        // and don't destroy the window
383        setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE);
384        // move this editor panel off the panel's position
385        getTargetFrame().setLocationRelativeTo(this);
386        getTargetFrame().pack();
387        getTargetFrame().setVisible(true);
388        log.debug("PanelEditor ctor done.");
389    }  // end ctor
390
391    /**
392     * Initializes the hiddencheckbox and its listener. This has been taken out
393     * of the init, as checkbox is enable/disabled by the editableBox.
394     */
395    private void hiddenCheckBoxListener() {
396        setShowHidden(hiddenBox.isSelected());
397        if (editableBox.isSelected()) {
398            hiddenBox.setEnabled(false);
399//            hiddenBox.setSelected(true);
400        } else {
401            hiddenBox.setEnabled(true);
402            hiddenBox.addActionListener(event -> setShowHidden(hiddenBox.isSelected()));
403        }
404
405    }
406
407    /**
408     * After construction, initialize all the widgets to their saved config
409     * settings.
410     */
411    @Override
412    public void initView() {
413        editableBox.setSelected(isEditable());
414        positionableBox.setSelected(allPositionable());
415        controllingBox.setSelected(allControlling());
416        //showCoordinatesBox.setSelected(showCoordinates());
417        showTooltipBox.setSelected(showToolTip());
418        hiddenBox.setSelected(showHidden());
419        menuBox.setSelected(getTargetFrame().getJMenuBar().isVisible());
420    }
421
422    static class ComboBoxItem {
423
424        private final String name;
425
426        protected ComboBoxItem(String n) {
427            name = n;
428        }
429
430        protected String getName() {
431            return name;
432        }
433
434        @Override
435        public String toString() {
436            // I18N split Bundle name
437            // use NamedBeanBundle property for basic beans like "Turnout" I18N
438            String bundleName;
439            if (SENSOR.equals(name)) {
440                bundleName = "BeanNameSensor";
441            } else if (SIGNAL_HEAD.equals(name)) {
442                bundleName = "BeanNameSignalHead";
443            } else if (SIGNAL_MAST.equals(name)) {
444                bundleName = "BeanNameSignalMast";
445            } else if (MEMORY.equals(name)) {
446                bundleName = "BeanNameMemory";
447            } else if (REPORTER.equals(name)) {
448                bundleName = "BeanNameReporter";
449            } else if (LIGHT.equals(name)) {
450                bundleName = "BeanNameLight";
451            } else if (GLOBAL_VARIABLE.equals(name)) {
452                bundleName = "BeanNameGlobalVariable";
453            } else if (AUDIO.equals(name)) {
454                bundleName = "BeanNameAudio";
455            } else {
456                bundleName = name;
457            }
458            return Bundle.getMessage(bundleName); // use NamedBeanBundle property for basic beans like "Turnout" I18N
459        }
460    }
461
462    /*
463     * itemListener for JComboBox.
464     */
465    @Override
466    public void itemStateChanged(ItemEvent e) {
467        if (e.getStateChange() == ItemEvent.SELECTED) {
468            ComboBoxItem item = (ComboBoxItem) e.getItem();
469            String name = item.getName();
470            JFrameItem frame = super.getIconFrame(name);
471            if (frame != null) {
472                frame.getEditor().reset();
473                frame.setVisible(true);
474            } else {
475                if (name.equals(FAST_CLOCK)) {
476                    addClock();
477                } else if (name.equals(RPSREPORTER)) {
478                    addRpsReporter();
479                } else {
480                    log.error("Unable to open Icon Editor \"{}\"", item.getName());
481                }
482            }
483            _addIconBox.setSelectedIndex(-1);
484        }
485    }
486
487    /**
488     * Handle close of editor window.
489     * <p>
490     * Overload/override method in JmriJFrame parent, which by default is
491     * permanently closing the window. Here, we just want to make it invisible,
492     * so we don't dispose it (yet).
493     */
494    @Override
495    public void windowClosing(java.awt.event.WindowEvent e) {
496        setVisible(false);
497    }
498
499    /**
500     * Create sequence of panels, etc, for layout: JFrame contains its
501     * ContentPane which contains a JPanel with BoxLayout (p1) which contains a
502     * JScollPane (js) which contains the targetPane.
503     * @param name the frame name.
504     * @return the frame.
505     */
506    public JmriJFrame makeFrame(String name) {
507        JmriJFrame targetFrame = new JmriJFrame(name);
508        targetFrame.setVisible(false);
509
510        JMenuBar menuBar = new JMenuBar();
511        JMenu editMenu = new JMenu(Bundle.getMessage("MenuEdit"));
512        menuBar.add(editMenu);
513        editMenu.add(new AbstractAction(Bundle.getMessage("OpenEditor")) {
514            @Override
515            public void actionPerformed(ActionEvent e) {
516                setVisible(true);
517            }
518        });
519        editMenu.addSeparator();
520        editMenu.add(new AbstractAction(Bundle.getMessage("DeletePanel")) {
521            @Override
522            public void actionPerformed(ActionEvent e) {
523                if (deletePanel()) {
524                    dispose();
525                }
526            }
527        });
528        targetFrame.setJMenuBar(menuBar);
529        // add maker menu
530        JMenu markerMenu = new JMenu(Bundle.getMessage("MenuMarker"));
531        menuBar.add(markerMenu);
532        markerMenu.add(new AbstractAction(Bundle.getMessage("AddLoco")) {
533            @Override
534            public void actionPerformed(ActionEvent e) {
535                locoMarkerFromInput();
536            }
537        });
538        markerMenu.add(new AbstractAction(Bundle.getMessage("AddLocoRoster")) {
539            @Override
540            public void actionPerformed(ActionEvent e) {
541                locoMarkerFromRoster();
542            }
543        });
544        markerMenu.add(new AbstractAction(Bundle.getMessage("RemoveMarkers")) {
545            @Override
546            public void actionPerformed(ActionEvent e) {
547                removeMarkers();
548            }
549        });
550
551        JMenu warrantMenu = jmri.jmrit.logix.WarrantTableAction.getDefault().makeWarrantMenu(isEditable());
552        if (warrantMenu != null) {
553            menuBar.add(warrantMenu);
554        }
555
556        targetFrame.addHelpMenu("package.jmri.jmrit.display.PanelTarget", true);
557        return targetFrame;
558    }
559
560    /*
561     ************* implementation of Abstract Editor methods **********
562     */
563
564    /**
565     * The target window has been requested to close, don't delete it at this
566     * time. Deletion must be accomplished via the Delete this panel menu item.
567     */
568    @Override
569    protected void targetWindowClosingEvent(java.awt.event.WindowEvent e) {
570        targetWindowClosing();
571    }
572
573    /**
574     * Called from TargetPanel's paint method for additional drawing by editor
575     * view
576     */
577    @Override
578    protected void paintTargetPanel(Graphics g) {
579        /*Graphics2D g2 = (Graphics2D)g;
580         drawPositionableLabelBorder(g2);*/
581    }
582
583    /**
584     * Set an object's location when it is created.
585     */
586    @Override
587    protected void setNextLocation(Positionable obj) {
588        int x = Integer.parseInt(nextX.getText());
589        int y = Integer.parseInt(nextY.getText());
590        obj.setLocation(x, y);
591    }
592
593    /**
594     * Create popup for a Positionable object. Popup items common to all
595     * positionable objects are done before and after the items that pertain
596     * only to specific Positionable types.
597     *
598     * @param p           the item containing or requiring the context menu
599     * @param event       the event triggering the menu
600     * @param selections  the list of all Positionables at this position
601     */
602    protected void showPopUp(Positionable p, JmriMouseEvent event, List<Positionable> selections) {
603        if (!((JComponent) p).isVisible()) {
604            return;     // component must be showing on the screen to determine its location
605        }
606        JPopupMenu popup = new JPopupMenu();
607        PositionablePopupUtil util = p.getPopupUtility();
608        if (p.isEditable()) {
609            // items for all Positionables
610            if (p.doViemMenu()) {
611                popup.add(p.getNameString());
612                setPositionableMenu(p, popup);
613                if (p.isPositionable()) {
614                    setShowCoordinatesMenu(p, popup);
615                    setShowAlignmentMenu(p, popup);
616                }
617                setDisplayLevelMenu(p, popup);
618                setHiddenMenu(p, popup);
619                setEmptyHiddenMenu(p, popup);
620                setValueEditDisabledMenu(p, popup);
621                setEditIdMenu(p, popup);
622                setEditClassesMenu(p, popup);
623                popup.addSeparator();
624                setLogixNGPositionableMenu(p, popup);
625                popup.addSeparator();
626            }
627
628            // Positionable items with defaults or using overrides
629            boolean popupSet = false;
630            popupSet = p.setRotateOrthogonalMenu(popup);
631            popupSet |= p.setRotateMenu(popup);
632            popupSet |= p.setScaleMenu(popup);
633            if (popupSet) {
634                popup.addSeparator();
635            }
636            popupSet = p.setEditIconMenu(popup);
637            if (popupSet) {
638                popup.addSeparator();
639            }
640            popupSet = p.setTextEditMenu(popup);
641            if (util != null) {
642                util.setFixedTextMenu(popup);
643                util.setTextMarginMenu(popup);
644                util.setTextBorderMenu(popup);
645                util.setTextFontMenu(popup);
646                util.setBackgroundMenu(popup);
647                util.setTextJustificationMenu(popup);
648                util.setTextOrientationMenu(popup);
649                util.copyItem(popup);
650                popup.addSeparator();
651                util.propertyUtil(popup);
652                util.setAdditionalEditPopUpMenu(popup);
653                popupSet = true;
654            }
655            if (popupSet) {
656                popup.addSeparator();
657            }
658            p.setDisableControlMenu(popup);
659
660            // for Positionables with unique item settings
661            p.showPopUp(popup);
662
663            setShowToolTipMenu(p, popup);
664            setRemoveMenu(p, popup);
665        } else {
666            p.showPopUp(popup);
667            if (util != null) {
668                util.setAdditionalViewPopUpMenu(popup);
669            }
670        }
671
672        if (selections.size() > 1) {
673            boolean found = false;
674            JMenu iconsBelowMenu = new JMenu(Bundle.getMessage("MenuItemIconsBelow"));
675            for (int i=0; i < selections.size(); i++) {
676                Positionable pos = selections.get(i);
677                if (found) {
678                    iconsBelowMenu.add(new AbstractAction(Bundle.getMessage(
679                            "PositionableTypeAndName", pos.getTypeString(), pos.getNameString())) {
680                        @Override
681                        public void actionPerformed(ActionEvent e) {
682                            showPopUp(pos, event, new ArrayList<>());
683                        }
684                    });
685                } else {
686                    if (p == pos) found = true;
687                }
688            }
689            popup.addSeparator();
690            popup.add(iconsBelowMenu);
691        }
692
693        popup.show((Component) p, p.getWidth() / 2, p.getHeight() / 2);
694    }
695
696    /**
697     * ***************************************************
698     */
699    private boolean delayedPopupTrigger;
700
701    @Override
702    public void mousePressed(JmriMouseEvent event) {
703        setToolTip(null); // ends tooltip if displayed
704        if (log.isDebugEnabled()) {
705            log.debug("mousePressed at ({},{}) _dragging= {}", event.getX(), event.getY(), _dragging);
706        }
707        _anchorX = event.getX();
708        _anchorY = event.getY();
709        _lastX = _anchorX;
710        _lastY = _anchorY;
711        List<Positionable> selections = getSelectedItems(event);
712        if (_dragging) {
713            return;
714        }
715        if (selections.size() > 0) {
716            if (event.isShiftDown() && selections.size() > 1) {
717                _currentSelection = selections.get(1);
718            } else {
719                _currentSelection = selections.get(0);
720            }
721            if (event.isPopupTrigger()) {
722                log.debug("mousePressed calls showPopUp");
723                if (event.isMetaDown() || event.isAltDown()) {
724                    // if requesting a popup and it might conflict with moving, delay the request to mouseReleased
725                    delayedPopupTrigger = true;
726                } else {
727                    // no possible conflict with moving, display the popup now
728                    if (_selectionGroup != null) {
729                        //Will show the copy option only
730                        showMultiSelectPopUp(event, _currentSelection);
731                    } else {
732                        showPopUp(_currentSelection, event, selections);
733                    }
734                }
735            } else if (!event.isControlDown()) {
736                _currentSelection.doMousePressed(event);
737                if (_multiItemCopyGroup != null && !_multiItemCopyGroup.contains(_currentSelection)) {
738                    _multiItemCopyGroup = null;
739                }
740                // _selectionGroup = null;
741            }
742        } else {
743            if (event.isPopupTrigger()) {
744                if (event.isMetaDown() || event.isAltDown()) {
745                    // if requesting a popup and it might conflict with moving, delay the request to mouseReleased
746                    delayedPopupTrigger = true;
747                } else {
748                    if (_multiItemCopyGroup != null) {
749                        pasteItemPopUp(event);
750                    } else if (_selectionGroup != null) {
751                        showMultiSelectPopUp(event, _currentSelection);
752                    } else {
753                        backgroundPopUp(event);
754                        _currentSelection = null;
755                    }
756                }
757            } else {
758                _currentSelection = null;
759            }
760        }
761        // if ((event.isControlDown() || _selectionGroup!=null) && _currentSelection!=null){
762        if ((event.isControlDown()) || event.isMetaDown() || event.isAltDown()) {
763            //Don't want to do anything, just want to catch it, so that the next two else ifs are not
764            //executed
765        } else if ((_currentSelection == null && _multiItemCopyGroup == null)
766                || (_selectRect != null && !_selectRect.contains(_anchorX, _anchorY))) {
767            _selectRect = new Rectangle(_anchorX, _anchorY, 0, 0);
768            _selectionGroup = null;
769        } else {
770            _selectRect = null;
771            _selectionGroup = null;
772        }
773        _targetPanel.repaint(); // needed for ToolTip
774    }
775
776    @Override
777    public void mouseReleased(JmriMouseEvent event) {
778        setToolTip(null); // ends tooltip if displayed
779        if (log.isDebugEnabled()) {
780            // in if statement to avoid inline conditional unless logging
781            log.debug("mouseReleased at ({},{}) dragging= {} selectRect is {}", event.getX(), event.getY(), _dragging,
782                    _selectRect == null ? "null" : "not null");
783        }
784        List<Positionable> selections = getSelectedItems(event);
785
786        if (_dragging) {
787            mouseDragged(event);
788        }
789        if (selections.size() > 0) {
790            if (event.isShiftDown() && selections.size() > 1) {
791                _currentSelection = selections.get(1);
792            } else {
793                _currentSelection = selections.get(0);
794            }
795            if (_multiItemCopyGroup != null && !_multiItemCopyGroup.contains(_currentSelection)) {
796                _multiItemCopyGroup = null;
797            }
798        } else {
799            if ((event.isPopupTrigger() || delayedPopupTrigger) && !_dragging) {
800                if (_multiItemCopyGroup != null) {
801                    pasteItemPopUp(event);
802                } else {
803                    backgroundPopUp(event);
804                    _currentSelection = null;
805                }
806            } else {
807                _currentSelection = null;
808
809            }
810        }
811        /*if (event.isControlDown() && _currentSelection!=null && !event.isPopupTrigger()){
812         amendSelectionGroup(_currentSelection, event);*/
813        if ((event.isPopupTrigger() || delayedPopupTrigger) && _currentSelection != null && !_dragging) {
814            if (_selectionGroup != null) {
815                //Will show the copy option only
816                showMultiSelectPopUp(event, _currentSelection);
817
818            } else {
819                showPopUp(_currentSelection, event, selections);
820            }
821        } else {
822            if (_currentSelection != null && !_dragging && !event.isControlDown()) {
823                _currentSelection.doMouseReleased(event);
824            }
825            if (allPositionable() && _selectRect != null) {
826                if (_selectionGroup == null && _dragging) {
827                    makeSelectionGroup(event);
828                }
829            }
830        }
831        delayedPopupTrigger = false;
832        _dragging = false;
833        _selectRect = null;
834
835        // if not sending MouseClicked, do it here
836        if (InstanceManager.getDefault(GuiLafPreferencesManager.class).isNonStandardMouseEvent()) {
837            mouseClicked(event);
838        }
839        _targetPanel.repaint(); // needed for ToolTip
840    }
841
842    @Override
843    public void mouseDragged(JmriMouseEvent event) {
844        setToolTip(null); // ends tooltip if displayed
845        if ((event.isPopupTrigger()) || (!event.isMetaDown() && !event.isAltDown())) {
846            if (_currentSelection != null) {
847                List<Positionable> selections = getSelectedItems(event);
848                if (selections.size() > 0) {
849                    if (selections.get(0) != _currentSelection) {
850                        _currentSelection.doMouseReleased(event);
851                    } else {
852                        _currentSelection.doMouseDragged(event);
853                    }
854                } else {
855                    _currentSelection.doMouseReleased(event);
856                }
857            }
858            return;
859        }
860        moveIt:
861        if (_currentSelection != null && getFlag(OPTION_POSITION, _currentSelection.isPositionable())) {
862            int deltaX = event.getX() - _lastX;
863            int deltaY = event.getY() - _lastY;
864            int minX = getItemX(_currentSelection, deltaX);
865            int minY = getItemY(_currentSelection, deltaY);
866            if (_selectionGroup != null && _selectionGroup.contains(_currentSelection)) {
867                for (Positionable comp : _selectionGroup) {
868                    minX = Math.min(getItemX(comp, deltaX), minX);
869                    minY = Math.min(getItemY(comp, deltaY), minY);
870                }
871            }
872            if (minX < 0 || minY < 0) {
873                break moveIt;
874            }
875            if (_selectionGroup != null && _selectionGroup.contains(_currentSelection)) {
876                for (Positionable comp : _selectionGroup) {
877                    moveItem(comp, deltaX, deltaY);
878                }
879                _highlightcomponent = null;
880            } else {
881                moveItem(_currentSelection, deltaX, deltaY);
882                _highlightcomponent = new Rectangle(_currentSelection.getX(), _currentSelection.getY(),
883                        _currentSelection.maxWidth(), _currentSelection.maxHeight());
884            }
885        } else {
886            if (allPositionable() && _selectionGroup == null) {
887                drawSelectRect(event.getX(), event.getY());
888            }
889        }
890        _dragging = true;
891        _lastX = event.getX();
892        _lastY = event.getY();
893        _targetPanel.repaint(); // needed for ToolTip
894    }
895
896    @Override
897    public void mouseMoved(JmriMouseEvent event) {
898        // log.debug("mouseMoved at ({},{})", event.getX(), event.getY());
899        if (_dragging || event.isPopupTrigger()) {
900            return;
901        }
902
903        List<Positionable> selections = getSelectedItems(event);
904        Positionable selection = null;
905        if (selections.size() > 0) {
906            if (event.isShiftDown() && selections.size() > 1) {
907                selection = selections.get(1);
908            } else {
909                selection = selections.get(0);
910            }
911        }
912        if (isEditable() && selection != null && selection.getDisplayLevel() > BKG) {
913            _highlightcomponent = new Rectangle(selection.getX(), selection.getY(), selection.maxWidth(), selection.maxHeight());
914            _targetPanel.repaint();
915        } else {
916            _highlightcomponent = null;
917            _targetPanel.repaint();
918        }
919        if (selection != null && selection.getDisplayLevel() > BKG && selection.showToolTip()) {
920            showToolTip(selection, event);
921            //selection.highlightlabel(true);
922            _targetPanel.repaint();
923        } else {
924            setToolTip(null);
925            _highlightcomponent = null;
926            _targetPanel.repaint();
927        }
928    }
929
930    @Override
931    public void mouseClicked(JmriMouseEvent event) {
932        setToolTip(null); // ends tooltip if displayed
933        if (log.isDebugEnabled()) {
934            log.debug("mouseClicked at ({},{}) dragging= {} selectRect is {}",
935                    event.getX(), event.getY(), _dragging, (_selectRect == null ? "null" : "not null"));
936        }
937        List<Positionable> selections = getSelectedItems(event);
938
939        if (selections.size() > 0) {
940            if (event.isShiftDown() && selections.size() > 1) {
941                _currentSelection = selections.get(1);
942            } else {
943                _currentSelection = selections.get(0);
944            }
945        } else {
946            _currentSelection = null;
947            if (event.isPopupTrigger()) {
948                if (_multiItemCopyGroup == null) {
949                    pasteItemPopUp(event);
950                } else {
951                    backgroundPopUp(event);
952                }
953            }
954        }
955        if (event.isPopupTrigger() && _currentSelection != null && !_dragging) {
956            if (_selectionGroup != null) {
957                showMultiSelectPopUp(event, _currentSelection);
958            } else {
959                showPopUp(_currentSelection, event, selections);
960            }
961            // _selectionGroup = null; // Show popup only works for a single item
962
963        } else {
964            if (_currentSelection != null && !_dragging && !event.isControlDown()) {
965                _currentSelection.doMouseClicked(event);
966            }
967        }
968        _targetPanel.repaint(); // needed for ToolTip
969        if (event.isControlDown() && _currentSelection != null && !event.isPopupTrigger()) {
970            amendSelectionGroup(_currentSelection);
971        }
972    }
973
974    @Override
975    public void mouseEntered(JmriMouseEvent event) {
976    }
977
978    @Override
979    public void mouseExited(JmriMouseEvent event) {
980        setToolTip(null);
981        _targetPanel.repaint();  // needed for ToolTip
982    }
983
984    protected ArrayList<Positionable> _multiItemCopyGroup = null;  // items gathered inside fence
985
986    @Override
987    protected void copyItem(Positionable p) {
988        _multiItemCopyGroup = new ArrayList<>();
989        _multiItemCopyGroup.add(p);
990    }
991
992    protected void pasteItemPopUp(final JmriMouseEvent event) {
993        if (!isEditable()) {
994            return;
995        }
996        if (_multiItemCopyGroup == null) {
997            return;
998        }
999        JPopupMenu popup = new JPopupMenu();
1000        JMenuItem edit = new JMenuItem(Bundle.getMessage("MenuItemPaste"));
1001        edit.addActionListener(e -> pasteItem(event));
1002        setBackgroundMenu(popup);
1003        showAddItemPopUp(event, popup);
1004        popup.add(edit);
1005        popup.show(event.getComponent(), event.getX(), event.getY());
1006    }
1007
1008    protected void backgroundPopUp(JmriMouseEvent event) {
1009        if (!isEditable()) {
1010            return;
1011        }
1012        JPopupMenu popup = new JPopupMenu();
1013        setBackgroundMenu(popup);
1014        showAddItemPopUp(event, popup);
1015        popup.show(event.getComponent(), event.getX(), event.getY());
1016    }
1017
1018    protected void showMultiSelectPopUp(final JmriMouseEvent event, Positionable p) {
1019        JPopupMenu popup = new JPopupMenu();
1020        JMenuItem copy = new JMenuItem(Bundle.getMessage("MenuItemCopy")); // changed "edit" to "copy"
1021        if (p.isPositionable()) {
1022            setShowAlignmentMenu(p, popup);
1023        }
1024        copy.addActionListener(e -> {
1025            _multiItemCopyGroup = new ArrayList<>();
1026            // must make a copy or pasteItem() will hang
1027            if (_selectionGroup != null) {
1028                _multiItemCopyGroup.addAll(_selectionGroup);
1029            }
1030        });
1031
1032        setMultiItemsPositionableMenu(popup); // adding Lock Position for all
1033        // selected items
1034
1035        setRemoveMenu(p, popup);
1036        //showAddItemPopUp(event, popup); // no need to Add when group selected
1037        popup.add(copy);
1038        popup.show(event.getComponent(), event.getX(), event.getY());
1039    }
1040
1041    protected void showAddItemPopUp(final JmriMouseEvent event, JPopupMenu popup) {
1042        if (!isEditable()) {
1043            return;
1044        }
1045        JMenu _add = new JMenu(Bundle.getMessage("MenuItemAddItem"));
1046        // for items in the following list, I18N is picked up later on
1047        addItemPopUp(new ComboBoxItem(RIGHT_TURNOUT), _add);
1048        addItemPopUp(new ComboBoxItem(LEFT_TURNOUT), _add);
1049        addItemPopUp(new ComboBoxItem(SLIP_TO_EDITOR), _add);
1050        addItemPopUp(new ComboBoxItem(SENSOR), _add);
1051        addItemPopUp(new ComboBoxItem(SIGNAL_HEAD), _add);
1052        addItemPopUp(new ComboBoxItem(SIGNAL_MAST), _add);
1053        addItemPopUp(new ComboBoxItem(MEMORY), _add);
1054        addItemPopUp(new ComboBoxItem(BLOCK_LABEL), _add);
1055        addItemPopUp(new ComboBoxItem(REPORTER), _add);
1056        addItemPopUp(new ComboBoxItem(LIGHT), _add);
1057        addItemPopUp(new ComboBoxItem(BACKGROUND), _add);
1058        addItemPopUp(new ComboBoxItem(MULTI_SENSOR), _add);
1059        addItemPopUp(new ComboBoxItem(RPSREPORTER), _add);
1060        addItemPopUp(new ComboBoxItem(FAST_CLOCK), _add);
1061        addItemPopUp(new ComboBoxItem(GLOBAL_VARIABLE), _add);
1062        addItemPopUp(new ComboBoxItem(AUDIO), _add);
1063        addItemPopUp(new ComboBoxItem(LOGIXNG), _add);
1064        addItemPopUp(new ComboBoxItem(ICON), _add);
1065        addItemPopUp(new ComboBoxItem("Text"), _add);
1066        popup.add(_add);
1067    }
1068
1069    protected void addItemPopUp(final ComboBoxItem item, JMenu menu) {
1070
1071        ActionListener a = new ActionListener() {
1072            //final String desiredName = name;
1073            @Override
1074            public void actionPerformed(ActionEvent e) {
1075                addItemViaMouseClick = true;
1076                getIconFrame(item.getName());
1077            }
1078
1079            ActionListener init(ComboBoxItem i) {
1080                return this;
1081            }
1082        }.init(item);
1083        JMenuItem addto = new JMenuItem(item.toString());
1084        addto.addActionListener(a);
1085        menu.add(addto);
1086    }
1087
1088    protected boolean addItemViaMouseClick = false;
1089
1090    @Override
1091    public void putItem(Positionable l) throws Positionable.DuplicateIdException {
1092        super.putItem(l);
1093        /*This allows us to catch any new items that are being pasted into the panel
1094         and add them to the selection group, so that the user can instantly move them around*/
1095        //!!!
1096        if (pasteItemFlag) {
1097            amendSelectionGroup(l);
1098            return;
1099        }
1100        if (addItemViaMouseClick) {
1101            addItemViaMouseClick = false;
1102            l.setLocation(_lastX, _lastY);
1103        }
1104    }
1105
1106    private void amendSelectionGroup(Positionable p) {
1107        if (p == null) {
1108            return;
1109        }
1110        if (_selectionGroup == null) {
1111            _selectionGroup = new ArrayList<>();
1112        }
1113        boolean removed = false;
1114        for (int i = 0; i < _selectionGroup.size(); i++) {
1115            if (_selectionGroup.get(i) == p) {
1116                _selectionGroup.remove(i);
1117                removed = true;
1118                break;
1119            }
1120        }
1121        if (!removed) {
1122            _selectionGroup.add(p);
1123        } else if (_selectionGroup.isEmpty()) {
1124            _selectionGroup = null;
1125        }
1126        _targetPanel.repaint();
1127    }
1128
1129    protected boolean pasteItemFlag = false;
1130
1131    protected void pasteItem(JmriMouseEvent e) {
1132        pasteItemFlag = true;
1133        XmlAdapter adapter;
1134        String className;
1135        int x;
1136        int y;
1137        int xOrig;
1138        int yOrig;
1139        if (_multiItemCopyGroup != null) {
1140            JComponent copied;
1141            int xoffset;
1142            int yoffset;
1143            x = _multiItemCopyGroup.get(0).getX();
1144            y = _multiItemCopyGroup.get(0).getY();
1145            xoffset = e.getX() - x;
1146            yoffset = e.getY() - y;
1147            /*We make a copy of the selected items and work off of that copy
1148             as amendments are made to the multiItemCopyGroup during this process
1149             which can result in a loop*/
1150            ArrayList<Positionable> _copyOfMultiItemCopyGroup = new ArrayList<>(_multiItemCopyGroup);
1151            Collections.copy(_copyOfMultiItemCopyGroup, _multiItemCopyGroup);
1152            for (Positionable comp : _copyOfMultiItemCopyGroup) {
1153                copied = (JComponent) comp;
1154                xOrig = copied.getX();
1155                yOrig = copied.getY();
1156                x = xOrig + xoffset;
1157                y = yOrig + yoffset;
1158                if (x < 0) {
1159                    x = 1;
1160                }
1161                if (y < 0) {
1162                    y = 1;
1163                }
1164                className = ConfigXmlManager.adapterName(copied);
1165                copied.setLocation(x, y);
1166                try {
1167                    adapter = (XmlAdapter) Class.forName(className).getDeclaredConstructor().newInstance();
1168                    Element el = adapter.store(copied);
1169                    adapter.load(el, this);
1170                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException
1171                    | jmri.configurexml.JmriConfigureXmlException
1172                    | RuntimeException ex) {
1173                        log.debug("Could not paste.", ex);
1174                }
1175                /*We remove the original item from the list, so we end up with
1176                 just the new items selected and allow the items to be moved around */
1177                amendSelectionGroup(comp);
1178                copied.setLocation(xOrig, yOrig);
1179            }
1180            _selectionGroup = null;
1181        }
1182        pasteItemFlag = false;
1183        _targetPanel.repaint();
1184    }
1185
1186    /**
1187     * Add an action to remove the Positionable item.
1188     */
1189    @Override
1190    public void setRemoveMenu(Positionable p, JPopupMenu popup) {
1191        popup.add(new AbstractAction(Bundle.getMessage("Remove")) {
1192            Positionable comp;
1193
1194            @Override
1195            public void actionPerformed(ActionEvent e) {
1196                if (_selectionGroup == null) {
1197                    comp.remove();
1198                } else {
1199                    removeMultiItems();
1200                }
1201            }
1202
1203            AbstractAction init(Positionable pos) {
1204                comp = pos;
1205                return this;
1206            }
1207        }.init(p));
1208    }
1209
1210    private void removeMultiItems() {
1211        boolean itemsInCopy = false;
1212        if (_selectionGroup == _multiItemCopyGroup) {
1213            itemsInCopy = true;
1214        }
1215        for (Positionable comp : _selectionGroup) {
1216            comp.remove();
1217        }
1218        //As we have removed all the items from the panel we can remove the group.
1219        _selectionGroup = null;
1220        //If the items in the selection group and copy group are the same we need to
1221        //clear the copy group as the originals no longer exist.
1222        if (itemsInCopy) {
1223            _multiItemCopyGroup = null;
1224        }
1225    }
1226
1227    // This adds a single CheckBox in the PopupMenu to set or clear all the selected
1228    // items "Lock Position" or Positionable setting, when clicked, all the items in
1229    // the selection will be changed accordingly.
1230    private void setMultiItemsPositionableMenu(JPopupMenu popup) {
1231        // This would do great with a "greyed" CheckBox if the multiple items have different states.
1232        // Then selecting the true or false state would force all to change to true or false
1233
1234        JCheckBoxMenuItem lockItem = new JCheckBoxMenuItem(Bundle.getMessage("LockPosition"));
1235        boolean allSetToMove = false;  // used to decide the state of the checkbox shown
1236        int trues = 0;                 // used to see if all items have the same setting
1237
1238        int size = _selectionGroup.size();
1239
1240        for (Positionable comp : _selectionGroup) {
1241            if (!comp.isPositionable()) {
1242                allSetToMove = true;
1243                trues++;
1244            }
1245
1246            lockItem.setSelected(allSetToMove);
1247
1248            lockItem.addActionListener(new ActionListener() {
1249                Positionable comp;
1250                JCheckBoxMenuItem checkBox;
1251
1252                @Override
1253                public void actionPerformed(ActionEvent e) {
1254                    comp.setPositionable(!checkBox.isSelected());
1255                    setSelectionsPositionable(!checkBox.isSelected(), comp);
1256                }
1257
1258                ActionListener init(Positionable pos, JCheckBoxMenuItem cb) {
1259                    comp = pos;
1260                    checkBox = cb;
1261                    return this;
1262                }
1263            }.init(comp, lockItem));
1264        }
1265
1266        // Add "~" to the Text when all items do not have the same setting,
1267        // until we get a "greyed" CheckBox ;) - GJM
1268        if ((trues != size) && (trues != 0)) {
1269            lockItem.setText("~ " + lockItem.getText());
1270            // uncheck box if all not the same
1271            lockItem.setSelected(false);
1272        }
1273        popup.add(lockItem);
1274    }
1275
1276    public void setBackgroundMenu(JPopupMenu popup) {
1277        // Panel background, not text background
1278        JMenuItem edit = new JMenuItem(Bundle.getMessage("FontBackgroundColor"));
1279        edit.addActionListener((ActionEvent event) -> {
1280            Color desiredColor = JmriColorChooser.showDialog(this,
1281                                 Bundle.getMessage("FontBackgroundColor"),
1282                                 getBackgroundColor());
1283            if (desiredColor!=null ) {
1284               setBackgroundColor(desiredColor);
1285           }
1286        });
1287        popup.add(edit);
1288    }
1289
1290    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(PanelEditor.class);
1291
1292}