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.AWTException;
023: import java.awt.CheckboxMenuItem;
024: import java.awt.Component;
025: import java.awt.HeadlessException;
026: import java.awt.Menu;
027: import java.awt.MenuContainer;
028: import java.awt.MenuItem;
029: import java.awt.PopupMenu;
030: import java.awt.Window;
031: import java.awt.event.ItemEvent;
032: import java.awt.event.ItemListener;
033: import java.awt.event.KeyEvent;
034: import java.awt.im.spi.InputMethodDescriptor;
035: import java.io.BufferedReader;
036: import java.io.InputStreamReader;
037: import java.net.URL;
038: import java.util.Enumeration;
039: import java.util.Iterator;
040: import java.util.LinkedList;
041: import java.util.List;
042: import java.util.Locale;
043: import java.util.prefs.Preferences;
044:
045: import org.apache.harmony.awt.ComponentInternals;
046: import org.apache.harmony.awt.ContextStorage;
047: import org.apache.harmony.awt.wtk.NativeIM;
048:
049: /**
050: * Helper class which manages resources shared by
051: * several InputMethodContexts, such as list of
052: * IM descriptors, IM selection popup, currently visible composition
053: * window, etc
054: */
055: public class IMManager {
056: private static final String SEL_KEY_NODE = "java/awt/im/selectionKey"; //$NON-NLS-1$
057: private static final String INPUT_STYLE_PROP = "java.awt.im.style"; //$NON-NLS-1$
058: private static final String BELOW_THE_SPOT = "below-the-spot"; //$NON-NLS-1$
059: private static final String SERVICES = "META-INF/services/"; //$NON-NLS-1$
060:
061: /**
062: * Input method selection popup menu
063: */
064: private static class IMSelection extends PopupMenu implements
065: ItemListener {
066:
067: private class IMenuItem extends CheckboxMenuItem {
068: private final Locale locale;
069:
070: IMenuItem(Locale loc) throws HeadlessException {
071: super (loc.getDisplayName());
072: locale = loc;
073: }
074:
075: private final InputMethodDescriptor getDesc() {
076: MenuContainer parent = getParent();
077: if (parent instanceof IMSubmenu) {
078: return ((IMSubmenu) parent).getDesc();
079: }
080: return null;
081: }
082:
083: private final Locale getLocale() {
084: return locale;
085: }
086:
087: @Override
088: public String paramString() {
089: return super .paramString() + ",desc=" + //$NON-NLS-1$
090: getDesc() + ",locale=" + locale; //$NON-NLS-1$
091: }
092:
093: }
094:
095: private class IMSubmenu extends Menu {
096: private final InputMethodDescriptor desc;
097:
098: IMSubmenu(InputMethodDescriptor imd)
099: throws HeadlessException {
100: super (imd.getInputMethodDisplayName(null, null));
101: desc = imd;
102: addLocales();
103: }
104:
105: private final InputMethodDescriptor getDesc() {
106: return desc;
107: }
108:
109: @Override
110: public String paramString() {
111: return super .paramString() + ",desc=" + desc; //$NON-NLS-1$
112: }
113:
114: private void addLocales() {
115: try {
116: Locale[] locs = desc.getAvailableLocales();
117: for (Locale element : locs) {
118: addMenuItem(element);
119: }
120: } catch (AWTException e) {
121: e.printStackTrace();
122: }
123: }
124:
125: private void addMenuItem(Locale loc) {
126: IMenuItem item = new IMenuItem(loc);
127: item.addItemListener(IMSelection.this );
128: add(item);
129: }
130:
131: private void checkItems() {
132: for (int i = 0; i < getItemCount(); i++) {
133: IMenuItem item = (IMenuItem) getItem(i);
134: try {
135: item.setState(item.getLocale().equals(
136: imContext.getLocale()));
137: // TODO: also check input method descriptor
138: } catch (Exception e) {
139: e.printStackTrace();
140: }
141: }
142: }
143: }
144:
145: InputMethodContext imContext;
146:
147: public IMSelection() {
148: Iterator<InputMethodDescriptor> it = getIMDescriptors()
149: .iterator();
150: // native IM is always first
151: addNativeIM(it);
152: while (it.hasNext()) {
153: addIM(it);
154: }
155: }
156:
157: private void addIM(Iterator<InputMethodDescriptor> it) {
158: InputMethodDescriptor desc = it.next();
159: IMSubmenu subMenu = new IMSubmenu(desc);
160: add(subMenu);
161: }
162:
163: private void addNativeIM(Iterator<InputMethodDescriptor> it) {
164: if (it.hasNext()) {
165: addIM(it);
166: }
167: add(new MenuItem("-")); //separator //$NON-NLS-1$
168: }
169:
170: public void itemStateChanged(ItemEvent e) {
171: if (imContext == null) {
172: return;
173: }
174: Object src = e.getSource();
175: if (src instanceof IMenuItem) {
176: IMenuItem item = (IMenuItem) src;
177: imContext.selectIM(item.getDesc(), item.getLocale());
178: }
179:
180: }
181:
182: private void show(Component origin, InputMethodContext imc) {
183: imContext = imc;
184:
185: for (int i = 0; i < getItemCount(); i++) {
186: MenuItem item = getItem(i);
187: if (!(item instanceof IMSubmenu)) {
188: // skip all non-menu elements, such as separators
189: continue;
190: }
191: IMSubmenu subMenu = (IMSubmenu) item;
192: InputMethodDescriptor desc = subMenu.getDesc();
193: if (desc == null) {
194: continue;
195: }
196: if (desc.hasDynamicLocaleList()) {
197: subMenu.removeAll();
198: subMenu.addLocales();
199: }
200: subMenu.checkItems();
201: i++;
202: }
203: show(origin, 50, 50);
204: }
205: }
206:
207: private static List<InputMethodDescriptor> imd; // available IM descriptors
208: private static IMSelection imPopup; // IM selection popup menu
209: // only 1 composition window is visible even if there're several InputContexts
210: private static Window curCompositionWindow;
211: // last InputMethodContext which had an active IM
212: private static InputMethodContext lastActiveIMC;
213:
214: static List<InputMethodDescriptor> getIMDescriptors() {
215: if (imd == null) {
216: imd = loadIMDescriptors();
217: }
218: return imd;
219: }
220:
221: /**
222: * Loads all IM descriptors from
223: * extension jars(services).
224: * Does roughly the same as
225: * Service.providers(InputMethodDescriptor.class)
226: * @return list of input method descriptors
227: */
228: private static List<InputMethodDescriptor> loadIMDescriptors() {
229: String nm = SERVICES + InputMethodDescriptor.class.getName();
230: Enumeration<URL> en;
231: LinkedList<InputMethodDescriptor> imdList = new LinkedList<InputMethodDescriptor>();
232: // first add native IM descriptor(is always present)
233: NativeIM nativeIM = ContextStorage.getNativeIM();
234: imdList.add(nativeIM);
235: try {
236: en = ClassLoader.getSystemResources(nm);
237: ClassLoader cl = ClassLoader.getSystemClassLoader();
238: while (en.hasMoreElements()) {
239: URL url = en.nextElement();
240: InputStreamReader isr = new InputStreamReader(url
241: .openStream(), "UTF-8"); //$NON-NLS-1$
242: BufferedReader br = new BufferedReader(isr);
243: String str = br.readLine();
244:
245: while (str != null) {
246: str = str.trim();
247: int comPos = str.indexOf("#"); //$NON-NLS-1$
248: if (comPos >= 0) {
249: str = str.substring(0, comPos);
250: }
251: if (str.length() > 0) {
252: imdList.add((InputMethodDescriptor) cl
253: .loadClass(str).newInstance());
254: }
255: str = br.readLine();
256: }
257:
258: }
259: } catch (Exception e) {
260: // just silently ignore exception ?
261: }
262: return imdList;
263: }
264:
265: private static void showIMPopup(InputMethodContext imc,
266: Window parent) {
267: List<InputMethodDescriptor> descriptors = getIMDescriptors();
268: if ((descriptors == null) || descriptors.isEmpty()) {
269: // show menu only if some non-native IMs are present
270: return;
271: }
272: if (imPopup == null) {
273: imPopup = new IMSelection();
274: }
275:
276: if (parent != null) {
277: parent.add(imPopup);
278: imPopup.show(parent, imc);
279: }
280: }
281:
282: private static int getPref(String key, int def) {
283: int pref = getPref(Preferences.userRoot(), key, def);
284:
285: if (pref != def) {
286: return pref;
287: }
288:
289: return getPref(Preferences.systemRoot(), key, def);
290: }
291:
292: private static int getPref(Preferences root, String key, int def) {
293: return root.node(SEL_KEY_NODE).getInt(key, def);
294: }
295:
296: static void selectIM(KeyEvent ke, InputMethodContext imc,
297: Window parent) {
298: int def = KeyEvent.VK_UNDEFINED;
299: int keyCode = getPref("keyCode", def); //$NON-NLS-1$
300: if (keyCode != def) {
301: int modifiers = getPref("modifiers", 0); //$NON-NLS-1$
302: if ((ke.getKeyCode() == keyCode)
303: && (ke.getModifiers() == modifiers)) {
304: IMManager.showIMPopup(imc, parent);
305:
306: }
307: }
308: }
309:
310: @SuppressWarnings("deprecation")
311: static void showCompositionWindow(Window w) {
312:
313: if (curCompositionWindow != null) {
314: if (curCompositionWindow != w) {
315: curCompositionWindow.hide();
316: }
317: }
318: curCompositionWindow = w;
319: if ((curCompositionWindow != null)
320: && !curCompositionWindow.isVisible()) {
321: curCompositionWindow.show();
322: }
323: }
324:
325: private static String getInputStyle() {
326: String propName = INPUT_STYLE_PROP;
327: String inputStyle = System.getProperty(propName);
328: if (inputStyle != null) {
329: return inputStyle;
330: }
331: return java.awt.Toolkit.getProperty(propName, null);
332: }
333:
334: static boolean belowTheSpot() {
335: return BELOW_THE_SPOT.equals(getInputStyle());
336: }
337:
338: static Window getWindow(Component comp) {
339: if (comp == null) {
340: return null;
341: }
342: Component parent = comp.getParent();
343: while ((parent != null) && !(parent instanceof Window)) {
344: parent = parent.getParent();
345: }
346: return (Window) parent;
347: }
348:
349: static void makeIMWindow(Window win) {
350: win.setFocusableWindowState(false);
351: win.setAlwaysOnTop(true);
352: ComponentInternals ci = ComponentInternals
353: .getComponentInternals();
354: ci.getNativeWindow(win).setIMStyle();
355: }
356:
357: public static final InputMethodContext getLastActiveIMC() {
358: return lastActiveIMC;
359: }
360:
361: static final void setLastActiveIMC(InputMethodContext lastActiveIMC) {
362: IMManager.lastActiveIMC = lastActiveIMC;
363: }
364: }
|