Creating a New Window Group

In the IDE, when you're involved in GUI editing, several helper windows open together with the Design mode of the editor. For example, when you open a TopComponent in Design mode, the Palette and the Properties window open too. However, if you had previously opened the TopComponent in Design mode and closed the Palette, the Palette isn't opened when next you open the TopComponent. Basically, the helper windows (i.e., in this case, the Palette and Properties window) are only open if you had them open previously, on the assumption that their previous open/closed state are the ones you would like to maintain.

If you had to code that for all your windows, it would be something like this in pseudo code:

 

if (TopComponentA is opened) {
   if (HelperTopComponentB was open previously and is now closed) {
         HelperTopComponentB must open again
   }
   if (HelperTopComponentC was open previously and is now closed) {
         HelperTopComponentC must open again
   }
}

That would be a lot of cumbersome work, since the above is only for the opening of the TopComponent; there'd have to be something similar for closing the TopComponent. Wouldn't it be cool if I could create a group of components and then simply do this:

 

group.open();

And, then, later, when I close a TopComponent, I would simply call this:

 

group.close();

Then, all the opened and closed helper windows would be saved in whatever state they're in, automatically, without me having to think about it. Now, that would be cool. Hmmm... I think I should create an issue in Issuezilla for this. Okay, let's do that. Oops. Wait. Section 2.3 of the "New Window System API Changes" is titled "Window Groups". That's, in fact, exactly what I was looking for... So, three cheers for Interface TopComponentGroup.

So, if you go to config/Windows2Local in your user directory, you should see (after closing the IDE at least once) the following:

In the previous two blog entries, I wrote about the "Modes" folder above. The first blog entry was about creating a new mode. The second was about two modes sharing the 'editor' area of the IDE (or of another application based on the NetBeans Platform). This time, let's look at the "Groups" folder. If you open the "Groups" folder, one of the subfolders is called "debugger". It contains files for 9 TopComponents, which are all opened at the same time when a debug process begins and closed at the same time when it ends.

