001package jmri.jmrit.display.palette; 002 003import java.awt.Color; 004import java.awt.Dimension; 005import java.awt.datatransfer.DataFlavor; 006import java.awt.datatransfer.Transferable; 007import java.awt.datatransfer.UnsupportedFlavorException; 008import java.awt.dnd.DnDConstants; 009import java.awt.dnd.DropTarget; 010import java.awt.dnd.DropTargetAdapter; 011import java.awt.dnd.DropTargetDropEvent; 012import java.awt.event.ActionListener; 013import java.io.IOException; 014import java.util.HashMap; 015 016import javax.annotation.Nonnull; 017import javax.swing.BorderFactory; 018import javax.swing.Box; 019import javax.swing.BoxLayout; 020import javax.swing.Icon; 021import javax.swing.JButton; 022import javax.swing.JLabel; 023import javax.swing.JPanel; 024 025import jmri.jmrit.catalog.CatalogPanel; 026import jmri.jmrit.catalog.DragJLabel; 027import jmri.jmrit.catalog.ImageIndexEditor; 028import jmri.jmrit.catalog.NamedIcon; 029import jmri.jmrit.display.DisplayFrame; 030import jmri.jmrit.display.Editor; 031import jmri.jmrit.display.LinkingLabel; 032import jmri.jmrit.display.PositionableLabel; 033import jmri.util.swing.ImagePanel; 034import jmri.util.swing.JmriJOptionPane; 035import jmri.util.swing.JmriMouseAdapter; 036import jmri.util.swing.JmriMouseEvent; 037import jmri.util.swing.JmriMouseListener; 038 039/** 040 * ItemPanel for plain Icons and Backgrounds. 041 * Does NOT use IconDialog class to add, replace or delete icons. 042 * @see ItemPanel palette class diagram 043 */ 044public class IconItemPanel extends ItemPanel { 045 046 protected JButton _catalogButton; 047 protected JButton _deleteIconButton; 048 protected CatalogPanel _catalog; 049 protected IconDisplayPanel _selectedIcon; 050 protected DataFlavor _positionableDataFlavor; 051 protected DataFlavor _namedIconDataFlavor; 052 protected int _level = Editor.ICONS; // sub classes can override (e.g. Background) 053 private NamedIcon _updateIcon; 054 055 /** 056 * Constructor for plain icons and backgrounds. 057 * 058 * @param type type 059 * @param parentFrame parentFrame 060 */ 061 public IconItemPanel(DisplayFrame parentFrame, String type) { 062 super(parentFrame, type); 063 setToolTipText(Bundle.getMessage("ToolTipDragIcon")); 064 } 065 066 @Override 067 public void init() { 068 if (!_initialized) { 069 super.init(); 070 initLinkPanel(); 071 _catalog = makeCatalog(); 072 add(_catalog); 073 } 074 } 075 076 /** 077 * Init for update of existing palette item type. 078 * 079 * @param doneAction doneAction 080 */ 081 public void init(ActionListener doneAction) { 082 _update = true; 083 _doneAction = doneAction; 084 _suppressDragging = true; // no dragging when updating 085 initIconFamiliesPanel(); 086 add(_iconPanel); 087 _catalog = makeCatalog(); 088 add(_catalog); 089 } 090 091 @Override 092 protected JPanel instructions() { 093 JPanel blurb = new JPanel(); 094 blurb.setLayout(new BoxLayout(blurb, BoxLayout.Y_AXIS)); 095 blurb.add(new JLabel(Bundle.getMessage("DragIconPanel"))); 096 blurb.add(new JLabel(Bundle.getMessage("DragIconCatalog"))); 097 JPanel panel = new JPanel(); 098 panel.add(blurb); 099 return panel; 100 } 101 102 protected CatalogPanel makeCatalog() { 103 CatalogPanel catalog = CatalogPanel.makeDefaultCatalog(false, false, true); 104 ImagePanel panel = catalog.getPreviewPanel(); 105 if (!_update) { 106 panel.setImage(_frame.getPreviewBackground()); 107 } else { 108 panel.setImage(_frame.getBackground(0)); //update always should be the panel background 109 catalog.setParent(this); 110 } 111 catalog.setToolTipText(Bundle.getMessage("ToolTipDragCatalog")); 112 catalog.setVisible(false); 113 return catalog; 114 } 115 116 @Override 117 protected void previewColorChange() { 118 if (_initialized) { 119 ImagePanel iconPanel = _catalog.getPreviewPanel(); 120 if (iconPanel != null) { 121 iconPanel.setImage(_frame.getPreviewBackground()); 122 } 123 } 124 super.previewColorChange(); 125 } 126 127 /* 128 * Plain icons have only one family, usually named "set". 129 * Override for plain icon and background and put all icons here. 130 */ 131 @Override 132 protected void initIconFamiliesPanel() { 133 super.initIconFamiliesPanel(); 134 if (!_update) { 135 _iconPanel.addMouseListener(JmriMouseListener.adapt(new IconListener())); 136 } 137 } 138 139 @Override 140 protected void makeFamiliesPanel() { 141 if (!_update) { 142 HashMap<String, HashMap<String, NamedIcon>> families = ItemPalette.getFamilyMaps(_itemType); 143 log.debug("makeFamiliesPanel Num families= {}", families.size()); 144 _currentIconMap = families.get("set"); 145 if (_currentIconMap == null) { 146 _currentIconMap = new HashMap<>(); 147 if (families.size() != 0) { 148 log.error("{} Unknown families found for {}", families.size(), _itemType); 149 } 150 } 151 makeDataFlavors(); 152 } else { 153 _currentIconMap = new HashMap<>(); 154 } 155 addIconsToPanel(); 156 makePreviewPanel(true, null); 157 } 158 private void addIconsToPanel() { 159 boolean isEmpty = _currentIconMap.isEmpty(); 160 if (_bottomPanel == null) { 161 makeBottomPanel(isEmpty); 162 } else { 163 if (isEmpty ^ _wasEmpty) { 164 remove(_bottomPanel); 165 makeBottomPanel(isEmpty); 166 } 167 } 168 _wasEmpty = isEmpty; 169 addIconsToPanel(_currentIconMap, _iconPanel, _update); 170 } 171 172 private void makeDataFlavors() { 173 try { 174 _positionableDataFlavor = new DataFlavor(Editor.POSITIONABLE_FLAVOR); 175 _namedIconDataFlavor = new DataFlavor(ImageIndexEditor.IconDataFlavorMime); 176 } catch (ClassNotFoundException cnfe) { 177 log.error("Unable to find class supporting {}", ImageIndexEditor.IconDataFlavorMime, cnfe); 178 } 179 if (!_update) { 180 new DropTarget(_iconPanel, DnDConstants.ACTION_COPY_OR_MOVE, new ADropTargetListener()); 181 } 182 } 183 184 @Override 185 protected JPanel makeIconDisplayPanel(String key, HashMap<String, NamedIcon> iconMap, boolean dropIcon) { 186 NamedIcon icon = iconMap.get(key); 187 return new IconDisplayPanel(key, icon, dropIcon); 188 } 189 190 @Override 191 protected JPanel makeItemButtonPanel() { 192 JPanel panel = new JPanel(); 193 panel.add(makeCatalogButton()); 194 if (_update) { 195 return panel; 196 } 197 JButton renameButton = new JButton(Bundle.getMessage("RenameIcon")); 198 renameButton.addActionListener(a -> renameIcon()); 199 panel.add(renameButton); 200 201 _deleteIconButton = new JButton(Bundle.getMessage("deleteIcon")); 202 _deleteIconButton.addActionListener(a -> deleteIcon()); 203 _deleteIconButton.setToolTipText(Bundle.getMessage("ToolTipDeleteIcon")); 204 panel.add(_deleteIconButton); 205 return panel; 206 } 207 208 private JButton makeCatalogButton() { 209 if (_catalogButton == null) { 210 _catalogButton = new JButton(Bundle.getMessage("ButtonShowCatalog")); 211 _catalogButton.addActionListener(a -> { 212 if (_catalog.isVisible()) { 213 hideCatalog(); 214 } else { 215 showCatalog(); 216 } 217 }); 218 _catalogButton.setToolTipText(Bundle.getMessage("ToolTipCatalog")); 219 } 220 return _catalogButton; 221 } 222 223 /** 224 * Replacement panel for _bottomPanel when no icon families exist for 225 * _itemType. 226 */ 227 @Override 228 protected JPanel makeSpecialBottomPanel(boolean update) { 229 JPanel _bottom2Panel = new JPanel(); 230 _bottom2Panel.add(makeCatalogButton()); 231 232 if(!update) { 233 JButton button = new JButton(Bundle.getMessage("RestoreDefault")); 234 button.addActionListener(a -> loadDefaultType()); 235 _bottom2Panel.add(button); 236 } 237 return _bottom2Panel; 238 } 239 240 protected void hideCatalog() { 241 Dimension oldDim = getSize(); 242 boolean isPalette = (_frame instanceof ItemPalette); 243 Dimension totalDim = _frame.getSize(); 244 _catalog.setVisible(false); 245 _catalog.invalidate(); 246 reSizeDisplay(isPalette, oldDim, totalDim); 247 _catalogButton.setText(Bundle.getMessage("ButtonShowCatalog")); 248 } 249 250 protected void showCatalog() { 251 Dimension oldDim = getSize(); 252 boolean isPalette = (_frame instanceof ItemPalette); 253 Dimension totalDim = _frame.getSize(); 254 _catalog.setVisible(true); 255 _catalog.invalidate(); 256 reSizeDisplay(isPalette, oldDim, totalDim); 257 _catalogButton.setText(Bundle.getMessage("HideCatalog")); 258 } 259 260 protected void putIcon(String name, NamedIcon icon) { 261 _currentIconMap.put(name, icon); 262 log.debug("putIcon {}", name); 263 updateIcons(); 264 } 265 266 /** 267 * Action item for makeBottomPanel. 268 */ 269 protected void deleteIcon() { 270 if (_selectedIcon == null) { 271 JmriJOptionPane.showMessageDialog(_frame, Bundle.getMessage("ToSelectIcon"), 272 Bundle.getMessage("ReminderTitle"), JmriJOptionPane.INFORMATION_MESSAGE); 273 return; 274 } 275 log.debug("deleteIcon {}", _selectedIcon._key); 276 _currentIconMap.remove(_selectedIcon._key); 277 updateIcons(); 278 } 279 280 @Override 281 protected void hideIcons() { 282 } 283 284 /* 285 * Family 'set' has changed 286 */ 287 private void updateIcons() { 288 _previewPanel.setVisible(false); // necessary to guarantee _iconPanel gets refreshed 289 addIconsToPanel(); 290 Dimension oldDim = getSize(); 291 boolean isPalette = (_frame instanceof ItemPalette); 292 Dimension totalDim = _frame.getSize(); 293 _previewPanel.setVisible(true); 294 if (!_update) { 295 ItemPalette.removeIconMap(_itemType, "set"); 296 ItemPalette.addFamily(_itemType, "set", _currentIconMap); 297 } 298 _iconPanel.invalidate(); 299 reSizeDisplay(isPalette, oldDim, totalDim); 300 } 301 302 private void renameIcon() { 303 if (_selectedIcon != null) { 304 String name = JmriJOptionPane.showInputDialog(_frame, Bundle.getMessage("NoIconName"), 305 Bundle.getMessage("QuestionTitle"), JmriJOptionPane.QUESTION_MESSAGE); 306 if (name != null) { 307 _currentIconMap.remove(_selectedIcon._key); 308 putIcon(name, _selectedIcon._icon); 309 _selectedIcon._key = name; 310 deselectIcon(); 311 } 312 } else { 313 JmriJOptionPane.showMessageDialog(_frame, Bundle.getMessage("ToSelectIcon"), 314 Bundle.getMessage("ReminderTitle"), JmriJOptionPane.INFORMATION_MESSAGE); 315 } 316 } 317 318 protected void setSelection(@Nonnull IconDisplayPanel panel) { 319 if (_selectedIcon != null && !panel.equals(_selectedIcon)) { 320 deselectIcon(); 321 } 322 String borderName = ItemPalette.convertText(panel._key); 323 panel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.red, 2), borderName)); 324 _selectedIcon = panel; 325 _catalog.deselectIcon(); 326 } 327 328 public void deselectIcon() { 329 if (_selectedIcon != null) { 330 String borderName = ItemPalette.convertText(_selectedIcon._key); 331 _selectedIcon.setBorder(BorderFactory.createTitledBorder( 332 BorderFactory.createLineBorder(Color.black, 1), borderName)); 333 _selectedIcon = null; 334 } 335 } 336 337 protected String setIconName(String name) { 338 name = JmriJOptionPane.showInputDialog(this, 339 Bundle.getMessage("NoIconName"), name); 340 if (name == null || name.trim().length() == 0) { 341 return null; 342 } 343 while (_currentIconMap.get(name) != null) { 344 JmriJOptionPane.showMessageDialog(this, 345 Bundle.getMessage("DuplicateIconName", name), 346 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 347 name = JmriJOptionPane.showInputDialog(this, 348 Bundle.getMessage("NoIconName"), name); 349 if (name == null || name.trim().length() == 0) { 350 return null; 351 } 352 } 353 return name; 354 } 355 356 protected void initLinkPanel() { 357 JPanel blurb = new JPanel(); 358 blurb.setLayout(new BoxLayout(blurb, BoxLayout.Y_AXIS)); 359 blurb.add(Box.createVerticalStrut(ItemPalette.STRUT_SIZE)); 360 blurb.add(new JLabel(Bundle.getMessage("ToLinkToURL", "Text"))); 361 blurb.add(new JLabel(Bundle.getMessage("enterPanel"))); 362 blurb.add(new JLabel(Bundle.getMessage("enterURL"))); 363 blurb.add(Box.createVerticalStrut(ItemPalette.STRUT_SIZE)); 364 blurb.add(new JLabel(Bundle.getMessage("LinkName"))); 365 blurb.add(_linkName); 366 _linkName.setToolTipText(Bundle.getMessage("ToolTipLink")); 367 blurb.setToolTipText(Bundle.getMessage("ToolTipLink")); 368 JPanel panel = new JPanel(); 369 panel.add(blurb); 370 JPanel linkPanel = new JPanel(); 371 linkPanel.add(panel); 372 add(linkPanel); 373 } 374 375 /* 376 * edit a PositionableLabel's icon 377 * @param icon the icon to be update/changed 378 */ 379 public void setUpdateIcon(NamedIcon icon) { 380 if (!_update || icon == null) { 381 return; 382 } 383 _updateIcon = icon; 384 String name = icon.getName(); 385 if (name == null) { 386 name =Bundle.getMessage("unNamed"); 387 } else { 388 java.io.File f = new java.io.File(name); 389 name = f.getName(); 390 int index = name.indexOf('.'); 391 if (index > 0) { 392 name = name.substring(0, index); 393 } 394 } 395 _currentIconMap.put(name, icon); 396 updateIcons(); 397 } 398 399 /* 400 * edit a PositionableLabel's icon 401 * @return return update/changed icon 402 */ 403 public NamedIcon getUpdateIcon() { 404 return _updateIcon; 405 } 406 407 class ADropTargetListener extends DropTargetAdapter { 408 ADropTargetListener() { 409 super(); 410 } 411 412 @Override 413 public void drop(DropTargetDropEvent e) { 414 boolean accepted = false; 415 try { 416 Transferable tr = e.getTransferable(); 417 if (e.isDataFlavorSupported(_positionableDataFlavor)) { 418 PositionableLabel label = (PositionableLabel)tr.getTransferData(_positionableDataFlavor); 419 NamedIcon newIcon = new NamedIcon((NamedIcon)label.getIcon()); 420 accepted = accept(label.getName(), newIcon); 421 } else if (e.isDataFlavorSupported(_namedIconDataFlavor)) { 422 NamedIcon icon = (NamedIcon) tr.getTransferData(_namedIconDataFlavor); 423 NamedIcon newIcon = new NamedIcon(icon); 424 accepted = accept(icon.getName(), newIcon); 425 } else if (e.isDataFlavorSupported(DataFlavor.stringFlavor)) { 426 String text = (String) tr.getTransferData(DataFlavor.stringFlavor); 427 log.debug("drop for stringFlavor {}", text); 428 NamedIcon newIcon = new NamedIcon(text, text); 429 accepted = accept(Bundle.getMessage("unNamed"), newIcon); 430 } else { 431 log.debug("IconDragJLabel.drop REJECTED!"); 432 e.rejectDrop(); 433 } 434 } catch (IOException | UnsupportedFlavorException ioe) { 435 } 436 if (!accepted) { 437 e.rejectDrop(); 438 log.debug("IconDragJLabel.drop REJECTED!"); 439 } else { 440 e.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE); 441 } 442 } 443 444 private boolean accept(String name, NamedIcon newIcon) { 445 if (name == null || newIcon == null) { 446 return false; 447 } 448 putIcon(name, newIcon); 449 if (log.isDebugEnabled()) { 450 log.debug("IconDragJLabel.drop COMPLETED for {}, {}", name, newIcon.getURL()); 451 } 452 return true; 453 } 454 455 } 456 457 public class IconDragJLabel extends DragJLabel /*implements DropTargetListener*/ { 458 459 int level; 460 461 public IconDragJLabel(DataFlavor flavor, NamedIcon icon, int zLevel) { 462 super(flavor, icon); 463 level = zLevel; 464 } 465 466 @Override 467 public boolean isDataFlavorSupported(DataFlavor flavor) { 468 return _dataFlavor.equals(flavor); 469 } 470 471 @Override 472 public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { 473 if (!isDataFlavorSupported(flavor)) { 474 return null; 475 } 476 String url = ((NamedIcon) getIcon()).getURL(); 477 log.debug("DragJLabel.getTransferData url= {}", url); 478 if (flavor.isMimeTypeEqual(Editor.POSITIONABLE_FLAVOR)) { 479 String link = _linkName.getText().trim(); 480 PositionableLabel l; 481 if (link.length() == 0) { 482 l = new PositionableLabel(NamedIcon.getIconByName(url), _frame.getEditor()); 483 } else { 484 l = new LinkingLabel(NamedIcon.getIconByName(url), _frame.getEditor(), link); 485 } 486 l.setLevel(level); 487 return l; 488 } else if (DataFlavor.stringFlavor.equals(flavor)) { 489 return _itemType + " for \"" + url + "\""; 490 } 491 return null; 492 } 493 } 494 495 class ADropJLabel extends DropJLabel { 496 ADropJLabel(Icon icon) { 497 super(icon); 498 } 499 ADropJLabel(Icon icon, HashMap<String, NamedIcon> iconMap) { 500 super(icon, iconMap); 501 } 502 503 @Override 504 protected void accept(DropTargetDropEvent e, NamedIcon newIcon) { 505 super.accept(e, newIcon); 506 _updateIcon = newIcon; 507 } 508 } 509 510 public class IconDisplayPanel extends JPanel implements JmriMouseListener { 511 String _key; 512 NamedIcon _icon; 513 514 public IconDisplayPanel(String key, NamedIcon icon, boolean dropIcon) { 515 super(); 516 _key = key; 517 _icon = icon; 518 JLabel image; 519 if (dropIcon) { 520 image = new ADropJLabel(icon); 521 } else { 522 image = new IconDragJLabel(_positionableDataFlavor, icon, _level); 523 image.addMouseListener(JmriMouseListener.adapt(this)); 524 addMouseListener(JmriMouseListener.adapt(new IconListener())); 525 } 526 wrapIconImage(icon, image, this, key); 527 } 528 529 @Override 530 public void mouseClicked(JmriMouseEvent event) { 531 if (event.getSource() instanceof JLabel) { 532 setSelection(this); 533 } else if (event.getSource() instanceof IconDisplayPanel) { 534 IconDisplayPanel panel = (IconDisplayPanel)event.getSource(); 535 setSelection(panel); 536 } 537 } 538 @Override 539 public void mousePressed(JmriMouseEvent event) { 540 } 541 @Override 542 public void mouseReleased(JmriMouseEvent event) { 543 } 544 @Override 545 public void mouseEntered(JmriMouseEvent event) { 546 } 547 @Override 548 public void mouseExited(JmriMouseEvent event) { 549 } 550 } 551 552 class IconListener extends JmriMouseAdapter { 553 @Override 554 public void mouseClicked(JmriMouseEvent event) { 555 if (event.getSource() instanceof IconDisplayPanel) { 556 IconDisplayPanel panel = (IconDisplayPanel)event.getSource(); 557 setSelection(panel); 558 } else { 559 deselectIcon(); 560 } 561 } 562 } 563 564 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(IconItemPanel.class); 565 566}