Monday, September 22, 2008

JFace-Viewers for Swing, is this possible?

You might think "Now he's gone completely crazy" but hold on and read through the next few paragrpahs to hopefully find out that I'm not.

0. The background


Do you sometimes have to code against Swing and have also been disappointed that you could not remember how to deal with Tables, Trees and TreeTables (I find myself always opening this tedious Swing-Tutorial to find how to do it)?

In last few days I worked on UFacekit's Swing implementation for Tree and TreeTable. JFace-Databinding has added support for Trees and TreeTables in 3.4 and naturally they build upon the JFace-Viewer implementation but naturally JFace-Viewers are bound to SWT and so it is impossible to use this support (or a slighlty one modified) Swing, right?

Well the above is not completely right the main JFace-Viewer-API is fairly free from SWT (besides some Widget, Item stuff) the internals are naturally not. After having noticed this I:

1. Extracted an Widget-Toolkit-Neutral API from JFace


... moved it to a new plugin (org.ufacekit.ui.viewers). I didn't only move the classes and interfaces to a new home I also added support for generics so all this casting is gone and done by the compiler for us.

A content provider now looks like this:

IContentProvider<Person,Collection<Person>> cp =
new IContentProvider<Person,Collection<Person>> {
// ...
}

and a collection can get iterated with a foreach-loop

for( Person p: v.getSelection() ) {
// ...
}

I rearranged some other classes and made interfaces from most of them, ... . So now I have a widget-toolkit-clean Viewer-API.

2. Copied some SWT-Classes (Widget, Table, TableItem, Tree, ...)


... replaced the internals through Swing-counter parts (some of the code is highly ineffecient because e.g. for a Tree we now have 3 Objects (UserObject, TreeItem, DefaultMutableTreeNode) )

3. Commented some JFace-code not needed to provide the minimum JFace-API


... providing a selection, and firing events when the selection changed but I currently e.g. don't need inline Editing so I simply commented all parts of the viewers that deal with this, including Mouse-Handling, ... . The problems from 2. & 3. are hidden from the user because all these are internal classes so I can replace them step by step.

4. Blog how nicely now I can setup a TreeTableViewer for Swing


(in fact SwingX because Swing doesn't has a TreeTable implementation by default - don't ask me why a Toolkit being around for such a long time doesn't has such a standard-control)

So now I don't have to remember how I have to create a TableTree in Swing which loads subnodes lazily because I can simple use the API I already know from JFace for SWT.

@Override
protected Component createUI(JFrame frame, List<Person> model) {
JXTreeTable tree = new JXTreeTable();

TableColumnExt c1 = new TableColumnExt(1);
c1.setHeaderValue("Givenname");
c1.setWidth(200);
tree.getColumnModel().addColumn(c1);

TableColumnExt c2 = new TableColumnExt(1);
c2.setHeaderValue("Surname");
c2.setWidth(200);
tree.getColumnModel().addColumn(c2);

JScrollPane scroll = new JScrollPane(tree);

TreeTableViewer<Person, Collection<Person>> viewer =
new TreeTableViewer<Person, Collection<Person>>(tree);
viewer.addSelectionChangedListener(
new ISelectionChangedListener<Person>() {
public void selectionChanged(SelectionChangedEvent<Person> event) {
for( Person p: event.getSelection() ) {
System.out.println(p);
}
}
});

TreeViewerColumn<Person> c = new TreeViewerColumn<Person>(viewer,c1);
c.setLabelProvider(new LabelConverter<Person>() {
@Override
public String getText(Person element) {
return element.getGivenname();
}
});
c = new TreeViewerColumn<Person>(viewer,c2);
c.setLabelProvider(new LabelConverter<Person>() {
@Override
public String getText(Person element) {
return element.getSurname();
}
});

viewer.setContentProvider(
new ITreeContentProvider<Person,Collection<Person>>() {
public Collection<Person> getChildren(Person parentElement) {
return parentElement.getChildren();
}

public Person getParent(Person element) {
return element.getParent();
}

public boolean hasChildren(Person element) {
return ((Person)element).getChildren().size() > 0;
}

public Collection<Person> getElements(Collection<Person> inputElement) {
return inputElement;
}

public void dispose() {
// TODO Auto-generated method stub
}

public void inputChanged(IViewer<Person, Collection<Person>> viewer,
Collection<Person> oldInput,
Collection<Person> newInput) {
// TODO Auto-generated method stub
}
});
viewer.setInput(model);
return scroll;
}

5. Summary


The internals are quite ugly there is a huge amount of bugs (I'm sure that not all is working smoothly already), missing functionality (e.g. Icon, Color and Font support) but I now have the foundation to add Tree and TreeTable support to the UFacekit-Library. Cleaning up and bugfixing can happen later. Like all other parts of the UFacekit-Project you can consume this swing-jface-bundle standalone because it has no dependency at all (besides the one on org.ufacekit.ui.viewers).

6. So am I now?


  • Completely Crazy

  • Fairly Crazy

  • A bit Crazy

  • Fairly normal if you know me and all the crazy ideas I already had

3 comments:

Unknown said...

Totally crazy !

But that sounds so good to me !

Maybe you have found your next talk for a java/eclipse user meeting

Anonymous said...

"Do you sometimes have to code against Swing and have also been disappointed that you could not remember how to deal with Tables, Trees and TreeTables"

No, it is quite the opposite. I'm experienced in Swing and often been disappointed that I don't know/remember how it is done in JFace.

Anonymous said...

Great tutorial! Very informative in how well worded and descriptive you were! You know they say that if one knows how to describe what they want really well, then life is just as good as how you describe it :)
Its great for people who feel like time is running against them and then land on your blog and feel like a whole burden was just lifted off of their shoulder.. I admire and respect people who take
time to make it easier for others.. Thanks a bunch! :)