From 4f2e1db6d73faba5ac2891df9c3d820dca0d2dfc Mon Sep 17 00:00:00 2001 From: bancika Date: Thu, 17 Sep 2020 17:35:17 +0200 Subject: [PATCH] reorganized Searchable Tree, new custom model --- .../src/org/diylc/presenter/update.xml | 4 + diylc/diylc-swing/dict.txt | 15 +- .../swing/plugins/tree/CustomTreeModel.java | 364 +++++++++++ .../plugins/tree/TreeGestureListener.java | 5 +- .../diylc/swing/plugins/tree/TreeNode.java | 82 +++ .../diylc/swing/plugins/tree/TreePanel.java | 573 ++++-------------- 6 files changed, 580 insertions(+), 463 deletions(-) create mode 100644 diylc/diylc-swing/src/org/diylc/swing/plugins/tree/CustomTreeModel.java create mode 100644 diylc/diylc-swing/src/org/diylc/swing/plugins/tree/TreeNode.java diff --git a/diylc/diylc-core/src/org/diylc/presenter/update.xml b/diylc/diylc-core/src/org/diylc/presenter/update.xml index 2c3cee80..cc60ec1b 100644 --- a/diylc/diylc-core/src/org/diylc/presenter/update.xml +++ b/diylc/diylc-core/src/org/diylc/presenter/update.xml @@ -3182,6 +3182,10 @@ IMPROVEMENT Switched from iText to OrsonPDF library for PDF creation. Render text as vectors to ensure consistency with display and avoid font issues + + IMPROVEMENT + Rewritten most of the code behind Searchable Tree functionality to make it faster and more stable + IMPROVEMENT Improved rendering precision, especially when using metric units diff --git a/diylc/diylc-swing/dict.txt b/diylc/diylc-swing/dict.txt index 9d6113a7..d08bf3e7 100644 --- a/diylc/diylc-swing/dict.txt +++ b/diylc/diylc-swing/dict.txt @@ -23,10 +23,8 @@ 5PDT 5PDT (Center OFF) AWG -Access User Files And C-B-E BJT -Click the lighbulb icon in the bottom-right corner of the window for more info. D-G-S JFET D-G-S MOSFET D-S-G JFET @@ -39,6 +37,8 @@ DP5T DPDT DPDT (Center OFF) Deutsch +Dimension Mode +Donate E-B-C BJT English Espaņol @@ -47,9 +47,11 @@ G-D-S JFET G-D-S MOSFET G-S-D JFET G-S-D MOSFET -Inline Italiano +Lin +Log NPN +Nand Nederlands Nor Not @@ -57,19 +59,14 @@ Or PAF PCB PNP -Round WOG A -Round WOG B +Rev log S-G-D JFET S-G-D MOSFET SMD SPDT SPDT (Center OFF) SPST -Selection is empty. -Soap Bar -Sprache Srpski -Version history is not available. Xnor Xor cm diff --git a/diylc/diylc-swing/src/org/diylc/swing/plugins/tree/CustomTreeModel.java b/diylc/diylc-swing/src/org/diylc/swing/plugins/tree/CustomTreeModel.java new file mode 100644 index 00000000..40ccac58 --- /dev/null +++ b/diylc/diylc-swing/src/org/diylc/swing/plugins/tree/CustomTreeModel.java @@ -0,0 +1,364 @@ +/* + * + * DIY Layout Creator (DIYLC). Copyright (c) 2009-2018 held jointly by the individual authors. + * + * This file is part of DIYLC. + * + * DIYLC is free software: you can redistribute it and/or modify it under the terms of the GNU + * General Public License as published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * DIYLC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with DIYLC. If not, see + * . + * + */ +package org.diylc.swing.plugins.tree; + +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.Collections; +import java.util.EventListener; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +import javax.swing.SwingUtilities; +import javax.swing.event.EventListenerList; +import javax.swing.event.TreeModelEvent; +import javax.swing.event.TreeModelListener; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreePath; + +import org.apache.log4j.Logger; +import org.diylc.appframework.miscutils.ConfigurationManager; +import org.diylc.appframework.miscutils.IConfigListener; +import org.diylc.common.ComponentType; +import org.diylc.common.Favorite; +import org.diylc.common.IBlockProcessor.InvalidBlockException; +import org.diylc.common.IPlugInPort; +import org.diylc.core.IDIYComponent; + +public class CustomTreeModel implements TreeModel { + + private static final Logger LOG = Logger.getLogger(CustomTreeModel.class); + + private IPlugInPort plugInPort; + private Object root = "root"; + private String searchText = null; + private Map> componentTypes; + private List categories; + private List visibleCategories; + private Map> visibleLeaves; + private Map typesByClass; + + private List favorites; + private List recentComponents; + private List blocks; + + private EventListenerList listenerList = new EventListenerList(); + + private static final String FAVORITES = "(Favorites)"; + private static final String RECENTLY_USED = "(Recently Used)"; + private static final String BUILDING_BLOCKS = "(Building Blocks)"; + + public CustomTreeModel(IPlugInPort plugInPort) { + super(); + this.plugInPort = plugInPort; + this.componentTypes = plugInPort.getComponentTypes(); + this.categories = new ArrayList(this.componentTypes.keySet()); + Collections.sort(this.categories); + this.categories.add(0, BUILDING_BLOCKS); + this.categories.add(0, RECENTLY_USED); + this.categories.add(0, FAVORITES); + this.visibleLeaves = new HashMap>(); + + this.typesByClass = new HashMap(); + for (Map.Entry> e : componentTypes.entrySet()) + for (ComponentType c : e.getValue()) + typesByClass.put(c.getInstanceClass().getCanonicalName(), c); + + this.initialize(); + updateVisibleCategories(); + } + + public String getSearchText() { + return searchText; + } + + public void setSearchText(String searchText) { + this.searchText = searchText.toLowerCase(); + updateVisibleCategories(); + } + + private void updateVisibleCategories() { + this.visibleCategories = new ArrayList(); + for (String category : this.categories) + if (categoryMatches(this.searchText, category)) + this.visibleCategories.add(new TreeNode(category, null, false)); + + this.visibleLeaves.clear(); + + fireTreeStructureChanged(new Object[] { root }); + } + + private List getLeavesFor(String category) { + if (this.visibleLeaves.containsKey(category)) + return this.visibleLeaves.get(category); + List visibleTypes = new ArrayList(); + + if (FAVORITES.equals(category)) { + for (Favorite fav : this.favorites) { + ComponentType type = this.typesByClass.get(fav.getName()); + if (this.searchText == null || type.getName().toLowerCase().contains(this.searchText)) { + visibleTypes.add(new TreeNode(type, new MouseAdapter() { + + @Override + public void mouseClicked(MouseEvent e) { + plugInPort.setNewComponentTypeSlot(type, null, false); + } + })); + } + } + } + + if (RECENTLY_USED.equals(category)) { + for (String className : this.recentComponents) { + ComponentType type = this.typesByClass.get(className); + if (this.searchText == null || type.getName().toLowerCase().contains(this.searchText)) { + visibleTypes.add(new TreeNode(type, new MouseAdapter() { + + @Override + public void mouseClicked(MouseEvent e) { + plugInPort.setNewComponentTypeSlot(type, null, false); + } + })); + } + } + } + + if (BUILDING_BLOCKS.equals(category)) { + for (String block : this.blocks) { + if (this.searchText == null || block.toLowerCase().contains(this.searchText)) { + visibleTypes.add(new TreeNode(block, new MouseAdapter() { + + long previousActionTime = 0; + + @Override + public void mouseClicked(MouseEvent e) { + if (e == null || SwingUtilities.isLeftMouseButton(e) && System.currentTimeMillis() - previousActionTime > 100) { + previousActionTime = System.currentTimeMillis(); + try { + plugInPort.loadBlock(block); + } catch (InvalidBlockException e1) { + e1.printStackTrace(); + } + } + } + }, true)); + } + } + } + + if (componentTypes.containsKey(category)) + for (ComponentType type : componentTypes.get(category)) + if (this.searchText == null + || category.toLowerCase().contains(this.searchText.toLowerCase()) || + + typeMatches(this.searchText, type)) + visibleTypes.add(new TreeNode(type, new MouseAdapter() { + + @Override + public void mouseClicked(MouseEvent e) { + plugInPort.setNewComponentTypeSlot(type, null, false); + } + })); + this.visibleLeaves.put(category, visibleTypes); + return visibleTypes; + } + + private boolean typeMatches(String searchText, ComponentType type) { + if (searchText == null || searchText.isEmpty()) + return true; + if (type.getName().toLowerCase().contains(searchText)) + return true; + return false; + } + + private boolean categoryMatches(String searchText, String category) { + if (searchText == null || searchText.isEmpty()) + return true; + if (category.toLowerCase().contains(searchText)) + return true; + + if (FAVORITES.equals(category)) { + for (Favorite fav : this.favorites) { + ComponentType type = typesByClass.get(fav.getName()); + if (type.getName().toLowerCase().contains(searchText)) + return true; + } + } + + if (RECENTLY_USED.equals(category)) { + for (String className : this.recentComponents) { + ComponentType type = typesByClass.get(className); + if (type.getName().toLowerCase().contains(searchText)) + return true; + } + } + + if (BUILDING_BLOCKS.equals(category)) { + for (String block : this.blocks) { + if (block.toLowerCase().contains(searchText)) + return true; + } + } + + if (componentTypes.containsKey(category)) + for (ComponentType type : componentTypes.get(category)) + if (typeMatches(searchText, type)) + return true; + return false; + } + + @Override + public Object getRoot() { + return root; + } + + @Override + public Object getChild(Object parent, int index) { + if (parent == root) + return visibleCategories.get(index); + + List typesFor = getLeavesFor(parent.toString()); + Object obj = typesFor.get(index); + return obj; + } + + @Override + public int getChildCount(Object parent) { + if (parent == root) + return visibleCategories.size(); + List typesFor = getLeavesFor(parent.toString()); + return typesFor.size(); + } + + @Override + public boolean isLeaf(Object node) { + if (node == root) + return false; + + if (node instanceof TreeNode) { + TreeNode p = (TreeNode) node; + return p.isLeaf(); + } + return true; + } + + @Override + public void valueForPathChanged(TreePath path, Object newValue) { + // read only, nothing to do here + } + + @Override + public int getIndexOfChild(Object parent, Object child) { + if (parent == root) + return visibleCategories.indexOf(child); + List typesFor = getLeavesFor(parent.toString()); + return typesFor.indexOf(child); + } + + @Override + public void addTreeModelListener(TreeModelListener l) { + listenerList.add(TreeModelListener.class, l); + } + + @Override + public void removeTreeModelListener(TreeModelListener l) { + listenerList.remove(TreeModelListener.class, l); + } + + protected void fireTreeStructureChanged(Object[] path) { + TreeModelEvent event = new TreeModelEvent(this, path); + EventListener[] listeners = listenerList.getListeners(TreeModelListener.class); + for (int i = 0; i < listeners.length; i++) + ((TreeModelListener) listeners[i]).treeStructureChanged(event); + } + + @SuppressWarnings("unchecked") + private void initialize() { + ConfigurationManager.getInstance().addConfigListener(IPlugInPort.FAVORITES_KEY, + new IConfigListener() { + + @Override + public void valueChanged(String key, Object value) { + List newFavorites = (List) value; + if (newFavorites != null && !newFavorites.equals(CustomTreeModel.this.favorites)) { + LOG.info("Detected favorites change"); + CustomTreeModel.this.favorites = new ArrayList(newFavorites); + CustomTreeModel.this.visibleLeaves.remove(FAVORITES); + fireTreeStructureChanged(new Object[] { root, CustomTreeModel.this.visibleCategories.get(0) }); + } else + LOG.info("Detected no favorites change"); + } + }); + this.favorites = new ArrayList((List) ConfigurationManager.getInstance() + .readObject(IPlugInPort.FAVORITES_KEY, new ArrayList())); + + ConfigurationManager.getInstance().addConfigListener(IPlugInPort.RECENT_COMPONENTS_KEY, + new IConfigListener() { + + @Override + public void valueChanged(String key, Object value) { + List newComponents = (List) value; + if (newComponents != null && !new HashSet(newComponents) + .equals(new HashSet(CustomTreeModel.this.recentComponents))) { + LOG.info("Detected recent component change"); + CustomTreeModel.this.recentComponents = new ArrayList(newComponents); + CustomTreeModel.this.visibleLeaves.remove(RECENTLY_USED); + fireTreeStructureChanged(new Object[] { root, CustomTreeModel.this.visibleCategories.get(1) }); + } else + LOG.info("Detected no recent component change"); + } + }); + this.recentComponents = new ArrayList((List) ConfigurationManager.getInstance() + .readObject(IPlugInPort.RECENT_COMPONENTS_KEY, new ArrayList())); + + ConfigurationManager.getInstance().addConfigListener(IPlugInPort.BLOCKS_KEY, + new IConfigListener() { + + @Override + public void valueChanged(String key, Object value) { + Map>> newBlocks = + (Map>>) value; + if (newBlocks != null) { + List blockNames = new ArrayList(newBlocks.keySet()); + Collections.sort(blockNames); + if (!blockNames.equals(CustomTreeModel.this.blocks)) { + LOG.info("Detected block change"); + CustomTreeModel.this.blocks = blockNames; + CustomTreeModel.this.visibleLeaves.remove(BUILDING_BLOCKS); + fireTreeStructureChanged(new Object[] { root, CustomTreeModel.this.visibleCategories.get(2 )}); + } else + LOG.info("Detected no block change"); + } else + LOG.info("Detected no block change"); + } + }); + Map>> newBlocks = + (Map>>) ConfigurationManager.getInstance() + .readObject(IPlugInPort.BLOCKS_KEY, null); + if (newBlocks == null) + this.blocks = new ArrayList(); + else { + this.blocks = new ArrayList(newBlocks.keySet()); + Collections.sort(this.blocks); + } + } +} diff --git a/diylc/diylc-swing/src/org/diylc/swing/plugins/tree/TreeGestureListener.java b/diylc/diylc-swing/src/org/diylc/swing/plugins/tree/TreeGestureListener.java index 203d1cbe..ba9742ac 100644 --- a/diylc/diylc-swing/src/org/diylc/swing/plugins/tree/TreeGestureListener.java +++ b/diylc/diylc-swing/src/org/diylc/swing/plugins/tree/TreeGestureListener.java @@ -28,7 +28,6 @@ import javax.swing.tree.TreePath; import org.diylc.common.ComponentType; import org.diylc.common.IPlugInPort; import org.diylc.swing.plugins.canvas.EmptyTransferable; -import org.diylc.swing.plugins.tree.TreePanel.Payload; /** * {@link DragGestureListener} for {@link TreePanel}. @@ -47,14 +46,14 @@ class TreeGestureListener implements DragGestureListener { @Override public void dragGestureRecognized(DragGestureEvent dge) { ComponentType type = null; - Payload payload = null; + TreeNode payload = null; if (presenter.getNewComponentTypeSlot() == null) { JTree tree = (JTree) dge.getComponent(); Point p = dge.getDragOrigin(); TreePath path = tree.getClosestPathForLocation(p.x, p.y); if (path != null && path.getLastPathComponent() instanceof DefaultMutableTreeNode) { DefaultMutableTreeNode leaf = (DefaultMutableTreeNode) path.getLastPathComponent(); - payload = (Payload) leaf.getUserObject(); + payload = (TreeNode) leaf.getUserObject(); if (payload != null && (payload.getComponentType() != null || payload.getClickListener() != null)) type = payload.getComponentType(); } diff --git a/diylc/diylc-swing/src/org/diylc/swing/plugins/tree/TreeNode.java b/diylc/diylc-swing/src/org/diylc/swing/plugins/tree/TreeNode.java new file mode 100644 index 00000000..b3dcb4f0 --- /dev/null +++ b/diylc/diylc-swing/src/org/diylc/swing/plugins/tree/TreeNode.java @@ -0,0 +1,82 @@ +/* + + DIY Layout Creator (DIYLC). + Copyright (c) 2009-2018 held jointly by the individual authors. + + This file is part of DIYLC. + + DIYLC is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + DIYLC is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with DIYLC. If not, see . + +*/ +package org.diylc.swing.plugins.tree; + +import java.awt.event.MouseListener; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.diylc.common.ComponentType; + +public class TreeNode { + private ComponentType componentType; + private String category; + private MouseListener clickListener; + private boolean isLeaf; + + private final Pattern contributedPattern = Pattern.compile("^(.*)\\[(.*)\\]"); + + public TreeNode(ComponentType componentType, MouseListener clickListener) { + super(); + this.componentType = componentType; + this.clickListener = clickListener; + this.isLeaf = true; + } + + public TreeNode(String category, MouseListener clickListener, boolean isLeaf) { + super(); + this.category = category; + this.clickListener = clickListener; + this.isLeaf = isLeaf; + } + + public ComponentType getComponentType() { + return componentType; + } + + public MouseListener getClickListener() { + return clickListener; + } + + public boolean isLeaf() { + return isLeaf; + } + + public String forDisplay() { + if (componentType == null) { + String display = category; + Matcher match = contributedPattern.matcher(display); + if (match.find()) { + String name = match.group(1); + String owner = match.group(2); + display = name + "[" + owner + "]"; + } + return display; + } + return componentType.getName(); + } + + @Override + public String toString() { + return componentType == null ? category : componentType.getName(); + } +} \ No newline at end of file diff --git a/diylc/diylc-swing/src/org/diylc/swing/plugins/tree/TreePanel.java b/diylc/diylc-swing/src/org/diylc/swing/plugins/tree/TreePanel.java index 55f3748d..60e83b4b 100644 --- a/diylc/diylc-swing/src/org/diylc/swing/plugins/tree/TreePanel.java +++ b/diylc/diylc-swing/src/org/diylc/swing/plugins/tree/TreePanel.java @@ -34,17 +34,12 @@ import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.List; -import java.util.Map; import java.util.Map.Entry; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import javax.swing.AbstractAction; import javax.swing.Icon; @@ -67,7 +62,6 @@ import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeCellRenderer; -import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; @@ -78,14 +72,12 @@ import org.diylc.appframework.miscutils.IConfigListener; import org.diylc.common.ComponentType; import org.diylc.common.Favorite; import org.diylc.common.Favorite.FavoriteType; -import org.diylc.common.IBlockProcessor.InvalidBlockException; import org.diylc.common.IPlugInPort; import org.diylc.core.IDIYComponent; import org.diylc.core.IView; import org.diylc.core.Template; import org.diylc.images.IconLoader; import org.diylc.lang.LangUtil; -import org.diylc.presenter.ComponentProcessor; import org.diylc.swing.ISwingUI; import org.diylc.swing.plugins.toolbox.ComponentButtonFactory; @@ -94,29 +86,24 @@ public class TreePanel extends JPanel { private static final long serialVersionUID = 1L; private static final Logger LOG = Logger.getLogger(TreePanel.class); - + private static final String CLICK_TO_INSTANTIATE = LangUtil.translate("Left click to instantiate this component, right click for more options"); public static final String COMPONENT_SHORTCUT_KEY = "componentShortcuts"; - private DefaultTreeModel treeModel; + private CustomTreeModel treeModel; private JTree tree; private JScrollPane treeScroll; private JTextField searchField; - private DefaultMutableTreeNode recentNode; - private DefaultMutableTreeNode blocksNode; - private DefaultMutableTreeNode favoritesNode; - private List recentComponents; private List favorites; - private List blocks; private IPlugInPort plugInPort; private ISwingUI swingUI; - private boolean initializing = false; - private JPopupMenu popup; + private JPopupMenu popup; + @SuppressWarnings("unchecked") public TreePanel(IPlugInPort plugInPort, ISwingUI swingUI) { this.plugInPort = plugInPort; this.swingUI = swingUI; @@ -140,76 +127,36 @@ public class TreePanel extends JPanel { add(getTreeScroll(), gbc); setPreferredSize(new Dimension(240, 200)); + + ConfigurationManager.getInstance().addConfigListener(IPlugInPort.FAVORITES_KEY, + new IConfigListener() { - ConfigurationManager.getInstance().addConfigListener(IPlugInPort.RECENT_COMPONENTS_KEY, new IConfigListener() { - - @SuppressWarnings("unchecked") - @Override - public void valueChanged(String key, Object value) { - List newComponents = (List) value; - if (newComponents != null - && !new HashSet(newComponents).equals(new HashSet(TreePanel.this.recentComponents))) { - LOG.info("Detected recent component change"); - refreshRecentComponents(newComponents); - } else - LOG.info("Detected no recent component change"); - TreePanel.this.recentComponents = new ArrayList(newComponents); - } - }); - - ConfigurationManager.getInstance().addConfigListener(IPlugInPort.BLOCKS_KEY, new IConfigListener() { - - @SuppressWarnings("unchecked") - @Override - public void valueChanged(String key, Object value) { - Map>> newBlocks = (Map>>) value; - if (newBlocks != null) { - List blockNames = new ArrayList(newBlocks.keySet()); - Collections.sort(blockNames); - if (!blockNames.equals(TreePanel.this.blocks)) { - LOG.info("Detected block change"); - refreshBuildingBlocks(blockNames); - } else - LOG.info("Detected no block change"); - } else - LOG.info("Detected no block change"); - } - }); - - ConfigurationManager.getInstance().addConfigListener(IPlugInPort.TEMPLATES_KEY, new IConfigListener() { - - @Override - public void valueChanged(String key, Object value) { - LOG.info("Detected variants change, repainting the tree"); - repaint(); - } - }); - - ConfigurationManager.getInstance().addConfigListener(IPlugInPort.FAVORITES_KEY, new IConfigListener() { - - @SuppressWarnings("unchecked") - @Override - public void valueChanged(String key, Object value) { - List newFavorites = (List) value; - if (newFavorites != null && !newFavorites.equals(TreePanel.this.favorites)) { - LOG.info("Detected favorites change"); - refreshFavorites(newFavorites); - } else - LOG.info("Detected no favorites change"); - } - }); + @Override + public void valueChanged(String key, Object value) { + List newFavorites = (List) value; + if (newFavorites != null && !newFavorites.equals(TreePanel.this.favorites)) { + LOG.info("Detected favorites change"); + TreePanel.this.favorites = new ArrayList(newFavorites); + } else + LOG.info("Detected no favorites change"); + } + }); + this.favorites = (List) ConfigurationManager.getInstance() + .readObject(IPlugInPort.FAVORITES_KEY, new ArrayList()); getTree().expandRow(0); - + initializeDnD(); } - + private void initializeDnD() { // Initialize drag source recognizer. DragSource.getDefaultDragSource().createDefaultDragGestureRecognizer(getTree(), - DnDConstants.ACTION_COPY_OR_MOVE | DnDConstants.ACTION_LINK, new TreeGestureListener(plugInPort)); + DnDConstants.ACTION_COPY_OR_MOVE | DnDConstants.ACTION_LINK, + new TreeGestureListener(plugInPort)); // Initialize drop target. - new DropTarget(getTree(), DnDConstants.ACTION_COPY_OR_MOVE, new TreeTargetListener(plugInPort), true); + new DropTarget(getTree(), DnDConstants.ACTION_COPY_OR_MOVE, new TreeTargetListener(plugInPort), + true); } public JScrollPane getTreeScroll() { @@ -218,220 +165,49 @@ public class TreePanel extends JPanel { treeScroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); } return treeScroll; - } + } - public DefaultMutableTreeNode getRecentNode() { - if (this.recentNode == null) { - this.recentNode = new DefaultMutableTreeNode(new Payload("(Recently Used)", null), true); - - @SuppressWarnings("unchecked") - List recent = - (List) ConfigurationManager.getInstance().readObject(IPlugInPort.RECENT_COMPONENTS_KEY, null); - if (recent != null) { - this.recentComponents = new ArrayList(recent); - refreshRecentComponents(recent); - } else { - this.recentComponents = new ArrayList(); - } - } - return this.recentNode; - } - - public DefaultMutableTreeNode getBlocksNode() { - if (this.blocksNode == null) { - this.blocksNode = new DefaultMutableTreeNode(new Payload("(Building Blocks)", null), true); - - @SuppressWarnings("unchecked") - Map>> newBlocks = - (Map>>) ConfigurationManager.getInstance().readObject( - IPlugInPort.BLOCKS_KEY, null); - if (newBlocks != null) { - List blockNames = new ArrayList(newBlocks.keySet()); - Collections.sort(blockNames); - refreshBuildingBlocks(blockNames); - } else { - this.blocks = new ArrayList(); - } - } - return this.blocksNode; - } - - public DefaultMutableTreeNode getFavoritesNode() { - if (this.favoritesNode == null) { - this.favoritesNode = new DefaultMutableTreeNode(new Payload("(Favorites)", null), true); - - @SuppressWarnings("unchecked") - List favorites = (List) ConfigurationManager.getInstance().readObject(IPlugInPort.FAVORITES_KEY, null); - if (favorites != null) { - refreshFavorites(favorites); - } else { - this.favorites = new ArrayList(); - } - } - return this.favoritesNode; - } - - @SuppressWarnings("unchecked") - private void refreshRecentComponents(List recentComponentClassList) { - getRecentNode().removeAllChildren(); - for (String componentClassName : recentComponentClassList) { - final ComponentType componentType; - try { - componentType = - ComponentProcessor.getInstance().extractComponentTypeFrom( - (Class>) Class.forName(componentClassName)); - Payload payload = new Payload(componentType, new MouseAdapter() { - - @Override - public void mouseClicked(MouseEvent e) { - if (plugInPort.getNewComponentTypeSlot() != componentType) - plugInPort.setNewComponentTypeSlot(componentType, null, false); - } - }); - final DefaultMutableTreeNode componentNode = new DefaultMutableTreeNode(payload, false); - String text = getSearchField().getText(); - boolean visible = - text.trim().length() == 0 || componentType.getName().toLowerCase().contains(text.toLowerCase()) - || componentType.getDescription().toLowerCase().contains(text.toLowerCase()) - || componentType.getCategory().toLowerCase().contains(text.toLowerCase()); - payload.setVisible(visible); - getRecentNode().add(componentNode); - } catch (ClassNotFoundException e) { - LOG.error("Could not create recent component button for " + componentClassName, e); - } - } - if (!initializing) - getTreeModel().nodeStructureChanged(getRecentNode()); - } - - private void refreshBuildingBlocks(List blocks) { - this.blocks = blocks; - - getBlocksNode().removeAllChildren(); - for (final String block : blocks) { - Payload payload = new Payload(block, new MouseAdapter() { - - long previousActionTime = 0; - - @Override - public void mouseClicked(MouseEvent e) { - if (e == null || SwingUtilities.isLeftMouseButton(e) && System.currentTimeMillis() - previousActionTime > 100) { - previousActionTime = System.currentTimeMillis(); - try { - plugInPort.loadBlock(block); - } catch (InvalidBlockException e1) { - // TODO Auto-generated catch block - e1.printStackTrace(); - } - } - } - }); - final DefaultMutableTreeNode componentNode = new DefaultMutableTreeNode(payload, false); - String text = getSearchField().getText(); - boolean visible = text.trim().length() == 0 || block.toLowerCase().contains(text.toLowerCase()); - payload.setVisible(visible); - getBlocksNode().add(componentNode); - } - if (!initializing) - getTreeModel().nodeStructureChanged(getBlocksNode()); - } - - private void refreshFavorites(List favorites) { - this.favorites = new ArrayList(favorites); - - getFavoritesNode().removeAllChildren(); - - Map> types = plugInPort.getComponentTypes(); - Map typesByClass = new HashMap(); - for (Map.Entry> e : types.entrySet()) - for (ComponentType c : e.getValue()) - typesByClass.put(c.getInstanceClass().getCanonicalName(), c); - - for (Favorite f : favorites) { - if (f.getType() == FavoriteType.Component) { - final ComponentType type = typesByClass.get(f.getName()); - if (type != null) { - DefaultMutableTreeNode componentNode = new DefaultMutableTreeNode(new Payload(type, new MouseAdapter() { - - @Override - public void mouseClicked(MouseEvent e) { - plugInPort.setNewComponentTypeSlot(type, null, false); - } - }), false); - getFavoritesNode().add(componentNode); - } - } else if (f.getType() == FavoriteType.Block) { - final String block = f.getName(); - Payload payload = new Payload(block, new MouseAdapter() { - - long previousActionTime = 0; - - @Override - public void mouseClicked(MouseEvent e) { - if (e == null || SwingUtilities.isLeftMouseButton(e) && System.currentTimeMillis() - previousActionTime > 100) { - previousActionTime = System.currentTimeMillis(); - try { - plugInPort.loadBlock(block); - } catch (InvalidBlockException e1) { - // TODO Auto-generated catch block - e1.printStackTrace(); - } - } - } - }); - final DefaultMutableTreeNode componentNode = new DefaultMutableTreeNode(payload, false); - String text = getSearchField().getText(); - boolean visible = text.trim().length() == 0 || block.toLowerCase().contains(text.toLowerCase()); - payload.setVisible(visible); - getFavoritesNode().add(componentNode); - } - } - if (!initializing) - getTreeModel().nodeStructureChanged(getFavoritesNode()); - } - - public DefaultTreeModel getTreeModel() { + public CustomTreeModel getTreeModel() { if (treeModel == null) { - final DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode("Components", true); - initializing = true; - rootNode.add(getFavoritesNode()); - rootNode.add(getRecentNode()); - rootNode.add(getBlocksNode()); - initializing = false; - Map> componentTypes = plugInPort.getComponentTypes(); - List categories = new ArrayList(componentTypes.keySet()); - Collections.sort(categories); - for (String category : categories) { - final DefaultMutableTreeNode categoryNode = new DefaultMutableTreeNode(new Payload(category, null)); - rootNode.add(categoryNode); - for (final ComponentType type : componentTypes.get(category)) { - final DefaultMutableTreeNode componentNode = new DefaultMutableTreeNode(new Payload(type, new MouseAdapter() { - - @Override - public void mouseClicked(MouseEvent e) { - plugInPort.setNewComponentTypeSlot(type, null, false); - } - }), false); - categoryNode.add(componentNode); - } - } - treeModel = new DefaultTreeModel(rootNode); + treeModel = new CustomTreeModel(plugInPort); treeModel.addTreeModelListener(new TreeModelAdapter() { + @Override - public void treeNodesRemoved(TreeModelEvent e) { - super.treeNodesRemoved(e); - } - - @Override - public void treeNodesChanged(TreeModelEvent e) { - // TODO Auto-generated method stub - super.treeNodesChanged(e); + public void treeStructureChanged(TreeModelEvent e) { + String searchText = treeModel.getSearchText(); + + // when we are searching for something, expand all nodes + if (searchText != null && !searchText.isEmpty()) { + int n = treeModel.getChildCount(treeModel.getRoot()); + for (int i = 0; i < n; i++) { + Object toExpand = treeModel.getChild(treeModel.getRoot(), i); + tree.expandPath(new TreePath(new Object[] { treeModel.getRoot(), toExpand } )); + } + } } }); } return treeModel; } + @SuppressWarnings("unchecked") + public static void setNodeExpandedState(JTree tree, DefaultMutableTreeNode node, + boolean expanded) { + ArrayList list = Collections.list(node.children()); + for (DefaultMutableTreeNode treeNode : list) { + setNodeExpandedState(tree, treeNode, expanded); + } + if (!expanded && node.isRoot()) { + return; + } + TreePath path = new TreePath(node.getPath()); + if (expanded) { + tree.expandPath(path); + } else { + tree.collapsePath(path); + } + } + public JTree getTree() { if (tree == null) { tree = new JTree(getTreeModel()); @@ -447,10 +223,10 @@ public class TreePanel extends JPanel { public void valueChanged(TreeSelectionEvent e) { if (!e.isAddedPath()) return; - - DefaultMutableTreeNode node = (DefaultMutableTreeNode) e.getPath().getLastPathComponent(); - if (node != null && node.getUserObject() != null) { - Payload payload = (Payload) node.getUserObject(); + + Object node = e.getPath().getLastPathComponent(); + if (node != null && node instanceof TreeNode) { + TreeNode payload = (TreeNode) node; if (payload.getClickListener() != null) { payload.getClickListener().mouseClicked(null); } @@ -471,9 +247,9 @@ public class TreePanel extends JPanel { getPopup().show(e.getComponent(), e.getX(), e.getY()); } else { TreePath path = tree.getClosestPathForLocation(e.getX(), e.getY()); - DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent(); - if (node != null && node.getUserObject() != null) { - Payload payload = (Payload) node.getUserObject(); + Object node = path.getLastPathComponent(); + if (node != null && node instanceof TreeNode) { + TreeNode payload = (TreeNode) node; if (payload.getClickListener() != null) { payload.getClickListener().mouseClicked(e); } @@ -495,18 +271,16 @@ public class TreePanel extends JPanel { public void popupMenuWillBecomeVisible(PopupMenuEvent e) { popup.removeAll(); - DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent(); + Object selectedNode = tree.getLastSelectedPathComponent(); - if (selectedNode == null || selectedNode.getUserObject() == null) + if (selectedNode == null || !(selectedNode instanceof TreeNode)) return; - Payload payload = (Payload) selectedNode.getUserObject(); - + TreeNode payload = (TreeNode) selectedNode; final ComponentType componentType = payload.getComponentType(); - final String identifier = - componentType == null ? "block:" + payload.toString() : componentType.getInstanceClass() - .getCanonicalName(); + final String identifier = componentType == null ? "block:" + payload.toString() + : componentType.getInstanceClass().getCanonicalName(); JMenu shortcutSubmenu = new JMenu("Assign Shortcut"); final JMenuItem noneItem = new JMenuItem("None"); @@ -515,8 +289,8 @@ public class TreePanel extends JPanel { @SuppressWarnings("unchecked") @Override public void actionPerformed(ActionEvent e) { - HashMap map = - (HashMap) ConfigurationManager.getInstance().readObject(COMPONENT_SHORTCUT_KEY, null); + HashMap map = (HashMap) ConfigurationManager + .getInstance().readObject(COMPONENT_SHORTCUT_KEY, null); if (map == null) map = new HashMap(); @@ -544,9 +318,8 @@ public class TreePanel extends JPanel { @SuppressWarnings("unchecked") @Override public void actionPerformed(ActionEvent e) { - HashMap map = - (HashMap) ConfigurationManager.getInstance().readObject(COMPONENT_SHORTCUT_KEY, - null); + HashMap map = (HashMap) ConfigurationManager + .getInstance().readObject(COMPONENT_SHORTCUT_KEY, null); if (map == null) map = new HashMap(); @@ -570,11 +343,12 @@ public class TreePanel extends JPanel { }); shortcutSubmenu.add(item); } - - if (selectedNode.isLeaf()) { + + if (getTreeModel().isLeaf(selectedNode)) { final Favorite fav = new Favorite(componentType == null ? FavoriteType.Block : FavoriteType.Component, - componentType == null ? payload.toString() : componentType.getInstanceClass().getCanonicalName()); + componentType == null ? payload.toString() + : componentType.getInstanceClass().getCanonicalName()); final boolean isFavorite = favorites != null && favorites.indexOf(fav) >= 0; final JMenuItem favoritesItem = new JMenuItem(isFavorite ? "Remove From Favorites" : "Add To Favorites", @@ -608,11 +382,12 @@ public class TreePanel extends JPanel { popup.add(item); } else { for (Template template : templates) { - JMenuItem item = ComponentButtonFactory.createVariantItem(plugInPort, template, componentType); + JMenuItem item = + ComponentButtonFactory.createVariantItem(plugInPort, template, componentType); popup.add(item); } } - } else if (selectedNode.isLeaf()) { + } else if (getTreeModel().isLeaf(selectedNode)) { popup.add(shortcutSubmenu); popup.add(new DeleteBlockAction(plugInPort, swingUI, payload.toString())); } @@ -679,55 +454,8 @@ public class TreePanel extends JPanel { public void process() { String text = searchField.getText(); - DefaultTreeModel model = getTreeModel(); - DefaultMutableTreeNode rootNode = (DefaultMutableTreeNode) model.getRoot(); - for (int i = 0; i < model.getChildCount(rootNode); i++) { - int visibleCount = 0; - DefaultMutableTreeNode categoryNode = (DefaultMutableTreeNode) model.getChild(rootNode, i); - for (int j = 0; j < model.getChildCount(categoryNode); j++) { - DefaultMutableTreeNode componentNode = (DefaultMutableTreeNode) model.getChild(categoryNode, j); - Object obj = componentNode.getUserObject(); - if (obj != null && obj.getClass().equals(Payload.class)) { - Payload payload = (Payload) obj; - boolean visible = false; - if (text.trim().length() == 0) { - visible = true; - } else if (payload.getComponentType() != null) { - ComponentType type = payload.getComponentType(); - visible = - type.getName().toLowerCase().contains(text.toLowerCase()) - || type.getDescription().toLowerCase().contains(text.toLowerCase()) - || type.getCategory().toLowerCase().contains(text.toLowerCase()); - } else { - visible = payload.toString().toLowerCase().contains(text.toLowerCase()); - } - if (visible != payload.isVisible()) { - payload.setVisible(visible); - model.nodeStructureChanged(componentNode); - } - if (visible) - visibleCount++; - } - } - - Object obj = categoryNode.getUserObject(); - if (obj != null && obj.getClass().equals(Payload.class)) { - Payload payload = (Payload) obj; - boolean categoryVisible = visibleCount > 0; - if (categoryVisible != payload.isVisible()) { - payload.setVisible(categoryVisible); - model.nodeStructureChanged(rootNode); - } - if (categoryVisible && text.trim().length() > 0) { - getTree().expandPath(new TreePath(categoryNode.getPath())); - } - } - } - - for (int i = 0; i < model.getChildCount(rootNode); i++) { - DefaultMutableTreeNode categoryNode = (DefaultMutableTreeNode) model.getChild(rootNode, i); - getTree().expandPath(new TreePath(categoryNode.getPath())); - } + CustomTreeModel model = getTreeModel(); + model.setSearchText(text); } }); } @@ -740,127 +468,68 @@ public class TreePanel extends JPanel { @SuppressWarnings("unchecked") @Override - public Component getTreeCellRendererComponent(final JTree tree, final Object value, final boolean selected, - final boolean expanded, final boolean leaf, final int row, final boolean hasFocus) { + public Component getTreeCellRendererComponent(final JTree tree, final Object value, + final boolean selected, final boolean expanded, final boolean leaf, final int row, + final boolean hasFocus) { super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus); - Object obj = ((DefaultMutableTreeNode) value).getUserObject(); - if (obj != null && obj.getClass().equals(Payload.class)) { - Payload payload = (Payload) obj; + Object obj = value; + if (obj != null && obj.getClass().equals(TreeNode.class)) { + TreeNode payload = (TreeNode) obj; if (payload.getComponentType() == null) { setToolTipText(null); if (leaf) setIcon(IconLoader.Component.getIcon()); - if (payload.isVisible()) - setPreferredSize(new Dimension(250, 20)); - else - setPreferredSize(new Dimension(0, 0)); + + setPreferredSize(new Dimension(250, 20)); } else { setToolTipText("" + payload.getComponentType().getName() + "
" - + payload.getComponentType().getDescription() + "
Author: " + payload.getComponentType().getAuthor() - + "

" + CLICK_TO_INSTANTIATE + ""); + + payload.getComponentType().getDescription() + "
Author: " + + payload.getComponentType().getAuthor() + "

" + CLICK_TO_INSTANTIATE + + ""); setIcon(payload.getComponentType().getIcon()); - if (payload.isVisible()) - setPreferredSize(new Dimension(250, 32)); - else - setPreferredSize(new Dimension(0, 0)); + setPreferredSize(new Dimension(250, 32)); } - + String shortCutHtml = ""; String variantsHtml = ""; - if (payload.isVisible()) { - HashMap shortcutMap = - (HashMap) ConfigurationManager.getInstance().readObject(TreePanel.COMPONENT_SHORTCUT_KEY, - null); - String identifier = - payload.getComponentType() == null ? "block:" + payload.toString() : payload.getComponentType() - .getInstanceClass().getCanonicalName(); - if (shortcutMap != null && shortcutMap.containsValue(identifier)) { - for (String key : shortcutMap.keySet()) { - if (shortcutMap.get(key).equals(identifier)) { - shortCutHtml = "  " - + key + " "; - } + HashMap shortcutMap = (HashMap) ConfigurationManager + .getInstance().readObject(TreePanel.COMPONENT_SHORTCUT_KEY, null); + String identifier = payload.getComponentType() == null ? "block:" + payload.toString() + : payload.getComponentType().getInstanceClass().getCanonicalName(); + if (shortcutMap != null && shortcutMap.containsValue(identifier)) { + for (String key : shortcutMap.keySet()) { + if (shortcutMap.get(key).equals(identifier)) { + shortCutHtml = + "  " + + key + " "; } } } - + + if (payload.getComponentType() != null) { List