Let's create our own group, add two TopComponents, and then open the TopComponents simultaneously.

 

     

  1. Create a new module project, with org.netbeans.modules.windowgroupsample as the code name base.

     

  2. Use the Window Component wizard twice, to create two TopComponents. In the first page of the wizard, choose "editor" for the first and "output" for the second (or anything else, it really doesn't matter). Make sure that you don't select the checkbox, so that the TopComponent won't be shown at start up. Let's say the first is called "OneTopComponent" and the second "TwoTopComponent" (which means you should type "One" and "Two" as the prefix in the wizard) and we'll put them both in the main package.

     

  3. Now we're going to create the window group. Right-click the top package, create a new XML document called "MyGroupWsgrp.xml", within a new subpackage called "groups". Add a subpackage to the new group and call that subpackage "MyGroup". Inside of it, create two XML documents, one called "OneTopComponentWstcgrp.xml" and the other "TwoTopComponentWstcgrp.xml". You should now see this in the Projects window:

     

  4. Next, put this in "MyGroupWsgrp.xml":

     

    <?xml version="1.0" encoding="UTF-8"?>
    
    <!DOCTYPE group PUBLIC
      "-//NetBeans//DTD Group Properties 2.0//EN"
      "http://www.netbeans.org/dtds/group-properties2_0.dtd">
    
    <group version="2.0">
        <module name="org.netbeans.modules.windowgroupsample" spec="1.0" />
        <name unique="MyGroup" />
        <state opened="false" />
    </group>

    Note: The value of the state element above specifies that the group will be closed, by default, when the application starts up.

     

  5. In "OneTopComponentWstcgrp.xml", change the content to this:

     

    <?xml version="1.0" encoding="UTF-8" ?>
    
    <!DOCTYPE tc-group PUBLIC
      "-//NetBeans//DTD Top Component in Group Properties 2.0//EN"
      "http://www.netbeans.org/dtds/tc-group2_0.dtd">
    
    <tc-group version="2.0">
        <module name="org.netbeans.modules.windowgroupsample" spec="1.0"/>
        <tc-id id="OneTopComponent" />
        <open-close-behavior open="true" close="true" />
    </tc-group>

    Note 1: The value of the tc-id element must match the value of the PREFERRED_ID String that was generated in your TopComponent, when you finished the Window Component wizard. Have a look, and notice that the two match.

    Note 2: The values of the open-close-behavior element are the flags that indicate what will happen when group.open() and group.close() are called. For example, if the open attribute is set to "true", then by default the TopComponent will open when the group opens.

    Similar to the above, change the content of "TwoTopComponentWstcgrp.xml" to this:

     

    <?xml version="1.0" encoding="UTF-8" ?>
    
    <!DOCTYPE tc-group PUBLIC
      "-//NetBeans//DTD Top Component in Group Properties 2.0//EN"
      "http://www.netbeans.org/dtds/tc-group2_0.dtd">
    
    <tc-group version="2.0">
        <module name="org.netbeans.modules.windowgroupsample" spec="1.0"/>
        <tc-id id="TwoTopComponent" />
        <open-close-behavior open="true" close="true" />
    </tc-group>

     

  6. Now we will register our new group in the XML Layer. Open the XML Layer and notice the Windows2 section at the end (all generated when you created your two TopComponents). Add the highlighted section below, to register our new group:

     

    <folder name="Windows2">
        <folder name="Components">
            <file name="OneTopComponent.settings" url="OneTopComponentSettings.xml"/>
            <file name="TwoTopComponent.settings" url="TwoTopComponentSettings.xml"/>
        </folder>
        <folder name="Modes">
            <folder name="editor">
                <file name="OneTopComponent.wstcref" url="OneTopComponentWstcref.xml"/>
            </folder>
            <folder name="output">
                <file name="TwoTopComponent.wstcref" url="TwoTopComponentWstcref.xml"/>
            </folder>
        </folder>
        <folder name="Groups">
            <file name="MyGroup.wsgrp" url="groups/MyGroupWsgrp.xml"/>
            <folder name="MyGroup">
                <file name="OneTopComponent.wstcgrp" url="groups/MyGroup/OneTopComponentWstcgrp.xml"/>
                <file name="TwoTopComponent.wstcgrp" url="groups/MyGroup/TwoTopComponentWstcgrp.xml"/>
            </folder>
        </folder>
    </folder>

     

  7. Save everything. Don't install the module yet, let's first refresh all our window positions to their defaults (just in case you've moved things around and things go wrong later, best to have everything at their defaults so that we can analyze the situation better). Close the IDE. Go to the user directory and delete the Windows2Local folder in the user directory's config folder.

     

  8. Start the IDE again. Install the module in the IDE. Close the IDE. Go back to the Windows2Local folder and, when you open the Groups folder, you should now see your new group definition file as well as a folder, containing a file for each of the two TopComponents that belongs to the group (according to your registration entries in the XML Layer):

     

  9. Now start the IDE again. Use the New Action wizard twice. The first time, create "ShowMyGroupAction" and stick this in the performAction() event:

     

    TopComponentGroup group = WindowManager.getDefault().findTopComponentGroup("MyGroup");
    if (group == null) {
        return;
    }
    group.open();

    Put the cursor on the first line above and, when the lightbulb appears, let the IDE generate import statements for these packages:

     

    import org.openide.windows.TopComponentGroup;
    import org.openide.windows.WindowManager;

    The second time you use the New Action wizard, create "HideMyGroupAction" and stick the following into the performAction() event:

     

    TopComponentGroup group = WindowManager.getDefault().findTopComponentGroup("MyGroup");
    if (group == null) {
        return;
    }
    group.close();

    Again let the IDE generate import statements for the two required packages.

     

  10. Install the module again. Now you can use the menu items to show and hide both TopComponents simultaneously. There's a lot of variations that apply here. If you close one of them after opening both, it will not be opened next time you use the menu item for showing both. And that's only one example of the way the Window System API now does all the thinking for you.

Read Section 2.3, where you'll find 5 scenarios and the ways that the open/close state of the windows is handled for you.

Comments:

Just a warning: TopComponent group semantics can be confusing

Posted by Rich Unger on October 20, 2006 at 07:33 AM PDT #

Thanks Rich!

Posted by Geertjan on October 20, 2006 at 08:31 PM PDT #

Thanks for this tutorial, it is the only place I have seen implementing a group explained in a straightforward way. One question: my application has TopComponents in different packages, and I cannot get the groups to work in that case. I can get your example to work, but if I move one of the classes to another package, for example, move TwoTopComponent and TwoAction to org.netbeans.modules.windowgroupsample.two, the TwoTopComponent no longer can be found in the group. Am I just missing updating an xml file or is this not even possible? Thanks

Posted by David Nedde on March 27, 2007 at 01:33 PM PDT #

Never mind - I got the modified example to work after I moved the Bundle.properties values for the moved class. I guess it would be nice if the Refactor -> Move Class operation updated the affected xml files also.

Posted by David Nedde on March 28, 2007 at 12:34 AM PDT #

Thank-you for your posts. They are very helpful.

I created a group based on the example along with section 8.8 of your book. The group opens my TopComponent (a BeanTreeView) and the properties sheet. When I select a node in my tree, the property shows up in the property sheet. "Clean and build all" and everything works fine on first run. On subsequent runs, however, selected nodes do not show up on the property sheet unless I close and re-open the property sheet (or minimize it in the dock and open it again).

o Is this because of an ordering issue?

o Does the property sheet have to be opened after my top component in order to pick up the ExplorerManager? What if the property sheet remains open because of a different function, then I can't force the order when my group opens anyway?

o Is there more to Groups definition to force this kind of sequential behaviour? Maybe close and open the property sheet? (where can I find a spec for the layer.xml anyway?)

Here is my layer.xml snippet:

<folder name="Groups">
<file name="TestCaseGroup.wsgrp" url="groups/TestCaseGroupWsgrp.xml"/>
<folder name="TestCaseGroup">
<file name="TestCaseTopComponent.wstcgrp" url="groups/TestCaseGroup/TestCaseTopComponentWstcgrp.xml"/>
<file name="properties.wstcgrp" url="groups/TestCaseGroup/propertiesWstcgrp.xml"/>
</folder>
</folder>


------

I am puzzled about another issue. My TopComponent should not be opened until a connection to a db is made. I have created a LookupListener to enable the menu item. I set persistence state to PERSISTENCE_NEVER. But if I open the group, then close the application with the group open, restart the application, then subsequent attempts to open the group appear to do nothing. I have only been able to circumvent this by adding the following to Installer:

public boolean closing() {

// Close window groups
TopComponentGroup group = WindowManager.getDefault().findTopComponentGroup("TestCaseGroup");
if (group != null) {
group.close();
}

return true;
}


I think that somehow on startup, there is an attempt to restore my group and hence TopComponent (which shouldn't because I have PERSISITENCE_NEVER for my component, and which will fail because my db connection isn't there yet). But something is happening, because if I close the group on shutdown, things are back to normal.

If there may be further insites into groups which someone may add, I'd appreciate it.

Thank-you

Posted by Glenn Heinze on May 30, 2007 at 07:35 AM PDT #

Hi Glenn. Sorry, I don't know the answers to your questions off hand. I very highly recommend that you join the mailing list (dev@openide.netbeans.org) and ask these questions there, also because all the experts are on that list and because discussing this there is more likely to result in the discussion being found by others, than through comments in this blog entry.

Posted by Geertjan on May 30, 2007 at 07:38 AM PDT #

Thanks for the response. I'll check out the list. And I do appreciate your weblog: I don't know how many times I was puzzled by something and was pointed in the right direction by your examples.

Cheers!

Posted by Glenn Heinze on May 31, 2007 at 01:51 AM PDT #

The issue is resolved if, after opening the group, the requestActive() method is called on the window with nodes. In my case, I actually had to activate another window and then activate the one I was interested in, otherwise the LookupListeners did not seem to register on the proxied focused TopComponent.

group.open();
TestCaseDiffTopComponent.getDefault().requestActive();
TestCaseTopComponent.getDefault().requestActive();

Posted by guest on June 01, 2007 at 12:58 AM PDT #

Hi, I execute l'example above create window group, but in Window2Local don't build my custom group, everyone could help me??? thanks in advance,
Best regard. Jhonny

Posted by jhonny on December 11, 2008 at 11:13 PM PST #

1. When I try to find my component group I always get null, so open() never gets called (I followed this set of instructions except I replaced One, Two and My with meaningful names.) It is unclear as to why this should ever return null in the first place unless the group doesn't exist; since the same code that defines the group also looks it up, this suggests that the code which checks for null doesn't need to, and should just assume it isn't.

2. This example assumes that all your windows are singleton ones (the sort which normally have entries in the Window menu.) What about the "usual" case, where you want some singleton windows to automatically appear when editing a certain document type? Are you supposed to leave the document type itself out of the group, and open the group from the TopComponent?

3. It sucks to have to write so much XML... some frameworks seem to replace Java with XML and then try to pretend "look, you didn't have to write any Java", when XML is actually worse. Is there a programmatic way of doing the same thing?

Posted by Trejkaz on March 28, 2009 at 05:43 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
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today