home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 1998 November
/
Chip_1998-11_cd.bin
/
tema
/
Cafe
/
jfc.bin
/
JTextComponent.java
< prev
next >
Wrap
Text File
|
1998-02-26
|
67KB
|
2,100 lines
/*
* @(#)JTextComponent.java 1.92 98/02/06
*
* Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
*
* This software is the confidential and proprietary information of Sun
* Microsystems, Inc. ("Confidential Information"). You shall not
* disclose such Confidential Information and shall use it only in
* accordance with the terms of the license agreement you entered into
* with Sun.
*
* SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
* SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
* SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
* THIS SOFTWARE OR ITS DERIVATIVES.
*
*/
package com.sun.java.swing.text;
import java.util.Hashtable;
import java.util.Enumeration;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.datatransfer.*;
import java.text.*;
import com.sun.java.swing.*;
import com.sun.java.swing.event.*;
import com.sun.java.swing.plaf.*;
import com.sun.java.accessibility.*;
/**
* <p>
* JTextComponent is the base class for swing text components. It
* tries to be compatible with the java.awt.TextComponent class
* where it can reasonably do so. Also provided are other services
* for additional flexibility (beyond the pluggable UI and bean
* support).
* <dl>
* <dt><b><font size=+1>Caret Changes</font></b>
* <dd>
* The caret is a pluggable object in swing text components.
* Notification of changes to the caret position and the selection
* are sent to implementations of the CaretListener interface that
* have been registered with the text component. The UI will
* install a default caret unless a customized caret has been
* set.
* <dt><b><font size=+1>Commands</font></b>
* <dd>
* <p>
* Text components provide a number of commands that can be used
* to manipulate the component. This is essentially the way that
* the component expresses it's capabilities. These are expressed
* in terms of the swing Action interface, using the TextAction
* implementation. The set of commands supported by the text
* component can be found with the
* <a href="#getActions">getActions</a> method. These actions
* can be bound to key events, fired from buttons, etc.
*
* <dt><b><font size=+1>Keymaps</font></b>
* <dd>
* <p>
* To facilitate flexible use of the keyboard, support for
* creating keymaps and binding various keystrokes to some kind of
* action is provided. In order to allow keymaps to be shared across
* multiple text components, they can use actions that extend TextAction.
* TextAction can determine which JTextComponent most recently has or had
* focus and therefore is the subject of the action (In the case that the
* ActionEvent sent to the action doesn't contain the target text component
* as it's source).
* <p>
* The use of the keymap is encouraged, but backward compatibilty
* with the awt mechanism is provided by giving the
* listeners a chance to steal the event by consuming it.
* Keyboard event distribution is handled the following order, with
* each distribution capable of consuming the event.
* <ol>
* <li>focus manager
* <li>registered KeyListener's
* <li>keymap handling using the current keymap
* <li>keyboard handling in JComponent (eg accelerators,
* component navigation, etc).
* </ol>
* <p>
* By default the component will create a keymap (named <b>DEFAULT_KEYMAP</b>)
* that is shared by all JTextComponent instances as the default keymap.
* Typically a look-and-feel implementation will install a different keymap
* that resolves to the default keymap for those bindings not found in the
* different keymap. The minimal bindings include:
* <ul>
* <li>inserting content into the editor for the
* printable keys.
* <li>removing content with the backspace and del
* keys.
* <li>caret movement forward and backward
* </ul>
*
* <dt><b><font size=+1>Model/View Split</font></b>
* <dd>
* <p>
* The text components have a model-view split. A text component pulls
* together the objects used to represent the model, view, and controller.
* The text document model may be shared by other views which act as observers
* of the model (eg. a document may be shared by multiple components).
*
* <p align=center><img src="images/editor.gif" HEIGHT=358 WIDTH=587></p>
*
* <p>
* The model is defined by the
* <a href="com.sun.java.swing.text.Document.html">Document</a>
* interface. This is intended to provide a flexible text storage mechanism
* that tracks change during edits and can be extended to more sophisticated
* models. The model interfaces are meant to capture the capabilities of
* expression given by SGML, a system used to express a wide variety of
* content. Changes to the model are sent in the form of a
* Each modification to the document causes notification of the
* details of the change to be sent to all observers in the form of a
* <a href="com.sun.java.swing.event.DocumentEvent.html">DocumentEvent</a>
* which allows the views to stay up to date with the model.
* This event is sent to observers that have implemented the
* <a href="com.sun.java.swing.event.DocumentListener.html">DocumentListener</a>
* interface and registered interest with the model being observed.
*
* <dt><b><font size=+1>Location Information</font></b>
* <dd>
* The capability of determining the location of text in
* the view is provided. There are two methods,
* <a href="#modelToView">modelToView</a> and
* <a href="#viewToModel">viewToModel</a> for determining
* this information.
* <dt><b><font size=+1>Undo/Redo support</font></b>
* <dd>
* Support of an edit history mechanism is provided to allow
* undo/redo operations. The text component does not itself
* provide the history buffer by default, but does provide
* the UndoableEdit records that can be used in conjunction
* with a history buffer to provide the undo/redo support.
* The support is provided by the Document model, which allows
* one to attach UndoableEditListener implementations.
*
* <dt><b><font size=+1>Thread Safety</font></b>
* <dd>
* The swing text components provide some support of thread
* safe operations. Because of the high level of configurability
* of the text components, it is possible to circumvent the
* protection provided. The protection primarily comes from
* the model, so the documentation of AbstractDocument
* describes the assumptions of the protection provided.
* The methods that are safe to call asynchronously are marked
* with comments.
* </dl>
*
* <p>
* Warning: serialized objects of this class will not be compatible with
* future swing releases. The current serialization support is appropriate
* for short term storage or RMI between Swing1.0 applications. It will
* not be possible to load serialized Swing1.0 objects with future releases
* of Swing. The JDK1.2 release of Swing will be the compatibility
* baseline for the serialized form of Swing objects.
*
* @beaninfo
* attribute: isContainer false
*
* @author Timothy Prinzing
* @version 1.92 02/06/98
* @see Document
* @see DocumentEvent
* @see DocumentListener
* @see Caret
* @see CaretEvent
* @see CaretListener
* @see TextUI
* @see View
* @see ViewFactory
*/
public abstract class JTextComponent extends JComponent implements Scrollable,
Accessible
{
/**
* Creates a new JTextComponent.
*/
public JTextComponent() {
super();
enableEvents(AWTEvent.KEY_EVENT_MASK);
caretEvent = new MutableCaretEvent(this);
addMouseListener(caretEvent);
addFocusListener(caretEvent);
setEditable(true);
setLayout(null); // layout is managed by View hierarchy
updateUI();
}
/**
* Fetches the user-interface factory for this text-oriented editor.
*
* @return the factory
*/
public TextUI getUI() { return (TextUI)ui; }
/**
* Sets the user-interface factory for this text-oriented editor
*
* @param ui the factory
*/
public void setUI(TextUI ui) {
super.setUI(ui);
}
/**
* Reloads the pluggable UI. The key used to fetch the
* new interface is <b>getUIClassID()</b>. The type of
* the UI is <b>TextUI</b>.
*/
public void updateUI() {
setUI((TextUI)UIManager.getUI(this));
invalidate();
}
/**
* Returns true if this component is completely opaque.
*
* @return true if this component is completely opaque.
*/
public boolean isOpaque() {
return opaque;
}
/**
* Sets whether or not the UI should render a background.
*
* @param o true if should render a background
*/
public void setOpaque(boolean o) {
opaque = o;
}
/**
* Adds a caret listener for notification of any changes
* to the caret.
*
* @param listener the listener
* @see com.sun.java.swing.event.CaretEvent
*/
public void addCaretListener(CaretListener listener) {
listenerList.add(CaretListener.class, listener);
}
/**
* Removes a caret listener.
*
* @param listener the listener
* @see com.sun.java.swing.event.CaretEvent
*/
public void removeCaretListener(CaretListener listener) {
listenerList.remove(CaretListener.class, listener);
}
/**
* Notifies all listeners that have registered interest for
* notification on this event type. The event instance
* is lazily created using the parameters passed into
* the fire method.
*
* @param e the event
* @see EventListenerList
*/
protected void fireCaretUpdate(CaretEvent e) {
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i = listeners.length-2; i>=0; i-=2) {
if (listeners[i]==CaretListener.class) {
((CaretListener)listeners[i+1]).caretUpdate(e);
}
}
}
/**
* Associates the editor with a text document.
* The currently registered factory is used to build a view for
* the document, which gets displayed by the editor.
*
* @param doc the document to display/edit
* @see #getDocument
* @beaninfo
* description: the text document model
* bound: true
* expert: true
*/
public void setDocument(Document doc) {
if (accessibleContext != null) {
model.removeDocumentListener(
((AccessibleJTextComponent)accessibleContext));
}
Document old = model;
model = doc;
firePropertyChange("document", old, doc);
revalidate();
repaint();
if (accessibleContext != null) {
model.addDocumentListener(
((AccessibleJTextComponent)accessibleContext));
}
}
/**
* Fetches the model associated with the editor. This is
* primarily for the UI to get at the minimal amount of
* state required to be a text editor. Subclasses will
* return the actual type of the model which will typically
* be something that extends Document.
*
* @return the model
*/
public Document getDocument() {
return model;
}
/**
* Fetches the command list for the editor. This is
* the list of commands supported by the plugged-in UI
* augmented by the collection of commands that the
* editor itself supports. These are useful for binding
* to events, such as in a keymap.
*
* @return the command list
*/
public Action[] getActions() {
return getUI().getEditorKit().getActions();
}
/**
* Sets margin space between the text component's border
* and its text. Setting it to null will cause the text component
* to use a default margin. The text component's default Border
* object will use this value to create the proper margin.
* However, if a non-default border is set on the text component,
* it is that Border object's responsibility to create the
* appropriate margin space (else this property will effectively
* be ignored).
*
* @param m the space between the border and the text
* @beaninfo
* description: desired space between the border and text area
* bound: true
*/
public void setMargin(Insets m) {
Insets old = margin;
margin = m;
firePropertyChange("margin", old, m);
invalidate();
}
/**
* Returns the margin between the text component's border and
* its text.
*
* @return the margin
*/
public Insets getMargin() {
if(margin == null) {
return ((TextUI)ui).getDefaultMargin();
} else {
return margin;
}
}
/**
* Fetches the cursor that allows text-oriented navigation over
* the view.
*
* @return the cursor
*/
public Caret getCaret() {
return caret;
}
/**
* Sets the caret to be used. By default this will be set
* by the UI that gets installed. This can be changed to
* a custom caret if desired.
*
* @param c the caret
* @see #getCaret
* @beaninfo
* description: the caret used to select/navigate
* bound: true
* expert: true
*/
public void setCaret(Caret c) {
if (caret != null) {
caret.removeChangeListener(caretEvent);
caret.deinstall(this);
}
Caret old = caret;
caret = c;
if (caret != null) {
caret.install(this);
caret.addChangeListener(caretEvent);
}
firePropertyChange("caret", old, caret);
}
/**
* Fetches the object responsible for making highlights.
*
* @return the highlighter
*/
public Highlighter getHighlighter() {
return highlighter;
}
/**
* Sets the highlighter to be used. By default this will be set
* by the UI that gets installed. This can be changed to
* a custom highlighter if desired.
*
* @param h the highlighter
* @see #getHighlighter
* @beaninfo
* description: object responsible for background highlights
* bound: true
* expert: true
*/
public void setHighlighter(Highlighter h) {
if (highlighter != null) {
highlighter.deinstall(this);
}
Highlighter old = highlighter;
highlighter = h;
if (highlighter != null) {
highlighter.install(this);
}
firePropertyChange("highlighter", old, h);
}
/**
* Sets the keymap to use for binding events to
* actions.
*
* @param map the keymap
* @see #getKeymap
* @beaninfo
* description: set of key event to action bindings to use
* bound: true
*/
public void setKeymap(Keymap map) {
Keymap old = keymap;
keymap = map;
firePropertyChange("keymap", old, keymap);
}
/**
* Fetches the keymap being used by the controller
*
* @return the keymap
*/
public Keymap getKeymap() {
return keymap;
}
/**
* Adds a new keymap into the keymap hierarchy. Keymap bindings
* resolve from bottom up so an attribute specified in a child
* will override an attribute specified in the parent.
*
* @param nm the name of the keymap (must be unique within the
* collection of named keymaps in the document). The name may
* be null if the keymap is unnamed, but the caller is responsible
* for managing the reference returned as an unnamed keymap can't
* be fetched by name.
* @param parent the parent keymap. This may be null if unspecified
* bindings need not be resolved in some other keymap.
* @return the keymap
*/
public static Keymap addKeymap(String nm, Keymap parent) {
Keymap map = new DefaultKeymap(nm, parent);
if (nm != null) {
// add a named keymap, a class of bindings
keymapTable.put(nm, map);
}
return map;
}
/**
* Removes a named keymap previously added to the document.
*
* @param nm the name of the keymap to remove
* @return the keymap
*/
public static Keymap removeKeymap(String nm) {
return (Keymap) keymapTable.remove(nm);
}
/**
* Fetches a named keymap previously added to the document.
*
* @param nm the name of the keymap
* @return the keymap
*/
public static Keymap getKeymap(String nm) {
return (Keymap) keymapTable.get(nm);
}
/**
* Binding record for creating key bindings
* <p>
* Warning: serialized objects of this class will not be compatible with
* future swing releases. The current serialization support is appropriate
* for short term storage or RMI between Swing1.0 applications. It will
* not be possible to load serialized Swing1.0 objects with future releases
* of Swing. The JDK1.2 release of Swing will be the compatibility
* baseline for the serialized form of Swing objects.
*/
public static class KeyBinding {
/**
* The key.
*/
public KeyStroke key;
/**
* The name of the action for the key.
*/
public String actionName;
/**
* Creates a new key binding.
*
* @param key the key
* @param actionName the name of the action for the key
*/
public KeyBinding(KeyStroke key, String actionName) {
this.key = key;
this.actionName = actionName;
}
}
/**
* <p>
* Loads a keymap with a bunch of
* bindings. This can be used to take a static table of
* definitions and load them into some keymap. The following
* example illustrates an example of binding some keys to
* the cut, copy, and paste actions associated with a
* JTextComponent. It also binds tab to insert the tab
* into the text component. A code fragment to accomplish
* this might look as follows:
* <pre>
* JTextComponent c = new JTextPane();
* Keymap k = c.getKeymap();
* JTextComponent.loadKeymap(k, c.getActions(), defaultBindings);
*
* static final JTextComponent.KeyBinding[] defaultBindings = {
* new JTextComponent.KeyBinding(
* KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_MASK),
* JTextComponent.copyAction),
* new JTextComponent.KeyBinding(
* KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_MASK),
* JTextComponent.pasteAction),
* new JTextComponent.KeyBinding(
* KeyStroke.getKeyStroke(KeyEvent.VK_X, InputEvent.CTRL_MASK),
* JTextComponent.cutAction),
* new JTextComponent.KeyBinding(
* KeyStroke.getKeyStroke('\t'),
* JTextComponent.insertContentAction)
* };
* </pre>
*
* @param map the keymap
* @param bindings the bindings
* @param actions the set of actions
*/
public static void loadKeymap(Keymap map, KeyBinding[] bindings, Action[] actions) {
Hashtable h = new Hashtable();
for (int i = 0; i < actions.length; i++) {
Action a = actions[i];
String value = (String)a.getValue(Action.NAME);
h.put((value!=null ? value:""), a);
}
for (int i = 0; i < bindings.length; i++) {
Action a = (Action) h.get(bindings[i].actionName);
if (a != null) {
map.addActionForKeyStroke(bindings[i].key, a);
}
}
}
/**
* Maps an event to an action if one is defined in the
* installed keymap, and perform the action. If the action is
* performed, the event is consumed.
* @returns true if an action was performed, false otherwise.
*/
private final boolean mapEventToAction(KeyEvent e) {
Keymap binding = getKeymap();
if (binding != null) {
KeyStroke k = KeyStroke.getKeyStrokeForEvent(e);
Action a = binding.getAction(k);
if (a != null) {
String command = null;
if (e.getKeyChar() != KeyEvent.CHAR_UNDEFINED) {
command = String.valueOf(e.getKeyChar());
}
ActionEvent ae = new ActionEvent(this,
ActionEvent.ACTION_PERFORMED,
command, e.getModifiers());
a.actionPerformed(ae);
e.consume();
return true;
}
}
return false;
}
/**
* Fetches the current color used to render the
* caret.
*
* @return the color
*/
public Color getCaretColor() {
return caretColor;
}
/**
* Sets the current color used to render the
* caret.
*
* @param c the color
* @see #getCaretColor
* @beaninfo
* description: the color used to render the caret
* bound: true
* preferred: true
*/
public void setCaretColor(Color c) {
Color old = caretColor;
caretColor = c;
firePropertyChange("caretColor", old, caretColor);
}
/**
* Fetches the current color used to render the
* selection.
*
* @return the color
*/
public Color getSelectionColor() {
return selectionColor;
}
/**
* Sets the current color used to render the
* selection.
*
* @param c the color
* @see #getSelectionColor
* @beaninfo
* description: color used to render selection background
* bound: true
* preferred: true
*/
public void setSelectionColor(Color c) {
Color old = selectionColor;
selectionColor = c;
firePropertyChange("selectionColor", old, selectionColor);
}
/**
* Fetches the current color used to render the
* selected text.
*
* @return the color
*/
public Color getSelectedTextColor() {
return selectedTextColor;
}
/**
* Sets the current color used to render the
* selected text.
*
* @param c the color
* @see #getSelectedTextColor
* @beaninfo
* description: color used to render selected text
* bound: true
* preferred: true
*/
public void setSelectedTextColor(Color c) {
Color old = selectedTextColor;
selectedTextColor = c;
firePropertyChange("selectedTextColor", old, selectedTextColor);
}
/**
* Fetches the current color used to render the
* selected text.
*
* @return the color
*/
public Color getDisabledTextColor() {
return disabledTextColor;
}
/**
* Sets the current color used to render the
* disabled text.
*
* @param c the color
* @see #getDisabledTextColor
* @beaninfo
* description: color used to render disabled text
* bound: true
* preferred: true
*/
public void setDisabledTextColor(Color c) {
Color old = disabledTextColor;
disabledTextColor = c;
firePropertyChange("disabledTextColor", old, disabledTextColor);
}
/**
* Replaces the currently selected content with new content
* represented by the given string. If there is no selection
* this amounts to an insert of the given text. If there
* is no replacement text this amounts to a removal of the
* current selection.
* <p>
* This is the method that is used by the default implementation
* of the action for inserting content that gets bound to the
* keymap actions.
* <p>
* This method is thread safe, although most Swing methods
* are not. Please see
* <A HREF="http://java.sun.com/products/jfc/swingdoc/threads.html">Threads
* and Swing</A> for more information.
*
* @param content the content to replace the selection with
*/
public void replaceSelection(String content) {
if (! isEditable()) {
getToolkit().beep();
return;
}
Document doc = getDocument();
if (doc != null) {
try {
int p0 = Math.min(caret.getDot(), caret.getMark());
int p1 = Math.max(caret.getDot(), caret.getMark());
if (p0 != p1) {
doc.remove(p0, p1 - p0);
}
if (content != null && content.length() > 0) {
doc.insertString(p0, content, null);
}
} catch (BadLocationException e) {
getToolkit().beep();
}
}
}
/**
* Fetches a portion of the text represented by the
* component.
*
* @param offs the offset
* @param len the length
* @return the text
* @exception BadLocationException of the offset or length are invalid
*/
public String getText(int offs, int len) throws BadLocationException {
return getDocument().getText(offs, len);
}
/**
* Converts the given location in the model to a place in
* the view coordinate system.
*
* @param pos the position
* @return the coordinates as a rectangle
* @exception BadLocationException if the given position does not represent a
* valid location in the associated document
* @see TextUI#modelToView
*/
public Rectangle modelToView(int pos) throws BadLocationException {
return getUI().modelToView(pos);
}
/**
* Converts the given place in the view coordinate system
* to the nearest representative location in the model.
*
* @param pt the location in the view to translate
* @return the offset from the start of the document
* @see TextUI#viewToModel
*/
public int viewToModel(Point pt) {
return getUI().viewToModel(pt);
}
/**
* Transfers the currently selected range in the associated
* text model to the system clipboard, removing the contents
* from the model. The current selection is reset.
*/
public void cut() {
try {
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
int p0 = Math.min(caret.getDot(), caret.getMark());
int p1 = Math.max(caret.getDot(), caret.getMark());
if (p0 != p1) {
Document doc = getDocument();
String srcData = doc.getText(p0, p1 - p0);
StringSelection contents = new StringSelection(srcData);
clipboard.setContents(contents, defaultClipboardOwner);
doc.remove(p0, p1 - p0);
}
} catch (BadLocationException e) {
}
}
/**
* Transfers the currently selected range in the associated
* text model to the system clipboard, leaving the contents
* in the text model. The current selection is remains intact.
*/
public void copy() {
try {
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
int p0 = Math.min(caret.getDot(), caret.getMark());
int p1 = Math.max(caret.getDot(), caret.getMark());
if (p0 != p1) {
Document doc = getDocument();
String srcData = doc.getText(p0, p1 - p0);
StringSelection contents = new StringSelection(srcData);
clipboard.setContents(contents, defaultClipboardOwner);
}
} catch (BadLocationException e) {
}
}
/**
* Transfers the contents of the system clipboard into the
* associated text model. If there is a selection in the
* associated view, it is replaced with the contents of the
* clipboard. If there is no selection, the clipboard contents
* are inserted in front of the current insert position in
* the associated view.
* @see #replaceSelection
*/
public void paste() {
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
Transferable content = clipboard.getContents(this);
if (content != null) {
try {
String dstData = (String)(content.getTransferData(DataFlavor.stringFlavor));
replaceSelection(dstData);
} catch (Exception e) {
System.err.println("Couldn't get clipboard contents in format: "+
DataFlavor.stringFlavor.getHumanPresentableName());
}
}
}
/**
* Moves the caret to a new position, leaving behind a
* mark defined by the last time setCaretPosition was
* called. This forms a selection.
*
* @param pos the position
* @see #setCaretPosition
*/
public void moveCaretPosition(int pos) {
caret.moveDot(pos);
}
/**
* The bound property name for the focus accelerator.
*/
public static final String FOCUS_ACCELERATOR_KEY = "focusAcceleratorKey";
/**
* Sets the key accelerator that will cause the receiving text
* component to get the focus. The accelerator will be the
* key combination of the <em>alt</em> key and the character
* given. By default, there is no focus accelerator key.
*
* @param aKey the key
* @see getFocusAccelerator
* @beaninfo
* description: accelerator character used to grab focus
* bound: true
*/
public void setFocusAccelerator(char aKey) {
aKey = Character.toUpperCase(aKey);
KeyStroke[] keyStrokes = getRegisteredKeyStrokes();
int i,c;
for(i=0,c=keyStrokes.length;i<c;i++) {
if(getActionForKeyStroke(keyStrokes[i]) == focusAction) {
if(keyStrokes[i].getKeyChar() == aKey)
return;
else
unregisterKeyboardAction(keyStrokes[i]);
break;
}
}
if(aKey != '\0') {
registerKeyboardAction(focusAction,KeyStroke.getKeyStroke(aKey,ActionEvent.ALT_MASK),
JComponent.WHEN_IN_FOCUSED_WINDOW);
}
char old = focusAccelerator;
focusAccelerator = aKey;
firePropertyChange(FOCUS_ACCELERATOR_KEY, old, focusAccelerator);
}
/**
* Returns the key accelerator that will cause the receiving
* text component to get the focus. Return '\0' if no focus
* accelerator has been set.
*
* @return the key
*/
public char getFocusAccelerator() {
return focusAccelerator;
}
/**
* Initialize from a stream. This creates a
* model of the type appropriate for the component
* and initializes the model from the stream.
* By default this will load the model as plain
* text.
*
* @param in The stream to read from
* @param desc An object describing the stream. This
* might be a string, a File, a URL, etc. Some kinds
* of documents (such as html for example) might be
* able to make use of this information.
* @exception IOException as thrown by the stream being
* used to initialize.
* @see EditorKit#createDefaultDocument
* @see #setDocument
* @see PlainDocument
*/
public void read(Reader in, Object desc) throws IOException {
EditorKit kit = getUI().getEditorKit();
Document doc = kit.createDefaultDocument();
if (desc != null) {
doc.putProperty(Document.StreamDescriptionProperty, desc);
}
try {
kit.read(in, doc, 0);
setDocument(doc);
} catch (BadLocationException e) {
throw new IOException(e.getMessage());
}
}
/**
* Stores the contents of the model into the given
* stream. By default this will store the model as plain
* text.
*
* @param out the output stream
* @exception IOException on any I/O error
*/
public void write(Writer out) throws IOException {
Document doc = getDocument();
try {
getUI().getEditorKit().write(out, doc, 0, doc.getLength());
} catch (BadLocationException e) {
throw new IOException(e.getMessage());
}
}
// --- java.awt.Component methods ----------------------------
/**
* Returns true if the focus can be traversed.
*
* @return true if the focus is traversable
*/
public boolean isFocusTraversable() {
return isEnabled();
}
/**
* Process any key events that the component itself
* recognizes. This will be called after the focus
* manager and any interested listeners have been
* given a chance to steal away the event. This
* method will only be called is the event has not
* yet been consumed. This method is called prior
* to the keyboard UI logic.
* <p>
* This is implemented to do nothing. Subclasses would
* normally override this method if they process some
* key events themselves. If the event is processed,
* it should be consumed.
*
* @param e the event
*/
protected void processComponentKeyEvent(KeyEvent e) {
int id = e.getID();
switch(id) {
case KeyEvent.KEY_TYPED:
if (mapEventToAction(e) == false) {
// default behavior is to input translated
// characters as content if the character
// hasn't been mapped in the keymap.
Keymap binding = getKeymap();
if (binding != null) {
Action a = binding.getDefaultAction();
if (a != null) {
ActionEvent ae = new ActionEvent(this,
ActionEvent.ACTION_PERFORMED,
String.valueOf(e.getKeyChar()),
e.getModifiers());
a.actionPerformed(ae);
e.consume();
}
}
}
break;
case KeyEvent.KEY_PRESSED:
mapEventToAction(e);
break;
case KeyEvent.KEY_RELEASED:
mapEventToAction(e);
break;
}
}
// --- java.awt.TextComponent methods ------------------------
/**
* Sets the position of the text insertion caret for the TextComponent.
* Note that the caret tracks change, so this may move if the underlying
* text of the component is changed.
*
* @param position the position
* @exception IllegalArgumentException if position is less than 0
* or greater than the length of the associated document
* @beaninfo
* description: the caret position
*/
public void setCaretPosition(int position) {
Document doc = getDocument();
if (doc != null) {
caret.setDot(position);
}
}
/**
* Returns the position of the text insertion caret for the
* text component.
*
* @return the position of the text insertion caret for the
* text component
*/
public int getCaretPosition() {
return caret.getDot();
}
/**
* Sets the text of this TextComponent to the specified text.
* <p>
* This method is thread safe, although most Swing methods
* are not. Please see
* <A HREF="http://java.sun.com/products/jfc/swingdoc/threads.html">Threads
* and Swing</A> for more information.
*
* @param t the new text to be set
* @see #getText
* @beaninfo
* description: the text of this component
*/
public void setText(String t) {
try {
Document doc = getDocument();
doc.remove(0, doc.getLength());
doc.insertString(0, t, null);
} catch (BadLocationException e) {
getToolkit().beep();
}
}
/**
* Returns the text contained in this TextComponent.
*
* @return the text
* @see #setText
*/
public String getText() {
Document doc = getDocument();
String txt;
try {
txt = doc.getText(0, doc.getLength());
} catch (BadLocationException e) {
txt = null;
}
return txt;
}
/**
* Returns the selected text contained in this TextComponent.
*
* @return the text
* @exception IllegalArgumentException if the selection doesn't
* have a valid mapping into the document for some reason
* @see #setText
*/
public String getSelectedText() {
String txt = null;
int p0 = Math.min(caret.getDot(), caret.getMark());
int p1 = Math.max(caret.getDot(), caret.getMark());
if (p0 != p1) {
try {
Document doc = getDocument();
txt = doc.getText(p0, p1 - p0);
} catch (BadLocationException e) {
throw illegalDocumentPosition;
}
}
return txt;
}
/**
* Returns the boolean indicating whether this TextComponent is
* editable or not.
*
* @return the boolean value
* @see #setEditable
*/
public boolean isEditable() {
return editable;
}
/**
* Sets the specified boolean to indicate whether or not this
* TextComponent should be editable.
*
* @param b the boolean to be set
* @see #isEditable
* @beaninfo
* description: specifies if the text can be edited
*/
public void setEditable(boolean b) {
editable = b;
}
/**
* Returns the selected text's start position.
*
* @return the start position
*/
public int getSelectionStart() {
int start = Math.min(caret.getDot(), caret.getMark());
return start;
}
/**
* Sets the selection start to the specified position. The new
* starting point is constrained to be before or at the current
* selection end.
* <p>
* This is available for backward compatiblitity to code
* that called this method on java.awt.TextComponent. This is
* implemented to forward to the Caret implementation which
* is where the actual selection is maintained.
*
* @param selectionStart the start position of the text
* @beaninfo
* description: starting location of the selection.
*/
public void setSelectionStart(int selectionStart) {
/* Route through select method to enforce consistent policy
* between selectionStart and selectionEnd.
*/
select(selectionStart, getSelectionEnd());
}
/**
* Returns the selected text's end position.
*
* @return the end position
*/
public int getSelectionEnd() {
int end = Math.max(caret.getDot(), caret.getMark());
return end;
}
/**
* Sets the selection end to the specified position. The new
* end point is constrained to be at or after the current
* selection start.
* <p>
* This is available for backward compatiblitity to code
* that called this method on java.awt.TextComponent. This is
* implemented to forward to the Caret implementation which
* is where the actual selection is maintained.
*
* @param selectionEnd the start position of the text
* @beaninfo
* description: ending location of the selection.
*/
public void setSelectionEnd(int selectionEnd) {
/* Route through select method to enforce consistent policy
* between selectionStart and selectionEnd.
*/
select(getSelectionStart(), selectionEnd);
}
/**
* Selects the text found between the specified start and end
* locations. This call is provided for backward compatibility.
* It is routed to a call to setCaretPosition
* followed by a call to moveCaretPostion. The preferred way
* to manage selection is by calling those methods directly.
*
* @param selectionStart the start position of the text
* @param selectionEnd the end position of the text
* @see setCaretPosition
* @see moveCaretPosition
*/
public void select(int selectionStart, int selectionEnd) {
setCaretPosition(selectionStart);
moveCaretPosition(selectionEnd);
}
/**
* Selects all the text in the TextComponent.
*/
public void selectAll() {
Document doc = getDocument();
if (doc != null) {
setCaretPosition(0);
moveCaretPosition(doc.getLength());
}
}
// --- Scrollable methods ---------------------------------------------
/**
* Returns the preferred size of the viewport for a view component.
* This is implemented to do the default behavior of returning
* the preferred size of the component.
*
* @return The preferredSize of a JViewport whose view is this Scrollable.
*/
public Dimension getPreferredScrollableViewportSize() {
return getPreferredSize();
}
/**
* Components that display logical rows or columns should compute
* the scroll increment that will completely expose one new row
* or column, depending on the value of orientation. Ideally,
* components should handle a partially exposed row or column by
* returning the distance required to completely expose the item.
* <p>
* The default implementation of this is to simply return 10% of
* the visible area. Subclasses are likely to be able to provide
* a much more reasonable value.
*
* @param visibleRect The view area visible within the viewport
* @param orientation Either SwingConstants.VERTICAL or SwingConstants.HORIZONTAL.
* @param direction Less than zero to scroll up/left, greater than zero for down/right.
* @return The "unit" increment for scrolling in the specified direction
* @see JScrollBar#setUnitIncrement
*/
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
switch(orientation) {
case SwingConstants.VERTICAL:
return visibleRect.height / 10;
case SwingConstants.HORIZONTAL:
return visibleRect.width / 10;
default:
throw new IllegalArgumentException("Invalid orientation: " + orientation);
}
}
/**
* Components that display logical rows or columns should compute
* the scroll increment that will completely expose one block
* of rows or columns, depending on the value of orientation.
* <p>
* The default implementation of this is to simply return the visible
* area. Subclasses will likely be able to provide a much more
* reasonable value.
*
* @param visibleRect The view area visible within the viewport
* @param orientation Either SwingConstants.VERTICAL or SwingConstants.HORIZONTAL.
* @param direction Less than zero to scroll up/left, greater than zero for down/right.
* @return The "block" increment for scrolling in the specified direction.
* @see JScrollBar#setBlockIncrement
*/
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
switch(orientation) {
case SwingConstants.VERTICAL:
return visibleRect.height;
case SwingConstants.HORIZONTAL:
return visibleRect.width;
default:
throw new IllegalArgumentException("Invalid orientation: " + orientation);
}
}
/**
* Return true if a viewport should always force the width of this
* Scrollable to match the width of the viewport. For example a noraml
* text view that supported line wrapping would return true here, since it
* would be undesirable for wrapped lines to disappear beyond the right
* edge of the viewport. Note that returning true for a Scrollable
* whose ancestor is a JScrollPane effectively disables horizontal
* scrolling.
* <p>
* Scrolling containers, like JViewport, will use this method each
* time they are validated.
*
* @return True if a viewport should force the Scrollables width to match its own.
*/
public boolean getScrollableTracksViewportWidth() {
return false;
}
/**
* Return true if a viewport should always force the height of this
* Scrollable to match the height of the viewport. For example a
* columnar text view that flowed text in left to right columns
* could effectively disable vertical scrolling by returning
* true here.
* <p>
* Scrolling containers, like JViewport, will use this method each
* time they are validated.
*
* @return True if a viewport should force the Scrollables height to match its own.
*/
public boolean getScrollableTracksViewportHeight() {
return false;
}
/////////////////
// Accessibility support
////////////////
/**
* Get the AccessibleContext associated with this JComponent
*
* @return the AccessibleContext of this JComponent
*/
public AccessibleContext getAccessibleContext() {
if (accessibleContext == null) {
accessibleContext = new AccessibleJTextComponent();
}
return accessibleContext;
}
/**
* Accessibility implementation for JTextComponent
* <p>
* Warning: serialized objects of this class will not be compatible with
* future swing releases. The current serialization support is appropriate
* for short term storage or RMI between Swing1.0 applications. It will
* not be possible to load serialized Swing1.0 objects with future releases
* of Swing. The JDK1.2 release of Swing will be the compatibility
* baseline for the serialized form of Swing objects.
*/
public class AccessibleJTextComponent extends AccessibleJComponent
implements AccessibleText, CaretListener, DocumentListener {
int caretPos;
/**
* Construct an AccessibleJTextComponent
*/
public AccessibleJTextComponent() {
Document doc = JTextComponent.this.getDocument();
if (doc != null) {
doc.addDocumentListener(this);
}
JTextComponent.this.addCaretListener(this);
caretPos = getCaretPosition();
}
/**
* Handle caret updates (fire appropriate property change event)
*
* @param e the CaretEvent
*/
public void caretUpdate(CaretEvent e) {
int dot = e.getDot();
int mark = e.getMark();
if (caretPos != dot) {
// the caret moved
firePropertyChange(ACCESSIBLE_CARET_PROPERTY,
new Integer(caretPos), new Integer(dot));
caretPos = dot;
}
if (mark != dot) {
// there is a selection
firePropertyChange(ACCESSIBLE_SELECTION_PROPERTY, null,
getSelectedText());
}
}
// DocumentListener methods
/**
* Handle document insert (fire appropriate property change event)
*
* @param e the DocumentEvent
*/
public void insertUpdate(DocumentEvent e) {
Caret c = JTextComponent.this.getCaret();
Integer dot = new Integer(c.getDot());
firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null, dot);
}
/**
* Handle document remove (fire appropriate property change event)
*
* @param e the DocumentEvent
*/
public void removeUpdate(DocumentEvent e) {
Caret c = JTextComponent.this.getCaret();
Integer dot = new Integer(c.getDot());
firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null, dot);
}
/**
* Handle document remove (fire appropriate property change event)
*
* @param e the DocumentEvent
*/
public void changedUpdate(DocumentEvent e) {
Caret c = JTextComponent.this.getCaret();
Integer dot = new Integer(c.getDot());
firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null, dot);
}
/**
* Get the state set of the JTextComponent.
* The AccessibleStateSet of an object is composed of a set of
* unique AccessibleState's. A change in the AccessibleStateSet
* of an object will cause a PropertyChangeEvent to
* be fired for the ACCESSIBLE_STATE_PROPERTY property.
*
* @return an instance of AccessibleStateSet containing the
* current state set of the object
* @see AccessibleStateSet
* @see AccessibleState
* @see #addPropertyChangeListener
*/
public AccessibleStateSet getAccessibleStateSet() {
AccessibleStateSet states = super.getAccessibleStateSet();
if (JTextComponent.this.isEditable()) {
states.add(AccessibleState.EDITABLE);
}
return states;
}
/**
* Gets the role of this object.
*
* @return an instance of AccessibleRole describing the role of the
* object
* @see AccessibleRole
*/
public AccessibleRole getAccessibleRole() {
return AccessibleRole.TEXT;
}
/**
* Gets the AccessibleText interface associated with this object
*
* @return an instance of AccessibleText
*/
public AccessibleText getAccessibleText() {
return this;
}
// --- interface AccessibleText methods ------------------------
/**
* Many of these methods are just convenience methods; they
* just call the equivalent on the parent
*/
/**
* Given a point in local coordinates, return the zero-based index
* of the character under that Point. If the point is invalid,
* this method returns -1.
*
* @param p the Point in local coordinates
* @return the zero-based index of the character under Point p.
*/
public int getIndexAtPoint(Point p) {
if (p == null) {
return -1;
}
return JTextComponent.this.viewToModel(p);
}
/**
* Determine the bounding box of the character at the given
* index into the string. The bounds are returned in local
* coordinates. If the index is invalid an empty rectangle is returned.
*
* @param i the index into the String
* @return the screen coordinates of the character's the bounding box
*/
public Rectangle getCharacterBounds(int i) {
if (i < 0 || i > model.getLength()-1) {
return null;
}
Rectangle rect;
try {
rect = modelToView(i);
} catch (BadLocationException e) {
rect = null;
}
return rect;
}
/**
* Return the number of characters (valid indicies)
*
* @return the number of characters
*/
public int getCharCount() {
return model.getLength();
}
/**
* Return the zero-based offset of the caret.
*
* Note: That to the right of the caret will have the same index
* value as the offset (the caret is between two characters).
* @return the zero-based offset of the caret.
*/
public int getCaretPosition() {
return JTextComponent.this.getCaretPosition();
}
/**
* Return the AttributeSet for a given character (at a given index)
*
* @param i the zero-based index into the text
* @return the AttributeSet of the character
*/
public AttributeSet getCharacterAttribute(int i) {
Element e = null;
for (e = model.getDefaultRootElement(); ! e.isLeaf(); ) {
int index = e.getElementIndex(i);
e = e.getElement(index);
}
return e.getAttributes();
}
/**
* Returns the start offset within the selected text.
* If there is no selection, but there is
* a caret, the start and end offsets will be the same.
*
* @return the index into the text of the start of the selection
*/
public int getSelectionStart() {
return JTextComponent.this.getSelectionStart();
}
/**
* Returns the end offset within the selected text.
* If there is no selection, but there is
* a caret, the start and end offsets will be the same.
*
* @return the index into teh text of the end of the selection
*/
public int getSelectionEnd() {
return JTextComponent.this.getSelectionEnd();
}
/**
* Returns the portion of the text that is selected.
*
* @return the portion of the text that is selected
*/
public String getSelectedText() {
return JTextComponent.this.getSelectedText();
}
/**
* Return the String at a given index.
*
* @param part the CHARACTER, WORD, or SENTENCE to retrieve
* @param index an index within the text
* @return the letter, word, or sentence
*/
public String getAtIndex(int part, int index) {
if (index < 0 || index > model.getLength()-1) {
return null;
}
switch (part) {
case AccessibleText.CHARACTER:
try {
return model.getText(index, 1);
} catch (BadLocationException e) {
return null;
}
case AccessibleText.WORD:
try {
String s = model.getText(0, model.getLength());
BreakIterator words = BreakIterator.getWordInstance();
words.setText(s);
int end = words.following(index);
return s.substring(words.previous(), end);
} catch (BadLocationException e) {
return null;
}
case AccessibleText.SENTENCE:
try {
String s = model.getText(0, model.getLength());
BreakIterator sentence = BreakIterator.getSentenceInstance();
sentence.setText(s);
int end = sentence.following(index);
return s.substring(sentence.previous(), end);
} catch (BadLocationException e) {
return null;
}
default:
return null;
}
}
/**
* Return the String after a given index.
*
* @param part the CHARACTER, WORD, or SENTENCE to retrieve
* @param index an index within the text
* @return the letter, word, or sentence
*/
public String getAfterIndex(int part, int index) {
if (index < 0 || index > model.getLength()-1) {
return null;
}
switch (part) {
case AccessibleText.CHARACTER:
try {
return model.getText(index+1, 1);
} catch (BadLocationException e) {
return null;
}
case AccessibleText.WORD:
try {
String s = model.getText(0, model.getLength());
BreakIterator words = BreakIterator.getWordInstance();
words.setText(s);
int start = words.following(index);
return s.substring(start, words.following(start));
} catch (BadLocationException e) {
return null;
}
case AccessibleText.SENTENCE:
try {
String s = model.getText(0, model.getLength());
BreakIterator sentence = BreakIterator.getSentenceInstance();
sentence.setText(s);
int start = sentence.following(index);
return s.substring(start, sentence.following(start));
} catch (BadLocationException e) {
return null;
}
default:
return null;
}
}
/**
* Return the String before a given index.
*
* @param part the CHARACTER, WORD, or SENTENCE to retrieve
* @param index an index within the text
* @return the letter, word, or sentence
*/
public String getBeforeIndex(int part, int index) {
if (index < 0 || index > model.getLength()-1) {
return null;
}
switch (part) {
case AccessibleText.CHARACTER:
try {
return model.getText(index-1, 1);
} catch (BadLocationException e) {
return null;
}
case AccessibleText.WORD:
try {
String s = model.getText(0, model.getLength());
BreakIterator words = BreakIterator.getWordInstance();
words.setText(s);
int end = words.next(index);
end = words.previous();
return s.substring(words.previous(), end);
} catch (BadLocationException e) {
return null;
}
case AccessibleText.SENTENCE:
try {
String s = model.getText(0, model.getLength());
BreakIterator sentence = BreakIterator.getSentenceInstance();
sentence.setText(s);
int end = sentence.next(index);
end = sentence.previous();
return s.substring(sentence.previous(), end);
} catch (BadLocationException e) {
return null;
}
default:
return null;
}
}
}
// --- serialization ---------------------------------------------
private void readObject(ObjectInputStream s)
throws ClassNotFoundException, IOException
{
s.defaultReadObject();
caretEvent = new MutableCaretEvent(this);
addMouseListener(caretEvent);
addFocusListener(caretEvent);
// PENDING(prinz)
// This should really be updateUI(), but the ui
// is currently being serialized so we'll use it.
getUI().installUI(this);
}
// --- member variables ----------------------------------
/**
* The document model.
*/
private Document model;
/**
* The caret used to display the insert position
* and navigate throught the document.
*
* PENDING(prinz)
* This should be serializable, default installed
* by UI.
*/
private transient Caret caret;
/**
* The object responsible for managing highlights.
*
* PENDING(prinz)
* This should be serializable, default installed
* by UI.
*/
private transient Highlighter highlighter;
/**
* The current key bindings in effect.
*
* PENDING(prinz)
* This should be serializable, default installed
* by UI.
*/
private transient Keymap keymap;
/**
* is the component opaque?
*/
private boolean opaque;
private transient MutableCaretEvent caretEvent;
private Color caretColor;
private Color selectionColor;
private Color selectedTextColor;
private Color disabledTextColor;
private boolean editable;
private Insets margin;
private char focusAccelerator;
private Action focusAction = new FocusAction();
private static ClipboardOwner defaultClipboardOwner = new ClipboardObserver();
static final IllegalArgumentException illegalDocumentPosition =
new IllegalArgumentException("Illegal document position");
static class ClipboardObserver implements ClipboardOwner {
public void lostOwnership(Clipboard clipboard, Transferable contents) {
}
}
/**
* package level access to focused text component
* so that JTextAction implementations can be
* reused across JTextComponent implementations.
*/
static final JTextComponent getFocusedComponent() {
return focusedComponent;
}
private static Hashtable keymapTable = null;
private JTextComponent editor;
private static JTextComponent focusedComponent;
static class DefaultKeymap implements Keymap {
DefaultKeymap(String nm, Keymap parent) {
this.nm = nm;
this.parent = parent;
bindings = new Hashtable();
}
/**
* Fetch the default action to fire if a
* key is typed (ie a KEY_TYPED KeyEvent is received)
* and there is no binding for it. Typically this
* would be some action that inserts text so that
* the keymap doesn't require an action for each
* possible key.
*/
public Action getDefaultAction() {
if (defaultAction != null) {
return defaultAction;
}
return (parent != null) ? parent.getDefaultAction() : null;
}
/**
* Set the default action to fire if a key is typed.
*/
public void setDefaultAction(Action a) {
defaultAction = a;
}
public String getName() {
return nm;
}
public Action getAction(KeyStroke key) {
Action a = (Action) bindings.get(key);
if ((a == null) && (parent != null)) {
a = parent.getAction(key);
}
return a;
}
public KeyStroke[] getBoundKeyStrokes() {
KeyStroke[] keys = new KeyStroke[bindings.size()];
int i = 0;
for (Enumeration e = bindings.keys() ; e.hasMoreElements() ;) {
keys[i++] = (KeyStroke) e.nextElement();
}
return keys;
}
public Action[] getBoundActions() {
Action[] actions = new Action[bindings.size()];
int i = 0;
for (Enumeration e = bindings.elements() ; e.hasMoreElements() ;) {
actions[i++] = (Action) e.nextElement();
}
return actions;
}
public KeyStroke[] getKeyStrokesForAction(Action a) {
// TBD
return null;
}
public boolean isLocallyDefined(KeyStroke key) {
return bindings.containsKey(key);
}
public void addActionForKeyStroke(KeyStroke key, Action a) {
bindings.put(key, a);
}
public void removeKeyStrokeBinding(KeyStroke key) {
bindings.remove(key);
}
public void removeBindings() {
bindings.clear();
}
public Keymap getResolveParent() {
return parent;
}
public void setResolveParent(Keymap parent) {
this.parent = parent;
}
/**
* String representation of the keymap... potentially
* a very long string.
*/
public String toString() {
return "Keymap[" + nm + "]" + bindings;
}
String nm;
Keymap parent;
Hashtable bindings;
Action defaultAction;
}
/**
* This is the name of the default keymap that will be shared by all
* JTextComponent instances unless they have had a different
* keymap set.
*/
public static final String DEFAULT_KEYMAP = "default";
/**
* Default bindings for the default keymap if no other bindings
* are given.
*/
static final KeyBinding[] defaultBindings = {
new KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0),
DefaultEditorKit.deletePrevCharAction),
new KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0),
DefaultEditorKit.deleteNextCharAction),
new KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, 0),
DefaultEditorKit.beginAction),
new KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_END, 0),
DefaultEditorKit.endAction),
new KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0),
DefaultEditorKit.forwardAction),
new KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0),
DefaultEditorKit.backwardAction),
new KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0),
DefaultEditorKit.upAction),
new KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0),
DefaultEditorKit.downAction)
};
static {
try {
keymapTable = new Hashtable(17);
Keymap binding = addKeymap(DEFAULT_KEYMAP, null);
binding.setDefaultAction(new DefaultEditorKit.DefaultKeyTypedAction());
EditorKit kit = new DefaultEditorKit();
loadKeymap(binding, defaultBindings, kit.getActions());
} catch (Throwable e) {
e.printStackTrace();
keymapTable = new Hashtable(17);
}
}
/**
* Event to use when firing a notification of change to caret
* position. This is mutable so that the event can be reused
* since caret events can be fairly high in bandwidth.
*/
static class MutableCaretEvent extends CaretEvent implements ChangeListener, MouseListener, FocusListener {
MutableCaretEvent(JTextComponent c) {
super(c);
}
final void fire() {
JTextComponent c = (JTextComponent) getSource();
if (c != null) {
Caret caret = c.getCaret();
dot = caret.getDot();
mark = caret.getMark();
c.fireCaretUpdate(this);
}
}
public final String toString() {
return "dot=" + dot + "," + "mark=" + mark;
}
// --- CaretEvent methods -----------------------
public final int getDot() {
return dot;
}
public final int getMark() {
return mark;
}
// --- ChangeListener methods -------------------
public final void stateChanged(ChangeEvent e) {
if (! dragActive) {
fire();
}
}
// --- FocusListener methods --------------------------------
/**
* Stashes the current focused JTextComponent reference
* for JTextAction instances to use if the ActionEvent
* doesn't contain the target text component.
*
* @param e the focus event
* @see JTextAction
* @see FocusListener#focusGained
*/
public void focusGained(FocusEvent e) {
focusedComponent = (JTextComponent) getSource();
}
/**
* Removes reference to focused text component that
* instances of JTextAction use.
*
* @param e the focus event
* @see JTextAction
* @see FocusListener#focusLost
*/
public void focusLost(FocusEvent e) {
// temp focus loss from menus causes problems
//focusedComponent = null;
}
// --- MouseListener methods -----------------------------------
/**
* Requests focus on the associated
* text component, and try to set the cursor position.
*
* @param e the mouse event
* @see MouseListener#mousePressed
*/
public final void mousePressed(MouseEvent e) {
dragActive = true;
}
/**
* Called when the mouse is released.
*
* @param e the mouse event
* @see MouseListener#mouseReleased
*/
public final void mouseReleased(MouseEvent e) {
dragActive = false;
fire();
}
public final void mouseClicked(MouseEvent e) {
}
public final void mouseEntered(MouseEvent e) {
}
public final void mouseExited(MouseEvent e) {
}
private boolean dragActive;
private int dot;
private int mark;
}
class FocusAction extends AbstractAction {
public void actionPerformed(ActionEvent e) {
requestFocus();
}
public boolean isEnabled() {
if(isEditable())
return true;
else
return false;
}
}
}