001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: /**
018: * @author Dmitry A. Durnev
019: * @version $Revision$
020: */package org.apache.harmony.awt.im;
021:
022: import java.awt.Component;
023: import java.awt.Dimension;
024: import java.awt.Graphics;
025: import java.awt.HeadlessException;
026: import java.awt.Point;
027: import java.awt.Rectangle;
028: import java.awt.TextField;
029: import java.awt.Toolkit;
030: import java.awt.event.InputMethodEvent;
031: import java.awt.event.KeyEvent;
032: import java.awt.font.TextHitInfo;
033: import java.awt.im.InputMethodRequests;
034: import java.text.CharacterIterator;
035:
036: import javax.swing.text.DefaultCaret;
037:
038: import org.apache.harmony.awt.ComponentInternals;
039: import org.apache.harmony.awt.text.InputMethodListenerImpl;
040: import org.apache.harmony.awt.text.InputMethodRequestsImpl;
041: import org.apache.harmony.awt.text.TextKit;
042:
043: /**
044: * Active IM client used to implement "below-the-spot" input style
045: * with active clients & "root-window" style with passive clients.
046: */
047: class CompositionWindow extends IMWindow {
048: private class ActiveClient extends TextField {
049:
050: private final InputMethodRequestsImpl imRequests;
051: private final InputMethodListenerImpl imListener;
052: private final DefaultCaret caret;
053:
054: ActiveClient() throws HeadlessException {
055: // prevent IM invocation on this component
056: enableInputMethods(false);
057: ComponentInternals ci = ComponentInternals
058: .getComponentInternals();
059: TextKit textKit = ci.getTextKit(this );
060: caret = (DefaultCaret) textKit.getCaret();
061: caret.setBlinkRate(0);
062: caret.setVisible(true);
063: imRequests = new InputMethodRequestsImpl(textKit) {
064: @Override
065: public TextHitInfo getLocationOffset(int x, int y) {
066: if (!isShowing()) {
067: return null;
068: }
069: return super .getLocationOffset(x, y);
070: }
071: };
072: imListener = new InputMethodListenerImpl(textKit) {
073: @Override
074: public void inputMethodTextChanged(InputMethodEvent ime) {
075: super .inputMethodTextChanged(ime);
076: // create KEY_TYPED event for each committed char
077: // and send it to passive client or just
078: // redirect this event to active client
079: if (client == null) {
080: return;
081: }
082: InputMethodRequests imr = client
083: .getInputMethodRequests();
084: if (imr != null) {
085: if (IMManager.belowTheSpot()) {
086: // position window below the spot:
087: TextHitInfo offset = TextHitInfo.leading(0);
088: Rectangle textLoc = imr
089: .getTextLocation(offset);
090: setLocationBelow(textLoc);
091: } else {
092: client.dispatchEvent(ime);
093: return;
094: }
095: }
096: sendCommittedText(ime);
097:
098: }
099: };
100: addInputMethodListener(imListener);
101: }
102:
103: private void sendCommittedText(InputMethodEvent ime) {
104: int n = ime.getCommittedCharacterCount();
105: // remove each committed char from text component
106: if (n > 0) {
107: setText(getText().substring(n));
108: }
109: char c;
110: CharacterIterator text = ime.getText();
111: if (text != null) {
112: c = text.first();
113: while (n-- > 0) {
114: sendChar((Component) ime.getSource(),
115: ime.getWhen(), c);
116: c = text.next();
117: }
118: }
119:
120: }
121:
122: private void sendChar(Component src, long when, char c) {
123: KeyEvent ke = new KeyEvent(src, KeyEvent.KEY_TYPED, when,
124: 0, KeyEvent.VK_UNDEFINED, c);
125: src.dispatchEvent(ke);
126:
127: }
128:
129: @Override
130: public InputMethodRequests getInputMethodRequests() {
131: return imRequests;
132: }
133:
134: @Override
135: public void paint(Graphics g) {
136: caret.paint(g);
137: }
138:
139: }
140:
141: private Component client;
142: private final ActiveClient activeClient;
143:
144: public CompositionWindow(Component client) {
145: setClient(client);
146: activeClient = new ActiveClient();
147: add(activeClient);
148: setSize(500, 40);
149: // use root window input style by default:
150: Dimension screenSize = getScreenSize();
151: int x = screenSize.width - getWidth();
152: int y = screenSize.height - 3 * getHeight();
153: setLocation(x, y);
154: }
155:
156: private Dimension getScreenSize() {
157: return Toolkit.getDefaultToolkit().getScreenSize();
158: }
159:
160: @Override
161: public InputMethodRequests getInputMethodRequests() {
162: return getActiveClient().getInputMethodRequests();
163: }
164:
165: Component getActiveClient() {
166: return activeClient;
167: }
168:
169: final void setClient(Component client) {
170: this .client = client;
171: }
172:
173: boolean isEmpty() {
174: return (activeClient.getText().length() == 0);
175: }
176:
177: private void setLocationBelow(Rectangle textLoc) {
178: Point loc = textLoc.getLocation();
179: loc.translate(0, textLoc.height);
180: Dimension screenSize = getScreenSize();
181: int h = getHeight();
182: int w = getWidth();
183: if (loc.y + h > screenSize.height) {
184: loc.y = textLoc.y - h;
185: }
186: if (loc.x + w > screenSize.width) {
187: loc.x = screenSize.width - w;
188: }
189: setLocation(loc);
190: }
191:
192: }
|