001package jmri.jmrit.display.controlPanelEditor; 002 003import java.awt.Color; 004import java.awt.Component; 005import java.awt.Container; 006import java.awt.Dimension; 007import java.awt.Font; 008import java.awt.Graphics; 009import java.awt.Point; 010import java.awt.Rectangle; 011import java.awt.Toolkit; 012import java.awt.datatransfer.Clipboard; 013import java.awt.datatransfer.ClipboardOwner; 014import java.awt.datatransfer.DataFlavor; 015import java.awt.datatransfer.Transferable; 016import java.awt.datatransfer.UnsupportedFlavorException; 017import java.awt.dnd.DnDConstants; 018import java.awt.dnd.DropTarget; 019import java.awt.dnd.DropTargetDragEvent; 020import java.awt.dnd.DropTargetDropEvent; 021import java.awt.dnd.DropTargetEvent; 022import java.awt.dnd.DropTargetListener; 023import java.awt.event.*; 024import java.awt.geom.Rectangle2D; 025import java.io.IOException; 026import java.util.ArrayList; 027import java.util.HashMap; 028import java.util.Iterator; 029import java.util.List; 030import java.util.ResourceBundle; 031 032import javax.annotation.Nonnull; 033import javax.swing.*; 034 035import jmri.*; 036import jmri.jmrit.catalog.ImageIndexEditor; 037import jmri.jmrit.catalog.NamedIcon; 038import jmri.jmrit.display.CoordinateEdit; 039import jmri.jmrit.display.Editor; 040import jmri.jmrit.display.IndicatorTrack; 041import jmri.jmrit.display.LinkingObject; 042import jmri.jmrit.display.LocoIcon; 043import jmri.jmrit.display.MemoryOrGVIcon; 044import jmri.jmrit.display.Positionable; 045import jmri.jmrit.display.PositionableIcon; 046import jmri.jmrit.display.PositionableJComponent; 047import jmri.jmrit.display.PositionableJPanel; 048import jmri.jmrit.display.PositionableLabel; 049import jmri.jmrit.display.PositionablePopupUtil; 050import jmri.jmrit.display.ReporterIcon; 051import jmri.jmrit.display.RpsPositionIcon; 052import jmri.jmrit.display.ToolTip; 053import jmri.jmrit.display.controlPanelEditor.shape.ShapeDrawer; 054import jmri.jmrit.display.palette.ColorDialog; 055import jmri.jmrit.display.palette.ItemPalette; 056import jmri.jmrit.logix.WarrantTableAction; 057import jmri.util.HelpUtil; 058import jmri.util.SystemType; 059import jmri.util.gui.GuiLafPreferencesManager; 060import jmri.util.swing.JmriJOptionPane; 061import jmri.util.swing.JmriMouseEvent; 062 063/** 064 * Provides a simple editor for adding jmri.jmrit.display items to a captive 065 * JFrame. 066 * <p> 067 * GUI is structured as a band of common parameters across the top, then a 068 * series of things you can add. 069 * <p> 070 * All created objects are put specific levels depending on their type (higher 071 * levels are in front): 072 * <ul> 073 * <li>BKG background 074 * <li>ICONS icons and other drawing symbols 075 * <li>LABELS text labels 076 * <li>TURNOUTS turnouts and other variable track items 077 * <li>SENSORS sensors and other independently modified objects 078 * </ul> 079 * Note that higher numbers appear behind lower numbers. 080 * <p> 081 * The "contents" List keeps track of all the objects added to the target frame 082 * for later manipulation. Extends the behavior it shares with PanelPro DnD 083 * implemented at JDK 1.2 for backward compatibility 084 * 085 * @author Pete Cressman Copyright: Copyright (c) 2009, 2010, 2011 086 */ 087public class ControlPanelEditor extends Editor implements DropTargetListener, ClipboardOwner { 088 089 protected JMenuBar _menuBar; 090 private JMenu _editorMenu; 091 protected JMenu _editMenu; 092 protected JMenu _fileMenu; 093 protected JMenu _optionMenu; 094 protected JMenu _iconMenu; 095 protected JMenu _zoomMenu; 096 private JMenu _markerMenu; 097 private JMenu _warrantMenu; 098 private JMenu _circuitMenu; 099 private JMenu _drawMenu; 100 private CircuitBuilder _circuitBuilder; 101 private final ArrayList<Rectangle> _highlightGroup = new ArrayList<>(); 102 private ShapeDrawer _shapeDrawer; 103 private ItemPalette _itemPalette; 104 private boolean _disableShapeSelection; 105 private boolean _disablePortalSelection = true; // only select PortalIcon in CircuitBuilder 106 private String _portalIconFamily = "Standard"; // initial default, must match xml, updated in setIconFamilysetPortalIconFamily 107 private HashMap<String, NamedIcon> _portalIconMap; 108 private JCheckBoxMenuItem disableLocoMarkerPopupMenuItem; 109 110 private final JCheckBoxMenuItem useGlobalFlagBox = new JCheckBoxMenuItem(Bundle.getMessage("CheckBoxGlobalFlags")); 111 private final JCheckBoxMenuItem positionableBox = new JCheckBoxMenuItem(Bundle.getMessage("CheckBoxPositionable")); 112 private final JCheckBoxMenuItem controllingBox = new JCheckBoxMenuItem(Bundle.getMessage("CheckBoxControlling")); 113 private final JCheckBoxMenuItem showTooltipBox = new JCheckBoxMenuItem(Bundle.getMessage("CheckBoxShowTooltips")); 114 private final JCheckBoxMenuItem hiddenBox = new JCheckBoxMenuItem(Bundle.getMessage("CheckBoxHidden")); 115 private final JCheckBoxMenuItem disableShapeSelect = new JCheckBoxMenuItem(Bundle.getMessage("disableShapeSelect")); 116 private final JRadioButtonMenuItem scrollBoth = new JRadioButtonMenuItem(Bundle.getMessage("ScrollBoth")); 117 private final JRadioButtonMenuItem scrollNone = new JRadioButtonMenuItem(Bundle.getMessage("ScrollNone")); 118 private final JRadioButtonMenuItem scrollHorizontal = new JRadioButtonMenuItem(Bundle.getMessage("ScrollHorizontal")); 119 private final JRadioButtonMenuItem scrollVertical = new JRadioButtonMenuItem(Bundle.getMessage("ScrollVertical")); 120 121 public ControlPanelEditor() { 122 } 123 124 public ControlPanelEditor(String name) { 125 super(name); 126 init(name); 127 } 128 129 @Override 130 protected void init(String name) { 131 setVisible(false); 132 java.awt.Container contentPane = this.getContentPane(); 133 contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS)); 134 135 // make menus 136 setGlobalSetsLocalFlag(false); 137 setUseGlobalFlag(false); 138 _menuBar = new JMenuBar(); 139 _circuitBuilder = new CircuitBuilder(this); 140 _shapeDrawer = new ShapeDrawer(this); 141 makeDrawMenu(); 142 makeWarrantMenu(true, true); 143 makeIconMenu(); 144 makeZoomMenu(); 145 makeMarkerMenu(); 146 makeOptionMenu(); 147 makeEditMenu(); 148 makeFileMenu(); 149 150 setJMenuBar(_menuBar); 151 addHelpMenu("package.jmri.jmrit.display.ControlPanelEditor", true); 152 153 super.setTargetPanel(null, null); 154 super.setTargetPanelSize(300, 300); 155 setInputFocusTraversal(); 156 makeDataFlavors(); 157 158 // set scrollbar initial state 159 setScroll(SCROLL_BOTH); 160 scrollBoth.setSelected(true); 161 super.setDefaultToolTip(new ToolTip(null, 0, 0, new Font("Serif", Font.PLAIN, 12), 162 Color.black, new Color(255, 250, 210), Color.black, null)); 163 // register the resulting panel for later configuration 164 ConfigureManager cm = InstanceManager.getNullableDefault(jmri.ConfigureManager.class); 165 if (cm != null) { 166 cm.registerUser(this); 167 } 168 pack(); 169 setVisible(true); 170 } 171 172 protected void makeIconMenu() { 173 _iconMenu = new JMenu(Bundle.getMessage("MenuIcon")); 174 _menuBar.add(_iconMenu, 0); 175 176 JMenuItem mi = new JMenuItem(Bundle.getMessage("CircuitBuilder")); 177 mi.addActionListener((ActionEvent event) -> _circuitBuilder.openCBWindow()); 178 setMenuAcceleratorKey(mi, KeyEvent.VK_B); 179 _iconMenu.add(mi); 180 181 _iconMenu.add(new JSeparator()); // below are different types of tables 182 183 mi = new JMenuItem(Bundle.getMessage("MenuItemItemPalette")); 184 mi.addActionListener(new ActionListener() { 185 Editor editor; 186 187 ActionListener init(Editor ed) { 188 editor = ed; 189 return this; 190 } 191 @Override 192 public void actionPerformed(ActionEvent e) { 193 _itemPalette = ItemPalette.getDefault(Bundle.getMessage("MenuItemItemPalette"), editor); 194 assert _itemPalette != null; 195 _itemPalette.setVisible(true); 196 } 197 }.init(this)); 198 setMenuAcceleratorKey(mi, KeyEvent.VK_P); 199 _iconMenu.add(mi); 200 201 _iconMenu.add(new jmri.jmrit.beantable.OBlockTableAction(Bundle.getMessage("MenuItemOBlockTable"))); 202 mi = (JMenuItem) _iconMenu.getMenuComponent(3); 203 setMenuAcceleratorKey(mi, KeyEvent.VK_O); 204 205 _iconMenu.add(new jmri.jmrit.beantable.ListedTableAction(Bundle.getMessage("MenuItemTableList"))); 206 mi = (JMenuItem) _iconMenu.getMenuComponent(4); 207 setMenuAcceleratorKey(mi, KeyEvent.VK_T); 208 } 209 210 @SuppressWarnings("deprecation") // META_MASK CTRL_MASK 211 private void setMenuAcceleratorKey (JMenuItem mi, int key) { 212 if (SystemType.isMacOSX()) { 213 mi.setAccelerator(KeyStroke.getKeyStroke(key, InputEvent.META_DOWN_MASK)); 214 } else { 215 mi.setAccelerator(KeyStroke.getKeyStroke(key, InputEvent.CTRL_DOWN_MASK)); 216 } 217 } 218 219 protected void makeCircuitMenu(boolean edit) { 220 if (edit) { 221 if (_circuitMenu == null) { 222 ItemPalette.loadIcons(); 223 _circuitMenu = _circuitBuilder.makeMenu(); 224 int idx = _menuBar.getComponentIndex(_warrantMenu); 225 _menuBar.add(_circuitMenu, ++idx); 226 _menuBar.revalidate(); 227 } 228 } else if (_circuitMenu != null) { 229 _circuitBuilder.closeCBWindow(); 230 _circuitMenu = null; 231 } 232 } 233 234 protected void makeDrawMenu() { 235 if (_drawMenu == null) { 236 _drawMenu = _shapeDrawer.makeMenu(); 237 _drawMenu.add(disableShapeSelect); 238 disableShapeSelect.addActionListener((ActionEvent event) -> _disableShapeSelection = disableShapeSelect.isSelected()); 239 } 240 _menuBar.add(_drawMenu, 0); 241 } 242 243 public boolean getShapeSelect() { 244 return !_disableShapeSelection; 245 } 246 247 public void setShapeSelect(boolean set) { 248 _disableShapeSelection = !set; 249 disableShapeSelect.setSelected(_disableShapeSelection); 250 } 251 252 public String getPortalIconFamily() { 253 return _portalIconFamily; 254 } 255 256 public void setPortalIconFamily(String family) { 257 if (family != null && !family.equals(_portalIconFamily)) { 258 _portalIconMap = null; 259 } 260 _portalIconFamily = family; 261 } 262 263 @Nonnull 264 public HashMap<String, NamedIcon> getPortalIconMap() { 265 if (_portalIconMap == null) { 266 ItemPalette.loadIcons(); 267 _portalIconMap = ItemPalette.getIconMap("Portal", _portalIconFamily); 268 if (_portalIconMap == null) { 269 HashMap<String, HashMap<String, NamedIcon>> familyMap = ItemPalette.getFamilyMaps("Portal"); 270 _portalIconMap = familyMap.get("Standard"); 271 // fill in the default PortalIconMap for CPE if it was null up to now. 272 // TODO check if this is ever called since we fixed getting it from ItemPalette above, remove for better maintainability 273 if (_portalIconMap == null) { 274 log.warn("empty PortalIconMap returned"); 275 _portalIconMap = new HashMap<>(); 276 _portalIconMap.put(PortalIcon.HIDDEN, 277 new NamedIcon("resources/icons/Invisible.gif", "resources/icons/Invisible.gif")); 278 _portalIconMap.put(PortalIcon.PATH, 279 new NamedIcon("resources/icons/greenSquare.gif", "resources/icons/greenSquare.gif")); 280 _portalIconMap.put(PortalIcon.VISIBLE, 281 new NamedIcon("resources/icons/throttles/RoundRedCircle20.png", "resources/icons/throttles/RoundRedCircle20.png")); 282 _portalIconMap.put(PortalIcon.TO_ARROW, 283 new NamedIcon("resources/icons/track/toArrow.gif", "resources/icons/track/toArrow.gif")); 284 _portalIconMap.put(PortalIcon.FROM_ARROW, 285 new NamedIcon("resources/icons/track/fromArrow.gif", "resources/icons/track/fromArrow.gif")); 286 } 287 } 288 } 289 // return a copy, not the ItemPalette's map! 290 return PositionableIcon.cloneMap(_portalIconMap, null); 291 } 292 293 public ShapeDrawer getShapeDrawer() { 294 return _shapeDrawer; 295 } 296 297 protected void makeZoomMenu() { 298 _zoomMenu = new JMenu(Bundle.getMessage("MenuZoom")); 299 _menuBar.add(_zoomMenu, 0); 300 JMenuItem addItem = new JMenuItem(Bundle.getMessage("NoZoom")); 301 _zoomMenu.add(addItem); 302 addItem.addActionListener((ActionEvent event) -> zoomRestore()); 303 304 addItem = new JMenuItem(Bundle.getMessage("Zoom", "...")); 305 _zoomMenu.add(addItem); 306 PositionableJComponent z = new PositionableJComponent(this); 307 z.setScale(getPaintScale()); 308 addItem.addActionListener(CoordinateEdit.getZoomEditAction(z)); 309 310 addItem = new JMenuItem(Bundle.getMessage("ZoomFit")); 311 _zoomMenu.add(addItem); 312 addItem.addActionListener((ActionEvent event) -> zoomToFit()); 313 } 314 315 protected void makeWarrantMenu(boolean edit, boolean addMenu) { 316 JMenu oldMenu = _warrantMenu; 317 _warrantMenu = jmri.jmrit.logix.WarrantTableAction.getDefault().makeWarrantMenu(edit); 318 if (_warrantMenu == null) { 319 _warrantMenu = new JMenu(ResourceBundle.getBundle("jmri.jmrit.logix.WarrantBundle").getString("MenuWarrant")); 320 JMenuItem aboutItem = new JMenuItem(Bundle.getMessage("AboutWarrant")); 321 HelpUtil.enableHelpOnButton(aboutItem, "package.jmri.jmrit.logix.Warrant"); 322 _warrantMenu.add(aboutItem); 323 aboutItem = new JMenuItem(Bundle.getMessage("AboutOBlock")); 324 HelpUtil.enableHelpOnButton(aboutItem, "package.jmri.jmrit.logix.OBlockTable"); 325 _warrantMenu.add(aboutItem); 326 aboutItem = new JMenuItem(Bundle.getMessage("AboutCircuitMenu")); 327 HelpUtil.enableHelpOnButton(aboutItem, "package.jmri.jmrit.display.CircuitBuilder"); 328 _warrantMenu.add(aboutItem); 329 aboutItem.addActionListener((ActionEvent event) -> { 330 makeCircuitMenu(true); 331// openCircuitWindow(); 332 }); 333 } 334 if (edit) { 335 makeCircuitMenu(edit); 336 JMenuItem item = new JMenuItem(Bundle.getMessage("OpenCircuitMenu")); 337 _warrantMenu.add(item); 338 item.addActionListener((ActionEvent event) -> _circuitBuilder.openCBWindow()); 339 } 340 if (addMenu) { 341 _menuBar.add(_warrantMenu, 0); 342 } else if (oldMenu != null) { 343 int idx = _menuBar.getComponentIndex(oldMenu); 344 _menuBar.remove(oldMenu); 345 _menuBar.add(_warrantMenu, idx); 346 347 } 348 _menuBar.revalidate(); 349 } 350 351 protected void makeMarkerMenu() { 352 _markerMenu = new JMenu(Bundle.getMessage("MenuMarker")); 353 _menuBar.add(_markerMenu); 354 _markerMenu.add(new AbstractAction(Bundle.getMessage("AddLoco")) { 355 @Override 356 public void actionPerformed(ActionEvent e) { 357 locoMarkerFromInput(); 358 } 359 }); 360 _markerMenu.add(new AbstractAction(Bundle.getMessage("AddLocoRoster")) { 361 @Override 362 public void actionPerformed(ActionEvent e) { 363 locoMarkerFromRoster(); 364 } 365 }); 366 _markerMenu.add(new AbstractAction(Bundle.getMessage("RemoveMarkers")) { 367 @Override 368 public void actionPerformed(ActionEvent e) { 369 removeMarkers(); 370 } 371 }); 372 InstanceManager.getOptionalDefault(UserPreferencesManager.class).ifPresent(prefsMgr -> { 373 _markerMenu.addSeparator(); 374 disableLocoMarkerPopupMenuItem = new JCheckBoxMenuItem( 375 new AbstractAction(Bundle.getMessage("DisableLocoMarkerPopup")) { 376 @Override 377 public void actionPerformed(ActionEvent e) { 378 enableDisableLocoMarkerPopups(); 379 } 380 }); 381 disableLocoMarkerPopupMenuItem.setSelected(isLocoMarkerPopupDisabled()); 382 _markerMenu.add(disableLocoMarkerPopupMenuItem); 383 }); 384 } 385 386 private void enableDisableLocoMarkerPopups() { 387 if (disableLocoMarkerPopupMenuItem != null) { 388 boolean selected = disableLocoMarkerPopupMenuItem.isSelected(); 389 setLocoMarkerPopupDisabled(selected); 390 } 391 } 392 393 protected void makeOptionMenu() { 394 _optionMenu = new JMenu(Bundle.getMessage("MenuOptions")); 395 _menuBar.add(_optionMenu, 0); 396 // use globals item 397 _optionMenu.add(useGlobalFlagBox); 398 useGlobalFlagBox.addActionListener((ActionEvent event) -> setUseGlobalFlag(useGlobalFlagBox.isSelected())); 399 useGlobalFlagBox.setSelected(useGlobalFlag()); 400 // positionable item 401 _optionMenu.add(positionableBox); 402 positionableBox.addActionListener((ActionEvent event) -> setAllPositionable(positionableBox.isSelected())); 403 positionableBox.setSelected(allPositionable()); 404 // controlable item 405 _optionMenu.add(controllingBox); 406 controllingBox.addActionListener((ActionEvent event) -> setAllControlling(controllingBox.isSelected())); 407 controllingBox.setSelected(allControlling()); 408 // hidden item 409 _optionMenu.add(hiddenBox); 410 hiddenBox.addActionListener((ActionEvent event) -> setShowHidden(hiddenBox.isSelected())); 411 hiddenBox.setSelected(showHidden()); 412 413 _optionMenu.add(showTooltipBox); 414 showTooltipBox.addActionListener((ActionEvent e) -> setAllShowToolTip(showTooltipBox.isSelected())); 415 showTooltipBox.setSelected(showToolTip()); 416 417 // Show/Hide Scroll Bars 418 JMenu scrollMenu = new JMenu(Bundle.getMessage("ComboBoxScrollable")); 419 _optionMenu.add(scrollMenu); 420 ButtonGroup scrollGroup = new ButtonGroup(); 421 scrollGroup.add(scrollBoth); 422 scrollMenu.add(scrollBoth); 423 scrollBoth.addActionListener((ActionEvent event) -> setScroll(SCROLL_BOTH)); 424 scrollGroup.add(scrollNone); 425 scrollMenu.add(scrollNone); 426 scrollNone.addActionListener((ActionEvent event) -> setScroll(SCROLL_NONE)); 427 scrollGroup.add(scrollHorizontal); 428 scrollMenu.add(scrollHorizontal); 429 scrollHorizontal.addActionListener((ActionEvent event) -> setScroll(SCROLL_HORIZONTAL)); 430 scrollGroup.add(scrollVertical); 431 scrollMenu.add(scrollVertical); 432 scrollVertical.addActionListener((ActionEvent event) -> setScroll(SCROLL_VERTICAL)); 433 } 434 435 private void makeFileMenu() { 436 _fileMenu = new JMenu(Bundle.getMessage("MenuFile")); 437 _menuBar.add(_fileMenu, 0); 438 _fileMenu.add(new jmri.jmrit.display.NewPanelAction(Bundle.getMessage("MenuItemNew"))); 439 440 _fileMenu.add(new jmri.configurexml.StoreXmlUserAction(Bundle.getMessage("FileMenuItemStore"))); 441 JMenuItem storeIndexItem = new JMenuItem(Bundle.getMessage("MIStoreImageIndex")); 442 _fileMenu.add(storeIndexItem); 443 storeIndexItem.addActionListener((ActionEvent event) -> InstanceManager.getDefault(CatalogTreeManager.class).storeImageIndex()); 444 445 JMenuItem editItem = new JMenuItem(Bundle.getMessage("renamePanelMenu", "...")); 446 PositionableJComponent z = new PositionableJComponent(this); 447 z.setScale(getPaintScale()); 448 editItem.addActionListener(CoordinateEdit.getNameEditAction(z)); 449 _fileMenu.add(editItem); 450 451 editItem = new JMenuItem(Bundle.getMessage("editIndexMenu")); 452 _fileMenu.add(editItem); 453 editItem.addActionListener((ActionEvent event) -> { 454 ImageIndexEditor ii = InstanceManager.getDefault(ImageIndexEditor.class); 455 ii.pack(); 456 ii.setVisible(true); 457 }); 458 459 editItem = new JMenuItem(Bundle.getMessage("PEView")); 460 _fileMenu.add(editItem); 461 editItem.addActionListener((ActionEvent event) -> { 462 changeView("jmri.jmrit.display.panelEditor.PanelEditor"); 463 if (_itemPalette != null) { 464 _itemPalette.dispose(); 465 } 466 }); 467 468 _fileMenu.addSeparator(); 469 JMenuItem deleteItem = new JMenuItem(Bundle.getMessage("DeletePanel")); 470 _fileMenu.add(deleteItem); 471 deleteItem.addActionListener((ActionEvent event) -> { 472 if (deletePanel()) { 473 dispose(); 474 } 475 }); 476 _fileMenu.addSeparator(); 477 editItem = new JMenuItem(Bundle.getMessage("CloseEditor")); 478 _fileMenu.add(editItem); 479 editItem.addActionListener((ActionEvent event) -> setAllEditable(false)); 480 } 481 482 /** 483 * Create an Edit menu to support cut/copy/paste. An incredible hack to get 484 * some semblance of CCP between panels. The hack works for one of two 485 * problems. 1. Invoking a copy to the system clipboard causes a delayed 486 * repaint placed on the EventQueue whenever ScrollBars are invoked. This 487 * repaint ends with a null pointer exception at 488 * javax.swing.plaf.basic.BasicScrollPaneUI.paint(BasicScrollPaneUI.java:90) 489 * This error occurs regardless of the method used to put the copy in the 490 * clipboard - JDK 1.2 style or 1.4 TransferHandler Fixed! Get the plaf glue 491 * (BasicScrollPaneUI) and call installUI(_panelScrollPane) See 492 * copyToClipboard() below, line 527 (something the Java code should have 493 * done) No scrollbars - no problem. Hack does not fix this problem. 2. The 494 * clipboard provides a shallow copy of what was placed there. For things 495 * that have an icon Map (ArrayLists) the Tranferable data is shallow. The 496 * Hack to work around this is: Place a reference to the panel copying to 497 * the clipboard in the clipboard and let the pasting panel callback to the 498 * copying panel to get the data. See public ArrayList<Positionable> 499 * getClipGroup() {} below. 500 */ 501 protected void makeEditMenu() { 502 _editMenu = new JMenu(Bundle.getMessage("ButtonEdit")); 503 _menuBar.add(_editMenu, 0); 504 _editMenu.setMnemonic(KeyEvent.VK_E); 505 JMenuItem menuItem = new JMenuItem(Bundle.getMessage("MenuItemCut")); 506 menuItem.addActionListener((ActionEvent event) -> { 507 copyToClipboard(); 508 removeSelections(null); 509 }); 510 setMenuAcceleratorKey(menuItem, KeyEvent.VK_X); 511 _editMenu.add(menuItem); 512 513 menuItem = new JMenuItem(Bundle.getMessage("MenuItemCopy")); 514 menuItem.addActionListener((ActionEvent event) -> copyToClipboard()); 515 setMenuAcceleratorKey(menuItem, KeyEvent.VK_C); 516 _editMenu.add(menuItem); 517 518 menuItem = new JMenuItem(Bundle.getMessage("MenuItemPaste")); 519 menuItem.addActionListener((ActionEvent event) -> pasteFromClipboard()); 520 setMenuAcceleratorKey(menuItem, KeyEvent.VK_V); 521 _editMenu.add(menuItem); 522 523 _editMenu.add(makeSelectTypeMenu()); 524 _editMenu.add(makeSelectLevelMenu()); 525 526 menuItem = new JMenuItem(Bundle.getMessage("SelectAll")); 527 menuItem.addActionListener((ActionEvent event) -> { 528 _selectionGroup = new ArrayList<>(getContents()); 529 _targetPanel.repaint(); 530 }); 531 setMenuAcceleratorKey(menuItem, KeyEvent.VK_A); 532 _editMenu.add(menuItem); 533 } 534 535 private JMenu makeSelectTypeMenu() { 536 JMenu menu = new JMenu(Bundle.getMessage("SelectType")); 537 ButtonGroup typeGroup = new ButtonGroup(); 538 // I18N use existing jmri.NamedBeanBundle keys 539 JRadioButtonMenuItem button = makeSelectTypeButton("IndicatorTrack", "jmri.jmrit.display.IndicatorTrackIcon"); 540 typeGroup.add(button); 541 menu.add(button); 542 button = makeSelectTypeButton("IndicatorTO", "jmri.jmrit.display.IndicatorTurnoutIcon"); 543 typeGroup.add(button); 544 menu.add(button); 545 button = makeSelectTypeButton("BeanNameTurnout", "jmri.jmrit.display.TurnoutIcon"); 546 typeGroup.add(button); 547 menu.add(button); 548 button = makeSelectTypeButton("BeanNameSensor", "jmri.jmrit.display.SensorIcon"); 549 typeGroup.add(button); 550 menu.add(button); 551 button = makeSelectTypeButton("Shape", "jmri.jmrit.display.controlPanelEditor.shape.PositionableShape"); 552 typeGroup.add(button); 553 menu.add(button); 554 button = makeSelectTypeButton("BeanNameSignalMast", "jmri.jmrit.display.SignalMastIcon"); 555 typeGroup.add(button); 556 menu.add(button); 557 button = makeSelectTypeButton("BeanNameSignalHead", "jmri.jmrit.display.SignalHeadIcon"); 558 typeGroup.add(button); 559 menu.add(button); 560 button = makeSelectTypeButton("BeanNameMemory", "jmri.jmrit.display.MemoryIcon"); 561 typeGroup.add(button); 562 menu.add(button); 563 button = makeSelectTypeButton("MemoryInput", "jmri.jmrit.display.PositionableJPanel"); 564 typeGroup.add(button); 565 menu.add(button); 566 button = makeSelectTypeButton("MultiSensor", "jmri.jmrit.display.MultiSensorIcon"); 567 typeGroup.add(button); 568 menu.add(button); 569 button = makeSelectTypeButton("LocoID", "jmri.jmrit.display.LocoIcon"); 570 typeGroup.add(button); 571 menu.add(button); 572 button = makeSelectTypeButton("BeanNameLight", "jmri.jmrit.display.LightIcon"); 573 typeGroup.add(button); 574 menu.add(button); 575 return menu; 576 } 577 578 private JRadioButtonMenuItem makeSelectTypeButton(String label, String className) { 579 JRadioButtonMenuItem button = new JRadioButtonMenuItem(Bundle.getMessage(label)); 580 button.addActionListener(new ActionListener() { 581 String cName; 582 583 ActionListener init(String name) { 584 cName = name; 585 return this; 586 } 587 588 @Override 589 public void actionPerformed(ActionEvent event) { 590 selectType(cName); 591 } 592 }.init(className)); 593 return button; 594 } 595 596 private void selectType(String name) { 597 try { 598 Class<?> cl = Class.forName(name); 599 _selectionGroup = new ArrayList<>(); 600 for (Positionable pos : getContents()) { 601 if (cl.isInstance(pos)) { 602 _selectionGroup.add(pos); 603 } 604 } 605 } catch (ClassNotFoundException cnfe) { 606 log.error("selectType Menu {}", cnfe.toString()); 607 } 608 _targetPanel.repaint(); 609 } 610 611 private JMenu makeSelectLevelMenu() { 612 JMenu menu = new JMenu(Bundle.getMessage("SelectLevel")); 613 ButtonGroup levelGroup = new ButtonGroup(); 614 JRadioButtonMenuItem button; 615 for (int i = 0; i < 11; i++) { 616 button = new JRadioButtonMenuItem(Bundle.getMessage("selectLevel", "" + i)); 617 levelGroup.add(button); 618 menu.add(button); 619 button.addActionListener(new ActionListener() { 620 int j; 621 622 ActionListener init(int k) { 623 j = k; 624 return this; 625 } 626 627 @Override 628 public void actionPerformed(ActionEvent event) { 629 selectLevel(j); 630 } 631 }.init(i)); 632 } 633 return menu; 634 } 635 636 private void selectLevel(int i) { 637 _selectionGroup = new ArrayList<>(); 638 for (Positionable pos : getContents()) { 639 if (pos.getDisplayLevel() == i) { 640 _selectionGroup.add(pos); 641 } 642 } 643 _targetPanel.repaint(); 644 } 645 646 ////////////////////////// end Menus ////////////////////////// 647 public CircuitBuilder getCircuitBuilder() { 648 return _circuitBuilder; 649 } 650 651 private void pasteFromClipboard() { 652 Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); 653 DataFlavor[] flavors = clipboard.getAvailableDataFlavors(); 654 for (DataFlavor flavor : flavors) { 655 if (_positionableListDataFlavor.equals(flavor)) { 656 try { 657 @SuppressWarnings("unchecked") 658 List<Positionable> clipGroup = (List<Positionable>) clipboard.getData(_positionableListDataFlavor); 659 if (clipGroup != null && clipGroup.size() > 0) { 660 Positionable pos = clipGroup.get(0); 661 int minX = pos.getLocation().x; 662 int minY = pos.getLocation().y; 663 // locate group at mouse point 664 for (int i = 1; i < clipGroup.size(); i++) { 665 pos = clipGroup.get(i); 666 minX = Math.min(minX, pos.getLocation().x); 667 minY = Math.min(minY, pos.getLocation().y); 668 } 669 if (_pastePending) { 670 abortPasteItems(); 671 } 672 _selectionGroup = new ArrayList<>(); 673 for (Positionable positionable : clipGroup) { 674 pos = positionable; 675 // make positionable belong to this editor 676 pos.setEditor(this); 677 pos.setLocation(pos.getLocation().x + _anchorX - minX, pos.getLocation().y + _anchorY - minY); 678 // now set display level in the pane. 679 pos.setDisplayLevel(pos.getDisplayLevel()); 680 try { 681 pos.setId(null); 682 putItem(pos); 683 } catch (Positionable.DuplicateIdException e) { 684 // This should never happen 685 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 686 } 687 pos.updateSize(); 688 pos.setVisible(true); 689 _selectionGroup.add(pos); 690 if (pos instanceof PositionableIcon) { 691 jmri.NamedBean bean = pos.getNamedBean(); 692 if (bean != null) { 693 ((PositionableIcon) pos).displayState(bean.getState()); 694 } 695 } else if (pos instanceof MemoryOrGVIcon) { 696 ((MemoryOrGVIcon) pos).displayState(); 697 } else if (pos instanceof PositionableJComponent) { 698 ((PositionableJComponent) pos).displayState(); 699 } 700 log.debug("Paste Added at ({}, {})", pos.getLocation().x, pos.getLocation().y); 701 } 702 } 703 return; 704 } catch (IOException ioe) { 705 log.warn("Editor Paste caught IOException", ioe); 706 } catch (UnsupportedFlavorException ufe) { 707 log.warn("Editor Paste caught UnsupportedFlavorException", ufe); 708 } 709 } 710 } 711 } 712 713 /* 714 * The editor instance is dragged. When dropped this editor will reference 715 * the list of positionables (_clipGroup) for pasting 716 */ 717 private void copyToClipboard() { 718 if (_selectionGroup != null) { 719 ArrayList<Positionable> dragGroup = new ArrayList<>(); 720 721 for (Positionable comp : _selectionGroup) { 722 Positionable pos = comp.deepClone(); 723 dragGroup.add(pos); 724 removeFromTarget(pos); // cloned item gets added to _targetPane during cloning 725 } 726 log.debug("copyToClipboard: cloned _selectionGroup, size= {}", _selectionGroup.size()); 727 _clipGroup = dragGroup; 728 729 Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); 730 clipboard.setContents(new PositionableListDnD(_clipGroup), this); 731 log.debug("copyToClipboard: setContents _selectionGroup, size= {}", _selectionGroup.size()); 732 } else { 733 _clipGroup = null; 734 } 735 } 736 737 ArrayList<Positionable> _clipGroup; 738 739 public ArrayList<Positionable> getClipGroup() { 740 if (log.isDebugEnabled()) { // avoid string concatination if not debug 741 log.debug("getClipGroup: _clipGroup{}", _clipGroup == null ? "=null" : ", size= " + _clipGroup.size()); 742 } 743 if (_clipGroup == null) { 744 return null; 745 } 746 ArrayList<Positionable> clipGrp = new ArrayList<>(); 747 for (Positionable _comp : _clipGroup) { 748 Positionable pos = _comp.deepClone(); 749 clipGrp.add(pos); 750 removeFromTarget(pos); // cloned item gets added to _targetPane during cloning 751 } 752 return clipGrp; 753 } 754 755 ///// implementation of ClipboardOwner 756 @Override 757 public void lostOwnership(Clipboard clipboard, Transferable contents) { 758 /* don't care */ 759 log.debug("lostOwnership: content flavor[0] = {}", contents.getTransferDataFlavors()[0]); 760 } 761 762 @Override 763 public void setAllEditable(boolean edit) { 764 if (_warrantMenu != null) { 765 _menuBar.remove(_warrantMenu); 766 } 767 if (_circuitMenu != null) { 768 _menuBar.remove(_circuitMenu); 769 _circuitMenu = null; 770 } 771 if (edit) { 772 if (_editorMenu != null) { 773 _menuBar.remove(_editorMenu); 774 } 775 if (_markerMenu != null) { 776 _menuBar.remove(_markerMenu); 777 } 778 if (_drawMenu == null) { 779 makeDrawMenu(); 780 } else { 781 _menuBar.add(_drawMenu, 0); 782 } 783 makeWarrantMenu(true, true); 784 785 if (_iconMenu == null) { 786 makeIconMenu(); 787 } else { 788 _menuBar.add(_iconMenu, 0); 789 } 790 if (_zoomMenu == null) { 791 makeZoomMenu(); 792 } else { 793 _menuBar.add(_zoomMenu, 0); 794 } 795 if (_optionMenu == null) { 796 makeOptionMenu(); 797 } else { 798 _menuBar.add(_optionMenu, 0); 799 } 800 if (_editMenu == null) { 801 makeEditMenu(); 802 } else { 803 _menuBar.add(_editMenu, 0); 804 } 805 if (_fileMenu == null) { 806 makeFileMenu(); 807 } else { 808 _menuBar.add(_fileMenu, 0); 809 } 810 } else { 811 if (_fileMenu != null) { 812 _menuBar.remove(_fileMenu); 813 } 814 if (_editMenu != null) { 815 _menuBar.remove(_editMenu); 816 } 817 if (_optionMenu != null) { 818 _menuBar.remove(_optionMenu); 819 } 820 if (_zoomMenu != null) { 821 _menuBar.remove(_zoomMenu); 822 } 823 if (_iconMenu != null) { 824 _menuBar.remove(_iconMenu); 825 } 826 if (_drawMenu != null) { 827 _menuBar.remove(_drawMenu); 828 } 829 if (InstanceManager.getDefault(jmri.jmrit.logix.OBlockManager.class).getNamedBeanSet().size() > 1) { 830 makeWarrantMenu(false, true); 831// _circuitMenu = null; 832 } 833 if (_markerMenu == null) { 834 makeMarkerMenu(); 835 } else { 836 _menuBar.add(_markerMenu, 0); 837 } 838 if (_editorMenu == null) { // replaces _fileMenu 839 _editorMenu = new JMenu(Bundle.getMessage("MenuEdit")); 840 _editorMenu.add(new AbstractAction(Bundle.getMessage("OpenEditor")) { 841 @Override 842 public void actionPerformed(ActionEvent e) { 843 setAllEditable(true); 844 } 845 }); 846 } 847 _menuBar.add(_editorMenu, 0); 848 } 849 super.setAllEditable(edit); 850 setTitle(); 851 _menuBar.revalidate(); 852 } 853 854 @Override 855 public void setUseGlobalFlag(boolean set) { 856 positionableBox.setEnabled(set); 857 controllingBox.setEnabled(set); 858 super.setUseGlobalFlag(set); 859 } 860 861 private void zoomRestore() { 862 List<Positionable> contents = getContents(); 863 for (Positionable p : contents) { 864 p.setLocation(p.getX() + _fitX, p.getY() + _fitY); 865 } 866 setPaintScale(1.0); 867 } 868 869 int _fitX = 0; 870 int _fitY = 0; 871 872 private void zoomToFit() { 873 double minX = 1000.0; 874 double maxX = 0.0; 875 double minY = 1000.0; 876 double maxY = 0.0; 877 List<Positionable> contents = getContents(); 878 for (Positionable p : contents) { 879 minX = Math.min(p.getX(), minX); 880 minY = Math.min(p.getY(), minY); 881 maxX = Math.max(p.getX() + p.getWidth(), maxX); 882 maxY = Math.max(p.getY() + p.getHeight(), maxY); 883 } 884 _fitX = (int) Math.floor(minX); 885 _fitY = (int) Math.floor(minY); 886 887 JFrame frame = getTargetFrame(); 888 Container contentPane = getTargetFrame().getContentPane(); 889 Dimension dim = contentPane.getSize(); 890 Dimension d = getTargetPanel().getSize(); 891 getTargetPanel().setSize((int) Math.ceil(maxX - minX), (int) Math.ceil(maxY - minY)); 892 893 JScrollPane scrollPane = getPanelScrollPane(); 894 scrollPane.getHorizontalScrollBar().setValue(0); 895 scrollPane.getVerticalScrollBar().setValue(0); 896 JViewport viewPort = scrollPane.getViewport(); 897 Dimension dv = viewPort.getExtentSize(); 898 899 int dX = frame.getWidth() - dv.width; 900 int dY = frame.getHeight() - dv.height; 901 log.debug("zoomToFit: layoutWidth= {}, layoutHeight= {}\n\tframeWidth= {}, frameHeight= {}, viewWidth= {}, viewHeight= {}\n\tconWidth= {}, conHeight= {}, panelWidth= {}, panelHeight= {}", 902 (maxX - minX), (maxY - minY), frame.getWidth(), frame.getHeight(), dv.width, dv.height, dim.width, dim.height, d.width, d.height); 903 double ratioX = dv.width / (maxX - minX); 904 double ratioY = dv.height / (maxY - minY); 905 double ratio = Math.min(ratioX, ratioY); 906 /* 907 if (ratioX<ratioY) { 908 if (ratioX>1.0) { 909 ratio = ratioX; 910 } else { 911 ratio = ratioY; 912 } 913 } else { 914 if (ratioY<1.0) { 915 ratio = ratioX; 916 } else { 917 ratio = ratioY; 918 } 919 } */ 920 _fitX = (int) Math.floor(minX); 921 _fitY = (int) Math.floor(minY); 922 for (Positionable p : contents) { 923 p.setLocation(p.getX() - _fitX, p.getY() - _fitY); 924 } 925 setScroll(SCROLL_BOTH); 926 setPaintScale(ratio); 927 setScroll(SCROLL_NONE); 928 scrollNone.setSelected(true); 929 //getTargetPanel().setSize((int)Math.ceil(maxX), (int)Math.ceil(maxY)); 930 frame.setSize((int) Math.ceil((maxX - minX) * ratio) + dX, (int) Math.ceil((maxY - minY) * ratio) + dY); 931 scrollPane.getHorizontalScrollBar().setValue(0); 932 scrollPane.getVerticalScrollBar().setValue(0); 933 log.debug("zoomToFit: ratio= {}, w= {}, h= {}, frameWidth= {}, frameHeight= {}", 934 ratio, (maxX - minX), (maxY - minY), frame.getWidth(), frame.getHeight()); 935 } 936 937 @Override 938 public void setTitle() { 939 String name = getName(); 940 if (name == null || name.length() == 0) { 941 name = Bundle.getMessage("untitled"); 942 } 943 String ending = " " + Bundle.getMessage("LabelEditor"); 944 String defaultName = Bundle.getMessage("ControlPanelEditor"); 945 defaultName = defaultName.substring(0, defaultName.length() - ending.length()); 946 if (name.endsWith(ending)) { 947 name = name.substring(0, name.length() - ending.length()); 948 } 949 if (name.equals(defaultName)) { 950 name = Bundle.getMessage("untitled") + "(" + name + ")"; 951 } 952 if (isEditable()) { 953 super.setTitle(name + " " + Bundle.getMessage("LabelEditor")); 954 } else { 955 super.setTitle(name); 956 } 957 setName(name); 958 } 959 960 // all content loaded from file. 961 public void loadComplete() { 962 log.debug("loadComplete"); 963 } 964 965 /** 966 * After construction, initialize all the widgets to their saved config 967 * settings. 968 */ 969 @Override 970 public void initView() { 971 positionableBox.setSelected(allPositionable()); 972 controllingBox.setSelected(allControlling()); 973 //showCoordinatesBox.setSelected(showCoordinates()); 974 showTooltipBox.setSelected(showToolTip()); 975 hiddenBox.setSelected(showHidden()); 976 switch (_scrollState) { 977 case SCROLL_NONE: 978 scrollNone.setSelected(true); 979 break; 980 case SCROLL_BOTH: 981 scrollBoth.setSelected(true); 982 break; 983 case SCROLL_HORIZONTAL: 984 scrollHorizontal.setSelected(true); 985 break; 986 case SCROLL_VERTICAL: 987 scrollVertical.setSelected(true); 988 break; 989 default: 990 log.warn("Unhandled scroll state: {}", _scrollState); 991 break; 992 } 993 log.debug("InitView done"); 994 } 995 996 ////////////////// Overridden methods of Editor ////////////////// 997 private boolean _manualSelection = false; 998 999 @Override 1000 public void deselectSelectionGroup() { 1001 _circuitBuilder.hidePortalIcons(false); 1002 super.deselectSelectionGroup(); 1003 } 1004 1005 protected Positionable getCurrentSelection(JmriMouseEvent event) { 1006 if (_pastePending && !event.isPopupTrigger() && !event.isMetaDown() && !event.isAltDown()) { 1007 return getCopySelection(event); 1008 } 1009 List<Positionable> selections = getSelectedItems(event); 1010 if (_disableShapeSelection || _disablePortalSelection) { 1011 ArrayList<Positionable> list = new ArrayList<>(); 1012 for (Positionable pos : selections) { 1013 if (_disableShapeSelection && pos instanceof jmri.jmrit.display.controlPanelEditor.shape.PositionableShape) { 1014 continue; 1015 } 1016 if (_disablePortalSelection && pos instanceof PortalIcon) { 1017 continue; 1018 } 1019 list.add(pos); 1020 } 1021 selections = list; 1022 } 1023 Positionable selection = null; 1024 if (selections.size() > 0) { 1025 if (event.isControlDown()) { 1026 if (event.isShiftDown() && selections.size() > 3) { 1027 if (_manualSelection) { 1028 // selection made - don't change it 1029 deselectSelectionGroup(); 1030 return _currentSelection; 1031 } 1032 // show list 1033 String[] selects = new String[selections.size()]; 1034 Iterator<Positionable> iter = selections.iterator(); 1035 int i = 0; 1036 while (iter.hasNext()) { 1037 Positionable pos = iter.next(); 1038 if (pos instanceof jmri.NamedBean) { 1039 selects[i++] = ((jmri.NamedBean) pos).getDisplayName(); 1040 } else { 1041 selects[i++] = pos.getNameString(); 1042 } 1043 } 1044 Object select = JmriJOptionPane.showInputDialog(this, Bundle.getMessage("multipleSelections"), 1045 Bundle.getMessage("QuestionTitle"), JmriJOptionPane.QUESTION_MESSAGE, 1046 null, selects, null); 1047 if (select != null) { 1048 iter = selections.iterator(); 1049 while (iter.hasNext()) { 1050 Positionable pos = iter.next(); 1051 String name; 1052 if (pos instanceof jmri.NamedBean) { 1053 name = ((jmri.NamedBean) pos).getDisplayName(); 1054 } else { 1055 name = pos.getNameString(); 1056 } 1057 if (select.equals(name)) { 1058 _manualSelection = true; 1059 highlight(pos); 1060 return pos; 1061 } 1062 } 1063 } else { 1064 selection = selections.get(selections.size() - 1); 1065 } 1066 } else { 1067 // select bottom-most item over the background, otherwise take the background item 1068 selection = selections.get(selections.size() - 1); 1069 if (selection.getDisplayLevel() <= BKG && selections.size() > 1) { 1070 selection = selections.get(selections.size() - 2); 1071 } 1072// _manualSelection = false; 1073 } 1074 } else { 1075 if (event.isShiftDown() && selections.size() > 1) { 1076 selection = selections.get(1); 1077 } else { 1078 selection = selections.get(0); 1079 } 1080 if (selection.getDisplayLevel() <= BKG) { 1081 selection = null; 1082 } 1083 _manualSelection = false; 1084 } 1085 } else { 1086 if (event.isControlDown() && (event.isPopupTrigger() || event.isMetaDown() || event.isAltDown())) { 1087 ActionListener ca; 1088 Editor ed = this; 1089 ca = e -> { 1090 if (_itemPalette != null) { 1091 _itemPalette.setEditor(ed); 1092 } 1093 }; 1094 new ColorDialog(this, getTargetPanel(), ColorDialog.ONLY, ca); 1095 } 1096 } 1097 if (!isEditable() && selection != null && selection.isHidden()) { 1098 selection = null; 1099 } 1100 return selection; 1101 } 1102 1103 private Positionable getCopySelection(JmriMouseEvent event) { 1104 if (_selectionGroup == null) { 1105 return null; 1106 } 1107 double x = event.getX(); 1108 double y = event.getY(); 1109 1110 for (Positionable p : _selectionGroup) { 1111 Rectangle2D.Double rect2D = new Rectangle2D.Double(p.getX() * _paintScale, 1112 p.getY() * _paintScale, 1113 p.maxWidth() * _paintScale, 1114 p.maxHeight() * _paintScale); 1115 if (rect2D.contains(x, y)) { 1116 return p; 1117 } 1118 } 1119 return null; 1120 } 1121 1122 /** 1123 * Capture key events. 1124 * 1125 * @param e the event 1126 */ 1127 @Override 1128 public void keyPressed(KeyEvent e) { 1129 int x = 0; 1130 int y = 0; 1131 switch (e.getKeyCode()) { 1132 case KeyEvent.VK_UP: 1133 case KeyEvent.VK_KP_UP: 1134 case KeyEvent.VK_NUMPAD8: 1135 y = -1; 1136 break; 1137 case KeyEvent.VK_DOWN: 1138 case KeyEvent.VK_KP_DOWN: 1139 case KeyEvent.VK_NUMPAD2: 1140 y = 1; 1141 break; 1142 case KeyEvent.VK_LEFT: 1143 case KeyEvent.VK_KP_LEFT: 1144 case KeyEvent.VK_NUMPAD4: 1145 x = -1; 1146 break; 1147 case KeyEvent.VK_RIGHT: 1148 case KeyEvent.VK_KP_RIGHT: 1149 case KeyEvent.VK_NUMPAD6: 1150 x = 1; 1151 break; 1152 case KeyEvent.VK_D: 1153 case KeyEvent.VK_DELETE: 1154 case KeyEvent.VK_MINUS: 1155 _shapeDrawer.delete(); 1156 break; 1157 case KeyEvent.VK_A: 1158 case KeyEvent.VK_INSERT: 1159 case KeyEvent.VK_PLUS: 1160 _shapeDrawer.add(e.isShiftDown()); 1161 break; 1162 default: 1163 return; 1164 1165 } 1166 if (e.isShiftDown()) { 1167 x *= 5; 1168 y *= 5; 1169 } 1170 if (_selectionGroup != null) { 1171 for (Positionable comp : _selectionGroup) { 1172 moveItem(comp, x, y); 1173 } 1174 } 1175 repaint(); 1176 } 1177 1178 ///////////////// Handle mouse events //////////////// 1179 private long _mouseDownTime = 0; 1180 1181 @Override 1182 public void mousePressed(JmriMouseEvent event) { 1183 _mouseDownTime = System.currentTimeMillis(); 1184 setToolTip(null); // ends tooltip if displayed 1185 log.debug("mousePressed at ({},{}) _dragging={}", event.getX(), event.getY(), _dragging); 1186 // " _selectionGroup= "+(_selectionGroup==null?"null":_selectionGroup.size())); 1187 boolean circuitBuilder = _circuitBuilder.saveSelectionGroup(_selectionGroup); 1188 _anchorX = event.getX(); 1189 _anchorY = event.getY(); 1190 _lastX = _anchorX; 1191 _lastY = _anchorY; 1192 1193 _currentSelection = getCurrentSelection(event); 1194 _circuitBuilder.doMousePressed(event, _currentSelection); 1195 1196 if (!event.isPopupTrigger() && !event.isMetaDown() && !event.isAltDown() && !circuitBuilder) { 1197 _shapeDrawer.doMousePressed(event, _currentSelection); 1198 if (_currentSelection != null) { 1199 _currentSelection.doMousePressed(event); 1200 if (isEditable()) { 1201 if (!event.isControlDown() 1202 && (_selectionGroup != null && !_selectionGroup.contains(_currentSelection))) { 1203 if (_pastePending) { 1204 abortPasteItems(); 1205 } 1206 deselectSelectionGroup(); 1207 } 1208 } 1209 } else { 1210 _highlightcomponent = null; 1211 if (_pastePending) { 1212 abortPasteItems(); 1213 } 1214 deselectSelectionGroup(); 1215 } 1216 } else if (_currentSelection == null || (_selectionGroup != null && !_selectionGroup.contains(_currentSelection))) { 1217 deselectSelectionGroup(); 1218 } 1219 _circuitBuilder.doMousePressed(event); 1220 _targetPanel.repaint(); // needed for ToolTip 1221 } 1222 1223 @Override 1224 public void mouseReleased(JmriMouseEvent event) { 1225 _mouseDownTime = 0; 1226 setToolTip(null); // ends tooltip if displayed 1227 if (log.isDebugEnabled()) { // avoid string concatination if not debug 1228 log.debug("mouseReleased at ({},{}) dragging={}, pastePending={}, selectRect is{} null", 1229 event.getX(), event.getY(), _dragging, _pastePending, (_selectRect == null ? "" : " not")); 1230 } 1231 Positionable selection = getCurrentSelection(event); 1232 1233 if ((event.isPopupTrigger() || event.isMetaDown() || event.isAltDown()) /*&& !_dragging*/) { 1234 if (selection != null) { 1235 _highlightcomponent = null; 1236 showPopUp(selection, event); 1237 } else if (_selectRect != null) { 1238 makeSelectionGroup(event); 1239 } 1240 } else { 1241 if (selection != null) { 1242 selection.doMouseReleased(event); 1243 } 1244 // when dragging, don't change selection group 1245 if (_pastePending && _dragging) { 1246 pasteItems(); 1247 } 1248 if (isEditable()) { 1249 _shapeDrawer.doMouseReleased(selection, event, this); 1250 1251 if (!_circuitBuilder.doMouseReleased(selection, _dragging)) { 1252 if (selection != null) { 1253 if (!_dragging) { 1254 modifySelectionGroup(selection, event); 1255 } 1256 } 1257 if (_selectRect != null) { 1258 makeSelectionGroup(event); 1259 } 1260 if (_currentSelection != null && (_selectionGroup == null || _selectionGroup.isEmpty())) { 1261 if (_selectionGroup == null) { 1262 _selectionGroup = new ArrayList<>(); 1263 } 1264 _selectionGroup.add(_currentSelection); 1265 } 1266 } 1267 _currentSelection = selection; 1268 } else { 1269 deselectSelectionGroup(); 1270 _currentSelection = null; 1271 _highlightcomponent = null; 1272 } 1273 } 1274 _selectRect = null; 1275 1276 // if not sending MouseClicked, do it here 1277 if (InstanceManager.getDefault(GuiLafPreferencesManager.class).isNonStandardMouseEvent()) { 1278 mouseClicked(event); 1279 } 1280 1281 _lastX = event.getX(); 1282 _lastY = event.getY(); 1283 _dragging = false; 1284 _currentSelection = null; 1285 _targetPanel.repaint(); // needed for ToolTip 1286// if (_debug) log.debug("mouseReleased at ("+event.getX()+","+event.getY()+ 1287// " _selectionGroup= "+(_selectionGroup==null?"null":_selectionGroup.size())); 1288 } 1289 1290 private long _clickTime; 1291 1292 @Override 1293 public void mouseClicked(JmriMouseEvent event) { 1294 if (InstanceManager.getDefault(GuiLafPreferencesManager.class).isNonStandardMouseEvent()) { 1295 long time = System.currentTimeMillis(); 1296 if (time - _clickTime < 20) { 1297 return; 1298 } 1299 _clickTime = time; 1300 } 1301 1302 setToolTip(null); // ends tooltip if displayed 1303 log.debug("mouseClicked at ({},{})", event.getX(), event.getY()); 1304 1305 Positionable selection = getCurrentSelection(event); 1306 if (_shapeDrawer.doMouseClicked(event, this)) { 1307 return; 1308 } 1309 1310 if (event.isPopupTrigger() || event.isMetaDown() || event.isAltDown()) { 1311 if (selection != null) { 1312 _highlightcomponent = null; 1313 showPopUp(selection, event); 1314 } 1315 } else if (selection != null) { 1316 if (_circuitBuilder.doMouseClicked(getSelectedItems(event), event)) { 1317 return; 1318 } else { 1319 selection.doMouseClicked(event); 1320 } 1321 if (selection instanceof IndicatorTrack) { 1322 WarrantTableAction.getDefault().mouseClickedOnBlock(((IndicatorTrack) selection).getOccBlock()); 1323 } 1324 } 1325 if (!isEditable()) { 1326 deselectSelectionGroup(); 1327 _currentSelection = null; 1328 _highlightcomponent = null; 1329 } 1330 _targetPanel.repaint(); // needed for ToolTip 1331 } 1332 1333 @Override 1334 public void mouseDragged(JmriMouseEvent event) { 1335 //if (_debug) log.debug("mouseDragged at ("+event.getX()+","+event.getY()+")"); 1336 setToolTip(null); // ends tooltip if displayed 1337 1338 long time = System.currentTimeMillis(); 1339 if (time - _mouseDownTime < 200) { 1340 return; // don't drag until sure mouse down was not just a select click 1341 } 1342 _dragging = true; 1343 1344 if (_circuitBuilder.doMouseDragged(_currentSelection, event)) { 1345 return; 1346 } 1347 1348 if (!event.isPopupTrigger() && !event.isMetaDown() && !event.isAltDown() && !_shapeDrawer.doMouseDragged(event) 1349 && (isEditable() || _currentSelection instanceof LocoIcon)) { 1350 moveIt: 1351 if (_currentSelection != null && getFlag(OPTION_POSITION, _currentSelection.isPositionable())) { 1352 int deltaX = event.getX() - _lastX; 1353 int deltaY = event.getY() - _lastY; 1354 int minX = getItemX(_currentSelection, deltaX); 1355 int minY = getItemY(_currentSelection, deltaY); 1356 if (_selectionGroup != null && _selectionGroup.contains(_currentSelection)) { 1357 for (Positionable comp : _selectionGroup) { 1358 minX = Math.min(getItemX(comp, deltaX), minX); 1359 minY = Math.min(getItemY(comp, deltaY), minY); 1360 } 1361 } 1362 if (minX < 0 || minY < 0) { 1363 // Don't allow move beyond the left or top borders 1364 break moveIt; 1365 /* 1366 // or use this choice: 1367 // Expand the panel to the left or top as needed by the move 1368 // Probably not the preferred solution - use the above break 1369 if (_selectionGroup!=null && _selectionGroup.contains(_currentSelection)) { 1370 List <Positionable> allItems = getContents(); 1371 for (int i=0; i<allItems.size(); i++){ 1372 moveItem(allItems.get(i), -deltaX, -deltaY); 1373 } 1374 } else { 1375 moveItem(_currentSelection, -deltaX, -deltaY); 1376 } 1377 */ 1378 } 1379 if (_selectionGroup != null && _selectionGroup.contains(_currentSelection) 1380 && !_circuitBuilder.dragPortal()) { 1381 for (Positionable comp : _selectionGroup) { 1382 moveItem(comp, deltaX, deltaY); 1383 } 1384 _highlightcomponent = null; 1385 } else { 1386 moveItem(_currentSelection, deltaX, deltaY); 1387 } 1388 } else if ((isEditable() && _selectionGroup == null)) { 1389 drawSelectRect(event.getX(), event.getY()); 1390 } 1391 } 1392 _highlightGroup.clear(); 1393 _lastX = event.getX(); 1394 _lastY = event.getY(); 1395 _targetPanel.repaint(); // needed for ToolTip 1396 } 1397 1398 @Override 1399 public void mouseMoved(JmriMouseEvent event) { 1400 //if (_debug) log.debug("mouseMoved at ("+event.getX()+","+event.getY()+")"); 1401 if (_dragging || event.isPopupTrigger() || event.isMetaDown() || event.isAltDown()) { 1402 return; 1403 } 1404 if (!(event.isShiftDown() && event.isControlDown()) && !_shapeDrawer.doMouseMoved(event)) { 1405 Positionable selection = getCurrentSelection(event); 1406 if (selection != null && selection.getDisplayLevel() > BKG && selection.showToolTip()) { 1407 showToolTip(selection, event); 1408 //selection.highlightlabel(true); 1409 } else { 1410 setToolTip(null); 1411 } 1412 } 1413 _targetPanel.repaint(); 1414 } 1415 1416 @Override 1417 public void mouseEntered(JmriMouseEvent event) { 1418 _targetPanel.repaint(); 1419 } 1420 1421 @Override 1422 public void mouseExited(JmriMouseEvent event) { 1423 setToolTip(null); 1424 _targetPanel.repaint(); // needed for ToolTip 1425 } 1426 1427 ////////////////// implementation of Abstract Editor methods ////////////////// 1428 /** 1429 * The target window has been requested to close, don't delete it at this 1430 * time. Deletion must be accomplished via the Delete this panel menu item. 1431 * 1432 * @param e the triggering event 1433 */ 1434 @Override 1435 protected void targetWindowClosingEvent(java.awt.event.WindowEvent e) { 1436 targetWindowClosing(); 1437 } 1438 1439 @Override 1440 protected void paintTargetPanel(Graphics g) { 1441 // needed to create PositionablePolygon 1442 _shapeDrawer.paint(g); // adds to rubber band line 1443 1444 if (!_highlightGroup.isEmpty()) { 1445 g.setColor(((TargetPane) getTargetPanel()).getHighlightColor()); 1446 for (Rectangle r : _highlightGroup) { 1447 g.drawRect(r.x, r.y, r.width, r.height); 1448 } 1449 } 1450 } 1451 1452 /** 1453 * Set an object's location when it is created. 1454 */ 1455 @Override 1456 public void setNextLocation(Positionable obj) { 1457 obj.setLocation(0, 0); 1458 } 1459 1460 /** 1461 * Set up selections for a paste. Note a copy of _selectionGroup is made 1462 * that is NOT in the _contents. This disconnected ArrayList is added to the 1463 * _contents when (if) a paste is made. The disconnected _selectionGroup can 1464 * be dragged to a new location. 1465 */ 1466 @Override 1467 protected void copyItem(Positionable p) { 1468 if (log.isDebugEnabled()) { // avoid string concatination if not debug 1469 log.debug("Enter copyItem: _selectionGroup size={}", 1470 _selectionGroup != null ? _selectionGroup.size() : "(null)"); 1471 } 1472 // If popup menu hit again, Paste selections and make another copy 1473 if (_pastePending) { 1474 pasteItems(); 1475 } 1476 if (_selectionGroup != null && !_selectionGroup.contains(p)) { 1477 deselectSelectionGroup(); 1478 } 1479 if (_selectionGroup == null) { 1480 _selectionGroup = new ArrayList<>(); 1481 _selectionGroup.add(p); 1482 } 1483 ArrayList<Positionable> selectionGroup = new ArrayList<>(); 1484 for (Positionable comp : _selectionGroup) { 1485 Positionable pos = comp.deepClone(); 1486 selectionGroup.add(pos); 1487 } 1488 _selectionGroup = selectionGroup; // group is now disconnected 1489 _pastePending = true; 1490 log.debug("Exit copyItem: _selectionGroup.size()={}", _selectionGroup.size()); 1491 } 1492 1493 void pasteItems() { 1494 if (_selectionGroup != null) { 1495 for (Positionable pos : _selectionGroup) { 1496 if (pos instanceof PositionableIcon) { 1497 jmri.NamedBean bean = pos.getNamedBean(); 1498 if (bean != null) { 1499 ((PositionableIcon) pos).displayState(bean.getState()); 1500 } 1501 } 1502 try { 1503 pos.setId(null); 1504 putItem(pos); 1505 } catch (Positionable.DuplicateIdException e) { 1506 // This should never happen 1507 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 1508 } 1509 log.debug("Add {}", pos.getNameString()); 1510 } 1511 if (_selectionGroup.get(0) instanceof LocoIcon) { 1512 LocoIcon p = (LocoIcon) _selectionGroup.get(0); 1513 CoordinateEdit f = new CoordinateEdit(); 1514 f.init("Train Name", p, false); 1515 f.initText(); 1516 f.setVisible(true); 1517 f.setLocationRelativeTo(p); 1518 } 1519 } 1520 _pastePending = false; 1521 } 1522 1523 /** 1524 * Showing the popup of a member of _selectionGroup causes an image to be 1525 * placed in to the _targetPanel. If the objects are not put into _contents 1526 * (putItem(p)) the image will persist. Thus set these transitory object 1527 * invisible. 1528 */ 1529 void abortPasteItems() { 1530 if (log.isDebugEnabled()) { // avoid string concatination if not debug 1531 log.debug("abortPasteItems: _selectionGroup{}", 1532 _selectionGroup == null ? "=null" : (".size=" + _selectionGroup.size())); 1533 } 1534 if (_selectionGroup != null) { 1535 for (Positionable comp : _selectionGroup) { 1536 comp.setVisible(false); 1537 comp.remove(); 1538 } 1539 } 1540 deselectSelectionGroup(); 1541 _pastePending = false; 1542 } 1543 1544 /** 1545 * Add an action to copy the Positionable item and the group to which is may 1546 * belong. 1547 * 1548 * @param p the copyable item 1549 * @param popup the menu to add it to 1550 */ 1551 public void setCopyMenu(Positionable p, JPopupMenu popup) { 1552 JMenuItem edit = new JMenuItem(Bundle.getMessage("MenuItemDuplicate")); 1553 edit.addActionListener(new ActionListener() { 1554 Positionable comp; 1555 1556 @Override 1557 public void actionPerformed(ActionEvent e) { 1558 copyItem(comp); 1559 } 1560 1561 ActionListener init(Positionable pos) { 1562 comp = pos; 1563 return this; 1564 } 1565 }.init(p)); 1566 popup.add(edit); 1567 } 1568 1569 @Override 1570 protected void setSelectionsScale(double s, Positionable p) { 1571 if (_circuitBuilder.saveSelectionGroup(_selectionGroup)) { 1572 p.setScale(s); 1573 } else { 1574 super.setSelectionsScale(s, p); 1575 } 1576 } 1577 1578 @Override 1579 protected void setSelectionsRotation(int k, Positionable p) { 1580 if (_circuitBuilder.saveSelectionGroup(_selectionGroup)) { 1581 p.rotate(k); 1582 } else { 1583 super.setSelectionsRotation(k, p); 1584 } 1585 } 1586 1587 /** 1588 * Create popup for a Positionable object. Popup items common to all 1589 * positionable objects are done before and after the items that pertain 1590 * only to specific Positionable types. 1591 * 1592 * @param p the item containing or requiring the context menu 1593 * @param event the event triggering the menu 1594 */ 1595 protected void showPopUp(Positionable p, JmriMouseEvent event) { 1596 if (!((JComponent) p).isVisible()) { 1597 return; // component must be showing on the screen to determine its location 1598 } 1599 JPopupMenu popup = new JPopupMenu(); 1600 1601 PositionablePopupUtil util = p.getPopupUtility(); 1602 if (p.isEditable()) { 1603 // items common to all 1604 if (p.doViemMenu()) { 1605 popup.add(p.getNameString()); 1606 setPositionableMenu(p, popup); 1607 if (p.isPositionable()) { 1608 setShowCoordinatesMenu(p, popup); 1609 setShowAlignmentMenu(p, popup); 1610 } 1611 setDisplayLevelMenu(p, popup); 1612 setHiddenMenu(p, popup); 1613 setEmptyHiddenMenu(p, popup); 1614 setValueEditDisabledMenu(p, popup); 1615 setEditIdMenu(p, popup); 1616 setEditClassesMenu(p, popup); 1617 popup.addSeparator(); 1618 setLogixNGPositionableMenu(p, popup); 1619 popup.addSeparator(); 1620 setCopyMenu(p, popup); 1621 } 1622 1623 // items with defaults or using overrides 1624 boolean popupSet = false; 1625// popupSet |= p.setRotateOrthogonalMenu(popup); 1626 popupSet |= p.setRotateMenu(popup); 1627 popupSet |= p.setScaleMenu(popup); 1628 if (popupSet) { 1629 popup.addSeparator(); 1630 popupSet = false; 1631 } 1632 popupSet = p.setEditItemMenu(popup); 1633 if (popupSet) { 1634 popup.addSeparator(); 1635 popupSet = false; 1636 } 1637 if (p instanceof PositionableLabel) { 1638 PositionableLabel pl = (PositionableLabel) p; 1639 if (pl.isText()) { 1640 setColorMenu(popup, (JComponent) p, ColorDialog.BORDER); 1641 setColorMenu(popup, (JComponent) p, ColorDialog.MARGIN); 1642 setColorMenu(popup, (JComponent) p, ColorDialog.FONT); 1643 if (!(pl instanceof ReporterIcon) && !(pl instanceof RpsPositionIcon)) { 1644 popupSet |= pl.setEditTextItemMenu(popup); 1645 } 1646 } 1647 } else if (p instanceof PositionableJPanel) { 1648 setColorMenu(popup, (JComponent) p, ColorDialog.BORDER); 1649 setColorMenu(popup, (JComponent) p, ColorDialog.MARGIN); 1650 setColorMenu(popup, (JComponent) p, ColorDialog.FONT); 1651 PositionableJPanel pj = (PositionableJPanel) p; 1652 popupSet |= pj.setEditTextItemMenu(popup); 1653 } 1654 if (p instanceof LinkingObject) { 1655 ((LinkingObject) p).setLinkMenu(popup); 1656 } 1657 if (popupSet) { 1658 popup.addSeparator(); 1659 popupSet = false; 1660 } 1661 p.setDisableControlMenu(popup); 1662 if (util != null) { 1663 util.setAdditionalEditPopUpMenu(popup); 1664 } 1665 // for Positionables with unique settings 1666 p.showPopUp(popup); 1667 1668 if (p.doViemMenu()) { 1669 setShowToolTipMenu(p, popup); 1670 setRemoveMenu(p, popup); 1671 } 1672 } else { 1673 if ((p instanceof LocoIcon) && !isLocoMarkerPopupDisabled()) { 1674 setCopyMenu(p, popup); 1675 } 1676 p.showPopUp(popup); 1677 if (util != null) { 1678 util.setAdditionalViewPopUpMenu(popup); 1679 } 1680 } 1681 popup.show((Component) p, p.getWidth() / 2 + (int) ((getPaintScale() - 1.0) * p.getX()), 1682 p.getHeight() / 2 + (int) ((getPaintScale() - 1.0) * p.getY())); 1683 1684 _currentSelection = null; 1685 } 1686 1687 public void setColorMenu(JPopupMenu popup, JComponent pos, int type) { 1688 String title; 1689 switch (type ) { 1690 case ColorDialog.BORDER: 1691 title = "SetBorderSizeColor"; 1692 break; 1693 case ColorDialog.MARGIN: 1694 title = "SetMarginSizeColor"; 1695 break; 1696 case ColorDialog.FONT: 1697 title = "SetFontSizeColor"; 1698 break; 1699 case ColorDialog.TEXT: 1700 title = "SetTextSizeColor"; 1701 break; 1702 default: 1703 title = "untitled"; 1704 return; 1705 } 1706 JMenuItem edit = new JMenuItem(Bundle.getMessage(title)); 1707 edit.addActionListener((ActionEvent event) -> new ColorDialog(this, pos, type, null)); 1708 popup.add(edit); 1709 } 1710 1711 /** 1712 * ******************* Circuitbuilder *********************************** 1713 */ 1714 protected void disableMenus() { 1715 _drawMenu.setEnabled(false); 1716 _warrantMenu.setEnabled(false); 1717 _iconMenu.setEnabled(false); 1718 _zoomMenu.setEnabled(false); 1719 _optionMenu.setEnabled(false); 1720 _editMenu.setEnabled(false); 1721 _fileMenu.setEnabled(false); 1722 _disablePortalSelection = false; 1723 } 1724 1725 public void resetEditor() { 1726 // enable menus 1727 _drawMenu.setEnabled(true); 1728 _warrantMenu.setEnabled(true); 1729 _iconMenu.setEnabled(true); 1730 _zoomMenu.setEnabled(true); 1731 _optionMenu.setEnabled(true); 1732 _editMenu.setEnabled(true); 1733 _fileMenu.setEnabled(true); 1734 // reset colors 1735 highlight(null); 1736 TargetPane targetPane = (TargetPane) getTargetPanel(); 1737 targetPane.setDefaultColors(); 1738 targetPane.revalidate(); 1739 setSelectionGroup(null); 1740 _disablePortalSelection = true; 1741 } 1742 1743 /** 1744 * Highlight an item. 1745 * 1746 * @param pos the item to hightlight 1747 */ 1748 protected void highlight(Positionable pos) { 1749 if (pos == null) { 1750 _highlightGroup.clear(); 1751 _highlightcomponent = null; 1752 } else { 1753 Rectangle rect = new Rectangle(pos.getX(), pos.getY(), 1754 pos.maxWidth(), pos.maxHeight()); 1755 _highlightcomponent = rect; 1756 if (!_dragging) { 1757 _highlightGroup.add(rect); 1758 } 1759 } 1760 repaint(); 1761 } 1762 1763 protected void setSelectionGroup(ArrayList<Positionable> group) { 1764 _highlightcomponent = null; 1765 _selectionGroup = group; 1766 repaint(); 1767 } 1768 1769 protected ArrayList<Positionable> getSelectionGroup() { 1770 return _selectionGroup; 1771 } 1772 1773 /** 1774 * ************************** DnD ************************************* 1775 */ 1776 protected void makeDataFlavors() { 1777 try { 1778 _positionableDataFlavor = new DataFlavor(POSITIONABLE_FLAVOR); 1779 _namedIconDataFlavor = new DataFlavor(ImageIndexEditor.IconDataFlavorMime); 1780 _positionableListDataFlavor = new DataFlavor(List.class, "JComponentList"); 1781 } catch (ClassNotFoundException cnfe) { 1782 log.error("Unable to find class supporting {}", ImageIndexEditor.IconDataFlavorMime, cnfe); 1783 } 1784 new DropTarget(this, DnDConstants.ACTION_COPY_OR_MOVE, this); 1785 } 1786 1787 DataFlavor _positionableDataFlavor; 1788 DataFlavor _positionableListDataFlavor; 1789 DataFlavor _namedIconDataFlavor; 1790 1791 /** 1792 * ************************* DropTargetListener *********************** 1793 */ 1794 @Override 1795 public void dragExit(DropTargetEvent evt) { 1796 } 1797 1798 @Override 1799 public void dragEnter(DropTargetDragEvent evt) { 1800 } 1801 1802 @Override 1803 public void dragOver(DropTargetDragEvent evt) { 1804 } 1805 1806 @Override 1807 public void dropActionChanged(DropTargetDragEvent evt) { 1808 } 1809 1810 @SuppressWarnings("unchecked") 1811 @Override 1812 public void drop(DropTargetDropEvent evt) { 1813 try { 1814 //Point pt = evt.getLocation(); coords relative to entire window 1815 Point pt = _targetPanel.getMousePosition(true); 1816 if (pt == null) { 1817 return; 1818 } 1819 Transferable tr = evt.getTransferable(); 1820 if (log.isDebugEnabled()) { // avoid string building if not debug 1821 DataFlavor[] flavors = tr.getTransferDataFlavors(); 1822 StringBuilder flavor = new StringBuilder(); 1823 for (DataFlavor flavor1 : flavors) { 1824 flavor.append(flavor1.getRepresentationClass().getName()).append(", "); 1825 } 1826 log.debug("Editor Drop: flavor classes={}", flavor); 1827 } 1828 if (tr.isDataFlavorSupported(_positionableDataFlavor)) { 1829 Positionable item = (Positionable) tr.getTransferData(_positionableDataFlavor); 1830 if (item == null) { 1831 return; 1832 } 1833 item.setLocation((int) Math.round(pt.x/getPaintScale()), (int) Math.round(pt.y/getPaintScale())); 1834 // now set display level in the pane. 1835 item.setDisplayLevel(item.getDisplayLevel()); 1836 item.setEditor(this); 1837 try { 1838 putItem(item); 1839 } catch (Positionable.DuplicateIdException e) { 1840 // This should never happen 1841 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 1842 } 1843 item.updateSize(); 1844 _circuitBuilder.doMouseReleased(item, true); 1845 evt.dropComplete(true); 1846 return; 1847 } else if (tr.isDataFlavorSupported(_namedIconDataFlavor)) { 1848 NamedIcon newIcon = new NamedIcon((NamedIcon) tr.getTransferData(_namedIconDataFlavor)); 1849 String url = newIcon.getURL(); 1850 NamedIcon icon = NamedIcon.getIconByName(url); 1851 PositionableLabel ni = new PositionableLabel(icon, this); 1852 // infer a background icon from its size 1853 assert icon != null; 1854 if (icon.getIconHeight() > 500 || icon.getIconWidth() > 600) { 1855 ni.setDisplayLevel(BKG); 1856 } else { 1857 ni.setDisplayLevel(ICONS); 1858 } 1859 ni.setLocation((int) Math.round(pt.x/getPaintScale()), (int) Math.round(pt.y/getPaintScale())); 1860 ni.setEditor(this); 1861 try { 1862 putItem(ni); 1863 } catch (Positionable.DuplicateIdException e) { 1864 // This should never happen 1865 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 1866 } 1867 ni.updateSize(); 1868 evt.dropComplete(true); 1869 return; 1870 } else if (tr.isDataFlavorSupported(DataFlavor.stringFlavor)) { 1871 String text = (String) tr.getTransferData(DataFlavor.stringFlavor); 1872 PositionableLabel l = new PositionableLabel(text, this); 1873 l.setSize(l.getPreferredSize().width, l.getPreferredSize().height); 1874 l.setDisplayLevel(LABELS); 1875 l.setLocation((int) Math.round(pt.x/getPaintScale()), (int) Math.round(pt.y/getPaintScale())); 1876 l.setEditor(this); 1877 try { 1878 putItem(l); 1879 } catch (Positionable.DuplicateIdException e) { 1880 // This should never happen 1881 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 1882 } 1883 evt.dropComplete(true); 1884 } else if (tr.isDataFlavorSupported(_positionableListDataFlavor)) { 1885 List<Positionable> dragGroup 1886 = (List<Positionable>) tr.getTransferData(_positionableListDataFlavor); 1887 for (Positionable pos : dragGroup) { 1888 pos.setEditor(this); 1889 pos.setLocation((int) Math.round(pt.x/getPaintScale()), (int) Math.round(pt.y/getPaintScale())); 1890 try { 1891 putItem(pos); 1892 } catch (Positionable.DuplicateIdException ignore) { 1893 try { 1894 // Duplicate id so clear the id 1895 pos.setId(null); 1896 putItem(pos); 1897 } catch (Positionable.DuplicateIdException e) { 1898 // This should never happen 1899 log.error("Editor.putItem() with null id has thrown DuplicateIdException", e); 1900 } 1901 } 1902 pos.updateSize(); 1903 log.debug("DnD Add {}", pos.getNameString()); 1904 } 1905 } else { 1906 log.warn("Editor DropTargetListener supported DataFlavors not available at drop from {}", tr.getClass().getName()); 1907 } 1908 } catch (IOException ioe) { 1909 log.warn("Editor DropTarget caught IOException", ioe); 1910 } catch (UnsupportedFlavorException ufe) { 1911 log.warn("Editor DropTarget caught UnsupportedFlavorException", ufe); 1912 } 1913 log.debug("Editor DropTargetListener drop REJECTED!"); 1914 evt.rejectDrop(); 1915 } 1916 1917 protected static class PositionableListDnD implements Transferable { 1918// ControlPanelEditor _sourceEditor; 1919 1920 List<Positionable> _sourceEditor; 1921 DataFlavor _dataFlavor; 1922 1923 PositionableListDnD(List<Positionable> source) { 1924 _sourceEditor = source; 1925 _dataFlavor = new DataFlavor(List.class, "JComponentList"); 1926 } 1927 1928 @Override 1929 @Nonnull 1930 public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { 1931 log.debug("PositionableListDnD.getTransferData:"); 1932 if (flavor.equals(_dataFlavor)) { 1933 return _sourceEditor; 1934 } 1935 throw new UnsupportedFlavorException(flavor); 1936 } 1937 1938 @Override 1939 public DataFlavor[] getTransferDataFlavors() { 1940 return new DataFlavor[]{_dataFlavor}; 1941 } 1942 1943 @Override 1944 public boolean isDataFlavorSupported(DataFlavor flavor) { 1945 return flavor.equals(_dataFlavor); 1946 } 1947 } 1948 1949 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ControlPanelEditor.class); 1950 1951}