The Source for Java Technology Collaboration
User: Password:



Patrick Keegan's Blog

Community: NetBeans Archives


Draft of Custom Desktop Database Tutorial

Posted by pkeegan on June 24, 2008 at 09:17 AM | Permalink | Comments (0)

I have published a draft of an extended tutorial on creating desktop Java applications on netbeans.org. The tutorial is based on my recent series of blog posts. Thanks to everybody who provided questions and suggestions! A lot of them have been incorporated into the tutorial. Others are on my to-do list and are not forgotten.

The main things that appear in the tutorial that were missing from the blog are currency and date rendering and more customizations of table columns.

The tutorial needs some more polishing, but I think it's in a reasonably useful state. Let me know what you think.

By the way, I'm going on vacation tomorrow, so excuse the upcoming silence. Talk to you in three weeks.



Using Beans Binding to Search in a Table

Posted by pkeegan on June 05, 2008 at 10:42 AM | Permalink | Comments (2)

Now and again someone will ask me how you can search records in a desktop database app. Here's a reasonably simple way to do so, using mechanisms that exist in Swing and the Beans Binding library. We will create a binding between the rowSorter property of the master table in the example in my previous entries and a text field that I've just added for the search string. For this binding we will need a binding converter so that the table knows how to respond to the search string.

To follow along, you can either continue with the project created in previous entries or begin with a new NetBeans project (Java Desktop Application project template) that connects to a database.

Let's get started. First of all, we'll add a label and a text field for the search field as shown below.

masterdetail5-searchfield.png

Now we will add a converter class to the project.

  1. Create a new Java class in your project. Call it RowSorterToStringConverter.
  2. Replace the generated code in the new class with the following code:
    package clientpurchaseapp;
    
    import javax.swing.JTable;
    import javax.swing.RowFilter;
    import javax.swing.table.TableRowSorter;
    import org.jdesktop.beansbinding.Converter;
    
    /**
     * Binding converter between String and regex RowFilter (encapsulated by RowSorterToStringConverter).
     *  */
    public class RowSorterToStringConverter extends Converter {
    
        private JTable table;
    
        public JTable getTable() {
            return table;
        }
    
        public void setTable(JTable table) {
            this.table = table;
        }
    
        @Override
        public Object convertForward(Object value) {
            return value.toString();
        }
    
        @Override
        public Object convertReverse(Object mask) {
            TableRowSorter sorter = new TableRowSorter(table.getModel());
    
            // The following statement makes the filter case-sensitive. If you want 
            //filter to work in a case-insensitive way, uncomment the line below, comment 
            //the 7 code lines below
            //sorter.setRowFilter(RowFilter.regexFilter(".*" + mask + ".*"));
    
            //The following 7 lines create a case-insensitive filter. If you want 
            //the filter to be case-sensitive, comment them out and uncomment the 
            //line above
            String m = mask.toString();
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < m.length(); i++) {
                char c = m.charAt(i);
                sb.append('[').append(Character.toLowerCase(c)).append(Character.toUpperCase(c)).append(']');
            }
            sorter.setRowFilter(RowFilter.regexFilter(".*" + sb + ".*"));
            return sorter;
        }
    }
    
  3. Adjust the package statement, if necessary.
  4. Save the file and compile it. Compiling the file enables you to treat it as a bean that you can add to the form by dragging and dropping from within the IDE's GUI builder.
  5. Drag the class from the Projects window and drop it in white area surrounding the form, as shown in the screenshot below. masterdetail5-dragconverter.png

    A node called rowSorterToStringConverter1 should appear in the Inspector window.

    masterdetail5-inspector.png
  6. Select the rowSorterToStringConverter1 node and set its table property to masterTable.

We'll use this converter when we create the binding.

To create the binding:

  1. In the main form, right-click the text field and choose Bind | text.
  2. In the Bind dialog, select masterTable as the binding source and rowSorter as the expression. masterdetail5-binding-basic.png
  3. Click the Advanced tab of the dialog box.
  4. From the Converter combo box, select rowSorterToStringConverter1. masterdetail5-binding-advanced.png
  5. Click OK to close the dialog and generate the binding code.

