Tải bản đầy đủ - 0 (trang)
Chapter 21. Carets, Highlighters, and Keymaps

Chapter 21. Carets, Highlighters, and Keymaps

Tải bản đầy đủ - 0trang

This document was created by an unregistered ChmMagic, please go to http://www.bisenter.com to register it. Thanks



.



In the next few sections, we'll take a closer look at these interfaces as well as the default implementations for Caret and



Highlighter. The default implementation ofKeymap is an inner class ofJTextComponent, which we can't subclass directly.

When we get to that discussion, we'll see why we typically won't need to.

I l@ve RuBoard



This document was created by an unregistered ChmMagic, please go to http://www.bisenter.com to register it. Thanks



.



I l@ve RuBoard



21.1 Carets

Carets represent the location where new text is inserted.



21.1.1 The Caret Interface

The Caret interface provides a number of useful features for dealing with text insertion and selection.



21.1.1.1 Properties



The Caret interface defines the properties shown inTable 21-1. The blinkRate property specifies the number of

milliseconds between Caret blinks. A value of0 indicates that theCaret shouldn't blink at all.



Table 21-1. Caret properties

Property



Data type



get



is



set



blinkRate



int



·



·



dot



int



·



·



magicCaretPosition



Point



·



·



mark



int



·



selectionVisible



boolean



·



·



visible



boolean



·



·



default Value



The dot property is the currentCaret position as an offset into theDocument model. The mark is the other end of

the current selection. If there is no selection, the value of mark is the same asdot. The selectionVisible property

designates whether the current selection (if any) should be decorated by the component's Highlighter.

The visible property indicates whether theCaret itself should be visible. This is almost always

true when the Caret's

text component is editable and has focus but may not be in other situations.



magicCaretPosition is a Point used when moving among lines with uneven end positions to ensure that the up and

down arrow keys produce the desired effect. For example, consider the following text:



Line 1 is long

Line 2



This document was created by an unregistered ChmMagic, please go to http://www.bisenter.com to register it. Thanks



.



Line 3 is long

If the caret was initially positioned before the o in long in line 3, you'd expect the up arrow key to move the caret to

the end of line 2. A second up arrow press should move the caret just before the o in long on the first line. This is

where the "magic" comes in. The first time the up arrow is pressed, magicCaretPosition is set to the old caret

location so that this position's x-coordinate can be used if the up arrow is pressed again. You will probably never

need to do anything with this property since the DefaultEditorKit manages it.



21.1.1.2 Events



Whenever the Caret's position changes, aChangeEvent should be fired to any interestedChangeListeners.



Caret defines the following standard methods for managing event listeners:

public abstract void addChangeListener(ChangeListener l)

public abstract void removeChangeListener(ChangeListener l)



21.1.1.3 Methods



In addition to the accessors for the properties listed earlier, the Caret interface defines the following four methods:



public void install( JTextComponent c)

Signal that the Caret is responsible for the given component. In addition to giving theCaret access to the

component, it also provides access to the Document model (to which theCaret can listen so it can update

the caret location when text is added or removed).



public void deinstall( JTextComponent c)

Signal that the Caret is no longer responsible for the given component. TheCaret should no longer be used

once this method has been called.



public void moveDot(int dot)

Called when a selection is being made. It should update the Caret to the specified position and update the



JTextComponent's Highlighter to reflect the new selection range.



public void paint(Graphics g)



.



This document was created by an unregistered ChmMagic, please go to http://www.bisenter.com to register it. Thanks



Render the Caret using the givenGraphics object.



21.1.2 The DefaultCaret Class

The DefaultCaret class provides a basic implementation of theCaret interface that renders itself as a thin vertical

line. This class extends Rectangle; implements the FocusListener, MouseListener, andMouseMotionListener

interfaces; and reacts to events sent to these listeners when they are fired by its installed JTextComponent.

As we'll see, extending DefaultCaret is a great way to create your ownCaret without having to worry about most of

the complicated details.



21.1.2.1 Properties



