| <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> |
| <html xmlns="http://www.w3.org/1999/xhtml"> |
| <head> |
| <!-- -*- xhtml -*- --> |
| <title>NetBeans Selection Management Tutorial for NetBeans Platform 6.0</title> |
| <link rel="stylesheet" type="text/css" href="https://netbeans.org/netbeans.css"> |
| <meta name="AUDIENCE" content="NBUSER"> |
| <meta name="TYPE" content="ARTICLE"> |
| <meta name="EXPIRES" content="N"> |
| <meta name="developer" content="tboudreau@netbeans.org"> |
| <meta name="indexed" content="y"> |
| <meta name="description" |
| content="A short guide to managing selection."> |
| <!-- Copyright (c) 2006 Sun Microsystems, Inc. All rights reserved. --> |
| <!-- Use is subject to license terms.--> |
| </head> |
| <body> |
| <h1>NetBeans Selection Management Tutorial I—Using a TopComponent's Lookup</h1> |
| |
| <p>This tutorial covers how to write components that provide a "selected |
| object" and how to write components that update themselves as the global |
| selection changes. |
| |
| |
| <p><b>Contents</b></p> |
| |
| <img src="../../images/articles/60/netbeans-stamp60-61.gif" class="stamp" width="114" height="114" alt="Content on this page applies to NetBeans IDE 6.1" title="Content on this page applies to NetBeans IDE 6.1"> </p> |
| <ul class="toc"> |
| <li><a href="#intro">Introduction to Selection</a></li> |
| <li><a href="#setup">Creating the Module Suite and Projects</a></li> |
| <li><a href="#createapi">Creating an API and Setting Up Dependencies</a></li> |
| <li><a href="#creating-the-viewer-component">Creating the Viewer Component</a></li> |
| <li><a href="#creating-the-editor-component">Creating the Editor Component</a></li> |
| <li><a href="#opening-the-editor-component">Opening Editor Components</a></li> |
| <li><a href="#running">Running the Code</a></li> |
| <li><a href="#the-point">So, What's the Point?</a></li> |
| <li><a href="#change-on-the-fly">Changing Selected Objects on the Fly</a></li> |
| <li><a href="#multiple-objects">Providing More Than One Object</a></li> |
| <li><a href="#misc-things-worth-noticing">Miscellaneous Things Worth Noticing</a></li> |
| <li><a href="#optional-clean-up">Optional Clean-Up</a></li> |
| <li><a href="#next-steps">Next Steps</a></li> |
| </ul> |
| |
| <p><b>To follow this tutorial, you need the software and resources listed in the following |
| table.</b></p> |
| |
| <table> |
| <tbody> |
| <tr> |
| <th class="tblheader" scope="col">Software or Resource</th> |
| <th class="tblheader" scope="col">Version Required</th> |
| </tr> |
| <tr> |
| <td class="tbltd1">NetBeans IDE</td> |
| <td class="tbltd1">version |
| <a href="http://download.netbeans.org/netbeans/6.1/final/">version 6.1</a> or<br> |
| version 6.0</td> |
| </tr> |
| <tr> |
| <td class="tbltd1">Java Developer Kit (JDK)</td> |
| <td class="tbltd1"><a href="http://java.sun.com/javase/downloads/index.jsp">version 6</a> or<br> |
| version 5</td> |
| </tr> |
| </tbody> |
| </table> |
| |
| <p class="tips">Optionally, for troubleshooting purposes, you |
| can <a href="http://plugins.netbeans.org/PluginPortal/faces/PluginDetailPage.jsp?pluginid=3146">download the completed sample</a>. |
| |
| |
| |
| <p><h2 class="tutorial"><a name="intro"></a>Introduction to Selection</h2> |
| |
| <p>"Selection" is an important concept for any non-trivial |
| application. NetBeans has two basic concepts of selection—the contents of |
| the focused <code> |
| <a href="http://wiki.netbeans.org/wiki/view/DevFaqWindowsTopComponent">TopComponent</a></code>'s |
| <code><a href="http://wiki.netbeans.org/wiki/view/DevFaqLookup">Lookup</a></code>, and |
| the focused <code>TopComponent</code>'s <a href="http://wiki.netbeans.org/wiki/view/DevFaqWhatIsANode"> |
| activated <code>Node</code>(s)</a>. Here you will deal only with the Lookup |
| portion of selection—doing more advanced things will be covered in a later |
| tutorial. |
| |
| <p>Selection is used to make possible such things as context sensitive |
| actions (actions that are enabled or disabled depending on what is displayed), |
| and palette windows such as the Property Sheet or Navigator components in |
| the IDE, which each display some aspect of whatever is selected. |
| |
| <p>Basically, each <code>TopComponent</code> has a bag of objects that it can put things |
| into, and which other code is able to query. That bag of objects is its |
| <i>Lookup</i>—essentially a Map where the keys are class objects and the |
| values are objects that extend or implement the key-class. The thing that |
| makes this approach tremendously useful is the ability to use this mechanism |
| to decouple the components that provide some object and the components that |
| consume those objects—so they can be implemented in separate modules, or |
| new editors for old objects can be provided and the rest of the system will |
| continue to work transparently. |
| |
| |
| |
| <!-- ===================================================================================== --> |
| <p><h2 class="tutorial"><a name="setup"></a>Creating the Module Suite and Projects</h2> |
| <p>The example for this tutorial will contain three modules, contained within a module suite, |
| as illustrated below:</p> |
| <p> |
| <img border="1" align="center" src="../../images/tutorials/selection-1/project-setup-60.png"/> |
| |
| <p>Start by |
| creating the module suite to contain all three modules: |
| <ol> |
| <li>Choose File > New Project (Ctrl-Shift-N). Under Categories, select NetBeans Modules. Under Projects, |
| select Module Suite Project and click Next.</li> |
| |
| <li>In the Name and Location panel, type <tt>SelectionSuite</tt> in Project Name. |
| Change the Project Location to any directory on your computer. |
| Click Finish.</li> |
| |
| <li>Choose File > New Project (Ctrl-Shift-N) again. Under Categories, select NetBeans Modules. Under Projects, |
| select Module Project and click Next.</li> |
| |
| <li>In the Name and Location panel, type <tt>MyAPI</tt> in Project Name. |
| The default in the wizard should be to create the module underneath the |
| directory where you just created the suite, which is fine. |
| Click Next. |
| |
| <li>In the Basic Module Configuration panel, replace <tt>yourorg</tt> in Code Name Base with <tt>myorg</tt>, |
| so that the whole code name base is <tt>org.myorg.myapi</tt>. |
| Add spaces to the default Module Display Name, so that it is changed to <tt>My API</tt>. |
| Leave the location of the localizing bundle and XML layer, so that they will be stored in a |
| package with the name <tt>org/myorg/myapi</tt>. Click Finish. |
| |
| |
| <li>You're going to create two more modules now—follow the same steps |
| above, but using the names "MyEditor" and "MyViewer". |
| The reason you are creating three modules will be clear as you continue.</li> |
| |
| </ol> |
| <p><h2 class="tutorial"><a name="createapi"></a>Creating an API and Setting Up Dependencies</h2> |
| What you're going to do here is create a trivial API class. In the real world, |
| such an API might represent files or some other kind of data that is being |
| modelled programmatically. For the purposes of this tutorial it will suffice |
| to have simple objects with a couple of properties. |
| |
| <ol><li><p>Right click the <code>org.myorg.myapi</code> package and choose |
| New > Java Class, as shown below:</p> |
| <p> |
| <img border="1" src="../../images/tutorials/selection-1/create-java-class-60.png"/></p> |
| </li> |
| <li>Name the class <code>APIObject</code>.</li> |
| <li>Replace the default code with the following: |
| <pre class=examplecode> |
| public final class APIObject { |
| |
| private final Date date = new Date(); |
| private static int count = 0; |
| private final int index; |
| |
| public APIObject() { |
| index = count++; |
| } |
| |
| public Date getDate() { |
| return date; |
| } |
| |
| public int getIndex() { |
| return index; |
| } |
| |
| public String toString() { |
| return index + " - " + date; |
| } |
| |
| } |
| </pre> |
| This will be all of the code that this module contains. As you can see, |
| each time a new instance of <code>APIObject</code> is created, a counter |
| is incremented—so there will be some unique attribute to each instance |
| of <code>APIObject</code>. |
| </li> |
| <li>The next step is to have your API module export the <code>org.myorg.myapi</code> |
| package so other modules can see classes in it. Right click the My API project |
| and choose Properties.</li> |
| |
| <li><p>In the API Versioning page |
| in the Project Properties dialog box, check the |
| checkbox for <code>org.myorg.api</code> in the |
| Public Packages list, shown below:</p> |
| <p> |
| <img border="1" src="../../images/tutorials/selection-1/export-package-60.png"/> |
| |
| </li> |
| |
| <li> |
| Now you need to set up some dependencies between your modules. The other two |
| modules, My Editor and My Viewer, will use the |
| <code>APIObject</code> class, so each of them needs to |
| say that they <i>depend on</i> the API module. |
| For each of the other module projects in turn, |
| right click the project node and choose Properties. |
| </li> |
| |
| <li> |
| <p>In the Libraries page of each Project |
| Properties dialog box, click the Add Dependency |
| button. In the dialog box that pops up, |
| type <code>APIObject</code>—there |
| should be only one match, which is your API module. |
| Select it and click |
| OK to add the dependency.</p> |
| <p> |
| <img border="1" src="../../images/tutorials/selection-1/add-deps-60.png"/> |
| |
| |
| </li> |
| </ol> |
| |
| <p><h2><a name="creating-the-viewer-component"></a>Creating the Viewer Component</h2> |
| <p>Now you will create a singleton component that will track if there is an |
| <code>APIObject</code> available in the global selection (i.e., if the |
| focused <code>TopComponent</code> has one in its Lookup). If there is one, it will display |
| some data about it. One common use case for this sort of thing is creating |
| master/detail views.</p> |
| <p> |
| A "singleton component" is a component like the Projects window in the |
| NetBeans IDE, or the Property Sheet or the Navigator—a component that |
| there is only ever one of in the system. The Window Component |
| wizard will automatically generate all of the code needed to create such |
| a singleton component—you just have to use the form designer or write code |
| to provide the contents of your singleton component. |
| |
| <ol> |
| <li>Right click the <code>org.myorg.myviewer</code> package and choose |
| New > Other.</li> |
| <li>In the resulting dialog, select Module Development > |
| Window Component and click Next (or press Enter).</li> |
| <li><p>On the "Basic Settings" page of the wizard, |
| select <code>navigator</code> as the |
| location in which to place your viewer component, and check the |
| checkbox to open the component on startup, |
| as shown below:</p> |
| <p> |
| <img border="1" src="../../images/tutorials/selection-1/new-window-60.png"/> |
| </p> |
| </li> |
| <li>Click Next to continue to the "Name, Icon and Location" |
| page of the wizard.</li> |
| <li>On the following page, name the class <code>MyViewer</code> and |
| click Finish (or press Enter).</li> |
| </ol> |
| |
| You now have a skeleton <code>TopComponent</code>—a singleton component called |
| <code>MyViewerTopComponent</code>. Click the editor tab for |
| <code>MyViewerTopComponent</code>—the form editor should be visible. |
| You will add two labels to the component, which will display some information |
| about the selected <code>APIObject</code> if there is one. |
| |
| <ol> |
| <li><p>Drag two JLabels to the form from the Palette, one |
| below the other.</p> |
| <p> |
| <img border="1" src="../../images/tutorials/selection-1/viewer-form-editor.png"/> |
| </p> |
| <p>Change the text of the first as shown above, so that by default it |
| displays "[nothing selected]". |
| </li> |
| <li>Click the Source button in the editor toolbar to switch to the |
| code editor</li> |
| <li>Modify the signature of the class, so that <code>MyViewerTopComponent</code> |
| implements <code>LookupListener</code>: |
| <pre class=examplecode> |
| public class MyViewerTopComponent extends TopComponent implements LookupListener { |
| </pre> |
| </li> |
| <li>Right-click in the editor and choose Fix Imports, so that |
| <code>LookupListener</code> is imported.</li> |
| |
| <li><p>Put the caret in the signature line as shown below. A lightbulb glyph |
| should appear in the editor margin. Press Alt-Enter, and then Enter again |
| when the popup appears with the text "Implement All Abstract Methods". |
| This will add the LookupListener method to your class.</p> |
| <p> |
| <img border="1" src="../../images/tutorials/selection-1/implement-methods.png"/> |
| </p> |
| </li> |
| |
| <li> |
| You now have a class that implements <code>LookupListener</code>. Now it needs |
| something to listen to. In your case, there is a convenient global Lookup |
| object, which simply proxies the Lookup of whatever component has focus—it |
| can be obtained from the call <code>Utilities.actionsGlobalContext()</code>. |
| So rather than tracking what component has focus yourself, you can simply |
| listen to this one global selection lookup, which will fire appropriate |
| changes whenever focus changes. Edit the source code so that it contains |
| the following methods as shown here: |
| |
| <pre class=examplecode> |
| private Lookup.Result result = null; |
| public void componentOpened() { |
| Lookup.Template tpl = new Lookup.Template (APIObject.class); |
| result = Utilities.actionsGlobalContext().lookup(tpl); |
| result.addLookupListener (this); |
| } |
| |
| public void componentClosed() { |
| result.removeLookupListener (this); |
| result = null; |
| } |
| |
| public void resultChanged(LookupEvent lookupEvent) { |
| Lookup.Result r = (Lookup.Result) lookupEvent.getSource(); |
| Collection c = r.allInstances(); |
| if (!c.isEmpty()) { |
| APIObject o = (APIObject) c.iterator().next(); |
| jLabel1.setText (Integer.toString(o.getIndex())); |
| jLabel2.setText (o.getDate().toString()); |
| } else { |
| jLabel1.setText("[no selection]"); |
| jLabel2.setText (""); |
| } |
| } |
| </pre> |
| <p><code>componentOpened()</code> is called whenever the component is |
| made visible by the window system; <code>componentClosed()</code> is |
| called whenever the user clicks the X button on its tab to close it. |
| So whenever the component is showing, you want it to be tracking the |
| selection—which is what the above code does. |
| |
| <p>The <code>resultChanged()</code> method is your implementation of |
| <code>LookupListener</code>. Whenever the selected <code>APIObject</code> |
| changes, it will update the two <code>JLabel</code>s you put on the form. |
| </li> |
| </ol> |
| |
| <p><h2><a name="creating-the-editor-component"></a>Creating the Editor Component</h2> |
| <p>Now you need something to actually provide instances of <code>APIObject</code>, |
| for this code to be of any use. Fortunately this is quite simple.</p> |
| <p> |
| You will create another <code>TopComponent</code>, this time, one that opens |
| in the editor area and offers an instance of <code>APIObject</code> from |
| its <code>Lookup</code>. You <i>could</i> use the Window Component template |
| again, but that template is designed for creating singleton components, rather |
| than components there can be many of. So you will simply create a <code>TopComponent</code> |
| subclass without the template, and an action which will open additional ones. |
| <ol> |
| |
| <li>You will need to add three dependencies to the My Editor module for |
| it to be able to find the classes you will be using. Right click the My Editor |
| project and choose Properties. On the Library page |
| of the Project Properties |
| dialog box, click the Add Dependency button, and type <code>TopComponent</code>. The |
| dialog should automatically suggest setting a dependency on the |
| Window System API. Do the same thing for <code>Lookups</code> (Utilities |
| API).</li> |
| |
| <li>Right-click the <code>org.myorg.myeditor</code> package in the |
| My Editor project, and choose New > JPanel Form.</li> |
| |
| <li>Name it "MyEditor", and finish the wizard.</li> |
| |
| <li>When the form editor opens, drop two JTextFields on the form, |
| one above the other. On the property sheet, set the Editable property |
| (checkbox) to <code>false</code> for each one.</li> |
| |
| <li>Click the Source button in the editor toolbar to switch to the code editor.</li> |
| |
| <li>Change the signature of <code>MyEditor</code> to extends <code>TopComponent</code> |
| instead of <code>javax.swing.JPanel</code>: |
| <pre class=examplecode> |
| public class MyEditor extends TopComponent {</pre> |
| </li> |
| |
| <li>Add the following code to the constructor of <code>MyEditor</code>: |
| <pre class=examplecode> |
| APIObject obj = new APIObject(); |
| associateLookup (Lookups.singleton (obj)); |
| jTextField1.setText ("APIObject #" + obj.getIndex()); |
| jTextField2.setText ("Created: " + obj.getDate()); |
| setDisplayName ("MyEditor " + obj.getIndex()); |
| </pre> |
| Right-click in the editor |
| and choose Fix Imports. |
| </li> |
| </ol> |
| |
| <p>The line |
| <code>associateLookup (Lookups.singleton (obj));</code> |
| will create a Lookup that contains only one object—the new |
| instance of <code>APIObject</code>—and assign that <code>Lookup</code> |
| to be what is returned by <code>MyEditor.getLookup()</code>. |
| While this is an artificial example, |
| you can imagine how <code>APIObject</code> might represent a file, an entity in a |
| database or anything else you might want to edit or view. Probably you |
| can also imagine one component that allowed you to select or edit multiple |
| unique instances of <code>APIObject</code>—that will be the subject of the |
| next tutorial. |
| |
| <p>To make your editor component at least somewhat interesting (though it |
| doesn't actually edit anything), you set the text |
| fields' values to values from the <code>APIObject</code>, so you have |
| something to display. |
| |
| <p><h2><a name="opening-the-editor-component"></a>Opening Editor Components</h2> |
| Now you need a way to open <code>MyEditor</code> components in the editor |
| area, so that there will be something to show. To do anything meaningful |
| with selection, you will need more than one editor so that there is more |
| than one <code>APIObject</code> to track. |
| |
| Since you will want multiple editors, |
| you need a simple action on the main menu which will create and open another instance |
| of <code>MyEditor</code> in the window system (as opposed to what the |
| Window Component template would create for us, which is an action that always |
| looks up a singleton component such as the Navigator or Property Sheet components |
| in the IDE). |
| |
| <ol> |
| <li>Right click the <code>org.myorg.myeditor</code> package and choose |
| New > Other.</li> |
| <li>In the dialog, choose Module Development > Action and |
| click Next.</li> |
| <li>Accept the defaults ("always enabled") and press Next |
| again.</li> |
| <li>On the GUI Registration page, accept the defaults by pressing Next |
| again (this will cause your action to appear at the top of the File |
| menu).</li> |
| <li>On the final page of the wizard, name the action |
| <code>OpenEditorAction</code> and set its display name to say |
| "Open Editor".</li> |
| <li>Press Finish to generate the action class.</li> |
| <li>The code editor should now be open over a class called <code>OpenEditorAction</code>, |
| which subclasses <code>CallableSystemAction</code> (a NetBeans subclass of |
| <code>javax.swing.Action</code> which lets you associate context sensitive |
| help with an action). Add the following code to its <code>performAction()</code> |
| method: |
| <pre class=examplecode> |
| MyEditor editor = new MyEditor(); |
| editor.open(); |
| editor.requestActive();</pre> |
| The above code will simply create a new instance of <code>MyEditor</code> (which |
| in turn will create a new instance of <code>APIObject</code> and put it in |
| its <code>Lookup</code>) and |
| open it in the window system. |
| </li> |
| </ol> |
| |
| <p><h2><a name="running"></a>Running the Code</h2> |
| <p>Now you're ready to run the tutorial. Simply right click <code>SelectionSuite</code>, |
| the module suite which owns your three modules, and choose Run from the popup |
| menu. When the IDE opens, simply choose File > Open Editor—invoke your |
| action. Do this a couple of times, so that there are several of your editor |
| components open. Your singleton <code>MyViewer</code> window should also be open. Notice how |
| the <code>MyViewer</code> window's contents change as you click different tabs, |
| as shown here: |
| <p> |
| <img border="1" src="../../images/tutorials/selection-1/result-1-60.png"/> |
| </p> |
| <p>If you click in the Projects window, notice |
| that the text changes to "[No Selection]", as shown below:</p> |
| <p> |
| <img border="1" src="../../images/tutorials/selection-1/result-2-60.png"/> |
| </p> |
| <p> |
| If you do not see the <code>MyViewer</code> window, you probably did not check |
| the checkbox in the wizard to open it on system start—simply go to the |
| Window menu and choose Open MyViewer Window to display it.</p> |
| |
| <p><h2><a name="the-point"></a>So, What's the Point?</h2> |
| You might be wondering what the point of this exercise is—you've just shown that |
| you can handle selection—big deal! The key to the importance of this is the |
| way the code is split into three modules: The My Viewer module knows nothing |
| about the My Editor module—either one can run by itself. They only share a |
| common dependency on My API. That's important—it means two things: 1. My Viewer |
| and My Editor can be developed and shipped independently, and 2. Any module that |
| wants to provide a different sort of editor than My Editor can do so, and the viewer |
| component will work perfectly with it, as long as the replacement editor offers an instance of |
| <code>APIObject</code> from its Lookup.</p> |
| |
| <p>To really picture the value of this, imagine <code>APIObject</code> were something |
| much more complex; imagine that <code>MyEditor</code> is an image editor, and <code> |
| APIObject</code> represents an image being edited. The thing that's powerful here is |
| that you could replace <code>MyEditor</code> with, say, an SVG vector-based editor, |
| and the viewer component (presumably showing attributes of the currently edited image) |
| will work transparently with that new editor. It is this model of doing things |
| that is the reason you can add new tools into the NetBeans IDE that work against Java |
| files, and they will work in different versions of NetBeans, and that you can have |
| an alternate editor (such as the form editor) for Java files and all the components |
| and actions that work against Java files still work when the form editor is used. |
| |
| <p>This is very much the way NetBeans works with Java and other source files— |
| in their case, the thing that is available from the editor's Lookup is a |
| <code><a href="http://wiki.netbeans.org/wiki/view/DevFaqDataObject">DataObject</a></code>, |
| and components like Navigator and the Property Sheet are simply watching what object is |
| being made available by the focused <code>TopComponent</code>. |
| |
| <p>Another valuable thing about this approach is that often people are migrating |
| existing applications to the NetBeans platform. The object that is part of the |
| data model, in that case, is probably existing, working code that should not |
| be changed in order to integrate it into NetBeans. By keeping the data model's |
| API in a separate module, the NetBeans integration can be kept separate from |
| the core business logic. |
| |
| <p><h2><a name="change-on-the-fly"></a>Changing Selected Objects on the Fly</h2> |
| To make it really evident how powerful this approach can be, you'll take one more step, |
| and add a button to your editor component that lets it replace the <code>APIObject</code> |
| it has with a new one on the fly. |
| |
| <ol> |
| <li>Open <code>MyEditor</code> in the form editor (click the Design toolbar |
| button in the editor toolbar), and drag a <code>JButton</code> to it.</li> |
| |
| <li>Set the <code>text</code> property of the JButton to "Replace".</li> |
| |
| <li>Right click the <code>JButton</code> and choose Events > Action > actionPerformed. |
| This will cause the code editor to open with the caret in an event handler method. |
| </li> |
| |
| <li>At the head of the class definition, you will add one final field: |
| <pre class=examplecode> |
| public class MyEditor extends TopComponent { |
| private final InstanceContent content = new InstanceContent();</pre> |
| InstanceContent is a class which allows us to modify the content of a Lookup |
| (specifically an instance of <code>AbstractLookup</code>) on the fly. |
| </li> |
| <li>Copy all of the lines you added earlier to the constructor to the clipboard, |
| and delete them from the constructor, except for the line beginning |
| "associateLookup...". That line of the constructor should be changed as follows: |
| <pre class=examplecode> |
| associateLookup (new AbstractLookup (content)); </pre> |
| </li> |
| <li>You will be using the lines that you put on the clipboard in the action |
| handler for the JButton—so you should run this code once when you first |
| initialize the component. Add the following line to the constructor, after |
| the line above: |
| <pre class=examplecode> |
| jButton1ActionPerformed (null);</pre> |
| </li> |
| <li>Modify the event handler method so it appears as follows, pasting |
| from the clipboard and adding the line at the end: |
| <pre class=examplecode> |
| private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) { |
| APIObject obj = new APIObject(); |
| jTextField1.setText ("APIObject #" + obj.getIndex()); |
| jTextField2.setText ("Created: " + obj.getDate()); |
| setDisplayName ("MyEditor " + obj.getIndex()); |
| content.set(Collections.singleton (obj), null); |
| }</pre> |
| </li> |
| <li>Right-click in the editor |
| and choose Fix Imports.</li> |
| </ol> |
| <p>You're now ready to run the suite again. Right click SelectionSuite again and |
| choose Run. Notice how, now, when you click the Replace button, all of the |
| components update, including the instance of <code>MyViewer</code>—everything.</p> |
| |
| <p> |
| <img border="1" src="../../images/tutorials/selection-1/first-run-60.png"/> |
| </p> |
| |
| <p><h2><a name="multiple-objects"></a>Providing More Than One Object</h2> |
| This is all well and good for decoupling, but isn't providing this one object |
| from your component a bit like having a <code>Map</code> that only contains |
| one key and one value? The answer is, yes, it is like that. Where this technique becomes |
| even more powerful is when you provide multiple objects from multiple APIs.</p> |
| |
| <p>As an example, it is very common in NetBeans to provide context sensitive |
| actions. A case in point is the built-in <code>SaveAction</code> that is part |
| of NetBeans' Actions API. What this action actually does is, it simply listens |
| for the presence of something called <code>SaveCookie</code> on the global context— |
| the same way your viewer window listens for <code>APIObject</code>. If a <code>SaveCookie</code> |
| appears (editors typically add one to their lookup when the content of the file |
| is modified but not yet saved), the action becomes enabled, so the Save toolbar |
| button and menu items become enabled. When the Save action is invoked, it calls |
| <code>SaveCookie.save()</code>, which in turn causes the <code>SaveCookie</code> to |
| disappear, so the Save action then becomes disabled until a new one appears. |
| |
| <p>As you may have noticed, context sensitivity was one option in the New |
| Action wizard. The actions currently generated by the wizard actually |
| use a way of doing this that pre-dates <code>Lookup</code>; the Lookup-based way of doing such |
| context sensitive actions is described |
| <a href="http://wiki.netbeans.org/wiki/view/DevFaqActionContextSensitive">in the developer FAQ</a>. |
| |
| <p>So the pattern in practice is to provide more than just a single object from |
| your component's <code>Lookup</code>—different auxilliary components and different |
| actions will be interested in different aspects of the object being edited. These |
| aspects can be cleanly separated into interfaces which those auxilliary components |
| and actions can depend on and listen for. |
| |
| |
| <p><h2><a name="misc-things-worth-noticing"></a>Miscellaneous Things Worth Noticing</h2> |
| |
| <p>While not directly related to the topic of this tutorial, it's worth noticing |
| that if you open three <code>MyEditor</code> instances, and shut down and restart |
| NetBeans, you end up with three <code>MyEditor</code> instances magically appearing |
| on restart. By default, your editors are serialized to disk on shutdown and |
| restored on restart.</p> |
| <p> |
| If you do not want this behavior, there are two other choices. Override |
| the following method on <code>MyEditor</code> to cause editors <i>never</i> to be reopened on restart: |
| <pre class=examplecode> |
| public int getPersistenceType() { |
| return PERSISTENCE_NEVER; |
| }</pre> |
| <p>If you want to persist the components that are open but discard those that have |
| been closed, return <code>PERSISTENCE_ONLY_OPENED</code> instead. The default |
| (for backward compatibility reasons) is <code>PERSISTENCE_ALWAYS</code>, which |
| is not appropriate for editor-style componenents—it means that even editors that have been closed |
| are kept forever and reloaded on restart.</p> |
| <p> |
| Note, though, that part of what is serialized to disk is the <i>location</i> of |
| your component in the main window. So singleton <code>TopComponents</code>s such as the |
| property sheet, or our viewer component, should use <code>PERSISTENCE_ALWAYS</code> - |
| otherwise if they are closed once by the user, the next time they are opened they |
| will appear in the editor area instead of where they are supposed to be.</p> |
| |
| <p><h2><a name="optional-clean-up"></a>Optional Clean-Up</h2> |
| The module template, by default, assumes you will want to use the |
| <code>layer.xml</code> file to install objects. In the case of the |
| My API module, it is actually not used. So a polite thing to do to get a |
| slight improvement in startup time would be the following: |
| <ol> |
| <li>Expand the Important Files node of the My API project</li> |
| <li>Double click the Module Manifest node</li> |
| <li>Delete the following line from the manifest: |
| <pre class=examplecode> |
| OpenIDE-Module-Layer: org/myorg/myapi/layer.xml</pre> |
| </li> |
| <li>Then delete the corresponding <code>layer.xml</code> file in |
| <code>org.myorg.myapi</code></li> |
| </ol> |
| |
| <br> |
| <div class="feedback-box"><a href="https://netbeans.org/about/contact_form.html?to=3&subject=Feedback:%20Selection%20Tutorial%20Part%201%20for%206.0">Send Us Your Feedback</a></div> |
| <br style="clear:both;" /> |
| |
| |
| <p><h2><a name="next-steps"></a>Next Steps</h2> |
| By now you may have noticed that some components have more granular selection logic, |
| and even involve multiple selection. In the <a href="nbm-selection-2.html"> |
| next tutorial</a> you will cover how to use |
| the <a href="https://netbeans.org/download/dev/javadoc/org-openide-nodes/overview-summary.html">Nodes API</a> to handle that. |
| |
| |
| |
| </body> |
| </html> |