Now when you run the application, you should be able to type in the Search Filter field and see that the list of rows is reduced to only rows that contain text matching what you have typed.

masterdetail5-filter-working.png

Note: If you have been following this whole series of posts, you will need to make a few changes to get the New Record and Edit Record buttons to work correctly. The Edit Record button doesn't work correctly because the number of the record to display is determined according to the records displayed but applied to the whole list of records in the database. In other words, if you select the first record in a filtered list, the first record of the whole database appears for editing in the dialog.

masterdetail5-synchproblem.png

The New Record button fails with an exception because the code to select the new row is determined according to number of records in the database table, not according to the number of records currently displayed in the table.

To fix the first problem, replace the line:

ec.setCurrentRecord(list.get(masterTable.getSelectedRow()));

with:

ec.setCurrentRecord(list.get(masterTable.convertRowIndexToModel(masterTable.getSelectedRow())));

To fix the second problem, replace the line:

int row = list.size() - 1;

with:

int row = masterTable.getRowCount() - 1;


Passing Dialog Input to the Main View and Database

Posted by pkeegan on May 27, 2008 at 08:33 PM | Permalink | Comments (3)

This is the fourth in a series of posts on creating a Java database application. In my last few posts, I started with skeleton code generated by the IDE and provided my own customizations, including adding a dialog to use for data entry and binding those fields with a table on the main form. In this post, I finish coding the connection between the dialog and the main form. I'll also add an Edit Client button and its corresponding Action code to the main form.

First let's hook up the buttons in the EditClient dialog with appropriate event-handling code. We already have save() and refresh() actions that are provided with the skeleton application. We will code the dialog so that the buttons reuse these actions. We can accomplish this by setting up a boolean property in the dialog that returns true when the Save Record button is pushed and returns false when Cancel is selected. Based on the value that is returned when the dialog is closed, the the save() or the refresh() action will be run from the main view class.

To set up the property, do the following:

  1. Open up the EditClient file and select the Source view.
  2. Place the cursor somewhere below the block of generated code that contains the initComponents() method.
  3. Press Alt-Insert and choose Add Property.
  4. In the Add Property dialog, type clientConfirmed as the property name.
  5. Set the type to boolean.
  6. Make sure the Generate Getters and Setters checkbox is selected.
  7. Click OK to close the dialog box and generate the code.

We'll set this property's value in event handling code for the buttons. Let's create the event listeners and handlers now:

  1. Switch to the Design view for the EditClient class.
  2. Select the Save button in the EditClient form.
  3. In the Properties window, click the Events button.
  4. Click the ellipsis (...) button next to the actionPerformed property.
  5. In the Handlers for actionPerformed dialog box, add a handler called saveNewClient.
  6. Within the saveNewClient method in the Source Editor (where the cursor jumps after you create the new handler), type the following code:
    setClientConfirmed(true);
    setVisible(false);
    
  7. Repeat steps 2-5 for the Cancel button and call its handler cancelNewClient.
  8. In the cancelNewRecord method, type the following:
    setClientConfirmed(false);
    setVisible(false);
    

Navigate to the newRecord() method and add the following code to the bottom of the method:

        if (ec.isClientConfirmed()) {
            save().run();
        } else {
            refresh().run();
        }

In the RefreshTask inner class, Thread.sleep is called four times to slow down the rollback code to better demonstrate how Swing Application Framework tasks work. We don't need this code for this application, so delete those four statements. Similarly, we don't need a try/catch block here, so delete the try and catch statements as well (but leave the rest of the body of the try block).

Since the save() and refresh() actions act on any changes made during the application's session, we will want to make the dialog modal and make the tables in the main form uneditable. We also need to make the dialog modal so that when the user presses either the Save or Cancel button, the setVisible() method doesn't return until the event handler (which includes the setClientConfirmed method) has run.

To make the dialog modal:

  1. Open the Design view of the EditClient class.
  2. Select the dialog.
  3. In the Properties window, click Properties and select the checkbox for the modal property.

To make the main form's Clients table uneditable:

  1. Open the main view class in the Source Editor and select the Design view.
  2. Right-click the top table and choose Table Contents.
  3. In the Customizer dialog, select the Columns tab.
  4. For each column, clear the Editable checkbox.
  5. Click Close.

