home *** CD-ROM | disk | FTP | other *** search
Java Source | 1999-09-19 | 13.3 KB | 408 lines |
- /*
- * @(#)ActiveClient.java 1.3 98/06/09
- *
- * Copyright 1997-1998 by Sun Microsystems, Inc.,
- * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
- * All rights reserved.
- */
-
- import java.awt.Component;
- import java.awt.Point;
- import java.awt.Rectangle;
- import java.awt.event.InputMethodEvent;
- import java.awt.event.InputMethodListener;
- import java.awt.im.InputMethodRequests;
- import java.awt.font.TextAttribute;
- import java.awt.font.TextHitInfo;
- import java.awt.font.TextLayout;
- import java.text.AttributedCharacterIterator;
- import java.text.AttributedCharacterIterator.Attribute;
- import java.text.AttributedString;
- import java.util.HashSet;
- import java.util.Map;
- import java.util.Set;
-
-
- /**
- * Implements on-the-spot text input, the most closely integrated form
- * of interaction with input methods. This class is an active client of
- * the input method framework, that is, actively uses its APIs to accomplish
- * integration.
- * <p>
- * This class directly implements the two client interfaces of the input method
- * framework, InputMethodListener and InputMethodRequests.
- * This is not required. Especially in cases where the public
- * interface matters (such as in a class library), it may be more
- * appropriate to hide the implementations of these two interfaces
- * in separate classes.
- *
- */
-
- public class ActiveClient extends LWTextComponent
- implements InputMethodListener, InputMethodRequests {
-
- // the composed text received from the input method,
- // with font information added so we can easily create a TextLayout
- private AttributedCharacterIterator composedText = null;
-
- // the caret received from the input method; relative to composed text
- private TextHitInfo caret = null;
-
- /**
- * Constructs an ActiveClient. Input methods are always enabled for
- * ActiveClient instances.
- * @param name the component name to be displayed above the text
- */
- public ActiveClient(String name) {
- super(name, true);
- enableInputMethods(true);
- addInputMethodListener(this);
- }
-
- /**
- * Implements getInputMethodRequests for ActiveClient by returning "this".
- * @return "this"
- */
- public InputMethodRequests getInputMethodRequests() {
- return this;
- }
-
- /**
- * Returns the text that the user has entered.
- * This override returns the concatenation of committed text
- * and composed text.
- */
- public AttributedCharacterIterator getDisplayText() {
- if (composedText == null) {
- return super.getDisplayText();
- } else {
- // We don't want to copy all the text and attribute data here.
- // Instead, we return a CompositeIterator which iterates over
- // the concatenation of two iterators.
- return new CompositeIterator(super.getDisplayText(), composedText);
- }
- }
-
- /**
- * Returns a text hit info indicating the current caret (insertion point).
- * This override returns the caret provided by the input method while
- * there is composed text; otherwise it returns a caret at the end
- * of the committed text. The caret provided by the input method may be null.
- */
- public TextHitInfo getCaret() {
- if (composedText == null) {
- return super.getCaret();
- } else if (caret == null) {
- return null;
- } else {
- // the caret provided by the input method is relative
- // to the composed text, so translate it to the entire text
- return caret.getOffsetHit(getCommittedTextLength());
- }
- }
-
- // InputMethodListener implementation
-
- // constant attribute set for use when copying composed text
- private static final Attribute[] IM_ATTRIBUTES =
- { TextAttribute.INPUT_METHOD_HIGHLIGHT };
-
- /**
- * Handles changes to the text entered through an input method.
- * Committed text contained in the event is appended to the
- * committed text of the text component. Composed text contained
- * in the event replaces any existing composed text in the text
- * component.
- * The caret defined in the event is saved and will
- * be returned by getCaret if there is composed text. The
- * component is redrawn.
- * <p>
- * In this simple component, we only
- * keep input method highlight attributes. Smarter components may want to
- * keep language, reading, input method segment, and other
- * attributes as well.
- */
- public void inputMethodTextChanged(InputMethodEvent event) {
- int committedCharacterCount = event.getCommittedCharacterCount();
- AttributedCharacterIterator text = event.getText();
- composedText = null;
- char c;
- if (text != null) {
- // copy the committed text
- int toCopy = committedCharacterCount;
- c = text.first();
- while (toCopy-- > 0) {
- insertCharacter(c);
- c = text.next();
- }
-
- // copy the composed text
- if (text.getEndIndex() - (text.getBeginIndex() + committedCharacterCount) > 0) {
- AttributedString composedTextString;
- composedTextString = new AttributedString(text,
- text.getBeginIndex() + committedCharacterCount, // skip over committed text
- text.getEndIndex(), IM_ATTRIBUTES);
- // add font information because TextLayout requires it
- composedTextString.addAttribute(TextAttribute.FONT, getFont());
- composedText = composedTextString.getIterator();
- }
- }
- event.consume();
- invalidateTextLayout();
- caret = event.getCaret();
- repaint();
- }
-
- /**
- * Handles changes to the caret within composed text.
- * The caret defined in the event is saved and will
- * be returned by getCaret if there is composed text. The
- * component is redrawn.
- */
- public void caretPositionChanged(InputMethodEvent event) {
- caret = event.getCaret();
- event.consume();
- repaint();
- }
-
- // InputMethodRequests implementation
- // Note that getCommittedTextLength is already implemented in LWTextComponent
-
- /**
- * Gets the location of a specified offset in the current composed text,
- * or of the selection in committed text.
- */
- public Rectangle getTextLocation(TextHitInfo offset) {
-
- // determine the text location in component coordinates
- Rectangle rectangle;
- if (offset == null) {
- // no composed text: return caret for committed text
- rectangle = getCaretRectangle();
- } else {
- // composed text: return caret within composed text
- TextHitInfo globalOffset = offset.getOffsetHit(getCommittedTextLength());
- rectangle = getCaretRectangle(globalOffset);
- }
-
- // translate to screen coordinates
- Point location = getLocationOnScreen();
- rectangle.translate(location.x, location.y);
-
- return rectangle;
- }
-
- /**
- * Gets the offset within the composed text for the specified absolute x
- * and y coordinates on the screen.
- */
- public TextHitInfo getLocationOffset(int x, int y) {
-
- // translate from screen coordinates to coordinates in the text layout
- Point location = getLocationOnScreen();
- Point textOrigin = getTextOrigin();
- x -= location.x + textOrigin.x;
- y -= location.y + textOrigin.y;
-
- // TextLayout maps locations far outside its bounds to locations within.
- // To avoid false hits, we use it only if it actually contains the location.
- // We also have to translate the TextHitInfo to be relative to composed text.
- TextLayout textLayout = getTextLayout();
- if (textLayout != null &&
- textLayout.getBounds().contains(x, y)) {
- return textLayout.hitTestChar(x, y).getOffsetHit(-getCommittedTextLength());
- } else {
- return null;
- }
- }
-
- /**
- * Gets the offset of the insert position in the committed text contained
- * in the text editing component. In this simple component, that's always
- * at the end of the committed text.
- */
- public int getInsertPositionOffset() {
- return getCommittedTextLength();
- }
-
- /**
- * Gets an iterator providing access to the entire text and attributes
- * contained in the text editing component except for uncommitted
- * text.
- */
- public AttributedCharacterIterator getCommittedText(int beginIndex,
- int endIndex, Attribute[] attributes) {
- return getCommittedText(beginIndex, endIndex);
- }
-
- /**
- * Returns null to indicate that the "Undo Commit" feature is not supported
- * by this simple text component.
- */
- public AttributedCharacterIterator cancelLatestCommittedText(Attribute[] attributes) {
- return null;
- }
-
- private static final AttributedCharacterIterator EMPTY_TEXT =
- (new AttributedString("")).getIterator();
-
- /**
- * Gets the currently selected text from the text editing component.
- * Since this simple text component doesn't support selections, this is
- * always an iterator over empty text.
- */
- public AttributedCharacterIterator getSelectedText(Attribute[] attributes) {
- return EMPTY_TEXT;
- }
- }
-
- /**
- * Iterates over the combined text of two AttributedCharacterIterators.
- * Assumes that no annotation spans the two iterators.
- */
-
- class CompositeIterator implements AttributedCharacterIterator {
-
- AttributedCharacterIterator iterator1;
- AttributedCharacterIterator iterator2;
- int begin1, end1;
- int begin2, end2;
- int endIndex;
- int currentIndex;
- AttributedCharacterIterator currentIterator;
- int currentIteratorDelta;
-
- /**
- * Constructs a CompositeIterator that iterates over the concatenation
- * of iterator1 and iterator2.
- * @param iterator1, iterator2 the base iterators that this composite iterator concatenates
- */
- CompositeIterator(AttributedCharacterIterator iterator1, AttributedCharacterIterator iterator2) {
- this.iterator1 = iterator1;
- this.iterator2 = iterator2;
- begin1 = iterator1.getBeginIndex();
- end1 = iterator1.getEndIndex();
- begin2 = iterator2.getBeginIndex();
- end2 = iterator2.getEndIndex();
- endIndex = (end1 - begin1) + (end2 - begin2);
- internalSetIndex(0);
- }
-
- // CharacterIterator implementation
-
- public char first() {
- return internalSetIndex(0);
- }
-
- public char last() {
- if (endIndex == 0) {
- return internalSetIndex(endIndex);
- } else {
- return internalSetIndex(endIndex - 1);
- }
- }
-
- public char next() {
- if (currentIndex < endIndex) {
- return internalSetIndex(currentIndex + 1);
- } else {
- return DONE;
- }
- }
-
- public char previous() {
- if (currentIndex > 0) {
- return internalSetIndex(currentIndex - 1);
- } else {
- return DONE;
- }
- }
-
- public char current() {
- return currentIterator.setIndex(currentIndex + currentIteratorDelta);
- }
-
- public char setIndex(int position) {
- if (position < 0 || position > endIndex) {
- throw new IllegalArgumentException("invalid index");
- }
- return internalSetIndex(position);
- }
-
- private char internalSetIndex(int position) {
- currentIndex = position;
- if (currentIndex < end1 - begin1) {
- currentIterator = iterator1;
- currentIteratorDelta = begin1;
- } else {
- currentIterator = iterator2;
- currentIteratorDelta = begin2 - (end1 - begin1);
- }
- return currentIterator.setIndex(currentIndex + currentIteratorDelta);
- }
-
- public int getBeginIndex() {
- return 0;
- }
-
- public int getEndIndex() {
- return endIndex;
- }
-
- public int getIndex() {
- return currentIndex;
- }
-
- // AttributedCharacterIterator implementation
-
- public int getRunStart() {
- return currentIterator.getRunStart() - currentIteratorDelta;
- }
-
- public int getRunLimit() {
- return currentIterator.getRunLimit() - currentIteratorDelta;
- }
-
- public int getRunStart(Attribute attribute) {
- return currentIterator.getRunStart(attribute) - currentIteratorDelta;
- }
-
- public int getRunLimit(Attribute attribute) {
- return currentIterator.getRunLimit(attribute) - currentIteratorDelta;
- }
-
- public int getRunStart(Set attributes) {
- return currentIterator.getRunStart(attributes) - currentIteratorDelta;
- }
-
- public int getRunLimit(Set attributes) {
- return currentIterator.getRunLimit(attributes) - currentIteratorDelta;
- }
-
- public Map getAttributes() {
- return currentIterator.getAttributes();
- }
-
- public Set getAllAttributeKeys() {
- Set keys = new HashSet(iterator1.getAllAttributeKeys());
- keys.addAll(iterator2.getAllAttributeKeys());
- return keys;
- }
-
- public Object getAttribute(Attribute attribute) {
- return currentIterator.getAttribute(attribute);
- }
-
- // Object overrides
-
- public Object clone() {
- try {
- CompositeIterator other = (CompositeIterator) super.clone();
- return other;
- } catch (CloneNotSupportedException e) {
- throw new InternalError();
- }
- }
- }
-
-