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