📄 smarttree.java
字号:
package com.ca.directory.jxplorer.tree;
import com.ca.commons.cbutil.*;
import com.ca.commons.naming.*;
import com.ca.directory.jxplorer.*;
import com.ca.directory.jxplorer.event.JXplorerEvent;
import com.ca.directory.jxplorer.event.JXplorerEventGenerator;
import com.ca.directory.jxplorer.search.SearchGUI;
import com.ca.directory.jxplorer.viewer.AttributeDisplay;
import com.ca.directory.jxplorer.viewer.PluggableEditor;
import javax.naming.*;
import javax.naming.directory.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.tree.*;
import java.awt.*;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.*;
import java.awt.dnd.peer.DragSourceContextPeer;
import java.awt.event.*;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* SmartTree displays the directory, using configurable
* icons. The SmartTree uses an internal 'SmartNode' class
* which stores information about a visible Entry. The
* display information is separate from the underlying
* directory; if the class is modified or extended, care
* must be taken to always keep the two in sych.
*/
// The drag and drop handling in this code owes a lot to the java world tutorial:
// http://www.javaworld.com/javaworld/javatips/jw-javatip97.html
public class SmartTree extends JTree
implements TreeSelectionListener, DataListener,
TreeExpansionListener, JXplorerEventGenerator,
DragGestureListener, DropTargetListener, DragSourceListener
{
boolean setup = false; // whether the delayed graphics constructor has been called.
boolean activated = false; // whether the delayed action stuff has been called.
public static String NODATA = CBIntText.get("cn=no entries");
JXplorerEventGenerator eventPublisher; // if registered, this object is used to publish external events
// (to programs that are using JXplorer as an embedded component)
//final JTree tree; // the main JTree over which everything is built.
Frame owner; // used for Swing/graphics L&F continuity
SmartNode root; // the node representing the first RDN of the rootDN
SmartNode rootDNBase; // the node representing the lowest RDN of the rootDN (may = top if only 1 rdn in rootDN)
DN rootDN; // the full root DN
SmartModel treeModel; // the tree model used to track tree data
DN currentDN; // the currently selected DN
SmartPopupTool popupTreeTool; // the right-mouse-click tree tools menu
DefaultTreeCellEditor treeEditor; // widget that allows user to edit node names
TreeCellEditor innerEditor; // the custom widget that actually creates the editor component.
SmartTreeCellRenderer treeRenderer; // widget that display the nodes (text + icons)
public boolean rootSet = false; // whether the root node is set.
DataSource treeDataSource; // where the tree obtains its data
Vector treeDataSinks = new Vector(); // a list of objects interested in tree changes
//int treeCapabilities; // a bit mask of DataQueries to respond to.
public DXEntry entry; //TE: the current entry.
String name; // a unique name for the tree.
AttributeDisplay pluggableEditorSource = null; // if initialised, this can find pluggable editors to set extended tree behaviour
private SearchGUI searchGUI = null;
static int treeNo = 0;
public boolean dragging = false; // whether a drag 'n drop operation is in progress.
/* Variables needed for DnD */
private DragSource dragSource = null;
//private DragSourceContext dragSourceContext = null;
private Point cursorLocation = null;
/**
* A holder for the number of results returned by a search.
* This is used in the status bar for user info.
*/
public int numOfResults = 0;
private static Logger log = Logger.getLogger(SmartTree.class.getName());
/**
* Constructor for SmartTree. The Tree starts off disconnected, and
* must be later linked to a data source (using @RegisterDataSource)
* to become active.
*
* @param Owner the owning awt component - used for look and feel updates
* @param name the 'name' of the tree - used for debugging,
* @param resourceLoader - a resource Loader used to get extra tree icons. May be null.
*/
public SmartTree(Frame Owner, String name, CBResourceLoader resourceLoader)
{
treeNo++;
owner = Owner;
this.name = name;
setRoot(NODATA);
setup = true; // one way or another, only do this once!
SmartNode.init(resourceLoader);
/*
* a custom renderer, shows mutli valued attributes and icons.
*/
treeRenderer = new SmartTreeCellRenderer();
setCellRenderer(treeRenderer);
/*
* custom editor, allows editing of multi-valued rdn.
*/
//treeEditor = new SmartTreeCellEditor(this, new DefaultTreeCellRenderer());
treeEditor = new SmartTreeCellEditor(this, treeRenderer);
setCellEditor(treeEditor);
treeModel = new SmartModel(root);
setModel(treeModel);
registerPopupTool(new SmartPopupTool(this));
// disallow multiple selections...
getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
addTreeSelectionListener(this);
addTreeExpansionListener(this);
// use the default editor, but nobble it to avoid editing when drag and drop
// is operating, and stick special handling in for multi valued rdn editing...
setTreeMouseListener();
setTreeCellEditorListener();
setEditable(true); // make sure the user can edit tree cells.
setupDragAndDrop();
}
public String getName()
{
return name;
}
public String toString()
{
return name;
}
/**
* Returns the currently selected Node.
*/
protected SmartNode getSelectedNode()
{
if (getSelectionPath() == null) return null;
return (SmartNode) getSelectionPath().getLastPathComponent();
}
/**
* Set the popup tool that will be used by the tree.
*/
public void registerPopupTool(SmartPopupTool tool)
{
popupTreeTool = tool;
}
/**
* returns a vector of DNs being the all inclusive list of all DNs
* in the subtree from the given apex DN. Used by ldif save ftn.
*
* @return a Vector of DNs
*/
public Vector getAllNodes(DN start)
{
if (start == null) return new Vector(0); // sanity check
SmartNode apex = treeModel.getNodeForDN(start); // need the smart node to find children
if (apex == null) return new Vector(0); // another sanity check
try
{
Vector result = new Vector(10);
result.add(start); //
Enumeration children = apex.children(); // find children of 'from'
while (children.hasMoreElements())
{
DN next = new DN(start);
next.addChildRDN(((SmartNode) children.nextElement()).getRDN());
result.addAll(getAllNodes(next));
}
return result;
}
catch (Exception e)
{
log.log(Level.WARNING, "error in SmartTree dump: ", e);
return new Vector(0);
}
}
/**
* This sets the root of the tree, creating the nodes
* necessary to display it. A small wrinkle: the
* 'rootDN' is the base DN as given by the user, e.g.
* 'o=Democorp,c=us'. This may contain multiple RDNs.
* the root node, however, is the top level node in the
* display tree; it has only a single RDN (i.e. 'c=us'
* in the above example.) The rootDN therefore may be
* displayed as a number of nodes in the tree (maybe the
* rootDN should be renamed the 'baseDN' or something...)
*
* @param rootString the DN of the root as a string
*/
public void setRoot(String rootString)
{
if (rootString == null)
rootString = "";
rootDN = new DN(rootString); // rootString MAY BE BLANK!
setRoot(rootDN);
}
/**
* This sets the root of the tree, creating the nodes
* necessary to display it. A small wrinkle: the
* 'rootDN' is the base DN as given by the user, e.g.
* 'o=Democorp,c=us'. This may contain multiple RDNs.
* the root node, however, is the top level node in the
* display tree; it has only a single RDN (i.e. 'c=us'
* in the above example.) The rootDN therefore may be
* displayed as a number of nodes in the tree (maybe the
* rootDN should be renamed the 'baseDN' or something...)
*
* @param rootDN the DN of the root as a DN object
*/
public void setRoot(DN rootDN)
{
rootSet = true;
if (rootDN == null) rootDN = new DN(); // equivalent to empty DN, 'cn=World'
/*
* Special handling for 'No Data' trees...
*/
if (NODATA.equals(rootDN.toString()))
{
rootDN = new DN(NODATA);
root = new SmartNode(NODATA);
rootDNBase = root;
rootSet = false;
if (treeModel != null)
{
treeModel.setRoot(root);
treeModel.reload();
}
return;
}
this.rootDN = rootDN;
root = new SmartNode(""); // equivalent to new SmartNode("cn=World")
root.setRoot(true);
root.setStructural(true);
treeModel.setRoot(root); // reset the tree Model
// set up the path to the lowest node of the DN, creating smart nodes for each RDN.
SmartNode parent = root;
rootDNBase = root;
for (int i = 0; i < rootDN.size(); i++)
{
SmartNode child = new SmartNode(rootDN.getRDN(i));
child.setStructural(true);
parent.add(child);
parent = child;
}
rootDNBase = parent;
rootDNBase.add(new SmartNode()); // stick in a dummy node so there's something to expand to...
treeModel.reload();
if (rootDN.size() > 0)
expandPath(treeModel.getPathForNode(rootDNBase));
else
collapseRow(0);
}
/**
* Forces a particular DN that is already in the tree to be displayed.
*/
public void expandDN(DN dn)
{
TreePath path = treeModel.getPathForDN(dn);
expandPath(path.getParentPath()); // without the 'getParentPath()' the next level children are shown as well.
}
/**
* Forces the root node of the tree to expand.
* (May trigger a list request)
*/
public void expandRoot()
{
expandRow(0);
}
/**
* Get the rootDN of the tree (often the same as the directory DSE
* naming prefix, e.g. 'o=DemoCorp,c=au'.
*
* @return the root DN
*/
public DN getRootDN()
{
return (rootSet) ? rootDN : null;
}
/**
* Get the root node of tree. Needed for root.setStructural() hack
*/
public SmartNode getRootNode()
{
return root;
}
/**
* Get the lowest smart node of the baseDN.
* For example, if base dn is ou=R&D,o=Democorp,c=au, this
* returns the SmartNode corresponding to ou=R&D.
*/
public SmartNode getLowestRootNode()
{
return rootDNBase;
}
/**
* Returns the tree model used by the Smart Tree to store node data in.
*/
public SmartModel getTreeModel()
{
return treeModel;
}
/**
* registers the data source the tree is to use to read its
* data (e.g. a JNDIBroker). There can be only one of these.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -