Is this your first time here? SwingWiki is a Java Swing Developer community site with an big archive of Swing-related usenet groups and mailing lists, but also tips, tricks and articles and book reviews written by your colleagues from around the world. If you came here through a search engine and did not find what you were looking for, make sure to check the wiki table of contents.

Using JXTreeTable, a first draft

From:[jdnc-interest@masked-domain]
Sent on:Fri, 25 Apr 2008 10:14:08 PDT
Hi Jeanette and Karl,

As promised here is my first draft of my documentation on how to properly implement JXTreeTables in your application. It is not done (some subjects to still be covered are mentioned) and I will add some UML documents later but thought I would get your comments and that it may be useful to some of your readers.

Best

--Alex

Using JXTreeTable

Introduction
This document is an introduction to an attempt to define a best practice for implementing SwingX JXTreeTables. This is based on my research and experimentation and discussion with the code authors on the java.net forum.



What to subclass to implement your functionality
It is suggested that the user should be able to accomplish everything they want by using the DefaultTreeTableModel class directly and subclass AbstractMutableTreeTableNode, ColumnFactory, and whatever highlighter/renderer/editor classes they need to implement their functionality. This guide is based on following that principle.


To Be Added
Need to nail down and test sorting, filtering, setting column sizes. And probably somethings I haven't thought of. Hopefully what I discover won't invalidate what I say here...



JXTreeTable--set ColumnFactory, Highlighters, TreeTableModel

ColumnFactory
JXTreeTable will use the ColumnFactory instance singleton by default, unless a specific ColumnFactory is set for the Table. A call to JXTreeTable.setColumnFactory() will set a specific column factory for your tree table.

Highlighters
Call JXTreeTable.setHighlighers() to define the initial set of highlighters to use. Highlighters can then be added and removed via calls to ColumnFactory.addHighlighter() and ColumnFactory.removeHighlighter()

TreeTableModel
Call JXTreeTable.setTreeTableModel() to bind your DefaultTreeTableModel (already created) to your JXTreeTable. Note that if you want to use a custom ColumnFactory for this JXTreeTable the TreeTableModel should not be passed to the constructor but to the setTreeTableModel() method, which should be called after setColumnFactory(). If you are using the default ColumnFactory (or have set one yourself) then either setting the TreeTableModel via the constructor or via the method call is fine.

Sample Code
This sample code shows instantiation of the JXTreeTable and basic setup (full runnable code is below).

[code]
// Initialize and setup the JXTreeTable
JXTreeTable myJXTreeTable = new JXTreeTable();
// This is false by default
myJXTreeTable.setRootVisible(true);
// Whatever other properties you need to set...

// Setup Highlighters...
Highlighter myHighlighter = new ColorHighlighter(
HighlightPredicate.ROLLOVER_ROW, Color.BLUE, Color.CYAN);
myJXTreeTable.setHighlighters(myHighlighter);

// Set the ColumnFactory; note that generally this is a singleton, I'm
// just creating
// an anonymous subclass here for convenience
// This must be done before setting the TreeTableModel or the
// ColumnFactory won't be
// used.
myJXTreeTable.setColumnFactory(myColumnFactory);

// Set the TreeTableModel.
// Must be done after ColumnFactory is set or it won't use the
// ColumnFactory.
myJXTreeTable.setTreeTableModel(myDefaultTreeTableModel);
[/code]



DefaultTreeTableModel--set RootNode, ColumnNames and ColumnCount

RootNode
Set the DefaultTreeTableModel's root TreeTableNode, already created. This can be passed to the constructor of the JXTreeTable, but it preferably set via JXTreeTable.setRoot() after the ColumnFactory and columnIdentifiers have been set.

ColumnNames and ColumnCount
Set the DefaultTreeTableModel's columnIdentifier property to a List of Objects representing each column, with a toString() implementation returning the column name (easiest is a list of Strings) either by passing this List to the constructor or calling DefaultTreeTableModel.setColumnIdentifiers(). The column names will come from the toString(), and the number of columns from the length of the list.

Sample Code
This sample code shows instantiation of the DefaultTreeTableModel and basic setup (full runnable code is below).

[code]
// Initialize and setup the DefaultTreeTableModel.
// This is where Column Names and Column Count is set, and the root
// TreeTableNode.

DefaultTreeTableModel myDefaultTreeTableModel = new DefaultTreeTableModel();

// Here we pass in a List of Strings, the length of the list defines the
// number of
// columns and each String defines the title of that column. These could
// be any
// object with an appropriate toString method.
myDefaultTreeTableModel.setColumnIdentifiers(theColumnNamesList);

// Here we set the root node of the tree. Note this should happen after
// setColumnIdentifiers() has been called.
myDefaultTreeTableModel.setRoot(myRootTreeTableNode);
[/code]



AbstractMutableTreeTableNode--Define what is displayed, define row/cell level editability, and how to process edits

What to render
TreeTableNode.getValueAt(int columnIndex) will be called for each column on node in the table (representing the row) to query what to show in the column.
A tree table node holds a reference to an arbitrary UserObject which is generally the real data object to be rendered (of course you can implement the getValueAt() method however you want, and complete ignore the UserObject entirely). By knowing what the UserObjects are or will be you can implement various logic to define what to display. Since these objects could be different classes for every row it is good to be flexible, and consistent functionality can be encapsulated by ensuring that all the UserObjects to be displayed in the table implement a certain interface (as is shown in the example).

What is editable
JXTreeTable's isCellEditable() function (at this moment in time) first checks the TreeTableModel to see if it is editable, and if this returns true then checks the TableColumnExt for the column. DefaultTreeTableModel (our TreeTableModel) passes this check along to the TreeTableNode by calling isEditable(). This means if you want to control editing solely at the column level via your ColumnFactory, you must override this and hard code it to return true (by default it always returns false). If you want to control editing at the row or cell level, override this and implement appropriately (at the row level by the node, at cell level by the node and column), but remember you also must make sure your TableColumnExt objects from your ColumnFactory are set appropriately. Of course you could control it from both places (or if you want a read-only table, leave it to the hard coded false).

How to update model
After the editor finishes editing, it will call TreeTableNode.setValueAt(newValue, columnID) to update the model. You must implement this to appropriately update your model. After it is done, the renderer will be displayed again, and getValueAt() called, so if you do not update appropriately you may not see your changes.

Sample Code
This sample code shows definition and instantiation of the TreeTableNode subclass and basic setup (full runnable code is below).

[code]
// Create some AbstractMutableTreeTableNodes.
MutableTreeTableNode myRootTreeTableNode = new MyMutableTreeTableNode();

// Set the UserObject for the root node. This could be done in the
// Constructor as well.
myRootTreeTableNode.setUserObject(myUserDataObject);


// AbstractMutableTreeTableNode subclass.
// We must implement getColumnCount() here as well because of lots of
// checking
// being done by SwingX. Just refer to the same list that the
// DefaultTreeTableModel
// is using, or whatever mechanism you choose to keep them in sync.
private class MyMutableTreeTableNode extends AbstractMutableTreeTableNode
{
[@masked-domain]
public boolean isEditable(int myColumn)
{
// Hard coded to true, will control editing via the ColumnFactory.
// Implement appropriately as you need your editing granularity, but
// it you
// want to control via the ColumnFactory make sure you hard code to
// true here.
return true;
}

[@masked-domain]
public int getColumnCount()
{
// return the same thing that the DefaultTreeTableModel uses to
// determine size
return theColumnNamesList.size();
}

[@masked-domain]
public Object getValueAt(int myColumn)
{
// Use the column index to determine what to return here.
// Note that this is using the interface I defined to cast the
// objects; could
// be a class too, or whatever you want to do.
switch (myColumn) {
case 0:
return ((MyTreeTableNodeInterface) getUserObject())
.getColumn1();
case 1:
return ((MyTreeTableNodeInterface) getUserObject())
.getColumn2();
case 2:
return ((MyTreeTableNodeInterface) getUserObject())
.getColumn3();
case 3:
return ((MyTreeTableNodeInterface) getUserObject())
.getColumn4();
default:
throw new ArrayIndexOutOfBoundsException(
"TreeTableNode--getValueAt called for bad column index");
}
}

[@masked-domain]
public void setValueAt(Object myValue, int myColumn)
{
super.setValueAt(myValue, myColumn);
switch (myColumn) {
case 0:
((MyTreeTableNodeInterface) getUserObject())
.setColumn1(myValue);
break;
case 1:
((MyTreeTableNodeInterface) getUserObject())
.setColumn2(myValue);
break;
case 2:
((MyTreeTableNodeInterface) getUserObject())
.setColumn3(myValue);
break;
case 3:
((MyTreeTableNodeInterface) getUserObject())
.setColumn4(myValue);
break;
default:
throw new ArrayIndexOutOfBoundsException(
"TreeTableNode--setValueAt called for bad column index");
}
}
}
[/code]





ColumnFactory--Renderers, Editors, Sorting, Visibility, Editability

ColumnFactory implementation
The ColumnFactory object dynamically generates TableColumnExt objects for the JXTreeTable, which define most of how the data is displayed. By implementing a custom ColumnFactory for your JXTreeTable, you can set the appropriate properties on the TableColumnExt objects to customize behavior in how your table is displayed. Further display behavior is implemented via the JXTreeTable's Highlighters, discussed below.

Per table vs generic custom ColumnFactory
Two methods are available to register your ColumnFactory implementation, depending on whether you wish to replace the standard ColumnFactory singleton used by default in all view objects, or whether you with to bind a ColumnFactory to your specific table. To replace the ColumnFactory singleton, you call ColumnFactory.setInstance(myColumnFactorySubclassInstance), while to set one up for a specific table, call JXTreeTable.setColumnFactory(myColumnFactorySubclassInstance).

Make sure you set your factory before you set the model for the Table, or it will not be used. For per table ColumnFactorys, this means the Table must be created with no model, the factory set, and then the model set via the setTreeTableModel() method. If the generic ColumnFactory is used, Table may be created with a model in their Constructor, as long as the ColumnFactory instance has already been set.

For simplicity and maintainability, I think it is better to have a one-to-one relationship between UI tables and ColumnFactorys so that each Table can be modified individually with no side effects on other tables, so I recommend using the per table implementation, and will do so for the remainder of the document.

Defining custom renderers and editors
To implement custom renderers and editors for each column, call TableColumnExt.setCellEditor(myEditor) and TableColumnExt.setCellRenderer(myRenderer) in the ColumnFactory.configureTableColumn() method to set the appropriate renderers and editors for the table column. SwingX provides a handy framework for generating renderers and editors, but discussion of that is external to this document.

Setting up sorting
TableColumnExt allows you to set both whether a table is sortable (setSortable()) and what comparator to use to sort the column (setComparator()). To control sorting, call these methods from the ColumnFactory's configureTableColumn() method.

Controlling editing and prototype value
TableColumnExt allows you to set whether a column is editable (setEditable()) and what prototype value to use for new rows (setPrototypeValue()). To configure editing and insertion, call these methods from the ColumnFactory's configureTableColumn() method. Remember though that before a TableColumnExt object's editable property is checked, the DefaultTreeTableModel check's the TreeTableNode's isEditable() method, which by default returns false (see above in AbstractMutableTreeTableNode), so if you want to control all editing from the TableColumnExt objects then make sure you override this and hard code it to true (as in the demo code).

Controlling column title and header appearance
TableColumnExt allows you to set a column's title (setTitle()) or even a custom renderer for the column's header (setHeaderRenderer()). To configure column header appearance, call these methods from the ColumnFactory's configureTableColumn() method. Note that by default column's titles will be set by the List of columnIdentifiers set in the DefaultTreeTableModel.

Controlling visible/hidden columns
TableColumnExt allows you to set which columns appear by default via the setVisible() method; the can later be shown/or hidden. To configure the default columns to show, call this method from the ColumnFactory's configureTableColumn() method.

Sample Code
This sample code shows definition and instantiation of the ColumnFactory subclass and basic setup (full runnable code is below).

[code]
// ColumnFactory subclass.
// The preferred hook for setting up your columns is the
// configureTableColumn() method,
// which is called automatically by the factory after the TableColumnExt
// has been created.
//
// TBD--how to configure column sizes...
//
// This is being done via anonymous subclassing as an easy demo...better
// to create
// a real class in application.
ColumnFactory myColumnFactory = new ColumnFactory() {

[@masked-domain]
public void configureTableColumn(TableModel myModel,
TableColumnExt myColumnExt)
{
super.configureTableColumn(myModel, myColumnExt);

// Figure out which column it is and set it up appropriately.
// Note this is not a particularily elegant or efficient way to
// figure out which
// column is being created but it is not called often.
final String myColumnTitle = myColumnExt.getTitle();
if (myColumnTitle.equals(theColumnNamesList.get(0))) {
myColumnExt.setEditable(false);
myColumnExt.setSortable(false);
}
else if (myColumnTitle.equals(theColumnNamesList.get(1))) {
myColumnExt.setEditable(true);
myColumnExt.setSortable(false);
}
else if (myColumnTitle.equals(theColumnNamesList.get(2))) {
myColumnExt.setEditable(false);
myColumnExt.setSortable(true);
}
else if (myColumnTitle.equals(theColumnNamesList.get(3))) {
myColumnExt.setEditable(true);
myColumnExt.setSortable(true);
}
}

};
[/code]




Highlighters--Further controlling table cell appearance
Various other customization of the tree table's display is configured via setting implementations of the Highlighter interface to your table. Highlighters are used to control cell colors and appearance properties, implement rollover or conditional beautification, and implement tooltips for your TreeTable.

How to set Highlighters
To set Highlighters for your TreeTable, call the setHighlighters() method to completely replace the current set of highlighters, or add and remove them individually via the addHighlighter() and removeHighlighter() methods.

Implementing Highlighters
Highlighting has two factors; determining whether a highlighter should be used, and doing the highlighting. Determining whether a highlighter should be used is handled by a HighlightPredicate object, passed to the highlighter via its constructor or access methods. Actual highlighting is handled by the Highlighter. SwingX comes with a variety of prebuilt Highlighters and HighlightPredicates, and custom highlighters and predicates are easily implemented. A more thorough discussion of highlighters is external to this document.

Sample Code
This sample code shows the instantiation of a Highlighter class and basic setup (full runnable code is below).

[code]
// Setup Highlighters...
Highlighter myHighlighter = new ColorHighlighter(
HighlightPredicate.ROLLOVER_ROW, Color.BLUE, Color.CYAN);
myJXTreeTable.setHighlighters(myHighlighter);
[/code]





Sample Application

This is a runnable application (just copy and past into JXTreeTableDemoApplication.java, in the root package) showing the basic techniques described above to implement a JXTreeTable.

Note: Requires SwingApplicationFramework

[/pre]
[code]
import java.awt.Color;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

import javax.swing.JScrollPane;
import javax.swing.table.TableModel;

