001package jmri.jmrit.catalog; 002 003import java.awt.datatransfer.DataFlavor; 004import java.awt.event.ActionEvent; 005import java.awt.event.MouseEvent; 006import java.util.Enumeration; 007import java.util.Set; 008 009import javax.swing.BoxLayout; 010import javax.swing.JLabel; 011import javax.swing.JMenu; 012import javax.swing.JMenuBar; 013import javax.swing.JMenuItem; 014import javax.swing.JPanel; 015import javax.swing.JPopupMenu; 016import javax.swing.JSplitPane; 017import javax.swing.JTree; 018import javax.swing.SwingConstants; 019import javax.swing.tree.*; 020 021import jmri.CatalogTreeManager; 022import jmri.CatalogTreeNode; 023import jmri.InstanceInitializer; 024import jmri.InstanceManager; 025import jmri.implementation.AbstractInstanceInitializer; 026import jmri.util.FileUtil; 027import jmri.util.JmriJFrame; 028import jmri.util.swing.JmriJOptionPane; 029 030import org.openide.util.lookup.ServiceProvider; 031 032/** 033 * A JFrame for creating and editing an Image Index. 034 * 035 * @author Pete Cressman Copyright 2009 036 */ 037public final class ImageIndexEditor extends JmriJFrame { 038 039 private static final String RESOURCES = "resources"; 040 private static final String MESSAGE_TITLE = "MessageTitle"; 041 private CatalogPanel _index; 042 private JTree _indexTree; 043 044 public static final String IconDataFlavorMime = DataFlavor.javaJVMLocalObjectMimeType 045 + ";class=jmri.jmrit.catalog.NamedIcon"; 046 047 /** 048 * Ctor for an unnamed ImageIndexEditor. 049 */ 050 private ImageIndexEditor() { 051 super(); 052 } 053 054 /** 055 * Ctor for a named ImageIndexEditor. 056 * 057 * @param name title to display on the editor frame 058 */ 059 private ImageIndexEditor(String name) { 060 super(name); 061 } 062 063 private void init() { 064 JMenuBar menuBar = new JMenuBar(); 065 JMenu findIcon = new JMenu(Bundle.getMessage("MenuFile")); 066 menuBar.add(findIcon); 067 JMenuItem storeItem = new JMenuItem(Bundle.getMessage("MIStoreImageIndex")); 068 findIcon.add(storeItem); 069 storeItem.addActionListener((ActionEvent event) -> 070 InstanceManager.getDefault(CatalogTreeManager.class).storeImageIndex()); 071 072 findIcon.addSeparator(); 073 JMenuItem openItem = new JMenuItem(Bundle.getMessage("openDirMenu")); 074 openItem.addActionListener((ActionEvent e) -> 075 InstanceManager.getDefault(DirectorySearcher.class).openDirectory()); 076 findIcon.add(openItem); 077 078 JMenuItem searchItem = new JMenuItem(Bundle.getMessage("searchFSMenu")); 079 searchItem.addActionListener((ActionEvent e) -> 080 InstanceManager.getDefault(DirectorySearcher.class).searchFS()); 081 findIcon.add(searchItem); 082 083 JMenu editMenu = new JMenu(Bundle.getMessage("MenuEdit")); 084 menuBar.add(editMenu); 085 addMenuItems(editMenu, null); 086 setJMenuBar(menuBar); 087 088 addHelpMenu("package.jmri.jmrit.catalog.ImageIndex", true); 089 090 JPanel mainPanel = new JPanel(); 091 mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS)); 092 JPanel labelPanel = new JPanel(); 093 labelPanel.add(new JLabel(Bundle.getMessage("dragIconsInstr"), SwingConstants.LEFT)); 094 mainPanel.add(labelPanel); 095 JPanel catalogsPanel = new JPanel(); 096 catalogsPanel.setLayout(new BoxLayout(catalogsPanel, BoxLayout.X_AXIS)); 097 JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, 098 makeCatalogPanel(), makeIndexPanel()); 099 splitPane.setContinuousLayout(true); 100 splitPane.setOneTouchExpandable(true); 101 catalogsPanel.add(splitPane); 102 mainPanel.add(catalogsPanel); 103 getContentPane().add(mainPanel); 104 105 addWindowListener(new java.awt.event.WindowAdapter() { 106 @Override 107 public void windowClosing(java.awt.event.WindowEvent e) { 108 InstanceManager.getDefault(DirectorySearcher.class).close(); 109 } 110 }); 111 setLocation(10, 200); 112 pack(); 113 setVisible(true); 114 } 115 116 private JPanel makeCatalogPanel() { 117 CatalogPanel _catalog = CatalogPanel.makeCatalog("defaultCatalog", "selectNode", true, false, true); // make sure both these properties keys exist 118 log.debug("init the new CatalogPanel for ImageIndexEditor.makeCatalogPanel()"); 119 CatalogTreeManager manager = InstanceManager.getDefault(jmri.CatalogTreeManager.class); 120 121 for (jmri.CatalogTree tree : manager.getNamedBeanSet()) { 122 if (tree.getSystemName().startsWith("IF")) { 123 _catalog.addTree(tree); 124 log.debug("added item to tree"); 125 } 126 } 127 128 _catalog.createNewBranch("IFJAR", "Program Directory", RESOURCES); 129 FileUtil.createDirectory(FileUtil.getUserFilesPath() + RESOURCES); 130 _catalog.createNewBranch("IFPREF", "Preferences Directory", FileUtil.getUserFilesPath() + RESOURCES); 131 return _catalog; 132 } 133 134 /* 135 * Provide node editing. 136 * 137 * @param evt mouse event providing x and y position in frame 138 */ 139 private void showTreePopUp(MouseEvent evt) { 140 int row = _indexTree.getRowForLocation(evt.getX(), evt.getY()); 141 if (row <= 0) { 142 return; 143 } 144 TreePath path = _indexTree.getPathForLocation(evt.getX(), evt.getY()); 145 String nodeName = path.getLastPathComponent().toString(); 146 log.debug("showTreePopUp node= {}", nodeName); 147 JPopupMenu popup = new JPopupMenu(); 148 popup.add(new JMenuItem(nodeName)); 149 popup.add(new javax.swing.JPopupMenu.Separator()); 150 151 addMenuItems(popup, (CatalogTreeNode) path.getLastPathComponent()); 152 popup.show(evt.getComponent(), evt.getX(), evt.getY()); 153 } 154 155 private void addMenuItems(javax.swing.JComponent editMenu, CatalogTreeNode node) { 156 JMenuItem addItem = new JMenuItem(Bundle.getMessage("addNode")); 157 addItem.addActionListener((ActionEvent e) -> addNode(node)); 158 editMenu.add(addItem); 159 JMenuItem renameItem = new JMenuItem(Bundle.getMessage("renameNode")); 160 renameItem.addActionListener((ActionEvent e) -> renameNode(node)); 161 editMenu.add(renameItem); 162 JMenuItem deleteItem = new JMenuItem(Bundle.getMessage("deleteNode")); 163 deleteItem.addActionListener((ActionEvent e) -> deleteNode(node)); 164 editMenu.add(deleteItem); 165 } 166 167 private JPanel makeIndexPanel() { 168 _index = CatalogPanel.makeCatalog("ImageIndex", "selectIndexNode", true, true, false); // make sure both these properties keys exist 169 log.debug("init the new CatalogPanel for ImageIndexEditor.makeIndexPanel()"); 170 // activate dragNdrop init(true,true) allows drop into same panel as the drag - i.e. duplicates icon in node 171 172 boolean found = false; 173 CatalogTreeManager manager = InstanceManager.getDefault(jmri.CatalogTreeManager.class); 174 for (jmri.CatalogTree tree : manager.getNamedBeanSet()) { 175 if (tree.getSystemName().startsWith("IX")) { 176 _index.addTree(tree); 177 found = true; 178 } 179 } 180 181 if (!found) { 182 _index.createNewBranch("IXII", Bundle.getMessage("ImageIndexRoot"), "ImageIndexRoot"); 183 } 184 _indexTree = _index.getTree(); 185 _indexTree.setSelectionRow(0); // begin with ImageIndexRoot selected 186 _indexTree.addMouseListener(new java.awt.event.MouseAdapter () { 187 @Override 188 public void mouseReleased(MouseEvent e) { 189 if (e.isPopupTrigger() || e.isMetaDown() || e.isAltDown()) { 190 showTreePopUp(e); 191 } 192 } 193 }); 194 return _index; 195 } 196 197 void addNode(CatalogTreeNode selectedNode) { 198 if (selectedNode == null) { 199 selectedNode = _index.getSelectedNode(); 200 } 201 if (selectedNode == null) { 202 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("selectAddNode"), 203 Bundle.getMessage(MESSAGE_TITLE), JmriJOptionPane.INFORMATION_MESSAGE); 204 } else { 205 String name = (String)JmriJOptionPane.showInputDialog(this, Bundle.getMessage("nameAddNode"), Bundle.getMessage("QuestionTitle"), 206 HEIGHT, null, null, ""); 207 if (name != null) { 208 if (!_index.insertNodeIntoModel(name, selectedNode)) { 209 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("duplicateNodeName", name), 210 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 211 } 212 } 213 } 214 invalidate(); 215 } 216 217 private void renameNode(CatalogTreeNode selectedNode) { 218 if (selectedNode == null) { 219 selectedNode = _index.getSelectedNode(); 220 } 221 if (selectedNode == null) { 222 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("selectRenameNode"), 223 Bundle.getMessage(MESSAGE_TITLE), JmriJOptionPane.INFORMATION_MESSAGE); 224 } else { 225 String name = JmriJOptionPane.showInputDialog(this, Bundle.getMessage("newNameNode"), 226 selectedNode.getUserObject().toString()); 227 if (name != null) { 228 if (!_index.renameNode(selectedNode, name)) { 229 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("duplicateNodeName", name), 230 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 231 } 232 233 } 234 } 235 invalidate(); 236 } 237 238 private void deleteNode(CatalogTreeNode selectedNode) { 239 if (selectedNode == null) { 240 selectedNode = _index.getSelectedNode(); 241 } 242 if (selectedNode == null) { 243 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("selectDeleteNode"), 244 Bundle.getMessage(MESSAGE_TITLE), JmriJOptionPane.INFORMATION_MESSAGE); 245 return; 246 } 247 log.debug("delete node \"{}\" level= {}.", selectedNode, selectedNode.getLevel()); 248 if (selectedNode.getLevel() <= 1) { 249 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("deleteRootNode"), 250 Bundle.getMessage(MESSAGE_TITLE), JmriJOptionPane.INFORMATION_MESSAGE); 251 } else { 252 int numNodes = countSubNodes(selectedNode); 253 int numIcons = countIcons(selectedNode); 254 int response = JmriJOptionPane.showConfirmDialog(this, 255 Bundle.getMessage("confirmDeleteNode", selectedNode.getUserObject(), numNodes, numIcons), 256 Bundle.getMessage("QuestionTitle"), JmriJOptionPane.YES_NO_OPTION, 257 JmriJOptionPane.QUESTION_MESSAGE); 258 if (response == JmriJOptionPane.YES_OPTION) { 259 CatalogTreeNode parent = (CatalogTreeNode)selectedNode.getParent(); 260 _index.removeNodeFromModel(selectedNode); 261 _index.setSelectedNode(parent); 262 } 263 } 264 } 265 266 int countSubNodes(CatalogTreeNode node) { 267 int cnt = 0; 268 Enumeration<TreeNode> e = node.children(); 269 while (e.hasMoreElements()) { 270 CatalogTreeNode n = (CatalogTreeNode)e.nextElement(); 271 cnt += countSubNodes(n) + 1; 272 } 273 return cnt; 274 } 275 276 private int countIcons(CatalogTreeNode node) { 277 int cnt = 0; 278 Enumeration<TreeNode> e = node.children(); 279 while (e.hasMoreElements()) { 280 CatalogTreeNode n =(CatalogTreeNode)e.nextElement(); 281 cnt += countIcons(n); 282 } 283 cnt += node.getNumLeaves(); 284 return cnt; 285 } 286 287 @ServiceProvider(service = InstanceInitializer.class) 288 public static class Initializer extends AbstractInstanceInitializer { 289 290 @Override 291 public <T> Object getDefault(Class<T> type) { 292 if (type.equals(ImageIndexEditor.class)) { 293 ImageIndexEditor instance = new ImageIndexEditor(Bundle.getMessage("editIndexFrame")); 294 instance.init(); 295 return instance; 296 } 297 return super.getDefault(type); 298 } 299 300 @Override 301 public Set<Class<?>> getInitalizes() { 302 Set<Class<?>> set = super.getInitalizes(); 303 set.add(ImageIndexEditor.class); 304 return set; 305 } 306 } 307 308 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ImageIndexEditor.class); 309 310}