JDesktopPane on the NetBeans Platform (Part 1)

Aside from Jesse and Tim's attendance, the recent NetBeans Platform Certified Training in Boston was special in that the last 2 days (of 5 days) was spent with the students talking about their various Java applications and how those applications might be ported to the NetBeans Platform. One of the presentations was done by a group of developers from a big financial institution who had a JDesktopPane containing a number of JFrames. Each JFrame provides a different tool in the application suite. And, when a JFrame is chosen, the JDesktopPane's menubar (and other items) changes to provide exactly the content relevant for the current tool.

How would such a structure be ported to the NetBeans Platform? Obviously, each JFrame should become a TopComponent. However, what about the JDesktopPane behavior? Switching from one TopComponent to another should add/remove items to/from the menubar and toolbar. Fortunately, Jesse was in the room and he knocked together a solution on the spot.

Below, in the first screenshot, Win1 is selected:

Now, in the second screenshot, Win2 is selected and the menubar and toolbar both change:

You can also see, above, that each window can be moved outside the application frame.

So, how to create an application on the NetBeans Platform where the selected TopComponent determines the content of the menubar and toolbar? Jesse has provided an API module and a module that uses the API.

The API module has two classes:

import org.openide.filesystems.FileSystem;
import org.openide.util.Utilities;

/\*\*
 \* Layer fragment active when in {@link Utilities#actionsGlobalContext}.
 \*/
public interface LayerProvider {
    FileSystem layer() throws Exception;
}

And here's the class implementing the above:

import java.net.URL;
import org.openide.filesystems.FileSystem;
import org.openide.filesystems.XMLFileSystem;
import org.xml.sax.SAXException;

/\*\*
 \* Layer provider based on an XML fragment.
 \*/
public final class FixedLayerProvider implements LayerProvider {
    
    private final FileSystem fs;

    public FixedLayerProvider(URL layerXML) throws SAXException {
        fs = new XMLFileSystem(layerXML);
    }

    public @Override FileSystem layer() {
        return fs;
    }
    
}

And, in the second module, there are two windows that start as follows:

public final class Win1TopComponent extends TopComponent {

    public Win1TopComponent() throws SAXException {
        initComponents();
        setName("Win1");
        associateLookup(Lookups.singleton(new FixedLayerProvider(Win1TopComponent.class.getResource("win1.xml"))));
    }
public final class Win2TopComponent extends TopComponent {

    public Win2TopComponent() throws SAXException {
        initComponents();
        setName("Win2");
        associateLookup(Lookups.singleton(new FixedLayerProvider(Win1TopComponent.class.getResource("win2.xml"))));
    }

In the module are also two XML files, in the same package as the two windows above, defining the XML fragments. So, in Jesse's words: "You supply the URL to a layer fragment. A couple of menus and actions for them are defined in the fragment XML." And: "The API is just to provide some stuff for the SFS whenever that object is in the selection (most easily by placing in a TC lookup or even implementing on a TC class)."

There are some problems with the solution, especially when you add top-level menus, rather than items within menus, since with top-level menus the menu does appear, but you have to click on it a few times before it posts anything. There could be a bug in the NetBeans Platform sources that would need to be fixed for this.

But, aside from that, this is at least a starting point for someone wanting this behavior. I made sure to put the windows into a "view" mode (in this case "properties") to ensure they'd be modal when undocked. If using this solution, you'd maybe create some new "view" modes that are undocked by default. Then, you'd provide a list in the main frame's menu bar, containing all the tools that the application provides. When a tool is selected, the related TopComponent would open in one of the "view" modes, i.e., undocked, in a specific place, and when the TopComponent is active the menubar and toolbar would change to match the content relevant for the window.

Comments:

Unmentioned in the blog entry is the impl class in the API module which scans LayerProvider's in the selection and loads them. In its current version it is pretty short too:

@ServiceProvider(service=FileSystem.class)
public final class DelegatingFS extends MultiFileSystem implements LookupListener {
private final Lookup.Result<LayerProvider> r = Utilities.actionsGlobalContext().lookupResult(LayerProvider.class);
public DelegatingFS() {
setPropagateMasks(true);
r.addLookupListener(this);
resultChanged(null);
}
public @Override void resultChanged(LookupEvent le) {
List<FileSystem> fss = new ArrayList<FileSystem>();
for (LayerProvider p : r.allInstances()) {
try {
fss.add(p.layer());
} catch (Exception x) {...}
}
setDelegates(fss.toArray(new FileSystem[fss.size()]));
}
}

In the future something like this could be part of the Platform, perhaps.

Posted by Jesse Glick on April 05, 2011 at 07:31 AM PDT #

Geertjan -
Thanks again for the great training/workshop last week! Might you post the project source for this example? Looks very useful.

Posted by Grant Harris on April 06, 2011 at 04:55 AM PDT #

By the way, I have developed this system further. Making it work well requires a patch in DataShadow which I put into 7.1. You can get a working demo (requires a 7.1 dev build) here: https://bitbucket.org/jglick/dynamicmenudemo

Posted by Jesse Glick on July 21, 2011 at 10:23 AM PDT #

This took me hours, so I wanted to share the information.
First: Thanks Jesse for your example.

But the first thing I noticed was, that the UnitTest SystemSubPathLayerProviderTest::testProvider fails with a StackOverflow when using Netbeans Platform 7.2.

What took me hours is the fact, that - using your example - you cannot hide menu-items by adding the suffix "_hidden" to shadows.
So "shadow_hidden" won't work! You have to use this spelling: "shadow.hidden"

e.g.:
<folder name="Help">
<file name="org-netbeans-modules-autoupdate-ui-actions-CheckForUpdatesAction.shadow.hidden"/>
</folder>

Posted by Thomas on October 15, 2012 at 12:56 PM PDT #

Post a Comment:
  • HTML Syntax: NOT allowed
About

Geertjan Wielenga (@geertjanw) is a Principal Product Manager in the Oracle Developer Tools group living & working in Amsterdam. He is a Java technology enthusiast, evangelist, trainer, speaker, and writer. He blogs here daily.

The focus of this blog is mostly on NetBeans (a development tool primarily for Java programmers), with an occasional reference to NetBeans, and sometimes diverging to topics relating to NetBeans. And then there are days when NetBeans is mentioned, just for a change.

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
12
13
14
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today