O'Reilly and Associates
October 7th, 1998
Java Swing

Java Swing

Addendum: Changes for Swing 1.1 Beta 3

by Robert Eckstein, co-author Java Swing

Wouldn't you know it? Just when the three of us thought we released an up-to-date book and we're ready to sit back and give our hands a rest, JavaSoft goes and changes a bunch of things. Oh well.... I guess we're used to it by now, since Marc, Dave, and I have spent many sleepless nights updating Java Swing to cover the JDK 1.2 Beta 4 release. In any case, Sun has released a third beta for the Swing development libraries, so we thought we'd continue to bring you up to speed.

Swing 1.1 Beta 3

Swing 1.1 Beta 3, released in early October 1998, represents the third beta for the 1.1 version of the Swing component libraries. As yet, there is no equivalent for JDK 1.2, although it will almost certainly surface in the next beta version of the JDK, if one is released. This release is very similar to the Swing 1.1 Beta 2 release, with the exception of multiple bug fixes and new Swing package names. (In fact, JavaSoft still allows you to download the Beta 2 release if you're not yet ready to switch.) Below, we want to add some descriptions to help you understand how to take advantage of some of the new features in both Beta 2 and Beta 3.

New Package Names

This is a simple change that is sure to cause lots of headaches. Essentially, what was before known as...

com.sun.java.swing.*

...in Beta 2 is now...

javax.swing.*

...in Beta 3. Again, let us mention that JavaSoft continues to allow you to download the Beta 2 libraries, which adhere to the old package name. However, you should strongly consider moving to the new. This means changing a ton of import statements at the front of your source files, and any fully qualified references to Swing classes that you may have lurking in your code. Luckily, JavaSoft now provides a tool that will help you make the transition with relative ease. It is called PackageRenamer, and you can download it from the JavaSoft PackageRenamer instruction page.

Faster Key Event Dispatching

The dispatching of KeyEvent objects through components has been optimized, with noticeable improvements. There are no API changes for this, however, if you create a frame with several components, each having their own key events, and benchmark the speed of Beta 2 versus the Beta 1 (or JDK 1.2 beta 4) versions, the difference is pretty obvious. We won't write source for this, as it would double the size of this article, but you can probably use the example from the next section to get started.

Outline Dragging with Internal Frames

Internal frames now contain a client property that allows programmers to specify how the drag of an internal frame might be represented on the screen. According to JavaSoft, this is in response to requests to speed up dragging of internal frames with many components embedded inside of them. Thus, instead of repositioning and re-copying the entire internal frame, a rectangle is XOR'd against the background, similar to how frame dragging occurred in Windows 3.1.

To activate this internal frame dragging, you can set the JDesktopPane.dragMode property to "outline," as shown below.

//  DragExample.java
//

import java.awt.*;
import java.awt.event.*;

import javax.swing.*;
import javax.swing.border.*;

public class DragExample extends JFrame {

    JButton buttons[] = new JButton[100];

    public DragExample() {

        super("Focus Example");
        JPanel mypanel = new JPanel();
        mypanel.setLayout(new GridLayout(10,10));

        for (int i = 0; i < 100; i++) {
            buttons[i] = new JButton("Number " + i);
            mypanel.add(buttons[i]);
        }

        JInternalFrame frame1 = new JInternalFrame("Internal Frame 1",
                                                 true, true, true, true);

        frame1.setBackground(Color.lightGray);
        frame1.getContentPane().setLayout(new GridLayout(2, 3));
        frame1.setSize(200, 200);

        frame1.setContentPane(mypanel);

        JDesktopPane desktop = new JDesktopPane();
        desktop.add(frame1, new Integer(1));
        desktop.setOpaque(true);

        //  Here is the client property for XOR rectangle dragging
        desktop.putClientProperty("JDesktopPane.dragMode","outline");

        Container contentPane = getContentPane();
        contentPane.add(desktop, BorderLayout.CENTER);
        setSize(new Dimension(600, 400));
        setVisible(true);
    }
    public static void main(String[] args) {
        new DragExample();
    }
}
Download the: source code

We should warn you that JavaSoft has provided this as only a short-term solution as of Beta 2, and a more comprehensive fix should be available in the future. Unfortunately, this change only covers moving the internal frame by dragging it via the title bar. Resizing the internal frame by grabbing any of the four corners still attempts to repaint the entire frame, despite the value of the JDesktopPane.dragMode client property. This remains true in Beta 3 as well.

Hue-Saturation-Brightness Panel in JColorChooser

This ability to choose a color through its hue-saturation-brightness (HSB) values has been re-added as a panel in the JColorChooser component. To see it, choose the "HSB" tab, which now appears in the default color chooser dialog. The following source code demonstrates how to bring up a simple JColorChooser dialog:
//  ColorPick.java
//
import java.awt.*;
import java.awt.event.*;

import javax.swing.*;
import javax.swing.colorchooser.*;

public class ColorPick extends JFrame {

    public static void main(String args[]) {
        ColorPick p = new ColorPick();
        JColorChooser chooser = new JColorChooser();
        Color c = chooser.showDialog(p, "Demo", Color.blue);
    }

}
Download the: source code

A word of warning: the blended rectangle in the center of the dialog can take a few seconds to refresh, depending on the graphics capabilities of the machine and the OS. If you don't wish this panel to appear in the dialog, you can attempt to remove it using the appropriate methods of JColorChooser.

Additions to JEditorPane

One of the slickest features of JEditorPane is that it contains a setPage() method; this method simplifies the task of loading an HTML page into the document of an editor pane. There are two versions of setPage() in the JEditorPane class:
    void setPage(java.net.URL url)
    void setPage(String s)
Both methods perform the same task. They each load the HTML document referenced by the String or URL into the JEditorPane's document, resetting the content-type accordingly. However, starting with Swing 1.1 Beta 2, the page loading is now handled asynchronously. This means that control is transferred back to your code and the method does not block until the page is loaded, as it did previously. For example, if you wanted to create a simple JEditorPane that displays the contents of the O'Reilly home page, you could do the following:
//  SimpleBrowser.java
//

import java.awt.*;
import java.awt.event.*;
import java.io.*;

import javax.swing.*;
import javax.swing.text.html.*;
import javax.swing.event.*;

public class SimpleBrowser extends JFrame {

    static JTextField textField;
    static JEditorPane editor;

    public SimpleBrowser(String s) {
        super(s);

        JPanel panel = new JPanel();
        panel.setLayout(new BorderLayout());
        panel.setBorder(BorderFactory.createRaisedBevelBorder());

        editor = new JEditorPane();
        textField  = new JTextField();
        JScrollPane scrollPane = new JScrollPane(editor);

        editor.setEditable(false);

        panel.add(new JLabel("Location:  "), BorderLayout.WEST);
        panel.add(textField, BorderLayout.CENTER);

        getContentPane().add(panel, BorderLayout.NORTH);
        getContentPane().add(scrollPane, BorderLayout.CENTER);

        textField.addActionListener(new TextFieldListener());
    }

    public static void main(String args[]) {
        SimpleBrowser frame = new SimpleBrowser("Simple Browser");
        frame.setSize(400,400);
        frame.setVisible(true);
    }

    class TextFieldListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            try {
                editor.setPage(textField.getText());
            } catch (IOException ex) {
                editor.setText("Page could not be loaded");
            }
        }
    }
}
Download the: source code

