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 LOGIXNG_TABLE = "LogixNGTable"; 110 private static final String ICON = "Icon"; 111 private static final String AUDIO = "Audio"; 112 private static final String LOGIXNG = "LogixNG"; 113 private final JTextField nextX = new JTextField("0", 4); 114 private final JTextField nextY = new JTextField("0", 4); 115 116 private final JCheckBox editableBox = new JCheckBox(Bundle.getMessage("CheckBoxEditable")); 117 private final JCheckBox positionableBox = new JCheckBox(Bundle.getMessage("CheckBoxPositionable")); 118 private final JCheckBox controllingBox = new JCheckBox(Bundle.getMessage("CheckBoxControlling")); 119 //private JCheckBox showCoordinatesBox = new JCheckBox(Bundle.getMessage("CheckBoxShowCoordinates")); 120 private final JCheckBox showTooltipBox = new JCheckBox(Bundle.getMessage("CheckBoxShowTooltips")); 121 private final JCheckBox hiddenBox = new JCheckBox(Bundle.getMessage("CheckBoxHidden")); 122 private final JCheckBox menuBox = new JCheckBox(Bundle.getMessage("CheckBoxMenuBar")); 123 private final JLabel scrollableLabel = new JLabel(Bundle.getMessage("ComboBoxScrollable")); 124 private final JComboBox<String> scrollableComboBox = new JComboBox<>(); 125 126 private final JButton labelAdd = new JButton(Bundle.getMessage("ButtonAddText")); 127 private final JTextField nextLabel = new JTextField(10); 128 129 private JComboBox<ComboBoxItem> _addIconBox; 130 131 public PanelEditor() { 132 } 133 134 public PanelEditor(String name) { 135 super(name, false, true); 136 init(name); 137 } 138 139 @Override 140 protected void init(String name) { 141 java.awt.Container contentPane = this.getContentPane(); 142 contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS)); 143 // common items 144 JPanel common = new JPanel(); 145 common.setLayout(new FlowLayout()); 146 common.add(new JLabel(" x:")); 147 common.add(nextX); 148 common.add(new JLabel(" y:")); 149 common.add(nextY); 150 contentPane.add(common); 151 setAllEditable(true); 152 setShowHidden(true); 153 super.setTargetPanel(null, makeFrame(name)); 154 super.setTargetPanelSize(400, 300); 155 super.setDefaultToolTip(new ToolTip(null, 0, 0, new Font("SansSerif", Font.PLAIN, 12), 156 Color.black, new Color(215, 225, 255), Color.black, null)); 157 // set scrollbar initial state 158 setScroll(SCROLL_BOTH); 159 160 // add menu - not using PanelMenu, because it now 161 // has other stuff in it? 162 JMenuBar menuBar = new JMenuBar(); 163 JMenu fileMenu = new JMenu(Bundle.getMessage("MenuFile")); 164 menuBar.add(fileMenu); 165 fileMenu.add(new jmri.jmrit.display.NewPanelAction(Bundle.getMessage("MenuItemNew"))); 166 fileMenu.add(new jmri.configurexml.StoreXmlUserAction(Bundle.getMessage("FileMenuItemStore"))); 167 JMenuItem storeIndexItem = new JMenuItem(Bundle.getMessage("MIStoreImageIndex")); 168 fileMenu.add(storeIndexItem); 169 storeIndexItem.addActionListener(event -> InstanceManager.getDefault(CatalogTreeManager.class).storeImageIndex()); 170 JMenuItem editItem = new JMenuItem(Bundle.getMessage("editIndexMenu")); 171 editItem.addActionListener(e -> { 172 ImageIndexEditor ii = InstanceManager.getDefault(ImageIndexEditor.class); 173 ii.pack(); 174 ii.setVisible(true); 175 }); 176 fileMenu.add(editItem); 177 178 editItem = new JMenuItem(Bundle.getMessage("CPEView")); 179 fileMenu.add(editItem); 180 editItem.addActionListener(event -> changeView("jmri.jmrit.display.controlPanelEditor.ControlPanelEditor")); 181 182 fileMenu.addSeparator(); 183 JMenuItem deleteItem = new JMenuItem(Bundle.getMessage("DeletePanel")); 184 fileMenu.add(deleteItem); 185 deleteItem.addActionListener(event -> { 186 if (deletePanel()) { 187 getTargetFrame().dispose(); 188 dispose(); 189 } 190 }); 191 192 setJMenuBar(menuBar); 193 addHelpMenu("package.jmri.jmrit.display.PanelEditor", true); 194 195 // allow renaming the panel 196 { 197 JPanel namep = new JPanel(); 198 namep.setLayout(new FlowLayout()); 199 JButton b = new JButton(Bundle.getMessage("renamePanelMenu", "...")); 200 b.addActionListener(new ActionListener() { 201 PanelEditor editor; 202 203 @Override 204 public void actionPerformed(ActionEvent e) { 205 JFrame frame = getTargetFrame(); 206 String oldName = frame.getTitle(); 207 // prompt for name 208 String newName = JmriJOptionPane.showInputDialog(null, Bundle.getMessage("PromptNewName"), oldName); 209 if ((newName == null) || (oldName.equals(newName))) { 210 return; // cancelled 211 } 212 if (InstanceManager.getDefault(EditorManager.class).contains(newName)) { 213 JmriJOptionPane.showMessageDialog(null, Bundle.getMessage("CanNotRename"), Bundle.getMessage("PanelExist"), 214 JmriJOptionPane.ERROR_MESSAGE); 215 return; 216 } 217 frame.setTitle(newName); 218 editor.setTitle(); 219 } 220 221 ActionListener init(PanelEditor e) { 222 editor = e; 223 return this; 224 } 225 }.init(this)); 226 namep.add(b); 227 this.getContentPane().add(namep); 228 } 229 // add a text label 230 { 231 JPanel panel = new JPanel(); 232 panel.setLayout(new FlowLayout()); 233 panel.add(labelAdd); 234 labelAdd.setEnabled(false); 235 labelAdd.setToolTipText(Bundle.getMessage("ToolTipWillActivate")); 236 panel.add(nextLabel); 237 labelAdd.addActionListener(new ActionListener() { 238 PanelEditor editor; 239 240 @Override 241 public void actionPerformed(ActionEvent a) { 242 editor.addLabel(nextLabel.getText()); 243 } 244 245 ActionListener init(PanelEditor e) { 246 editor = e; 247 return this; 248 } 249 }.init(this)); 250 nextLabel.addKeyListener(new KeyAdapter() { 251 @Override 252 public void keyReleased(KeyEvent a) { 253 if (nextLabel.getText().equals("")) { 254 labelAdd.setEnabled(false); 255 labelAdd.setToolTipText(Bundle.getMessage("ToolTipWillActivate")); 256 } else { 257 labelAdd.setEnabled(true); 258 labelAdd.setToolTipText(null); 259 } 260 } 261 }); 262 this.getContentPane().add(panel); 263 } 264 265 // Selection of the type of entity for the icon to represent is done from a combobox 266 _addIconBox = new JComboBox<>(); 267 _addIconBox.setMinimumSize(new Dimension(75, 75)); 268 _addIconBox.setMaximumSize(new Dimension(200, 200)); 269 _addIconBox.addItem(new ComboBoxItem(RIGHT_TURNOUT)); 270 _addIconBox.addItem(new ComboBoxItem(LEFT_TURNOUT)); 271 _addIconBox.addItem(new ComboBoxItem(SLIP_TO_EDITOR)); 272 _addIconBox.addItem(new ComboBoxItem(SENSOR)); // NOI18N 273 _addIconBox.addItem(new ComboBoxItem(SIGNAL_HEAD)); 274 _addIconBox.addItem(new ComboBoxItem(SIGNAL_MAST)); 275 _addIconBox.addItem(new ComboBoxItem(MEMORY)); 276 _addIconBox.addItem(new ComboBoxItem(BLOCK_LABEL)); 277 _addIconBox.addItem(new ComboBoxItem(REPORTER)); 278 _addIconBox.addItem(new ComboBoxItem(LIGHT)); 279 _addIconBox.addItem(new ComboBoxItem(BACKGROUND)); 280 _addIconBox.addItem(new ComboBoxItem(MULTI_SENSOR)); 281 _addIconBox.addItem(new ComboBoxItem(RPSREPORTER)); 282 _addIconBox.addItem(new ComboBoxItem(FAST_CLOCK)); 283 _addIconBox.addItem(new ComboBoxItem(GLOBAL_VARIABLE)); 284 _addIconBox.addItem(new ComboBoxItem(LOGIXNG_TABLE)); 285 _addIconBox.addItem(new ComboBoxItem(AUDIO)); 286 _addIconBox.addItem(new ComboBoxItem(LOGIXNG)); 287 _addIconBox.addItem(new ComboBoxItem(ICON)); 288 _addIconBox.setSelectedIndex(-1); 289 _addIconBox.addItemListener(this); // must be AFTER no selection is set 290 JPanel p1 = new JPanel(); 291 p1.setLayout(new BoxLayout(p1, BoxLayout.Y_AXIS)); 292 JPanel p2 = new JPanel(); 293 p2.setLayout(new FlowLayout()); 294 p2.add(new JLabel(Bundle.getMessage("selectTypeIcon"))); 295 p1.add(p2); 296 p1.add(_addIconBox); 297 contentPane.add(p1); 298 299 // edit, position, control controls 300 { 301 // edit mode item 302 contentPane.add(editableBox); 303 editableBox.addActionListener(event -> { 304 setAllEditable(editableBox.isSelected()); 305 hiddenCheckBoxListener(); 306 }); 307 editableBox.setSelected(isEditable()); 308 // positionable item 309 contentPane.add(positionableBox); 310 positionableBox.addActionListener(event -> setAllPositionable(positionableBox.isSelected())); 311 positionableBox.setSelected(allPositionable()); 312 // controlable item 313 contentPane.add(controllingBox); 314 controllingBox.addActionListener(event -> setAllControlling(controllingBox.isSelected())); 315 controllingBox.setSelected(allControlling()); 316 // hidden item 317 contentPane.add(hiddenBox); 318 hiddenCheckBoxListener(); 319 hiddenBox.setSelected(showHidden()); 320 321 /* 322 contentPane.add(showCoordinatesBox); 323 showCoordinatesBox.addActionListener(new ActionListener() { 324 public void actionPerformed(ActionEvent e) { 325 setShowCoordinates(showCoordinatesBox.isSelected()); 326 } 327 }); 328 showCoordinatesBox.setSelected(showCoordinates()); 329 */ 330 contentPane.add(showTooltipBox); 331 showTooltipBox.addActionListener(e -> setAllShowToolTip(showTooltipBox.isSelected())); 332 showTooltipBox.setSelected(showToolTip()); 333 334 contentPane.add(menuBox); 335 menuBox.addActionListener(e -> setPanelMenuVisible(menuBox.isSelected())); 336 menuBox.setSelected(true); 337 338 // Show/Hide Scroll Bars 339 JPanel scrollPanel = new JPanel(); 340 scrollPanel.setLayout(new FlowLayout()); 341 scrollableLabel.setLabelFor(scrollableComboBox); 342 scrollPanel.add(scrollableLabel); 343 scrollPanel.add(scrollableComboBox); 344 contentPane.add(scrollPanel); 345 scrollableComboBox.addItem(Bundle.getMessage("ScrollNone")); 346 scrollableComboBox.addItem(Bundle.getMessage("ScrollBoth")); 347 scrollableComboBox.addItem(Bundle.getMessage("ScrollHorizontal")); 348 scrollableComboBox.addItem(Bundle.getMessage("ScrollVertical")); 349 scrollableComboBox.setSelectedIndex(SCROLL_BOTH); 350 scrollableComboBox.addActionListener(e -> setScroll(scrollableComboBox.getSelectedIndex())); 351 } 352 353 // register the resulting panel for later configuration 354 ConfigureManager cm = InstanceManager.getNullableDefault(jmri.ConfigureManager.class); 355 if (cm != null) { 356 cm.registerUser(this); 357 } 358 359 // when this window closes, set contents of target uneditable 360 addWindowListener(new java.awt.event.WindowAdapter() { 361 362 HashMap<String, JFrameItem> iconAdderFrames; 363 364 @Override 365 public void windowClosing(java.awt.event.WindowEvent e) { 366 for (JFrameItem frame : iconAdderFrames.values()) { 367 frame.dispose(); 368 } 369 } 370 371 WindowAdapter init(HashMap<String, JFrameItem> f) { 372 iconAdderFrames = f; 373 return this; 374 } 375 }.init(_iconEditorFrame)); 376 377 // and don't destroy the window 378 setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE); 379 // move this editor panel off the panel's position 380 getTargetFrame().setLocationRelativeTo(this); 381 getTargetFrame().pack(); 382 getTargetFrame().setVisible(true); 383 log.debug("PanelEditor ctor done."); 384 } // end ctor 385 386 /** 387 * Initializes the hiddencheckbox and its listener. This has been taken out 388 * of the init, as checkbox is enable/disabled by the editableBox. 389 */ 390 private void hiddenCheckBoxListener() { 391 setShowHidden(hiddenBox.isSelected()); 392 if (editableBox.isSelected()) { 393 hiddenBox.setEnabled(false); 394// hiddenBox.setSelected(true); 395 } else { 396 hiddenBox.setEnabled(true); 397 hiddenBox.addActionListener(event -> setShowHidden(hiddenBox.isSelected())); 398 } 399 400 } 401 402 /** 403 * After construction, initialize all the widgets to their saved config 404 * settings. 405 */ 406 @Override 407 public void initView() { 408 editableBox.setSelected(isEditable()); 409 positionableBox.setSelected(allPositionable()); 410 controllingBox.setSelected(allControlling()); 411 //showCoordinatesBox.setSelected(showCoordinates()); 412 showTooltipBox.setSelected(showToolTip()); 413 hiddenBox.setSelected(showHidden()); 414 menuBox.setSelected(getTargetFrame().getJMenuBar().isVisible()); 415 } 416 417 static class ComboBoxItem { 418 419 private final String name; 420 421 protected ComboBoxItem(String n) { 422 name = n; 423 } 424 425 protected String getName() { 426 return name; 427 } 428 429 @Override 430 public String toString() { 431 // I18N split Bundle name 432 // use NamedBeanBundle property for basic beans like "Turnout" I18N 433 String bundleName; 434 if (SENSOR.equals(name)) { 435 bundleName = "BeanNameSensor"; 436 } else if (SIGNAL_HEAD.equals(name)) { 437 bundleName = "BeanNameSignalHead"; 438 } else if (SIGNAL_MAST.equals(name)) { 439 bundleName = "BeanNameSignalMast"; 440 } else if (MEMORY.equals(name)) { 441 bundleName = "BeanNameMemory"; 442 } else if (REPORTER.equals(name)) { 443 bundleName = "BeanNameReporter"; 444 } else if (LIGHT.equals(name)) { 445 bundleName = "BeanNameLight"; 446 } else if (GLOBAL_VARIABLE.equals(name)) { 447 bundleName = "BeanNameGlobalVariable"; 448 } else if (LOGIXNG_TABLE.equals(name)) { 449 bundleName = "BeanNameLogixNGTable"; 450 } else if (AUDIO.equals(name)) { 451 bundleName = "BeanNameAudio"; 452 } else { 453 bundleName = name; 454 } 455 return Bundle.getMessage(bundleName); // use NamedBeanBundle property for basic beans like "Turnout" I18N 456 } 457 } 458 459 /* 460 * itemListener for JComboBox. 461 */ 462 @Override 463 public void itemStateChanged(ItemEvent e) { 464 if (e.getStateChange() == ItemEvent.SELECTED) { 465 ComboBoxItem item = (ComboBoxItem) e.getItem(); 466 String name = item.getName(); 467 JFrameItem frame = super.getIconFrame(name); 468 if (frame != null) { 469 frame.getEditor().reset(); 470 frame.setVisible(true); 471 } else { 472 if (name.equals(FAST_CLOCK)) { 473 addClock(); 474 } else if (name.equals(RPSREPORTER)) { 475 addRpsReporter(); 476 } else { 477 log.error("Unable to open Icon Editor \"{}\"", item.getName()); 478 } 479 } 480 _addIconBox.setSelectedIndex(-1); 481 } 482 } 483 484 /** 485 * Handle close of editor window. 486 * <p> 487 * Overload/override method in JmriJFrame parent, which by default is 488 * permanently closing the window. Here, we just want to make it invisible, 489 * so we don't dispose it (yet). 490 */ 491 @Override 492 @SuppressFBWarnings(value = "OVERRIDING_METHODS_MUST_INVOKE_SUPER", 493 justification = "Don't want to close window yet") 494 public void windowClosing(java.awt.event.WindowEvent e) { 495 setVisible(false); 496 } 497 498 /** 499 * Create sequence of panels, etc, for layout: JFrame contains its 500 * ContentPane which contains a JPanel with BoxLayout (p1) which contains a 501 * JScollPane (js) which contains the targetPane. 502 * @param name the frame name. 503 * @return the frame. 504 */ 505 public JmriJFrame makeFrame(String name) { 506 JmriJFrame targetFrame = new JmriJFrameWithPermissions(name); 507 targetFrame.setVisible(false); 508 509 JMenuBar menuBar = new JMenuBar(); 510 JMenu editMenu = new JMenu(Bundle.getMessage("MenuEdit")); 511 menuBar.add(editMenu); 512 editMenu.add(new AbstractAction(Bundle.getMessage("OpenEditor")) { 513 @Override 514 public void actionPerformed(ActionEvent e) { 515 setVisible(true); 516 } 517 }); 518 editMenu.addSeparator(); 519 editMenu.add(new AbstractAction(Bundle.getMessage("DeletePanel")) { 520 @Override 521 public void actionPerformed(ActionEvent e) { 522 if (deletePanel()) { 523 dispose(); 524 } 525 } 526 }); 527 targetFrame.setJMenuBar(menuBar); 528 // add maker menu 529 JMenu markerMenu = new JMenu(Bundle.getMessage("MenuMarker")); 530 menuBar.add(markerMenu); 531 markerMenu.add(new AbstractAction(Bundle.getMessage("AddLoco")) { 532 @Override 533 public void actionPerformed(ActionEvent e) { 534 locoMarkerFromInput(); 535 } 536 }); 537 markerMenu.add(new AbstractAction(Bundle.getMessage("AddLocoRoster")) { 538 @Override 539 public void actionPerformed(ActionEvent e) { 540 locoMarkerFromRoster(); 541 } 542 }); 543 markerMenu.add(new AbstractAction(Bundle.getMessage("RemoveMarkers")) { 544 @Override 545 public void actionPerformed(ActionEvent e) { 546 removeMarkers(); 547 } 548 }); 549 550 JMenu warrantMenu = jmri.jmrit.logix.WarrantTableAction.getDefault().makeWarrantMenu(isEditable()); 551 if (warrantMenu != null) { 552 menuBar.add(warrantMenu); 553 } 554 555 targetFrame.addHelpMenu("package.jmri.jmrit.display.PanelTarget", true); 556 return targetFrame; 557 } 558 559 /* 560 ************* implementation of Abstract Editor methods ********** 561 */ 562 563 /** 564 * The target window has been requested to close, don't delete it at this 565 * time. Deletion must be accomplished via the Delete this panel menu item. 566 */ 567 @Override 568 protected void targetWindowClosingEvent(java.awt.event.WindowEvent e) { 569 targetWindowClosing(); 570 } 571 572 /** 573 * Called from TargetPanel's paint method for additional drawing by editor 574 * view 575 */ 576 @Override 577 protected void paintTargetPanel(Graphics g) { 578 /*Graphics2D g2 = (Graphics2D)g; 579 drawPositionableLabelBorder(g2);*/ 580 } 581 582 /** 583 * Set an object's location when it is created. 584 */ 585 @Override 586 protected void setNextLocation(Positionable obj) { 587 int x = Integer.parseInt(nextX.getText()); 588 int y = Integer.parseInt(nextY.getText()); 589 obj.setLocation(x, y); 590 } 591 592 /** 593 * Create popup for a Positionable object. Popup items common to all 594 * positionable objects are done before and after the items that pertain 595 * only to specific Positionable types. 596 * 597 * @param p the item containing or requiring the context menu 598 * @param event the event triggering the menu 599 * @param selections the list of all Positionables at this position 600 */ 601 protected void showPopUp(Positionable p, JmriMouseEvent event, List<Positionable> selections) { 602 if (!((JComponent) p).isVisible()) { 603 return; // component must be showing on the screen to determine its location 604 } 605 JPopupMenu popup = new JPopupMenu(); 606 PositionablePopupUtil util = p.getPopupUtility(); 607 if (p.isEditable()) { 608 // items for all Positionables 609 if (p.doViemMenu()) { 610 popup.add(p.getNameString()); 611 setPositionableMenu(p, popup); 612 if (p.isPositionable()) { 613 setShowCoordinatesMenu(p, popup); 614 setShowAlignmentMenu(p, popup); 615 } 616 setDisplayLevelMenu(p, popup); 617 setHiddenMenu(p, popup); 618 setEmptyHiddenMenu(p, popup); 619 setValueEditDisabledMenu(p, popup); 620 setEditIdMenu(p, popup); 621 setEditClassesMenu(p, popup); 622 popup.addSeparator(); 623 setLogixNGPositionableMenu(p, popup); 624 popup.addSeparator(); 625 } 626 627 // Positionable items with defaults or using overrides 628 boolean popupSet = false; 629 popupSet = p.setRotateOrthogonalMenu(popup); 630 popupSet |= p.setRotateMenu(popup); 631 popupSet |= p.setScaleMenu(popup); 632 if (popupSet) { 633 popup.addSeparator(); 634 } 635 popupSet = p.setEditIconMenu(popup); 636 if (popupSet) { 637 popup.addSeparator(); 638 } 639 popupSet = p.setTextEditMenu(popup); 640 if (util != null) { 641 util.setFixedTextMenu(popup); 642 util.setTextMarginMenu(popup); 643 util.setTextBorderMenu(popup); 644 util.setTextFontMenu(popup); 645 util.setBackgroundMenu(popup); 646 util.setTextJustificationMenu(popup); 647 util.setTextOrientationMenu(popup); 648 util.copyItem(popup); 649 popup.addSeparator(); 650 util.propertyUtil(popup); 651 util.setAdditionalEditPopUpMenu(popup); 652 popupSet = true; 653 } 654 if (popupSet) { 655 popup.addSeparator(); 656 } 657 p.setDisableControlMenu(popup); 658 659 // for Positionables with unique item settings 660 p.showPopUp(popup); 661 662 setShowToolTipMenu(p, popup); 663 setRemoveMenu(p, popup); 664 } else { 665 p.showPopUp(popup); 666 if (util != null) { 667 util.setAdditionalViewPopUpMenu(popup); 668 } 669 } 670 671 if (selections.size() > 1) { 672 boolean found = false; 673 JMenu iconsBelowMenu = new JMenu(Bundle.getMessage("MenuItemIconsBelow")); 674 for (int i=0; i < selections.size(); i++) { 675 Positionable pos = selections.get(i); 676 if (found) { 677 iconsBelowMenu.add(new AbstractAction(Bundle.getMessage( 678 "PositionableTypeAndName", pos.getTypeString(), pos.getNameString())) { 679 @Override 680 public void actionPerformed(ActionEvent e) { 681 showPopUp(pos, event, new ArrayList<>()); 682 } 683 }); 684 } else { 685 if (p == pos) found = true; 686 } 687 } 688 popup.addSeparator(); 689 popup.add(iconsBelowMenu); 690 } 691 692 popup.show((Component) p, p.getWidth() / 2, p.getHeight() / 2); 693 } 694 695 /** 696 * *************************************************** 697 */ 698 private boolean delayedPopupTrigger; 699 700 @Override 701 public void mousePressed(JmriMouseEvent event) { 702 setToolTip(null); // ends tooltip if displayed 703 if (log.isDebugEnabled()) { 704 log.debug("mousePressed at ({},{}) _dragging= {}", event.getX(), event.getY(), _dragging); 705 } 706 _anchorX = event.getX(); 707 _anchorY = event.getY(); 708 _lastX = _anchorX; 709 _lastY = _anchorY; 710 List<Positionable> selections = getSelectedItems(event); 711 if (_dragging) { 712 return; 713 } 714 if (selections.size() > 0) { 715 if (event.isShiftDown() && selections.size() > 1) { 716 _currentSelection = selections.get(1); 717 } else { 718 _currentSelection = selections.get(0); 719 } 720 if (event.isPopupTrigger()) { 721 log.debug("mousePressed calls showPopUp"); 722 if (event.isMetaDown() || event.isAltDown()) { 723 // if requesting a popup and it might conflict with moving, delay the request to mouseReleased 724 delayedPopupTrigger = true; 725 } else { 726 // no possible conflict with moving, display the popup now 727 if (_selectionGroup != null) { 728 //Will show the copy option only 729 showMultiSelectPopUp(event, _currentSelection); 730 } else { 731 showPopUp(_currentSelection, event, selections); 732 } 733 } 734 } else if (!event.isControlDown()) { 735 _currentSelection.doMousePressed(event); 736 if (_multiItemCopyGroup != null && !_multiItemCopyGroup.contains(_currentSelection)) { 737 _multiItemCopyGroup = null; 738 } 739 // _selectionGroup = null; 740 } 741 } else { 742 if (event.isPopupTrigger()) { 743 if (event.isMetaDown() || event.isAltDown()) { 744 // if requesting a popup and it might conflict with moving, delay the request to mouseReleased 745 delayedPopupTrigger = true; 746 } else { 747 if (_multiItemCopyGroup != null) { 748 pasteItemPopUp(event); 749 } else if (_selectionGroup != null) { 750 showMultiSelectPopUp(event, _currentSelection); 751 } else { 752 backgroundPopUp(event); 753 _currentSelection = null; 754 } 755 } 756 } else { 757 _currentSelection = null; 758 } 759 } 760 // if ((event.isControlDown() || _selectionGroup!=null) && _currentSelection!=null){ 761 if ((event.isControlDown()) || event.isMetaDown() || event.isAltDown()) { 762 //Don't want to do anything, just want to catch it, so that the next two else ifs are not 763 //executed 764 } else if ((_currentSelection == null && _multiItemCopyGroup == null) 765 || (_selectRect != null && !_selectRect.contains(_anchorX, _anchorY))) { 766 _selectRect = new Rectangle(_anchorX, _anchorY, 0, 0); 767 _selectionGroup = null; 768 } else { 769 _selectRect = null; 770 _selectionGroup = null; 771 } 772 _targetPanel.repaint(); // needed for ToolTip 773 } 774 775 @Override 776 public void mouseReleased(JmriMouseEvent event) { 777 setToolTip(null); // ends tooltip if displayed 778 if (log.isDebugEnabled()) { 779 // in if statement to avoid inline conditional unless logging 780 log.debug("mouseReleased at ({},{}) dragging= {} selectRect is {}", event.getX(), event.getY(), _dragging, 781 _selectRect == null ? "null" : "not null"); 782 } 783 List<Positionable> selections = getSelectedItems(event); 784 785 if (_dragging) { 786 mouseDragged(event); 787 } 788 if (selections.size() > 0) { 789 if (event.isShiftDown() && selections.size() > 1) { 790 _currentSelection = selections.get(1); 791 } else { 792 _currentSelection = selections.get(0); 793 } 794 if (_multiItemCopyGroup != null && !_multiItemCopyGroup.contains(_currentSelection)) { 795 _multiItemCopyGroup = null; 796 } 797 } else { 798 if ((event.isPopupTrigger() || delayedPopupTrigger) && !_dragging) { 799 if (_multiItemCopyGroup != null) { 800 pasteItemPopUp(event); 801 } else { 802 backgroundPopUp(event); 803 _currentSelection = null; 804 } 805 } else { 806 _currentSelection = null; 807 808 } 809 } 810 /*if (event.isControlDown() && _currentSelection!=null && !event.isPopupTrigger()){ 811 amendSelectionGroup(_currentSelection, event);*/ 812 if ((event.isPopupTrigger() || delayedPopupTrigger) && _currentSelection != null && !_dragging) { 813 if (_selectionGroup != null) { 814 //Will show the copy option only 815 showMultiSelectPopUp(event, _currentSelection); 816 817 } else { 818 showPopUp(_currentSelection, event, selections); 819 } 820 } else { 821 if (_currentSelection != null && !_dragging && !event.isControlDown()) { 822 _currentSelection.doMouseReleased(event); 823 } 824 if (allPositionable() && _selectRect != null) { 825 if (_selectionGroup == null && _dragging) { 826 makeSelectionGroup(event); 827 } 828 } 829 } 830 delayedPopupTrigger = false; 831 _dragging = false; 832 _selectRect = null; 833 834 // if not sending MouseClicked, do it here 835 if (InstanceManager.getDefault(GuiLafPreferencesManager.class).isNonStandardMouseEvent()) { 836 mouseClicked(event); 837 } 838 _targetPanel.repaint(); // needed for ToolTip 839 } 840 841 @Override 842 public void mouseDragged(JmriMouseEvent event) { 843 setToolTip(null); // ends tooltip if displayed 844 if ((event.isPopupTrigger()) || (!event.isMetaDown() && !event.isAltDown())) { 845 if (_currentSelection != null) { 846 List<Positionable> selections = getSelectedItems(event); 847 if (selections.size() > 0) { 848 if (selections.get(0) != _currentSelection) { 849 _currentSelection.doMouseReleased(event); 850 } else { 851 _currentSelection.doMouseDragged(event); 852 } 853 } else { 854 _currentSelection.doMouseReleased(event); 855 } 856 } 857 return; 858 } 859 moveIt: 860 if (_currentSelection != null && getFlag(OPTION_POSITION, _currentSelection.isPositionable())) { 861 int deltaX = event.getX() - _lastX; 862 int deltaY = event.getY() - _lastY; 863 int minX = getItemX(_currentSelection, deltaX); 864 int minY = getItemY(_currentSelection, deltaY); 865 if (_selectionGroup != null && _selectionGroup.contains(_currentSelection)) { 866 for (Positionable comp : _selectionGroup) { 867 minX = Math.min(getItemX(comp, deltaX), minX); 868 minY = Math.min(getItemY(comp, deltaY), minY); 869 } 870 } 871 if (minX < 0 || minY < 0) { 872 break moveIt; 873 } 874 if (_selectionGroup != null && _selectionGroup.contains(_currentSelection)) { 875 for (Positionable comp : _selectionGroup) { 876 moveItem(comp, deltaX, deltaY); 877 } 878 _highlightcomponent = null; 879 } else { 880 moveItem(_currentSelection, deltaX, deltaY); 881 _highlightcomponent = new Rectangle(_currentSelection.getX(), _currentSelection.getY(), 882 _currentSelection.maxWidth(), _currentSelection.maxHeight()); 883 } 884 } else { 885 if (allPositionable() && _selectionGroup == null) { 886 drawSelectRect(event.getX(), event.getY()); 887 } 888 } 889 _dragging = true; 890 _lastX = event.getX(); 891 _lastY = event.getY(); 892 _targetPanel.repaint(); // needed for ToolTip 893 } 894 895 @Override 896 public void mouseMoved(JmriMouseEvent event) { 897 // log.debug("mouseMoved at ({},{})", event.getX(), event.getY()); 898 if (_dragging || event.isPopupTrigger()) { 899 return; 900 } 901 902 List<Positionable> selections = getSelectedItems(event); 903 Positionable selection = null; 904 if (selections.size() > 0) { 905 if (event.isShiftDown() && selections.size() > 1) { 906 selection = selections.get(1); 907 } else { 908 selection = selections.get(0); 909 } 910 } 911 if (isEditable() && selection != null && selection.getDisplayLevel() > BKG) { 912 _highlightcomponent = new Rectangle(selection.getX(), selection.getY(), selection.maxWidth(), selection.maxHeight()); 913 _targetPanel.repaint(); 914 } else { 915 _highlightcomponent = null; 916 _targetPanel.repaint(); 917 } 918 if (selection != null && selection.getDisplayLevel() > BKG && selection.showToolTip()) { 919 showToolTip(selection, event); 920 //selection.highlightlabel(true); 921 _targetPanel.repaint(); 922 } else { 923 setToolTip(null); 924 _highlightcomponent = null; 925 _targetPanel.repaint(); 926 } 927 } 928 929 @Override 930 public void mouseClicked(JmriMouseEvent event) { 931 setToolTip(null); // ends tooltip if displayed 932 if (log.isDebugEnabled()) { 933 log.debug("mouseClicked at ({},{}) dragging= {} selectRect is {}", 934 event.getX(), event.getY(), _dragging, (_selectRect == null ? "null" : "not null")); 935 } 936 List<Positionable> selections = getSelectedItems(event); 937 938 if (selections.size() > 0) { 939 if (event.isShiftDown() && selections.size() > 1) { 940 _currentSelection = selections.get(1); 941 } else { 942 _currentSelection = selections.get(0); 943 } 944 } else { 945 _currentSelection = null; 946 if (event.isPopupTrigger()) { 947 if (_multiItemCopyGroup == null) { 948 pasteItemPopUp(event); 949 } else { 950 backgroundPopUp(event); 951 } 952 } 953 } 954 if (event.isPopupTrigger() && _currentSelection != null && !_dragging) { 955 if (_selectionGroup != null) { 956 showMultiSelectPopUp(event, _currentSelection); 957 } else { 958 showPopUp(_currentSelection, event, selections); 959 } 960 // _selectionGroup = null; // Show popup only works for a single item 961 962 } else { 963 if (_currentSelection != null && !_dragging && !event.isControlDown()) { 964 _currentSelection.doMouseClicked(event); 965 } 966 } 967 _targetPanel.repaint(); // needed for ToolTip 968 if (event.isControlDown() && _currentSelection != null && !event.isPopupTrigger()) { 969 amendSelectionGroup(_currentSelection); 970 } 971 } 972 973 @Override 974 public void mouseEntered(JmriMouseEvent event) { 975 } 976 977 @Override 978 public void mouseExited(JmriMouseEvent event) { 979 setToolTip(null); 980 _targetPanel.repaint(); // needed for ToolTip 981 } 982 983 protected ArrayList<Positionable> _multiItemCopyGroup = null; // items gathered inside fence 984 985 @Override 986 protected void copyItem(Positionable p) { 987 _multiItemCopyGroup = new ArrayList<>(); 988 _multiItemCopyGroup.add(p); 989 } 990 991 protected void pasteItemPopUp(final JmriMouseEvent event) { 992 if (!isEditable()) { 993 return; 994 } 995 if (_multiItemCopyGroup == null) { 996 return; 997 } 998 JPopupMenu popup = new JPopupMenu(); 999 JMenuItem edit = new JMenuItem(Bundle.getMessage("MenuItemPaste")); 1000 edit.addActionListener(e -> pasteItem(event)); 1001 setBackgroundMenu(popup); 1002 showAddItemPopUp(event, popup); 1003 popup.add(edit); 1004 popup.show(event.getComponent(), event.getX(), event.getY()); 1005 } 1006 1007 protected void backgroundPopUp(JmriMouseEvent event) { 1008 if (!isEditable()) { 1009 return; 1010 } 1011 JPopupMenu popup = new JPopupMenu(); 1012 setBackgroundMenu(popup); 1013 showAddItemPopUp(event, popup); 1014 popup.show(event.getComponent(), event.getX(), event.getY()); 1015 } 1016 1017 protected void showMultiSelectPopUp(final JmriMouseEvent event, Positionable p) { 1018 JPopupMenu popup = new JPopupMenu(); 1019 JMenuItem copy = new JMenuItem(Bundle.getMessage("MenuItemCopy")); // changed "edit" to "copy" 1020 if (p.isPositionable()) { 1021 setShowAlignmentMenu(p, popup); 1022 } 1023 copy.addActionListener(e -> { 1024 _multiItemCopyGroup = new ArrayList<>(); 1025 // must make a copy or pasteItem() will hang 1026 if (_selectionGroup != null) { 1027 _multiItemCopyGroup.addAll(_selectionGroup); 1028 } 1029 }); 1030 1031 setMultiItemsPositionableMenu(popup); // adding Lock Position for all 1032 // selected items 1033 1034 setRemoveMenu(p, popup); 1035 //showAddItemPopUp(event, popup); // no need to Add when group selected 1036 popup.add(copy); 1037 popup.show(event.getComponent(), event.getX(), event.getY()); 1038 } 1039 1040 protected void showAddItemPopUp(final JmriMouseEvent event, JPopupMenu popup) { 1041 if (!isEditable()) { 1042 return; 1043 } 1044 JMenu _add = new JMenu(Bundle.getMessage("MenuItemAddItem")); 1045 // for items in the following list, I18N is picked up later on 1046 addItemPopUp(new ComboBoxItem(RIGHT_TURNOUT), _add); 1047 addItemPopUp(new ComboBoxItem(LEFT_TURNOUT), _add); 1048 addItemPopUp(new ComboBoxItem(SLIP_TO_EDITOR), _add); 1049 addItemPopUp(new ComboBoxItem(SENSOR), _add); 1050 addItemPopUp(new ComboBoxItem(SIGNAL_HEAD), _add); 1051 addItemPopUp(new ComboBoxItem(SIGNAL_MAST), _add); 1052 addItemPopUp(new ComboBoxItem(MEMORY), _add); 1053 addItemPopUp(new ComboBoxItem(BLOCK_LABEL), _add); 1054 addItemPopUp(new ComboBoxItem(REPORTER), _add); 1055 addItemPopUp(new ComboBoxItem(LIGHT), _add); 1056 addItemPopUp(new ComboBoxItem(BACKGROUND), _add); 1057 addItemPopUp(new ComboBoxItem(MULTI_SENSOR), _add); 1058 addItemPopUp(new ComboBoxItem(RPSREPORTER), _add); 1059 addItemPopUp(new ComboBoxItem(FAST_CLOCK), _add); 1060 addItemPopUp(new ComboBoxItem(GLOBAL_VARIABLE), _add); 1061 addItemPopUp(new ComboBoxItem(LOGIXNG_TABLE), _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}