You can now run the application and click New Client to add a new record. When you press Save in the New Client dialog, the record is saved. When you press Cancel, the new record you have changed is rolled back.

This is all well and good, but by disabling the editability of the table on the main form, we can no longer edit existing records. To solve this, we'll add an Edit button to the main client form so that we can edit existing records. For event-handling, we'll take advantage of the Swing Application Framework's Action facility.

To add the button and its corresponding event-handling code, do the following:

  1. Drag the New Client Button a bit to the left.
  2. Drag a button from the palette into the opening just created.
  3. Right-click the button and choose Set Action.
  4. In the Action field, select Create New Action.
  5. For Action Method, type editClient.
  6. For Text, type Edit Client.
  7. Click the Advanced Tab and select recordSelected for the Enabled Property.

    This generates an annotation attribute to ensure that the button and any other trigger for the action (e.g. a menu item) are only enabled when a record is selected.

  8. Click OK to close the dialog box.

    The Source view of the file should appear with the cursor in the following new method:

        @Action(enabledProperty = "recordSelected")
        public void editClient() {
        }
    
  9. Within the method, paste the following code:
            setSaveNeeded(true);
            JFrame mainFrame = ClientAndPurchaseApp.getApplication().getMainFrame();
            EditClient ec = new EditClient(mainFrame, false);
            ec.setCurrentRecord(list.get(masterTable.getSelectedRow()));
            ec.setVisible(true);
            if (ec.isClientConfirmed()) {
                save().run();
            } else {
                refresh().run();
            }
    

Most of that code is copied straight from the newRecord action. The key difference is the line ec.setCurrentRecord(list.get(masterTable.getSelectedRow()));, which populates the current record in the dialog with the currently selected record.

The Client part of the application is almost completely set. You should be able to freely add, edit, and delete records from your CLIENTS table using the specialized GUI we have created.

One last detail: the main form still has the title of Database Application Example, and it's not obvious where to change. Hint: it's not within the GUI Builder.

To change the title of the main frame of the application:

  1. In the Projects window, select the project's node and choose Properties.
  2. In the Project Properties dialog box, select the Application node.
  3. Edit the Title property and any other properties that are important to you.
masterdetail4-projpropertiesapp.png

Now when you run the application, most of the key elements are in place. In the screenshot below, you can see the Edit Client dialog as it appears after having selected a record and pressed the Edit button.

masterdetail4-nearfinishedapp.png

I could continue with customization of the bottom part of the main form and other fine tuning of the application, but I'll save most of those details for the tutorial and individual blog posts with more atomic examples. As always, keep your questions coming and I'll try to deal with as many of them as I can.



Beans Binding Between Separate Forms

Posted by pkeegan on May 21, 2008 at 07:45 PM | Permalink | Comments (15)

Continuing from my last post, I'll show the next steps in the creation of this simple (but not too simple) client purchase application. This time, our main focus is in creating a separate dialog which we will use for data entry. We'll need to do a few tricks so that input from the dialog is propagated to the main form and then the database.

But first we'll need to clear up a few loose ends. As I alluded to last time, I use AUTO_INCREMENT attributes for the ID columns of the CLIENTS, COUNTRIES, and ORDERS tables. This means that whenever a new row is added to those tables, that row's AUTO_INCREMENT field is given a unique value (the value of the last new record + 1). For me using AUTO_INCREMENT is a handy way to ensure having unique records.

When you generate the skeleton of the application in the New Java Desktop Application wizard, the IDE generates two entity classes (Clients.java and Orders.java) that represent database tables with the same names. However, these entity classes are missing code to deal with the AUTO_INCREMENT fields. Without that code, you will get errors when trying to enter new records. To fix that:

  1. Open the Clients.java class.
  2. Navigate to the line after the one containing @Id.
  3. Enter the line @GeneratedValue(strategy=GenerationType.IDENTITY).
  4. Press Ctrl-Shift-I to generate the necessary import statements for this annotation.
  5. Repeat the process for Orders.java.
masterdetail2-identityannotation.png