DefaultCaret does not add any properties to theCaret interface except the ones it inherits from

java.awt.Rectangle. Table 21-2 shows the default values it supplies.



Table 21-2. DefaultCaret properties

Property



Data type get is set Default value



blinkRate



int



·



·



0



dot



int



·



·



0



magicCaretPosition



Point



·



·



null



mark



int



·



selectionVisible



boolean



·



·



false



visible



boolean



·



·



false



0



See also the properties ofjava.awt.Rectangle (not in this book).



21.1.2.2 Events



A ChangeEvent is fired to registered listeners whenever the caret's position changes. The following standard

methods are provided for working with ChangeEvent s. (Note that thegetChangeListeners( ) method did not exist

prior to SDK 1.4, and getListeners( ) did not exist prior to SDK 1.3.)



protected void fireStateChanged( )

public void addChangeListener(ChangeListener l)



This document was created by an unregistered ChmMagic, please go to http://www.bisenter.com to register it. Thanks



public void removeChangeListener(ChangeListener l)

public ChangeListener[] getChangeListeners( )

public EventListener[] getListeners(Class listenerType)



21.1.2.3 Constructor



public DefaultCaret( )

Create a nonblinking DefaultCaret.



21.1.2.4 Caret methods



DefaultCaret provides the following implementations of the methods defined by theCaret interface:



public void install(JTextComponent c)

Set the caret's component property and register aFocusListener, MouseListener,



MouseMotionListener, andPropertyChangeListener for the component as well as a

DocumentListener for the component's Document . For the first three it registers itself, either directly or

indirectly. For the other two it registers an instance of UpdateHandler, an inner class.



public void deinstall(JTextComponent c)

Remove all the listeners registered by the install( ) method, set thecomponent property to null, and (if the

blink rate has been set) stop the Timer that controls the blinking (seeChapter 27).



public void moveDot(int dot)

Move the Caret to the specified position and update the component'sHighlighter so it can highlight the

area over which the cursor has been dragged.



public void paint(Graphics g)

Convert the current caret position ( dot) to view coordinates, then render the caret by drawing a thin vertical

line. Subclasses that override this method must also override the damage( ) method (or elseg's clipping

area may be off, which can prevent the caret from being drawn).



This document was created by an unregistered ChmMagic, please go to http://www.bisenter.com to register it. Thanks



.



21.1.2.5 FocusListener methods



These methods are implemented from the FocusListener interface:



public void focusGained(FocusEvent e)

Called when the caret's component gains focus. If the component is enabled and editable, the caret

becomes visible.



public void focusLost(FocusEvent e)

Called when the caret's component loses focus. A permanent loss of focus (in which e.isTemporary( )

returns false) causes the caret to become invisible.



21.1.2.6 Mouse methods



These methods are implemented from the MouseListener and MouseMotionListener interfaces. They define how



DefaultCaret behaves in response to mouse gestures.



public void mouseClicked(MouseEvent e)

Update the position of the caret. A double-click selects a word; a triple-click selects a line.



public void mouseDragged(MouseEvent e)

Call moveCaret( ) (described later).



public void mousePressed(MouseEvent e)

Call positionCaret( ) (described later) and request focus for the caret's component if it is enabled.



public void mouseEntered(MouseEvent e)

public void mouseExited(MouseEvent e)

public void mouseMoved(MouseEvent e)

public void mouseReleased(MouseEvent e)



This document was created by an unregistered ChmMagic, please go to http://www.bisenter.com to register it. Thanks



.



These methods do nothing in this implementation.



21.1.2.7 Protected methods



In addition to implementing all of the methods defined in the Caret interface, DefaultCaret adds several useful

methods of its own:



protected void adjustVisibility(Rectangle nloc)

Called whenever the caret's position changes. This implementation calls the component's



scrollRectToVisible( ) method (from the proper thread usingSwingUtilities.invokeLater( ) if necessary)

to ensure that the caret is visible. Subclasses may choose to change this policy.



protected void damage(Rectangle r)

