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