Not bad, huh? Kind of makes you want to just take a day off and write a browser. Again, be aware that the page will load asynchronously. If you want to type in a new URL before the first finishes loading, you can do that at any time. 

On a related note, limited support for character encodings has been added. This is important for documents which do not adhere to the standard character sets. Some of the other common character sets include ISO-8859-1, ISO-8859-5, Shift-jis, Euc-jp, and UTF-8. When a file is loaded that uses such a character set, its characters are converted to Unicode and processed normally. JavaSoft was not specific as to which character types it supported. However, when I tried it with Shift-jis, it threw an exception.

Finally, you can use the setContentType() method of JEditorPane to explicitly set the content type of a document. The prototype for the call looks like the following:

   public final void setContentType(String type)
The string parameter expect the MIME content type. Currently, these three are supported:
 
  • text/plain - ordinary text
  • text/html - hypertext meta-language (HTML)
  • text/rtf - rich text format


Calling this method is equivalent to calling both getEditorKitForContentType() and setEditorKit(). Note that you can specify the extra character set information in the URL of setPage() as well; JEditorPane will correctly parse the encoding information after the semicolon.

The HTML Package

If you've worked with the HTML package before (in its infancy), you should note that the package has again "morphed." The underlying package now makes use of the HotJava engine, which supports SGML DTD-based content. In addition, the source now includes limited support for HTML 3.2, including frames, tables, forms, and varying character sets through the use of the HTML tag. We won't go into too much detail here, as many of the changes are not yet complete and some still throw various exceptions. However, we encourage you to look at the Swing documentation for more information on where this package is going.

You'll notice from our example above that we did not provide support for clicking on any links. Don't despair! This is also ridiculously simple! The key thing to remember here is that JEditorPane will generate hyperlink events if the editable property is set to false (i.e., setting JEditorPane.setEditable(false) )-  these events occur when the user clicks on a hyperlink in the text. With the added support for HotJava in the latest iteration of the javax.swing.text.html package, we can now provide more complete support for the JEditorPane "browser" that we defined above, including support for setting only the page inside the target HTML frame:

//  Browser.java
//

import java.awt.*;
import java.awt.event.*;
import java.io.*;

import javax.swing.*;
import javax.swing.text.html.*;
import javax.swing.event.*;

public class Browser extends JFrame {

    static JTextField textField;
    static JEditorPane editor;

    public Browser(String s) {
        super(s);

        JPanel panel = new JPanel();
        panel.setLayout(new BorderLayout());

        editor = new JEditorPane();
       textField  = new JTextField();
        JScrollPane scrollPane = new JScrollPane(editor);

        editor.setEditable(false);

        panel.add(new JLabel("Location:  "), BorderLayout.WEST);
        panel.add(textField, BorderLayout.CENTER);

        getContentPane().add(panel, BorderLayout.NORTH);
        getContentPane().add(scrollPane, BorderLayout.CENTER);

        editor.addHyperlinkListener(new HyperListener());
        textField.addActionListener(new TextFieldListener());
    }