import org.jdesktop.application.Application;
import org.jdesktop.application.SingleFrameApplication;
import org.jdesktop.swingx.JXTreeTable;
import org.jdesktop.swingx.decorator.ColorHighlighter;
import org.jdesktop.swingx.decorator.HighlightPredicate;
import org.jdesktop.swingx.decorator.Highlighter;
import org.jdesktop.swingx.table.ColumnFactory;
import org.jdesktop.swingx.table.TableColumnExt;
import org.jdesktop.swingx.treetable.AbstractMutableTreeTableNode;
import org.jdesktop.swingx.treetable.DefaultTreeTableModel;
import org.jdesktop.swingx.treetable.MutableTreeTableNode;

public class JXTreeTableDemoApplication extends SingleFrameApplication
{

private static final List<String> theColumnNamesList = Arrays.asList(
"Column 1", "Column 2", "Column 3", "Column 4");

// Create a dummy user data object used by the TreeTableNode to return
// data.
// This could be any object you want to represent in the tree table.
// Note that it implements the interface below as a convenience, plenty
// of
// ways to accomplish this though.
private final class MyUserObject implements MyTreeTableNodeInterface
{
String myString1 = RandomGenerator.generateRandomString(10);
String myString2 = RandomGenerator.generateRandomString(10);
String myString3 = RandomGenerator.generateRandomString(10);
String myString4 = RandomGenerator.generateRandomString(10);

public Object getColumn1()
{
return myString1;
}

public Object getColumn2()
{
return myString2;
}

public Object getColumn3()
{
return myString3;
}

public Object getColumn4()
{
return myString4;
}

[@masked-domain]
public void setColumn1(Object myValue)
{
myString1 = myValue.toString();
}

[@masked-domain]
public void setColumn2(Object myValue)
{
myString2 = myValue.toString();
}

[@masked-domain]
public void setColumn3(Object myValue)
{
myString3 = myValue.toString();
}

[@masked-domain]
public void setColumn4(Object myValue)
{
myString4 = myValue.toString();
}
}

// AbstractMutableTreeTableNode subclass.
// We must implement getColumnCount() here as well because of lots of
// checking
// being done by SwingX. Just refer to the same list that the
// DefaultTreeTableModel
// is using, or whatever mechanism you choose to keep them in sync.
private class MyMutableTreeTableNode extends AbstractMutableTreeTableNode
{
[@masked-domain]
public boolean isEditable(int myColumn)
{
// Hard coded to true, will control editing via the ColumnFactory.
// Implement appropriately as you need your editing granularity, but
// it you
// want to control via the ColumnFactory make sure you hard code to
// true here.
return true;
}

[@masked-domain]
public int getColumnCount()
{
// return the same thing that the DefaultTreeTableModel uses to
// determine size
return theColumnNamesList.size();
}

[@masked-domain]
public Object getValueAt(int myColumn)
{
// Use the column index to determine what to return here.
// Note that this is using the interface I defined to cast the
// objects; could
// be a class too, or whatever you want to do.
switch (myColumn) {
case 0:
return ((MyTreeTableNodeInterface) getUserObject())
.getColumn1();
case 1:
return ((MyTreeTableNodeInterface) getUserObject())
.getColumn2();
case 2:
return ((MyTreeTableNodeInterface) getUserObject())
.getColumn3();
case 3:
return ((MyTreeTableNodeInterface) getUserObject())
.getColumn4();
default:
throw new ArrayIndexOutOfBoundsException(
"TreeTableNode--getValueAt called for bad column index");
}
}

[@masked-domain]
public void setValueAt(Object myValue, int myColumn)
{
super.setValueAt(myValue, myColumn);
switch (myColumn) {
case 0:
((MyTreeTableNodeInterface) getUserObject())
.setColumn1(myValue);
break;
case 1:
((MyTreeTableNodeInterface) getUserObject())
.setColumn2(myValue);
break;
case 2:
((MyTreeTableNodeInterface) getUserObject())
.setColumn3(myValue);
break;
case 3:
((MyTreeTableNodeInterface) getUserObject())
.setColumn4(myValue);
break;
default:
throw new ArrayIndexOutOfBoundsException(
"TreeTableNode--setValueAt called for bad column index");
}
}
}

// Create an interface that the TreeTableNode Object will use to interface
// with user Objects.
// This way multiple user Objects can be stored as Nodes in the table and
// the TreeTableNode
// implementation will know how to extract their values. There are of course
// plenty of other
// ways to do this.
interface MyTreeTableNodeInterface
{
public Object getColumn1();

public Object getColumn2();

public Object getColumn3();

public Object getColumn4();

public void setColumn1(Object myValue);

public void setColumn2(Object myValue);

public void setColumn3(Object myValue);

public void setColumn4(Object myValue);
}

[@masked-domain]
protected void startup()
{
// Boilerplate SwingApplicationFramework code
getMainFrame().setTitle("SwingX Test Application");

// Create a dummy user data object used by the TreeTableNode to return
// data.
MyTreeTableNodeInterface myUserDataObject = new MyUserObject();

// Create some AbstractMutableTreeTableNodes.
MutableTreeTableNode myRootTreeTableNode = new MyMutableTreeTableNode();

// Set the UserObject for the root node. This could be done in the
// Constructor as well.
myRootTreeTableNode.setUserObject(myUserDataObject);

// Add a couple more nodes to the tree to test sorting
MyMutableTreeTableNode myNewMutableTreeTableNode = new MyMutableTreeTableNode();
myNewMutableTreeTableNode.setUserObject(new MyUserObject());
myRootTreeTableNode.insert(myNewMutableTreeTableNode, 0);
myNewMutableTreeTableNode = new MyMutableTreeTableNode();
myNewMutableTreeTableNode.setUserObject(new MyUserObject());
myRootTreeTableNode.insert(myNewMutableTreeTableNode, 0);

// ColumnFactory subclass.
// The preferred hook for setting up your columns is the
// configureTableColumn() method,
// which is called automatically by the factory after the TableColumnExt
// has been created.
//
// TBD--how to configure column sizes...
//
// This is being done via anonymous subclassing as an easy demo...better
// to create
// a real class in application.
ColumnFactory myColumnFactory = new ColumnFactory() {

[@masked-domain]
public void configureTableColumn(TableModel myModel,
TableColumnExt myColumnExt)
{
super.configureTableColumn(myModel, myColumnExt);

// Figure out which column it is and set it up appropriately.
// Note this is not a particularily elegant or efficient way to
// figure out which
// column is being created but it is not called often.
final String myColumnTitle = myColumnExt.getTitle();
if (myColumnTitle.equals(theColumnNamesList.get(0))) {
myColumnExt.setEditable(false);
myColumnExt.setSortable(false);
}
else if (myColumnTitle.equals(theColumnNamesList.get(1))) {
myColumnExt.setEditable(true);
myColumnExt.setSortable(false);
}
else if (myColumnTitle.equals(theColumnNamesList.get(2))) {
myColumnExt.setEditable(false);
myColumnExt.setSortable(true);
}
else if (myColumnTitle.equals(theColumnNamesList.get(3))) {
myColumnExt.setEditable(true);
myColumnExt.setSortable(true);
}
}

};

// Initialize and setup the DefaultTreeTableModel.
// This is where Column Names and Column Count is set, and the root
// TreeTableNode.

DefaultTreeTableModel myDefaultTreeTableModel = new DefaultTreeTableModel();

// Here we pass in a List of Strings, the length of the list defines the
// number of
// columns and each String defines the title of that column. These could
// be any
// object with an appropriate toString method.
myDefaultTreeTableModel.setColumnIdentifiers(theColumnNamesList);

// Here we set the root node of the tree. Note this should happen after
// setColumnIdentifiers() has been called.
myDefaultTreeTableModel.setRoot(myRootTreeTableNode);

// Initialize and setup the JXTreeTable
JXTreeTable myJXTreeTable = new JXTreeTable();
// This is false by default; we wouldn't see anything without adding
// nodes :)
myJXTreeTable.setRootVisible(true);
// Whatever other properties you need to set...

// Setup Highlighters...
Highlighter myHighlighter = new ColorHighlighter(
HighlightPredicate.ROLLOVER_ROW, Color.BLUE, Color.CYAN);
myJXTreeTable.setHighlighters(myHighlighter);

// Set the ColumnFactory; note that generally this is a singleton, I'm
// just creating
// an anonymous subclass here for convenience
// This must be done before setting the TreeTableModel or the
// ColumnFactory won't be
// used.
myJXTreeTable.setColumnFactory(myColumnFactory);

// Set the TreeTableModel.
// Must be done after ColumnFactory is set or it won't use the
// ColumnFactory.
myJXTreeTable.setTreeTableModel(myDefaultTreeTableModel);

show(new JScrollPane(myJXTreeTable));
}

public static void main(String[] args)
{
Application.launch(JXTreeTableDemoApplication.class, args);
}

}

