| <!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 Platform Porting 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="gwielenga@netbeans.org"> |
| <meta name="indexed" content="y"> |
| <meta name="description" |
| content="A short guide to porting a Swing application to the NetBeans PLatform."> |
| </head> |
| <body> |
| <h1>NetBeans Platform Porting Tutorial</h1> |
| |
| <p>This tutorial demonstrates how to port a simple Swing application to the NetBeans Platform. |
| Though the sample is simple, the basic concepts of "porting" an application to the NetBeans |
| Platform will become clear. At the end, some general principles will be identified, based on the |
| steps taken in the tutorial. Hopefully, they will be useful to you when porting your own |
| Swing applications to the NetBeans Platform. |
| <p><strong class="notes">Note: </strong>This tutorial is out of date! The latest version of this tutorial |
| <a href="https://platform.netbeans.org/tutorials/nbm-porting-basic.html">is found here</a>.</p> |
| |
| <p><b>Contents</b></p> |
| |
| <img src="../images/articles/68/netbeans-stamp-65-67-68.gif" 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 Porting</a></li> |
| <li><a href="#getting">Getting the Anagram Game</a></li> |
| <li><a href="#compliance">Levels of Compliance</a></li> |
| <ul> |
| <li><a href="#creating">Creating the Module Project Source Structure</a> |
| <li><a href="#porting0">Porting Level 0: Launchable</a> |
| <li><a href="#porting1">Porting Level 1: Integration</a> |
| <li><a href="#porting3">Porting Level 2: Use Case Support</a> |
| <li><a href="#porting4">Porting Level 3: Aligned</a> |
| </ul> |
| </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 6.5 or above</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> |
| <tr> |
| <td class="tbltd1"><a href="http://plugins.netbeans.org/PluginPortal/faces/PluginDetailPage.jsp?pluginid=2753">Download the Sample</a></td> |
| <td class="tbltd1"></td> |
| </tr> |
| </tbody> |
| </table> |
| |
| <h2 class="tutorial"><a name="intro"></a>Introduction to Porting</h2> |
| |
| <p>Before beginning this procedure, it makes sense to ask why one would want to do so in the |
| first place. A typical Swing application consists of a user interface layer on top of a |
| general framework. The general framework normally provides features dealing with an |
| application's infrastructure, such |
| as an application's menu bar, windowing system (also known as "docking system"), and |
| lifecycle management. Typically this framework is reused by many applications within the |
| same company. The NetBeans Platform exists specifically to cater to these infrastructural |
| concerns. You do not need to create these on your own for your own Swing applications. You |
| can simply move the useful parts of your own application to the NetBeans Platform and then, |
| from that point onwards, the NetBeans Platform will be the new underlying 'plumbing' layer |
| of your application. You can then focus on the more interesting parts of your application, |
| specifically, the user interface. This will speed up your development process and give you |
| a consistent basis for all your applications. |
| |
| <p>In this tutorial, we will begin with the Anagram Game, which is a standard Swing application |
| sample that is distributed with NetBeans IDE. We will, step by step, move it to the NetBeans |
| Platform and then see the advantages and disadvantages of doing so. |
| |
| <!-- ===================================================================================== --> |
| |
| <h2 class="tutorial"><a name="getting"></a>Getting the Anagram Game</h2> |
| |
| <p>We begin by getting the Anagram Game, which is one of the IDE's standard Java samples, |
| from the New Project wizard. Then we run it and analyze its parts. |
| |
| <ol> |
| <li>Choose File > New Project (Ctrl-Shift-N). Under Categories, select Samples > Java. Under Projects, |
| select Anagram Game. Click Next and Finish. |
| <p><p>You should now see the Anagram Game application outlined in the Projects window, as shown here: |
| |
| <p><p><img border="1" src="../images/tutorials/porting/ag0.png"/> |
| |
| <p>The application contains the following classes: |
| |
| <ul> |
| <li><b><tt>WordLibrary.java</tt></b>. Provides a list of scrambled words, as well |
| as their unscrambled equivalents, together with getters and setters for accessing them.</li> |
| <li><b><tt>About.java</tt></b>. Provides the About box, accessed from the File menu.</li> |
| <li><b><tt>Anagrams.java</tt></b>. Provides the main user interface of the application, |
| principally consisting of a <tt>JPanel</tt> containing labels and text fields. Also included |
| is a menu bar containing a File menu, with the menu items 'About' and 'Exit'.</li> |
| </ul> |
| |
| <p><li>Run the application and you should see the following: |
| |
| <p><p><img border="1" src="../images/tutorials/porting/ag1.png"/> |
| |
| <p><li>When you specify the correctly unscrambled word, you will see this: |
| |
| <p><p><img border="1" src="../images/tutorials/porting/ag2.png"/> |
| |
| </ol> |
| <p>Before porting this application to the NetBeans Platform, we need to think |
| about <i>the stages in which we want to port our application</i>. In other |
| words, you do not need to port everything at once. And there are different |
| levels to which you can integrate your application, from a mostly superfical |
| level to a level that aligns your application completely with the paradigms |
| and purposes of the NetBeans Platform. The next section will show the levels |
| of compliance your application can have with the NetBeans Platform. |
| <br /> |
| |
| <!-- ===================================================================================== --> |
| <h2 class="tutorial"><a name="compliance"></a>Levels of Compliance</h2> |
| |
| <p>Converting an application to be fit for a framework such as the NetBeans Platform can be done |
| on various levels. The integration can be shallow and use just a few integration points or |
| it can be deeper, tightly following the paradigms of the NetBeans Platform. |
| |
| <p>The stages can be described as follows: |
| |
| |
| <h4 id="section-LevelsOfCompliance-Level0Launchable">Level 0: Launchable</h4> |
| <p>One or more of the following can be done to make your application launchable with as |
| few changes as possible: |
| <ul> |
| <li>Enhance your manifest with NetBeans key/value pairs so that your JAR is recognized as a |
| NetBeans module. |
| <li>Use dependencies between modules. With instances of plain Class-Path you can set dependencies |
| between modules. |
| <li>Register a menu item. Simply register a menu item in the declarative |
| layer file (<tt>layer.xml</tt>) of your module, to |
| invoke your original application. This file is created automatically when you create a new |
| module project in NetBeans IDE, as you will see later. |
| </ul> |
| <p>In this tutorial, we will do all of the above. We will enhance the manifest, which the module |
| project wizard will do for us. We will create a menu item that will |
| invoke our application. To do so, we will move our application's classes into a module source |
| structure. Then we will use the New Action wizard to create a new menu item, which will |
| automatically be registered for us, and a dependency will be set on the Utilities API, which |
| we will need to implement the Action. From that action, we will invoke our application. |
| |
| <h4 id="section-LevelsOfCompliance-Level1Integrated">Level 1: Integrated</h4> |
| <p>Here are some pointers for integrating the application more |
| tightly with the NetBeans Platform: |
| <ul> |
| <li>Integrate visually to get the benefits of the NetBeans Window System, which is |
| also known as a docking framework. |
| <li>Use NetBeans Dialog APIs, primarily the <tt>TopComponent</tt> class and the |
| <tt>DialogDisplayer</tt> class. |
| <li>Change initialization code of your application, use the <tt>ModuleInstall</tt> |
| class or declarative registrations, through the layer file or the META-INF/services folder. |
| </ul> |
| <p>In this tutorial, we will first create a <tt>JPanel</tt> and move the relevant <tt>JPanel</tt> |
| from the <tt>JFrame</tt> to the new <tt>JPanel</tt>. We will use a <tt>DialogDisplayer</tt>, from |
| within our action, to call the <tt>JPanel</tt>. The <tt>DialogDisplayer</tt> integrates nicely with |
| the NetBeans Platform, unlike a <tt>JFrame</tt> or a <tt>JPanel</tt>. For example, the |
| <tt>DialogDisplayer</tt> is modal and it works well with the IDE's integrated JavaHelp. |
| Next, we will move the <tt>JPanel</tt> to a <tt>TopComponent</tt>. The <tt>TopComponent</tt> class |
| creates a window on the NetBeans Platform, which in our case will show our <tt>JPanel</tt>. |
| |
| |
| <h4 id="section-LevelsOfCompliance-Level2UseCaseSupport">Level 2: Use Case Support</h4> |
| <p>This level of compliance with the NetBeans Platform is concerned with one or more of the following activities: |
| <ul> |
| <li>Bind your application to other modules by inspecting existing functionality and trying to use it. |
| <li>Simplify the workflow to fit into the NetBeans Platform paradigms. |
| <li>Listen to the global selection to discover what other modules are doing and update your state accordingly. |
| </ul> |
| <p>In this tutorial, we will listen for the existence of <tt>EditorCookies</tt>, using the following statement: |
| |
| <pre>EditorCookie ec = activatedNodes[0].getLookup().lookup(EditorCookie.class);</pre> |
| <p>A cookie is a <i>capability</i> and cookies are a powerful feature of NetBeans. |
| With a Java interface, your object's capabilities are fixed at compile time, while NetBeans cookies |
| allow your object to behave dynamically because your object can expose capabilities, or |
| not, based on its state. An <tt>EditorCookie</tt> |
| defines an editor, with interfaces for common activities such as opening a document, closing the editor, |
| background loading of files, document saving, and modification notifications. We will listen for the |
| existence of such a cookie and then we will pass the content of the editor to the <tt>TopComponent</tt>, in the |
| form of words. By doing this, we are doing what the first item above outlines, i.e., inspecting existing |
| functionality and reusing it within the context of our ported application. This is a modest level of integration. |
| However, it pays off because you are reusing functionality provided by the NetBeans Platform. |
| |
| <h4 id="section-LevelsOfCompliance-Level3Aligned">Level 3: Aligned</h4> |
| <p>In this final stage of your porting activity, you are concerned with the following thoughts, first and foremost: |
| <ul> |
| <li>Become a good citizen of the NetBeans Platform, by exposing your own state to other modules so that they know what you are doing. |
| <li>Eliminate duplicated functionality, by |
| reusing the Navigator, Favorites window, Task List, Progress API, etc., instead of creating or maintaining your own. |
| <li>Cooperate with other modules and adapt your application to the NetBeans Platform way of doing things. |
| </ul> |
| <p>Towards the end of this tutorial, we will adopt this level of compliance by letting our <tt>TopComponent</tt> |
| expose a <tt>SaveCookie</tt> when changes are made to the "Guessed Word" text field. By doing this, we will |
| enable the Save menu item under the Tools menu. This kind of integration brings the full benefits of the |
| NetBeans Platform, however it also requires some effort to attain. |
| |
| |
| |
| <!-- ===================================================================================== --> |
| |
| <h2 class="tutorial"><a name="creating"></a>Creating the Module Project Source Structure</h2> |
| |
| <p>First, let's create the source structure of our module. We use a wizard to do so. This |
| is the typical first step of creating a new module for a NetBeans Platform application. |
| |
| <ol> |
| <li>Choose File > New Project (Ctrl-Shift-N). Under Categories, select NetBeans Modules. Under Projects, |
| select Module, as shown below:</li> |
| |
| <p><p><img border="1" src="../images/tutorials/porting/agp1.png"/> |
| |
| <p>Click Next. |
| |
| <p><li>Type <tt>AnagramGamePlugin</tt> in Project Name and choose somewhere |
| to store the module, as shown below: |
| |
| <p><p><img border="1" src="../images/tutorials/porting/agp2.png"/> |
| |
| <p>Click Next. |
| |
| <p><li>Type a unique name in the Code Name Base field, |
| which provides the unique identifier for your module. |
| It could be anything, but here it is <tt>org.netbeans.modules.anagramgameplugin</tt>.</p> |
| <p>Also make sure to fill in the XML Layer field, which specifies the location |
| and name of the module's configuration file. By default, the field is empty |
| because not all NetBeans modules need a configuration file. In this case, you will |
| need one, so fill out the field as shown below: |
| |
| <p><p><img border="1" src="../images/tutorials/porting/agp3.png"/> |
| |
| <p><li>Click Finish. |
| |
| <p><p><p>Below the Anagram Game sample, you should now see the source structure of your |
| new module, as shown here: |
| |
| <p><p><img border="1" src="../images/tutorials/porting/agp4.png"/> |
| |
| |
| </ol><p>Above, we can see that we now have the original application, |
| together with the module to which it |
| will be ported. In the next sections, we will begin porting the application to |
| the module, using the porting levels described earlier. |
| <br /> |
| |
| <!-- ===================================================================================== --> |
| <h2 class="tutorial"><a name="porting0"></a>Porting Level 0: Launchable</h2> |
| |
| |
| <p>At this stage, we simply want to be able to launch our application from |
| a module. Here we will create a menu item that invokes the application. We begin |
| by copying the application's sources into the module source structure.</p> |
| <ol> |
| <li>Copy the two packages from the Anagram Game into the module. Below, |
| the new packages and classes in the module are highlighted:</li> |
| <p><p><img border="1" src="../images/tutorials/porting/agport0.png"/> |
| |
| <li>Use the New Action Wizard to create a new menu item. To do |
| so, right-click the module project, choose New > Other and |
| then Module Development > Action. Click Next and then |
| specify that you want a menu item that will always be enabled. |
| |
| <li>Click Next and specify the location of the menu item that |
| will invoke the application. Here, the Window menu |
| is chosen, but it could be any menu at all and it could be between |
| any menu item within the menu: |
| <p><p><img border="1" src="../images/tutorials/porting/agport2.png"/> |
| <p><b>Note:</b> The screenshot of the wizard step above is truncated to save space |
| in this tutorial. |
| <li>In the final step of the wizard, specify a name, display |
| name, and package for the action. These could, again, be anything, |
| but the screenshot below shows the values used in this tutorial: |
| <p><p><img border="1" src="../images/tutorials/porting/agport3.png"/> |
| |
| <li>Click Finish and the new Java class is added, together with new registration |
| entries in the configuration file, <tt>layer.xml</tt>. |
| <li>Next, modify the action, so that we can |
| invoke the <tt>JFrame</tt> from the menu item. To do so, copy the |
| content of the <tt>main</tt> method from the <tt>Anagrams</tt> |
| class to the <tt>performAction</tt> in the action: |
| |
| <p><pre>public void performAction() { |
| new Anagrams().setVisible(true); |
| }</pre> |
| |
| </li> |
| |
| <p><li>Right-click the module in the Projects |
| window and choose Install/Reload in Development IDE |
| or Install/Reload in Target Platform. Depending |
| on which of these you choose, the module will |
| either be installed in the current IDE or in a |
| new instance of the IDE that automatically starts |
| up. |
| |
| <li>Under the Window menu, or whichever menu you specified earlier, |
| you should find the menu item |
| that you created. Choose it and your application appears. |
| |
| |
| </ol> |
| |
| <p>The application is displayed, but note that it is not |
| well integrated with the NetBeans Platform. For example, |
| it is not modal. In the next section, we will integrate |
| it more tightly with the NetBeans Platform. |
| |
| <br /> |
| |
| <!-- ===================================================================================== --> |
| |
| <h2 class="tutorial"><a name="porting1"></a>Porting Level 1: Integration</h2> |
| |
| <p>In this section, we integrate the application |
| more tightly by creating a new window, so that we have a user |
| interface, i.e., |
| a window, to which we can move those contents of |
| the <tt>JFrame</tt> that are useful to our new application. |
| |
| <ol> |
| <li>Right-click the AnagramGamePlugin in the Projects window and then |
| choose New > Other. Under Categories, select Module Development. Under File Types, |
| select Window Component. Click Next. |
| |
| <p><li>Choose the position where you would |
| like the window to appear. For purposes of this |
| tutorial choose "editor", which will place the |
| Anagram game in the main part of the application. |
| Optionally, specify whether the window should |
| open automatically when the application starts up. |
| Click Next. |
| |
| <p><li>Type <tt>Anagram</tt> in Class Name |
| Prefix and select <tt>org.netbeans.modules.anagramgameplugin</tt> |
| in Package, as shown here: |
| |
| <p><p><img border="1" src="../images/tutorials/porting/agf2.png"/> |
| |
| <p>Above, notice that the IDE shows the files it |
| will create and modify. |
| |
| <p><li>Click Finish. |
| <p><p>Now you have a set of new Java and XML source |
| files, as shown here: |
| |
| <p><p><img border="1" src="../images/tutorials/porting/agf3.png"/> |
| |
| <p><li>Open the <tt>Anagrams</tt> class in |
| the Anagram sample, as well as the |
| <tt>AnagramTopComponent</tt>, which was created |
| in the previous step. |
| <p><p>When you click the mouse in the |
| Anagram class, notice that the labels and |
| text fields are in a Swing container, in |
| this case a <tt>JPanel</tt>, as shown here: |
| |
| <p><p><img border="1" src="../images/tutorials/porting/agf4.png"/> |
| |
| <p><b>Tip:</b> If the Swing components were not within a container, |
| you could select them all with the mouse, then right-click and |
| choose Enclose In, to let the IDE create a container within which |
| all the selected components would be enclosed. |
| |
| <p><li>Right-click the <tt>JPanel</tt> and copy it. |
| Paste it in the TopComponent and you should see |
| the old user interface in your new plugin: |
| |
| <p><p><img border="1" src="../images/tutorials/porting/agf5.png"/> |
| |
| <p><li>Only one thing needs to be transferred, at |
| this point: the variable declared in the original |
| <tt>JFrame</tt>. Open the <tt>JFrame</tt> and copy |
| the variable: |
| |
| <p><p><img border="1" src="../images/tutorials/porting/agf6.png"/> |
| |
| <p>Paste it into the source view of the |
| TopComponent: |
| |
| <p><p><img border="1" src="../images/tutorials/porting/agf7.png"/> |
| |
| <p><li>Right-click the module in the Projects |
| window and choose Install/Reload in Development IDE |
| or Install/Reload in Target Platform. Depending |
| on which of these you choose the module will |
| either be installed in the current IDE or in a |
| new instance of the IDE that automatically starts |
| up. |
| |
| <p><p>You should now see the Anagram Game window, which |
| you defined in this section. You will also find |
| a new menu item that opens the window, under the |
| Window menu.</p> |
| |
| <p>Also notice that the game works as before. You |
| need to click New Word once, to have the module |
| call up a new word, and then you can use it as |
| before:</p> |
| <p><p><img border="1" src="../images/tutorials/porting/agf8.png"/> |
| |
| </ol><p> |
| <br /> |
| |
| |
| |
| <!-- ===================================================================================== --> |
| <h2 class="tutorial"><a name="porting3"></a>Porting Level 2: Use Case Support</h2> |
| |
| |
| <p>In this section, we are concerned with listening to the global selection and making use |
| of data we find there.The global selection, also called the default lookup, is the registry for |
| global singletons and instances of objects which have been registered in the system by modules. |
| Here we query the lookup for <tt>EditorCookies</tt> and make use of the <tt>EditorCookie</tt>'s |
| document to fill the string array that defines the scrambled words displayed in the <tt>TopComponent</tt>. </p> |
| <ol> |
| <li>We begin by tweaking the <tt>WordLibrary</tt> class. We do this so that |
| we can set its list of words externally. The sample provides a hardcoded list, |
| but we want to be able to set that list ourselves, via an external action. Therefore, |
| add this method to <tt>WordLibrary</tt>: |
| |
| <p><pre>public static void setScrambledWordList(String[] inScrambledWordList) { |
| SCRAMBLED_WORD_LIST = inScrambledWordList; |
| }</pre> |
| |
| <p>Next, we will create an action that will obtain the content of a Java class, |
| break the content down into words, and fill the <tt>SCRAMBLED_WORD_LIST</tt> string array |
| with these words. |
| |
| </li> |
| |
| |
| <li>Right-click the project node in the Projects window, choose New > Action, |
| and then choose "Conditionally Enabled" in the first panel of the New Action wizard. |
| Select "EditorCookie" in the Cookie Classes drop-down list, because we want the action |
| to be sensitive to EditorCookies. This means that |
| the action will act on this type of Cookie and only be enabled when |
| one is available from the current selection. Click Next. |
| |
| <li>Unselect "Global Menu Item". Select "Editor Context Menu Item". In the "Content Type" |
| drop-down list, choose "text/x-java". Click Next, give the action a prefix and display name, |
| then click Finish. |
| |
| <li>In the Source Editor, redefine the <tt>performAction</tt> as follows: |
| |
| <p><pre>protected void performAction(Node[] activatedNodes) { |
| try { |
| |
| <b>//Look up the EditorCookie for the current node:</b> |
| EditorCookie ec = activatedNodes[0].getLookup().lookup(EditorCookie.class); |
| |
| <b>//Get the EditorCookie's document:</b> |
| StyledDocument doc = ec.getDocument(); |
| |
| <b>//Get the complete textual content:</b> |
| String all = doc.getText(0, doc.getLength()); |
| |
| <b>//Make words from the content:</b> |
| String[] tokens = all.split(" "); |
| |
| <b>//Pass the words to the WordLibrary class:</b> |
| WordLibrary.setScrambledWordList(tokens); |
| |
| <b>//Open the TopComponent:</b> |
| TopComponent win = AnagramTopComponent.findInstance(); |
| win.open(); |
| win.requestActive(); |
| |
| } catch (BadLocationException ex) { |
| Exceptions.printStackTrace(ex); |
| } |
| }</pre> |
| |
| <li>Install the module, right-click in the Java editor, and choose the new menu item |
| you see there. The TopComponent opens. When you click to the next word, the words |
| from the Java class appear in the Scrambled Word text field. |
| |
| </ol> |
| |
| <p>The result of this exercise is that you now see the content of the Java class in the Scrambled Word text field. |
| Of course, these words are not really scrambled and you cannot really unscramble them. However, your module is |
| making use of the content of files supported by a different module altogether, and it is updating its state |
| accordingly. |
| |
| <br /> |
| |
| |
| <!-- ===================================================================================== --> |
| <h2 class="tutorial"><a name="porting4"></a>Porting Level 3: Aligned</h2> |
| |
| |
| <p>In this section, we are concerned with becoming a "good citizen" of the |
| NetBeans Platform. We are going to expose the state of the TopComponent to |
| the other modules, so that we can cooperate with them. As an example of this, we |
| will modify the TopComponent to offer a <tt>SaveCookie</tt>, which gives |
| the user a way to store the text typed in the text field. By offering the |
| <tt>SaveCookie</tt> when changes are made in the text field, the Save button |
| and the Save menu item under the File menu will become enabled. When the user |
| selects the button or menu item, a dialog will be displayed and the button |
| and menu item will become disabled, until the next time that a change is made |
| to the text field.</p> |
| <ol> |
| |
| <li>Begin by setting a dependency on the Dialogs API, by right-clicking |
| the project node in the Projects window, choosing Properties, and |
| then adding the Dialogs API and the Nodes API in the Libraries panel. |
| |
| <li>Next, we need to create a node within our <tt>TopComponent</tt>. We need to do so because |
| we need the <tt>TopComponent</tt> to expose a <tt>SaveCookie</tt>, i.e., the capability to save. |
| Such capabilities apply to nodes and therefore we need to create a node, add an implementation |
| of <tt>SaveCookie</tt> to its set of cookies, and then set the node as |
| the <tt>TopComponent's</tt> activated node. Here is the node, with comments inline to explain |
| what it is for: |
| |
| <p><pre>private class DummyNode extends AbstractNode { |
| |
| SaveCookieImpl impl; |
| |
| public DummyNode() { |
| super(Children.LEAF); |
| impl = new SaveCookieImpl(); |
| } |
| |
| <b>//We will call this method, i.e., dummyNode.fire(), |
| //from a document listener set on the text field:</b> |
| public void fire(boolean modified) { |
| if (modified) { |
| <b>//If the text is modified, |
| //we add the SaveCookie implementation to the cookieset:</b> |
| getCookieSet().assign(SaveCookie.class, impl); |
| } else { |
| <b>//Otherwise, we make no assignment |
| //and the SaveCookie is not made available:</b> |
| getCookieSet().assign(SaveCookie.class); |
| } |
| } |
| |
| private class SaveCookieImpl implements SaveCookie { |
| |
| public void save() throws IOException { |
| |
| Confirmation msg = new NotifyDescriptor.Confirmation("Do you want to save \"" + |
| guessedWord.getText() + "\"?", NotifyDescriptor.OK_CANCEL_OPTION, |
| NotifyDescriptor.QUESTION_MESSAGE); |
| |
| Object result = DialogDisplayer.getDefault().notify(msg); |
| |
| <b>//When user clicks "Yes", indicating they really want to save, |
| //we need to disable the Save button and Save menu item, |
| //so that it will only be usable when the next change is made |
| //to the text field:</b> |
| if (NotifyDescriptor.YES_OPTION.equals(result)) { |
| fire(false); |
| <b>//Implement your save functionality here.</b> |
| } |
| |
| } |
| } |
| }</pre> |
| |
| </li> |
| |
| <p>Declare the <tt>DummyNode</tt> at the top of the class: |
| |
| <p><pre>private DummyNode dummyNode;</pre> |
| |
| <p><li>Next, in the TopComponent's constructor, we set the activated node: |
| |
| <p><pre>setActivatedNodes(new Node[]{dummyNode = new DummyNode()});</pre> |
| |
| <li>Finally, we listen for change on the text field's document, |
| and fire change events whenever the text field's document changes, by |
| adding this document listener code to the end of the <tt>TopComponent</tt>'s |
| constructor: |
| |
| <p><pre>guessedWord.getDocument().addDocumentListener(new DocumentListener() { |
| |
| public void insertUpdate(DocumentEvent arg0) { |
| dummyNode.fire(true); |
| } |
| |
| public void removeUpdate(DocumentEvent arg0) { |
| dummyNode.fire(true); |
| } |
| |
| public void changedUpdate(DocumentEvent arg0) { |
| dummyNode.fire(true); |
| } |
| |
| });</pre> |
| |
| <li>By default, you have a Save menu item under the File menu, but no Save button |
| in the toolbar. For testing purposes, you may want a Save button in the toolbar. |
| For this purpose, add the following to the layer: |
| |
| <p><pre><folder name="Toolbars"> |
| <folder name="File"> |
| <file name="org-openide-actions-SaveAction.instance"/> |
| </folder> |
| </folder></pre> |
| |
| <li>Install the module, make a change in the "Guessed Word" text field, and notice that the Save button |
| and the Save menu item become enabled. Select either the button or the menu item, click Yes in the |
| dialog, and notice that the Save functionality is disabled. |
| |
| |
| </ol> |
| <br /> |
| |
| <p>Congratulations! Now that your module is making use of existing functionality, you have taken one step |
| in successfully aligning it with the NetBeans Platform. Other modules can be now be |
| plugged into the NetBeans Platform to take advantage of, or even extend, features added by your |
| module. Hence, not only can your module benefit from what the NetBeans Platform provides, |
| but you've created something that other modules can use as well. |
| |
| <p>You need to continue finding ways |
| to further align your original application with the functionality offered by the NetBeans Platform, |
| in order to make it even more of a good "good citizen" and useful member of the community of modules |
| within the application. |
| |
| <p>For example, you can write a new node, with child nodes for each |
| word defined in the class: |
| |
| <pre>public class WordListNode extends AbstractNode { |
| |
| private int index; |
| private final WordLibrary wordLibrary; |
| |
| public WordListNode() { |
| this(WordLibrary.getDefault()); |
| } |
| |
| private WordListNode(WordLibrary w) { |
| super(new WordListChildren(w)); |
| wordLibrary = w; |
| } |
| |
| WordListNode(int index, WordLibrary w) { |
| super(Children.LEAF); |
| this.index = index; |
| this.wordLibrary = w; |
| |
| setName("Index: " + index); |
| setDisplayName(wordLibrary.getWord(index)); |
| } |
| |
| @Override |
| public String getHtmlDisplayName() { |
| return "<b>" + wordLibrary.getWord(index) + "</b> (<i>" + wordLibrary.getScrambledWord(index) + "</i>)"; |
| } |
| |
| private static class WordListChildren extends Children.Keys<Integer> { |
| private final WordLibrary wordLibrary; |
| |
| public WordListChildren(WordLibrary wordLibrary) { |
| this.wordLibrary = wordLibrary; |
| } |
| |
| @Override |
| protected void addNotify() { |
| List<Integer> arr = new ArrayList<Integer>(); |
| for (int i = 0; i < wordLibrary.getSize(); i++) { |
| arr.add(i); |
| } |
| setKeys(arr); |
| } |
| |
| @Override |
| protected void removeNotify() { |
| setKeys(Collections.<Integer>emptyList()); |
| } |
| |
| @Override |
| protected Node[] createNodes(Integer index) { |
| WordListNode node = new WordListNode(index, wordLibrary); |
| return new Node[] { node }; |
| } |
| } |
| |
| }</pre> |
| |
| <p>In return, the lifecycle of the original application is now handled by the NetBeans Platform |
| and you can leverage as much of the existing modules' functionality as is reasonable for your module. In fact, |
| your original application is now no longer an application, but an integral part of a larger application. |
| |
| <!-- ===================================================================================== --> |
| |
| <h2 class="tutorial"><a name="next-steps"></a>Next Steps</h2> |
| |
| <p>There are several next steps one can take at this point, aside from further aligning |
| the module with the NetBeans Platform, as outlined above: |
| |
| <ul> |
| <li><b>Become aware of the differences between standard Swing applications |
| and the NetBeans Platform.</b> For the most part, the standard Swing approach to |
| creating a user interface will continue to work for your NetBeans Platform |
| application. However, the NetBeans Platform approach is better, easier, or both |
| in some cases. One example is that of the NetBeans Dialogs API. The standard Swing approach, |
| via, for example, the <tt>JOptionsPane</tt>, works OK, but using the NetBeans Dialogs API is easier, |
| because it automatically centers your dialog in the application and allows you to dismiss it |
| with the ESC key. Using the Dialogs API also lets you plug in a different DialogDisplayer, |
| which can make it easier to customize or test your application. |
| |
| <p>Below is a list of the principle differences between the typical Swing approach |
| and that of the NetBeans Platform: |
| |
| <ul> |
| <li>Loading of images |
| <li>Loading of resource bundles and localized string |
| <li>Assigning of mnemonics to labels and buttons |
| <li>Showing dialogs |
| </ul> |
| <p>For details on all of the above items, read |
| this FAQ: <a href="http://wiki.netbeans.org/wiki/view/DevFaqNbIdeosyncracies">Common calls that should be done slightly differently in NetBeans than standard Swing apps (loading images, localized strings, showing dialogs)</a>. |
| |
| <p>In addition, note that, since the NetBeans Platform now handles the lifecycle of your module, since it is |
| now part of the whole application, you can no longer use <tt>System.exit</tt>. Instead, you need to use <tt>LifecycleManager</tt>. To |
| run code on start up, which should only be done when absolutely necessary, you need to use the NetBeans <tt>ModuleInstall</tt> class and, |
| specifically, its <tt>restored</tt> method. A useful reference in this context is <a href="http://www.ociweb.com/jnb/jnbOct2005.html#porting">Porting a Java Swing Application to the NetBeans Platform</a>, |
| by Tom Wheeler, in <a href="http://www.ociweb.com/jnb/jnbOct2005.html#porting">Building A Complete NetBeans Platform Application</a>. |
| |
| <li><b>Create a module project for each distinct part of your application.</b> The |
| NetBeans Platform provides a modular architecture out of the box. Break your |
| application into one or more modules. Doing so requires some analysis of your |
| original application and an assessment of which parts could best fit within |
| a new module and how to communicate between them. Since the example in this |
| tutorial was simple, we only needed one module. A next step might be to put the |
| <tt>WordLibrary</tt> class in a separate module and expose it as a public API. |
| Doing so would let other modules |
| provide user interfaces on top of the API provided by the first module, as shown here: |
| <p><p><img border="1" src="../images/tutorials/porting/agf9.png"/> |
| <p>As shown above, you need to put the modules in a module suite. Then set |
| a dependency in the plugin module on the API module, using the Libraries |
| panel in the plugin module's Project Properties dialog box. The size of each module, i.e., |
| when one should create a new module or continue developing within an existing one, is a |
| question of debate. Smaller is better, in general. |
| |
| <li><b>Always keep reevaluating what you really need to port.</b> Look at the NetBeans Platform and |
| decide where there is overlap with your own application. Where there is overlap, |
| such as the menu bar and About box, decide what you want to do. Typically, you |
| want to leverage as much as possible from the NetBeans Platform. Therefore, you |
| would port as little as possible from your own application, while keeping as much |
| of it as is useful to you. |
| |
| <li><b>Move distinct parts of your user interface to one or more TopComponents.</b> On the NetBeans |
| Platform, the <tt>TopComponent</tt> class provides the top level Swing container. In effect, |
| it is a window. Move the user interface from your original application to one or |
| more of these windows and discard your original <tt>JFrame</tt>s. |
| <li><b>Copy the Java classes that do not provide user interface elements.</b> We simply |
| copied the original <tt>WordLibrary.java</tt> class. You can do the same with the model |
| of your own Swing applications. You might need to tweak some code to smoothen the transition |
| between the old Swing application and the new NetBeans Platform application, but (as |
| in the case shown in this tutorial) this might not even be necessary. |
| |
| </ul> |
| |
| <br /> |
| |
| </div> |
| |
| |
| <br> |
| <div class="feedback-box"><a href="https://netbeans.org/about/contact_form.html?to=3&subject=Feedback:%20Swing%20Porting%20Tutorial">Send Us Your Feedback</a></div> |
| <br style="clear:both;" /> |
| <!-- ======================================================================================== --> |
| |
| <h2><a name="nextsteps"></a>Next Steps</h2> |
| |
| <p>For more information about creating and developing NetBeans modules, see the following resources: |
| <ul> |
| <p><li><a href="https://netbeans.org/kb/trails/platform.html">Other Related Tutorials</a></li> |
| <p><li><a href="https://netbeans.org/download/dev/javadoc/">NetBeans API Javadoc</a></li> |
| </ul> |
| |
| <hr> |
| <!-- ======================================================================================== --> |
| |
| |
| </body> |
| </html> |