This is an important method in the implementation of DefaultCaret. It is responsible for asking the caret's

component to partially repaint itself, which causes the caret to be actually drawn. It is also responsible for

setting values to the caret's x, y, width, andheight fields (inherited from java.awt.Rectangle) so that other

parts of DefaultCaret's implementation know where (in screen coordinates) the caret is located. This

information is used, for example, to erase the previous location of the caret when the caret moves. Also, it

sometimes determines the clipping area of the Graphics object passed intopaint( ). The next section

explains in detail how this works.

protected final JTextComponent getComponent( )

Provide access to the caret's component. The install( ) method (which is called when theCaret is added to

the component) stores the component for future use, making it available here.

protected Highlighter.HighlightPainter getSelectionPainter( )

Return an object capable of making highlight decorations, which can be passed to the addHighlight( )

method of the component's Highlighter when a new selection is made. This implementation returns an

instance of DefaultHighlightPainter (an inner class ofDefaultHighlighter) with a value ofnull for its



color property. This and other classes related toHighlighter are covered later in this chapter.



protected void moveCaret(MouseEvent e)

Called when the mouse is dragged (by mouseDragged( )) or Shift-clicked (indirectly bymousePressed(



)), which usually updates the current selection.



protected void positionCaret(MouseEvent e)



This document was created by an unregistered ChmMagic, please go to http://www.bisenter.com to register it. Thanks



Called when the mouse is clicked (indirectly by mousePressed( )). It moves the dot without making a

selection. It also clears the magicCaretPosition property.



protected final void repaint( )

This method simply calls getComponent( ).repaint(x, y, width, height) , which eventually causes the



paint( ) method to be called. The caret'sx, y, width, andheight fields should have been set properly by the

damage( ) method. Unlike most Swing methods,repaint( ) is thread-safe.



21.1.3 Custom Carets

Let's take a crack at creating our own Caret . The typical way to create a custom caret is to extend

DefaultCaret and

override paint( ) and damage( ) . That's what we'll do forCornerCaret , a simple five-pixel-by-five-pixel, L-shaped

caret, but we'll also add a constructor to make it blink by default. To use this new caret, simply call setCaret(new



CornerCaret( )) on any Swing text component.(The main( ) method is provided for demonstration purposes only.

CornerCaret would be complete without it.)



// CornerCaret.java

//

import javax.swing.*;

import javax.swing.text.*;

import java.awt.*;

public class CornerCaret extends DefaultCaret {

public CornerCaret( ) {

setBlinkRate(500); // Half a second

}

protected synchronized void damage(Rectangle r) {

if (r == null) return;

// Give values to x,y,width,height (inherited from java.awt.Rectangle).

x = r.x;

y = r.y + (r.height * 4 / 5 - 3);

width = 5;

height = 5;

repaint( ); // Calls getComponent( ).repaint(x, y, width, height)

}

public void paint(Graphics g) {

JTextComponent comp = getComponent( );

if (comp == null) return;



This document was created by an unregistered ChmMagic, please go to http://www.bisenter.com to register it. Thanks



.



int dot = getDot( );

Rectangle r = null;

try {

r = comp.modelToView(dot);

} catch (BadLocationException e) { return; }

if (r == null) return;

int dist = r.height * 4 / 5 - 3; // Will be distance from r.y to top

if ( (x != r.x) || (y != r.y + dist) ) {

// paint( ) has been called directly, without a previous call to

// damage( ), so do some cleanup. (This happens, for example, when the

// text component is resized.)

repaint( ); // Erase previous location of caret.

x = r.x; // Set new values for x,y,width,height.

y = r.y + dist;

width = 5;

height = 5;

}

if ( isVisible( ) ) {

g.setColor(comp.getCaretColor( ));

g.drawLine(r.x, r.y + dist, r.x, r.y + dist + 4); // Five vertical pixels

g.drawLine(r.x, r.y + dist + 4, r.x + 4, r.y + dist + 4); // Five horiz px

}

}

public static void main(String args[]) {

JFrame frame = new JFrame("CornerCaret demo");

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

JTextArea area = new JTextArea(8, 32);

area.setCaret(new CornerCaret( ));

area.setText("This is the story\nof the hare who\nlost his spectacles.");

frame.getContentPane( ).add(new JScrollPane(area), BorderLayout.CENTER);

frame.pack( );

frame.setVisible(true);

}

}

