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                setEditIdMenu(p, popup);
621                setEditClassesMenu(p, popup);
622                popup.addSeparator();
623                setLogixNGPositionableMenu(p, popup);
624                popup.addSeparator();
625            }
626
627            // Positionable items with defaults or using overrides
628            boolean popupSet = false;
629            popupSet = p.setRotateOrthogonalMenu(popup);
630            popupSet |= p.setRotateMenu(popup);
631            popupSet |= p.setScaleMenu(popup);
632            if (popupSet) {
633                popup.addSeparator();
634            }
635            popupSet = p.setEditIconMenu(popup);
636            if (popupSet) {
637                popup.addSeparator();
638            }
639            popupSet = p.setTextEditMenu(popup);
640            if (util != null) {
641                util.setFixedTextMenu(popup);
642                util.setTextMarginMenu(popup);
643                util.setTextBorderMenu(popup);
644                util.setTextFontMenu(popup);
645                util.setBackgroundMenu(popup);
646                util.setTextJustificationMenu(popup);
647                util.setTextOrientationMenu(popup);
648                util.copyItem(popup);
649                popup.addSeparator();
650                util.propertyUtil(popup);
651                util.setAdditionalEditPopUpMenu(popup);
652                popupSet = true;
653            }
654            if (popupSet) {
655                popup.addSeparator();
656            }
657            p.setDisableControlMenu(popup);
658
659            // for Positionables with unique item settings
660            p.showPopUp(popup);
661
662            setShowToolTipMenu(p, popup);
663            setRemoveMenu(p, popup);
664        } else {
665            p.showPopUp(popup);
666            if (util != null) {
667                util.setAdditionalViewPopUpMenu(popup);
668            }
669        }
670
671        if (selections.size() > 1) {
672            boolean found = false;
673            JMenu iconsBelowMenu = new JMenu(Bundle.getMessage("MenuItemIconsBelow"));
674            for (int i=0; i < selections.size(); i++) {
675                Positionable pos = selections.get(i);
676                if (found) {
677                    iconsBelowMenu.add(new AbstractAction(Bundle.getMessage(
678                            "PositionableTypeAndName", pos.getTypeString(), pos.getNameString())) {
679                        @Override
680                        public void actionPerformed(ActionEvent e) {
681                            showPopUp(pos, event, new ArrayList<>());
682                        }
683                    });
684                } else {
685                    if (p == pos) found = true;
686                }
687            }
688            popup.addSeparator();
689            popup.add(iconsBelowMenu);
690        }
691
692        popup.show((Component) p, p.getWidth() / 2, p.getHeight() / 2);
693    }
694
695    /**
696     * ***************************************************
697     */
698    private boolean delayedPopupTrigger;
699
700    @Override
701    public void mousePressed(JmriMouseEvent event) {
702        setToolTip(null); // ends tooltip if displayed
703        if (log.isDebugEnabled()) {
704            log.debug("mousePressed at ({},{}) _dragging= {}", event.getX(), event.getY(), _dragging);
705        }
706        _anchorX = event.getX();
707        _anchorY = event.getY();
708        _lastX = _anchorX;
709        _lastY = _anchorY;
710        List<Positionable> selections = getSelectedItems(event);
711        if (_dragging) {
712            return;
713        }
714        if (selections.size() > 0) {
715            if (event.isShiftDown() && selections.size() > 1) {
716                _currentSelection = selections.get(1);
717            } else {
718                _currentSelection = selections.get(0);
719            }
720            if (event.isPopupTrigger()) {
721                log.debug("mousePressed calls showPopUp");
722                if (event.isMetaDown() || event.isAltDown()) {
723                    // if requesting a popup and it might conflict with moving, delay the request to mouseReleased
724                    delayedPopupTrigger = true;
725                } else {
726                    // no possible conflict with moving, display the popup now
727                    if (_selectionGroup != null) {
728                        //Will show the copy option only
729                        showMultiSelectPopUp(event, _currentSelection);
730                    } else {
731                        showPopUp(_currentSelection, event, selections);
732                    }
733                }
734            } else if (!event.isControlDown()) {
735                _currentSelection.doMousePressed(event);
736                if (_multiItemCopyGroup != null && !_multiItemCopyGroup.contains(_currentSelection)) {
737                    _multiItemCopyGroup = null;
738                }
739                // _selectionGroup = null;
740            }
741        } else {
742            if (event.isPopupTrigger()) {
743                if (event.isMetaDown() || event.isAltDown()) {
744                    // if requesting a popup and it might conflict with moving, delay the request to mouseReleased
745                    delayedPopupTrigger = true;
746                } else {
747                    if (_multiItemCopyGroup != null) {
748                        pasteItemPopUp(event);
749                    } else if (_selectionGroup != null) {
750                        showMultiSelectPopUp(event, _currentSelection);
751                    } else {
752                        backgroundPopUp(event);
753                        _currentSelection = null;
754                    }
755                }
756            } else {
757                _currentSelection = null;
758            }
759        }
760        // if ((event.isControlDown() || _selectionGroup!=null) && _currentSelection!=null){
761        if ((event.isControlDown()) || event.isMetaDown() || event.isAltDown()) {
762            //Don't want to do anything, just want to catch it, so that the next two else ifs are not
763            //executed
764        } else if ((_currentSelection == null && _multiItemCopyGroup == null)
765                || (_selectRect != null && !_selectRect.contains(_anchorX, _anchorY))) {
766            _selectRect = new Rectangle(_anchorX, _anchorY, 0, 0);
767            _selectionGroup = null;
768        } else {
769            _selectRect = null;
770            _selectionGroup = null;
771        }
772        _targetPanel.repaint(); // needed for ToolTip
773    }
774
775    @Override
776    public void mouseReleased(JmriMouseEvent event) {
777        setToolTip(null); // ends tooltip if displayed
778        if (log.isDebugEnabled()) {
779            // in if statement to avoid inline conditional unless logging
780            log.debug("mouseReleased at ({},{}) dragging= {} selectRect is {}", event.getX(), event.getY(), _dragging,
781                    _selectRect == null ? "null" : "not null");
782        }
783        List<Positionable> selections = getSelectedItems(event);
784
785        if (_dragging) {
786            mouseDragged(event);
787        }
788        if (selections.size() > 0) {
789            if (event.isShiftDown() && selections.size() > 1) {
790                _currentSelection = selections.get(1);
791            } else {
792                _currentSelection = selections.get(0);
793            }
794            if (_multiItemCopyGroup != null && !_multiItemCopyGroup.contains(_currentSelection)) {
795                _multiItemCopyGroup = null;
796            }
797        } else {
798            if ((event.isPopupTrigger() || delayedPopupTrigger) && !_dragging) {
799                if (_multiItemCopyGroup != null) {
800                    pasteItemPopUp(event);
801                } else {
802                    backgroundPopUp(event);
803                    _currentSelection = null;
804                }
805            } else {
806                _currentSelection = null;
807
808            }
809        }
810        /*if (event.isControlDown() && _currentSelection!=null && !event.isPopupTrigger()){
811         amendSelectionGroup(_currentSelection, event);*/
812        if ((event.isPopupTrigger() || delayedPopupTrigger) && _currentSelection != null && !_dragging) {
813            if (_selectionGroup != null) {
814                //Will show the copy option only
815                showMultiSelectPopUp(event, _currentSelection);
816
817            } else {
818                showPopUp(_currentSelection, event, selections);
819            }
820        } else {
821            if (_currentSelection != null && !_dragging && !event.isControlDown()) {
822                _currentSelection.doMouseReleased(event);
823            }
824            if (allPositionable() && _selectRect != null) {
825                if (_selectionGroup == null && _dragging) {
826                    makeSelectionGroup(event);
827                }
828            }
829        }
830        delayedPopupTrigger = false;
831        _dragging = false;
832        _selectRect = null;
833
834        // if not sending MouseClicked, do it here
835        if (InstanceManager.getDefault(GuiLafPreferencesManager.class).isNonStandardMouseEvent()) {
836            mouseClicked(event);
837        }
838        _targetPanel.repaint(); // needed for ToolTip
839    }
840
841    @Override
842    public void mouseDragged(JmriMouseEvent event) {
843        setToolTip(null); // ends tooltip if displayed
844        if ((event.isPopupTrigger()) || (!event.isMetaDown() && !event.isAltDown())) {
845            if (_currentSelection != null) {
846                List<Positionable> selections = getSelectedItems(event);
847                if (selections.size() > 0) {
848                    if (selections.get(0) != _currentSelection) {
849                        _currentSelection.doMouseReleased(event);
850                    } else {
851                        _currentSelection.doMouseDragged(event);
852                    }
853                } else {
854                    _currentSelection.doMouseReleased(event);
855                }
856            }
857            return;
858        }
859        moveIt:
860        if (_currentSelection != null && getFlag(OPTION_POSITION, _currentSelection.isPositionable())) {
861            int deltaX = event.getX() - _lastX;
862            int deltaY = event.getY() - _lastY;
863            int minX = getItemX(_currentSelection, deltaX);
864            int minY = getItemY(_currentSelection, deltaY);
865            if (_selectionGroup != null && _selectionGroup.contains(_currentSelection)) {
866                for (Positionable comp : _selectionGroup) {
867                    minX = Math.min(getItemX(comp, deltaX), minX);
868                    minY = Math.min(getItemY(comp, deltaY), minY);
869                }
870            }
871            if (minX < 0 || minY < 0) {
872                break moveIt;
873            }
874            if (_selectionGroup != null && _selectionGroup.contains(_currentSelection)) {
875                for (Positionable comp : _selectionGroup) {
876                    moveItem(comp, deltaX, deltaY);
877                }
878                _highlightcomponent = null;
879            } else {
880                moveItem(_currentSelection, deltaX, deltaY);
881                _highlightcomponent = new Rectangle(_currentSelection.getX(), _currentSelection.getY(),
882                        _currentSelection.maxWidth(), _currentSelection.maxHeight());
883            }
884        } else {
885            if (allPositionable() && _selectionGroup == null) {
886                drawSelectRect(event.getX(), event.getY());
887            }
888        }
889        _dragging = true;
890        _lastX = event.getX();
891        _lastY = event.getY();
892        _targetPanel.repaint(); // needed for ToolTip
893    }
894
895    @Override
896    public void mouseMoved(JmriMouseEvent event) {
897        // log.debug("mouseMoved at ({},{})", event.getX(), event.getY());
898        if (_dragging || event.isPopupTrigger()) {
899            return;
900        }
901
902        List<Positionable> selections = getSelectedItems(event);
903        Positionable selection = null;
904        if (selections.size() > 0) {
905            if (event.isShiftDown() && selections.size() > 1) {
906                selection = selections.get(1);
907            } else {
908                selection = selections.get(0);
909            }
910        }
911        if (isEditable() && selection != null && selection.getDisplayLevel() > BKG) {
912            _highlightcomponent = new Rectangle(selection.getX(), selection.getY(), selection.maxWidth(), selection.maxHeight());
913            _targetPanel.repaint();
914        } else {
915            _highlightcomponent = null;
916            _targetPanel.repaint();
917        }
918        if (selection != null && selection.getDisplayLevel() > BKG && selection.showToolTip()) {
919            showToolTip(selection, event);
920            //selection.highlightlabel(true);
921            _targetPanel.repaint();
922        } else {
923            setToolTip(null);
924            _highlightcomponent = null;
925            _targetPanel.repaint();
926        }
927    }
928
929    @Override
930    public void mouseClicked(JmriMouseEvent event) {
931        setToolTip(null); // ends tooltip if displayed
932        if (log.isDebugEnabled()) {
933            log.debug("mouseClicked at ({},{}) dragging= {} selectRect is {}",
934                    event.getX(), event.getY(), _dragging, (_selectRect == null ? "null" : "not null"));
935        }
936        List<Positionable> selections = getSelectedItems(event);
937
938        if (selections.size() > 0) {
939            if (event.isShiftDown() && selections.size() > 1) {
940                _currentSelection = selections.get(1);
941            } else {
942                _currentSelection = selections.get(0);
943            }
944        } else {
945            _currentSelection = null;
946            if (event.isPopupTrigger()) {
947                if (_multiItemCopyGroup == null) {
948                    pasteItemPopUp(event);
949                } else {
950                    backgroundPopUp(event);
951                }
952            }
953        }
954        if (event.isPopupTrigger() && _currentSelection != null && !_dragging) {
955            if (_selectionGroup != null) {
956                showMultiSelectPopUp(event, _currentSelection);
957            } else {
958                showPopUp(_currentSelection, event, selections);
959            }
960            // _selectionGroup = null; // Show popup only works for a single item
961
962        } else {
963            if (_currentSelection != null && !_dragging && !event.isControlDown()) {
964                _currentSelection.doMouseClicked(event);
965            }
966        }
967        _targetPanel.repaint(); // needed for ToolTip
968        if (event.isControlDown() && _currentSelection != null && !event.isPopupTrigger()) {
969            amendSelectionGroup(_currentSelection);
970        }
971    }
972
973    @Override
974    public void mouseEntered(JmriMouseEvent event) {
975    }
976
977    @Override
978    public void mouseExited(JmriMouseEvent event) {
979        setToolTip(null);
980        _targetPanel.repaint();  // needed for ToolTip
981    }
982
983    protected ArrayList<Positionable> _multiItemCopyGroup = null;  // items gathered inside fence
984
985    @Override
986    protected void copyItem(Positionable p) {
987        _multiItemCopyGroup = new ArrayList<>();
988        _multiItemCopyGroup.add(p);
989    }
990
991    protected void pasteItemPopUp(final JmriMouseEvent event) {
992        if (!isEditable()) {
993            return;
994        }
995        if (_multiItemCopyGroup == null) {
996            return;
997        }
998        JPopupMenu popup = new JPopupMenu();
999        JMenuItem edit = new JMenuItem(Bundle.getMessage("MenuItemPaste"));
1000        edit.addActionListener(e -> pasteItem(event));
1001        setBackgroundMenu(popup);
1002        showAddItemPopUp(event, popup);
1003        popup.add(edit);
1004        popup.show(event.getComponent(), event.getX(), event.getY());
1005    }
1006
1007    protected void backgroundPopUp(JmriMouseEvent event) {
1008        if (!isEditable()) {
1009            return;
1010        }
1011        JPopupMenu popup = new JPopupMenu();
1012        setBackgroundMenu(popup);
1013        showAddItemPopUp(event, popup);
1014        popup.show(event.getComponent(), event.getX(), event.getY());
1015    }
1016
1017    protected void showMultiSelectPopUp(final JmriMouseEvent event, Positionable p) {
1018        JPopupMenu popup = new JPopupMenu();
1019        JMenuItem copy = new JMenuItem(Bundle.getMessage("MenuItemCopy")); // changed "edit" to "copy"
1020        if (p.isPositionable()) {
1021            setShowAlignmentMenu(p, popup);
1022        }
1023        copy.addActionListener(e -> {
1024            _multiItemCopyGroup = new ArrayList<>();
1025            // must make a copy or pasteItem() will hang
1026            if (_selectionGroup != null) {
1027                _multiItemCopyGroup.addAll(_selectionGroup);
1028            }
1029        });
1030
1031        setMultiItemsPositionableMenu(popup); // adding Lock Position for all
1032        // selected items
1033
1034        setRemoveMenu(p, popup);
1035        //showAddItemPopUp(event, popup); // no need to Add when group selected
1036        popup.add(copy);
1037        popup.show(event.getComponent(), event.getX(), event.getY());
1038    }
1039
1040    protected void showAddItemPopUp(final JmriMouseEvent event, JPopupMenu popup) {
1041        if (!isEditable()) {
1042            return;
1043        }
1044        JMenu _add = new JMenu(Bundle.getMessage("MenuItemAddItem"));
1045        // for items in the following list, I18N is picked up later on
1046        addItemPopUp(new ComboBoxItem(RIGHT_TURNOUT), _add);
1047        addItemPopUp(new ComboBoxItem(LEFT_TURNOUT), _add);
1048        addItemPopUp(new ComboBoxItem(SLIP_TO_EDITOR), _add);
1049        addItemPopUp(new ComboBoxItem(SENSOR), _add);
1050        addItemPopUp(new ComboBoxItem(SIGNAL_HEAD), _add);
1051        addItemPopUp(new ComboBoxItem(SIGNAL_MAST), _add);
1052        addItemPopUp(new ComboBoxItem(MEMORY), _add);
1053        addItemPopUp(new ComboBoxItem(BLOCK_LABEL), _add);
1054        addItemPopUp(new ComboBoxItem(REPORTER), _add);
1055        addItemPopUp(new ComboBoxItem(LIGHT), _add);
1056        addItemPopUp(new ComboBoxItem(BACKGROUND), _add);
1057        addItemPopUp(new ComboBoxItem(MULTI_SENSOR), _add);
1058        addItemPopUp(new ComboBoxItem(RPSREPORTER), _add);
1059        addItemPopUp(new ComboBoxItem(FAST_CLOCK), _add);
1060        addItemPopUp(new ComboBoxItem(GLOBAL_VARIABLE), _add);
1061        addItemPopUp(new ComboBoxItem(AUDIO), _add);
1062        addItemPopUp(new ComboBoxItem(LOGIXNG), _add);
1063        addItemPopUp(new ComboBoxItem(ICON), _add);
1064        addItemPopUp(new ComboBoxItem("Text"), _add);
1065        popup.add(_add);
1066    }
1067
1068    protected void addItemPopUp(final ComboBoxItem item, JMenu menu) {
1069
1070        ActionListener a = new ActionListener() {
1071            //final String desiredName = name;
1072            @Override
1073            public void actionPerformed(ActionEvent e) {
1074                addItemViaMouseClick = true;
1075                getIconFrame(item.getName());
1076            }
1077
1078            ActionListener init(ComboBoxItem i) {
1079                return this;
1080            }
1081        }.init(item);
1082        JMenuItem addto = new JMenuItem(item.toString());
1083        addto.addActionListener(a);
1084        menu.add(addto);
1085    }
1086
1087    protected boolean addItemViaMouseClick = false;
1088
1089    @Override
1090    public void putItem(Positionable l) throws Positionable.DuplicateIdException {
1091        super.putItem(l);
1092        /*This allows us to catch any new items that are being pasted into the panel
1093         and add them to the selection group, so that the user can instantly move them around*/
1094        //!!!
1095        if (pasteItemFlag) {
1096            amendSelectionGroup(l);
1097            return;
1098        }
1099        if (addItemViaMouseClick) {
1100            addItemViaMouseClick = false;
1101            l.setLocation(_lastX, _lastY);
1102        }
1103    }
1104
1105    private void amendSelectionGroup(Positionable p) {
1106        if (p == null) {
1107            return;
1108        }
1109        if (_selectionGroup == null) {
1110            _selectionGroup = new ArrayList<>();
1111        }
1112        boolean removed = false;
1113        for (int i = 0; i < _selectionGroup.size(); i++) {
1114            if (_selectionGroup.get(i) == p) {
1115                _selectionGroup.remove(i);
1116                removed = true;
1117                break;
1118            }
1119        }
1120        if (!removed) {
1121            _selectionGroup.add(p);
1122        } else if (_selectionGroup.isEmpty()) {
1123            _selectionGroup = null;
1124        }
1125        _targetPanel.repaint();
1126    }
1127
1128    protected boolean pasteItemFlag = false;
1129
1130    protected void pasteItem(JmriMouseEvent e) {
1131        pasteItemFlag = true;
1132        XmlAdapter adapter;
1133        String className;
1134        int x;
1135        int y;
1136        int xOrig;
1137        int yOrig;
1138        if (_multiItemCopyGroup != null) {
1139            JComponent copied;
1140            int xoffset;
1141            int yoffset;
1142            x = _multiItemCopyGroup.get(0).getX();
1143            y = _multiItemCopyGroup.get(0).getY();
1144            xoffset = e.getX() - x;
1145            yoffset = e.getY() - y;
1146            /*We make a copy of the selected items and work off of that copy
1147             as amendments are made to the multiItemCopyGroup during this process
1148             which can result in a loop*/
1149            ArrayList<Positionable> _copyOfMultiItemCopyGroup = new ArrayList<>(_multiItemCopyGroup);
1150            Collections.copy(_copyOfMultiItemCopyGroup, _multiItemCopyGroup);
1151            for (Positionable comp : _copyOfMultiItemCopyGroup) {
1152                copied = (JComponent) comp;
1153                xOrig = copied.getX();
1154                yOrig = copied.getY();
1155                x = xOrig + xoffset;
1156                y = yOrig + yoffset;
1157                if (x < 0) {
1158                    x = 1;
1159                }
1160                if (y < 0) {
1161                    y = 1;
1162                }
1163                className = ConfigXmlManager.adapterName(copied);
1164                copied.setLocation(x, y);
1165                try {
1166                    adapter = (XmlAdapter) Class.forName(className).getDeclaredConstructor().newInstance();
1167                    Element el = adapter.store(copied);
1168                    adapter.load(el, this);
1169                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException
1170                    | jmri.configurexml.JmriConfigureXmlException
1171                    | RuntimeException ex) {
1172                        log.debug("Could not paste.", ex);
1173                }
1174                /*We remove the original item from the list, so we end up with
1175                 just the new items selected and allow the items to be moved around */
1176                amendSelectionGroup(comp);
1177                copied.setLocation(xOrig, yOrig);
1178            }
1179            _selectionGroup = null;
1180        }
1181        pasteItemFlag = false;
1182        _targetPanel.repaint();
1183    }
1184
1185    /**
1186     * Add an action to remove the Positionable item.
1187     */
1188    @Override
1189    public void setRemoveMenu(Positionable p, JPopupMenu popup) {
1190        popup.add(new AbstractAction(Bundle.getMessage("Remove")) {
1191            Positionable comp;
1192
1193            @Override
1194            public void actionPerformed(ActionEvent e) {
1195                if (_selectionGroup == null) {
1196                    comp.remove();
1197                } else {
1198                    removeMultiItems();
1199                }
1200            }
1201
1202            AbstractAction init(Positionable pos) {
1203                comp = pos;
1204                return this;
1205            }
1206        }.init(p));
1207    }
1208
1209    private void removeMultiItems() {
1210        boolean itemsInCopy = false;
1211        if (_selectionGroup == _multiItemCopyGroup) {
1212            itemsInCopy = true;
1213        }
1214        for (Positionable comp : _selectionGroup) {
1215            comp.remove();
1216        }
1217        //As we have removed all the items from the panel we can remove the group.
1218        _selectionGroup = null;
1219        //If the items in the selection group and copy group are the same we need to
1220        //clear the copy group as the originals no longer exist.
1221        if (itemsInCopy) {
1222            _multiItemCopyGroup = null;
1223        }
1224    }
1225
1226    // This adds a single CheckBox in the PopupMenu to set or clear all the selected
1227    // items "Lock Position" or Positionable setting, when clicked, all the items in
1228    // the selection will be changed accordingly.
1229    private void setMultiItemsPositionableMenu(JPopupMenu popup) {
1230        // This would do great with a "greyed" CheckBox if the multiple items have different states.
1231        // Then selecting the true or false state would force all to change to true or false
1232
1233        JCheckBoxMenuItem lockItem = new JCheckBoxMenuItem(Bundle.getMessage("LockPosition"));
1234        boolean allSetToMove = false;  // used to decide the state of the checkbox shown
1235        int trues = 0;                 // used to see if all items have the same setting
1236
1237        int size = _selectionGroup.size();
1238
1239        for (Positionable comp : _selectionGroup) {
1240            if (!comp.isPositionable()) {
1241                allSetToMove = true;
1242                trues++;
1243            }
1244
1245            lockItem.setSelected(allSetToMove);
1246
1247            lockItem.addActionListener(new ActionListener() {
1248                Positionable comp;
1249                JCheckBoxMenuItem checkBox;
1250
1251                @Override
1252                public void actionPerformed(ActionEvent e) {
1253                    comp.setPositionable(!checkBox.isSelected());
1254                    setSelectionsPositionable(!checkBox.isSelected(), comp);
1255                }
1256
1257                ActionListener init(Positionable pos, JCheckBoxMenuItem cb) {
1258                    comp = pos;
1259                    checkBox = cb;
1260                    return this;
1261                }
1262            }.init(comp, lockItem));
1263        }
1264
1265        // Add "~" to the Text when all items do not have the same setting,
1266        // until we get a "greyed" CheckBox ;) - GJM
1267        if ((trues != size) && (trues != 0)) {
1268            lockItem.setText("~ " + lockItem.getText());
1269            // uncheck box if all not the same
1270            lockItem.setSelected(false);
1271        }
1272        popup.add(lockItem);
1273    }
1274
1275    public void setBackgroundMenu(JPopupMenu popup) {
1276        // Panel background, not text background
1277        JMenuItem edit = new JMenuItem(Bundle.getMessage("FontBackgroundColor"));
1278        edit.addActionListener((ActionEvent event) -> {
1279            Color desiredColor = JmriColorChooser.showDialog(this,
1280                                 Bundle.getMessage("FontBackgroundColor"),
1281                                 getBackgroundColor());
1282            if (desiredColor!=null ) {
1283               setBackgroundColor(desiredColor);
1284           }
1285        });
1286        popup.add(edit);
1287    }
1288
1289    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(PanelEditor.class);
1290
1291}