class RandomGenerator
{
private static final Random theRandomizer = new Random();
private static final int theCharRange = 1 + 'Z' - 'A';

public static String generateRandomString(int myI)
{
StringBuffer myBuffer = new StringBuffer();
while (myI-- > 0) {
myBuffer
.append((char) ((Math.abs(theRandomizer.nextInt()) % theCharRange) + 'A' - 1));
}
return myBuffer.toString();
}
}[/code]
[Message sent by forum member 'alexkogon' (alexkogon)]

http://forums.java.net/jive/thread.jspa?messageID=271257

---------------------------------------------------------------------
To unsubscribe, e-mail: [jdnc-unsubscribe@masked-domain]
For additional commands, e-mail: [jdnc-help@masked-domain]
Found what you were looking for? If not - continue at Wiki Index

Other messages in this topic

SenderDate sentSubject
Kleopatra [fastegal@masked-domain]Wed, 11 Jun 2008 10:06:00 +0200Re: Using JXTreeTable, a first draft
Kleopatra [fastegal@masked-domain]Wed, 11 Jun 2008 09:55:03 +0200Re: Using JXTreeTable, a first draft
[jdnc-interest@masked-domain]Tue, 27 May 2008 11:56:52 PDTRe: Using JXTreeTable, a first draft
[jdnc-interest@masked-domain]Tue, 27 May 2008 09:52:18 PDTRe: Using JXTreeTable, a first draft
[jdnc-interest@masked-domain]Tue, 27 May 2008 07:50:54 PDTRe: Using JXTreeTable, a first draft
[jdnc-interest@masked-domain]Tue, 27 May 2008 06:36:30 PDTRe: Using JXTreeTable, a first draft
[jdnc-interest@masked-domain]Wed, 14 May 2008 05:59:21 PDTRe: Using JXTreeTable, a first draft
Kleopatra [fastegal@masked-domain]Tue, 06 May 2008 11:51:59 +0200Re: Using JXTreeTable, a first draft
[jdnc-interest@masked-domain]Fri, 02 May 2008 04:42:52 PDTRe: Using JXTreeTable, a first draft
[jdnc-interest@masked-domain]Wed, 30 Apr 2008 08:50:54 PDTRe: Using JXTreeTable, a first draft
[jdnc-interest@masked-domain]Wed, 30 Apr 2008 08:42:20 PDTRe: Using JXTreeTable, a first draft
[jdnc-interest@masked-domain]Wed, 30 Apr 2008 05:33:32 PDTRe: Using JXTreeTable, a first draft
[jdnc-interest@masked-domain]Wed, 30 Apr 2008 05:17:27 PDTRe: Using JXTreeTable, a first draft

 
Recent changes | RSS changes | Table of contents | News Archive | Terms And Conditions | Register | The Quest For Software++| Ruby Resources
FitNesse Resources