001package jmri.jmrit.display.palette; 002 003import java.awt.BorderLayout; 004import java.awt.Component; 005import java.awt.Dimension; 006import java.awt.GraphicsEnvironment; 007import java.awt.event.ActionEvent; 008import java.net.URL; 009import java.util.Enumeration; 010import java.util.HashMap; 011import java.util.Iterator; 012import java.util.List; 013import java.util.Map.Entry; 014import javax.annotation.Nonnull; 015import javax.annotation.concurrent.GuardedBy; 016import javax.swing.*; 017import javax.swing.event.ChangeEvent; 018import javax.swing.event.ChangeListener; 019import javax.swing.tree.TreeNode; 020 021import jmri.CatalogTree; 022import jmri.CatalogTreeManager; 023import jmri.InstanceManager; 024import jmri.CatalogTreeLeaf; 025import jmri.CatalogTreeNode; 026import jmri.jmrit.catalog.DirectorySearcher; 027import jmri.jmrit.catalog.ImageIndexEditor; 028import jmri.jmrit.catalog.NamedIcon; 029import jmri.jmrit.display.DisplayFrame; 030import jmri.jmrit.display.Editor; 031import jmri.jmrit.picker.PickListModel; 032import jmri.util.FileUtil; 033import jmri.util.swing.JmriJOptionPane; 034 035import org.jdom2.Element; 036 037import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 038 039/** 040 * Container for adding items to Control Panels. Starting point for palette package. 041 * <p> 042 * Loads and stores icons used in Control Panel Editor panels. 043 * For background colors to work on a particular editor instance, select the 044 * 'Item Palette' item under 'Add Items' menu and configure the 'Backgrounds' tab 045 * ItemPalette for that editor. Otherwise any item can be dragged and 046 * dropped to any editor. 047 * <p> 048 * The icons are displayed on the background of the last editor to call the 049 * ItemPalette instance. In session the user can set it to another color or a white/gray 050 * squares pattern using the "View on:" combo. This choice is shared across tabs 051 * as a field on the {@link jmri.jmrit.display.DisplayFrame} parent frame. 052 * <p> 053 * <a href="doc-files/ItemPalette-ClassDiagram.png"><img src="doc-files/ItemPalette-ClassDiagram.png" alt="UML Class diagram" height="50%" width="50%"></a> 054 * 055 * @author Pete Cressman Copyright (c) 2010, 2018 056 * @author Egbert Broerse Copyright (c) 2017 057 */ 058/* 059@startuml jmri/jmrit/display/palette/doc-files/ItemPalette-ClassDiagram.png 060 061abstract class JPanel 062package "jmri.util.swing.ImagePanel" { 063 class ImagePanel { 064-BufferedImage back 065+setImage() 066+paintComponent() 067} 068} 069package "jmri.util.swing.DrawSquares" { 070 class "DrawSquares" { 071+DrawSquares() 072} 073} 074abstract class ItemPanel { 075-String type 076#int previewBgSet 077#BufferedImage[] _backgrounds 078#MakeBgCombo() 079} 080JPanel --|> ItemPanel 081abstract class FamilyItemPanel 082class TableItemPanel 083class IndicatorItemPanel 084IndicatorItemPanel : type = "Indicator" 085object viewOnCombo 086viewOnCombo : -int choice 087viewOnCombo : +EventListener InitListener 088object preview 089preview : -image = 1 090preview : +EventListener comboListener 091object TurnoutItemPanel 092TurnoutItemPanel : type = "Turnout" 093TableItemPanel -- TurnoutItemPanel 094object SensorItemPanel 095SensorItemPanel : type = "Sensor" 096TableItemPanel -- SensorItemPanel 097class SignalMastItemPanel 098SignalMastItemPanel : type = "SignalMast" 099TableItemPanel --|> SignalMastItemPanel 100class MultiSensorItemPanel 101MultiSensorItemPanel : type = "MultiSensor" 102TableItemPanel --|> MultiSensorItemPanel 103class IconItemPanel 104class BackgroundItemPanel 105BackgroundItemPanel : type = "Background" 106IconItemPanel --|> BackgroundItemPanel 107class ClockItemPanel 108ClockItemPanel : type = "Clock" 109IconItemPanel --|> ClockItemPanel 110class DecoratorPanel 111DecoratorPanel : #int previewBgSet 112DecoratorPanel : #BufferedImage[] _backgrounds 113JPanel --|> DecoratorPanel 114abstract class DragJComponent 115JPanel --|> DragJComponent 116class TextItemPanel 117TextItemPanel : type = "Text" 118 119ItemPanel --|> FamilyItemPanel 120FamilyItemPanel --|> TableItemPanel 121FamilyItemPanel --|> IndicatorItemPanel 122DecoratorPanel *-- viewOnCombo 123FamilyItemPanel *-- viewOnCombo : if != SignalMast 124FamilyItemPanel *-- preview 125IconItemPanel *-- viewOnCombo : if != Background 126SignalMastItemPanel *-- viewOnCombo 127viewOnCombo ..> preview: setImage[n] 128viewOnCombo -- DrawSquares 129ItemPanel --|> IconItemPanel 130ItemPanel --|> TextItemPanel 131DecoratorPanel -- TextItemPanel 132ImagePanel -- preview 133DragJComponent --|> ReporterItemPanel 134ReporterItemPanel *-- preview 135' MemoryItemPanel not shown 136 137@enduml 138*/ 139 140public class ItemPalette extends DisplayFrame implements ChangeListener { 141 142 public static final int STRUT_SIZE = 5; 143 static final String RED_X = "resources/icons/misc/X-red.gif"; 144 145 @GuardedBy("ItemPalette") 146 static JTabbedPane _tabPane; 147 @GuardedBy("ItemPalette") 148 static HashMap<String, ItemPanel> _tabIndex; 149 150 private static volatile HashMap<String, HashMap<String, HashMap<String, NamedIcon>>> _iconMaps; 151 // for now, special case 4 level maps since IndicatorTO is the only case. 152 private static volatile HashMap<String, HashMap<String, HashMap<String, HashMap<String, NamedIcon>>>> _indicatorTOMaps; 153 private static int tabWidth; 154 private ItemPanel _currentItemPanel; 155 156 /** 157 * Store palette icons in preferences file catalogTrees.xml 158 */ 159 public static void storeIcons() { 160 if (_iconMaps == null) { 161 return; // never loaded 162 } 163 CatalogTreeManager manager = InstanceManager.getDefault(jmri.CatalogTreeManager.class); 164 // unfiltered, xml-stored, item palette icon tree 165 CatalogTree tree = manager.getBySystemName("NXPI"); 166 // discard old version 167 if (tree != null) { 168 manager.deregister(tree); 169 } 170 tree = manager.newCatalogTree("NXPI", "Item Palette"); 171 CatalogTreeNode root = tree.getRoot(); 172 173 for (Entry<String, HashMap<String, HashMap<String, NamedIcon>>> entry : _iconMaps.entrySet()) { 174 root.add(store3levelMap(entry.getKey(), entry.getValue())); 175 if (log.isDebugEnabled()) { 176 log.debug("Add type node {}", entry.getKey()); 177 } 178 } 179 180 for (Entry<String, HashMap<String, HashMap<String, HashMap<String, NamedIcon>>>> entry : _indicatorTOMaps.entrySet()) { 181 CatalogTreeNode typeNode = new CatalogTreeNode(entry.getKey()); 182 for (Entry<String, HashMap<String, HashMap<String, NamedIcon>>> ent : entry.getValue().entrySet()) { 183 typeNode.add(store3levelMap(ent.getKey(), ent.getValue())); 184 log.debug("Add IndicatorTO node {}", ent.getKey()); 185 } 186 root.add(typeNode); 187 log.debug("Add IndicatorTO node {}", entry.getKey()); 188 } 189 } 190 191 static CatalogTreeNode store3levelMap(String type, HashMap<String, HashMap<String, NamedIcon>> familyMap) { 192 CatalogTreeNode typeNode = new CatalogTreeNode(type); 193 for (Entry<String, HashMap<String, NamedIcon>> mapEntry : familyMap.entrySet()) { 194 String family = mapEntry.getKey(); 195 CatalogTreeNode familyNode = new CatalogTreeNode(family); 196 HashMap<String, NamedIcon> iconMap = mapEntry.getValue(); 197 for (Entry<String, NamedIcon> iconEntry : iconMap.entrySet()) { 198 String state = iconEntry.getKey(); 199 String path = iconEntry.getValue().getURL(); 200 familyNode.addLeaf(state, path); 201 } 202 typeNode.add(familyNode); 203 log.debug("Add familyNode {}", familyNode); 204 } 205 return typeNode; 206 } 207 208 public static void loadIcons() { 209 if (_iconMaps == null) { 210 // long t = System.currentTimeMillis(); 211 InstanceManager.getDefault(jmri.CatalogTreeManager.class).loadImageIndex(); 212 _iconMaps = new HashMap<>(); 213 _indicatorTOMaps = new HashMap<>(); 214 215 if (!loadSavedIcons()) { 216 loadDefaultIcons(); 217 } 218 } 219 } 220 221 static boolean loadSavedIcons() { 222 CatalogTreeManager manager = InstanceManager.getDefault(jmri.CatalogTreeManager.class); 223 CatalogTree tree = manager.getBySystemName("NXPI"); 224 if (tree != null) { 225 CatalogTreeNode root = tree.getRoot(); 226 Enumeration<TreeNode> e = root.children(); 227 while (e.hasMoreElements()) { 228 CatalogTreeNode node = (CatalogTreeNode)e.nextElement(); 229 String typeName = (String) node.getUserObject(); 230 // detect this is a 4 level map collection. 231 // not very elegant (i.e. extensible), but maybe all that's needed. 232 if (typeName.equals("IndicatorTO")) { 233 HashMap<String, HashMap<String, HashMap<String, NamedIcon>>> familyTOMap 234 = loadIndicatorFamilyMap(node); 235 log.debug("Add {} indicatorTO families to item type {} for _indicatorTOMaps.", 236 familyTOMap.size(), typeName ); 237 _indicatorTOMaps.put(typeName, familyTOMap); 238 } else { 239 HashMap<String, HashMap<String, NamedIcon>> familyMap 240 = loadFamilyMap(node); 241 _iconMaps.put(typeName, familyMap); 242 log.debug("Add item type {} to _iconMaps.", typeName); 243 } 244 } 245 log.debug("Icon Map has {} members", _iconMaps.size()); 246 return true; 247 } 248 return false; 249 } 250 251 static HashMap<String, HashMap<String, HashMap<String, NamedIcon>>> 252 loadIndicatorFamilyMap(CatalogTreeNode node) { 253 HashMap<String, HashMap<String, HashMap<String, NamedIcon>>> familyMap 254 = new HashMap<>(); 255 Enumeration<TreeNode> ee = node.children(); 256 while (ee.hasMoreElements()) { 257 CatalogTreeNode famNode = (CatalogTreeNode)ee.nextElement(); 258 String name = (String) famNode.getUserObject(); 259 familyMap.put(name, loadFamilyMap(famNode)); 260 } 261 return familyMap; 262 } 263 264 static HashMap<String, HashMap<String, NamedIcon>> loadFamilyMap(CatalogTreeNode node) { 265 HashMap<String, HashMap<String, NamedIcon>> familyMap = new HashMap<>(); 266 Enumeration<TreeNode> ee = node.children(); 267 while (ee.hasMoreElements()) { 268 CatalogTreeNode famNode = (CatalogTreeNode)ee.nextElement(); 269 String familyName = (String)famNode.getUserObject(); 270 HashMap<String, NamedIcon> iconMap = new HashMap<>(); 271 List<CatalogTreeLeaf> list = famNode.getLeaves(); 272 for (CatalogTreeLeaf catalogTreeLeaf : list) { 273 String iconName = catalogTreeLeaf.getName(); 274 String path = catalogTreeLeaf.getPath(); 275 NamedIcon icon = NamedIcon.getIconByName(path); 276 if (icon == null) { 277 log.warn("loadFamilyMap cannot find icon \"{}\" in family\"{}\" at path \"{}\"", iconName, familyName, path); 278 String fileName = RED_X; 279 icon = new NamedIcon(fileName, fileName); 280 } 281 iconMap.put(iconName, icon); 282 log.debug("Add {} icon to family \"{}\"", iconName, familyName); 283 } 284 familyMap.put(familyName, iconMap); 285 } 286 return familyMap; 287 } 288 289 static List<Element> getDefaultIconItemTypes() throws org.jdom2.JDOMException, java.io.IOException { 290 URL file = FileUtil.findURL("xml/defaultPanelIcons.xml"); 291 if (file == null) { 292 throw new IllegalArgumentException("defaultPanelIcons file (xml/defaultPanelIcons.xml) doesn't exist."); 293 } 294 jmri.jmrit.XmlFile xf = new jmri.jmrit.XmlFile() { 295 }; 296 Element root = xf.rootFromURL(file); 297 return (root.getChild("ItemTypes").getChildren()); 298 } 299 300 static void loadDefaultIcons() { 301 try { 302 List<Element> typeList = getDefaultIconItemTypes(); 303 for (Element type : typeList) { 304 String typeName = type.getName(); 305 List<Element> families = type.getChildren(); 306 loadFamilies(typeName, families); 307 } 308 } catch (org.jdom2.JDOMException | java.io.IOException e) { 309 log.error("error reading file \"defaultPanelIcons.xml\" due to: ", e); 310 } 311 } 312 313 static void loadFamilies(String typeName, List<Element> families) { 314 // detect this is a 4 level map collection. 315 // not very elegant (i.e. extensible), but maybe all that's needed. 316 if (typeName.equals("IndicatorTO")) { 317 HashMap<String, HashMap<String, HashMap<String, NamedIcon>>> familyTOMap 318 = loadDefaultIndicatorTOMap(families, null); 319 _indicatorTOMaps.put(typeName, familyTOMap); 320 log.debug("Add {} indicatorTO families to item type {} to _indicatorTOMaps.", 321 familyTOMap.size(), typeName); 322 } else { 323 HashMap<String, HashMap<String, NamedIcon>> familyMap = loadDefaultFamilyMap(families, null); 324 _iconMaps.put(typeName, familyMap); 325 log.debug("Add {} families to item type \"{}\" to _iconMaps.", 326 familyMap.size(), typeName); 327 } 328 } 329 330 @SuppressFBWarnings(value="DLS_DEAD_LOCAL_STORE",justification="Stores are not dead. Both statements APPEND additional items into their maps") 331 static public void loadMissingItemType(String itemType) { 332 try { 333 Element thisType = null; 334 List<Element> typeList = getDefaultIconItemTypes(); 335 for (Element type : typeList) { 336 String typeName = type.getName(); 337 if (typeName.equals(itemType)) { 338 thisType = type; 339 break; 340 } 341 } 342 if (thisType == null) { 343 log.error("No type \"{}\" in file \"defaultPanelIcons.xml\"", itemType); 344 return; 345 } 346 List<Element> families = thisType.getChildren(); 347 if (itemType.equals("IndicatorTO")) { 348 HashMap<String, HashMap<String, HashMap<String, NamedIcon>>> familyMaps = _indicatorTOMaps.get(itemType); 349 familyMaps = loadDefaultIndicatorTOMap(families, familyMaps); 350 } else { 351 HashMap<String, HashMap<String, NamedIcon>> familyMap = ItemPalette.getFamilyMaps(itemType); 352 familyMap = loadDefaultFamilyMap(families, familyMap); 353 } 354 InstanceManager.getDefault(CatalogTreeManager.class).indexChanged(true); 355 } catch (org.jdom2.JDOMException | java.io.IOException ex) { 356 log.error("error reading file \"defaultPanelIcons.xml\" due to: ", ex); 357 } 358 } 359 360 static HashMap<String, HashMap<String, NamedIcon>> loadDefaultFamilyMap( 361 List<Element> families, HashMap<String, HashMap<String, NamedIcon>> familyMap) { 362 if (familyMap == null) { 363 familyMap = new HashMap<>(); 364 } 365 for (Element family : families) { 366 String familyName = family.getName(); 367 HashMap<String, NamedIcon> iconMap = new HashMap<>(); 368 // Map of all icons of in family, familyName 369 List<Element> iconfiles = family.getChildren(); 370 for (Element iconfile : iconfiles) { 371 String iconName = iconfile.getName(); 372 String fileName = iconfile.getText().trim(); 373 if (fileName.length() == 0) { 374 log.warn("loadDefaultFamilyMap: icon \"{}\" in family \"{}\" has no image file.", iconName, familyName); 375 fileName = RED_X; 376 } 377 NamedIcon icon = NamedIcon.getIconByName(fileName); 378 if (icon == null) { 379 log.warn("loadDefaultFamilyMap: icon \"{}\" in family \"{}\" cannot get icon from file \"{}\".", iconName, familyName, fileName); 380 fileName = RED_X; 381 icon = new NamedIcon(fileName, fileName); 382 } 383 iconMap.put(iconName, icon); 384 } 385 familyMap.put(familyName, iconMap); 386 if (log.isDebugEnabled()) { 387 log.debug("Add {} icons to family \"{}\"", iconMap.size(), familyName); 388 } 389 } 390 return familyMap; 391 } 392 393 static HashMap<String, HashMap<String, HashMap<String, NamedIcon>>> 394 loadDefaultIndicatorTOMap(List<Element> typeList, HashMap<String, HashMap<String, HashMap<String, NamedIcon>>> familyTOMap) { 395 if (familyTOMap == null) { 396 familyTOMap = new HashMap<>(); 397 } 398 // HashMap<String, HashMap<String, HashMap<String, NamedIcon>>> familyTOMap = new HashMap<>(); 399 // Map of all families of type, typeName 400 for (Element element : typeList) { 401 String familyName = element.getName(); 402 List<Element> types = element.getChildren(); 403 HashMap<String, HashMap<String, NamedIcon>> familyMap = loadDefaultFamilyMap(types, null); 404 familyTOMap.put(familyName, familyMap); 405 if (log.isDebugEnabled()) { 406 log.debug("Add {} IndicatorTO sub-families to item type {} to IndicatorTO families.", familyMap.size(), familyName); 407 } 408 } 409 return familyTOMap; 410 } 411 412 public static ItemPalette getDefault(String title, @Nonnull Editor ed) { 413 if (GraphicsEnvironment.isHeadless()) { 414 return null; 415 } 416 ItemPalette instance = InstanceManager.getOptionalDefault(ItemPalette.class).orElseGet(() -> InstanceManager.setDefault(ItemPalette.class, new ItemPalette(title, ed))); 417 if (!ed.equals(instance.getEditor())) { 418 instance.updateBackground(ed); 419 InstanceManager.getDefault(jmri.util.PlaceWindow.class).nextTo(ed, null, instance); 420 } 421 // pack before setLocation 422 instance.pack(); 423 InstanceManager.getDefault(jmri.util.PlaceWindow.class).nextTo(ed, null, instance); 424 instance.setVisible(true); 425 return instance; 426 } 427 428 public void setEditor(Editor ed) { 429 updateBackground(ed); 430 InstanceManager.getDefault(jmri.util.PlaceWindow.class).nextTo(ed, null, this); 431 } 432 433 public ItemPalette(String title, Editor ed) { 434 super(title, ed); 435 init(); 436 setTitle(Bundle.getMessage("ItemPaletteTitle")); 437 } 438 439 private void init() { 440 loadIcons(); 441 addWindowListener(new java.awt.event.WindowAdapter() { 442 @Override 443 public void windowClosing(java.awt.event.WindowEvent e) { 444 closePanels(e); 445 } 446 }); 447 448 makeMenus(); 449 buildTabPane(this); 450 451 setLayout(new BorderLayout(5, 5)); 452 synchronized (this) { 453 add(_tabPane, BorderLayout.CENTER); 454 JScrollPane sp = (JScrollPane) _tabPane.getSelectedComponent(); 455 _currentItemPanel = (ItemPanel) sp.getViewport().getView(); 456 _currentItemPanel.hideIcons(); 457 } 458 } 459 460 /* 461 * Add the tabs on the Control Panel Editor. 462 */ 463 static void buildTabPane(ItemPalette palette) { 464 //synchronized (ItemPalette.class) { 465 _tabPane = new JTabbedPane(); 466 _tabIndex = new HashMap<>(); 467 //} 468 tabWidth = getTabWidth(); 469 470 ItemPanel itemPanel = new TableItemPanel<>(palette, "Turnout", null, 471 PickListModel.turnoutPickModelInstance()); 472 addItemTab(itemPanel, "Turnout", "BeanNameTurnout"); 473 // panel shown on start 474 475 itemPanel = new TableItemPanel<>(palette, "Sensor", null, 476 PickListModel.sensorPickModelInstance()); 477 addItemTab(itemPanel, "Sensor", "BeanNameSensor"); 478 479 itemPanel = new SignalHeadItemPanel(palette, "SignalHead", null, 480 PickListModel.signalHeadPickModelInstance()); 481 addItemTab(itemPanel, "SignalHead", "BeanNameSignalHead"); 482 483 itemPanel = new SignalMastItemPanel(palette, "SignalMast", null, 484 PickListModel.signalMastPickModelInstance()); 485 addItemTab(itemPanel, "SignalMast", "BeanNameSignalMast"); 486 487 itemPanel = new MemoryItemPanel(palette, "Memory", null, 488 PickListModel.memoryPickModelInstance()); 489 addItemTab(itemPanel, "Memory", "BeanNameMemory"); 490 491 itemPanel = new GlobalVariableItemPanel(palette, "Global Variable", null, 492 PickListModel.globalVariablePickModelInstance()); 493 addItemTab(itemPanel, "GlobalVariable", "BeanNameGlobalVariable"); 494 495 itemPanel = new ReporterItemPanel(palette, "Reporter", null, 496 PickListModel.reporterPickModelInstance()); 497 addItemTab(itemPanel, "Reporter", "BeanNameReporter"); 498 499 itemPanel = new TableItemPanel<>(palette, "Light", null, 500 PickListModel.lightPickModelInstance()); 501 addItemTab(itemPanel, "Light", "BeanNameLight"); 502 503 itemPanel = new MultiSensorItemPanel(palette, "MultiSensor", null, 504 PickListModel.multiSensorPickModelInstance()); 505 addItemTab(itemPanel, "MultiSensor", "MultiSensor"); 506 507 itemPanel = new IconItemPanel(palette, "Icon"); 508 addItemTab(itemPanel, "Icon", "Icon"); 509 510 itemPanel = new BackgroundItemPanel(palette, "Background"); 511 addItemTab(itemPanel, "Background", "Background"); 512 513 itemPanel = new TextItemPanel(palette, "Text"); 514 addItemTab(itemPanel, "Text", "Text"); 515 516 itemPanel = new RPSItemPanel(palette, "RPSReporter", null); 517 addItemTab(itemPanel, "RPSReporter", "RPSreporter"); 518 519 itemPanel = new ClockItemPanel(palette, "FastClock"); 520 addItemTab(itemPanel, "FastClock", "FastClock"); 521 522 itemPanel = new IndicatorItemPanel(palette, "IndicatorTrack", null); 523 addItemTab(itemPanel, "IndicatorTrack", "IndicatorTrack"); 524 525 itemPanel = new IndicatorTOItemPanel(palette, "IndicatorTO", null, 526 PickListModel.turnoutPickModelInstance()); 527 addItemTab(itemPanel, "IndicatorTO", "IndicatorTO"); 528 529 itemPanel = new PortalItemPanel(palette, "Portal", null); 530 addItemTab(itemPanel, "Portal", "BeanNamePortal"); 531 532 setTabs(); 533 _tabPane.addChangeListener(palette); 534 } 535 536 static void addItemTab(ItemPanel itemPanel, String key, String tabTitle) { 537 itemPanel.init(); 538 JScrollPane scrollPane = new JScrollPane(itemPanel); 539 synchronized (ItemPalette.class) { 540 _tabPane.add(Bundle.getMessage(tabTitle), scrollPane); 541 _tabIndex.put(key, itemPanel); 542 log.debug("_tabIndex.size()={}", _tabIndex.size()); 543 } 544 } 545 546 static int getTabWidth() { 547 int maxTabWidth = 0; 548 for (Entry<String, String> t : ItemPanel.NAME_MAP.entrySet()) { 549 maxTabWidth = Math.max(maxTabWidth, new JLabel(Bundle.getMessage(t.getValue())).getWidth()); 550 } 551 return maxTabWidth; 552 } 553 554 static void setTabs() { 555 JLabel lab = new JLabel(); 556 lab.setPreferredSize(new Dimension(tabWidth, 30)); 557 synchronized (ItemPalette.class) { 558 for (int i = 0; i == _tabPane.getTabCount(); i++) { 559 _tabPane.setTabComponentAt(i, lab); 560 } 561 } 562 } 563 564// @Override 565// public void setPreviewBg(int index) { 566// super.setPreviewBg(index); 567// // introduced loop, deprecated and replaced by updating at the moment a tab is brought to front. 568// } 569 570 @Override 571 public void stateChanged(ChangeEvent e) { 572 JTabbedPane tp = (JTabbedPane) e.getSource(); 573 JScrollPane sp = (JScrollPane) tp.getSelectedComponent(); 574 ItemPanel newItemPanel = (ItemPanel) sp.getViewport().getView(); 575 newItemPanel.closeDialogs(); 576 newItemPanel.previewColorChange(); 577 newItemPanel.revalidate(); 578 // elegantly close previous ItemPanel 579 if (_currentItemPanel != null) { 580 _currentItemPanel.closeDialogs(); 581 } 582 _currentItemPanel = newItemPanel; 583 } 584 585 private void makeMenus() { 586 JMenuBar menuBar = new JMenuBar(); 587 JMenu findIcon = new JMenu(Bundle.getMessage("findIconMenu")); 588 menuBar.add(findIcon); 589 590 JMenuItem editItem = new JMenuItem(Bundle.getMessage("editIndexMenu")); 591 editItem.addActionListener((ActionEvent e) -> { 592 ImageIndexEditor ii = InstanceManager.getDefault(ImageIndexEditor.class); 593 ii.pack(); 594 ii.setVisible(true); 595 }); 596 findIcon.add(editItem); 597 findIcon.addSeparator(); 598 599 JMenuItem openItem = new JMenuItem(Bundle.getMessage("openDirMenu")); 600 openItem.addActionListener((ActionEvent e) -> InstanceManager.getDefault(DirectorySearcher.class).openDirectory()); 601 findIcon.add(openItem); 602 603 JMenuItem searchItem = new JMenuItem(Bundle.getMessage("searchFSMenu")); 604 searchItem.addActionListener((ActionEvent e) -> DirectorySearcher.instance().searchFS()); 605 findIcon.add(searchItem); 606 607 setJMenuBar(menuBar); 608 addHelpMenu("package.jmri.jmrit.display.ItemPalette", true); 609 } 610 611 private void closePanels(java.awt.event.WindowEvent e) { 612 synchronized(this) { 613 java.awt.Component[] comps = _tabPane.getComponents(); 614 if (log.isDebugEnabled()) { 615 log.debug("closePanels: tab count= {}", _tabPane.getTabCount()); 616 } 617 for (Component comp : comps) { 618 javax.swing.JViewport vp = (javax.swing.JViewport) ((JScrollPane) comp).getComponent(0); 619 Component ip = vp.getView(); 620 if (ip instanceof ItemPanel) { 621 ((ItemPanel) ip).closeDialogs(); 622 } 623 } 624 } 625 super.windowClosing(e); 626 } 627 628 /* 629 * Look for duplicate name of family in the iterated set. 630 */ 631 static private boolean familyNameOK(String type, String family, Iterator<String> it) { 632 if (family == null || family.length() == 0) { 633 JmriJOptionPane.showMessageDialog(null, 634 Bundle.getMessage("EnterFamilyName"), 635 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 636 return false; 637 } 638 while (it.hasNext()) { 639 String f = it.next(); 640 if (family.equals(f)) { 641 JmriJOptionPane.showMessageDialog(null, 642 java.text.MessageFormat.format(Bundle.getMessage("DuplicateFamilyName"), family, type), 643 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 644 return false; 645 } 646 } 647 return true; 648 } 649 650 /** 651 * Add a new Family of icons to the device type. 652 * 653 * @param type type to retrieve 654 * @param family name for iconMap "family" 655 * @param iconMap icon HashMap providing the images 656 * @return result 657 */ 658 static protected boolean addFamily(String type, String family, HashMap<String, NamedIcon> iconMap) { 659 if (family == null) { 660 return false; 661 } 662 HashMap<String, HashMap<String, NamedIcon>> typeMap = ItemPalette.getFamilyMaps(type); 663 typeMap.put(family, iconMap); 664 InstanceManager.getDefault(CatalogTreeManager.class).indexChanged(true); 665 return true; 666 } 667 668 /** 669 * Get all the Families of icons for a given device type. 670 * 671 * @param type type 672 * @return map of families 673 */ 674 static public @Nonnull HashMap<String, HashMap<String, NamedIcon>> getFamilyMaps(String type) { 675 return _iconMaps.computeIfAbsent(type, k -> new HashMap<>()); 676 } 677 678 /** 679 * Remove a Family of icons from the device type. 680 * 681 * @param type type 682 * @param family family 683 */ 684 static protected void removeIconMap(String type, String family) { 685 if (log.isDebugEnabled()) { 686 log.debug("removeIconMap for family \"{}\" in type \"{}\"", family, type); 687 } 688 HashMap<String, HashMap<String, NamedIcon>> families = getFamilyMaps(type); 689 families.remove(family); 690 InstanceManager.getDefault(CatalogTreeManager.class).indexChanged(true); 691 if (log.isDebugEnabled()) { 692 for (String s : families.keySet()) { 693 log.debug("removeIconMap remaining Keys: family \"{}\" in type \"{}\"", s, type); 694 } 695 } 696 } 697 698 /* 699 * Get a clone of the Family of icons for a given device type and family. 700 */ 701 static public HashMap<String, NamedIcon> getIconMap(String type, String family) { 702 HashMap<String, HashMap<String, NamedIcon>> itemMap = _iconMaps.get(type); 703 if (itemMap == null) { 704 log.error("getIconMap failed. item type \"{}\" not found.", type); 705 return null; 706 } 707 HashMap<String, NamedIcon> iconMap = itemMap.get(family); 708 if (iconMap == null) { 709 log.warn("getIconMap failed. family \"{}\" not found in item type \"{}\"", family, type); 710 return null; 711 } 712 return cloneMap(iconMap); 713 } 714 715 /** 716 * ************ Currently only needed for IndicatorTO type ************** 717 * @param type type 718 * @param family family 719 * @param iconMap iconMap 720 * @return result 721 */ 722 // add entire family 723 static protected boolean addLevel4Family(String type, String family, 724 HashMap<String, HashMap<String, NamedIcon>> iconMap) { 725 Iterator<String> iter = getLevel4FamilyMaps(type).keySet().iterator(); 726 if (familyNameOK(type, family, iter)) { 727 getLevel4FamilyMaps(type).put(family, iconMap); 728 InstanceManager.getDefault(CatalogTreeManager.class).indexChanged(true); 729 return true; 730 } 731 return false; 732 } 733 734 // add entire family 735 static protected void addLevel4FamilyMap(String type, String family, 736 String key, HashMap<String, NamedIcon> iconMap) { 737 HashMap<String, HashMap<String, NamedIcon>> familyMap = getLevel4Family(type, family); 738 familyMap.put(key, iconMap); 739 InstanceManager.getDefault(CatalogTreeManager.class).indexChanged(true); 740 } 741 742 // Currently only needed for IndicatorTO type 743 static protected HashMap<String, HashMap<String, HashMap<String, NamedIcon>>> 744 getLevel4FamilyMaps(String type) { 745 return _indicatorTOMaps.get(type); 746 } 747 748 // Currently only needed for IndicatorTO type 749 static protected HashMap<String, HashMap<String, NamedIcon>> 750 getLevel4Family(String type, String family) { 751 HashMap<String, HashMap<String, HashMap<String, NamedIcon>>> map = _indicatorTOMaps.get(type); 752 return map.get(family); 753 } 754 755 // Currently only needed for IndicatorTO type 756 static protected void removeLevel4IconMap(String type, String family, String key) { 757 if (log.isDebugEnabled()) { 758 log.debug("removeLevel4IconMap for indicator family \"{}\" in type \"{}\" with key \"{}\"", 759 family, type, key); 760 } 761 if (key != null) { 762 _indicatorTOMaps.get(type).get(family).remove(key); 763 } else { 764 _indicatorTOMaps.get(type).remove(family); 765 } 766 InstanceManager.getDefault(CatalogTreeManager.class).indexChanged(true); 767 } 768 769 /////////////////////////////////////////////////////////////////////////////// 770 771 static protected HashMap<String, NamedIcon> cloneMap(HashMap<String, NamedIcon> map) { 772 HashMap<String, NamedIcon> clone = new HashMap<>(); 773 if (map != null) { 774 for (Entry<String, NamedIcon> entry : map.entrySet()) { 775 String name = entry.getKey(); 776 NamedIcon icon = new NamedIcon(entry.getValue()); 777 clone.put(name, icon); 778 } 779 } 780 return clone; 781 } 782 783 /** 784 * Default key names as listed in defaultPanelIcons.xml are Bundle keys and 785 * nodes in the CatalogTree. However users also define icon sets and store 786 * them in the CatalogTree. The names the user has defined for these sets 787 * (i.e.family" name) are also nodes in the CatalogTree. So it is expected 788 * that such names will fall through as an Exception. Thus these names are 789 * returned as the user has entered them. There is no failure of I18N here. 790 * @param name key name 791 * @return usable UI display name 792 */ 793 static public String convertText(String name) { 794 String cName; 795 try { 796 // NOI18N 797 cName = Bundle.getMessage(name); 798 } catch (Exception e) { 799 cName = name; 800 } 801 return cName; 802 } 803 804 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ItemPalette.class); 805 806}