blob: eaf976f5cd1273c3ba98f7d9f1c268c10a1cf4b3 [file] [log] [blame]
<!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.9</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="indexed" content="y"/>
<meta name="description"
content="A short guide to managing selection."/>
<!-- Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. -->
<!-- Use is subject to license terms.-->
</head>
<body>
<h1>NetBeans Selection Management Tutorial I&#8212;Using a TopComponent's Lookup</h1>
<p>This tutorial covers how to write components that provide a &quot;selected
object&quot; and how to write components that update themselves as the global
selection changes.</p>
<p><b class="notes">Note:</b> This document is not the current version of this tutorial. See
<a href="../nbm-selection-1.html">the current version
of this tutorial here</a>.</p>
<p><b>Contents</b></p>
<p><img src="../../images/articles/69/netbeans-stamp69.png" class="stamp" width="114" height="114" alt="Content on this page applies to NetBeans IDE 6.5, 6.7, 6.8" title="Content on this page applies to NetBeans IDE 6.5, 6.7, 6.8"/></p>
<ul class="toc">
<li><a href="#intro">Introduction to Selection</a></li>
<li><a href="#setup">Creating the NetBeans Platform Application Project</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="#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"><a href="https://netbeans.org/downloads/index.html">NetBeans IDE</a></td>
<td class="tbltd1">version 6.9 or above</td>
</tr>
<tr>
<td class="tbltd1"><a href="http://java.sun.com/javase/downloads/index.jsp">Java Developer Kit (JDK)</a></td>
<td class="tbltd1">Version 6</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>&quot;Selection&quot; is an important concept for any non-trivial
application. NetBeans has two basic concepts of selection&#8212;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&#8212;doing more advanced things will be covered in a later
tutorial.</p>
<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>
<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>&#8212;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&#8212;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 NetBeans Platform Application Project</h2>
<p>The example for this tutorial will contain three modules, contained within a NetBeans Platform application,
as illustrated below:</p>
<p>
<img src="../../images/tutorials/selection-1/project-setup-69.png" alt="projects window showing setup" />
</p>
<p>Start by
creating the NetBeans Platform application that
will contain all three modules:</p>
<ol>
<li><p>Choose File &gt; New Project (Ctrl-Shift-N). Under Categories, select NetBeans Modules. Under Projects,
select NetBeans Platform Application:</p>
<p>
<img src="../../images/tutorials/selection-1/new-app-1.png" alt="new app, first panel" />
</p>
<p>Click Next.</p></li>
<li><p>In the Name and Location panel, type <tt>SelectionSuite</tt> in Project Name.
Change the Project Location to any directory on your computer:</p>
<p>
<img src="../../images/tutorials/selection-1/new-app-2.png" alt="new app, second panel" />
</p>
<p>Click Finish.</p>
<p>You now have a NetBeans Platform application project, which is the container
for the modules that you will be creating throughout this tutorial:</p>
<p>
<img src="../../images/tutorials/selection-1/new-app-3.png" alt="new app, third panel" />
</p>
<p>Via the configuration files shown in the screenshot above, the NetBeans
Platform application project provides a set of pre-defined modules, such as
modules that give your application a window system and an action system. The configuration
files also
sets up the application to be built via the Ant build system. In the next
steps, you will be adding three custom modules to the application. The first module will provide an API
class, while the other two will share that API module between them. That will ensure
that the other two modules do not depend on each other, making them loosely coupled. More
on this will become clear as you work through this tutorial.</p>
</li>
<li><p>Choose File &gt; New Project (Ctrl-Shift-N) again. Under Categories, select NetBeans Modules. Under Projects,
select Module:</p>
<p>
<img src="../../images/tutorials/selection-1/new-module-1.png" alt="new module, first panel" />
</p>
<p>Click Next.</p></li>
<li><p>In the Name and Location panel, type <tt>MyAPI</tt> in Project Name.
Now look at the Project Location field and the Project Folder field in the screenshot below.
As you can see, the default in the wizard is to create the module within the
directory where you just created the "SelectionSuite" application, which means that
the module's sources will be organized within the folder where the application
is defined. That is the standard way to organize NetBeans Platform application sources.</p>
<p>
<img src="../../images/tutorials/selection-1/new-module-2.png" alt="new module, second panel" />
</p>
<p>Click Next.</p></li>
<li><p>In the Basic Module Configuration panel, set the
following values:</p>
<p></p><ul>
<li><b>Code Name Base.</b> The code name base is a string that
uniquely identifies a module. By convention, the value of the code name base is the main
package of the module. Therefore, if the main
package of your module is going to be "org.me.foo", then that would
normally also be the value of the code name base of the module.
In this case, since the base package will be "org.myorg.myapi",
set the code name base to <tt>org.myorg.myapi</tt>. That will
be the string used to identify the module, so that that will also
be the string used by other modules that will need to make use of
code within this module.</li>
<li><b>Module Display Name.</b> Set the module display name to <tt>My API</tt>. That
is the text you will see displayed for the module in the Projects window in the IDE.</li>
<li><b>Localizing Bundle.</b> Leave the location of the localizing bundle,
so that localization keys/values will be stored in the
main package, with the name <tt>org/myorg/myapi</tt>. </li>
<li><b>Generate XML Layer.</b> When you click this checkbox, an XML file will be generated
and it will be registered in the manifest file of the module. The XML file is known as
the "layer file" and each module in the application can have, at most, one of these files.
The layer file contains registration entries for contributions to the
central registry of the application. For example, new menus and windows
provided by a module to the application
need to be registered in the module's layer file. In this case, do not let
the IDE create an XML layer file, because this module will not
need to register anything in the central registry of the application.</li>
<li><b>Generate OSGi Bundle.</b> Two module systems are supported by the NetBeans Platform,
the NetBeans module system and the OSGi framework. Depending on your business
requirements, select the module system of your choice. We will use the default module
system in this tutorial, that is, the NetBeans module system. Therefore, leave this
checkbox unselected.</li>
</ul>
<p>The result should be as follows:</p>
<p>
<img src="../../images/tutorials/selection-1/new-module-3.png" alt="new module, third panel" />
</p>
<p>Click Finish.</p></li>
<li><p>You're going to create two more modules now&#8212;follow step 3, 4, and 5
above, twice, using the names &quot;MyEditor&quot; (code name base <tt>org.myorg.myeditor</tt>)
and &quot;MyViewer&quot; (code name base <tt>org.myorg.myviewer</tt>). In both cases,
however, let the wizard generate a layer file, because the
viewer and editor modules will both be making contributions to the central
registry of the application.</p>
<p>At the end of this step, the structure of the application should be as follows:</p>
<p>
<img src="../../images/tutorials/selection-1/project-setup-69.png" alt="projects window showing setup" />
</p>
<p>The reason you are creating three modules in the application is that you are creating a modular
application, where the viewer and editor modules will be loosely coupled
from each other, only sharing the API module between them. The usefulness
of this approach will become clearer as you continue with this tutorial.</p></li>
</ol>
<h2 class="tutorial"><a name="createapi"></a>Creating an API and Setting Up Dependencies</h2>
<p>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.</p>
<ol><li><p>Right click the <code>org.myorg.myapi</code> package and choose
New &gt; Java Class.</p>
</li>
<li>Name the class <code>APIObject</code> and click Finish.</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&#8212;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 alt="api versioning tab" src="../../images/tutorials/selection-1/export-package-69.png"/>
</p>
<p>Click OK. Now expand the Important Files
node and open the Project Metadata file. On disk,
this file is named <code>project.xml</code>. Inside this
file, notice the following section, which was
added when you clicked OK in the dialog above:</p>
<pre class="examplecode">
&lt;public-packages&gt;
&lt;package&gt;org.myorg.myapi&lt;/package&gt;
&lt;/public-packages&gt;
</pre>
<p>When the module is compiled, the information above
in the <code>project.xml</code> file is added to the module's
manifest file.</p>
</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 these two modules 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>&#8212;there
should be only one match, which is your API module.
Select it and click
OK to add the dependency. You should see the following:</p>
<p>
<img src="../../images/tutorials/selection-1/add-deps-69.png" alt="adding a dependency"/>
</p>
<p>Click OK. When you open the Project Metadata file
in the Important Files node of the My Editor
module and the My Viewer module, you should
see that the section below has been added:</p>
<pre class="examplecode">
&lt;module-dependencies&gt;
&lt;dependency&gt;
&lt;code-name-base&gt;org.myorg.myapi&lt;/code-name-base&gt;
&lt;build-prerequisite/&gt;
&lt;compile-dependency/&gt;
&lt;run-dependency&gt;
&lt;specification-version&gt;1.0&lt;/specification-version&gt;
&lt;/run-dependency&gt;
&lt;/dependency&gt;
&lt;/module-dependencies&gt;
</pre>
<p>Notice that the code name base of the MyAPI module is used
to identify it here. When the module is compiled, the information above
in the <code>project.xml</code> file is added to the module's
manifest file.</p>
</li>
</ol>
<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 &quot;singleton component&quot; is a component like the Projects window in the
NetBeans IDE, or the Property Sheet or the Navigator&#8212;a component that
there is only ever one of in the system. The Window wizard
will automatically generate all of the code needed to create such
a singleton component&#8212;you just have to use the form designer or write code
to provide the contents of your singleton component.</p>
<ol>
<li>Right click the <code>org.myorg.myviewer</code> package and choose
New &gt; Other.</li>
<li>In the resulting dialog, select Module Development &gt;
Window and click Next (or press Enter).</li>
<li><p>On the &quot;Basic Settings&quot; page of the wizard,
select <code>explorer</code> as the
location in which to place your viewer component, and check the
checkbox to cause the window to open on startup,
as shown below:</p>
<p>
<img src="../../images/tutorials/selection-1/new-window-69.png" alt="new window wizard" />
</p>
</li>
<li>Click Next to continue to the &quot;Name, Icon and Location&quot;
page of the wizard.</li>
<li>On the following page, name the class <code>MyViewer</code> and
click Finish (or press Enter).</li>
</ol>
<p>You now have a skeleton <code>TopComponent</code>&#8212;a singleton component called
<code>MyViewerTopComponent</code>. It is registered in the layer file of the MyViewer
module as follows. Notice, in line 6 below, that it will be displayed in the "explorer" mode, that is,
the leftmost side of the application. If it has a different value there, change it now
to "explorer":</p>
<pre class="examplecode">
&lt;folder name="Windows2"&gt;
&lt;folder name="Components"&gt;
&lt;file name="MyViewerTopComponent.settings" url="MyViewerTopComponentSettings.xml"/&gt;
&lt;/folder&gt;
&lt;folder name="Modes"&gt;
&lt;folder name="explorer"&gt;
&lt;file name="MyViewerTopComponent.wstcref" url="MyViewerTopComponentWstcref.xml"/&gt;
&lt;/folder&gt;
&lt;/folder&gt;
&lt;/folder&gt;
</pre>
<p>Open the <code>MyViewerTopComponent</code> file and click its Design tab&#8212;the
"Matisse" GUI Builder (also known as the "form editor") opens.
You will add two labels to the component, which will display some information
about the selected <code>APIObject</code> if there is one.</p>
<ol>
<li><p>Drag two JLabels to the form from the Palette, one
below the other.</p>
<p>
<img alt="" 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]".</p>
</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 &quot;Implement All Abstract Methods&quot;.
This will add the LookupListener method to your class:</p>
<p>
<img alt="implement methods" src="../../images/tutorials/selection-1/implement-methods.png"/>
</p>
</li>
<li>
<p>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&#8212;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. </p>
<p>Edit the source code of the <code>MyViewerTopComponent</code> so that its
<code>componentOpened</code>, <code>componentClosed</code>,
and <code>resultChanged</code> methods are as follows:</p>
<pre class="examplecode">
private Lookup.Result result = null;
public void componentOpened() {
result = Utilities.actionsGlobalContext().lookupResult(APIObject.class);
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>
<ul>
<li><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&#8212;which is what the above code does.</li>
<li>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>
</ul>
<p>The required import statements for the <code>MyViewerTopComponent</code> are
as follows:</p>
<pre class="examplecode">
import java.util.Collection;
import java.util.logging.Logger;
import org.myorg.myapi.APIObject;
import org.openide.util.LookupEvent;
import org.openide.util.NbBundle;
import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager;
import org.netbeans.api.settings.ConvertAsProperties;
import org.openide.util.Lookup;
import org.openide.util.LookupListener;
import org.openide.util.Utilities;</pre>
</li>
</ol>
<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 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.</p>
<ol>
<li>You will need 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> (Lookup
API). Also set a dependency on the Utilities API, which provides
various helpful supporting classes that are made available by the
NetBeans Platform.</li>
<li>Right-click the <code>org.myorg.myeditor</code> package in the
My Editor project, and choose New &gt; JPanel Form.</li>
<li>Name it &quot;MyEditor&quot;, 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&#8212;the new
instance of <code>APIObject</code>&#8212;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>&#8212;that will be the subject of the
next tutorial.</p>
<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>
<p>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.</p>
<p>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 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).</p>
<ol>
<li>Right click the <code>org.myorg.myeditor</code> package and choose
New &gt; Java Class.</li>
<li>Create a class named <code>OpenEditorAction</code> and click Finish.</li>
<li>Define the class as follows:
<pre class="examplecode">
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class OpenEditorAction implements ActionListener{
@Override
public void actionPerformed(ActionEvent e) {
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>
<li>Now we need to register the new ActionListener in the <code>layer.xml</code>
file, which defines all the contributions to the application's
central registry. Do so as follows:
<pre class="examplecode">
&lt;folder name="Actions"&gt;
&lt;folder name="File"&gt;
&lt;file name="org-myorg-myeditor-MyEditorAction.instance"&gt;
&lt;attr name="instanceCreate" methodvalue="org.openide.awt.Actions.alwaysEnabled"/&gt;
&lt;attr name="delegate" newvalue="org.myorg.myeditor.OpenEditorAction"/&gt;
&lt;attr name="displayName" bundlevalue="org.myorg.myeditor.Bundle#CTL_MyEditorAction"/&gt;
&lt;/file&gt;
&lt;/folder&gt;
&lt;/folder&gt;
&lt;folder name="Menu"&gt;
&lt;folder name="File"&gt;
&lt;file name="MyEditorAction.shadow"&gt;
&lt;attr name="originalFile" stringvalue="Actions/File/org-myorg-myeditor-MyEditorAction.instance"/&gt;
&lt;/file&gt;
&lt;/folder&gt;
&lt;/folder&gt;</pre>
<p>Above, you have defined entries within the "Actions" folder and within the "Menu" folder.
The Menu folder specifies that the File menu will have an entry
for the specific Action that you registered in the Actions folder,
where you specified that the "<a href="http://bits.netbeans.org/dev/javadoc/org-openide-awt/org/openide/awt/Actions.html#alwaysEnabled%28java.awt.event.ActionListener,%20java.lang.String,%20java.lang.String,%20boolean%29">Actions.alwaysEnabled</a>"
method, provided by the NetBeans Platform, will handle the display of the
<code>OpenEditorAction</code> class. For details on the above and other related attributes,
see the article <a href="http://netbeans.dzone.com/news/which-netbeans-platform-action">Which NetBeans Platform Action Class Should I Use?</a></p></li>
<li>As indicated by the "displayName" attribute above,
in the <code>Bundle.properties</code> file you need to define this key/value pair:
<pre class="examplecode">
CTL_MyEditorAction=Open Editor
</pre></li>
<li>Set a dependency on the UI Utilities API, which provides the <code>Actions.alwaysEnabled</code>
method you referred to in the Actions folder of the <code>layer.xml</code> file, shown above.</li>
</ol>
<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 application which contains your three modules, and choose Run from the popup
menu. When the IDE opens, simply choose File &gt; Open Editor&#8212;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>
<p>
<img alt="selection" src="../../images/tutorials/selection-1/result-1-69.png"/>
</p>
<p>If you click in the Viewer window, notice
that the text changes to &quot;[No Selection]&quot;, as shown below:</p>
<p>
<img alt="no selection" src="../../images/tutorials/selection-1/result-2-69.png"/>
</p>
<p>
<b>Note:</b> 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&#8212;simply go to the
Window menu and choose MyViewer to display it.</p>
<h2><a name="the-point"></a>So, What's the Point?</h2>
<p>You might be wondering what the point of this exercise is&#8212;you've just shown that
you can handle selection&#8212;big deal! The key to the importance of this is the
way the code is split into three modules&#8212;the My Viewer module knows nothing
about the My Editor module&#8212;either one can run by itself. They only share a
common dependency on My API. That's important&#8212;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>
<p>This is very much the way NetBeans works with Java and other source files&mdash;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>
<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>
<p>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.</p>
<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 &quot;Replace&quot;.</li>
<li>Right click the <code>JButton</code> and choose Events &gt; Action &gt; 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>
<a href="http://bits.netbeans.org/dev/javadoc/org-openide-util-lookup/org/openide/util/lookup/InstanceContent.html">InstanceContent</a>
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
&quot;associateLookup...&quot;. 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&#8212;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>&#8212;everything.</p>
<p>
<img alt="controlling selection" src="../../images/tutorials/selection-1/first-run-69.png"/>
</p>
<h2><a name="multiple-objects"></a>Providing More Than One Object</h2>
<p>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&#8212;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>
<p>So the pattern in practice is to provide more than just a single object from
your component's <code>Lookup</code>&#8212;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:</p>
<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&#8212;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>&#8212;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>
<div class="feedback-box"><a href="https://netbeans.org/about/contact_form.html?to=3&amp;subject=Feedback:%20Selection%20Tutorial%20Part%201%20">Send Us Your Feedback</a></div>
<h2><a name="next-steps"></a>Next Steps</h2>
<p>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.</p>
</body>
</html>