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