Note: You can also use code completion here. It takes three selections to get the entire line, but the import statements are generated for free.

Once you have added these annotations, you can run the application and start adding data. Click the top New button to add a client. With a client selected, click the bottom New button to add an order for that client. Click Save to push your changes to the database. Click Refresh to back out any unsaved changes.

masterdetail2-skeletonapp.png

I like tables for browsing data, but I think they leave something to be desired for data entry. So for this application, we'll add dialogs for data entry.

To create and populate the JDialog, follow these steps:

  1. Right-click the package that contains your classes and choose New | Other. Select Swing GUI Forms | JDialog Form template and name it EditClient.
  2. From the Palette window drag, drop, and arrange components for the customer's personal details.

    You should have JLabels for each of the following fields: first name, last name, address, city, state, zip code, country, and phone number. You should have JTextFields for each of those fields, except for country, for which we will use a JComboBox.

  3. Edit the display text for JLabels.
  4. Add two buttons and name them Save and Cancel.
  5. (Optional) Rename all of the components you have added to more memorable names, such as firstNameLabel. You can do this inline in the Inspector window.

The resulting layout should look something like what you see below.

masterdetail2-clientdialoglayout.png

Now we need to bind the various fields to the corresponding columns in the table. We can't bind directly to components from other forms in the Bind dialog box, so we'll have to create an intermediary property of type Clients to hold the record. When the user presses New, the property will be given the value of the currently selected record.

We can quickly generate the bean property with the IDE's bean support:

  1. At the top of the design area of the EditClient form, click the Source tab. Click somewhere within the class, such as above the variable declaration block.
  2. Press Alt-Insert (or right-click and choose Insert Code) and choose Add Property.
  3. In the Add Property dialog, name the property currentRecord, give it the type Clients, select Generate Getter and Setter, and select Generate Property Change Support.
  4. Click OK to generate the property.
masterdetail2-beanpropeditor.png

We now need to customize the generated setCurrentRecord method. Replace the body of the method with these three lines:

        Clients oldRecord = this.currentRecord;
        this.currentRecord = currentRecord;
        propertyChangeSupport.firePropertyChange("currentRecord", oldRecord, currentRecord);

Now we need to add code to the New action to open the dialog and clear the currentRecord property when a user wants to add a new record:

        JFrame mainFrame = ClientAndPurchaseApp.getApplication().getMainFrame();
        EditClient ec = new EditClient(mainFrame, false);
        ec.setCurrentRecord(c);
        ec.setVisible(true);

For now, we won't code the Save and Cancel buttons on the dialog (we've had enough digressions!). I'll cover that in an upcoming post.

With those preliminaries out of the way, we can proceed with the binding of the text fields. We'll be binding the text property of each text field to the corresponding property of the Clients object represented by currentRecord.

To bind a dialog text field to the appropriate property of currentRecord:

  1. Right-click a text field and Choose Bind | text.
  2. In the Bind dialog box, select Form as the Binding Source (note that Form is at the very bottom of the drop-down list).
  3. In the Binding Expression drop-down list, expand the currentRecord node and select the property corresponding to the text field that you are binding.
  4. Click OK to close the Bind dialog box.
masterdetail2-addressbinding.png

Do this procedure for each of the text fields in the dialog. For now, don't bind the JComboBox to anything. We'll need to do some other preparation to get that to work properly. I'll cover that in my next post.

Now you should be able to run the application, press the first New button, and enter data in the dialog. The Save and Cancel buttons on the dialog don't do anything yet, but we can save the records from the main frame. In the next post, we'll clear up some of these loose ends so that the application behaves more like applications that we are used to.



Input on a New Desktop Java Database Tutorial

Posted by pkeegan on May 13, 2008 at 09:20 AM | Permalink | Comments (14)

Recently I've found time again to work on actual tutorials. I don't have anything written yet, but I have something resembling a plan, which you can find here: http://wiki.netbeans.org/PlanGuiBuilderDocImprovements.

