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