0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017:
0018: package java.awt;
0019:
0020: import java.awt.event.FocusEvent;
0021: import java.awt.event.FocusListener;
0022: import java.awt.event.InputEvent;
0023: import java.awt.event.KeyEvent;
0024: import java.awt.event.KeyListener;
0025: import java.awt.event.MouseEvent;
0026: import java.awt.event.MouseListener;
0027: import java.awt.event.MouseMotionListener;
0028: import java.awt.event.TextEvent;
0029: import java.awt.event.TextListener;
0030: import java.text.BreakIterator;
0031: import java.util.EventListener;
0032: import java.util.HashMap;
0033: import javax.accessibility.Accessible;
0034: import javax.accessibility.AccessibleContext;
0035: import javax.accessibility.AccessibleRole;
0036: import javax.accessibility.AccessibleState;
0037: import javax.accessibility.AccessibleStateSet;
0038: import javax.accessibility.AccessibleText;
0039: import javax.swing.event.DocumentEvent;
0040: import javax.swing.event.DocumentListener;
0041: import javax.swing.text.AbstractDocument;
0042: import javax.swing.text.AttributeSet;
0043: import javax.swing.text.BadLocationException;
0044: import javax.swing.text.Document;
0045: import javax.swing.text.Element;
0046: import javax.swing.text.PlainDocument;
0047: import javax.swing.text.View;
0048: import javax.swing.text.ViewFactory;
0049: import javax.swing.text.Position.Bias;
0050:
0051: import org.apache.harmony.awt.internal.nls.Messages;
0052: import org.apache.harmony.awt.state.TextComponentState;
0053: import org.apache.harmony.awt.text.AWTTextAction;
0054: import org.apache.harmony.awt.text.ActionNames;
0055: import org.apache.harmony.awt.text.ActionSet;
0056: import org.apache.harmony.awt.text.RootViewContext;
0057: import org.apache.harmony.awt.text.TextCaret;
0058: import org.apache.harmony.awt.text.TextFactory;
0059: import org.apache.harmony.awt.text.TextKit;
0060:
0061: public class TextComponent extends Component implements Accessible {
0062: private static final long serialVersionUID = -2214773872412987419L;
0063:
0064: /**
0065: * Maps KeyEvents to keyboard actions
0066: */
0067: static class KeyMap {
0068: private static final HashMap<AWTKeyStroke, Object> actions = new HashMap<AWTKeyStroke, Object>();
0069: static {
0070: add(KeyEvent.VK_ENTER, 0, ActionNames.insertBreakAction);
0071: add(KeyEvent.VK_TAB, 0, ActionNames.insertTabAction);
0072: add(KeyEvent.VK_DELETE, 0, ActionNames.deleteNextCharAction);
0073: add(KeyEvent.VK_BACK_SPACE, 0,
0074: ActionNames.deletePrevCharAction);
0075: add(KeyEvent.VK_LEFT, 0, ActionNames.backwardAction);
0076: add(KeyEvent.VK_RIGHT, 0, ActionNames.forwardAction);
0077: add(KeyEvent.VK_UP, 0, ActionNames.upAction);
0078: add(KeyEvent.VK_DOWN, 0, ActionNames.downAction);
0079: add(KeyEvent.VK_HOME, 0, ActionNames.beginLineAction);
0080: add(KeyEvent.VK_END, 0, ActionNames.endLineAction);
0081: add(KeyEvent.VK_PAGE_UP, 0, ActionNames.pageUpAction);
0082: add(KeyEvent.VK_PAGE_DOWN, 0, ActionNames.pageDownAction);
0083: add(KeyEvent.VK_RIGHT, InputEvent.CTRL_MASK,
0084: ActionNames.nextWordAction);
0085: add(KeyEvent.VK_LEFT, InputEvent.CTRL_MASK,
0086: ActionNames.previousWordAction);
0087: add(KeyEvent.VK_PAGE_UP, InputEvent.CTRL_MASK,
0088: ActionNames.beginAction);
0089: add(KeyEvent.VK_PAGE_DOWN, InputEvent.CTRL_MASK,
0090: ActionNames.endAction);
0091: add(KeyEvent.VK_HOME, InputEvent.CTRL_MASK,
0092: ActionNames.beginAction);
0093: add(KeyEvent.VK_END, InputEvent.CTRL_MASK,
0094: ActionNames.endAction);
0095: add(KeyEvent.VK_LEFT, InputEvent.SHIFT_MASK,
0096: ActionNames.selectionBackwardAction);
0097: add(KeyEvent.VK_RIGHT, InputEvent.SHIFT_MASK,
0098: ActionNames.selectionForwardAction);
0099: add(KeyEvent.VK_UP, InputEvent.SHIFT_MASK,
0100: ActionNames.selectionUpAction);
0101: add(KeyEvent.VK_DOWN, InputEvent.SHIFT_MASK,
0102: ActionNames.selectionDownAction);
0103: add(KeyEvent.VK_HOME, InputEvent.SHIFT_MASK,
0104: ActionNames.selectionBeginLineAction);
0105: add(KeyEvent.VK_END, InputEvent.SHIFT_MASK,
0106: ActionNames.selectionEndLineAction);
0107: add(KeyEvent.VK_PAGE_UP, InputEvent.SHIFT_MASK,
0108: ActionNames.selectionPageUpAction);
0109: add(KeyEvent.VK_PAGE_DOWN, InputEvent.SHIFT_MASK,
0110: ActionNames.selectionPageDownAction);
0111: add(KeyEvent.VK_LEFT, InputEvent.CTRL_MASK
0112: | InputEvent.SHIFT_MASK,
0113: ActionNames.selectionPreviousWordAction);
0114: add(KeyEvent.VK_RIGHT, InputEvent.CTRL_MASK
0115: | InputEvent.SHIFT_MASK,
0116: ActionNames.selectionNextWordAction);
0117: add(KeyEvent.VK_PAGE_UP, InputEvent.CTRL_MASK
0118: | InputEvent.SHIFT_MASK,
0119: ActionNames.selectionBeginAction);
0120: add(KeyEvent.VK_PAGE_DOWN, InputEvent.CTRL_MASK
0121: | InputEvent.SHIFT_MASK,
0122: ActionNames.selectionEndAction);
0123: add(KeyEvent.VK_A, InputEvent.CTRL_MASK,
0124: ActionNames.selectAllAction);
0125: add(KeyEvent.VK_INSERT, InputEvent.SHIFT_MASK,
0126: ActionNames.pasteAction);
0127: add(KeyEvent.VK_V, InputEvent.CTRL_MASK,
0128: ActionNames.pasteAction);
0129: add(KeyEvent.VK_INSERT, InputEvent.CTRL_MASK,
0130: ActionNames.copyAction);
0131: add(KeyEvent.VK_C, InputEvent.CTRL_MASK,
0132: ActionNames.copyAction);
0133: add(KeyEvent.VK_X, InputEvent.CTRL_MASK,
0134: ActionNames.cutAction);
0135: }
0136:
0137: private static void add(int vk, int mask, String actionName) {
0138: Object action = ActionSet.actionMap.get(actionName);
0139: actions.put(AWTKeyStroke.getAWTKeyStroke(vk, mask), action);
0140: }
0141:
0142: static AWTTextAction getAction(KeyEvent e) {
0143: return (AWTTextAction) actions.get(AWTKeyStroke
0144: .getAWTKeyStrokeForEvent(e));
0145: }
0146: }
0147:
0148: protected class AccessibleAWTTextComponent extends
0149: AccessibleAWTComponent implements AccessibleText,
0150: TextListener {
0151: private static final long serialVersionUID = 3631432373506317811L;
0152:
0153: public AccessibleAWTTextComponent() {
0154: // only add this as listener
0155: TextComponent.this .addTextListener(this );
0156: }
0157:
0158: @Override
0159: public AccessibleRole getAccessibleRole() {
0160: return AccessibleRole.TEXT;
0161: }
0162:
0163: @Override
0164: public AccessibleStateSet getAccessibleStateSet() {
0165: AccessibleStateSet result = super .getAccessibleStateSet();
0166: if (isEditable()) {
0167: result.add(AccessibleState.EDITABLE);
0168: }
0169: return result;
0170: }
0171:
0172: @Override
0173: public AccessibleText getAccessibleText() {
0174: return this ;
0175: }
0176:
0177: public int getCaretPosition() {
0178: return TextComponent.this .getCaretPosition();
0179: }
0180:
0181: public int getCharCount() {
0182: return document.getLength();
0183: }
0184:
0185: public int getSelectionEnd() {
0186: return TextComponent.this .getSelectionEnd();
0187: }
0188:
0189: public int getSelectionStart() {
0190: return TextComponent.this .getSelectionStart();
0191: }
0192:
0193: public int getIndexAtPoint(Point p) {
0194: return -1;
0195: }
0196:
0197: public Rectangle getCharacterBounds(int arg0) {
0198: // TODO: implement
0199: return null;
0200: }
0201:
0202: public String getSelectedText() {
0203: String selText = TextComponent.this .getSelectedText();
0204: return ("".equals(selText) ? null : selText); //$NON-NLS-1$
0205: }
0206:
0207: public String getAfterIndex(int part, int index) {
0208: int offset = 0;
0209: switch (part) {
0210: case AccessibleText.CHARACTER:
0211: return (index == document.getLength()) ? null
0212: : getCharacter(index + 1);
0213: case AccessibleText.WORD:
0214: try {
0215: offset = getWordEnd(index) + 1;
0216: offset = getWordEnd(offset + 1);
0217: } catch (final BadLocationException e) {
0218: return null;
0219: }
0220: return getWord(offset);
0221: case AccessibleText.SENTENCE:
0222: // not implemented yet
0223: default:
0224: return null;
0225: }
0226: }
0227:
0228: public String getAtIndex(int part, int index) {
0229: if (document.getLength() <= 0) {
0230: return null; // compatibility
0231: }
0232: switch (part) {
0233: case AccessibleText.CHARACTER:
0234: return getCharacter(index);
0235: case AccessibleText.WORD:
0236: return getWord(index);
0237: case AccessibleText.SENTENCE:
0238: return getLine(index);
0239: default:
0240: return null;
0241: }
0242: }
0243:
0244: public String getBeforeIndex(int part, int index) {
0245: int offset = 0;
0246: switch (part) {
0247: case AccessibleText.CHARACTER:
0248: return (index == 0) ? null : getCharacter(index - 1);
0249: case AccessibleText.WORD:
0250: try {
0251: offset = getWordStart(index) - 1;
0252: offset = getWordStart(offset - 1);
0253: } catch (final BadLocationException e) {
0254: return null;
0255: }
0256: return (offset < 0) ? null : getWord(offset);
0257: case AccessibleText.SENTENCE:
0258: BreakIterator bi = BreakIterator.getSentenceInstance();
0259: bi.setText(getText());
0260: offset = bi.preceding(index);
0261: offset = bi.previous() + 1;
0262: return (offset < 0) ? null : getLine(offset);
0263: default:
0264: return null;
0265: }
0266: }
0267:
0268: public AttributeSet getCharacterAttribute(int arg0) {
0269: // TODO: implement
0270: return null;
0271: }
0272:
0273: public void textValueChanged(TextEvent e) {
0274: // TODO: implement
0275: }
0276: }
0277:
0278: /**
0279: * Handles key actions and updates document on
0280: * key typed events
0281: */
0282: final class KeyHandler implements KeyListener {
0283: public void keyPressed(KeyEvent e) {
0284: performAction(e);
0285: }
0286:
0287: public void keyReleased(KeyEvent e) {
0288: }
0289:
0290: public void keyTyped(KeyEvent e) {
0291: if (insertCharacter(e)) {
0292: // repaint();
0293: } else {
0294: performAction(e);
0295: }
0296: }
0297:
0298: boolean insertCharacter(KeyEvent e) {
0299: if (!isEditable()) {
0300: return false;
0301: }
0302: char ch = e.getKeyChar();
0303: if (Character.getType(ch) != Character.CONTROL || ch > 127) {
0304: getTextKit()
0305: .replaceSelectedText(Character.toString(ch));
0306: e.consume();
0307: return true;
0308: }
0309: return false;
0310: }
0311:
0312: void performAction(KeyEvent e) {
0313: AWTTextAction action = KeyMap.getAction(e);
0314: if (action != null) {
0315: performTextAction(action);
0316: e.consume();
0317: }
0318: }
0319: }
0320:
0321: /**
0322: * Handles Document changes, updates view
0323: */
0324: final class DocumentHandler implements DocumentListener {
0325: private Rectangle getScrollPosition() {
0326: Rectangle pos = getClient();
0327: pos.translate(scrollPosition.x, scrollPosition.y);
0328: return pos;
0329: }
0330:
0331: public void insertUpdate(DocumentEvent e) {
0332: if (getBounds().isEmpty()) {
0333: // update view only when component is "viewable"
0334: return;
0335: }
0336: getRootView().insertUpdate(e, getScrollPosition(),
0337: getRootView().getViewFactory());
0338: updateBidiInfo();
0339: generateEvent();
0340: }
0341:
0342: private void updateBidiInfo() {
0343: // TODO catch BIDI chars here
0344: }
0345:
0346: public void removeUpdate(DocumentEvent e) {
0347: if (getBounds().isEmpty()) {
0348: // update view only when component is "viewable"
0349: return;
0350: }
0351: getRootView().removeUpdate(e, getScrollPosition(),
0352: getRootView().getViewFactory());
0353: generateEvent();
0354: }
0355:
0356: public void changedUpdate(DocumentEvent e) {
0357: if (getBounds().isEmpty()) {
0358: // update view only when component is "viewable"
0359: return;
0360: }
0361: getRootView().changedUpdate(e, getScrollPosition(),
0362: getRootView().getViewFactory());
0363: generateEvent();
0364: }
0365:
0366: View getRootView() {
0367: return rootViewContext.getView();
0368: }
0369: }
0370:
0371: /**
0372: * Implements all necessary document/view/caret operations for text handling
0373: */
0374: class TextKitImpl implements TextKit, ViewFactory,
0375: RootViewContext.ViewFactoryGetter {
0376: public boolean isEditable() {
0377: return TextComponent.this .isEditable();
0378: }
0379:
0380: public void replaceSelectedText(String text) {
0381: int dot = caret.getDot();
0382: int mark = caret.getMark();
0383: try {
0384: int start = Math.min(dot, mark);
0385: int length = Math.abs(dot - mark);
0386: synchronized (TextComponent.this ) {
0387: document.replace(start, length, text, null);
0388: }
0389: } catch (final BadLocationException e) {
0390: }
0391: }
0392:
0393: public TextCaret getCaret() {
0394: return caret;
0395: }
0396:
0397: public Document getDocument() {
0398: return document;
0399: }
0400:
0401: public String getSelectedText() {
0402: String s = null;
0403: int dot = caret.getDot();
0404: int mark = caret.getMark();
0405: if (dot == mark) {
0406: return null;
0407: }
0408: try {
0409: s = document.getText(Math.min(dot, mark), Math.abs(dot
0410: - mark));
0411: } catch (final BadLocationException e) {
0412: }
0413: return s;
0414: }
0415:
0416: public int getSelectionStart() {
0417: return Math.min(caret.getDot(), caret.getMark());
0418: }
0419:
0420: public int getSelectionEnd() {
0421: return Math.max(caret.getDot(), caret.getMark());
0422: }
0423:
0424: public Rectangle getVisibleRect() {
0425: return new Rectangle(-scrollPosition.x, -scrollPosition.y,
0426: w, h);
0427: }
0428:
0429: public View getRootView() {
0430: return rootViewContext.getView();
0431: }
0432:
0433: public Rectangle modelToView(int pos)
0434: throws BadLocationException {
0435: return modelToView(pos, Bias.Forward);
0436: }
0437:
0438: public Rectangle modelToView(int pos, Bias bias)
0439: throws BadLocationException {
0440: Rectangle mRect = getModelRect();
0441: if (mRect.isEmpty()) {
0442: return null;
0443: }
0444: Shape shape = getRootView().modelToView(pos, mRect, bias);
0445: if (shape != null) {
0446: return shape.getBounds();
0447: }
0448: return null;
0449: }
0450:
0451: public Component getComponent() {
0452: return TextComponent.this ;
0453: }
0454:
0455: public int viewToModel(Point p, Bias[] biasRet) {
0456: return getRootView().viewToModel(p.x, p.y, getModelRect(),
0457: biasRet);
0458: }
0459:
0460: public void scrollRectToVisible(Rectangle rect) {
0461: TextComponent.this .scrollRectToVisible(rect);
0462: }
0463:
0464: public boolean isScrollBarArea(int x, int y) {
0465: return false;
0466: }
0467:
0468: public void addCaretListeners(EventListener listener) {
0469: // do nothing
0470: }
0471:
0472: public void paintLayeredHighlights(Graphics g, int p0, int p1,
0473: Shape shape, View view) {
0474: caret.getHighlighter().paintLayeredHighlights(g, p0, p1,
0475: shape, view);
0476: }
0477:
0478: public void revalidate() {
0479: TextComponent.this .revalidate();
0480: }
0481:
0482: public Color getDisabledTextColor() {
0483: return SystemColor.textInactiveText;
0484: }
0485:
0486: public Color getSelectedTextColor() {
0487: return SystemColor.textHighlightText;
0488: }
0489:
0490: public View create(Element element) {
0491: // do nothing
0492: return null;
0493: }
0494:
0495: public ViewFactory getViewFactory() {
0496: return this ;
0497: }
0498: }
0499:
0500: class State extends Component.ComponentState implements
0501: TextComponentState {
0502: final Dimension textSize = new Dimension();
0503:
0504: public String getText() {
0505: return TextComponent.this .getText();
0506: }
0507:
0508: public Dimension getTextSize() {
0509: return textSize;
0510: }
0511:
0512: public void setTextSize(Dimension size) {
0513: textSize.setSize(size);
0514: }
0515:
0516: @Override
0517: public boolean isEnabled() {
0518: return super .isEnabled() && isEditable();
0519: }
0520:
0521: public Rectangle getClient() {
0522: return TextComponent.this .getClient();
0523: }
0524:
0525: public Insets getInsets() {
0526: return getNativeInsets();
0527: }
0528: }
0529:
0530: protected volatile transient TextListener textListener;
0531:
0532: private final AWTListenerList<TextListener> textListeners = new AWTListenerList<TextListener>(
0533: this );
0534:
0535: AbstractDocument document;
0536:
0537: final transient TextCaret caret;
0538:
0539: final transient RootViewContext rootViewContext;
0540:
0541: final Point scrollPosition = new Point();
0542:
0543: private boolean editable;
0544:
0545: private final Insets BORDER = new Insets(2, 2, 2, 2);
0546:
0547: private final State state;
0548:
0549: TextComponent() {
0550: state = new State();
0551: editable = true;
0552: dispatchToIM = true; // had been disabled by createBehavior()
0553: setFont(new Font("DialogInput", Font.PLAIN, 12)); // QUICK FIX //$NON-NLS-1$
0554: document = new PlainDocument();
0555: // text = new StringBuffer();
0556: setTextKit(new TextKitImpl());
0557: rootViewContext = createRootViewContext();
0558: rootViewContext.getView().append(createView());
0559: rootViewContext.getView().setSize(w, h);
0560: caret = createCaret();
0561: setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
0562: addAWTMouseListener(getMouseHandler());
0563: addAWTMouseMotionListener(getMotionHandler());
0564: addAWTFocusListener((FocusListener) caret);
0565: addAWTKeyListener(new KeyHandler());
0566: // document handler must be added after caret's listener has been added!
0567: document.addDocumentListener(new DocumentHandler());
0568: }
0569:
0570: /**
0571: * Gets current mouse motion events handler.
0572: * To be overridden.
0573: */
0574: MouseMotionListener getMotionHandler() {
0575: return (MouseMotionListener) caret;
0576: }
0577:
0578: /**
0579: * Gets current mouse events handler.
0580: * To be overridden.
0581: */
0582: MouseListener getMouseHandler() {
0583: return (MouseListener) caret;
0584: }
0585:
0586: /**
0587: * Re-calculates internal layout, repaints component
0588: */
0589: void revalidate() {
0590: // to be overridden by TextArea
0591: invalidate();
0592: validate();
0593: repaint();
0594: }
0595:
0596: /**
0597: * Creates and initializes root view associated with this
0598: * TextComponent, document, text kit.
0599: * @return this component's root view
0600: */
0601: private RootViewContext createRootViewContext() {
0602: TextFactory factory = TextFactory.getTextFactory();
0603: RootViewContext c = factory.createRootView(null);
0604: c.setComponent(this );
0605: c.setDocument(document);
0606: c.setViewFactoryGetter((TextKitImpl) getTextKit());
0607: return c;
0608: }
0609:
0610: /**
0611: * Creates default plain view
0612: */
0613: View createView() {
0614: TextFactory factory = TextFactory.getTextFactory();
0615: View v = factory.createPlainView(document
0616: .getDefaultRootElement());
0617: return v;
0618: }
0619:
0620: /**
0621: * @return new text caret associated with this TextComponent
0622: */
0623: TextCaret createCaret() {
0624: TextFactory factory = TextFactory.getTextFactory();
0625: TextCaret c = factory.createCaret();
0626: c.setComponent(this );
0627: return c;
0628: }
0629:
0630: @Override
0631: ComponentBehavior createBehavior() {
0632: return new HWBehavior(this );
0633: }
0634:
0635: @Override
0636: public void addNotify() {
0637: // toolkit.lockAWT();
0638: // try {
0639: super .addNotify();
0640: // } finally {
0641: // toolkit.unlockAWT();
0642: // }
0643: // ajust caret position if was invalid
0644: int maxPos = document.getLength();
0645: if (getCaretPosition() > maxPos) {
0646: caret.setDot(maxPos, caret.getDotBias());
0647: }
0648: }
0649:
0650: @Override
0651: public AccessibleContext getAccessibleContext() {
0652: toolkit.lockAWT();
0653: try {
0654: return super .getAccessibleContext();
0655: } finally {
0656: toolkit.unlockAWT();
0657: }
0658: }
0659:
0660: public String getText() {
0661: toolkit.lockAWT();
0662: try {
0663: return document.getText(0, document.getLength());
0664: } catch (BadLocationException e) {
0665: return ""; //$NON-NLS-1$
0666: } finally {
0667: toolkit.unlockAWT();
0668: }
0669: }
0670:
0671: @Override
0672: protected String paramString() {
0673: /* The format is based on 1.5 release behavior
0674: * which can be revealed by the following code:
0675: * System.out.println(new TextField());
0676: */
0677: toolkit.lockAWT();
0678: try {
0679: return (super .paramString()
0680: + ",text=" + getText() //$NON-NLS-1$
0681: + (isEditable() ? ",editable" : "") + ",selection=" + getSelectionStart() //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
0682: + "-" + getSelectionEnd()); //$NON-NLS-1$
0683: } finally {
0684: toolkit.unlockAWT();
0685: }
0686: }
0687:
0688: @Override
0689: public void enableInputMethods(boolean enable) {
0690: toolkit.lockAWT();
0691: try {
0692: // TODO: implement
0693: super .enableInputMethods(enable);
0694: } finally {
0695: toolkit.unlockAWT();
0696: }
0697: }
0698:
0699: @Override
0700: public Color getBackground() {
0701: toolkit.lockAWT();
0702: try {
0703: if (isDisplayable() && !isBackgroundSet()) {
0704: return (isEditable() ? SystemColor.window
0705: : SystemColor.control);
0706: }
0707: return super .getBackground();
0708: } finally {
0709: toolkit.unlockAWT();
0710: }
0711: }
0712:
0713: public int getCaretPosition() {
0714: toolkit.lockAWT();
0715: try {
0716: return caret.getDot();
0717: } finally {
0718: toolkit.unlockAWT();
0719: }
0720: }
0721:
0722: public String getSelectedText() {
0723: toolkit.lockAWT();
0724: try {
0725: String s = null;
0726: try {
0727: int length = getSelectionEnd() - getSelectionStart();
0728: s = document.getText(getSelectionStart(), length);
0729: } catch (final BadLocationException e) {
0730: }
0731: return s;
0732: } finally {
0733: toolkit.unlockAWT();
0734: }
0735: }
0736:
0737: public int getSelectionEnd() {
0738: toolkit.lockAWT();
0739: try {
0740: return Math.max(caret.getDot(), caret.getMark());
0741: } finally {
0742: toolkit.unlockAWT();
0743: }
0744: }
0745:
0746: public int getSelectionStart() {
0747: toolkit.lockAWT();
0748: try {
0749: return Math.min(caret.getDot(), caret.getMark());
0750: } finally {
0751: toolkit.unlockAWT();
0752: }
0753: }
0754:
0755: public boolean isEditable() {
0756: toolkit.lockAWT();
0757: try {
0758: return editable;
0759: } finally {
0760: toolkit.unlockAWT();
0761: }
0762: }
0763:
0764: @Override
0765: public void removeNotify() {
0766: toolkit.lockAWT();
0767: try {
0768: super .removeNotify();
0769: } finally {
0770: toolkit.unlockAWT();
0771: }
0772: }
0773:
0774: public void select(int selectionStart, int selectionEnd) {
0775: toolkit.lockAWT();
0776: try {
0777: setSelectionEnd(selectionEnd);
0778: setSelectionStart(selectionStart);
0779: } finally {
0780: toolkit.unlockAWT();
0781: }
0782: }
0783:
0784: public void selectAll() {
0785: toolkit.lockAWT();
0786: try {
0787: select(0, document.getLength());
0788: } finally {
0789: toolkit.unlockAWT();
0790: }
0791: }
0792:
0793: @Override
0794: public void setBackground(Color c) {
0795: toolkit.lockAWT();
0796: try {
0797: super .setBackground(c);
0798: } finally {
0799: toolkit.unlockAWT();
0800: }
0801: }
0802:
0803: public void setCaretPosition(int position) {
0804: toolkit.lockAWT();
0805: try {
0806: if (position < 0) {
0807: // awt.101=position less than zero.
0808: throw new IllegalArgumentException(Messages
0809: .getString("awt.101")); //$NON-NLS-1$
0810: }
0811: position = Math.min(document.getLength(), position);
0812: } finally {
0813: toolkit.unlockAWT();
0814: }
0815: caret.setDot(position, caret.getDotBias());
0816: }
0817:
0818: public void setEditable(boolean b) {
0819: toolkit.lockAWT();
0820: try {
0821: if (editable != b) { // to avoid dead loop in repaint()
0822: editable = b;
0823: repaint(); // background color changes
0824: }
0825: } finally {
0826: toolkit.unlockAWT();
0827: }
0828: }
0829:
0830: public void setSelectionEnd(int selectionEnd) {
0831: // toolkit.lockAWT();
0832: // try {
0833: selectionEnd = Math.min(selectionEnd, document.getLength());
0834: int start = getSelectionStart();
0835: if (selectionEnd < start) {
0836: caret.setDot(selectionEnd, caret.getDotBias());
0837: } else {
0838: caret.setDot(start, caret.getDotBias());
0839: caret.moveDot(Math.max(start, selectionEnd), caret
0840: .getDotBias());
0841: }
0842: // } finally {
0843: // toolkit.unlockAWT();
0844: // }
0845: }
0846:
0847: public void setSelectionStart(int selectionStart) {
0848: // toolkit.lockAWT();
0849: // try {
0850: selectionStart = Math.max(selectionStart, 0);
0851: int end = getSelectionEnd();
0852: if (selectionStart > end) {
0853: caret.setDot(selectionStart, caret.getDotBias());
0854: } else {
0855: caret.setDot(end, caret.getDotBias());
0856: caret.moveDot(Math.min(end, selectionStart), caret
0857: .getDotBias());
0858: }
0859: // } finally {
0860: // toolkit.unlockAWT();
0861: // }
0862: }
0863:
0864: public void setText(String text) {
0865: toolkit.lockAWT();
0866: try {
0867: if (text == null) {
0868: text = ""; //$NON-NLS-1$
0869: }
0870: } finally {
0871: toolkit.unlockAWT();
0872: }
0873: try {
0874: if (isDisplayable()) {
0875: // reset position to 0 if was displayable
0876: caret.setDot(0, caret.getDotBias());
0877: }
0878: int oldCaretPos = caret.getDot();
0879: synchronized (this ) {
0880: document.replace(0, document.getLength(), text, null);
0881: }
0882: if (!isDisplayable() && (oldCaretPos != caret.getDot())) {
0883: // return caret back to emulate "no movement"
0884: caret.setDot(oldCaretPos, caret.getDotBias());
0885: }
0886: } catch (BadLocationException e) {
0887: e.printStackTrace();
0888: }
0889: }
0890:
0891: @SuppressWarnings("unchecked")
0892: @Override
0893: public <T extends EventListener> T[] getListeners(
0894: Class<T> listenerType) {
0895: if (TextListener.class.isAssignableFrom(listenerType)) {
0896: return (T[]) getTextListeners();
0897: }
0898: return super .getListeners(listenerType);
0899: }
0900:
0901: public void addTextListener(TextListener l) {
0902: textListeners.addUserListener(l);
0903: // for compatibility only:
0904: textListener = AWTEventMulticaster.add(textListener, l);
0905: }
0906:
0907: public void removeTextListener(TextListener l) {
0908: textListeners.removeUserListener(l);
0909: // for compatibility only:
0910: textListener = AWTEventMulticaster.remove(textListener, l);
0911: }
0912:
0913: public TextListener[] getTextListeners() {
0914: return textListeners.getUserListeners(new TextListener[0]);
0915: }
0916:
0917: @Override
0918: protected void processEvent(AWTEvent e) {
0919: if (toolkit.eventTypeLookup.getEventMask(e) == AWTEvent.TEXT_EVENT_MASK) {
0920: processTextEvent((TextEvent) e);
0921: } else {
0922: super .processEvent(e);
0923: }
0924: }
0925:
0926: protected void processTextEvent(TextEvent e) {
0927: for (TextListener listener : textListeners.getUserListeners()) {
0928: switch (e.getID()) {
0929: case TextEvent.TEXT_VALUE_CHANGED:
0930: listener.textValueChanged(e);
0931: break;
0932: }
0933: }
0934: }
0935:
0936: /**
0937: * Calculates and sets new scroll position
0938: * to make rectangle visible
0939: * @param r rectangle to make visible by
0940: * scrolling
0941: */
0942: void scrollRectToVisible(final Rectangle r) {
0943: Point viewPos = getViewPosition();
0944: Insets ins = getInsets();
0945: Dimension viewSize = getClient().getSize();
0946: if ((viewSize.height <= 0) || (viewSize.width <= 0)) {
0947: return; // FIX
0948: }
0949: int dx;
0950: int dy;
0951: r.x -= ins.left;
0952: r.y -= ins.top;
0953: if (r.x > 0) {
0954: if (r.x + r.width > viewSize.width) {
0955: int dx2 = r.x + r.width - viewSize.width;
0956: dx = Math.min(r.x, dx2);
0957: } else {
0958: dx = 0;
0959: }
0960: } else if (r.x < 0) {
0961: if (r.x + r.width < viewSize.width) {
0962: int dx2 = r.x + r.width - viewSize.width;
0963: dx = Math.max(r.x, dx2);
0964: } else {
0965: dx = 0;
0966: }
0967: } else {
0968: dx = 0;
0969: }
0970: if (r.y > 0) {
0971: if (r.y + r.height > viewSize.height) {
0972: int dy2 = r.y + r.height - viewSize.height;
0973: dy = Math.min(r.y, dy2);
0974: } else {
0975: dy = 0;
0976: }
0977: } else if (r.y < 0) {
0978: if (r.y + r.height < viewSize.height) {
0979: int dy2 = r.y + r.height - viewSize.height;
0980: dy = Math.max(r.y, dy2);
0981: } else {
0982: dy = 0;
0983: }
0984: } else {
0985: dy = 0;
0986: }
0987: if (dx != 0 || dy != 0) {
0988: int x = viewPos.x + dx;
0989: int y = viewPos.y + dy;
0990: Point point = new Point(x, y);
0991: setViewPosition(point);
0992: repaint();
0993: }
0994: }
0995:
0996: /**
0997: * Sets new scroll position
0998: * @param point scroll position to set
0999: */
1000: void setViewPosition(Point point) {
1001: scrollPosition.setLocation(-point.x, -point.y);
1002: }
1003:
1004: /**
1005: * Gets current scroll position
1006: */
1007: Point getViewPosition() {
1008: return new Point(-scrollPosition.x, -scrollPosition.y);
1009: }
1010:
1011: Rectangle getClient() {
1012: Insets insets = getInsets();
1013: return new Rectangle(insets.left, insets.top, w - insets.right
1014: - insets.left, h - insets.top - insets.bottom);
1015: }
1016:
1017: /**
1018: * Gets the rectangle with height required to hold all text
1019: * and width equal to component's client width(only visible
1020: * part of text fits this width)
1021: */
1022: Rectangle getModelRect() {
1023: Rectangle clientRect = getClient();
1024: clientRect.translate(scrollPosition.x, scrollPosition.y);
1025: View view = rootViewContext.getView();
1026: int ySpan = (int) view.getPreferredSpan(View.Y_AXIS);
1027: clientRect.height = ySpan;
1028: return clientRect;
1029: }
1030:
1031: private void generateEvent() {
1032: postEvent(new TextEvent(this , TextEvent.TEXT_VALUE_CHANGED));
1033: }
1034:
1035: @Override
1036: void prepaint(Graphics g) {
1037: toolkit.theme.drawTextComponentBackground(g, state);
1038: g.setFont(getFont());
1039: g.setColor(isEnabled() ? getForeground()
1040: : SystemColor.textInactiveText);
1041: Rectangle r = getModelRect();
1042: rootViewContext.getView().setSize(r.width, r.height);
1043: Rectangle client = getClient();
1044: Shape oldClip = g.getClip();
1045: g.clipRect(client.x, client.y, client.width, client.height);
1046: document.readLock();
1047: try {
1048: rootViewContext.getView().paint(g, r);
1049: caret.paint(g);
1050: } finally {
1051: document.readUnlock();
1052: }
1053:
1054: g.setClip(oldClip);
1055: }
1056:
1057: @Override
1058: Insets getNativeInsets() {
1059: return (Insets) BORDER.clone();
1060: }
1061:
1062: @Override
1063: Insets getInsets() {
1064: // to be overridden by TextArea
1065: return getNativeInsets();
1066: }
1067:
1068: @Override
1069: AccessibleContext createAccessibleContext() {
1070: return new AccessibleAWTTextComponent();
1071: }
1072:
1073: private String getLine(int index) {
1074: String result = null;
1075: BreakIterator bi = BreakIterator.getSentenceInstance();
1076: bi.setText(getText());
1077: int end = bi.following(index);
1078: int start = bi.preceding(end - 1);
1079: try {
1080: result = document.getText(start, end - start);
1081: } catch (BadLocationException e) {
1082: e.printStackTrace();
1083: }
1084: return result;
1085: }
1086:
1087: private String getWord(int index) {
1088: int start = 0;
1089: int length = 0;
1090: String result = null;
1091: try {
1092: start = getWordStart(index);
1093: length = getWordEnd(index) - start;
1094: result = document.getText(start, length);
1095: } catch (final BadLocationException e) {
1096: }
1097: return result;
1098: }
1099:
1100: private String getCharacter(int index) {
1101: String s = null;
1102: try {
1103: s = document.getText(index, 1);
1104: } catch (final BadLocationException e) {
1105: }
1106: return s;
1107: }
1108:
1109: private int getWordEnd(final int pos) throws BadLocationException {
1110: BreakIterator bi = BreakIterator.getWordInstance();
1111: int length = document.getLength();
1112: if (pos < 0 || pos > length) {
1113: // awt.2B=No word at {0}
1114: throwException(Messages.getString("awt.2B", pos), pos); //$NON-NLS-1$
1115: }
1116: String content = document.getText(0, length);
1117: bi.setText(content);
1118: return (pos < bi.last()) ? bi.following(pos) : pos;
1119: }
1120:
1121: private int getWordStart(final int pos) throws BadLocationException {
1122: BreakIterator bi = BreakIterator.getWordInstance();
1123: int length = document.getLength();
1124: if (pos < 0 || pos > length) {
1125: // awt.2B=No word at {0}
1126: throwException(Messages.getString("awt.2B", pos), pos); //$NON-NLS-1$
1127: }
1128: String content = null;
1129: content = document.getText(0, length);
1130: bi.setText(content);
1131: int iteratorWordStart = pos;
1132: if (pos < length - 1) {
1133: iteratorWordStart = bi.preceding(pos + 1);
1134: } else {
1135: bi.last();
1136: iteratorWordStart = bi.previous();
1137: }
1138: return iteratorWordStart;
1139: }
1140:
1141: private static void throwException(final String s, final int i)
1142: throws BadLocationException {
1143: throw new BadLocationException(s, i);
1144: }
1145:
1146: @Override
1147: void setEnabledImpl(boolean value) {
1148: if (value != isEnabled()) { // to avoid dead loop in repaint()
1149: super .setEnabledImpl(value);
1150: if (isShowing()) {
1151: repaint();
1152: }
1153: }
1154: }
1155:
1156: @Override
1157: void postprocessEvent(AWTEvent e, long eventMask) {
1158: // have to call system listeners without AWT lock
1159: // to avoid deadlocks in code common with UI text
1160: if (eventMask == AWTEvent.FOCUS_EVENT_MASK) {
1161: preprocessFocusEvent((FocusEvent) e);
1162: } else if (eventMask == AWTEvent.KEY_EVENT_MASK) {
1163: preprocessKeyEvent((KeyEvent) e);
1164: } else if (eventMask == AWTEvent.MOUSE_EVENT_MASK) {
1165: preprocessMouseEvent((MouseEvent) e);
1166: } else if (eventMask == AWTEvent.MOUSE_MOTION_EVENT_MASK) {
1167: preprocessMouseMotionEvent((MouseEvent) e);
1168: } else {
1169: super .postprocessEvent(e, eventMask);
1170: }
1171: }
1172:
1173: void performTextAction(AWTTextAction action) {
1174: action.performAction(getTextKit());
1175: }
1176: }
|