/*******************************************************************************
* Breakout Cave Survey Visualizer
*
* Copyright (C) 2014 James Edwards
*
* jedwards8 at fastmail dot fm
*
* This program 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 2 of the License, or (at your option) any later
* version.
*
* This program 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
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*******************************************************************************/
package org.andork.ui.debug;
import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.Container;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.AWTEventListener;
import java.awt.event.ContainerEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JTree;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeExpansionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import org.andork.util.ArrayUtils;
@SuppressWarnings("serial")
public class ComponentTree extends JTree {
private class AWTEventHandler implements AWTEventListener {
@Override
public void eventDispatched(AWTEvent event) {
if (event instanceof ContainerEvent) {
ContainerEvent ce = (ContainerEvent) event;
DefaultMutableTreeNode contNode = getNode(ce.getContainer());
if (contNode != null) {
if (event.getID() == ContainerEvent.COMPONENT_ADDED) {
DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) contNode.getParent();
if (contNode == getRoot() || isExpanded(new TreePath(parentNode.getPath()))) {
int childIndex = ArrayUtils.strictIndexOf(ce.getContainer().getComponents(), ce.getChild());
DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(ce.getChild());
if (isExpanded(new TreePath(contNode.getPath()))) {
updateChildren(childNode);
}
contNode.insert(childNode, childIndex);
}
} else if (event.getID() == ContainerEvent.COMPONENT_REMOVED) {
int childIndex = childIndex(contNode, ce.getChild());
if (childIndex >= 0) {
contNode.remove(childIndex);
}
}
}
}
}
}
private class ExpansionHandler implements TreeExpansionListener {
@Override
public void treeCollapsed(TreeExpansionEvent event) {
}
@Override
public void treeExpanded(TreeExpansionEvent event) {
DefaultMutableTreeNode node = getNode(event);
for (int i = 0; i < node.getChildCount(); i++) {
DefaultMutableTreeNode child = (DefaultMutableTreeNode) node.getChildAt(i);
Component childComp = (Component) child.getUserObject();
if (childComp instanceof Container) {
Container childCont = (Container) childComp;
setChildren(child, childCont.getComponents());
}
}
}
}
/**
*
*/
private static final long serialVersionUID = 8961491609539291891L;
private static int childIndex(DefaultMutableTreeNode node, Object childUserObject) {
for (int i = 0; i < node.getChildCount(); i++) {
DefaultMutableTreeNode child = (DefaultMutableTreeNode) node.getChildAt(i);
if (child.getUserObject() == childUserObject) {
return i;
}
}
return -1;
}
private static DefaultMutableTreeNode childNode(DefaultMutableTreeNode node, Object childUserObject) {
int childIndex = childIndex(node, childUserObject);
return childIndex < 0 ? null : (DefaultMutableTreeNode) node.getChildAt(childIndex);
}
private static DefaultMutableTreeNode getNode(TreeExpansionEvent event) {
return getNode(event.getPath());
}
private static DefaultMutableTreeNode getNode(TreePath path) {
return (DefaultMutableTreeNode) path.getLastPathComponent();
}
private static List<Component> getPath(Component c) {
List<Component> result = new ArrayList<Component>();
while (c != null) {
result.add(0, c);
if (c instanceof Window) {
break;
}
c = c.getParent();
}
return result;
}
private static void setChildren(DefaultMutableTreeNode node, Component[] children) {
for (int i = 0; i < children.length; i++) {
int existingIndex = childIndex(node, children[i]);
if (existingIndex < 0) {
node.insert(new DefaultMutableTreeNode(children[i]), i);
} else if (existingIndex != i) {
DefaultMutableTreeNode existingChild = (DefaultMutableTreeNode) node.getChildAt(existingIndex);
node.insert(existingChild, i);
}
}
while (node.getChildCount() > children.length) {
node.remove(children.length);
}
}
private static void updateChildren(DefaultMutableTreeNode node) {
if (node.getUserObject() instanceof Container) {
setChildren(node, ((Container) node.getUserObject()).getComponents());
}
}
private AWTEventHandler awtEventHandler = new AWTEventHandler();
private boolean awtEventHandlerRegistered = false;
public ComponentTree() {
super(new DefaultTreeModel(null));
addTreeExpansionListener(new ExpansionHandler());
}
public void focus(Component c) {
if (c == null) {
setRootComponent(null);
return;
}
List<Component> path = getPath(c);
setRootComponent(path.get(0));
DefaultMutableTreeNode node = getRoot();
for (int i = 1; i < path.size() && node != null; i++) {
expandPath(new TreePath(node.getPath()));
node = childNode(node, path.get(i));
}
if (node != null) {
TreePath nodePath = new TreePath(node.getPath());
setSelectionPath(nodePath);
Rectangle pathBounds = getPathBounds(nodePath);
if (pathBounds != null) {
pathBounds.x = 0;
scrollRectToVisible(pathBounds);
}
}
}
public DefaultMutableTreeNode getNode(Component c) {
List<Component> path = getPath(c);
DefaultMutableTreeNode node = getRoot();
if (node == null || node.getUserObject() != path.get(0)) {
return null;
}
for (int i = 1; i < path.size() && node != null; i++) {
Component child = path.get(i);
node = childNode(node, child);
}
return node;
}
public DefaultMutableTreeNode getRoot() {
return (DefaultMutableTreeNode) getModel().getRoot();
}
public void setAWTEventHandlerRegistered(boolean registered) {
if (registered && !awtEventHandlerRegistered) {
awtEventHandlerRegistered = true;
Toolkit.getDefaultToolkit().addAWTEventListener(awtEventHandler, AWTEvent.CONTAINER_EVENT_MASK);
} else if (!registered && awtEventHandlerRegistered) {
awtEventHandlerRegistered = false;
Toolkit.getDefaultToolkit().removeAWTEventListener(awtEventHandler);
}
}
public void setRootComponent(Component rootComp) {
Object rootUserObject = getRoot() == null ? null : getRoot().getUserObject();
if (rootComp != rootUserObject) {
DefaultTreeModel model = (DefaultTreeModel) getModel();
if (rootComp == null) {
model.setRoot(null);
} else {
DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(rootComp);
updateChildren(rootNode);
for (int i = 0; i < rootNode.getChildCount(); i++) {
updateChildren((DefaultMutableTreeNode) rootNode.getChildAt(i));
}
model.setRoot(rootNode);
}
}
}
}