    public static void main(String args[]) {
        Browser frame = new Browser("Browser");
        frame.setSize(400,400);
        frame.setVisible(true);
    }

    class HyperListener implements HyperlinkListener {

        public void hyperlinkUpdate(HyperlinkEvent e) {
            if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
                JEditorPane sourcePane = (JEditorPane) e.getSource();
                if (e instanceof HTMLFrameHyperlinkEvent) {
                    HTMLFrameHyperlinkEvent event=(HTMLFrameHyperlinkEvent)e;
                    HTMLDocument doc=(HTMLDocument)sourcePane.getDocument();
                    doc.processHTMLFrameHyperlinkEvent(event);
                } else {
                    try {
                        textField.setText("Contacting " +
                            e.getURL().toString() + " ...");
                        sourcePane.setPage(e.getURL());
                        textField.setText(e.getURL().toString());
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                }
            }
        }
    }

    class TextFieldListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            try {
                editor.setPage(textField.getText());
            } catch (IOException ex) {
                editor.setText("Page could not be loaded");
            }
        }
    }
}
Download the: source code

Alas, we confess, we lifted the HyperListener class almost directly from the examples given with the package. However, it does demonstrate that working with the new HTML package is not only easy, but also is kind of fun. Here is an example page with tables from the Javadocs of the Swing Beta 3 libraries themselves:

Tables

Tables have again taken a slight change in the column resizing policy. With Swing 1.1 Beta 1, two new constants appeared that can be passed to the JTable method setAutoResizeMode(). These constants are:
 
  • JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS-- resize only this column and each of the following columns to support the new size.
  • JTable.AUTO_RESIZE_NEXT_COLUMN-- resize only this column and the following columns to support the new size.


Note that these constants regulate how the table will be sized when the auto-resizing is activated (e.g., there has been no call setAutoResizeMode(AUTO_RESIZE_OFF);  ). For example, here is example source code that presents a simple table. To resize the columns, drag along the walls of the headers at the top:

//  SimpleTable.java
//

import java.awt.*;
import javax.swing.*;

public class SimpleTable extends JFrame {

  public SimpleTable() {
    super("Simple JTable");

    JTable jt = new JTable(new String[][] {
                               {"AAA", "BBB", "CCC", "DDD"},
                               {"EEE", "FFF", "GGG", "HHH"} },
                  new String[] {"Header1", "Header2",
                                "Header3", "Header4"} );

    jt.setAutoResizeMode(JTable.AUTO_RESIZE_NEXT_COLUMN);

    JScrollPane jsp = new JScrollPane(jt);
    setContentPane(jsp);
  }

  public static void main(String args[]) {
    SimpleTable st = new SimpleTable();
    st.setSize(300, 100);
    st.setVisible(true);
  }
}
Download the: source code

First, try enlarging the "Header 1" column by placing the cursor on the border of "Header 1" and "Header 2" and dragging to the right. Note that both headers resize themselves to support the change.

Next, delete the line...

    jt.setAutoResizeMode(JTable.AUTO_RESIZE_NEXT_COLUMN);
...and recompile. Then try resizing "Header 1" again. Aha! This time, each of the remaining headers shrinks to support the new size of "Header 1." Magic? Nope. Actually, you've simply used the default:
    jt.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS);

Support for these resizing policies has resulted in a new field in the TableColumn class to maintain the preferred width of the column; you probably won't need to worry about this. However, when you're working with tables, it is recommended that you use the setPreferredWidth() method instead of the setWidth() to set the default width of each of the table columns (you can get each of the columns with JTable's getColumn() method), as the latter approach is now deprecated. The preferred width of the column does not change if the table is reduced in size such that each column falls below its preferred width.

Text Package

Java Swing now supports the use of bi-directional text, although full support for this feature requires JDK 1.2. When the caret passes over any bi-directional text, it changes to indicate which direction the text is flowing. As you might expect, the model and view interact accordingly such that the text is processed normally in the model. The only difference is a "bias" argument that indicates how the text should be treated.

If you're familiar with how the emacs text editor works, you'll be glad to know that the default text storage mechanism is now a class called GapContent. This class provides a more simplistic approach to working with a document by providing a "gap" in the Unicode which forms a smaller buffer by which changes to the text can be made. Again, this is an underlying class which most programmers will not need to be familiar with.

Localization

As a side note, the JFileChooser, JColorChooser, and JOptionPane classes now load their strings from a separate file, which better supports an internationalization strategy. Again, this will probably have no impact for programmers.

Internet Explorer 4.0

There were several issues with Internet Explorer 4.0 that have been fixed. We won't cover them here; we encourage you to look at the CHANGES.TXT that comes with the distribution for more information.

Return to: Java Swing
 
 


O'Reilly Home | O'Reilly Bookstores | How to Order | O'Reilly Contacts
International | About O'Reilly | Affiliated Companies

© 2001, O'Reilly & Associates, Inc.