2008-04-02

Who says a RootPane always has to be at the top?

Some friends and I have started a project to extend functionality inside the NetBeans RCP (Rich Client Platform). We call it PlatformX, and it is located on the web at http://platformx.netbeans.org. We hope to have the repository public soon for others to use and contribute.

One of the APIs I'm working on is called RootPaneTopComponent. For the uninitiated in NetBeans RCP, a TopComponent is a Swing component managed by the NetBeans platform. They may work as regular components or can also be used as dockable/undockable components which may be moved around in the application.

A RootPaneTopComponent is a TopComponent which implements the RootPaneContainer interface. It allows one to use a glass pane, a menu and menu items, or a layered pane in more component based classes such as JFrame, JInternalFrame, and JApplet do for the more encompassing classes, so this new class in PlatformX allows the same thing yet at a lower level.

I'll have more information as soon as possible about this class and its sister class CloneableRootPaneTopComponent when I get it finished and we are closer to having a more public release of our first Platformx APIs and repository access. Until now here are some simple screen shots.

Imagine you have a dockable component you need disabled or the ability to cover with some user blocking message until another application state has been reached, and you do not want to block the entire window or a dialog. This is where component level glass panes come in handy:

Now you see me:


now you don't:

and I'll leave it up to you to figure out ways you could use this in your applications today. I'm using it to block certain components which need the user to login to be able to use them.

I mentioned the menu bar. I have also coded this particular class to allow north, south, east, and west components to be placed around it. This could be used for multiple things. I have chosen in this example to imagine some type of an editor which might have search functionality and maybe a notes editor which could show different notes for paragraphs or diagrams or any thing else one might think of:

No notes showing:


and now they are:

Anyways, this is just a simple example to show some of the capabilities of what I'm working on at the moment. This entry is to make it easier to show the others working on the project how it works right now more than anything, but it can be a preview as well :-D. Hopefully it, along with some other things, will be released soon. It's NetBeans, so of course it is open-source.

2007-12-18

Central Lookup: Creating a central repository and lookup for an application context in a NetBeans RCP application

I have been working on a NetBeans RCP project where I needed a central place to register different interfaces and allow different views to operate on these regular POJOs one would use in a regular Swing application and to be able to listen to changes to them from the standpoint of some kind of an Application Context where different instances will come and go. A good example is a desktop application requiring a login. You will have user information and want to be able to track a global login or validation token.

I create a simple class and API in a NetBeans module and called it CentralLookup. This is all the code for such a simple thing (please ignore the formatting of the code...maybe I can update it later, but it seems blogger isn't wanting to format it correctly when posted):


package org.netbeans.modules.centrallookup.api;

import java.util.Collection;

import org.openide.util.Lookup.Result;

import org.openide.util.lookup.AbstractLookup;

import org.openide.util.lookup.InstanceContent;

/**
* Class used to house anything one might want to store

* in a central lookup which can affect anything within

* the application. It can be thought of as a central context

* where any application data may be stored and watched.

*

* A singleton instance is created using @see getDefault().

* This class is as thread safe as Lookup. Lookup appears to be safe.

* @author Wade Chandler

* @version 1.0

*/

public class CentralLookup extends AbstractLookup {

private InstanceContent content = null;

private static CentralLookup def = new CentralLookup();

public CentralLookup(InstanceContent content) {
super(content);
this.content = content;
}

public CentralLookup() {

this(new InstanceContent());
}

public void add(Object instance) {

content.add(instance);
}

public void remove(Object instance) {

content.remove(instance);
}

public static CentralLookup getDefault(){
return def;
}
}


The nice thing about this code is that it is real simple. The strange thing for me is that there hasn't been something already implemented in the base RCP. It seems pretty common a thing. For instance, the normal global lookups deal with Actions, Services, and Nodes (which are part of the NetBeans data model), and that is fine until one needs to work with generic interfaces and classes from a global perspective.


Regardless, I can now write code to listen to changes in this global context or Lookup in this case. The code doesn't have to know about any implementation except for the code which does the injection, and the code in other places can just setup a result and a listener. The following illustrates this a bit, and of course it is just a sub-set of the code:

final class CentralLookupTest1TopComponent extends TopComponent {

private static CentralLookupTest1TopComponent instance;
private Lookup.Result userInfoResult = null;
private CentralLookupTest1TopComponent() {

Lookup.Template template = new Lookup.Template(UserInformation.class);

CentralLookup cl = CentralLookup.getDefault();
userInfoResult = cl.lookup(template);
userInfoResult.addLookupListener(new UserInformationListener());
}

private javax.swing.JTextField name;
private javax.swing.JTextField pwd;
private javax.swing.JTextField token;
private javax.swing.JTextField uid;

private class SetterRunnable implements Runnable {
UserInformation ui = null;

public SetterRunnable(UserInformation ui) {
this.ui = ui;
}

public void run() {
name.setText(ui.getName());
pwd.setText(ui.getPassword());
uid.setText(ui.getUserID());
token.setText(ui.getToken());
}
}

private class UserInformationListener implements LookupListener {

public void resultChanged(LookupEvent evt) {
Object o = evt.getSource();
if (o != null) {
Lookup.Result r = (Lookup.Result) o;
Collection infos = r.allInstances();
if (infos.isEmpty()) {
EventQueue.invokeLater(new SetterRunnable(new DefaultUserInformation()));
} else {
Iterator it = infos.iterator();
while (it.hasNext()) {
UserInformation info = it.next();
EventQueue.invokeLater(new SetterRunnable(info));
}
}
}
}
}
}


Notice we have a result. The result has attached to it a listener. The listener knows about DefaultUserInformation and the UserInformation interface, and that is it. It then knows it needs to listen for instances of UserInformation and act accordingly. It doesn't know how it got there, but knows what it must do with it. We then have some other code in another class which will inject the instances into the CentralLookup based on some user input removing any previous:

private void buttonActionPerformed(java.awt.event.ActionEvent evt) {
//ok we need to remove any user information from the central lookup
//and then we need to add this new one

CentralLookup cl = CentralLookup.getDefault();
Collection infos = cl.lookupAll(UserInformation.class);

if(!infos.isEmpty()){
Iterator it = infos.iterator();
while(it.hasNext()){
UserInformation info = it.next();
cl.remove(info);
}
}

DefaultUserInformation info = new DefaultUserInformation();
info.setName(name.getText());
info.setPassword(pwd.getText());
info.setUserID(uid.getText());
info.setToken(token.getText());
cl.add(info);

}


You can get a good idea from here the possibilities. This is a rather simple example, but it certainly shows it working. This can be used for any other services which need to use regular POJOs and not much else. Why add more overhead of class wrappers etc when they are not needed for everything? Sometimes one just needs a dynamic application context which can breakup the application into a more modular framework of simple classes and interfaces without major restrictions.

I have an AVI video formatted video of this in action. It is a very simple example, but it shows it working. The one piece of code doesn't know anything other than the fact that user information will appear magically in the CentralLookup in the form of the UserInformation interface.