There are several things worth mentioning here.

First, damage( ) gets a Rectangle directly as a parameter, butpaint( ) has to obtain one manually via



getComponent( ).modelToView( ) (or getComponent( ).getUI( ).modelToView( )). Despite this, the fields of

the rectangles are the same. The x and y fields are the coordinates of the cursor, thoughy is at the top of the text, not

the baseline. The value of height depends on the font, but the value ofwidth is meaningless (probably0).



This document was created by an unregistered ChmMagic, please go to http://www.bisenter.com to register it. Thanks



The paint( ) method should check whether the cursor is visible before drawing anything and is expected to honor the

value of the component's caretColor property. Because the caret is drawn "over" (after) the component's content,

large cursors must take care not to obscure the text underneath. (One way to handle this is to draw in XOR

[eXclusive OR] mode.) See Figure 21-2.



Figure 21-2. Demonstrations of the CornerCaret and FancyCaret



The damage( ) method is responsible for setting the value of the caret'sx, y, width, andheight fields (inherited from



Rectangle) to cover anything that is drawn bypaint( ). If not, only part (or possibly none) of the caret actually

appears after paint( ) is called, or caret fragments may be left behind when the caret moves. Finally,

damage( ) calls

the caret's repaint( ) method, which eventually causes thepaint( ) method to be called.

FancyCaret is more complicated thanCornerCaret because it is rendered using the width of the character it is on.

It also draws the caret in XOR mode, which allows the text to show through the caret.

When a pixel is drawn in XOR mode, its new color does not necessarily become the drawing color. Instead, its new

color becomes a mix of its previous color and the drawing color. If the previous color is the same as the drawing

color, the new color is the XOR color. If the previous color is the same as the XOR color, the new color is the drawing

color. If the previous color is a third color, the new color is some undefined (but reasonable) other color. FancyCaret

takes advantage of this by setting the drawing color to the caret color and the XOR color to the component's

background color. Here's the code:



// FancyCaret.java

//



This document was created by an unregistered ChmMagic, please go to http://www.bisenter.com to register it. Thanks



import javax.swing.*;

import javax.swing.text.*;

import java.awt.*;

public class FancyCaret extends DefaultCaret {

protected synchronized void damage(Rectangle r) {

if (r == null) return;

// Give values to x,y,width,height (inherited from java.awt.Rectangle).

x = r.x;

y = r.y;

height = r.height;

// A value for width was probably set by paint( ), which we leave alone. But the

// first call to damage( ) precedes the first call to paint( ), so in this case we

// must be prepared to set a valid width or else paint( ) receives a bogus clip

// area, and caret is not drawn properly.

if (width <= 0) width = getComponent( ).getWidth( );

repaint( ); // Calls getComponent( ).repaint(x, y, width, height)

}

public void paint(Graphics g) {

JTextComponent comp = getComponent( );

if (comp == null) return;

int dot = getDot( );

Rectangle r = null;

char dotChar;

try {

r = comp.modelToView(dot);

if (r == null) return;

dotChar = comp.getText(dot, 1).charAt(0);

} catch (BadLocationException e) { return; }

if ( (x != r.x) || (y != r.y) ) {

// paint( ) has been called directly, without a previous call to

// damage( ), so do some cleanup. (This happens, for example, when the

// text component is resized.)

repaint( ); // Erase previous location of caret.

x = r.x; // Update dimensions (width is set later in this method).

y = r.y;

height = r.height;

}

g.setColor(comp.getCaretColor( ));



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

Chapter 21. Carets, Highlighters, and Keymaps

Tải bản đầy đủ ngay(0 tr)

×