Tải bản đầy đủ (.pdf) (99 trang)

Java Swing phần 7 pps

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (767.72 KB, 99 trang )

Java Swing - O’Reilly

- 595 -
public JPasswordField()
Creates a new password field with zero columns.
public JPasswordField(String text)
Creates a new text field displaying the input text (using the echo character).
public JPasswordField(int columns)
Creates a new text field with the requested number of columns.
public JPasswordField(String text, int columns)
Creates a new text field with the specified number of columns, displaying the input text
(using the echo character).
public JPasswordField(Document doc, String text, int columns)
This constructor (called by all the others) creates a new text field that uses the specified
document model, displays the specified string of text (using the echo character), and
contains the requested number of columns. The echo character is set to "*" by this
constructor.
19.1.4.3 Data Protection Methods
public void cut()
public void copy()
These methods are overridden to disable cut and copy behavior in password fields. They
simply call getToolkit().beep(). If these methods were not overridden, it would be
possible for hidden passwords to be copied from password fields and pasted into nonhidden
fields.
public string getText(int offs, int len) throws BadLocationException
Defined in this class only for the purpose of being marked as deprecated. The
getPassword() method should be used instead.
19.1.4.4 Miscellaneous Methods
public boolean echoCharIsSet()
Indicates whether or not an echo character has been set. Note that a default echo character
(*) is defined, so this method always returns true unless the echo character is explicitly set


to "\u0000."
19.1.5 The JTextArea Class
The JTextArea class is modeled after AWT's TextArea, providing much of the same functionality.
Generally, it is used to allow the user to enter a textual message, or to display textual information to
the user. Figure 19.7 shows a JTextArea.
Java Swing - O’Reilly

- 596 -
Figure 19.7. JTextArea

One significant change from the AWT TextArea is that JTextArea does not directly support
scrolling. However, adding the ability to scroll the contents of a JTextArea is very simple; just add
the JTextArea to the viewport of a JScrollPane (JScrollPanes are described in more detail in
Chapter 11). JTextArea implements Scrollable, so a scroll pane can be intelligent about scrolling
it.
19.1.5.1 Properties
JTextArea
defines properties shown in Table 19.6. The document property defaults to a new
PlainDocument. AccessibleJTextArea extends the
JTextComponent.AccessibleJTextComponent class.
Table 19.6, JTextArea Properties
Property Data Type get is set bound Default Value
document* Document




PlainDocument
UIClassID* String




"TextAreaUI"
accessibleContext* AccessibleContext



AccessibleJTextArea
columns int



0
font* Font




From superclass
lineCount int



From document
lineWrap boolean




false

managingFocus* boolean


true
minimumSize* Dimension




Preferred size (if columns
or rows !=0) or
super.minimumSize
preferredScrollableViewportSize* Dimension



See comments below
preferredSize* Dimension




See comments below
rows int



0
scrollableTracksViewportWidth* boolean




see comments below
tabSize int




8
wrapStyleWord boolean




false
See also properties from the javax.swing.text.JTextComponent class (Table 19.1)
The columns attribute specifies the number of columns to be displayed by the component. For
variable-width fonts, the width of each column is based on the width of the lowercase character m.
The font property is listed here because getFont() has been overridden to revalidate the
component, allowing it to resize based on the size of the new font. lineCount provides access to
the number of lines contained by the text area's document. What constitutes a "line" is document-
dependent, but typically a line is a sequence of characters that end with a \n. This is not the same as
rows, which is simply the number of rows of text displayed by the component.
The lineWrap property indicates whether or not text should wrap around to the next line when the
end of a line is reached. This should be set to true when using a nonscrollable JTextArea
(otherwise, text which runs off the edge of the component cannot be seen) and set to false when
Java Swing - O’Reilly

- 597 -
horizontal scrolling is enabled. Remember that scrolling is enabled by placing the text area in a

JScrollPane. The wrapStyleWord property allows you to specify how the text should be broken
up if lineWrap is true. By default, wrapStyleWord is set to false, so lines are broken on
character boundaries. If set to true, lines are only broken on word boundaries. Figure 19.8 shows a
JTextArea with wrapStyleWord set to true.
Figure 19.8. JTextArea with wrapStyleWord set to true

The managingFocus property is always true. This indicates that pressing the TAB key does not
cause focus to be moved to the next component (it just inserts a tab). If you want to be able to use
the TAB key to exit the text area, you'll need to subclass JTextArea and override
isManagingFocus() to return false.
The
minimumSize property is returned as the preferredSize if either rows or columns is nonzero.
Otherwise, it is as defined in the superclass. preferredScrollableViewportSize reflects the
preferred size of the component if used in a JScrollPane. It is returned as the column width times
the number of columns and the row height times the number of rows. If either of these values is
zero, the value returned by the superclass method is used. The preferredSize property uses the
superclass implementation, but adjusts the width and height if the columns and rows properties are
set. The scrollableTracksViewportWidth property will be true if lineWrap is true. If not, its
value is determined by the superclass.
The tabSize property specifies the number of characters that a tab character should expand to.
19.1.5.2 Events
JTextArea
does not fire any new event types. It fires PropertyChangeEvents when its lineWrap,
tabSize, or wrapStyleWord properties change.

The names of the properties fired by this class do not follow the standard property naming convention.
The names used are "LineWrap," "TabSize," and "WrapStyleWord."

19.1.5.3 Constructors
public JTextArea()

