blob: 54252cfdd95e3c0ec6ebe043ef3bd5eafe7fd140 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.awt.im;
27
28import java.awt.AWTEvent;
29import java.awt.Component;
30import java.awt.GraphicsEnvironment;
31import java.awt.HeadlessException;
32import java.awt.Rectangle;
33import java.awt.Toolkit;
34import java.awt.Window;
35import java.awt.event.KeyEvent;
36import java.awt.event.InputMethodEvent;
37import java.awt.font.TextHitInfo;
38import java.awt.im.InputMethodRequests;
39import java.awt.im.spi.InputMethod;
40import java.security.AccessController;
41import java.text.AttributedCharacterIterator;
42import java.text.AttributedCharacterIterator.Attribute;
43import java.text.AttributedString;
44import java.text.CharacterIterator;
45import javax.swing.JFrame;
46import sun.awt.InputMethodSupport;
47import sun.security.action.GetPropertyAction;
48
49/**
50 * The InputMethodContext class provides methods that input methods
51 * can use to communicate with their client components.
52 * It is a subclass of InputContext, which provides methods for use by
53 * components.
54 *
55 * @author JavaSoft International
56 */
57
58public class InputMethodContext
59 extends sun.awt.im.InputContext
60 implements java.awt.im.spi.InputMethodContext {
61
62 private boolean dispatchingCommittedText;
63
64 // Creation of the context's composition area handler is
65 // delayed until we really need a composition area.
66 private CompositionAreaHandler compositionAreaHandler;
67 private Object compositionAreaHandlerLock = new Object();
68
69 static private boolean belowTheSpotInputRequested;
70 private boolean inputMethodSupportsBelowTheSpot;
71
72 static {
73 // check whether we should use below-the-spot input
74 // get property from command line
75 String inputStyle = (String) AccessController.doPrivileged
76 (new GetPropertyAction("java.awt.im.style", null));
77 // get property from awt.properties file
78 if (inputStyle == null) {
79 inputStyle = Toolkit.getDefaultToolkit().
80 getProperty("java.awt.im.style", null);
81 }
82 belowTheSpotInputRequested = "below-the-spot".equals(inputStyle);
83 }
84
85 /**
86 * Constructs an InputMethodContext.
87 */
88 public InputMethodContext() {
89 super();
90 }
91
92 void setInputMethodSupportsBelowTheSpot(boolean supported) {
93 inputMethodSupportsBelowTheSpot = supported;
94 }
95
96 boolean useBelowTheSpotInput() {
97 return belowTheSpotInputRequested && inputMethodSupportsBelowTheSpot;
98 }
99
100 private boolean haveActiveClient() {
101 Component client = getClientComponent();
102 return client != null
103 && client.getInputMethodRequests() != null;
104 }
105
106 // implements java.awt.im.spi.InputMethodContext.dispatchInputMethodEvent
107 public void dispatchInputMethodEvent(int id,
108 AttributedCharacterIterator text, int committedCharacterCount,
109 TextHitInfo caret, TextHitInfo visiblePosition) {
110 // We need to record the client component as the source so
111 // that we have correct information if we later have to break up this
112 // event into key events.
113 Component source;
114
115 source = getClientComponent();
116 if (source != null) {
117 InputMethodEvent event = new InputMethodEvent(source,
118 id, text, committedCharacterCount, caret, visiblePosition);
119
120 if (haveActiveClient() && !useBelowTheSpotInput()) {
121 source.dispatchEvent(event);
122 } else {
123 getCompositionAreaHandler(true).processInputMethodEvent(event);
124 }
125 }
126 }
127
128 /**
129 * Dispatches committed text to a client component.
130 * Called by composition window.
131 *
132 * @param client The component that the text should get dispatched to.
133 * @param text The iterator providing access to the committed
134 * (and possible composed) text.
135 * @param committedCharacterCount The number of committed characters in the text.
136 */
137 synchronized void dispatchCommittedText(Component client,
138 AttributedCharacterIterator text,
139 int committedCharacterCount) {
140 // note that the client is not always the current client component -
141 // some host input method adapters may dispatch input method events
142 // through the Java event queue, and we may have switched clients while
143 // the event was in the queue.
144 if (committedCharacterCount == 0
145 || text.getEndIndex() <= text.getBeginIndex()) {
146 return;
147 }
148 long time = System.currentTimeMillis();
149 dispatchingCommittedText = true;
150 try {
151 InputMethodRequests req = client.getInputMethodRequests();
152 if (req != null) {
153 // active client -> send text as InputMethodEvent
154 int beginIndex = text.getBeginIndex();
155 AttributedCharacterIterator toBeCommitted =
156 (new AttributedString(text, beginIndex, beginIndex + committedCharacterCount)).getIterator();
157
158 InputMethodEvent inputEvent = new InputMethodEvent(
159 client,
160 InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
161 toBeCommitted,
162 committedCharacterCount,
163 null, null);
164
165 client.dispatchEvent(inputEvent);
166 } else {
167 // passive client -> send text as KeyEvents
168 char keyChar = text.first();
169 while (committedCharacterCount-- > 0 && keyChar != CharacterIterator.DONE) {
170 KeyEvent keyEvent = new KeyEvent(client, KeyEvent.KEY_TYPED,
171 time, 0, KeyEvent.VK_UNDEFINED, keyChar);
172 client.dispatchEvent(keyEvent);
173 keyChar = text.next();
174 }
175 }
176 } finally {
177 dispatchingCommittedText = false;
178 }
179 }
180
181 public void dispatchEvent(AWTEvent event) {
182 // some host input method adapters may dispatch input method events
183 // through the Java event queue. If the component that the event is
184 // intended for isn't an active client, or if we're using below-the-spot
185 // input, we need to dispatch this event
186 // to the input window. Note that that component is not necessarily the
187 // current client component, since we may have switched clients while
188 // the event was in the queue.
189 if (event instanceof InputMethodEvent) {
190 if (((Component) event.getSource()).getInputMethodRequests() == null
191 || (useBelowTheSpotInput() && !dispatchingCommittedText)) {
192 getCompositionAreaHandler(true).processInputMethodEvent((InputMethodEvent) event);
193 }
194 } else {
195 // make sure we don't dispatch our own key events back to the input method
196 if (!dispatchingCommittedText) {
197 super.dispatchEvent(event);
198 }
199 }
200 }
201
202 /**
203 * Gets this context's composition area handler, creating it if necessary.
204 * If requested, it grabs the composition area for use by this context.
205 * The composition area's text is not updated.
206 */
207 private CompositionAreaHandler getCompositionAreaHandler(boolean grab) {
208 synchronized(compositionAreaHandlerLock) {
209 if (compositionAreaHandler == null) {
210 compositionAreaHandler = new CompositionAreaHandler(this);
211 }
212 compositionAreaHandler.setClientComponent(getClientComponent());
213 if (grab) {
214 compositionAreaHandler.grabCompositionArea(false);
215 }
216
217 return compositionAreaHandler;
218 }
219 }
220
221 /**
222 * Grabs the composition area for use by this context.
223 * If doUpdate is true, updates the composition area with previously sent
224 * composed text.
225 */
226 void grabCompositionArea(boolean doUpdate) {
227 synchronized(compositionAreaHandlerLock) {
228 if (compositionAreaHandler != null) {
229 compositionAreaHandler.grabCompositionArea(doUpdate);
230 } else {
231 // if this context hasn't seen a need for a composition area yet,
232 // just close it without creating the machinery
233 CompositionAreaHandler.closeCompositionArea();
234 }
235 }
236 }
237
238 /**
239 * Releases and closes the composition area if it is currently owned by
240 * this context's composition area handler.
241 */
242 void releaseCompositionArea() {
243 synchronized(compositionAreaHandlerLock) {
244 if (compositionAreaHandler != null) {
245 compositionAreaHandler.releaseCompositionArea();
246 }
247 }
248 }
249
250 /**
251 * Calls CompositionAreaHandler.isCompositionAreaVisible() to see
252 * whether the composition area is visible or not.
253 * Notice that this method is always called on the AWT event dispatch
254 * thread.
255 */
256 boolean isCompositionAreaVisible() {
257 if (compositionAreaHandler != null) {
258 return compositionAreaHandler.isCompositionAreaVisible();
259 }
260
261 return false;
262 }
263 /**
264 * Calls CompositionAreaHandler.setCompositionAreaVisible to
265 * show or hide the composition area.
266 * As isCompositionAreaVisible method, it is always called
267 * on AWT event dispatch thread.
268 */
269 void setCompositionAreaVisible(boolean visible) {
270 if (compositionAreaHandler != null) {
271 compositionAreaHandler.setCompositionAreaVisible(visible);
272 }
273 }
274
275 /**
276 * Calls the current client component's implementation of getTextLocation.
277 */
278 public Rectangle getTextLocation(TextHitInfo offset) {
279 return getReq().getTextLocation(offset);
280 }
281
282 /**
283 * Calls the current client component's implementation of getLocationOffset.
284 */
285 public TextHitInfo getLocationOffset(int x, int y) {
286 return getReq().getLocationOffset(x, y);
287 }
288
289 /**
290 * Calls the current client component's implementation of getInsertPositionOffset.
291 */
292 public int getInsertPositionOffset() {
293 return getReq().getInsertPositionOffset();
294 }
295
296 /**
297 * Calls the current client component's implementation of getCommittedText.
298 */
299 public AttributedCharacterIterator getCommittedText(int beginIndex,
300 int endIndex,
301 Attribute[] attributes) {
302 return getReq().getCommittedText(beginIndex, endIndex, attributes);
303 }
304
305 /**
306 * Calls the current client component's implementation of getCommittedTextLength.
307 */
308 public int getCommittedTextLength() {
309 return getReq().getCommittedTextLength();
310 }
311
312
313 /**
314 * Calls the current client component's implementation of cancelLatestCommittedText.
315 */
316 public AttributedCharacterIterator cancelLatestCommittedText(Attribute[] attributes) {
317 return getReq().cancelLatestCommittedText(attributes);
318 }
319
320 /**
321 * Calls the current client component's implementation of getSelectedText.
322 */
323 public AttributedCharacterIterator getSelectedText(Attribute[] attributes) {
324 return getReq().getSelectedText(attributes);
325 }
326
327 private InputMethodRequests getReq() {
328 if (haveActiveClient() && !useBelowTheSpotInput()) {
329 return getClientComponent().getInputMethodRequests();
330 } else {
331 return getCompositionAreaHandler(false);
332 }
333 }
334
335 // implements java.awt.im.spi.InputMethodContext.createInputMethodWindow
336 public Window createInputMethodWindow(String title, boolean attachToInputContext) {
337 InputContext context = attachToInputContext ? this : null;
338 return createInputMethodWindow(title, context, false);
339 }
340
341 // implements java.awt.im.spi.InputMethodContext.createInputMethodJFrame
342 public JFrame createInputMethodJFrame(String title, boolean attachToInputContext) {
343 InputContext context = attachToInputContext ? this : null;
344 return (JFrame)createInputMethodWindow(title, context, true);
345 }
346
347 static Window createInputMethodWindow(String title, InputContext context, boolean isSwing) {
348 if (GraphicsEnvironment.isHeadless()) {
349 throw new HeadlessException();
350 }
351 if (isSwing) {
352 return new InputMethodJFrame(title, context);
353 } else {
354 Toolkit toolkit = Toolkit.getDefaultToolkit();
355 if (toolkit instanceof InputMethodSupport) {
356 return ((InputMethodSupport)toolkit).createInputMethodWindow(
357 title, context);
358 }
359 }
360 throw new InternalError("Input methods must be supported");
361 }
362
363 /**
364 * @see java.awt.im.spi.InputMethodContext#enableClientWindowNotification
365 */
366 public void enableClientWindowNotification(InputMethod inputMethod, boolean enable) {
367 super.enableClientWindowNotification(inputMethod, enable);
368 }
369
370 /**
371 * Disables or enables decorations for the composition window.
372 */
373 void setCompositionAreaUndecorated(boolean undecorated) {
374 if (compositionAreaHandler != null) {
375 compositionAreaHandler.setCompositionAreaUndecorated(undecorated);
376 }
377 }
378}