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