23.2 Overview of the Editor Kits


The following sections provide an overview of the various editor kits. With minor variations, this information applies to all editor kits.

23.2.1 The EditorKit Class

The EditorKit class is the abstract base class for all editor kits. It has a number of methods that define the model (e.g., createDefaultDocument( )), the view (e.g., getViewFactory( )), the capabilities (getActions( )), and the I/O strategy (read( ) and write( )) for a given type of document content. Figure 23-2 shows the EditorKit class and the many classes and interfaces it interacts with.

Figure 23-2. EditorKit class diagram
figs/swng2.2302.gif

This figure shows several important things about the EditorKit class. First, each EditorKit instance is typically associated with a single JEditorPane (though in some cases, it doesn't care). The EditorKit defines how to create a default Document as well as how to read and write the Document to a stream. In addition, each EditorKit may define a ViewFactory responsible for creating View objects for each Element in the Document. Finally, the diagram shows that an EditorKit may define a set of Actions that it supports.

The other classes shown in the diagram are the subclasses of EditorKit and AbstractAction. We'll look at each of these classes, as well as some inner classes not shown on the diagram, throughout this chapter.

23.2.1.1 Properties

EditorKit defines the properties shown in Table 23-3. The actions property defines the set of actions that can be used on a text component, which uses a model and a view produced by this EditorKit. contentType indicates the MIME type of the data that this kit supports. The viewFactory property is the ViewFactory object used to create View objects for the Elements of the document type produced by the kit. The accessors for all three of these properties are abstract.

Table 23-3. EditorKit properties

Property

Data type

get

is

set

Default value

actions

Action[]

·

   

Abstract

contentType

String

·

   

Abstract

viewFactory

ViewFactory

·

   

Abstract

23.2.1.2 Abstract methods

The following methods must be defined by implementations of this class:

public abstract Object clone( )

Return a copy of the editor kit. EditorKit implements Cloneable so that new instances can be created quickly.

public abstract Caret createCaret( )

Create a new Caret to be used with the JEditorPane. However, this method is currently not called anywhere within Swing Carets are initially installed via the createCaret( ) method in BasicTextUI and can be changed via a call to setCaret( ) on any JTextComponent.

public abstract Document createDefaultDocument( )

Called by the BasicTextUI when installing a UI for a component and by JTextComponent before loading a new document from a stream. It should return a new Document object of the appropriate type for the kit. Figure 23-3 shows how this method is called when a new JEditorPane is created without specifying a document.

Figure 23-3. Default document creation using EditorKit
figs/swng2.2303.gif
public abstract void read(InputStream in, Document doc, int pos) throws IOException, BadLocationException
public abstract void read(Reader in, Document doc, int pos) throws IOException, BadLocationException

Populate the given Document, based on data read from the stream or reader. The data read is inserted at the specified document position (use 0 if this is a new, empty document).

public abstract void write(OutputStream out, Document doc, int pos, int len) throws IOException, BadLocationException
public abstract void write(Writer out, Document doc, int pos, int len) throws IOException, BadLocationException

Write the specified portion of the document to the specified stream. To write the entire document, call write(aWriter, aDoc, 0, aDoc.getLength( )).

These read( ) and write( ) methods support any arbitrary text format. For simple text, the data read and written is plain text; more advanced editor kits might be able to read attribute and style information or well-defined markup languages like HTML or XML.

23.2.1.3 Other methods

These methods are not abstract, but the default implementations do nothing:

public void install( JEditorPane c)

Called once the content type for the JEditorPane has been determined. It associates the kit with the editor pane. Subclasses that need to query or listen to the editor pane for any reason should take advantage of this method.

public void deinstall( JEditorPane c)

Called when the JEditorPane changes its editor kit. For example, subclasses may take advantage of this method to remove listeners from the pane.

23.2.2 The TextAction Class

Way back in Chapter 3, we introduced the Action interface and the AbstractAction default implementation. To quickly review, Actions are a way to encapsulate some common piece of functionality, along with an associated name, icon, and other attributes. Action extends the ActionListener interface, so any Action can be added as a listener to components that fire Action events.

When working with any word processor or basic editor, you perform "actions" all the time. The simplest example occurs every time you type a character. Some action is performed to add that character to the document and the display. More interesting examples include making a segment of text bold, copying a selection of text, or changing the current font.

Swing provides support for many of these common activities through classes that extend the TextAction abstract class. The specific actions available are defined as inner classes of the EditorKit subclasses. We'll cover each of these in the sections that follow. In this section, we'll briefly cover the TextAction class itself, so you understand what all these new actions have in common. Appendix B lists all the actions provided by Swing's standard components.

Note that if you want to define your own Action implementations that manipulate text components, they don't have to extend TextAction. In most cases you'll want to because its getTextComponent( ) method makes it convenient to create a single instance that can work with multiple text components.

23.2.2.1 Constructor
public TextAction(String name)

Pass the action name up to its superclass, where it is assigned to the Action.NAME property. Recall from the discussion of Action that this property is typically the primary key used to identify actions.

23.2.2.2 Static method
public static final Action[] augmentList(Action[] list1, Action[] list2)

Add the actions in the second list to those in the first list. Any action names in both lists are mapped to the Action specified in the second list. This method can be used by an EditorKit that adds new actions to the actions supported by its superclass.

23.2.2.3 Protected methods
protected final JTextComponent getFocusedComponent( )

Return JTextComponent.getFocusedComponent( ).

protected final JTextComponent getTextComponent(ActionEvent e)

Determine the currently active text component, allowing actions to be shared by multiple text components. If the given event has a JTextComponent as its target, that component is returned. If not, the result of getFocusedComponent( ) is returned.

23.2.3 The DefaultEditorKit Class

DefaultEditorKit, a subclass of EditorKit, provides a great deal of default behavior applicable to most document types. As we'll see, the most interesting features available in this class are provided by the actions it supports through its numerous inner classes.

When used directly, DefaultEditorKit supports only plain-text data. Its actions deal with tasks such as copying and pasting data, and its I/O methods read and write only plain text. However, the important thing about this class is that it can be extended to add more features, and the actions it defines are still useful. We'll see extensions of DefaultEditorKit in the sections that follow.

23.2.3.1 Properties

DefaultEditorKit defines values for the properties shown in Table 23-4. Each action is an instance of an inner class capable of performing a given task. The contentType for editors using DefaultEditorKit is "text/plain", indicating that styled text is not supported. The viewFactory property is set to null, indicating that users of this class must provide ViewFactory support via the TextUI.

Table 23-4. DefaultEditorKit properties

Property

Data type

get

is

set

Default value

actionso

Action[]

·

   

Array of 53* TextAction objects

contentTypeo

String

·

   

"text/plain"

viewFactoryo

ViewFactory

·

   

null

ooverridden

*As of 1.4; there were 47 when Swing was first released.

See also properties from the EditorKit class (Table 23-3).

23.2.3.2 Constants

DefaultEditorKit defines 52[3] action names (Strings, shown in Table 23-5) as constant values, though their names aren't all-caps as you'd expect given the Java naming conventions.

[3] The keen observer will notice that the properties table indicated that there were 53 actions available. One action is not defined as a constant but is still available under the name dump-model. This action just calls dump( ) on the document of the currently focused JTextComponent (assuming it's an AbstractDocument), causing the model structure to be printed to System.err.

All but six of these names are public; six are declared with default (package) access and are thus intended for internal use. Behind each action name is an inner class (some classes are used by multiple actions) that extends TextAction to carry out the action. Several of these action classes are public, so we describe them in more detail after this section. However, even with the nonpublic classes, you can get to an instance of the action using these constants, along with the actions property. The following example shows a common strategy for doing this. We store all the actions in a local Hashtable so that we can access them by name. In this example, we then retrieve the "cut" action from the table using the DefaultEditorKit.cutAction constant:

Hashtable actionHash = new Hashtable( ); Action[] actions = edKit.getActions( ); for (int i=0; i<actions.length; i++) {   String name = (String)actions[i].getValue(Action.NAME);   actionHash.put(name, actions[i]); } Action cut = (Action)actionHash.get(DefaultEditorKit.cutAction);

Table 23-5. DefaultEditorKit action name constants

Constant

Description

backwardAction

Move the caret back one position.

beepAction

Create a beep (Toolkit.beep( )).

beginAction

Move the caret to the beginning of the document.

beginLineAction

Move the caret to the beginning of the current line.

beginParagraphAction

Move the caret to the beginning of the current paragraph.

beginWordAction

Move the caret to the beginning of the current word.

copyAction

Copy the selected region and place it on the system clipboard.

cutAction

Cut the selected region and place it on the system clipboard.

defaultKeyTypedAction

Insert the pressed key (default when there is no special key mapping for a pressed key).

deleteNextCharAction

Delete the character following the caret position.

deletePrevCharAction

Delete the character before the caret position.

downAction

Move the caret down one position.

endAction

Move the caret to the end of the document.

endLineAction

Move the caret to the end of the current line.

endParagraphAction

Move the caret to the end of the current paragraph.

endWordAction

Move the caret to the end of the current word.

forwardAction

Move the caret forward one position.

insertBreakAction

Insert a line or paragraph break (\n) into the document; if there is a current selection, it is removed first.

insertContentAction

Insert content into the document; if there is a current selection, it is removed first.

insertTabAction

Insert a tab character into the document; if there is a current selection, it is removed first.

nextWordAction

Move the caret to the beginning of the next word.

pageDownAction

Page the document down.

pageUpAction

Page the document up.

pasteAction

Paste the contents of the system clipboard at the caret position; if there is a current selection, it is replaced by the pasted content.

previousWordAction

Move the caret to the beginning of the previous word.

readOnlyAction

Set the editor to read-only mode; results in a call to setEditable(false) on the JTextComponent.

selectAllAction

Highlight the entire document.

selectionBackwardAction

Adjust the current selection by moving the caret back one position.

selectionBeginAction

Adjust the current selection by moving the caret back to the beginning of the document.

selectionBeginLineAction

Adjust the current selection by moving the caret back to the beginning of the current line.

selectionBeginParagraphAction

Adjust the current selection by moving the caret back to the beginning of the current paragraph.

selectionBeginWordAction

Adjust the current selection by moving the caret back to the beginning of the current word.

selectionDownAction

Adjust the current selection by moving the caret down one row.

selectionEndAction

Adjust the current selection by moving the caret to the end of the document.

selectionEndLineAction

Adjust the current selection by moving the caret to the end of the current line.

selectionEndParagraphAction

Adjust the current selection by moving the caret to the end of the current paragraph.

selectionEndWordAction

Adjust the current selection by moving the caret to the end of the current word.

selectionForwardAction

Adjust the current selection by moving the caret forward one position.

selectionNextWordAction

Adjust the current selection by moving the caret to the beginning of the next word.

selectionPageDownActionn

Page the document down, moving the selection.

selectionPageLeftActionn

Page the document left, moving the selection.

selectionPageRightActionn

Page the document right, moving the selection.

selectionPageUpActionn

Page the document up, moving the selection.

selectionPreviousWordAction

Adjust the current selection by moving the caret to the beginning of the previous word.

selectionUpAction

Adjust the current selection by moving the caret down one row.

selectLineAction

Select the current line.

selectParagraphAction

Select the current paragraph.

selectWordAction

Select the current word.

toggleComponentOrientationActionn

Switches the component's orientation (between left-to-right and right-to-left).

unselectActionn

Eliminate any selection without affecting its content.

upAction

Move the caret up one position.

writableAction

Set the editor to writable mode; results in a call to setEditable(true) on the JTextComponent.

nNot public (declared with default package access).

It would have been nice if EditorKit had been enhanced to take advantage of ActionMap to spare us this sort of effort. Perhaps someday . . .

23.2.3.3 Using actions

Let's look at a simple example that shows how these actions can be used. This program creates a JTextArea and adds all the available actions to a menu (the list is pretty long, so we split it into two submenus). As we discussed in Chapter 14, we can add these Action objects directly to the menu. The default action names appear as menu selections.

Since JTextArea gets its actions from the DefaultEditorKit, you'll see each of the actions listed in Table 23-5 when you run this program. By blindly adding all the actions, we avoid interacting with the editor kit directly in this program. At the end of this section, we'll look at a much more useful example that uses DefaultEditorKit directly.

// TextActionExample.java // import javax.swing.*; import javax.swing.text.*; // Simple TextAction example public class TextActionExample {   public static void main(String[] args) {     // Create a text area.     JTextArea ta = new JTextArea( );     ta.setLineWrap(true);     // Add all actions to the menu (split into two menus to make it more usable).     Action[] actions = ta.getActions( );     JMenuBar menubar = new JMenuBar( );     JMenu actionmenu = new JMenu("Actions");     menubar.add(actionmenu);     JMenu firstHalf = new JMenu("1st Half");     JMenu secondHalf = new JMenu("2nd Half");     actionmenu.add(firstHalf);     actionmenu.add(secondHalf);     int mid = actions.length/2;     for (int i=0; i<mid; i++) {       firstHalf.add(actions[i]);     }     for (int i=mid; i<actions.length; i++) {       secondHalf.add(actions[i]);     }     // Show it.     JFrame f = new JFrame( );     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);     f.getContentPane( ).add(ta);     f.setJMenuBar(menubar);     f.setSize(300, 200);     f.setVisible(true);   } }

That's all there is to it! All we did was call getActions( ) on the JTextArea (which ultimately retrieved the actions from a DefaultEditorKit) and added each action to the menu. Of course, most of these actions would never be provided as menu options, and for those that would, you'd want to change the label (the default labels are all lowercase, and the words are hyphen-separated, as in cut-to-clipboard). The example at the end of the section is a bit more realistic.

23.2.3.4 Constructor
public DefaultEditorKit( )

This default constructor defines no behavior.

23.2.3.5 Methods

The following methods provide default implementations for all the abstract methods defined in EditorKit:

public Object clone( )

Return a new DefaultEditorKit.

public Caret createCaret( )

Return null. See EditorKit.createCaret( ).

public Document createDefaultDocument( )

Create a new PlainDocument instance and return it.

public void read(InputStream in, Document doc, int pos) throws IOException, BadLocationException
public void read(Reader in, Document doc, int pos) throws IOException, BadLocationException

Read plain text from the supplied Reader (in), adding the text at the specified position. The version that takes an InputStream simply wraps the stream in an InputStreamReader and calls the other version.

public void write(OutputStream out, Document doc, int pos, int len) throws IOException, BadLocationException
public void write(Writer out, Document doc, int pos, int len) throws IOException, BadLocationException

Write len plain text characters to the supplied Writer (out), starting at position pos. The version that takes an OutputStream simply wraps the stream in an OutputStreamWriter and calls the other version.

23.2.4 Useful Actions

In the next few sections, we'll give a brief overview of some key actions defined as public static inner classes of DefaultEditorKit. The first of these is the default action used to insert text into the active JTextComponent. The constructors aren't described individually, as they simply set up the proper name for each action. We'll simply describe the actionPerformed method that implements the purpose of each action. In each case, the method has the standard signature, public void actionPerformed(ActionEvent e).

DefaultEditorKit.DefaultKeyTypedAction

Ins ert the actionCommand value from the ActionEvent into the active JTextComponent using replaceSelection( ). If the first character has a value less than 0x20, this does nothing. This action adds ordinary (noncommand) keystrokes to the document's content.

DefaultEditorKit.BeepAction

Call Toolkit.getDefaultToolkit( ).beep( ) to produce an audible alert.

DefaultEditorKit.CopyAction

Call copy( ) on the active JTextComponent, if it can be determined.

DefaultEditorKit.CutAction

Call cut( ) on the active JTextComponent, if it can be determined.

DefaultEditorKit.InsertBreakAction

Replace the active JTextComponent's current selection with a newline character (\n). If there is no selection, a newline is inserted.

DefaultEditorKit.InsertContentAction

Insert the actionCommand value from the event into the active JTextComponent, using replaceSelection( ). If the action command is null, a beep is sounded.

DefaultEditorKit.InsertTabAction

Replace the active JTextComponent's current selection with a tab character (\t). If there is no selection, a tab is inserted.

DefaultEditorKit.PasteAction

Call paste( ) on the active JTextComponent.

23.2.5 A Simple Text Editor

In the next example, we'll show how to provide some of the things you'd expect from a basic editor. Our first editor supports the following features:

  • Cut, copy, and paste via toolbar buttons, menu selection, and default key shortcuts

  • Select-all capability via menu selection

  • Quick keyboard navigation using nextWordAction, previousWordAction, selectionNextWordAction, and selectionPreviousWordAction

  • Saving and loading documents

When we run it, our simple editor looks something like Figure 23-4.[4]

[4] Text in this and subsequent figures is from "Computing Machinery and Intelligence" by A.M. Turning (Mind, 1950).

Figure 23-4. SimpleEditor
figs/swng2.2304.gif

Here's the source code for SimpleEditor. It's designed to be easily extensible, allowing us to add features of the more advanced editor kits. This class serves as the base class for the other examples in this chapter.

// SimpleEditor.java // import javax.swing.*; import javax.swing.text.*; import java.awt.*; import java.io.*; import java.awt.event.*; import java.util.Hashtable; // An example showing several DefaultEditorKit features. This class is designed // to be easily extended for additional functionality. public class SimpleEditor extends JFrame {   public static void main(String[] args) {     SimpleEditor editor = new SimpleEditor( );     editor.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);     editor.setVisible(true);   }   // Create an editor.   public SimpleEditor( ) {     super("Swing Editor");     textComp = createTextComponent( );     makeActionsPretty( );     Container content = getContentPane( );     content.add(textComp, BorderLayout.CENTER);     content.add(createToolBar( ), BorderLayout.NORTH);     setJMenuBar(createMenuBar( ));     setSize(320, 240);   }   // Create the JTextComponent subclass.   protected JTextComponent createTextComponent( ) {     JTextArea ta = new JTextArea( );     ta.setLineWrap(true);     return ta;   }   // Add icons and friendly names to actions we care about.   protected void makeActionsPretty( ) {     Action a;     a = textComp.getActionMap( ).get(DefaultEditorKit.cutAction);     a.putValue(Action.SMALL_ICON, new ImageIcon("icons/cut.gif"));     a.putValue(Action.NAME, "Cut");     a = textComp.getActionMap( ).get(DefaultEditorKit.copyAction);     a.putValue(Action.SMALL_ICON, new ImageIcon("icons/copy.gif"));     a.putValue(Action.NAME, "Copy");     a = textComp.getActionMap( ).get(DefaultEditorKit.pasteAction);     a.putValue(Action.SMALL_ICON, new ImageIcon("icons/paste.gif"));     a.putValue(Action.NAME, "Paste");     a = textComp.getActionMap( ).get(DefaultEditorKit.selectAllAction);     a.putValue(Action.NAME, "Select All");   }   // Create a simple JToolBar with some buttons.   protected JToolBar createToolBar( ) {     JToolBar bar = new JToolBar( );     // Add simple actions for opening and saving.     bar.add(getOpenAction( )).setText("");     bar.add(getSaveAction( )).setText("");     bar.addSeparator( );     // Add Cut/Copy/Paste buttons.     bar.add(textComp.getActionMap( ).get(DefaultEditorKit.cutAction)).setText("");     bar.add(textComp.getActionMap( ).get(DefaultEditorKit.copyAction)).setText("");     bar.add(textComp.getActionMap( ).get(DefaultEditorKit.pasteAction)).setText("");     return bar;   }   // Create a JMenuBar with file and edit menus.   protected JMenuBar createMenuBar( ) {     JMenuBar menubar = new JMenuBar( );     JMenu file = new JMenu("File");     JMenu edit = new JMenu("Edit");     menubar.add(file);     menubar.add(edit);     file.add(getOpenAction( ));     file.add(getSaveAction( ));     file.add(new ExitAction( ));     edit.add(textComp.getActionMap( ).get(DefaultEditorKit.cutAction));     edit.add(textComp.getActionMap( ).get(DefaultEditorKit.copyAction));     edit.add(textComp.getActionMap( ).get(DefaultEditorKit.pasteAction));     edit.add(textComp.getActionMap( ).get(DefaultEditorKit.selectAllAction));     return menubar;   }   // Subclass can override to use a different open action.   protected Action getOpenAction( ) { return openAction; }   // Subclass can override to use a different save action.   protected Action getSaveAction( ) { return saveAction; }   protected JTextComponent getTextComponent( ) { return textComp; }   private Action openAction = new OpenAction( );   private Action saveAction = new SaveAction( );   private JTextComponent textComp;   private Hashtable actionHash = new Hashtable( );   // ********** ACTION INNER CLASSES ********** //   // A very simple exit action   public class ExitAction extends AbstractAction {     public ExitAction( ) { super("Exit"); }     public void actionPerformed(ActionEvent ev) { System.exit(0); }   }   // An action that opens an existing file   class OpenAction extends AbstractAction {     public OpenAction( ) {        super("Open", new ImageIcon("icons/open.gif"));      }     // Query user for a filename and attempt to open and read the file into the     // text component.     public void actionPerformed(ActionEvent ev) {       JFileChooser chooser = new JFileChooser( );       if (chooser.showOpenDialog(SimpleEditor.this) !=           JFileChooser.APPROVE_OPTION)         return;       File file = chooser.getSelectedFile( );       if (file == null)         return;       FileReader reader = null;       try {         reader = new FileReader(file);         textComp.read(reader, null);       }       catch (IOException ex) {         JOptionPane.showMessageDialog(SimpleEditor.this,         "File Not Found", "ERROR", JOptionPane.ERROR_MESSAGE);       }       finally {         if (reader != null) {           try {             reader.close( );           } catch (IOException x) {}         }       }     }   }   // An action that saves the document to a file   class SaveAction extends AbstractAction {     public SaveAction( ) {       super("Save", new ImageIcon("icons/save.gif"));     }     // Query user for a filename and attempt to open and write the text component's     // content to the file.     public void actionPerformed(ActionEvent ev) {       JFileChooser chooser = new JFileChooser( );       if (chooser.showSaveDialog(SimpleEditor.this) !=           JFileChooser.APPROVE_OPTION)         return;       File file = chooser.getSelectedFile( );       if (file == null)         return;       FileWriter writer = null;       try {         writer = new FileWriter(file);         textComp.write(writer);       }       catch (IOException ex) {         JOptionPane.showMessageDialog(SimpleEditor.this,         "File Not Saved", "ERROR", JOptionPane.ERROR_MESSAGE);       }       finally {         if (writer != null) {           try {             writer.close( );           } catch (IOException x) {}         }       }     }   } }

Let's look at a few of the methods from this example. The first interesting method is called makeActionsPretty( ). This method adds icons to the actions we're going to display and changes the text for these actions. This way, our user interface displays nice names like Cut instead of cut-to-clipboard. It uses the ActionMap associated with the text component to look up the standard actions, as described in Chapter 3.

In createToolBar( ), we get instances of two inner classes, OpenAction and SaveAction, and add them to our JToolBar. We get these actions by calling getOpenAction( ) and getSaveAction( ) to allow subclasses to provide different implementations of these actions. We then get the cut, copy, and paste actions. We've chosen not to display the text for the actions in the toolbar, so we call setText("") on the JButton returned by each add( ) call. For more details on Swing's handy JToolBar class, see Chapter 14.

The createMenuBar( ) method is similar to createToolBar( ). We add two additional actions here: exit and select-all. In this method, we don't strip the text from the menu items, allowing both the icon and text to be displayed.

Finally, we define action classes for exiting the application, and for opening and saving files. These last two actions call the JTextComponent's read( ) and write( ) methods, which take advantage of the editor kit's read( ) and write( ) methods. For more details on another handy Swing class we've used here, JFileChooser, see Chapter 12.

If you compare this example to the version from our first edition,[5] you'll see some good evidence of the maturation of Swing over the last few years. We've been able to remove an entire page of code because of features that Swing now gives us for free. ActionMaps eliminated the need for our own code to organize and look up the text components' Actions. We also had a method that added entries to the component's KeyMap for moving around by word and extending the selection using the keyboard. We made no effort to do this in an L&F-appropriate way. Luckily, the new InputMap associated with JTextArea provides all these capabilities for us, and it is L&F-sensitive. In the Windows, Metal, and Motif L&Fs, you can move the caret a word at a time by holding down Ctrl while pressing an arrow key; in the Mac L&F, the Command key serves this purpose. In all L&Fs, holding down Shift during cursor movement extends the selection in that direction.

[5] You can download the first edition's examples at http://www.oreilly.com/catalog/jswing. You'll find Simple-Editor.java in in the ch24 directory.

23.2.6 The StyledEditorKit Class

StyledEditorKit extends DefaultEditorKit to provide additional features for styled text. It is the kit used by the JTextPane class. Like DefaultEditorKit, this class defines a number of new TextActions. In this class, all of these action classes are public. We'll look at each of them at the end of this section.

23.2.6.1 Properties

StyledEditorKit defines the properties and default values shown in Table 23-6. The actions property is the set of actions defined for DefaultEditorKit, augmented with actions for setting text alignment and font family, size, and style attributes. The exact actions provided are shown in Table 23-7. In this table, the first column is the inner class name, the next column is the action name (Action.NAME), and the last column lists parameter values passed to the specific action class constructor. Additional actions can be created by instantiating the desired action class and passing different values to the constructor.

Table 23-6. StyledEditorKit properties

Property

Data type

get

is

set

Default value

actionso

Action[]

·

   

DefaultEditorKit's actions and 18 more

characterAttributeRun

Element

·

   

null

inputAttributes

MutableAttributeSet

·

     

viewFactoryo

ViewFactory

·

   

StyledViewFactory( )

ooverridden

See also properties from the DefaultEditorKit class (Table 23-4).

Table 23-7. StyledEditorKit default actions

Action name

Action class

Constructor argument

center-justify

AlignmentAction

StyleConstants.ALIGN_CENTER

font-bold

BoldAction

 

font-family-Monospaced

FontFamilyAction

"Monospaced"

font-family-SansSerif

FontFamilyAction

"SansSerif"

font-family-Serif

FontFamilyAction

"Serif"

font-italic

ItalicAction

 

font-size-8

FontSizeAction

8

font-size-10

FontSizeAction

10

font-size-12

FontSizeAction

12

font-size-14

FontSizeAction

14

font-size-16

FontSizeAction

16

font-size-18

FontSizeAction

18

font-size-24

FontSizeAction

24

font-size-36

FontSizeAction

36

font-size-48

FontSizeAction

48

font-underline

UnderlineAction

 

insert-break

StyledInsertBreakAction

 

left-justify

AlignmentAction

StyleConstants.ALIGN_LEFT

right-justify

AlignmentAction

StyleConstants.ALIGN_RIGHT

The characterAttributeRun property indicates the Element in which the caret is currently located. This is updated whenever the kit's JEditorPane fires a CaretEvent. Similarly, the inputAttributes property provides access to the attribute set in use at the current caret position. This property is used by the JTextPane class whenever content is added to the document.

The viewFactory defined by StyledEditorKit is an inner class capable of creating View objects for different types of Elements. The View objects created by this factory depend on the name property of the Element passed to the create( ) method, as shown in Table 23-8.

Table 23-8. StyledEditorKit.StyledViewFactory view creation policy

Element name

View class created

AbstractDocument.ContentElementName

LabelView

AbstractDocument.ParagraphElementName

ParagraphView

AbstractDocument.SectionElementName

BoxView

AbstractDocument.ComponentElementName

ComponentView

AbstractDocument.IconElementName

IconView

Other

LabelView

23.2.6.2 Constructor
public StyledEditorKit( )

This default constructor provides no behavior.

23.2.6.3 EditorKit methods

These methods override those defined in DefaultEditorKit or EditorKit:

public Object clone( )

Return a new StyledEditorKit that is a copy of this editor kit.

public Document createDefaultDocument( )

Return a new instance of DefaultStyledDocument.

public void install( JEditorPane c)

Called when the kit is associated with a JEditorPane. It adds itself (actually, an instance of a nonpublic inner class) as a CaretListener of the given pane.

public void deinstall( JEditorPane c)

Called to indicate that the given pane is no longer using this kit. It removes the caret listener added by install( ).

23.2.7 The StyledEditorKit.StyledTextAction Class

As discussed earlier, StyledEditor defines several public inner classes to perform actions related to styled text. This public abstract class extends TextAction and serves as the base class for all the other action inner classes.

23.2.7.1 Constructor
public StyledTextAction(String nm)

Pass the action name up to the superclass.

23.2.7.2 Protected methods

These protected methods are available to any subclass of StyledTextAction. None of them does anything really new, they just save you a few steps for certain common tasks. If you define your own styled text actions, some of these methods will come in handy.

protected final JEditorPane getEditor(ActionEvent e)

Provide convenient access to the JEditorPane with which the given event is associated. If neither the event source nor the currently focused component is a JEditorPane, this method throws an IllegalArgumentException.

protected final StyledDocument getStyledDocument( JEditorPane e)

This convenience method gets the current document from the given pane and returns it as a StyledDocument. If the document is not an instance of StyledDocu-ment (or a subclass), this method throws an IllegalArgumentException.

protected final StyledEditorKit getStyledEditorKit( JEditorPane e)

This convenience method gets the current editor kit from the given pane and returns it as a StyledEditorKit. If it is not an instance of StyledEditorKit (or a subclass), this method throws an IllegalArgumentException.

protected final void setCharacterAttributes( JEditorPane editor, AttributeSet attr, boolean replace)

Set the character attributes for the currently selected text, if there is a selection, or the current input attributes. The replace parameter indicates whether the given attributes should replace the existing ones or be added.

protected final void setParagraphAttributes( JEditorPane editor, AttributeSet attr, boolean replace)

Call setParagraphAttributes( ) for the currently selected range of the given editor pane.

The following seven classes are public, static extensions of the StyledTextAction abstract class. Instances of these classes are provided as default actions for the StyledEditorKit, but you can create additional instances if the exact action you want is not provided as a default. Each class contains only a constructor and its actionPerformed( ) method, so we'll detail only the interesting methods as we did for DefaultEditorKit's actions.

Unless otherwise noted, each of these classes uses the setCharacterAttributes( ) method, defined in StyledTextAction, to update the attributes for the current selection, if there is one, or the attributes for text to be inserted.

23.2.7.3 The StyledEditorKit.FontFamilyAction class
public FontFamilyAction(String name, String family)

Create an action with the supplied name to establish the specified font family (SansSerif, Serif, or the like).

public void actionPerformed(ActionEvent e)

By default, set the current font family to the value established by the constructor. However, if the target of the event matches the current JEditorPane, and the event's actionCommand property is not null, the action command is used as the new font family instead.

23.2.7.4 The StyledEditorKit.FontSizeAction class
public FontSizeAction(String name, int size)

Create an action with the specified name and font size.

public void actionPerformed(ActionEvent e)

By default, set the current font size to the value established by the constructor. However, if the target of the given event matches the current JEditorPane, and the event's actionCommand property is not null, the action command (converted from a String to an int) is used as the new font size instead.

23.2.7.5 The StyledEditorKit.ForegroundAction class
public ForegroundAction(String name, Color fg)

Create an action with the specified name and color.

public void actionPerformed(ActionEvent e)

By default, set the current foreground color to the value established by the constructor. However, if the target of the given event matches the current JEditorPane, and the event's actionCommand property is not null, the action command is used as the new color instead.

The task of converting the actionCommand string to a Color is handled by the Color.decode( ) method. Typically, colors are written as hexadecimal numbers in which the first eight bits represent red, the next eight represent green, and the last eight represent blue. For example, 0xFF0000 is red, 0x000000 is black, and 0xFF00FF is magenta. All Color.decode( ) does is convert such a string to the corresponding Color.

23.2.7.6 The StyledEditorKit.AlignmentAction class
public AlignmentAction(String name, int alignment)

Create an action with the given name and alignment value. The value must be one of the alignment constants defined in SwingConstants: ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT, or ALIGN_JUSTIFIED.

public void actionPerformed(ActionEvent e)

By default, set the current alignment to the value established by the constructor. However, if the target of the given event matches the current JEditorPane, and the event's actionCommand property is not null, the numeric value of the action command (the String is parsed as an int) is used as the new alignment instead.

Note that unlike the other action classes discussed here, this one uses setParagraphAttributes( ) rather than setCharacterAttributes( ) since alignment is a paragraph property.

23.2.7.7 The StyledEditorKit.BoldAction class
public void actionPerformed(ActionEvent e)

Check the current attribute set to see if the bold attribute is turned on and toggle this value for the current selection or specified attributes.

23.2.7.8 The StyledEditorKit.ItalicAction class
public void actionPerformed(ActionEvent e)

Check the current input attribute set to see if the italic attribute is turned on and toggle this value for the current selection or input attributes.

23.2.7.9 The StyledEditorKit.UnderlineAction class
public void actionPerformed(ActionEvent e)

Check the current attribute set to see if the underline attribute is turned on and toggle this value for the current selection or given attributes.

23.2.8 A Better Editor

Earlier in this chapter, we created a class called SimpleEditor that used some of the actions provided by DefaultEditorKit. Now we'll extend that class to create StyleEditor, which uses many of the new actions we've just introduced. When run, StyledEditor looks like Figure 23-5.

Figure 23-5. StyledEditor: a text editor that supports user-defined styles
figs/swng2.2305.gif

There's not a lot in this class that's new. We override several of the methods defined in SimpleEditor to add actions to the toolbar and menu. These include actions for changing the font's style, size, and family. We also take advantage of the text component's InputMap to add new key mappings that change the font style since these aren't built into Swing the way SimpleEditor's actions were. We use the default AWT toolkit's getMenuShortcutKeymask( ) method to accomplish this in an L&F-appropriate manner. Under most L&Fs, the keys Ctrl-B, Ctrl-I, and Ctrl-U toggle bold, italics and underlining while in the Mac L&F, Command-B, Command-I, and Command-U are used instead.

Here's the code for our new and improved editor:

// StyledEditor.java // import javax.swing.*; import javax.swing.text.*; import java.awt.event.*; import java.awt.Toolkit; // An extension of SimpleEditor that adds styled-text features public class StyledEditor extends SimpleEditor{   public static void main(String[] args) {     StyledEditor editor = new StyledEditor( );     editor.setVisible(true);     editor.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);   }   // Create a styed editor.   public StyledEditor( ) {     updateInputMap( );  // Install our style-related keystrokes   }   // Override to create a JTextPane.   protected JTextComponent createTextComponent( ) {     return new JTextPane( );   }   // Add icons and friendly names for font actions.   protected void makeActionsPretty( ) {     super.makeActionsPretty( );     Action a;     a = getTextComponent( ).getActionMap( ).get("font-bold");     a.putValue(Action.SMALL_ICON, new ImageIcon("icons/bold.gif"));     a.putValue(Action.NAME, "Bold");     a = getTextComponent( ).getActionMap( ).get("font-italic");     a.putValue(Action.SMALL_ICON, new ImageIcon("icons/italic.gif"));     a.putValue(Action.NAME, "Italic");     a = getTextComponent( ).getActionMap( ).get("font-underline");     a.putValue(Action.SMALL_ICON, new ImageIcon("icons/underline.gif"));     a.putValue(Action.NAME, "Underline");     a = getTextComponent( ).getActionMap( ).get("font-family-SansSerif");     a.putValue(Action.NAME, "SansSerif");     a = getTextComponent( ).getActionMap( ).get("font-family-Monospaced");     a.putValue(Action.NAME, "Monospaced");     a = getTextComponent( ).getActionMap( ).get("font-family-Serif");     a.putValue(Action.NAME, "Serif");     a = getTextComponent( ).getActionMap( ).get("font-size-10");     a.putValue(Action.NAME, "10");     a = getTextComponent( ).getActionMap( ).get("font-size-12");     a.putValue(Action.NAME, "12");     a = getTextComponent( ).getActionMap( ).get("font-size-16");     a.putValue(Action.NAME, "16");     a = getTextComponent( ).getActionMap( ).get("font-size-24");     a.putValue(Action.NAME, "24");   }   // Add key mappings for font style features.   protected void updateInputMap( ) {     // Extend the input map used by our text component.     InputMap map = getTextComponent( ).getInputMap( );     int mask = Toolkit.getDefaultToolkit( ).getMenuShortcutKeyMask( );     KeyStroke bold = KeyStroke.getKeyStroke(KeyEvent.VK_B, mask, false);     KeyStroke italic = KeyStroke.getKeyStroke(KeyEvent.VK_I, mask, false);     KeyStroke under = KeyStroke.getKeyStroke(KeyEvent.VK_U, mask, false);     map.put(bold, "font-bold");     map.put(italic, "font-italic");     map.put(under, "font-underline");   }   // Add font actions to the toolbar.   protected JToolBar createToolBar( ) {     JToolBar bar = super.createToolBar( );     bar.addSeparator( );     bar.add(getTextComponent( ).getActionMap( ).get("font-bold")).setText("");     bar.add(getTextComponent( ).getActionMap( ).get("font-italic")).setText("");     bar.add(getTextComponent( ).getActionMap( ).get("font-underline")).setText("");     return bar;   }   // Add font actions to the menu.   protected JMenuBar createMenuBar( ) {     JMenuBar menubar = super.createMenuBar( );     JMenu font = new JMenu("Font");     menubar.add(font);     JMenu style = new JMenu("Style");     JMenu family = new JMenu("Family");     JMenu size = new JMenu("Size");     font.add(style);     font.add(family);     font.add(size);     style.add(getTextComponent( ).getActionMap( ).get("font-bold"));     style.add(getTextComponent( ).getActionMap( ).get("font-underline"));     style.add(getTextComponent( ).getActionMap( ).get("font-italic"));     family.add(getTextComponent( ).getActionMap( ).get("font-family-SansSerif"));     family.add(getTextComponent( ).getActionMap( ).get("font-family-Monospaced"));     family.add(getTextComponent( ).getActionMap( ).get("font-family-Serif"));     size.add(getTextComponent( ).getActionMap( ).get("font-size-10"));     size.add(getTextComponent( ).getActionMap( ).get("font-size-12"));     size.add(getTextComponent( ).getActionMap( ).get("font-size-16"));     size.add(getTextComponent( ).getActionMap( ).get("font-size-24"));     // Don't forget that we can define new actions too!     size.add(new StyledEditorKit.FontSizeAction("64", 64));     return menubar;   } } 

The last thing we do is create a new action using one of the StyledEditorKit inner classes, to remind ourselves that we're not restricted to those actions defined as defaults by the kit. We're free to create new instances as needed.

23.2.8.1 Saving styled documents

One feature that's missing from our StyledEditor is the ability to read and write styled text. Unfortunately, StyledEditorKit does not override the read( ) and write( ) methods, so any documents saved from our StyledEditor are saved as plain text. To fix this problem, we'd ideally want to create a new editor kit that saved all the Style and Element information associated with the Document.

For now, we'll provide an alternative solution by extending our editor once again and adding the ability to serialize the Document object to a file and then read it back in.[6] Note that we'll actually serialize all the attribute and style information that is part of the document. We can't do the same sort of thing in an editor kit subclass because the editor kit's read( ) methods are set up to read the contents of a file into an existing document. Serializing the entire document and then reading it back would not fit this model, since the process of reading a serialized object creates a new document.

[6] It's worth mentioning two drawbacks to this strategy. First, serialization saves the state of the entire Document object. This is less efficient than designing your own representation for saving a document though these days, when one common word processor routinely produces large files for relatively small documents, this disadvantage might not be significant. More important, serialization from one version of Java is not likely to be compatible with another. So you could save a document with this editor running under 1.3, upgrade to 1.4, and find that you can't read any documents you saved. Java is moving to a more durable XML- and JavaBeans-based file format, but DefaultStyledDocument doesn't yet expose enough of its contents to work this way.

Here's the source for an editor that allows styled documents to be saved and opened without losing their text attributes. All we've done is provided new implementations of the getSaveAction( ) and getOpenAction( ) methods and defined the new actions returned by these methods.

// IOStyledEditor.java // import javax.swing.*; import javax.swing.text.*; import java.awt.event.*; import java.io.*; // An extension of StyledEditor that adds document serialization public class IOStyledEditor extends StyledEditor {   public static void main(String[] args) {     IOStyledEditor te = new IOStyledEditor( );     te.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);     te.setVisible(true);   }   // Provide a new open action.   protected Action getOpenAction( ) {     if (inAction == null)       inAction = new InAction( );     return inAction;   }   // Provide a new save action.   protected Action getSaveAction( ) {     if (outAction == null)       outAction = new OutAction( );     return outAction;   }   private Action inAction;   private Action outAction;   // An action that saves the document as a serialized object   class OutAction extends AbstractAction {     public OutAction( ) {       super("Serialize Out", new ImageIcon("icons/save.gif"));     }     public void actionPerformed(ActionEvent ev) {       JFileChooser chooser = new JFileChooser( );       if (chooser.showSaveDialog(IOStyledEditor.this) !=           JFileChooser.APPROVE_OPTION)         return;       File file = chooser.getSelectedFile( );       if (file == null)         return;       FileOutputStream writer = null;       try {         Document doc = getTextComponent( ).getDocument( );         writer = new FileOutputStream(file);         ObjectOutputStream oos = new ObjectOutputStream(writer);         oos.writeObject(doc);  // Write the document.       }       catch (IOException ex) {         JOptionPane.showMessageDialog(IOStyledEditor.this,         "File Not Saved", "ERROR", JOptionPane.ERROR_MESSAGE);       }       finally {         if (writer != null) {           try {             writer.close( );           } catch (IOException x) {}         }       }     }   }   // An action that reads the document as a serialized object   class InAction extends AbstractAction {     public InAction( ) {       super("Serialize In", new ImageIcon("icons/open.gif"));     }     public void actionPerformed(ActionEvent ev) {       JFileChooser chooser = new JFileChooser( );       if (chooser.showOpenDialog(IOStyledEditor.this) !=           JFileChooser.APPROVE_OPTION)         return;       File file = chooser.getSelectedFile( );       if (file == null)         return;       FileInputStream reader = null;       try {         reader = new FileInputStream(file);         ObjectInputStream ois = new ObjectInputStream(reader);         Object o = ois.readObject( );  // Read the document.         getTextComponent( ).setDocument((Document)o);       }       catch (IOException ex) {         JOptionPane.showMessageDialog(IOStyledEditor.this,         "File Input Error", "ERROR", JOptionPane.ERROR_MESSAGE);       }       catch (ClassNotFoundException ex) {         JOptionPane.showMessageDialog(IOStyledEditor.this,         "Class Not Found", "ERROR", JOptionPane.ERROR_MESSAGE);       }       finally {         if (reader != null) {           try {             reader.close( );           } catch (IOException x) {}         }       }     }   } }

As you can see from the code, all we've done is take advantage of the fact that the Swing document classes are serializable. This allows us to write the Document model out to a file and read it back in without losing any of the information contained in the model. If something goes wrong while reading or writing the file, an appropriate warning dialog pops up using the JOptionPane class from Chapter 10.



Java Swing
Graphic Java 2: Mastering the Jfc, By Geary, 3Rd Edition, Volume 2: Swing
ISBN: 0130796670
EAN: 2147483647
Year: 2001
Pages: 289
Authors: David Geary

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net