Creates a default text area.
public JTextArea(Document doc)
Creates a text area using the input document.
public JTextArea(int rows, int columns)
Creates a text area with the specified number of rows and columns.
Java Swing - O’Reilly

- 598 -
public JTextArea(String text)
Creates a text area displaying the specified text.
public JTextArea(String text, int rows, int columns)
Creates a text area with the specified number of rows and columns displaying the given text.
public JTextArea(Document doc, String text, int rows, int columns)
Uses the given Document, populated with the given text to create a text area of the specified
size. All of the other constructors use this one.
19.1.5.4 Text Manipulation Methods
The following convenience methods make it easy to modify the contents of the text area's document
model.
public void append(String str)
Appends the given text to the end of the document.
public void insert(String str, int pos)
Inserts the specified text at the given position (offset from the beginning of the document).
To insert text at the beginning of the document, use a value of
0 for the second argument.
public void replaceRange(String str, int start, int end)
Uses the input text to replace a section of the document, beginning with the character at the
start position and ending with the character at the end position.
19.1.5.5 Line Transformation Methods
These methods can be used to find the character offset of a given line (see the distinction between
line and row in the Properties section earlier in the chapter) and vice-versa. Note that the first line

of the document is line 0.
public int getLineEndOffset(int line) throws BadLocationException
Returns the character offset (from the beginning of the document) which marks the end of
the specified line. This is actually the offset of the first character of the next line.
public int getLineOfOffset(int offset) throws BadLocationException
Returns the line number that contains the given character offset (from the beginning of the
document).
public int getLineStartOffset(int line) throws BadLocationException
Returns the character offset (from the beginning of the document) that marks the beginning
of the specified line.
Java Swing - O’Reilly

- 599 -
The following example shows how these three methods work:
// OffsetTest.java





//
import javax.swing.*;
import javax.swing.text.*;

