Update of /cvsroot/squirrel-sql/mavenize/thirdparty-non-maven/treetable/src/main/java/com/sun/treetable
In directory sfp-cvsdas-3.v30.ch3.sourceforge.com:/tmp/cvs-serv29983/thirdparty-non-maven/treetable/src/main/java/com/sun/treetable
Added Files:
FileSystemModel2.java AbstractCellEditor.java
AbstractTreeTableModel.java MergeSort.java TreeTableModel.java
TreeTableModelAdapter.java TreeTableExample2.java
JTreeTable.java
Log Message:
thirdparty dependency.
--- NEW FILE: FileSystemModel2.java ---
package com.sun.treetable;
/*
* FileSystemModel2.java
*
* Copyright 1997, 1998 Sun Microsystems, Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY
* DAMAGES OR LIABILITIES SUFFERED BY LICENSEE AS A RESULT OF OR
* RELATING TO USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE OR
* ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE
* FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT,
* SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
* CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF
* THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS
* BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*/
import java.io.IOException;
import java.io.File;
import java.util.Date;
import java.util.Stack;
import javax.swing.SwingUtilities;
import javax.swing.tree.TreePath;
/**
* FileSystemModel2 is a TreeTableModel representing a hierarchical file system.
* <p>
* This will recursively load all the children from the path it is created with. The loading is done with the
* method reloadChildren, and happens in another thread. The method isReloading can be invoked to check if
* there are active threads. The total size of all the files are also accumulated.
* <p>
* By default links are not descended. java.io.File does not have a way to distinguish links, so a file is
* assumed to be a link if its canonical path does not start with its parent path. This may not cover all
* cases, but works for the time being.
* <p>
* Reloading happens such that all the files of the directory are loaded and immediately available. The
* background thread then recursively loads all the files of each of those directories. When each directory
* has finished loading all its sub files they are attached and an event is generated in the event dispatching
* thread. A more ambitious approach would be to attach each set of directories as they are loaded and
* generate an event. Then, once all the direct descendants of the directory being reloaded have finished
* loading, it is resorted based on total size.
* <p>
* While you can invoke reloadChildren as many times as you want, care should be taken in doing this. You
* should not invoke reloadChildren on a node that is already being reloaded, or going to be reloaded (meaning
* its parent is reloading but it hasn't started reloading that directory yet). If this is done odd results
* may happen. FileSystemModel2 does not enforce any policy in this manner, and it is up to the user of
* FileSystemModel2 to ensure it doesn't happen.
*
* @version 1.12 05/12/98
* @author Philip Milne
* @author Scott Violet
*/
public class FileSystemModel2 extends AbstractTreeTableModel
{
// Names of the columns.
static protected String[] cNames = { "Name", "Size", "Type", "Modified" };
// Types of the columns.
static protected Class[] cTypes = { TreeTableModel.class, Integer.class, String.class, Date.class };
// The the returned file length for directories.
public static final Integer ZERO = new Integer(0);
/** An array of MergeSorter sorters, that will sort based on size. */
static Stack sorters = new Stack();
/**
* True if the receiver is valid, once set to false all Threads loading files will stop.
*/
protected boolean isValid;
/**
* Node currently being reloaded, this becomes somewhat muddy if reloading is happening in multiple
* threads.
*/
protected FileNode reloadNode;
/** > 0 indicates reloading some nodes. */
int reloadCount;
/** Returns true if links are to be descended. */
protected boolean descendLinks;
/**
* Returns a MergeSort that can sort on the totalSize of a FileNode.
*/
protected static MergeSort getSizeSorter()
{
synchronized (sorters)
{
if (sorters.size() == 0) { return new SizeSorter(); }
return (MergeSort) sorters.pop();
}
}
/**
* Should be invoked when a MergeSort is no longer needed.
*/
protected static void recycleSorter(MergeSort sorter)
{
synchronized (sorters)
{
sorters.push(sorter);
}
}
/**
* Creates a FileSystemModel2 rooted at File.separator, which is usually the root of the file system. This
* does not load it, you should invoke <code>reloadChildren</code> with the root to start loading.
*/
public FileSystemModel2()
{
this(File.separator);
}
/**
* Creates a FileSystemModel2 with the root being <code>rootPath</code>. This does not load it, you should
* invoke <code>reloadChildren</code> with the root to start loading.
*/
public FileSystemModel2(String rootPath)
{
super(null);
isValid = true;
root = new FileNode(new File(rootPath));
}
//
// The TreeModel interface
//
/**
* Returns the number of children of <code>node</code>.
*/
public int getChildCount(Object node)
{
Object[] children = getChildren(node);
return (children == null) ? 0 : children.length;
}
/**
* Returns the child of <code>node</code> at index <code>i</code>.
*/
public Object getChild(Object node, int i)
{
return getChildren(node)[i];
}
/**
* Returns true if the passed in object represents a leaf, false otherwise.
*/
public boolean isLeaf(Object node)
{
return ((FileNode) node).isLeaf();
}
//
// The TreeTableNode interface.
//
/**
* Returns the number of columns.
*/
public int getColumnCount()
{
return cNames.length;
}
/**
* Returns the name for a particular column.
*/
public String getColumnName(int column)
{
return cNames[column];
}
/**
* Returns the class for the particular column.
*/
public Class getColumnClass(int column)
{
return cTypes[column];
}
/**
* Returns the value of the particular column.
*/
public Object getValueAt(Object node, int column)
{
FileNode fn = (FileNode) node;
try
{
switch (column)
{
case 0:
return fn.getFile().getName();
case 1:
if (fn.isTotalSizeValid()) { return new Integer((int) ((FileNode) node).totalSize()); }
return null;
case 2:
return fn.isLeaf() ? "File" : "Directory";
case 3:
return fn.lastModified();
}
}
catch (SecurityException se)
{
}
return null;
}
//
// Some convenience methods.
//
/**
* Reloads the children of the specified node.
*/
public void reloadChildren(Object node)
{
FileNode fn = (FileNode) node;
synchronized (this)
{
reloadCount++;
}
fn.resetSize();
new Thread(new FileNodeLoader((FileNode) node)).start();
}
/**
* Stops and waits for all threads to finish loading.
*/
public void stopLoading()
{
isValid = false;
synchronized (this)
{
while (reloadCount > 0)
{
try
{
wait();
}
catch (InterruptedException ie)
{
}
}
}
isValid = true;
}
/**
* If <code>newValue</code> is true, links are descended. Odd results may happen if you set this while
* other threads are loading.
*/
public void setDescendsLinks(boolean newValue)
{
descendLinks = newValue;
}
/**
* Returns true if links are to be automatically descended.
*/
public boolean getDescendsLinks()
{
return descendLinks;
}
/**
* Returns the path <code>node</code> represents.
*/
public String getPath(Object node)
{
return ((FileNode) node).getFile().getPath();
}
/**
* Returns the total size of the receiver.
*/
public long getTotalSize(Object node)
{
return ((FileNode) node).totalSize();
}
/**
* Returns true if the receiver is loading any children.
*/
public boolean isReloading()
{
return (reloadCount > 0);
}
/**
* Returns the path to the node that is being loaded.
*/
public TreePath getPathLoading()
{
FileNode rn = reloadNode;
if (rn != null) { return new TreePath(rn.getPath()); }
return null;
}
/**
* Returns the node being loaded.
*/
public Object getNodeLoading()
{
return reloadNode;
}
protected File getFile(Object node)
{
FileNode fileNode = ((FileNode) node);
return fileNode.getFile();
}
protected Object[] getChildren(Object node)
{
FileNode fileNode = ((FileNode) node);
return fileNode.getChildren();
}
protected static FileNode[] EMPTY_CHILDREN = new FileNode[0];
// Used to sort the file names.
static private MergeSort fileMS = new MergeSort()
{
public int compareElementsAt(int beginLoc, int endLoc)
{
return ((String) toSort[beginLoc]).compareTo((String) toSort[endLoc]);
}
};
/**
* A FileNode is a derivative of the File class - though we delegate to the File object rather than
* subclassing it. It is used to maintain a cache of a directory's children and therefore avoid repeated
* access to the underlying file system during rendering.
*/
class FileNode
{
/** java.io.File the receiver represents. */
protected File file;
/** Parent FileNode of the receiver. */
private FileNode parent;
/** Children of the receiver. */
protected FileNode[] children;
/** Size of the receiver and all its children. */
protected long totalSize;
/** Valid if the totalSize has finished being calced. */
protected boolean totalSizeValid;
/** Path of the receiver. */
protected String canonicalPath;
/**
* True if the canonicalPath of this instance does not start with the canonical path of the parent.
*/
protected boolean isLink;
/** Date last modified. */
protected Date lastModified;
protected FileNode(File file)
{
this(null, file);
}
protected FileNode(FileNode parent, File file)
{
this.parent = parent;
this.file = file;
try
{
canonicalPath = file.getCanonicalPath();
}
catch (IOException ioe)
{
canonicalPath = "";
}
if (parent != null)
{
isLink = !canonicalPath.startsWith(parent.getCanonicalPath());
}
else
{
isLink = false;
}
if (isLeaf())
{
totalSize = file.length();
totalSizeValid = true;
}
}
/**
* Returns the date the receiver was last modified.
*/
public Date lastModified()
{
if (lastModified == null && file != null)
{
lastModified = new Date(file.lastModified());
}
return lastModified;
}
/**
* Returns the the string to be used to display this leaf in the JTree.
*/
public String toString()
{
return file.getName();
}
/**
* Returns the java.io.File the receiver represents.
*/
public File getFile()
{
return file;
}
/**
* Returns size of the receiver and all its children.
*/
public long totalSize()
{
return totalSize;
}
/**
* Returns the parent of the receiver.
*/
public FileNode getParent()
{
return parent;
}
/**
* Returns true if the receiver represents a leaf, that is it is isn't a directory.
*/
public boolean isLeaf()
{
return file.isFile();
}
/**
* Returns true if the total size is valid.
*/
public boolean isTotalSizeValid()
{
return totalSizeValid;
}
/**
* Clears the date.
*/
protected void resetLastModified()
{
lastModified = null;
}
/**
* Sets the size of the receiver to be 0.
*/
protected void resetSize()
{
alterTotalSize(-totalSize);
}
/**
* Loads the children, caching the results in the children instance variable.
*/
protected FileNode[] getChildren()
{
return children;
}
/**
* Recursively loads all the children of the receiver.
*/
protected void loadChildren(MergeSort sorter)
{
totalSize = file.length();
children = createChildren(null);
for (int counter = children.length - 1; counter >= 0; counter--)
{
Thread.yield(); // Give the GUI CPU time to draw itself.
if (!children[counter].isLeaf() && (descendLinks || !children[counter].isLink()))
{
children[counter].loadChildren(sorter);
}
totalSize += children[counter].totalSize();
if (!isValid)
{
counter = 0;
}
}
if (isValid)
{
if (sorter != null)
{
sorter.sort(children);
}
totalSizeValid = true;
}
}
/**
* Loads the children of of the receiver.
*/
protected FileNode[] createChildren(MergeSort sorter)
{
FileNode[] retArray = null;
try
{
String[] files = file.list();
if (files != null)
{
if (sorter != null)
{
sorter.sort(files);
}
retArray = new FileNode[files.length];
String path = file.getPath();
for (int i = 0; i < files.length; i++)
{
File childFile = new File(path, files[i]);
retArray[i] = new FileNode(this, childFile);
}
}
}
catch (SecurityException se)
{
}
if (retArray == null)
{
retArray = EMPTY_CHILDREN;
}
return retArray;
}
/**
* Returns true if the children have been loaded.
*/
protected boolean loadedChildren()
{
return (file.isFile() || (children != null));
}
/**
* Gets the path from the root to the receiver.
*/
public FileNode[] getPath()
{
return getPathToRoot(this, 0);
}
/**
* Returns the canonical path for the receiver.
*/
public String getCanonicalPath()
{
return canonicalPath;
}
/**
* Returns true if the receiver's path does not begin with the parent's canonical path.
*/
public boolean isLink()
{
return isLink;
}
protected FileNode[] getPathToRoot(FileNode aNode, int depth)
{
FileNode[] retNodes;
if (aNode == null)
{
if (depth == 0) return null;
else retNodes = new FileNode[depth];
}
else
{
depth++;
retNodes = getPathToRoot(aNode.getParent(), depth);
retNodes[retNodes.length - depth] = aNode;
}
return retNodes;
}
/**
* Sets the children of the receiver, updates the total size, and if generateEvent is true a tree
* structure changed event is created.
*/
protected void setChildren(FileNode[] newChildren, boolean generateEvent)
{
long oldSize = totalSize;
totalSize = file.length();
children = newChildren;
for (int counter = children.length - 1; counter >= 0; counter--)
{
totalSize += children[counter].totalSize();
}
if (generateEvent)
{
FileNode[] path = getPath();
fireTreeStructureChanged(FileSystemModel2.this, path, null, null);
FileNode parent = getParent();
if (parent != null)
{
parent.alterTotalSize(totalSize - oldSize);
}
}
}
protected synchronized void alterTotalSize(long sizeDelta)
{
if (sizeDelta != 0 && (parent = getParent()) != null)
{
totalSize += sizeDelta;
nodeChanged();
parent.alterTotalSize(sizeDelta);
}
else
{
// Need a way to specify the root.
totalSize += sizeDelta;
}
}
/**
* This should only be invoked on the event dispatching thread.
*/
protected synchronized void setTotalSizeValid(boolean newValue)
{
if (totalSizeValid != newValue)
{
nodeChanged();
totalSizeValid = newValue;
FileNode parent = getParent();
if (parent != null)
{
parent.childTotalSizeChanged(this);
}
}
}
/**
* Marks the receivers total size as valid, but does not invoke node changed, nor message the parent.
*/
protected synchronized void forceTotalSizeValid()
{
totalSizeValid = true;
}
/**
* Invoked when a childs total size has changed.
*/
protected synchronized void childTotalSizeChanged(FileNode child)
{
if (totalSizeValid != child.isTotalSizeValid())
{
if (totalSizeValid)
{
setTotalSizeValid(false);
}
else
{
FileNode[] children = getChildren();
for (int counter = children.length - 1; counter >= 0; counter--)
{
if (!children[counter].isTotalSizeValid()) { return; }
}
setTotalSizeValid(true);
}
}
}
/**
* Can be invoked when a node has changed, will create the appropriate event.
*/
protected void nodeChanged()
{
FileNode parent = getParent();
if (parent != null)
{
FileNode[] path = parent.getPath();
int[] index = { getIndexOfChild(parent, this) };
Object[] children = { this };
fireTreeNodesChanged(FileSystemModel2.this, path, index, children);
}
}
}
/**
* FileNodeLoader can be used to reload all the children of a particular node. It first resets the children
* of the FileNode it is created with, and in its run method will reload all of that nodes children.
* FileNodeLoader may not be running in the event dispatching thread. As swing is not thread safe it is
* important that we don't generate events in this thread. SwingUtilities.invokeLater is used so that
* events are generated in the event dispatching thread.
*/
class FileNodeLoader implements Runnable
{
/** Node creating children for. */
FileNode node;
/** Sorter. */
MergeSort sizeMS;
FileNodeLoader(FileNode node)
{
this.node = node;
node.resetLastModified();
node.setChildren(node.createChildren(fileMS), true);
node.setTotalSizeValid(false);
}
public void run()
{
FileNode[] children = node.getChildren();
sizeMS = getSizeSorter();
for (int counter = children.length - 1; counter >= 0; counter--)
{
if (!children[counter].isLeaf())
{
reloadNode = children[counter];
loadChildren(children[counter]);
reloadNode = null;
}
if (!isValid)
{
counter = 0;
}
}
recycleSorter(sizeMS);
if (isValid)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
MergeSort sorter = getSizeSorter();
sorter.sort(node.getChildren());
recycleSorter(sorter);
node.setChildren(node.getChildren(), true);
synchronized (FileSystemModel2.this)
{
reloadCount--;
FileSystemModel2.this.notifyAll();
}
}
});
}
else
{
synchronized (FileSystemModel2.this)
{
reloadCount--;
FileSystemModel2.this.notifyAll();
}
}
}
protected void loadChildren(FileNode node)
{
if (!node.isLeaf() && (descendLinks || !node.isLink()))
{
final FileNode[] children = node.createChildren(null);
for (int counter = children.length - 1; counter >= 0; counter--)
{
if (!children[counter].isLeaf())
{
if (descendLinks || !children[counter].isLink())
{
children[counter].loadChildren(sizeMS);
}
else
{
children[counter].forceTotalSizeValid();
}
}
if (!isValid)
{
counter = 0;
}
}
if (isValid)
{
final FileNode fn = node;
// Reset the children
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
MergeSort sorter = getSizeSorter();
sorter.sort(children);
recycleSorter(sorter);
fn.setChildren(children, true);
fn.setTotalSizeValid(true);
fn.nodeChanged();
}
});
}
}
else
{
node.forceTotalSizeValid();
}
}
}
/**
* Sorts the contents, which must be instances of FileNode based on totalSize.
*/
static class SizeSorter extends MergeSort
{
public int compareElementsAt(int beginLoc, int endLoc)
{
long firstSize = ((FileNode) toSort[beginLoc]).totalSize();
long secondSize = ((FileNode) toSort[endLoc]).totalSize();
if (firstSize != secondSize) { return (int) (secondSize - firstSize); }
return ((FileNode) toSort[beginLoc]).toString().compareTo(((FileNode) toSort[endLoc]).toString());
}
}
}
--- NEW FILE: JTreeTable.java ---
package com.sun.treetable;
/*
* @(#)JTreeTable.java 1.2 98/10/27
*
* Copyright 1997, 1998 Sun Microsystems, Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY
* DAMAGES OR LIABILITIES SUFFERED BY LICENSEE AS A RESULT OF OR
* RELATING TO USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE OR
* ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE
* FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT,
* SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
* CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF
* THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS
* BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*/
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.tree.*;
import javax.swing.table.*;
import java.awt.Dimension;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.util.EventObject;
/**
* This example shows how to create a simple JTreeTable component, by using a JTree as a renderer (and editor)
* for the cells in a particular column in the JTable.
*
* @version 1.2 10/27/98
* @author Philip Milne
* @author Scott Violet
*/
public class JTreeTable extends JTable
{
/** A subclass of JTree. */
protected TreeTableCellRenderer tree;
public JTreeTable(TreeTableModel treeTableModel)
{
super();
// Create the tree. It will be used as a renderer and editor.
tree = new TreeTableCellRenderer(treeTableModel);
// Install a tableModel representing the visible rows in the tree.
super.setModel(new TreeTableModelAdapter(treeTableModel, tree));
// Force the JTable and JTree to share their row selection models.
ListToTreeSelectionModelWrapper selectionWrapper = new ListToTreeSelectionModelWrapper();
tree.setSelectionModel(selectionWrapper);
setSelectionModel(selectionWrapper.getListSelectionModel());
// Install the tree editor renderer and editor.
setDefaultRenderer(TreeTableModel.class, tree);
setDefaultEditor(TreeTableModel.class, new TreeTableCellEditor());
// No grid.
setShowGrid(false);
// No intercell spacing
setIntercellSpacing(new Dimension(0, 0));
// And update the height of the trees row to match that of
// the table.
if (tree.getRowHeight() < 1)
{
// Metal looks better like this.
setRowHeight(18);
}
}
/**
* Overridden to message super and forward the method to the tree. Since the tree is not actually in the
* component hieachy it will never receive this unless we forward it in this manner.
*/
public void updateUI()
{
super.updateUI();
if (tree != null)
{
tree.updateUI();
}
// Use the tree's default foreground and background colors in the
// table.
LookAndFeel.installColorsAndFont(this, "Tree.background", "Tree.foreground", "Tree.font");
}
/* Workaround for BasicTableUI anomaly. Make sure the UI never tries to
* paint the editor. The UI currently uses different techniques to
* paint the renderers and editors and overriding setBounds() below
* is not the right thing to do for an editor. Returning -1 for the
* editing row in this case, ensures the editor is never painted.
*/
public int getEditingRow()
{
return (getColumnClass(editingColumn) == TreeTableModel.class) ? -1 : editingRow;
}
/**
* Overridden to pass the new rowHeight to the tree.
*/
public void setRowHeight(int rowHeight)
{
super.setRowHeight(rowHeight);
if (tree != null && tree.getRowHeight() != rowHeight)
{
tree.setRowHeight(getRowHeight());
}
}
/**
* Returns the tree that is being shared between the model.
*/
public JTree getTree()
{
return tree;
}
/**
* A TreeCellRenderer that displays a JTree.
*/
public class TreeTableCellRenderer extends JTree implements TableCellRenderer
{
/** Last table/tree row asked to renderer. */
protected int visibleRow;
public TreeTableCellRenderer(TreeModel model)
{
super(model);
}
/**
* updateUI is overridden to set the colors of the Tree's renderer to match that of the table.
*/
public void updateUI()
{
super.updateUI();
// Make the tree's cell renderer use the table's cell selection
// colors.
TreeCellRenderer tcr = getCellRenderer();
if (tcr instanceof DefaultTreeCellRenderer)
{
DefaultTreeCellRenderer dtcr = ((DefaultTreeCellRenderer) tcr);
// For 1.1 uncomment this, 1.2 has a bug that will cause an
// exception to be thrown if the border selection color is
// null.
// dtcr.setBorderSelectionColor(null);
dtcr.setTextSelectionColor(UIManager.getColor("Table.selectionForeground"));
dtcr.setBackgroundSelectionColor(UIManager.getColor("Table.selectionBackground"));
}
}
/**
* Sets the row height of the tree, and forwards the row height to the table.
*/
public void setRowHeight(int rowHeight)
{
if (rowHeight > 0)
{
super.setRowHeight(rowHeight);
if (JTreeTable.this != null && JTreeTable.this.getRowHeight() != rowHeight)
{
JTreeTable.this.setRowHeight(getRowHeight());
}
}
}
/**
* This is overridden to set the height to match that of the JTable.
*/
public void setBounds(int x, int y, int w, int h)
{
super.setBounds(x, 0, w, JTreeTable.this.getHeight());
}
/**
* Sublcassed to translate the graphics such that the last visible row will be drawn at 0,0.
*/
public void paint(Graphics g)
{
g.translate(0, -visibleRow * getRowHeight());
super.paint(g);
}
/**
* TreeCellRenderer method. Overridden to update the visible row.
*/
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int column)
{
if (isSelected) setBackground(table.getSelectionBackground());
else setBackground(table.getBackground());
visibleRow = row;
return this;
}
}
/**
* TreeTableCellEditor implementation. Component returned is the JTree.
*/
public class TreeTableCellEditor extends AbstractCellEditor implements TableCellEditor
{
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int r,
int c)
{
return tree;
}
/**
* Overridden to return false, and if the event is a mouse event it is forwarded to the tree.
* <p>
* The behavior for this is debatable, and should really be offered as a property. By returning false,
* all keyboard actions are implemented in terms of the table. By returning true, the tree would get a
* chance to do something with the keyboard events. For the most part this is ok. But for certain keys,
* such as left/right, the tree will expand/collapse where as the table focus should really move to a
* different column. Page up/down should also be implemented in terms of the table. By returning false
* this also has the added benefit that clicking outside of the bounds of the tree node, but still in
* the tree column will select the row, whereas if this returned true that wouldn't be the case.
* <p>
* By returning false we are also enforcing the policy that the tree will never be editable (at least by
* a key sequence).
*/
public boolean isCellEditable(EventObject e)
{
if (e instanceof MouseEvent)
{
for (int counter = getColumnCount() - 1; counter >= 0; counter--)
{
if (getColumnClass(counter) == TreeTableModel.class)
{
MouseEvent me = (MouseEvent) e;
MouseEvent newME =
new MouseEvent(tree, me.getID(), me.getWhen(), me.getModifiers(), me.getX()
- getCellRect(0, counter, true).x, me.getY(), me.getClickCount(), me.isPopupTrigger());
tree.dispatchEvent(newME);
break;
}
}
}
return false;
}
}
/**
* ListToTreeSelectionModelWrapper extends DefaultTreeSelectionModel to listen for changes in the
* ListSelectionModel it maintains. Once a change in the ListSelectionModel happens, the paths are updated
* in the DefaultTreeSelectionModel.
*/
class ListToTreeSelectionModelWrapper extends DefaultTreeSelectionModel
{
/** Set to true when we are updating the ListSelectionModel. */
protected boolean updatingListSelectionModel;
public ListToTreeSelectionModelWrapper()
{
super();
getListSelectionModel().addListSelectionListener(createListSelectionListener());
}
/**
* Returns the list selection model. ListToTreeSelectionModelWrapper listens for changes to this model
* and updates the selected paths accordingly.
*/
ListSelectionModel getListSelectionModel()
{
return listSelectionModel;
}
/**
* This is overridden to set <code>updatingListSelectionModel</code> and message super. This is the only
* place DefaultTreeSelectionModel alters the ListSelectionModel.
*/
public void resetRowSelection()
{
if (!updatingListSelectionModel)
{
updatingListSelectionModel = true;
try
{
super.resetRowSelection();
}
finally
{
updatingListSelectionModel = false;
}
}
// Notice how we don't message super if
// updatingListSelectionModel is true. If
// updatingListSelectionModel is true, it implies the
// ListSelectionModel has already been updated and the
// paths are the only thing that needs to be updated.
}
/**
* Creates and returns an instance of ListSelectionHandler.
*/
protected ListSelectionListener createListSelectionListener()
{
return new ListSelectionHandler();
}
/**
* If <code>updatingListSelectionModel</code> is false, this will reset the selected paths from the
* selected rows in the list selection model.
*/
protected void updateSelectedPathsFromSelectedRows()
{
if (!updatingListSelectionModel)
{
updatingListSelectionModel = true;
try
{
// This is way expensive, ListSelectionModel needs an
// enumerator for iterating.
int min = listSelectionModel.getMinSelectionIndex();
int max = listSelectionModel.getMaxSelectionIndex();
clearSelection();
if (min != -1 && max != -1)
{
for (int counter = min; counter <= max; counter++)
{
if (listSelectionModel.isSelectedIndex(counter))
{
TreePath selPath = tree.getPathForRow(counter);
if (selPath != null)
{
addSelectionPath(selPath);
}
}
}
}
}
finally
{
updatingListSelectionModel = false;
}
}
}
/**
* Class responsible for calling updateSelectedPathsFromSelectedRows when the selection of the list
* changse.
*/
class ListSelectionHandler implements ListSelectionListener
{
public void valueChanged(ListSelectionEvent e)
{
updateSelectedPathsFromSelectedRows();
}
}
}
}
--- NEW FILE: TreeTableExample2.java ---
package com.sun.treetable;
/*
* TreeTableExample2.java
*
* Copyright 1997, 1998 Sun Microsystems, Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY
* DAMAGES OR LIABILITIES SUFFERED BY LICENSEE AS A RESULT OF OR
* RELATING TO USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE OR
* ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE
* FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT,
* SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
* CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF
* THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS
* BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*/
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;
import javax.swing.table.*;
import javax.swing.tree.*;
import java.awt.*;
import java.awt.event.*;
import java.text.NumberFormat;
/**
* Assembles the UI. The UI consists of a JTreeTable and a status label. As nodes are loaded by the
* FileSystemModel2, in a background thread, the status label updates as well as the renderer to draw the node
* that is being loaded differently.
*
* @author Scott Violet
* @author Philip Milne
*/
public class TreeTableExample2
{
/** Number of instances of TreeTableExample2. */
protected static int ttCount;
/** Model for the JTreeTable. */
protected FileSystemModel2 model;
/** Used to represent the model. */
protected JTreeTable treeTable;
/** Row the is being reloaded. */
protected int reloadRow;
/** TreePath being reloaded. */
protected TreePath reloadPath;
/**
* A counter increment as the Timer fies and the same path is being reloaded.
*/
protected int reloadCounter;
/** Timer used to update reload state. */
protected Timer timer;
/** Used to indicate status. */
protected JLabel statusLabel;
/** Frame containing everything. */
protected JFrame frame;
/** Path created with. */
protected String path;
public TreeTableExample2(String path)
{
this.path = path;
ttCount++;
frame = createFrame();
Container cPane = frame.getContentPane();
JMenuBar mb = createMenuBar();
model = createModel(path);
treeTable = createTreeTable();
statusLabel = createStatusLabel();
cPane.add(new JScrollPane(treeTable));
cPane.add(statusLabel, BorderLayout.SOUTH);
reloadRow = -1;
frame.setJMenuBar(mb);
frame.pack();
frame.show();
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
reload(model.getRoot());
}
});
}
/**
* Creates and return a JLabel that is used to indicate the status of loading.
*/
protected JLabel createStatusLabel()
{
JLabel retLabel = new JLabel(" ");
retLabel.setHorizontalAlignment(JLabel.RIGHT);
retLabel.setBorder(new BevelBorder(BevelBorder.LOWERED));
return retLabel;
}
/**
* Creates and returns the instanceof JTreeTable that will be used. This also creates, but does not start,
* the Timer that is used to update the display as files are loaded.
*/
protected JTreeTable createTreeTable()
{
JTreeTable treeTable = new JTreeTable(model);
treeTable.getColumnModel().getColumn(1).setCellRenderer(new IndicatorRenderer());
Reloader rl = new Reloader();
timer = new Timer(700, rl);
timer.setRepeats(true);
treeTable.getTree().addTreeExpansionListener(rl);
return treeTable;
}
/**
* Creates the FileSystemModel2 that will be used.
*/
protected FileSystemModel2 createModel(String path)
{
return new FileSystemModel2(path);
}
/**
* Creates the JFrame that will contain everything.
*/
protected JFrame createFrame()
{
JFrame retFrame = new JFrame("TreeTable II");
retFrame.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent we)
{
if (--ttCount == 0)
{
System.exit(0);
}
}
});
return retFrame;
}
/**
* Creates a menu bar.
*/
protected JMenuBar createMenuBar()
{
JMenu fileMenu = new JMenu("File");
JMenuItem menuItem;
menuItem = new JMenuItem("Open");
menuItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent ae)
{
JFileChooser fc = new JFileChooser(path);
fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
int result = fc.showOpenDialog(frame);
if (result == JFileChooser.APPROVE_OPTION)
{
String newPath = fc.getSelectedFile().getPath();
new TreeTableExample2(newPath);
}
}
});
fileMenu.add(menuItem);
fileMenu.addSeparator();
menuItem = new JMenuItem("Reload");
menuItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent ae)
{
TreePath path = treeTable.getTree().getSelectionPath();
if (path != null)
{
model.stopLoading();
reload(path.getLastPathComponent());
}
}
});
fileMenu.add(menuItem);
menuItem = new JMenuItem("Stop");
menuItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent ae)
{
model.stopLoading();
}
});
fileMenu.add(menuItem);
fileMenu.addSeparator();
menuItem = new JMenuItem("Exit");
menuItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent ae)
{
System.exit(0);
}
});
fileMenu.add(menuItem);
// Create a menu bar
JMenuBar menuBar = new JMenuBar();
menuBar.add(fileMenu);
// Menu for the look and feels (lafs).
UIManager.LookAndFeelInfo[] lafs = UIManager.getInstalledLookAndFeels();
ButtonGroup lafGroup = new ButtonGroup();
JMenu optionsMenu = new JMenu("Options");
menuBar.add(optionsMenu);
for (int i = 0; i < lafs.length; i++)
{
JRadioButtonMenuItem rb = new JRadioButtonMenuItem(lafs[i].getName());
optionsMenu.add(rb);
rb.setSelected(UIManager.getLookAndFeel().getName().equals(lafs[i].getName()));
rb.putClientProperty("UIKey", lafs[i]);
rb.addItemListener(new ItemListener()
{
public void itemStateChanged(ItemEvent ae)
{
JRadioButtonMenuItem rb2 = (JRadioButtonMenuItem) ae.getSource();
if (rb2.isSelected())
{
UIManager.LookAndFeelInfo info = (UIManager.LookAndFeelInfo) rb2.getClientProperty("UIKey");
try
{
UIManager.setLookAndFeel(info.getClassName());
SwingUtilities.updateComponentTreeUI(frame);
}
catch (Exception e)
{
System.err.println("unable to set UI " + e.getMessage());
}
}
}
});
lafGroup.add(rb);
}
return menuBar;
}
/**
* Invoked to reload the children of a particular node. This will also restart the timer.
*/
protected void reload(Object node)
{
model.reloadChildren(node);
if (!timer.isRunning())
{
timer.start();
}
}
/**
* Updates the status label based on reloadRow.
*/
protected void updateStatusLabel()
{
if (reloadPath != null)
{
statusLabel.setText("Reloading: " + model.getPath(reloadPath.getLastPathComponent()));
if ((reloadCounter % 4) < 2)
{
statusLabel.setForeground(Color.red);
}
else
{
statusLabel.setForeground(Color.blue);
}
}
else if (!model.isReloading())
{
statusLabel.setText("Total Size: "
+ NumberFormat.getInstance().format(model.getTotalSize(model.getRoot())));
statusLabel.setForeground(Color.black);
}
}
/**
* Reloader is the ActionListener used in the Timer. In response to the timer updating it will reset the
* reloadRow/reloadPath and generate the necessary event so that the display will update. It also
* implements the TreeExpansionListener so that if the tree is altered while loading the reloadRow is
* updated accordingly.
*/
class Reloader implements ActionListener, TreeExpansionListener
{
public void actionPerformed(ActionEvent ae)
{
if (!model.isReloading())
{
// No longer loading.
timer.stop();
if (reloadRow != -1)
{
generateChangeEvent(reloadRow);
}
reloadRow = -1;
reloadPath = null;
}
else
{
// Still loading, see if paths changed.
TreePath newPath = model.getPathLoading();
if (newPath == null)
{
// Hmm... Will usually indicate the reload thread
// completed between time we asked if reloading.
if (reloadRow != -1)
{
generateChangeEvent(reloadRow);
}
reloadRow = -1;
reloadPath = null;
}
else
{
// Ok, valid path, see if matches last path.
int newRow = treeTable.getTree().getRowForPath(newPath);
if (newPath.equals(reloadPath))
{
reloadCounter = (reloadCounter + 1) % 8;
if (newRow != reloadRow)
{
int lastRow = reloadRow;
reloadRow = newRow;
generateChangeEvent(lastRow);
}
generateChangeEvent(reloadRow);
}
else
{
int lastRow = reloadRow;
reloadCounter = 0;
reloadRow = newRow;
reloadPath = newPath;
if (lastRow != reloadRow)
{
generateChangeEvent(lastRow);
}
generateChangeEvent(reloadRow);
}
}
}
updateStatusLabel();
}
/**
* Generates and update event for the specified row. FileSystemModel2 could do this, but it would not
* know when the row has changed as a result of expanding/collapsing nodes in the tree.
*/
protected void generateChangeEvent(int row)
{
if (row != -1)
{
AbstractTableModel tModel = (AbstractTableModel) treeTable.getModel();
tModel.fireTableChanged(new TableModelEvent(tModel, row, row, 1));
}
}
//
// TreeExpansionListener
//
/**
* Invoked when the tree has expanded.
*/
public void treeExpanded(TreeExpansionEvent te)
{
updateRow();
}
/**
* Invoked when the tree has collapsed.
*/
public void treeCollapsed(TreeExpansionEvent te)
{
updateRow();
}
/**
* Updates the reloadRow and path, this does not genernate a change event.
*/
protected void updateRow()
{
reloadPath = model.getPathLoading();
if (reloadPath != null)
{
reloadRow = treeTable.getTree().getRowForPath(reloadPath);
}
}
}
/**
* A renderer that will give an indicator when a cell is being reloaded.
*/
class IndicatorRenderer extends DefaultTableCellRenderer
{
/**
* Makes sure the number of displayed in an internationalized manner.
*/
protected NumberFormat formatter;
/** Row that is currently being painted. */
protected int lastRow;
IndicatorRenderer()
{
setHorizontalAlignment(JLabel.RIGHT);
formatter = NumberFormat.getInstance();
}
/**
* Invoked as part of DefaultTableCellRenderers implemention. Sets the text of the label.
*/
public void setValue(Object value)
{
setText((value == null) ? "---" : formatter.format(value));
}
/**
* Returns this.
*/
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int column)
{
super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
lastRow = row;
return this;
}
/**
* If the row being painted is also being reloaded this will draw a little indicator.
*/
public void paint(Graphics g)
{
if (lastRow == reloadRow)
{
int width = getWidth();
int height = getHeight();
g.setColor(getBackground());
g.fillRect(0, 0, width, height);
g.setColor(getForeground());
int diameter = Math.min(width, height);
if (reloadCounter < 5)
{
g.fillArc((width - diameter) / 2, (height - diameter) / 2, diameter, diameter, 90,
-(reloadCounter * 90));
}
else
{
g.fillArc((width - diameter) / 2, (height - diameter) / 2, diameter, diameter, 90,
(4 - reloadCounter % 4) * 90);
}
}
else
{
super.paint(g);
}
}
}
public static void main(String[] args)
{
if (args.length > 0)
{
for (int counter = args.length - 1; counter >= 0; counter--)
{
new TreeTableExample2(args[counter]);
}
}
else
{
String path;
try
{
path = System.getProperty("user.home");
if (path != null)
{
new TreeTableExample2(path);
}
}
catch (SecurityException se)
{
path = null;
}
if (path == null)
{
System.out.println("Could not determine home directory");
}
}
}
}
--- NEW FILE: TreeTableModel.java ---
package com.sun.treetable;
/*
* TreeTableModel.java
*
* Copyright 1997, 1998 Sun Microsystems, Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY
* DAMAGES OR LIABILITIES SUFFERED BY LICENSEE AS A RESULT OF OR
* RELATING TO USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE OR
* ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE
* FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT,
* SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
* CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF
* THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS
* BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
*/
import javax.swing.tree.TreeModel;
/**
* TreeTableModel is the model used by a JTreeTable. It extends TreeModel to add methods for getting
* inforamtion about the set of columns each node in the TreeTableModel may have. Each column, like a column
* in a TableModel, has a name and a type associated with it. Each node in the TreeTableModel can return a
* value for each of the columns and set that value if isCellEditable() returns true.
*
* @author Philip Milne
* @author Scott Violet
*/
public interface TreeTableModel extends TreeModel
{
/**
* Returns the number ofs availible column.
*/
public int getColumnCount();
/**
* Returns the name for column number <code>column</code>.
*/
public String getColumnName(int column);
/**
* Returns the type for column number <code>column</code>.
*/
public Class getColumnClass(int column);
/**
* Returns the value to be displayed for node <code>node</code>, at column number <code>column</code>.
*/
public Object getValueAt(Object node, int column);
/**
* Indicates whether the the value for node <code>node</code>, at column number <code>column</code> is
* editable.
*/
public boolean isCellEditable(Object node, int column);
/**
* Sets the value for node <code>node</code>, at column number <code>column</code>.
*/
public void setValueAt(Object aValue, Object node, int column);
}
--- NEW FILE: AbstractCellEditor.java ---
package com.sun.treetable;
/*
* AbstractCellEditor.java
*
* Copyright 1997, 1998 Sun Microsystems, Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY
* DAMAGES OR LIABILITIES SUFFERED BY LICENSEE AS A RESULT OF OR
* RELATING TO USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE OR
* ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE
* FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT,
* SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
* CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF
* THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS
* BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*/
import java.awt.Component;
import java.awt.event.*;
import java.awt.AWTEvent;
import javax.swing.*;
import javax.swing.event.*;
import java.util.EventObject;
import java.io.Serializable;
/**
* @version 1.2 10/27/98 A base class for CellEditors, providing default implementations for all methods in
* the CellEditor interface and support for managing a series of listeners.
* @author Philip Milne
*/
public class AbstractCellEditor implements CellEditor
{
protected EventListenerList listenerList = new EventListenerList();
public Object getCellEditorValue()
{
return null;
}
public boolean isCellEditable(EventObject e)
{
return true;
}
public boolean shouldSelectCell(EventObject anEvent)
{
return false;
}
public boolean stopCellEditing()
{
return true;
}
public void cancelCellEditing()
{
}
public void addCellEditorListener(CellEditorListener l)
{
listenerList.add(CellEditorListener.class, l);
}
public void removeCellEditorListener(CellEditorListener l)
{
listenerList.remove(CellEditorListener.class, l);
}
/*
* Notify all listeners that have registered interest for
* notification on this event type.
* @see EventListenerList
*/
protected void fireEditingStopped()
{
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i = listeners.length - 2; i >= 0; i -= 2)
{
if (listeners[i] == CellEditorListener.class)
{
((CellEditorListener) listeners[i + 1]).editingStopped(new ChangeEvent(this));
}
}
}
/*
* Notify all listeners that have registered interest for
* notification on this event type.
* @see EventListenerList
*/
protected void fireEditingCanceled()
{
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i = listeners.length - 2; i >= 0; i -= 2)
{
if (listeners[i] == CellEditorListener.class)
{
((CellEditorListener) listeners[i + 1]).editingCanceled(new ChangeEvent(this));
}
}
}
}
--- NEW FILE: MergeSort.java ---
package com.sun.treetable;
/*
* MergeSort.java
*
* Copyright 1997, 1998 Sun Microsystems, Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY
* DAMAGES OR LIABILITIES SUFFERED BY LICENSEE AS A RESULT OF OR
* RELATING TO USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE OR
* ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE
* FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT,
* SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
* CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF
* THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS
* BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*/
/**
* An implementation of MergeSort, needs to be subclassed to compare the terms.
*
* @author Scott Violet
*/
public abstract class MergeSort extends Object
{
protected Object toSort[];
protected Object swapSpace[];
public void sort(Object array[])
{
if (array != null && array.length > 1)
{
int maxLength;
maxLength = array.length;
swapSpace = new Object[maxLength];
toSort = array;
this.mergeSort(0, maxLength - 1);
swapSpace = null;
toSort = null;
}
}
public abstract int compareElementsAt(int beginLoc, int endLoc);
protected void mergeSort(int begin, int end)
{
if (begin != end)
{
int mid;
mid = (begin + end) / 2;
this.mergeSort(begin, mid);
this.mergeSort(mid + 1, end);
this.merge(begin, mid, end);
}
}
protected void merge(int begin, int middle, int end)
{
int firstHalf, secondHalf, count;
firstHalf = count = begin;
secondHalf = middle + 1;
while ((firstHalf <= middle) && (secondHalf <= end))
{
if (this.compareElementsAt(secondHalf, firstHalf) < 0) swapSpace[count++] = toSort[secondHalf++];
else swapSpace[count++] = toSort[firstHalf++];
}
if (firstHalf <= middle)
{
while (firstHalf <= middle)
swapSpace[count++] = toSort[firstHalf++];
}
else
{
while (secondHalf <= end)
swapSpace[count++] = toSort[secondHalf++];
}
for (count = begin; count <= end; count++)
toSort[count] = swapSpace[count];
}
}
--- NEW FILE: TreeTableModelAdapter.java ---
package com.sun.treetable;
/*
* @(#)TreeTableModelAdapter.java 1.2 98/10/27
*
* Copyright 1997, 1998 Sun Microsystems, Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY
* DAMAGES OR LIABILITIES SUFFERED BY LICENSEE AS A RESULT OF OR
* RELATING TO USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE OR
* ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE
* FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT,
* SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
* CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF
* THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS
* BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*/
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.table.AbstractTableModel;
import javax.swing.tree.TreePath;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeExpansionListener;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
/**
* This is a wrapper class takes a TreeTableModel and implements the table model interface. The implementation
* is trivial, with all of the event dispatching support provided by the superclass: the AbstractTableModel.
*
* @version 1.2 10/27/98
* @author Philip Milne
* @author Scott Violet
*/
public class TreeTableModelAdapter extends AbstractTableModel
{
JTree tree;
TreeTableModel treeTableModel;
public TreeTableModelAdapter(TreeTableModel treeTableModel, JTree tree)
{
this.tree = tree;
this.treeTableModel = treeTableModel;
tree.addTreeExpansionListener(new TreeExpansionListener()
{
// Don't use fireTableRowsInserted() here; the selection model
// would get updated twice.
public void treeExpanded(TreeExpansionEvent event)
{
fireTableDataChanged();
}
public void treeCollapsed(TreeExpansionEvent event)
{
fireTableDataChanged();
}
});
// Install a TreeModelListener that can update the table when
// tree changes. We use delayedFireTableDataChanged as we can
// not be guaranteed the tree will have finished processing
// the event before us.
treeTableModel.addTreeModelListener(new TreeModelListener()
{
public...
[truncated message content] |