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 extends IDIYComponent>>) 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
+ + "