public class OffsetTest {
public static void main(String[] args) {

// Create a JTextField with three lines of text
JTextArea ta = new JTextArea();
ta.setLineWrap(true);

ta.append("The first line.\n");
ta.append("Line Two!\n");
ta.append("This is the 3rd line of the document.");

// Print some results . . .
try {
System.out.println(ta.getLineEndOffset(0) + " (end of line 0)");
System.out.println(ta.getLineStartOffset(1) + " (start of line 1)");
System.out.println(ta.getLineOfOffset(20) +
" (line containing position 20)");

int theEnd = ta.getLineEndOffset(2);
System.out.println(theEnd + " (end of last line)");
System.out.println(ta.getText(ta.getLineEndOffset(2), 2));
}
catch (BadLocationException ex) { System.out.println("BAD!"); }

// Layout . . .
JFrame f = new JFrame();
f.addWindowListener(new BasicWindowMonitor());
f.setContentPane(ta);
f.setSize(150, 150);
f.setVisible(true);
}
}
When run, this little program produces the following output, along with the frame shown in Figure
19.9. Note that the `\n' counts as a character in the document.
Figure 19.9. OffsetTest frame

16 (end of line 0)

16 (start of line 1)
1 (line containing position 20)
64 (end of last line)
BAD!
Java Swing - O’Reilly

- 600 -
The last line of output shows that getLineEndOffset() returns an index of the character after the
last character in the specified line. When called on the last line, this offset is not a valid document
offset.
19.1.5.6 Protected Methods
protected Document createDefaultModel()
Creates a default model object to be used if one is not provided as a constructor argument.
The default object created by JTextArea is a PlainDocument.
protected int getColumnWidth()
Returns the width (in pixels) of a single column. The default implementation returns the
width of the character m in the current font. It's important to realize that unless you are using
a fixed-width font (e.g., Courier), this method is only useful for determining a maximum
column width.
protected int getRowHeight()
Returns the height (in pixels) of each row. This is defined by the height of the current font.
protected String paramString()
Returns the parameter string used to represent this text area as a string. This is the result of
super.paramString() with the number of rows and columns in the component appended
to the string.
19.1.5.7 Understanding JTextArea Sizing
The following code produces a result you might not expect:
// UnstableTA.java






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

public class UnstableTA {
public static void main(String[] args) {
JTextArea area = new JTextArea(3, 10);

JFrame f = new JFrame();
f.addWindowListener(new BasicWindowMonitor());
f.getContentPane().setLayout(new FlowLayout());
f.getContentPane().add(area);
f.setSize(200, 110);
f.setVisible(true);
}
}
Java Swing - O’Reilly

- 601 -
At first glance, this looks fine.
[3]
However, things start getting a little strange if you enter text into
the area. Figure 19.10 shows the display when it's first displayed and then after adding some data to
the text area.
[3]
Prior to the Swing 1.0.2 release, this example would have produced a tiny 1x1 character text area.
Figure 19.10. Unstable JTextArea—size changes after adding data


The text area expanded both horizontally and vertically to accommodate the text we entered.
Chances are this is not the behavior you'd like. Fortunately, there are several strategies we can use
to get more usable results. They include:
• Place the JTextArea in a JScrollPane.
• Explicitly specify the preferredSize of the JTextArea.
• Use a layout manager that doesn't use the preferredSize of its components.
The first strategy is useful when you want to fix the size of the displayed area without restricting the
total amount of text the user can enter. A simple change to our previous example accomplishes this
goal. Just replace the old add() call with:
f.getContentPane().add(new JScrollPane(area));
The second strategy gives you complete control over the size of the text area. If you just want the
size to reflect the dimensions you supplied in the constructor, you can use the
preferredScrollableViewportSize , which uses the rows and columns you specified, and add in
the text area's insets. Adding the following lines to our example is one way to make this change:
Dimension d = area.getPreferredScrollableViewportSize();
Insets in = area.getInsets();
d.width = d.width + in.left + in.right;
d.height = d.height + in.top + in.bottom;
area.setPreferredSize(d);
The last strategy allows the text area to fill the available space. Note that using this strategy does
not tie the size of the text area to the specified number of rows and columns. The
JTextArea grows
to fill the space available in the current layout. To see an example of this strategy, change the
setLayout() and add() calls from the original example to this:
f.getContentPane().setLayout(new BorderLayout());
f.getContentPane().add(area, BorderLayout.CENTER);
Another common strategy is to combine the first and third options to create a scrollable text area
that fills all available space.
Java Swing - O’Reilly


- 602 -
19.1.6 The JEditorPane Class
JEditorPane is an extension of JTextComponent capable of displaying various types of content,
such as HTML and RTF. It is not intended to be used as a full-featured web browser, but can be
used to view simple HTML and is ideal for integrating online help into Java applications.
JEditorPanes work closely with EditorKit objects which are plugged into the editor pane to
customize it for a particular content type. Without an EditorKit telling it how to work, a
JEditorPane can't function. In this chapter, we are going to ignore this complexity and just look at
how we might use a JEditorPane as a very simple web browser. We'll get into the details of
JEditorPane and EditorKit in Chapter 24.
Figure 19.11. JEditorPane showing an HTML page

Figure 19.11 shows the JEditorPane in action, displaying a portion of the old style javadoc for the
JEditorPane class. Here's the code:
// HTMLExample.java










//
import javax.swing.*;
import javax.swing.event.*;
import java.io.*;


public class HTMLExample {
public static void main(String[] args) {
JEditorPane pane = null;
try {
Java Swing - O’Reilly

- 603 -
pane = new JEditorPane(args[0]);
}
catch (IOException ex) {
ex.printStackTrace(System.err);
System.exit(1);
}
pane.setEditable(false);

// Add a hyperlink listener
final JEditorPane finalPane = pane;
pane.addHyperlinkListener(new HyperlinkListener() {
public void hyperlinkUpdate(HyperlinkEvent ev) {
try {
if (ev.getEventType() == HyperlinkEvent.EventType.ACTIVATED)
finalPane.setPage(ev.getURL());
} catch (IOException ex) { ex.printStackTrace(System.err); }
}
});

JFrame frame = new JFrame();
frame.addWindowListener(new BasicWindowMonitor());
frame.setContentPane(new JScrollPane(pane));

frame.setSize(350,400);
frame.setVisible(true);
}
}
We've created a very minimal HTML browser.
[4]
In a real application, you'd want to do things like
change the cursor while new pages are being loaded and handle exceptions more elegantly. The
anonymous inner class in this example shows a quick way to enable hyperlinks when viewing text
in a JEditorPane. We'll look at the classes and methods used here in the next few pages.
[4]
This simple browser will not handle all HTML pages. The Swing html package is still being enhanced to provide better HTML support. However, significant
progress has been made since earlier releases.
19.1.6.1 Properties
Table 19.7 shows the properties defined by JEditorPane. The accessibleContext property
depends on the type of EditorKit in use. If an HTMLEditorKit is installed, a special
AccessibleJEditorPaneHTML object will be used. Otherwise, its superclass,
AccessibleJEditorPane is used. AccessibleJEditorPane extends the
JTextComponent.AccessibleJTextComponent class.
Table 19.7, JEditorPane Properties
Property Data Type get is set bound Default Value
UIClassID* String



"EditorPaneUI"
accessibleContext* AccessibleContext




AccessibleJEditorPane or
AccessibleJEditorPaneHTML
contentType String



From editorKit
editorKit EditorKit




null
managingFocus* boolean


true
page URL



null
scrollableTracksViewportWidth* boolean



true
Content Type Class
See also properties from the javax.swing.text.JTextComponent class (Table 19.1)
Java Swing - O’Reilly


- 604 -
The contentType property reflects the type of content displayed by the editor. This value is taken
from the installed EditorKit and typically has values such as "text/plain," "text/html," and
"text/rtf." The editorKit supplies everything needed to work with a particular content type.
EditorKit and its default implementations are described in detail in Chapter 24.
ManagingFocus is set to true for JEditorPane, reflecting the fact that using the TAB key will not
cause focus to move to the next component. The page property specifies the URL of the current
page being displayed. The scrollableTracksViewportWidth property is set to true in this class.
19.1.6.2 Events
JEditorPane
s fire a new type of event called a HyperlinkEvent. Typically, this is fired when the
user clicks on a hyperlink in the currently displayed document; the program normally responds by
loading a new page. To support this new event type, a new event class and listener interface are
available in the javax.swing.event package. These are described briefly at the end of this section.
As you'd expect, the following methods are provided for working with these events.
public synchronized void addHyperlinkListener(HyperlinkListener listener)
public synchronized void removeHyperlinkListener(HyperlinkListener listener)
public void fireHyperlinkUpdate(HyperlinkEvent e)
JEditorPane objects also fire PropertyChangeEvents when the editorKit property is
changed.
19.1.6.3 Constructors
The following constructors are provided. Note that the last two may throw an IOException if they
are unable to load the specified URL.
public JEditorPane()
Creates an empty pane.
public JEditorPane(String url) throws IOException
public JEditorPane(URL initialPage) throws IOException
Create a pane displaying the specified URL. contentType and editorKit are set based on
the content type of the

URLConnection created from the given URL. Because these
constructors attempt to open a URL, they may throw an IOException if the URL cannot be
found.
19.1.6.4 EditorKit Methods
The following methods are available for managing EditorKits. You won't need to use any of these
methods if you're not defining your own
EditorKits for working with various content types.
public EditorKit getEditorKitForContentType(String type)
Returns an
EditorKit for the given content type. An attempt is made to create the
appropriate EditorKit if one has not already been set (via a call to
Java Swing - O’Reilly

- 605 -
setEditorKitForContentType()). If the appropriate EditorKit cannot be created, a
DefaultEditorKit is returned.
public void setEditorKitForContentType(String type, EditorKit k)
Explicitly sets the EditorKit to be used for a given content type.
public static EditorKit createEditorKitForContentType(String type)
Attempts to create a new EditorKit instance for the given content type. In order for this
method to return a non-null object, the content type must have been associated with an
editor kit class name by a call to register-EditorKitForContentType().
public static void registerEditorKitForContentType(String type, String classname)
Called to associate a content type to an editor kit class name. It is called three times in the
JEditorPane initializer block. These calls define the mappings shown in Table 19.8.
Table 19.8, Default Content-Type Mappings
Content Type Class
application/rtf javax.swing.text.rtf.RTFEditorKit
text/plain javax.swing.JEditorPane.PlainEditorKit
[5]


text/html javax.swing.text.html.HTMLEditorKit
text/rtf javax.swing.text.rtf.RTFEditorKit
[5]
A package private subclass of DefaultEditorKit.
protected EditorKit createDefaultEditorKit()
Called to create a default editor kit. It returns a new instance of a package private subclass of
DefaultEditorKit called PlainEditorKit. This method is not used typically, as it is only
called by getEditorKit() if no editor kit has been set for the pane.
19.1.6.5 Miscellaneous Methods
public void setPage(String url) throws IOException
A convenience method used to set the current page, given a URL string. An
IOException
will be thrown if the given URL cannot be loaded.
protected void scrollToReference(String reference)
Used by
setPage() to scroll the display to the specified reference within the current
document. This provides support for URLs containing references like JComponent
updateUI
. Note that this method only works with HTML documents.
19.1.7 The HyperlinkListener Interface
This interface (found in javax.swing.event) defines a single method, used to respond to
hyperlink activations.
public abstract void hyperlinkUpdate(HyperlinkEvent e)
Java Swing - O’Reilly

- 606 -
Called to indicate that a hyperlink request has been made. Typical implementations of this
method obtain the new URL from the event and call setPage() on the associated
JEditorPane. See the JEditorPane example earlier in the chapter to see how this method

can be used.
19.1.8 The HyperlinkEvent Class
This event class (found in javax.swing.event) describes a hyperlink request.
19.1.8.1 Properties
HyperlinkEvent
defines the properties shown in Table 19.9. The description property allows a
description of the link (typically the URL text string) to be defined. This is useful when a URL can't
be formed from the text, meaning that the URL will be null. The eventType property defines the
type of event that has occurred. The possible values are described below. The URL property reflects
the URL the event refers to.
Table 19.9, HyperlinkEvent Properties
Property Data Type get is set bound Default Value
description string



null
eventType HyperlinkEvent.EventType



From constructor
URL URL



From constructor
See also the java.util.EventObject class (not covered in this book).
19.1.8.2 Constructors
public HyperlinkEvent(Object source, HyperlinkEvent.EventType type, URL u)

public HyperlinkEvent(Object source, HyperlinkEvent.EventType type, URL u, string desc)
Creates a new event with the specified arguments. The value of the type parameter is taken
from the constant values defined in the HyperlinkEvent.EventType class: ENTERED,
EXITED, and ACTIVATED. The only value currently used is ACTIVATED. The desc parameter
is optional.
19.1.8.3 Inner Classes
public static final class EventType
This simple inner class just holds a single String value and defines three constants of type
HyperlinkEvent.EventType. These constants are ENTERED, EXITED, and ACTIVATED.
Currently, only ACTIVATED is fired by the HTMLEditorKit. It is used to indicate that a
hyperlink has been activated (clicked) by the user. The other values could be used to change
the cursor type when the cursor is moved over (or away from) a hyperlink.
19.1.9 The JTextPane Class
JTextPane is a complex extension of JEditorPane that provides functionality typical of a basic
word processor, including features such as multicolored text, multiple fonts and text styles, image
embedding, and more. There's a great deal to discuss with respect to JTextPane and the many
classes it interacts with.
Java Swing - O’Reilly

- 607 -
In this chapter, we'll just touch the surface, showing how you can do a few simple things using a
JTextPane that you couldn't easily do with the AWT or JTextArea. Since most of the properties
and methods defined in JTextPane use classes and interfaces that we've not yet defined, we'll omit
all of the details of JTextPane properties and methods from this section. For a more complete
discussion of JTextPane, refer to Chapter 21.
19.1.9.1 Using the JTextPane
In this example, we'll show how to use a JTextPane as a more powerful text window for displaying
diagnostic information. In AWT, or with a Swing JTextArea, you are limited to a single font, style,
and foreground color. Using a
JTextPane, we can easily add text using a variety of formats. This is

done using
AttributeSets to define the features of the displayed text. For the purpose of this
example, it's enough to understand that an AttributeSet (we'll actually use SimpleAttributeSet,
a mutable extension of AttributeSet) defines a group of attributes and that the values of these
attributes can be set by calling various static methods on the StyleConstants class.
Here's a simple utility for displaying diagnostic messages to a user. Three types of messages
(informational, warning, and error) are displayed using different colors and text styles.
// Diagnostic.java








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

public class Diagnostic {
// Create new new Diagnostic display using a black JTextPane
public Diagnostic() {
pane = new JTextPane();
pane.setBackground(Color.black);
pane.setEditable(false);
}

// Show an informational message (green, plain text)

public void showInfo(String msg) {
SimpleAttributeSet attrs = new SimpleAttributeSet();
StyleConstants.setForeground(attrs, Color.green);
showMsg(msg, attrs);
}

// Show a warning message (yellow, italic text)
public void showWarning(String msg) {
SimpleAttributeSet attrs = new SimpleAttributeSet();
StyleConstants.setForeground(attrs, Color.yellow);
StyleConstants.setItalic(attrs, true);
showMsg(msg, attrs);
}

// Show an error message (red, bold/italic text)
public void showError(String msg) {
SimpleAttributeSet attrs = new SimpleAttributeSet();
Java Swing - O’Reilly

- 608 -
StyleConstants.setForeground(attrs, Color.red);
StyleConstants.setItalic(attrs, true);
StyleConstants.setBold(attrs, true);
showMsg(msg, attrs);
}

// Return the visual component to be displayed
public Component getComponent() { return pane; }

// Show a text message using the specified AttributeSet

protected void showMsg(String msg, AttributeSet attrs) {
Document doc = pane.getDocument();
msg += "\n";
try {
doc.insertString(doc.getLength(), msg, attrs);
} catch (BadLocationException ex) { ex.printStackTrace(); }
}

private JTextPane pane;

// A sample test program
public static void main(String[] args) {
Diagnostic diag = new Diagnostic();
JFrame f = new JFrame();
f.addWindowListener(new BasicWindowMonitor());
f.getContentPane().add(diag.getComponent());
f.setSize(300,200);
f.setVisible(true);

// Display a few messages
diag.showInfo("System normal");
diag.showWarning("Disk space low");
diag.showError("Out of memory");
diag.showError("Program performed an illegal operation");
diag.showInfo("System normal");
}
}
Over the course of the next two chapters, we'll explain all of the strange-looking methods used in
this example. You might want to refer back to this example as you read those chapters. Figure 19.12


shows the display generated by this class. Unfortunately, we can't show you the colors.
Figure 19.12. JTextPane diagnostic example


19.2 More to Come
In this chapter, we've shown how easy it is to do simple things with the Swing text framework.
However, if you want to do more than we've demonstrated in this chapter, Swing has a lot to offer.
Java Swing - O’Reilly

- 609 -
Over the next five chapters, we'll examine every class and interface in the Swing text package,
building many interesting and powerful sample programs as we go.
The order of the next five chapters is fairly important. You should read the next two chapters in
sequence to gain an understanding of the interfaces and classes used to describe the document
model. The chapter discussing Text Views, Chapter 23, is fairly advanced and will be optional for
many readers. Most of the material covered in the last chapter will make sense even if you skip the
Views chapter.
Chapter 20. Document Model and Events
In the previous chapter, we introduced the Swing text components, ignoring many of the details in
order to simplify the introduction. In this chapter,
[1]
we'll give a detailed explanation of the many
classes and interfaces that make up the basic text model. Additionally, we'll look at the classes and
interfaces involved in delivering events to provide notification of document model changes.
[1]
Certain sections of this chapter refer to Swing's undo facility, and assume that you already understand the basics of it. If you've not yet read Chapter 18, we
recommend that you read at least the introductory section of it before continuing here.
20.1 The Document Model
Before getting into the details of the classes and interfaces that make up the Swing text model, we'll
give a brief overview of the top-level interfaces that serve as the model's key abstractions. These

interfaces, and the relationships between them, are shown in Figure 20.1.
Figure 20.1. High-level Document class diagram

The first—and arguably most important—interface to look at is called Document. The model
representation of any text component (even a simple text field) in Swing is defined by this interface.
A Document is little more than an arbitrary collection of text (though Swing's implementations of
the interface provide much more).
Document objects are partitioned by Element s that describe the structural pieces of a Document,
such as paragraphs or sections of specially formatted text (e.g., italics). Elements are made up of
child
Elements, forming a tree with a "root" Element as the base and arbitrary levels of child
Elements below it. Each Document defines one or more Elements as its root Elements, from which
the entire structure of the Document can be determined. Typically, only a single root Element is
defined. However, more complex document types might contain multiple root elements, to support
Java Swing - O’Reilly

- 610 -
multiple ways of structuring the same document content. The key thing to understand is that the
Document contains the data; the Elements just give it structure.
Each Element has an associated AttributeSet, which defines things such as the font or text style
to be applied to the Element. The attributes themselves may be any arbitrary Object (the value),
indexed by another Object (the key).
Arbitrary positions in the document may be represented using Position objects. Position
provides the ability to track a given location in the document as its absolute offsets change. For
example, if you created a position that pointed to the beginning of a given sentence, that position
would always refer to the beginning of the sentence, no matter what other changes were made to the
document.
The
Document, Element, Position, and AttributeSet interfaces are all described in detail later in
this section.

20.1.1 The Document Interface
We've described the Document interface a bit already. Now let's take a look at its properties,
constructors, and methods.
20.1.1.1 Properties
Table 20.1 shows the properties defined by the Document interface. The properties defined by the
Document interface are fairly straightforward. A property called defaultRootElement is provided
to access the Element at the root of the default Element structure hierarchy. In addition, the
rootElements property specifies an array of all root elements (including the default) available in
the Document. Most Document types only define a single root.
Table 20.1, Document Properties
Property Data Type get is set bound Default Value
defaultRootElement Element



endPosition Position



length int



rootElements Element[]



startPosition Position




Two Positions, startPosition and endPosition, are provided. These properties always
reference the beginning and end of the document, regardless of changes made to it throughout its
lifetime. Finally, the length property specifies the total number of characters
[2]
in the Document.
[2]
We'll see in the next chapter that Documents are not limited to holding text. In most cases, length refers to the number of characters in the
Document, but embedded images and components may also be included in this count, each being stored as a single character. Newline characters count as
well, so a document containing "Hello\nWorld" would have a length of 11.
20.1.1.2 Events
Implementations of the Document interface fire DocumentEvents and UndoableEditEvents to
indicate changes that have been made to the Document's contents. UndoableEditEvent and
UndoableEditListener were covered in Chapter 18. The corresponding DocumentEvent classes
will be discussed at the end of this chapter.
Java Swing - O’Reilly

- 611 -
Document defines the following standard methods for managing event listeners.
public abstract void addDocumentListener(DocumentListener listener)
public abstract void removeDocumentListener(DocumentListener listener)
public abstract void addUndoableEditListener(UndoableEditListener listener)
public abstract void removeUndoableEditListener(UndoableEditListener listener)
20.1.1.3 Constants
Document
defines the two constants, shown in Table 20.2, which may be used as keys when calling
getProperty() or putProperty().
Table 20.2, Document Constants
Constant Type Description
StreamDescriptionProperty String

The property name used to describe any information known about the
stream from which the Document was initialized (if it was initialized
from a stream)
TitleProperty String
The property name used to store the Document's name, if it has one
20.1.1.4 Text Manipulation Methods
These methods manipulate the contents of the document. They can all throw a
BadLocationException. This is an exception (defined later in the chapter) used throughout the text
package to indicate that an attempt has been made to reference a document offset that does not
exist. For example, attempting to insert text at offset 200 of a 100-character document would result
in a BadLocation-Exception.
public abstract String getText(int offset, int length) throws BadLocationException
Retrieves the text at the specified location in the Document. The returned string will be of
the requested length, beginning at the specified offset.
public abstract void getText(int offset, int length, Segment txt) throws
BadLocationException
Retrieves the text at the specified location in the
Document. The returned text will be of the
requested length, beginning at the specified offset, and is returned in the input
Segment
(defined later in the chapter) object.
public abstract void insertString(int offset, String str, AttributeSet a) throws
BadLocationException
Inserts the specified string at the given offset in the Document. The new String should be
described by the given AttributeSet, and the Document should update its Element
structures as necessary to reflect this.
public abstract void remove(int offs, int len) throws BadLocationException
Removes the specified section of text from the Document. The Document should update its
Element structures to reflect the change.
20.1.1.5 Other Methods

Java Swing - O’Reilly

- 612 -
public abstract Position createPosition(int offs) throws BadLocationException
Creates a Position object used to track the contents of the Document at the specified offset.
Section 20.1.7, later in this chapter, contains an example showing how this might be used.
public abstract Object getProperty(Object key)
public abstract void putProperty(Object key, Object value)
Retrieves and inserts (respectively) arbitrary properties associated with the document. These
properties can be used to store things such as the document title, author, etc.
public abstract void render(Runnable r)
Executes the given Runnable, guaranteeing that the contents of the model will not be
changed while the Runnable is running. The input Runnable may not modify the model.
This method allows the Document to be painted without concerns about its contents
changing during the painting process. It is called by the TextUI's paint() method.
20.1.2 The Element Interface
Element is an interface used to describe an arbitrary portion of a document. However, it's important
to realize that Elements do not actually contain a portion of the document, they just define a way of
structuring it. Because of this, a Document may be described by multiple sets of Elements, each
defining a different logical structure.
Each Element is described by an AttributeSet, which defines things such as the font style or
color used in the Element. Note that this means that all Document content described by a given
Element will have the same set of attributes. Elements may contain other Elements, allowing a
document to be described by an arbitrary tree structure. Figure 20.2 shows a purely hypothetical
example of the structure of a series of Elements. In this diagram, there is a single "document"
element made up of two "paragraph" elements, each of which is, in turn, made up of a pair of
"sentence" elements. Finally, one of the sentences is further split into two more elements, one for a
plain text area and the other for a section of bold text. Remember, this is only an example of how
elements fit together; the element types here do not map directly to anything provided by Swing.
Figure 20.2. Sample Element structure


Another way to understand the concept of an
Element is to look at mark-up languages such as
HTML or XML. Documents defined by such mark-up languages can be easily represented using the
Java Swing - O’Reilly

- 613 -
Swing Document/Element model. Each "element" from the mark-up language simply maps to an
Element in the document model. Consider the following short HTML document.
<HTML>
<H1>
The Document Model
</H1>
<H2>
The Document Interface
</H2>
<H2>
The Element Interface
</H2>
<P>
Element is an interface used to describe an arbitrary portion of a document.
<BR>
Each Element is described by an AttributeSet.
</P>
</HTML>
A possible element representation of this document is shown in Figure 20.3. Attributes that define
the appearance of different elements types (such as H1, H2, P) are stored in the branch elements,
while each line of text within an element is stored in a leaf element.
Figure 20.3. Sample Element structure for a simple HTML document


20.1.2.1 Properties
The Element interface defines the properties
[3]
shown in Table 20.3. The attributes property
defines a set of attributes, which apply to the text represented by the Element. The AttributeSet
interface is described in more detail in the next section. For now, it's enough to understand that any
arbitrary information relevant to the Element can be stored by the attributes property.
[3]
elementIndex could technically be considered an indexed property. However, the argument passed to getElementIndex() is actually
an offset into the Document, not a simple array index.
Table 20.3, Element Properties
Property Data Type get is set bound Default Value
attributes AttributeSet



document Document



element (indexed) Element



elementCount int



Java Swing - O’Reilly


- 614 -
endOffset int



leaf boolean





name String



parentElement Element



startOffset int



The document property provides access to the Document this Element describes while
parentElement defines the Element that contains this Element (if this is not a root Element). The
element property is an indexed property containing the Element's child elements, while
elementCount specifies the number of children (possibly zero) the Element contains. The child
elements are always kept in order, so you are assured that element[0] will reference offsets in the
document that appear before those referenced by element[1].
The startOffset and endOffset properties specify the offsets from the beginning of the

document that the Element covers. These values change as data is added to or before the Element.
The leaf property indicates whether or not this is a leaf Element (one that has no children), and the
name specifies an arbitrary name for the Element.
20.1.2.2 Miscellaneous Method
public abstract int getElementIndex(int offset)
Returns the index of the child element closest to the given offset (character offset from the
beginning of the Document). This index can be used as input to the getElement() method
to retrieve the appropriate child.
20.1.3 The ElementIterator Class
This class, first introduced in the JDK1.2 beta4 release, provides a strategy for iterating over some
or all of the elements that make up a document. It's used by the AbstractWriter class (see Chapter
24), but could be used for other purposes as well.
ElementIterator orders the elements depth-first, meaning that the root element (or whatever
element you specify as a starting point) will be the first element returned. The root's first child is
then returned, followed by the first child of that child, and so on until a leaf is reached. Once a leaf
is reached, the next leaf is returned, and so on, until the last leaf of a child is reached, at which point
the next sibling of that child is returned (and continues with its descendants).
Common use of this class would look like this:
ElementIterator it = new ElementIterator(myDocument);
Element e = it.first();
do {
// do something with "e"
e = it.next();
} while (e != null);
20.1.3.1 Constructors
public ElementIterator(Document document)
Creates an iterator that will begin at
document's default root element.
Java Swing - O’Reilly


- 615 -
public ElementIterator(Element root)
Creates an iterator that will begin at root.
20.1.3.2 Methods
ElementIterator
defines the following public methods.
public synchronized Object clone()
Returns a copy of the iterator. The clone is set at the same point in the iteration as the
original iterator. You must call current(), first(), or next() before calling clone(), or
a NullPointerException will be thrown.
public Element current()
Returns the current element in the iteration.
public int depth()
Returns the current depth of the internal stack used by the iterator. This indicates the number
of non-leaf elements at or above the level of the current element.
public Element first()
Resets the iterator to the first element and returns it.
public Element next()
Returns the current element and moves the iterator to the next element.
public Element previous()
Returns the previous element. It does not change the state of the iterator (i.e., calling
next();previous();next();previous() would actually move you forward two
elements).
20.1.4 The AttributeSet Interface
AttributeSet is an interface used to collect an arbitrary set of key/value pairs. An attribute value
is any arbitrary Object, accessible by an attribute key (another Object). Typically, as we'll see in
much more detail in the next chapter, attributes are used to define things such as the font or color of
the text described by the Elements this AttributeSet is applied to.
The interface provides methods for accessing the attributes, but not for setting them. In the next
section, we'll introduce the MutableAttributeSet interface, which provides methods for settings

attributes.
An AttributeSet may have a resolving parent which is another AttributeSet. The relationship
between an AttributeSet and its resolving parent is much like the relationship between a Java
class and its superclass: attributes not found in a given AttributeSet will be searched for in the
Java Swing - O’Reilly

- 616 -
set's resolving parent, just as method implementations not found in a class are searched for in its
superclass. This can go on indefinitely until the attribute is found or a set with no parent is reached.
20.1.4.1 Properties
Table 20.4 shows the properties defined by the AttributeSet interface. The attributeCount
property simply specifies the number of attributes contained by the set. attributeNames is an
Enumeration containing the attribute keys (not necessarily names, since keys are of type Object).
Note that this property does not contain the attributes defined in the set's parent. resolveParent
represents the parent set used to resolve attribute keys not found in the current set. This property
will be null if there is no parent set.
Table 20.4, AttributeSet Properties
Property Data Type get is set bound Default Value
attributeCount int



attributeNames Enumeration



resolveParent AttributeSet




20.1.4.2 Methods
public abstract boolean containsAttribute(Object name, Object value)
Returns true if the set (or its resolving parent) contains the given attribute and its value
matches that of the second given object. Equality is tested using the equals() method.
public abstract boolean containsAttributes(AttributeSet attributes)
Returns true if the set (or its resolving parent) contains values for all of the attributes in the
AttributeSet and all of these values are equal. Equality is tested using the equals()
method on each attribute.
public abstract AttributeSet copyAttributes()
Creates and returns a copy of the AttributeSet.
public abstract Object getAttribute(Object key)
Searches for an attribute whose key matches the given object. If the attribute is not found
locally, the set's resolving parent is searched (if it exists). This process repeats until the
attribute is found. If the value cannot be found, null is returned.
public abstract Enumeration getAttributeNames()
Returns an Enumeration of objects representing the keys of the attributes in the set. Only
the attributes defined locally in the set are returned.
public abstract boolean isDefined(Object attrName)
Returns true if the given attribute key is defined in the set. The resolving parent is not
checked.
public abstract boolean isEqual(AttributeSet attr)
Java Swing - O’Reilly

- 617 -
Returns true if the set's attributes and values exactly match those of the given set. Equality
is tested using the equals() method on each attribute.
20.1.4.3 Inner Interfaces
AttributeSet
contains four inner-interfaces. Each of these is entirely empty, serving only to
identify attribute keys as belonging to a particular category. There is no requirement for attribute

keys to implement these interfaces, but we'll see in the next chapter that Swing provides several pre-
defined attribute keys that do.
public interface CharacterAttribute
public interface ColorAttribute
public interface FontAttribute
public interface ParagraphAttribute
20.1.5 The MutableAttributeSet Interface
MutableAttributeSet is an extension of the AttributeSet interface that adds methods for
modifying (as opposed to just accessing) the attributes in a set.
20.1.5.1 Properties
MutableAttributeSet
does not define any properties. It inherits those defined in its super-
interface AttributeSet. It does add a setResolveParent() method for the resolveParent
attribute, since this was a read-only attribute in the AttributeSet interface. (See Table 20.5.)
Table 20.5, MutableAttributeSet Properties
Property Data Type get is set bound Default Value
resolveParent* AttributeSet






See also properties from the AttributeSet interface (Table 20.4.
20.1.5.2 Methods
public abstract void addAttribute(Object name, Object value)
Adds an attribute with the specified key and value.
public abstract void addAttributes(AttributeSet attributes)
Adds all of the attributes in the given set.
public abstract void removeAttribute(Object name)

Removes the attribute with the specified key.
public abstract void removeAttributes(AttributeSet attributes)
Removes all attributes that match those in the given set in both key and value. Equality is
defined by the
equals() method.
public abstract void removeAttributes(Enumeration names)
Java Swing - O’Reilly

- 618 -
Removes all attributes that have keys that match those found in the Enumeration.
20.1.6 The SimpleAttributeSet Class
Swing provides a simple implementation of the MutableAttributeSet interface, which maintains
attributes using a Hashtable. The class structure is shown in Figure 20.4.
Figure 20.4. The SimpleAttributeSet class hierarchy

20.1.6.1 Properties
Table 20.6 shows the default property values defined by SimpleAttributeSet. A newly created
SimpleAttributeSet contains an empty Hashtable. The resolveParent (when set) is stored in
the table just like any other attribute, using the attribute key StyleConstants.ResolveParent.
This means that the attributeCount property will include this attribute and that attributeNames
will include this object. The empty property is false if the set has any attributes, even if the only
attribute in the set is the resolveParent.
Table 20.6, SimpleAttributeSet Properties
Property Data Type get is set bound Default Value
attributeCount* int



0
attributeNames* Enumeration




empty
empty* boolean



true
resolveParent* AttributeSet






null
20.1.6.2 Constant
SimpleAttributeSet
defines the constant shown in Table 20.7.
Table 20.7, SimpleAttributeSet Constant
Constant Type Description
EMPTY AttributeSet
An empty SimpleAttributeSet
20.1.6.3 Constructors
public SimpleAttributeSet()
Creates a new set containing no attributes.
public SimpleAttributeSet(AttributeSet source)
Creates a set containing the attributes and values from the given set. It does not use the set
as a resolving parent; it just copies the set's contents.

Java Swing - O’Reilly

- 619 -
20.1.6.4 Add/Remove Methods
public void addAttribute(Object name, Object value)
Adds the specified value for the specified name. If the key already exists, its value is
changed to the new value. Note that the use of a Hashtable as the attribute storage
mechanism prohibits an attribute from having a value of null (Hashtable.put() would
throw a NullPointerException).
public void addAttributes(AttributeSet attributes)
Iterates over the given set of attributes, adding each one to the set using addAttribute().
public void removeAttribute(Object name)
Removes the specified attribute from the set. If the given key is not found, this method does
nothing. It does not attempt to find the attribute in the resolving parent.
public void removeAttributes(AttributeSet attributes)
Removes each element found in the given set from the current set. Only attributes whose
key and value match will be removed. Equality is defined by the equals() method. This
method does not attempt to remove attributes from the resolving parent.
public void removeAttributes(Enumeration names)
Removes each of the attributes in the given Enumeration from the set. The Enumeration
should contain attribute keys to be removed. Equality is defined by the equals() method.
This method does not attempt to remove attributes from the resolving parent.
20.1.6.5 Query Methods
public boolean containsAttribute(Object name, Object value)
Checks whether the specified attribute exists in the set and has a value matching the input.
Equality is defined by the equals() method. This method searches the resolving parent if
the attribute is not found locally.
public boolean containsAttributes(AttributeSet attributes)
Checks whether every attribute in the given set is found in the current set. This method only
returns

true if every attribute is found with a matching value. It searches the resolving
parent for attributes not found locally. Equality is defined by the equals() method.
public Object getAttribute(Object name)
Returns the value for the named attribute. If the attribute is not found, the resolving parent
(if one has been set) is searched. If the attribute is not found up the parent chain, null is
returned.
public boolean isDefined(Object attrName)

Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Tải bản đầy đủ ngay
×