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