| <!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>Selection Management Tutorial for NetBeans Platform</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, 2011, 2012, 2013, 2014 Oracle and/or its affiliates. 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> |
| |
| <p><strong class="notes">Note: </strong>This document uses NetBeans Platform 8.0 and |
| NetBeans IDE 8.0. If you |
| are using an earlier version, see <a href="74/nbm-selection-1.html">the previous version |
| of this document</a>.</p> |
| |
| <p><b>Contents</b></p> |
| |
| <p><img src="../images/articles/80/netbeans-stamp.png" class="stamp" width="114" height="114" alt="Content on this page applies to NetBeans IDE 8.0" title="Content on this page applies to NetBeans IDE 8.0"/></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="#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="#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 8.0 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 7 or above</td> |
| </tr> |
| </tbody> |
| </table> |
| |
| <p class="tips">For troubleshooting purposes, you are welcome to download the <a href="http://java.net/projects/nb-api-samples/sources/api-samples/show/versions/8.0/tutorials/selection-management/1-of-4/EventManager">completed tutorial source code</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 <tt>Lookup</tt> |
| portion of selection—doing more advanced things will be covered in a later |
| tutorial in this four part series.</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>—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 NetBeans Platform Application Project</h2> |
| |
| <p>The loosely-coupled modular example used in this tutorial will contain three modules constituting a NetBeans Platform application, |
| as illustrated below:</p> |
| <p> |
| <img src="../images/tutorials/selection-1/73/result-1.png" alt="projects window showing setup" /> |
| </p> |
| |
| <p>Start by |
| creating the NetBeans Platform application that |
| will contain all three modules:</p> |
| |
| <div class="indent"> |
| |
| <ol> |
| <li><p>Choose File > New Project (Ctrl-Shift-N). Under Categories, select NetBeans Modules. Under Projects, |
| select NetBeans Platform Application:</p> |
| |
| <p> |
| <img src="../images/tutorials/selection-1/73/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>EventManager</tt> in Project Name. |
| Change the Project Location to any directory on your computer:</p> |
| <p> |
| <img src="../images/tutorials/selection-1/73/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/73/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 > New Project (Ctrl-Shift-N) again. |
| Under Categories, select NetBeans Modules. Under Projects, |
| select Module:</p> |
| |
| <p> |
| <img src="../images/tutorials/selection-1/73/new-mod-1.png" alt="new module, first panel" /> |
| </p> |
| |
| <p>Click Next.</p> |
| <p>(Alternatively, expand the "EventManager" |
| project node, right-click the "Modules" node, and then choose "Add New".)</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. |
| As you can see, the default in the wizard is to create the module within the |
| directory where you just created the "EventManager" 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/73/new-mod-2.png" alt="new module, first 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 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. </li> |
| |
| </ul> |
| |
| <p>The result should be as follows:</p> |
| |
| <p> |
| <img src="../images/tutorials/selection-1/73/new-mod-3.png" alt="new module, third panel" /> |
| </p> |
| |
| <p>Click Finish.</p></li> |
| |
| <li><p>You're going to create two more modules now—follow step 3, 4, and 5 |
| above, twice, using the names "MyEditor" (code name base <tt>org.myorg.myeditor</tt>) |
| and "MyViewer" (code name base <tt>org.myorg.myviewer</tt>).</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/73/result-1.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> |
| |
| </div> |
| |
| <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 a simple object named "Event", representing a random event, possibly an event such as a calendar event |
| or an event within a programmatic sequence. An Event has an index, providing a unique identifier, and a date.</p> |
| |
| <div class="indent"> |
| |
| <ol><li><p>Right click the <code>org.myorg.myapi</code> package and choose |
| New > Java Class. Name the class <code>Event</code> and click Finish. |
| Replace the default code with the following:</p> |
| <pre class="examplecode">package org.myorg.myapi; |
| |
| import java.util.Date; |
| |
| public final class Event { |
| |
| private final Date date = new Date(); |
| private static int count = 0; |
| private final int index; |
| |
| public Event() { |
| index = count++; |
| } |
| |
| public Date getDate() { |
| return date; |
| } |
| |
| public int getIndex() { |
| return index; |
| } |
| |
| @Override |
| 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>Event</code> is created, a counter |
| is incremented—so there will be some unique identifier to each instance |
| of <code>Event</code>. |
| </li> |
| <li><p>The next step is to have your API module export the <code>org.myorg.myapi</code> |
| package so other modules can see the Event class in it. By default, all |
| packages are hidden from all other modules in the application. |
| Right click the My API project |
| and choose Properties. In the API Versioning page |
| in the Project Properties dialog box, check the |
| checkbox for <code>org.myorg.myapi</code> in the |
| Public Packages list, as shown below:</p> |
| <p> |
| <img alt="api versioning tab" src="../images/tutorials/selection-1/73/export-package-1.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"> |
| <public-packages> |
| <package>org.myorg.myapi</package> |
| </public-packages></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> |
| <p>Now you need to set up some dependencies between your modules. The other two |
| modules, My Editor and My Viewer, will use the |
| <code>Event</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. |
| In the Libraries page of the Project |
| Properties dialog box of both My Editor and My Viewer, click the Add Dependency |
| button. In the dialog box that pops up, |
| type <code>Event</code>—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/73/export-package-2.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"><module-dependencies> |
| <dependency> |
| <code-name-base>org.myorg.myapi</code-name-base> |
| <build-prerequisite/> |
| <compile-dependency/> |
| <run-dependency> |
| <specification-version>1.0</specification-version> |
| </run-dependency> |
| </dependency> |
| </module-dependencies></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. In the Projects window, the new contract between |
| the modules is represented as follows:</p> |
| <p> |
| <img src="../images/tutorials/selection-1/73/result-2.png" alt="adding a dependency"/> |
| </p> |
| </li> |
| |
| </ol> |
| |
| </div> |
| |
| <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>Event</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> |
| |
| <div class="indent"> |
| |
| <h3><a name="generate-tc"></a>Generating a Singleton TopComponent</h3> |
| |
| <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 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.</p> |
| |
| <div class="indent"> |
| |
| <ol> |
| <li><p>Right click the <code>org.myorg.myviewer</code> package and choose |
| New > Other. In the resulting dialog, select Module Development > |
| Window, as shown below:</p> |
| <p> |
| <img src="../images/tutorials/selection-1/73/new-win-1.png" alt="adding a dependency"/> |
| </p> |
| <p>Click Next (or press Enter).</p></li> |
| <li><p>On the "Basic Settings" 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/73/new-win-2.png" alt="new window wizard" /> |
| </p> |
| </li> |
| <li><p>Click Next to continue to the "Name, Icon and Location" |
| page of the wizard. Set <code>MyViewer</code> as the class name prefix:</p> |
| <p> |
| <img src="../images/tutorials/selection-1/73/new-win-3.png" alt="new window wizard" /> |
| </p> |
| </li> |
| <li><p>Click Finish and you should see the following:</p> |
| <p> |
| <img src="../images/tutorials/selection-1/73/new-win-4.png" alt="new window wizard" /> |
| </p> |
| </li> |
| <li> |
| <p>You now have a skeleton <code>TopComponent</code>—a singleton component called |
| <code>MyViewerTopComponent</code>. Open the <code>MyViewerTopComponent</code> file and click its Source tab. |
| Via the annotations that you can see at the top |
| of the Java source file, <code>MyViewerTopComponent</code> will be registered in |
| the layer file of the MyViewer |
| module, together with an <code>Action</code> for opening the <code>MyViewerTopComponent</code> |
| from the Window menu:</p> |
| |
| <pre class="examplecode">@<a href="http://bits.netbeans.org/dev/javadoc/org-openide-windows/org/openide/windows/TopComponent.Description.html">TopComponent.Description</a>( |
| preferredID = "MyViewerTopComponent", |
| persistenceType = TopComponent.PERSISTENCE_ALWAYS) |
| @<a href="http://bits.netbeans.org/dev/javadoc/org-openide-windows/org/openide/windows/TopComponent.Registration.html">TopComponent.Registration</a>( |
| mode = "explorer", |
| openAtStartup = true) |
| @<a href="http://bits.netbeans.org/dev/javadoc/org-openide-awt/org/openide/awt/ActionID.html">ActionID</a>( |
| category = "Window", |
| id = "org.myorg.myviewer.MyViewerTopComponent") |
| @<a href="http://bits.netbeans.org/dev/javadoc/org-openide-awt/org/openide/awt/ActionReference.html">ActionReference</a>( |
| path = "Menu/Window" |
| /*, position = 333 */) |
| @<a href="http://bits.netbeans.org/dev/javadoc/org-openide-windows/org/openide/windows/TopComponent.OpenActionRegistration.html">TopComponent.OpenActionRegistration</a>( |
| displayName = "#CTL_MyViewerAction", |
| preferredID = "MyViewerTopComponent")</pre></li> |
| |
| </ol> |
| |
| |
| </div> |
| |
| <div class="indent"> |
| |
| <h3><a name="publish-tc"></a>Creating a Context Sensitive TopComponent</h3> |
| |
| <p>Open the <code>MyViewerTopComponent</code> file and click its Design tab—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>Event</code> if there is one.</p> |
| |
| <div class="indent"> |
| |
| <ol> |
| <li><p>Drag two JLabels to the form from the Palette (Ctrl-Shift-8), one |
| below the other, as shown below:</p> |
| <p> |
| <img alt="" src="../images/tutorials/selection-1/73/new-sel-1.png"/> |
| </p> |
| <p>Press F2 on the first label and |
| then change the text 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. 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> |
| <p>Right-click in the editor and choose Fix Imports, so that |
| <code>LookupListener</code> is imported. 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 <code>LookupListener</code>'s method to your class:</p> |
| <p> |
| <img alt="implement methods" src="../images/tutorials/selection-1/implement-methods.png"/> |
| </p> |
| |
| <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 <code>Lookup</code> |
| object, which 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 |
| listen to this one global selection <code>Lookup</code>, which will fire appropriate |
| changes whenever focus changes. </p> |
| </li> |
| |
| <li> |
| <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<Event> result = null; |
| |
| @Override |
| public void componentOpened() { |
| result = Utilities.actionsGlobalContext().lookupResult(Event.class); |
| result.addLookupListener (this); |
| } |
| |
| @Override |
| public void componentClosed() { |
| result.removeLookupListener(this); |
| } |
| |
| @Override |
| public void resultChanged(LookupEvent lookupEvent) { |
| Collection<? extends Event> allEvents = result.allInstances(); |
| if (!allEvents.isEmpty()) { |
| Event event = allEvents.iterator().next(); |
| jLabel1.setText(Integer.toString(event.getIndex())); |
| jLabel2.setText(event.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—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>Event</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 org.myorg.myapi.Event; |
| import org.netbeans.api.settings.ConvertAsProperties; |
| import org.openide.awt.ActionID; |
| import org.openide.awt.ActionReference; |
| import org.openide.util.*; |
| import org.openide.windows.TopComponent;</pre> |
| |
| </li> |
| |
| </ol> |
| |
| </div> |
| </div> |
| |
| <h2><a name="creating-the-editor-component"></a>Creating the Editor Component</h2> |
| |
| <p>Now you need something to actually provide instances of <code>Event</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>Event</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> |
| |
| <div class="indent"> |
| |
| <ol> |
| |
| <li>You will need to add four 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, as well the UI Utilities API, which provide |
| 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 > 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> and annotate the class to specify the |
| location of the window and the menu item for opening it: |
| <pre class="examplecode">@<a href="http://bits.netbeans.org/dev/javadoc/org-openide-windows/org/openide/windows/TopComponent.Description.html">TopComponent.Description</a>( |
| preferredID = "MyEditorTopComponent", |
| persistenceType = TopComponent.PERSISTENCE_ALWAYS) |
| @<a href="http://bits.netbeans.org/dev/javadoc/org-openide-windows/org/openide/windows/TopComponent.Registration.html">TopComponent.Registration</a>( |
| mode = "editor", |
| openAtStartup = true) |
| @<a href="http://bits.netbeans.org/dev/javadoc/org-openide-awt/org/openide/awt/ActionID.html">ActionID</a>( |
| category = "Window", |
| id = "org.myorg.myeditor.MyEditorTopComponent") |
| @<a href="http://bits.netbeans.org/dev/javadoc/org-openide-awt/org/openide/awt/ActionReference.html">ActionReference</a>( |
| path = "Menu/Window") |
| @<a href="http://bits.netbeans.org/dev/javadoc/org-openide-windows/org/openide/windows/TopComponent.OpenActionRegistration.html">TopComponent.OpenActionRegistration</a>( |
| displayName = "#CTL_MyEditorAction") |
| @<a href="http://bits.netbeans.org/dev/javadoc/org-openide-util/org/openide/util/NbBundle.Messages.html">NbBundle.Messages</a>({ |
| "CTL_MyEditorAction=MyEditor" |
| }) |
| |
| public class MyEditor extends TopComponent {</pre> |
| </li> |
| |
| <li>Add the following code to the constructor of <code>MyEditor</code>, after |
| the <tt>initComponents()</tt> statement: |
| <pre class="examplecode">Event obj = new Event(); |
| associateLookup(Lookups.singleton(obj)); |
| jTextField1.setText ("Event #" + obj.getIndex()); |
| jTextField2.setText ("Created: " + obj.getDate()); |
| setDisplayName ("MyEditor " + obj.getIndex());</pre></li> |
| <li><p>Right-click in the editor |
| and choose Fix Imports, which should result in the following |
| import section at the top of your class:</p> |
| <pre class="examplecode">import org.myorg.myapi.Event; |
| import org.openide.awt.ActionID; |
| import org.openide.awt.ActionReference; |
| import org.openide.util.lookup.Lookups; |
| import org.openide.windows.TopComponent;</pre> |
| </li> |
| |
| </ol> |
| </div> |
| </div> |
| |
| <p>The line |
| <code>associateLookup(Lookups.singleton(obj));</code> |
| will create a <code>Lookup</code> that contains only one object—the new |
| instance of <code>Event</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>Event</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 allows you to select or edit multiple |
| unique instances of <code>Event</code>—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>Event</code>, so you have |
| something to display.</p> |
| |
| <h2><a name="running"></a>Running the Code</h2> |
| |
| <p>Now you're ready to run the tutorial. Simply right click <code>EventManager</code>, |
| the application which contains your three modules, and choose Run from the popup |
| menu. When the IDE opens, simply choose Window > 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> |
| |
| <p> |
| <img alt="selection" src="../images/tutorials/selection-1/73/result-3.png"/> |
| </p> |
| |
| <p>If you click in the Viewer window, notice |
| that the text changes to "[No Selection]", as shown below:</p> |
| |
| <p> |
| <img alt="no selection" src="../images/tutorials/selection-1/73/result-4.png"/> |
| </p> |
| <p class="notes"> |
| <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—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—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>Event</code> from its Lookup.</p> |
| |
| <p>To really picture the value of this, imagine <code>Event</code> were something |
| much more complex; imagine that <code>MyEditor</code> is an image editor, and <code> |
| Event</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—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>Event</code> |
| it has with a new one on the fly.</p> |
| |
| <div class="indent"> |
| |
| <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> |
| <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 |
| "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) { |
| Event obj = new Event(); |
| jTextField1.setText ("Event #" + 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> |
| |
| </div> |
| |
| <p>You're now ready to run the Event Manager again. Right click EventManager 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 alt="controlling selection" src="../images/tutorials/selection-1/73/result-5.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—the |
| same way your viewer window listens for <code>Event</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>—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> |
| |
| <div class="feedback-box"><a href="https://netbeans.org/about/contact_form.html?to=3&subject=Feedback:%20Selection%208.0%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> |