Over the next few weeks, I'll be blogging about creating a Swing desktop application with database connectivity. These postings will essentially serve as a rough sneak preview of a full-fledged tutorial on the subject that I'll later post to netbeans.org. The tutorial will go beyond simple database connectivity and show things such as one-to-many and many-to-one relationships as well as how to bind database tables to a variety of GUI components. We'll use a MySQL database that has tables for client info, order info, and countries. There will be a one-to-many relationship between the client and order tables. There will be a many-to-one relationship between client and countries tables. Along the way, I'll be looking at any feedback that comes through and do my best to respond to it, whether in quick responses, in separate articles, or by modifying the final tutorial. Chances are that I'll also tweak the structure along the way as I find better ways of doing things.

To start off, I'll provide an SQL script that provides a beginning database structure:

CREATE TABLE CLIENTS (
	ID INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
	FIRST_NAME VARCHAR(20),
	SURNAME VARCHAR(30),
        ADDRESS VARCHAR(30),
	CITY VARCHAR(30),
	STATE_ VARCHAR(30),
	ZIP INTEGER,
	COUNTRY_ID INTEGER,
	PHONE INTEGER
);

    CREATE TABLE EMAIL_ADDRESSES (
    CLIENT_ID INTEGER NOT NULL,
    ADDRESS VARCHAR(50) NOT NULL PRIMARY KEY,
    FORMAT INTEGER,
    FOREIGN KEY (CLIENT_ID) REFERENCES CLIENTS(ID)
    );
    
CREATE TABLE ORDERS (
    ID INTEGER NOT NULL AUTO_INCREMENT,
    CLIENT_ID INTEGER NOT NULL,
    PRODUCT VARCHAR(50) NOT NULL,
    AMOUNT INTEGER NOT NULL,
    PRIMARY KEY(ID),
    FOREIGN KEY (CLIENT_ID) REFERENCES CLIENTS(ID)
);

CREATE TABLE PRODUCTS (
    MODEL VARCHAR(50) NOT NULL PRIMARY KEY,
    PRICE DECIMAL NOT NULL
);

CREATE TABLE COUNTRIES (
    COUNTRY_ID INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
    COUNTRY VARCHAR(30) 
);

ALTER TABLE CLIENTS    
ADD CONSTRAINT COUNTRIES_FK Foreign Key (COUNTRY_ID)
   REFERENCES COUNTRIES (COUNTRY_ID);

