[pkg-java] r3438 - in trunk/rhino/debian: . patches
src_from_treetable_article_see_bug409629
Arnaud Vandyck
avdyk at alioth.debian.org
Fri May 11 12:22:02 UTC 2007
Author: avdyk
Date: 2007-05-11 12:22:01 +0000 (Fri, 11 May 2007)
New Revision: 3438
Added:
trunk/rhino/debian/src_from_treetable_article_see_bug409629/
trunk/rhino/debian/src_from_treetable_article_see_bug409629/AbstractCellEditor.java
trunk/rhino/debian/src_from_treetable_article_see_bug409629/AbstractTreeTableModel.java
trunk/rhino/debian/src_from_treetable_article_see_bug409629/FileSystemModel2.java
trunk/rhino/debian/src_from_treetable_article_see_bug409629/JTreeTable.java
trunk/rhino/debian/src_from_treetable_article_see_bug409629/MergeSort.java
trunk/rhino/debian/src_from_treetable_article_see_bug409629/TreeTableExample2.java
trunk/rhino/debian/src_from_treetable_article_see_bug409629/TreeTableModel.java
trunk/rhino/debian/src_from_treetable_article_see_bug409629/TreeTableModelAdapter.java
Removed:
trunk/rhino/debian/patches/02-dont_build-toolsrc.patch
Modified:
trunk/rhino/debian/changelog
Log:
rhino (1.6.R5-2) unstable; urgency=low
* added java classes from a tutorial from Sun (BSD license as mentionned
in #409629), thanks to Roktas, Filipe Lautert and Bruno Harbulot for
filing bug, giving infos and proposed a patch (even if it's not
used). I've just removed the 02-dont_build-toolsrc.patch. I added the
classes examples if needed but it seems not to be the case (closes:
#409629).
-- Arnaud Vandyck <avdyk at debian.org> Fri, 11 May 2007 14:07:21 +0200
Modified: trunk/rhino/debian/changelog
===================================================================
--- trunk/rhino/debian/changelog 2007-05-10 14:47:45 UTC (rev 3437)
+++ trunk/rhino/debian/changelog 2007-05-11 12:22:01 UTC (rev 3438)
@@ -1,3 +1,14 @@
+rhino (1.6.R5-2) unstable; urgency=low
+
+ * added java classes from a tutorial from Sun (BSD license as mentionned
+ in #409629), thanks to Roktas, Filipe Lautert and Bruno Harbulot for
+ filing bug, giving infos and proposed a patch (even if it's not
+ used). I've just removed the 02-dont_build-toolsrc.patch. I added the
+ classes examples if needed but it seems not to be the case (closes:
+ #409629).
+
+ -- Arnaud Vandyck <avdyk at debian.org> Fri, 11 May 2007 14:07:21 +0200
+
rhino (1.6.R5-1) unstable; urgency=low
* New upstream maintenance release.
Deleted: trunk/rhino/debian/patches/02-dont_build-toolsrc.patch
===================================================================
--- trunk/rhino/debian/patches/02-dont_build-toolsrc.patch 2007-05-10 14:47:45 UTC (rev 3437)
+++ trunk/rhino/debian/patches/02-dont_build-toolsrc.patch 2007-05-11 12:22:01 UTC (rev 3438)
@@ -1,13 +0,0 @@
---- build.xml~ 2005-09-19 07:20:18.000000000 +0000
-+++ build.xml 2006-11-06 11:44:43.000000000 +0000
-@@ -34,8 +34,8 @@
-
- <target name="compile" depends="init">
- <ant antfile="src/build.xml" target="compile"/>
-- <ant antfile="toolsrc/build.xml" target="compile"/>
-- <antcall target="xmlimplsrc-compile" />
-+ <!-- ant antfile="toolsrc/build.xml" target="compile"/ -->
-+ <!-- antcall target="xmlimplsrc-compile" / -->
- </target>
-
- <target name="compile-all" depends="compile">
Added: trunk/rhino/debian/src_from_treetable_article_see_bug409629/AbstractCellEditor.java
===================================================================
--- trunk/rhino/debian/src_from_treetable_article_see_bug409629/AbstractCellEditor.java 2007-05-10 14:47:45 UTC (rev 3437)
+++ trunk/rhino/debian/src_from_treetable_article_see_bug409629/AbstractCellEditor.java 2007-05-11 12:22:01 UTC (rev 3438)
@@ -0,0 +1,60 @@
+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;
+
+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));
+ }
+ }
+ }
+}
Added: trunk/rhino/debian/src_from_treetable_article_see_bug409629/AbstractTreeTableModel.java
===================================================================
--- trunk/rhino/debian/src_from_treetable_article_see_bug409629/AbstractTreeTableModel.java 2007-05-10 14:47:45 UTC (rev 3437)
+++ trunk/rhino/debian/src_from_treetable_article_see_bug409629/AbstractTreeTableModel.java 2007-05-11 12:22:01 UTC (rev 3438)
@@ -0,0 +1,195 @@
+/*
+ * @(#)AbstractTreeTableModel.java 1.2 98/10/27
+ *
+ * Copyright 1997, 1998 by Sun Microsystems, Inc.,
+ * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
+ * All rights reserved.
+ *
+ * This software is the confidential and proprietary information
+ * of Sun Microsystems, Inc. ("Confidential Information"). You
+ * shall not disclose such Confidential Information and shall use
+ * it only in accordance with the terms of the license agreement
+ * you entered into with Sun.
+ */
+
+import javax.swing.tree.*;
+import javax.swing.event.*;
+
+/**
+ * @version 1.2 10/27/98
+ * An abstract implementation of the TreeTableModel interface, handling the list
+ * of listeners.
+ * @author Philip Milne
+ */
+
+public abstract class AbstractTreeTableModel implements TreeTableModel {
+ protected Object root;
+ protected EventListenerList listenerList = new EventListenerList();
+
+ public AbstractTreeTableModel(Object root) {
+ this.root = root;
+ }
+
+ //
+ // Default implmentations for methods in the TreeModel interface.
+ //
+
+ public Object getRoot() {
+ return root;
+ }
+
+ public boolean isLeaf(Object node) {
+ return getChildCount(node) == 0;
+ }
+
+ public void valueForPathChanged(TreePath path, Object newValue) {}
+
+ // This is not called in the JTree's default mode: use a naive implementation.
+ public int getIndexOfChild(Object parent, Object child) {
+ for (int i = 0; i < getChildCount(parent); i++) {
+ if (getChild(parent, i).equals(child)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public void addTreeModelListener(TreeModelListener l) {
+ listenerList.add(TreeModelListener.class, l);
+ }
+
+ public void removeTreeModelListener(TreeModelListener l) {
+ listenerList.remove(TreeModelListener.class, l);
+ }
+
+ /*
+ * Notify all listeners that have registered interest for
+ * notification on this event type. The event instance
+ * is lazily created using the parameters passed into
+ * the fire method.
+ * @see EventListenerList
+ */
+ protected void fireTreeNodesChanged(Object source, Object[] path,
+ int[] childIndices,
+ Object[] children) {
+ // Guaranteed to return a non-null array
+ Object[] listeners = listenerList.getListenerList();
+ TreeModelEvent e = null;
+ // 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]==TreeModelListener.class) {
+ // Lazily create the event:
+ if (e == null)
+ e = new TreeModelEvent(source, path,
+ childIndices, children);
+ ((TreeModelListener)listeners[i+1]).treeNodesChanged(e);
+ }
+ }
+ }
+
+ /*
+ * Notify all listeners that have registered interest for
+ * notification on this event type. The event instance
+ * is lazily created using the parameters passed into
+ * the fire method.
+ * @see EventListenerList
+ */
+ protected void fireTreeNodesInserted(Object source, Object[] path,
+ int[] childIndices,
+ Object[] children) {
+ // Guaranteed to return a non-null array
+ Object[] listeners = listenerList.getListenerList();
+ TreeModelEvent e = null;
+ // 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]==TreeModelListener.class) {
+ // Lazily create the event:
+ if (e == null)
+ e = new TreeModelEvent(source, path,
+ childIndices, children);
+ ((TreeModelListener)listeners[i+1]).treeNodesInserted(e);
+ }
+ }
+ }
+
+ /*
+ * Notify all listeners that have registered interest for
+ * notification on this event type. The event instance
+ * is lazily created using the parameters passed into
+ * the fire method.
+ * @see EventListenerList
+ */
+ protected void fireTreeNodesRemoved(Object source, Object[] path,
+ int[] childIndices,
+ Object[] children) {
+ // Guaranteed to return a non-null array
+ Object[] listeners = listenerList.getListenerList();
+ TreeModelEvent e = null;
+ // 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]==TreeModelListener.class) {
+ // Lazily create the event:
+ if (e == null)
+ e = new TreeModelEvent(source, path,
+ childIndices, children);
+ ((TreeModelListener)listeners[i+1]).treeNodesRemoved(e);
+ }
+ }
+ }
+
+ /*
+ * Notify all listeners that have registered interest for
+ * notification on this event type. The event instance
+ * is lazily created using the parameters passed into
+ * the fire method.
+ * @see EventListenerList
+ */
+ protected void fireTreeStructureChanged(Object source, Object[] path,
+ int[] childIndices,
+ Object[] children) {
+ // Guaranteed to return a non-null array
+ Object[] listeners = listenerList.getListenerList();
+ TreeModelEvent e = null;
+ // 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]==TreeModelListener.class) {
+ // Lazily create the event:
+ if (e == null)
+ e = new TreeModelEvent(source, path,
+ childIndices, children);
+ ((TreeModelListener)listeners[i+1]).treeStructureChanged(e);
+ }
+ }
+ }
+
+ //
+ // Default impelmentations for methods in the TreeTableModel interface.
+ //
+
+ public Class getColumnClass(int column) { return Object.class; }
+
+ /** By default, make the column with the Tree in it the only editable one.
+ * Making this column editable causes the JTable to forward mouse
+ * and keyboard events in the Tree column to the underlying JTree.
+ */
+ public boolean isCellEditable(Object node, int column) {
+ return getColumnClass(column) == TreeTableModel.class;
+ }
+
+ public void setValueAt(Object aValue, Object node, int column) {}
+
+
+ // Left to be implemented in the subclass:
+
+ /*
+ * public Object getChild(Object parent, int index)
+ * public int getChildCount(Object parent)
+ * public int getColumnCount()
+ * public String getColumnName(Object node, int column)
+ * public Object getValueAt(Object node, int column)
+ */
+}
Added: trunk/rhino/debian/src_from_treetable_article_see_bug409629/FileSystemModel2.java
===================================================================
--- trunk/rhino/debian/src_from_treetable_article_see_bug409629/FileSystemModel2.java 2007-05-10 14:47:45 UTC (rev 3437)
+++ trunk/rhino/debian/src_from_treetable_article_see_bug409629/FileSystemModel2.java 2007-05-11 12:22:01 UTC (rev 3438)
@@ -0,0 +1,774 @@
+/*
+ * FileSystemModel2.java
+ *
+ * Copyright (c) 1998 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of Sun
+ * Microsystems, Inc. ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Sun.
+ *
+ * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
+ * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
+ * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
+ * THIS SOFTWARE OR ITS DERIVATIVES.
+ *
+ */
+
+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());
+ }
+ }
+}
Added: trunk/rhino/debian/src_from_treetable_article_see_bug409629/JTreeTable.java
===================================================================
--- trunk/rhino/debian/src_from_treetable_article_see_bug409629/JTreeTable.java 2007-05-10 14:47:45 UTC (rev 3437)
+++ trunk/rhino/debian/src_from_treetable_article_see_bug409629/JTreeTable.java 2007-05-11 12:22:01 UTC (rev 3438)
@@ -0,0 +1,353 @@
+/*
+ * @(#)JTreeTable.java 1.2 98/10/27
+ *
+ * Copyright 1997, 1998 by Sun Microsystems, Inc.,
+ * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
+ * All rights reserved.
+ *
+ * This software is the confidential and proprietary information
+ * of Sun Microsystems, Inc. ("Confidential Information"). You
+ * shall not disclose such Confidential Information and shall use
+ * it only in accordance with the terms of the license agreement
+ * you entered into with Sun.
+ */
+
+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();
+ }
+ }
+ }
+}
Added: trunk/rhino/debian/src_from_treetable_article_see_bug409629/MergeSort.java
===================================================================
--- trunk/rhino/debian/src_from_treetable_article_see_bug409629/MergeSort.java 2007-05-10 14:47:45 UTC (rev 3437)
+++ trunk/rhino/debian/src_from_treetable_article_see_bug409629/MergeSort.java 2007-05-11 12:22:01 UTC (rev 3438)
@@ -0,0 +1,84 @@
+/*
+ * MergeSort.java
+ *
+ * Copyright (c) 1998 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of Sun
+ * Microsystems, Inc. ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Sun.
+ *
+ * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
+ * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
+ * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
+ * THIS SOFTWARE OR ITS DERIVATIVES.
+ *
+ */
+
+/**
+ * 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];
+ }
+}
Added: trunk/rhino/debian/src_from_treetable_article_see_bug409629/TreeTableExample2.java
===================================================================
--- trunk/rhino/debian/src_from_treetable_article_see_bug409629/TreeTableExample2.java 2007-05-10 14:47:45 UTC (rev 3437)
+++ trunk/rhino/debian/src_from_treetable_article_see_bug409629/TreeTableExample2.java 2007-05-11 12:22:01 UTC (rev 3438)
@@ -0,0 +1,480 @@
+/*
+ * TreeTableExample2.java
+ *
+ * Copyright (c) 1998 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of Sun
+ * Microsystems, Inc. ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Sun.
+ *
+ * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
+ * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
+ * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
+ * THIS SOFTWARE OR ITS DERIVATIVES.
+ *
+ */
+
+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");
+ }
+ }
+ }
+}
Added: trunk/rhino/debian/src_from_treetable_article_see_bug409629/TreeTableModel.java
===================================================================
--- trunk/rhino/debian/src_from_treetable_article_see_bug409629/TreeTableModel.java 2007-05-10 14:47:45 UTC (rev 3437)
+++ trunk/rhino/debian/src_from_treetable_article_see_bug409629/TreeTableModel.java 2007-05-11 12:22:01 UTC (rev 3438)
@@ -0,0 +1,68 @@
+/*
+ * TreeTableModel.java
+ *
+ * Copyright (c) 1998 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of Sun
+ * Microsystems, Inc. ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Sun.
+ *
+ * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
+ * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
+ * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
+ * THIS SOFTWARE OR ITS DERIVATIVES.
+ *
+ */
+
+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);
+}
Added: trunk/rhino/debian/src_from_treetable_article_see_bug409629/TreeTableModelAdapter.java
===================================================================
--- trunk/rhino/debian/src_from_treetable_article_see_bug409629/TreeTableModelAdapter.java 2007-05-10 14:47:45 UTC (rev 3437)
+++ trunk/rhino/debian/src_from_treetable_article_see_bug409629/TreeTableModelAdapter.java 2007-05-11 12:22:01 UTC (rev 3438)
@@ -0,0 +1,125 @@
+/*
+ * @(#)TreeTableModelAdapter.java 1.2 98/10/27
+ *
+ * Copyright 1997, 1998 by Sun Microsystems, Inc.,
+ * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
+ * All rights reserved.
+ *
+ * This software is the confidential and proprietary information
+ * of Sun Microsystems, Inc. ("Confidential Information"). You
+ * shall not disclose such Confidential Information and shall use
+ * it only in accordance with the terms of the license agreement
+ * you entered into with Sun.
+ */
+
+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 void treeNodesChanged(TreeModelEvent e) {
+ delayedFireTableDataChanged();
+ }
+
+ public void treeNodesInserted(TreeModelEvent e) {
+ delayedFireTableDataChanged();
+ }
+
+ public void treeNodesRemoved(TreeModelEvent e) {
+ delayedFireTableDataChanged();
+ }
+
+ public void treeStructureChanged(TreeModelEvent e) {
+ delayedFireTableDataChanged();
+ }
+ });
+ }
+
+ // Wrappers, implementing TableModel interface.
+
+ public int getColumnCount() {
+ return treeTableModel.getColumnCount();
+ }
+
+ public String getColumnName(int column) {
+ return treeTableModel.getColumnName(column);
+ }
+
+ public Class getColumnClass(int column) {
+ return treeTableModel.getColumnClass(column);
+ }
+
+ public int getRowCount() {
+ return tree.getRowCount();
+ }
+
+ protected Object nodeForRow(int row) {
+ TreePath treePath = tree.getPathForRow(row);
+ return treePath.getLastPathComponent();
+ }
+
+ public Object getValueAt(int row, int column) {
+ return treeTableModel.getValueAt(nodeForRow(row), column);
+ }
+
+ public boolean isCellEditable(int row, int column) {
+ return treeTableModel.isCellEditable(nodeForRow(row), column);
+ }
+
+ public void setValueAt(Object value, int row, int column) {
+ treeTableModel.setValueAt(value, nodeForRow(row), column);
+ }
+
+ /**
+ * Invokes fireTableDataChanged after all the pending events have been
+ * processed. SwingUtilities.invokeLater is used to handle this.
+ */
+ protected void delayedFireTableDataChanged() {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ fireTableDataChanged();
+ }
+ });
+ }
+}
+
More information about the pkg-java-commits
mailing list