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