A few notes on the structure:

  • I use AUTO_INCREMENT in some of the tables so that there is a unique identifier for each row in those tables. For this feature to work properly within the application, you need to add the @GeneratedValue(strategy=GenerationType.IDENTITY annotation for that column in the table's entity class. See http://weblogs.java.net/blog/pkeegan/archive/2007/12/index.html for some more context.
  • The foreign key in the ORDERS table is there to link each order record with a client. In the application's UI, all ORDER records are displayed for the selected CLIENT.
  • The foreign key in the CLIENTS table points to a COUNTRIES table. We will use this relationship in the application to enable the user to select a client's country from a combo box.
  • EMAIL_ADDRESSES is a separate table with a foreign key linking it to the CLIENTS table. This is in attempt to keep one of the entry dialogs looking as much as possible like http://www.netbeans.org/kb/60/java/quickstart-gui.html, where it is possible to enter multiple email addresses person. The motivation is so that this new tutorial can build on that previous one (or a similar version of it), but very likely I will delete that table in the end and merely have an EMAIL column in the CLIENTS table.

After you have created the above database and have connected to it from IDE (see Connecting to a MySQL Database), you can go ahead and create the initial application skeleton by following these steps:

  1. Choose File | New Project.
  2. Select the Java category and the Java Desktop Application template.
  3. In the Name and Location page of the wizard, select the Database Application skeleton.
  4. In the Master Table page of the wizard, select the connection to the just-created database. Then select the clients table, and then move ID from Columns to Include to Available Columns. clientrecordproject1.png
  5. In the Detail Options page, click the Table radio button and select the orders table from the combo box.
  6. clientrecordproject2.png
  7. Click Finish to exit the wizard.
  8. Choose Run | Run Main Project to see the main application window. clientrecord-runwithoutdata.png

So that's the start of the application. The next steps include:

  • Adding @GeneratedValue(strategy=GenerationType.IDENTITY annotations (as noted above) to the identity columns in the Client.java and Countries.java entity classes.
  • Creating an entity class for the COUNTRIES table and modifying the CLIENTS entity class to handle the relation.
  • Customizing the generated JTables (e.g. changing column headings, making the columns read-only). You can get a head start on this by right-clicking the JTable and choosing Table Contents.
  • Creating separate dialogs for the entry of client and order records.

I'll cover those topics and more starting early next week. I welcome all feedback!



JavaHelp and Swing Application Framework

Posted by pkeegan on May 15, 2007 at 07:27 AM | Permalink | Comments (6)

Updated 27 March 2008 to account for changes that appeared in NetBeans IDE 6.0 (and are still relevant for 6.1)

After a long long hiatus, I'm going to try my hand at blogging again. The main reason is to share things that I learn in my work in a more frequent and incremental way than I do with the tutorials I write. I expect this will also give me a nice chance to have more onversations with readers than you do when you are buried behind more monolothic documents. In doing this, I'm obviously taking a few cues from Geertjan.

At least initially, my focus will be on Swing things, old and new, particularly as programmed in NetBeans. But whereas experienced programmers like Hans and Josh are able to take on pretty sophisticated topics, I'll be taking an almost exclusively "by dummies" angle.

To kick things off, I'll start with a topic I have a fairly long history with: in-product help (or "online help" as it's known in the industry, though I avoid this term because it is so obviously misleading to someone not familiar with industry jargon). The help extension to the Java platform is called JavaHelp. It's now open source.

So how do you add help to a Swing application? I've written lots of help for NetBeans, but had never tried to program an application to use JavaHelp. But as I was playing around with recent development builds of NetBeans IDE 6.0, the Help menu that comes as part of the Java Desktop Application template all but dared me to try. As it turns out, it's pretty straightforward. The general procedure is:

  • Create basic metadata for a JavaHelp help set and a sample topic or two.
  • Add one of the JavaHelp binaries to your application's classpath.
  • Add code to connect the JavaHelp system to your application.


Let's do a run-through of this process using my favorite IDE, NetBeans. To spice things up a little, I'll use the NetBeans IDE 6.0 Preview (M9) release. This release is pretty far from final, but it's got a lot of cool things worth showing off, including support for the Swing Application Framework. The most visible impact on this mini-tutorial will be the snazzy application skeleton you get without coding anything plus a simple way to handle actions. If you prefer to use the 5.5 version, the most essential points here will work. You just won't see the fancy new template or the Actions part of the Swing Application Framework in, ahem, action.

First we'll create a skeleton application that already has menus and windowing behavior built in.
  1. Choose File > New Project.
  2. Select the General category and then select the Java Desktop Application template.
  3. Click Next.
  4. On the Name and Location page of the wizard, type AppWithHelp for the project name. helpapp_newproj.png
  5. For application shell, select Basic Application.
  6. Click Finish.


To get the help set into your project directory:
  1. Download the basic help set that we will use (including metadata and help content).
  2. Unzip the file on your system.
  3. Copy the javahelp folder into the directory containing your project. (javahelp should be at the same level as code>src, test, and nbproject).


Now we have to make sure that the IDE knows to package the JavaHelp files in the application as part of the build process:
  1. In the IDE, right-click the AppWithHelp project and choose Properties.
  2. Select the Sources node.
  3. Next to the Source Package Folders table, click Add Folder.
  4. In the Add Source folder dialog box, select the javahelp folder and click Open. helpapp_addhelpfiles.png
  5. Click Close to exit the Project Properties dialog.
  6. In the Edit Project Properties dialog box that comes up, click Regenerate.
You can now view the help files from the Projects window and edit them in the Source Editor. When you build your project, the help files will be included in it. The metadata files are in the appwithhelp.docs package. A couple of dummy help topics are in appwithhelp.docs.content. For info on the metadata files and how JavaHelp works in general, check out the JavaHelp User's Guide.

Quick digression: Metadata for JavaHelp help sets are simple XML files. In the JavaHelp documentation, you might notice that these files have special extensions (like .hs, .jhm, .idx, and .toc). However, we're using .xml as the extensions for these files so that we don't have to do any extra configuring to get the IDE's XML editing features for them.

Now we need to add the basic JavaHelp library to the project and add the code that calls into that library.

To add the JavaHelp binary to the application:
  1. If you haven't already done so, download the JavaHelp distro from https://javahelp.dev.java.net/#binary
  2. Unpack the zip file.
  3. In the IDE's Projects window, right-click the AppWithHelp project's Libraries node and choose Add JAR/Folder.
  4. In the file chooser, navigate to the jh.jar file and click Open.
  5. You should find the jh.jar file in the javahelp2_0_04\jh2.0\javahelp\lib directory.


To add a method to the application that will open up your help set in the JavaHelp viewer:
  1. Open the appwithhelp/AppWithHelpFrame.java file.
  2. In the Source Editor, click the Source button to display the source view of the class.
  3. Insert the openHelp() method below directly into the class, perhaps in the area just above the taskMonitorPropertyChange method. Note the @Action annotation. You will see the use of this later in the tutorial.
    /**This method opens up the help viewer for specified help set 
     * and displays the home ID of that help set
     */  
        @Action public void openHelp() {
        // Identify the location of the .hs file 
        String pathToHS = "/appwithhelp/docs/appwithhelp-hs.xml";
        //Create a URL for the location of the help set
        try {
          URL hsURL = getClass().getResource(pathToHS);
            hs = new HelpSet(null, hsURL);
        } catch (Exception ee) {
            // Print info to the console if there is an exception
            System.out.println( "HelpSet " + ee.getMessage());
            System.out.println("Help Set "+ pathToHS +" not found");
            return;
        }
      
        // Create a HelpBroker object for manipulating the help set
        hb = hs.createHelpBroker();
        //Display help set
        hb.setDisplayed(true);
    }
    
  4. Add the following variable declarations as well, perhaps at the bottom of the file near the variables declaration guarded block.
        private HelpSet hs;
        private HelpBroker hb;
        private URL hsURL;
    
  5. Right-click in the Source Editor and choose Fix Imports. You should see the Fix All Imports dialog with recommendations for the HelpBroker, HelpSet, and URL classes.
  6. helpapp_fiximports.png
  7. Click OK to generate the import statments for the classes.
  8. Press Ctrl-S to save the file. The red error marks should then disappear (though it might take a few seconds).
Now we need a UI component for the user to open the Help from. Let's create a menu item in the Help menu:
  1. In the Source Editor's toolbar, click the Design button to return the IDE to design mode for the AppWithHelpFrame.java file.
  2. In the Inspector window right-click JFrame > menuBar [JMenuBar] > helpMenu [JMenu] and choose Add From Palette > Swing Menus > Menu Item.
  3. Rename the variable from jMenuItem1 to contentsMenuItem. helpapp_inspector.png
  4. Right-click contentsMenuItem and choose Move Up.
You should now have a Help menu with two items (Item, About). You can test this in the form.

Finally, it's time to hook the menu item to the openHelp() method.
  1. In the Inspector window, select contentsMenuItem.
  2. In the Properties window, select the action property and select openHelp from the dropdown list. (Yes, the IDE detected that the openHelp method is an action because of the @Action annotation.) helpapp_action.png
  3. Back in the Inspector, right click contentsMenuItem and choose Set Action.
  4. In the Set Action dialog, set the following attributes:
    • Text: Contents...
    • Tool Tip: Opens the application's help window
    • Accelerator: control pressed H (entered by pressing Ctrl-H)
    helpapp_action-editor.png
  5. Click OK.
If you want to see what code was generated by these procedures, click back to the AppWithHelpFrame.java file's Source View and expand the Generated Code code fold. There you will find the this snippet of code which adds the Contents menu item to the Help menu and connects it to the openHelp() method:
        
contentsMenuItem.setAction(actionMap.get("openHelp")); // NOI18N
contentsMenuItem.setText(resourceMap.getString("contentsMenuItem.text")); // NOI18N

contentsMenuItem.setName("contentsMenuItem"); // NOI18N
fileMenu.add(contentsMenuItem);


At this point, you should be able to see your application and JavaHelp in action:
  1. In the IDE, right-click the project node and choose Run Project. The project will build and, after several seconds, run.
  2. In the running application, choose Help > Contents, and see a Help window with a table of contents, index, and a few dummy topics.
helpapp_helpwindow.png

Here I've shown the most basic variant of adding a help set to your application. Another important aspect of this is setting up your application's build system to use JavaHelp. This will bring you the important benefits like the automatic creation of a search database and validating help metadata. If I'm not distracted by something more pressing and there's enough demand, I'll might cover these topics in ensuing posts.

NetBeans Day - holy cow!

Posted by pkeegan on June 27, 2005 at 05:56 PM | Permalink | Comments (0)

I just finished up an article on NetBeans Day for the JavaOne Today, the conference's daily newsletter. It was fun trying to sound like a newspaper reporter, but now I want to reinsert my own voice into the proceedings, and I'll start with one word.

Wow!

There were the announcements (collaboration plugins, Matisse, new refactoring) and the stats (550 people or so attending according to Greg Sporar, who also has a recap). But beyond that I was impressed by how focused the attendees were. I figured that since it was a free event, there would be a lot of people popping their heads in and out. Easy come, easy go. But no. It was standing room only at the beginning and standing room only at the end. Check out some photos here.

At one point, Tim Boudreau was talking in front of 150-200 people about plugin module development and digging into some API details. I cringed, figuring that most of the people there were IDE users and not particularly interested in adding to the IDE or building with the platform. I was waiting for the awkward mass exodus of people suddenly finding a need for a drink of water or a walk on Market St. But it never came. People stuck with him and had a ton of questions. I imagine the BOFs that the NetBeans team are doing at JavaOne will be similarly popular.

I don't think anybody on the team was really prepared for the in-person developer response yesterday. At this time last year, 3.6 was the current release and we were just beginning to publicize and sell users on vast changes to the IDE's project workflow. Replacing the 3.x "filesystems" concept with the 4.x Ant-based project system was a big risk for us, but one we felt we needed to take to avoid sinking into irrelevance. People were curious about our new direction, but were taking a wait and see approach.

Since then, we've seen a huge increase in our download numbers and some great press, which tells us that we're on the right track. But there is no more gratifying validation than seeing the excitement of real users in person.



Our big little NetBeans IDE book

Posted by pkeegan on June 23, 2005 at 03:32 PM | Permalink | Comments (1)

My name is Patrick Keegan. I've been a NetBeans IDE technical writer for the past six years. I'm starting this web log to communicate my impressions from JavaOne 2005 and I imagine I'll continue with it as long as I feel like I have something unique to say in this ever increasingly densely populated blogosphere. Today I'll start with a round of shameless self-promotion and get that out of the way.

I'm coming to this year's JavaOne as co-author of the NetBeans IDE Field Guide. Pretty exciting for me personally and professionally and hopefully interesting for some of you as Java technology fans. If you're at the conference, stop by the Digital Guru stand to flip through a copy. Or come to NetBeans Day on Sunday and get a free copy (get there early!). I'll be at the Digital Guru stand Monday 4 - 4:30 p.m. with two of my co-authors - Ludovic Champenois and Charlie Hunt to sign copies and talk about the book. If you aren't coming to the conference, you can find it for a discount at Bookpool as well as other online vendors.

When we started writing on the book, we wanted to include as much information as possible about the IDE. There's lots of documentation on the NetBeans project web site and we've gotten better at organizing and presenting it. But for many (like me) there's an undeniable pleasure about being able to leave the computer at the desk, flop down on a couch, flip open a book and start reading. (Also, since NetBeans is the premier everything in one package IDE, why not have a single book to reflect that.)

That said, it's awfully hard to make a book comprehensive, comprehensible, *and* timely all at the same time. However, I think we've done a pretty good job in all three categories. The book delves deeply into the IDE's edit/compile/debug features and explores development of desktop, web, enterprise, and mobile apps. Timeliness is not the norm for such books when the target is constantly in motion as NetBeans IDE is, especially given the typical 3-4 month lag between submitting the manuscript and seeing the printed book. I'm proud of the fact that the Field Guide is coming out only a month after the 4.1 release and will remain relevant for a long time.

More later. Hope to see you at JavaOne!



Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds