Subject: JDC Tech Tips October 5, 1999

From: [email-masked]
Date: Wed, 6 Oct 1999 03:15:37 GMT

J  D  C    T  E  C  H    T  I  P  S

                      TIPS, TECHNIQUES, AND SAMPLE CODE

WELCOME to the Java Developer Connection(sm) (JDC) Tech Tips, 
October 5, 1999. This issue covers:

         * Swing Document Locations
         * Keymaps

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

SWING DOCUMENT LOCATIONS

In Java(tm) Foundation Classes (JFC) Project Swing, a text pane
that you use to edit text is backed by a "document". For example,
a JTextPane has, by default, a DefaultStyledDocument underlying it.
The document is what actually stores text and other content
that is displayed in the pane.

Suppose that you are using a Swing document in your application, 
and you'd like to keep track of specific locations in the document. 
So you decide to use character offsets for this purpose, for
example, a location that is 47 characters into the document.

This approach does work until you edit the document. For example, 
suppose you delete the first 10 characters. You'll then need to 
update offset 47 to 37 to keep the location correct.

A better approach to track document locations is Swing's Position
interface. If a class implements the Position interface, it defines 
a location in a document. If the document changes, for instance, 
after editing, the location is updated as necessary. To see how this 
works, consider the following example:

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

    public class PosDemo {
        static Position lastpos;

        public static void main(String args[]) {
            JFrame frame = new JFrame("Position demo");
            frame.addWindowListener(new WindowAdapter() {
                public void windowClosing(WindowEvent e) {
                    System.exit(0);
                }
            });

            final JTextPane pane = new JTextPane();
            pane.setPreferredSize(new Dimension(600, 400));

            JButton setbutton = new JButton("Set Position");
            JButton gobutton = new JButton("Go To Position");

            setbutton.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    try {
                        int dot = pane.getCaret().getDot();
                        Document doc = pane.getDocument();
                        lastpos = doc.createPosition(dot);
                        pane.requestFocus();
                    }
                    catch (BadLocationException exc) {
                        System.err.println(exc);
                    }
                }
            });

            gobutton.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    if (lastpos == null) {
                        Toolkit.getDefaultToolkit().beep();
                    }
                    else {
                        int pos = lastpos.getOffset();
                        pane.getCaret().setDot(pos);
                        pane.requestFocus();
                    }
                }
            });

            JPanel buttonpanel = new JPanel();
            buttonpanel.add(setbutton);
            buttonpanel.add(gobutton);

            JPanel panel = new JPanel();
            panel.setLayout(new BorderLayout());
            panel.add("North", buttonpanel);
            panel.add("Center", pane);

            frame.getContentPane().add(panel);
            frame.pack();
            frame.setVisible(true);
        }
    }

The demo sets up a text window, along with Set Position and Go To
Position buttons. Suppose you type some text, move the text caret
somewhere in that text, and then select Set Position. At this point
you've recorded a location in the text. Then suppose that you edit
the text, adding and deleting characters before and after the
position you selected. If you then select the Go To Position button, 
the caret will move back to the original location. In other words, 
the action of the Go To Position button takes into account the 
changed offset of the location based on edits you've done.

To see what's going on here, enter a distinct pattern like "XYZ" 
into the middle of some random text. Move the caret to that pattern 
and then select Set Position. Doing it this way makes it clear that 
the pattern itself is being tracked as a location, rather than 
simply a specific character offset in the document.

These three lines in the example program do the actual location 
saving:

    int dot = pane.getCaret().getDot();
    Document doc = pane.getDocument();
    lastpos = doc.createPosition(dot);

The first line retrieves the current caret offset (dot). The second
line gets the document. And the third line creates a Position instance 
in the document from the caret offset. The process is reversed later 
in the program with the lines:

    int pos = lastpos.getOffset();
    pane.getCaret().setDot(pos);

where getOffset retrieves the updated offset.

KEYMAPS

You may have never considered the details of what happens
when you type characters into a text area. But this process is
important to understand, especially if you're trying to customize 
how an application handles input from the keyboard.

Swing text components use what are called "keymaps". A keymap maps 
keyboard keys to resulting actions. For example, the Backspace key 
deletes the character preceding the text caret. Or the Delete 
key removes the character after the caret. By contrast, simply typing 
a character such as "z" inserts that character as text content.

It's possible to override the default keymap with your own, as this
example illustrates:

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

    public class KeyMap {
        public static void main(String args[]) {
            JFrame frame = new JFrame("Keymap demo");
            frame.addWindowListener(new WindowAdapter() {
                public void windowClosing(WindowEvent e) {
                    System.exit(0);
                }
            });

            final JTextPane pane = new JTextPane();
            pane.setPreferredSize(new Dimension(600, 400));

            Keymap km = pane.getKeymap();
            KeyStroke ks = KeyStroke.getKeyStroke(
                KeyEvent.VK_Z, Event.CTRL_MASK);
            Action act = new TextAction("Ctrl-Z") {
                public void actionPerformed(ActionEvent e) {
                    pane.replaceSelection("ZZZ");
                }
            };
            km.addActionForKeyStroke(ks, act);

            JPanel panel = new JPanel();
            panel.add("Center", pane);

            frame.getContentPane().add(panel);
            frame.pack();
            frame.setVisible(true);
        }
    }

This demo overrides the default action for Ctrl-Z (which is to do
nothing). With this override in place, Ctrl-Z inserts "ZZZ" into
the text pane. In other words, a keystroke (a combination of a key 
and possibly a modifier like Ctrl) has been added to the keymap. 
And an action has been specified for the keystroke such that 
replaceSelection is called to insert text or replace selected text  
with new text.

This feature can be used in an application such as a word processor,
where the user binds arbitrary text strings to keys.

There are several interesting points to note about keymaps. One is
that a default keymap is set up for you when you use JTextPane. It
contains bindings for keystrokes such as Ctrl-X (cut selected text).
The example above changes this keymap by adding a keystroke/action
pair to it. This modification will be reflected in all text panes
in the application. So if you don't want to disturb the default
keymap, you can create your own.

If you set a keymap to null, as in:

    pane.setKeymap(null);

then keyboard input to the text pane is disabled.

There is also the concept of parent and child keymaps. If a
keystroke is not resolved in a child keymap, then it is searched for
in the parent. An application might, for example, create its own
keymap, and use the default keymap as a parent. The parent keymap 
is used to resolve keystrokes not found in the application-specific 
keymap. In the example above, Ctrl-Z might be resolved in a keymap 
that has been created in the application, with Ctrl-X resolved in 
the default parent keymap.

.  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .

- NOTE
The names on the JDC mailing list are used for internal Sun
Microsystems(tm) purposes only.  To remove your name from the list,
see Subscribe/Unsubscribe below.

- FEEDBACK
Comments?  Send your feedback on the JDC Tech Tips to:

jdc-webmaster

- SUBSCRIBE/UNSUBSCRIBE
The JDC Tech Tips are sent to you because you elected to subscribe
when you registered as a JDC member.  To unsubscribe from JDC Email,
go to the following address and enter the email address you wish to
remove from the mailing list:

http://developer.java.sun.com/unsubscribe.html

To become a JDC member and subscribe to this newsletter go to:

http://java.sun.com/jdc/

- ARCHIVES
You'll find the JDC Tech Tips archives at:

http://developer.java.sun.com/developer/TechTips/index.html

- COPYRIGHT
Copyright 1999 Sun Microsystems, Inc. All rights reserved.
901 San Antonio Road, Palo Alto, California 94303 USA.

This document is protected by copyright.  For more information, see:

http://developer.java.sun.com/developer/copyright.html

This issue of the JDC Tech Tips is written by Glen McCluskey.

JDC Tech Tips 
October 5, 1999