ICU-3713 add translit input method tool to icu4j

X-SVN-Rev: 15054
This commit is contained in:
Doug Felt 2004-04-26 23:47:45 +00:00
parent 813069f084
commit 8fa8d3c4d6
6 changed files with 714 additions and 480 deletions

View File

@ -1,467 +0,0 @@
/*
* ===========================================================================
* IBM ICU Transliterator IME - TransliteratorInputMethod.java version 1.0
* (C) Copyright IBM Corp. 2004. All Rights Reserved
* ===========================================================================
*/
package com.ibm.icu.dev.tool.ime;
import java.awt.*;
import java.util.*;
import javax.swing.*;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.InputEvent;
import java.awt.event.InputMethodEvent;
import java.awt.event.KeyEvent;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.font.TextAttribute;
import java.awt.font.TextHitInfo;
import java.awt.im.InputMethodHighlight;
import java.awt.im.spi.InputMethod;
import java.awt.im.spi.InputMethodContext;
import java.text.AttributedString;
import com.ibm.icu.text.Transliterator;
import com.ibm.icu.text.ReplaceableString;
import com.ibm.icu.text.Collator;
public class TransliteratorInputMethod implements InputMethod {
// windows - shared by all instances of this input method
private static Window statusWindow;
// current or last statusWindow owner instance
private static TransliteratorInputMethod statusWindowOwner;
// true if Solaris style; false if PC style
private static boolean attachedStatusWindow = false;
// remember the statusWindow location per instance
private Rectangle clientWindowLocation;
// status window location in PC style
private static Point globalStatusWindowLocation;
// keep live input method instances (synchronized using statusWindow)
private static HashSet inputMethodInstances = new HashSet(5);
// per-instance state
InputMethodContext inputMethodContext;
private boolean active;
private boolean disposed;
private StringBuffer rawText;
private Transliterator transliterator = null;
private Transliterator.Position index = null;
private ReplaceableString replaceableText = null;
private ResourceBundle rb = null;
public TransliteratorInputMethod() {
rawText = new StringBuffer();
replaceableText = new ReplaceableString(rawText);
try {
rb = ResourceBundle.getBundle("com.ibm.icu.dev.tool.ime.Transliterator");
}
catch(MissingResourceException m) {
System.out.println("Transliterator resources missing: " + m);
}
}
public void setInputMethodContext(InputMethodContext context) {
inputMethodContext = context;
String title = null;
if (statusWindow == null) {
try {
title = rb.getString("title");
}
catch (MissingResourceException m) {
title = "Transliterator Input Method";
}
Window sw = context.createInputMethodWindow(title, false);
// This is to fix a problem in Java 1.4
try {
sw.setFocusableWindowState(true);
}
catch(NoSuchMethodError err) {
/* we do not need to worry about catching this, the setFocusableWindowState
only exists in Java 1.4. In Java 1.3 it is not necessary to make the
status window focusable, because the default setting is focusable. */
}
// get all the ICU Transliterators
Enumeration e = Transliterator.getAvailableIDs();
TreeSet types = new TreeSet(new LabelComparator());
while(e.hasMoreElements()) {
String id = (String) e.nextElement();
String name = Transliterator.getDisplayName(id);
JLabel label = new JLabel(name);
label.setName(id);
types.add(label);
}
// add the transliterators to the combo box
System.out.println("types: " + types);
JComboBox choices = new JComboBox(types.toArray());
choices.setEditable(false);
choices.setRenderer(new NameRenderer());
choices.setActionCommand("transliterator");
synchronized (this.getClass()) {
if (statusWindow == null) {
statusWindow = sw;
statusWindow.addComponentListener(new ComponentListener() {
public void componentResized(ComponentEvent e) {}
public void componentMoved(ComponentEvent e) {
synchronized (statusWindow) {
if (!attachedStatusWindow) {
Component comp = e.getComponent();
if (comp.isVisible()) {
globalStatusWindowLocation = comp.getLocation();
}
}
}
}
public void componentShown(ComponentEvent e) {}
public void componentHidden(ComponentEvent e) {}
});
// setup the listener, to handle selection of a transliterator
choices.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JComboBox cb = (JComboBox)e.getSource();
JLabel item = (JLabel)cb.getSelectedItem();
// construct the actual transliterator
// commit any text that may be present first
commitAll();
transliterator = Transliterator.getInstance(item.getName());
index = new Transliterator.Position();
rawText.setLength(0);
index.contextLimit = replaceableText.length();
index.contextStart = 0;
index.start = 0;
index.limit = replaceableText.length();
}
});
statusWindow.add(choices);
statusWindowOwner = this;
updateStatusWindow();
statusWindow.pack();
choices.setSelectedIndex(0);
}
}
}
inputMethodContext.enableClientWindowNotification(this, attachedStatusWindow);
synchronized (statusWindow) {
inputMethodInstances.add(this);
}
}
public boolean setLocale(Locale locale) {
return false;
}
public Locale getLocale() {
return Locale.getDefault();
}
void updateStatusWindow() {
synchronized (statusWindow) {
statusWindow.pack();
if (attachedStatusWindow) {
if (clientWindowLocation != null) {
statusWindow.setLocation(clientWindowLocation.x,
clientWindowLocation.y + clientWindowLocation.height);
}
} else {
setPCStyleStatusWindow();
}
}
}
private void setPCStyleStatusWindow() {
synchronized (statusWindow) {
if (globalStatusWindowLocation == null) {
Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
globalStatusWindowLocation = new Point(d.width - statusWindow.getWidth(),
d.height - statusWindow.getHeight() - 25);
}
statusWindow.setLocation(globalStatusWindowLocation.x, globalStatusWindowLocation.y);
}
}
private void setStatusWindowForeground(Color fg) {
synchronized (statusWindow) {
if (statusWindowOwner != this) {
return;
}
}
}
private void toggleStatusWindowStyle() {
synchronized (statusWindow) {
if (attachedStatusWindow) {
attachedStatusWindow = false;
setPCStyleStatusWindow();
} else {
attachedStatusWindow = true;
}
Iterator itr = inputMethodInstances.iterator();
while (itr.hasNext()) {
TransliteratorInputMethod im = (TransliteratorInputMethod)itr.next();
im.inputMethodContext.enableClientWindowNotification(im, attachedStatusWindow);
}
}
}
public void setCharacterSubsets(Character.Subset[] subsets) {
// ignore
}
public void reconvert() {
// not supported yet
throw new UnsupportedOperationException();
}
public void dispatchEvent(AWTEvent event) {
if (!active && (event instanceof KeyEvent)) {
System.out.println(rb.getString("activeError"));
}
if (disposed) {
System.out.println(rb.getString("disposedError"));
}
if (!(event instanceof InputEvent)) {
System.out.println(rb.getString("inputError"));
}
if (event.getID() == KeyEvent.KEY_TYPED) {
KeyEvent e = (KeyEvent) event;
if (handleCharacter(e.getKeyChar())) {
e.consume();
}
}
}
public void activate() {
if (active) {
System.out.println(rb.getString("activateError"));
}
System.out.println("activate");
active = true;
synchronized (statusWindow) {
statusWindowOwner = this;
updateStatusWindow();
if (!statusWindow.isVisible()) {
statusWindow.setVisible(true);
}
setStatusWindowForeground(Color.black);
}
}
public void deactivate(boolean isTemporary) {
if (!active) {
System.out.println(rb.getString("deactivateError"));
}
System.out.println("deactivate");
setStatusWindowForeground(Color.lightGray);
active = false;
}
public void hideWindows() {
if (active) {
System.out.println(rb.getString("hideError"));
}
synchronized (statusWindow) {
if (statusWindowOwner == this) {
statusWindow.setVisible(false);
}
}
}
public void removeNotify() {
}
public void endComposition() {
commitAll();
}
public void notifyClientWindowChange(Rectangle location) {
clientWindowLocation = location;
synchronized (statusWindow) {
if (!attachedStatusWindow || statusWindowOwner != this) {
return;
}
if (location != null) {
statusWindow.setLocation(location.x, location.y+location.height);
if (!statusWindow.isVisible()) {
if (active) {
setStatusWindowForeground(Color.black);
} else {
setStatusWindowForeground(Color.lightGray);
}
statusWindow.setVisible(true);
}
} else {
statusWindow.setVisible(false);
}
}
}
public void dispose() {
if (active) {
System.out.println(rb.getString("disposeError"));
}
if (disposed) {
System.out.println(rb.getString("multipleDisposeError"));
}
synchronized (statusWindow) {
inputMethodInstances.remove(this);
}
disposed = true;
}
public Object getControlObject() {
return null;
}
public void setCompositionEnabled(boolean enable) {
// not supported yet
throw new UnsupportedOperationException();
}
public boolean isCompositionEnabled() {
// always enabled
return true;
}
/**
* Attempts to handle a typed character.
* @return whether the character was handled
*/
private boolean handleCharacter(char ch) {
if (ch == '\n') {
commitAll();
return true;
}
else if (ch == '\b') {
if (index.contextLimit > index.contextStart) {
rawText.deleteCharAt(index.contextLimit - 1);
--index.limit;
--index.contextLimit;
sendText();
return true;
}
}
else {
rawText.append(ch);
index.limit = rawText.length();
index.contextLimit = rawText.length();
transliterator.transliterate(replaceableText, index);
sendText();
index.contextStart = index.start;
return true;
}
return false;
}
/* commits all chunks */
void commitAll() {
if(transliterator != null) {
transliterator.finishTransliteration(replaceableText, index);
sendText();
rawText.setLength(0);
index.start = 0;
index.contextStart = 0;
index.limit = 0;
index.contextLimit = 0;
}
}
void sendText() {
AttributedString as = null;
TextHitInfo caret = null;
int committedCharacterCount = 0;
InputMethodHighlight highlight = InputMethodHighlight.SELECTED_RAW_TEXT_HIGHLIGHT;
String text = null;
if(index.start > index.contextStart) {
text = rawText.substring(index.contextStart, index.start);
as = new AttributedString(text);
committedCharacterCount = text.length();
inputMethodContext.dispatchInputMethodEvent(
InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
as.getIterator(),
committedCharacterCount,
null,
null);
}
if(index.contextLimit > index.start) {
text = rawText.substring(index.start, index.contextLimit);
as = new AttributedString(text);
committedCharacterCount = 0;
as.addAttribute(TextAttribute.INPUT_METHOD_HIGHLIGHT, highlight);
caret = TextHitInfo.leading(text.length());
inputMethodContext.dispatchInputMethodEvent(
InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
as.getIterator(),
committedCharacterCount,
caret,
null);
}
}
}
class NameRenderer extends JLabel implements ListCellRenderer {
public Component getListCellRendererComponent(
JList list,
Object value,
int index,
boolean isSelected,
boolean cellHasFocus) {
String s = ((JLabel)value).getText();
setText(s);
if(isSelected) {
setBackground(list.getSelectionBackground());
setForeground(list.getSelectionForeground());
}
else {
setBackground(list.getBackground());
setForeground(list.getForeground());
}
setEnabled(list.isEnabled());
setFont(list.getFont());
setOpaque(true);
return this;
}
}
class LabelComparator implements Comparator {
public int compare(Object obj1, Object obj2) {
Collator collator = Collator.getInstance();
return collator.compare(((JLabel)obj1).getText(), ((JLabel)obj2).getText());
}
public boolean equals(Object obj1) {
return this.equals(obj1);
}
}

View File

@ -1,7 +0,0 @@
#
#
# fully-qualified name of the java.awt.im.spi.InputMethodDescriptor
# implementation class
#
com.ibm.icu.dev.tool.ime.TransliteratorInputMethodDescriptor

View File

@ -0,0 +1,707 @@
/*
*******************************************************************************
* Copyright (C) 2004, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package com.ibm.icu.dev.tool.translitime;
import java.awt.AWTEvent;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.InputEvent;
import java.awt.event.InputMethodEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.font.TextAttribute;
import java.awt.font.TextHitInfo;
import java.awt.im.InputMethodHighlight;
import java.awt.im.spi.InputMethod;
import java.awt.im.spi.InputMethodContext;
import java.text.AttributedString;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.MissingResourceException;
import java.util.TreeSet;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.ListCellRenderer;
import com.ibm.icu.text.Transliterator;
import com.ibm.icu.text.ReplaceableString;
import com.ibm.icu.text.Collator;
import com.ibm.icu.lang.UCharacter;
import com.ibm.icu.impl.Utility;
public class TransliteratorInputMethod implements InputMethod {
// windows - shared by all instances of this input method
private static Window statusWindow;
// current or last statusWindow owner instance
private static TransliteratorInputMethod statusWindowOwner;
// true if Solaris style; false if PC style
private static boolean attachedStatusWindow = false;
// remember the statusWindow location per instance
private Rectangle clientWindowLocation;
// status window location in PC style
private static Point globalStatusWindowLocation;
// keep live input method instances (synchronized using statusWindow)
private static HashSet inputMethodInstances = new HashSet(5);
// per-instance state
InputMethodContext imc;
private boolean enabled = true;
private boolean active;
private boolean disposed;
private Transliterator transliterator;
private int desiredContext;
private StringBuffer buffer;
private Transliterator.Position index;
private ReplaceableString replaceableText;
private ResourceBundle rb;
// we will treat index as follows:
// contextStart is always 0
// start is always 0 except just after the transliterator has processed the initial text,
// we will always immediately call update and reset this to 0
// limit we'll ignore
// contextLimit is the cursor position
private static boolean TRACE_EVENT = false;
private static boolean TRACE_BUFFER = true;
public TransliteratorInputMethod() {
buffer = new StringBuffer();
replaceableText = new ReplaceableString(buffer);
index = new Transliterator.Position();
try {
rb = ResourceBundle.getBundle("com.ibm.icu.dev.tool.translitime.Transliterator");
}
catch(MissingResourceException m) {
System.out.println("Transliterator resources missing: " + m);
}
}
public void setInputMethodContext(InputMethodContext context) {
imc = context;
String title = null;
if (statusWindow == null) {
try {
title = rb.getString("title");
}
catch (MissingResourceException m) {
title = "Transliterator Input Method";
}
Window sw = context.createInputMethodWindow(title, false);
// get all the ICU Transliterators
Enumeration e = Transliterator.getAvailableIDs();
TreeSet types = new TreeSet(new LabelComparator());
while(e.hasMoreElements()) {
String id = (String) e.nextElement();
String name = Transliterator.getDisplayName(id);
JLabel label = new JLabel(name);
label.setName(id);
types.add(label);
}
// add the transliterators to the combo box
JComboBox choices = new JComboBox(types.toArray());
choices.setEditable(false);
choices.setRenderer(new NameRenderer());
choices.setActionCommand("transliterator");
synchronized (this.getClass()) {
if (statusWindow == null) {
statusWindow = sw;
statusWindow.addComponentListener(new ComponentListener() {
public void componentResized(ComponentEvent e) {}
public void componentMoved(ComponentEvent e) {
synchronized (statusWindow) {
if (!attachedStatusWindow) {
Component comp = e.getComponent();
if (comp.isVisible()) {
globalStatusWindowLocation = comp.getLocation();
}
}
}
}
public void componentShown(ComponentEvent e) {}
public void componentHidden(ComponentEvent e) {}
});
// setup the listener, to handle selection of a transliterator
choices.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JComboBox cb = (JComboBox)e.getSource();
JLabel item = (JLabel)cb.getSelectedItem();
// construct the actual transliterator
// commit any text that may be present first
commitAll();
transliterator = Transliterator.getInstance(item.getName());
desiredContext = transliterator.getMaximumContextLength();
reset();
}
});
statusWindow.add(choices);
statusWindowOwner = this;
updateStatusWindow();
statusWindow.pack();
choices.setSelectedIndex(0);
}
}
}
imc.enableClientWindowNotification(this, attachedStatusWindow);
synchronized (statusWindow) {
inputMethodInstances.add(this);
}
}
public boolean setLocale(Locale locale) {
return false;
}
public Locale getLocale() {
return Locale.getDefault();
}
void updateStatusWindow() {
synchronized (statusWindow) {
statusWindow.pack();
if (attachedStatusWindow) {
if (clientWindowLocation != null) {
statusWindow.setLocation(clientWindowLocation.x,
clientWindowLocation.y + clientWindowLocation.height);
}
} else {
setPCStyleStatusWindow();
}
}
}
private void setPCStyleStatusWindow() {
synchronized (statusWindow) {
if (globalStatusWindowLocation == null) {
Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
globalStatusWindowLocation = new Point(d.width - statusWindow.getWidth(),
d.height - statusWindow.getHeight() - 25);
}
statusWindow.setLocation(globalStatusWindowLocation.x, globalStatusWindowLocation.y);
}
}
private void setStatusWindowForeground(Color fg) {
synchronized (statusWindow) {
if (statusWindowOwner != this) {
return;
}
statusWindow.setForeground(fg);
statusWindow.repaint();
}
}
private void toggleStatusWindowStyle() {
synchronized (statusWindow) {
if (attachedStatusWindow) {
attachedStatusWindow = false;
setPCStyleStatusWindow();
} else {
attachedStatusWindow = true;
}
Iterator itr = inputMethodInstances.iterator();
while (itr.hasNext()) {
TransliteratorInputMethod im = (TransliteratorInputMethod)itr.next();
im.imc.enableClientWindowNotification(im, attachedStatusWindow);
}
}
}
public void setCharacterSubsets(Character.Subset[] subsets) {
// ignore
}
public void reconvert() {
// not supported yet
throw new UnsupportedOperationException();
}
private String eventInfo(AWTEvent event) {
String info = event.toString();
StringBuffer buf = new StringBuffer();
int index1 = info.indexOf("[");
int index2 = info.indexOf(",", index1);
buf.append(info.substring(index1+1, index2));
index1 = info.indexOf("] on ");
index2 = info.indexOf("[", index1);
if (index2 != -1) {
int index3 = info.lastIndexOf(".", index2);
if (index3 < index1 + 4) {
index3 = index1 + 4;
}
buf.append(" on ");
buf.append(info.substring(index3+1, index2));
}
return buf.toString();
}
public void dispatchEvent(AWTEvent event) {
final int MODIFIERS = InputEvent.CTRL_MASK | InputEvent.META_MASK | InputEvent.ALT_MASK | InputEvent.ALT_GRAPH_MASK;
switch (event.getID()) {
case MouseEvent.MOUSE_PRESSED:
if (enabled) {
if (TRACE_EVENT) System.out.println("TIM: " + eventInfo(event));
// we'll get this even if the user is scrolling, can we rely on the component?
// commitAll(); // don't allow even clicks within our own edit area
}
break;
case KeyEvent.KEY_TYPED: {
if (enabled) {
KeyEvent ke = (KeyEvent)event;
if (TRACE_EVENT) System.out.println("TIM: " + eventInfo(ke));
if ((ke.getModifiers() & MODIFIERS) != 0) {
commitAll(); // assume a command, let it go through
} else {
if (handleTyped(ke.getKeyChar())) {
ke.consume();
}
}
}
} break;
case KeyEvent.KEY_PRESSED: {
if (enabled) {
KeyEvent ke = (KeyEvent)event;
if (TRACE_EVENT) System.out.println("TIM: " + eventInfo(ke));
if (handlePressed(ke.getKeyCode())) {
ke.consume();
}
}
} break;
case KeyEvent.KEY_RELEASED: {
// this won't autorepeat, which is better for toggle actions
KeyEvent ke = (KeyEvent)event;
if (ke.getKeyCode() == KeyEvent.VK_SPACE && ke.isControlDown()) {
setCompositionEnabled(!enabled);
}
} break;
default:
break;
}
}
public void activate() {
active = true;
synchronized (statusWindow) {
statusWindowOwner = this;
updateStatusWindow();
if (!statusWindow.isVisible()) {
statusWindow.setVisible(true);
}
setStatusWindowForeground(Color.black);
}
}
public void deactivate(boolean isTemporary) {
setStatusWindowForeground(Color.lightGray);
active = false;
}
public void hideWindows() {
synchronized (statusWindow) {
if (statusWindowOwner == this) {
statusWindow.setVisible(false);
}
}
}
public void removeNotify() {
}
public void endComposition() {
// in jdk.5, this allows scrolling, but it also allows clicking within the
// uncommitted text and then dragging outside of it.
System.out.println("about to end composition");
commitAll();
System.out.println("ended composition");
}
public void notifyClientWindowChange(Rectangle location) {
clientWindowLocation = location;
synchronized (statusWindow) {
if (!attachedStatusWindow || statusWindowOwner != this) {
return;
}
if (location != null) {
statusWindow.setLocation(location.x, location.y+location.height);
if (!statusWindow.isVisible()) {
if (active) {
setStatusWindowForeground(Color.black);
} else {
setStatusWindowForeground(Color.lightGray);
}
statusWindow.setVisible(true);
}
} else {
statusWindow.setVisible(false);
}
}
}
public void dispose() {
synchronized (statusWindow) {
inputMethodInstances.remove(this);
}
disposed = true;
}
public Object getControlObject() {
return null;
}
public void setCompositionEnabled(boolean enable) {
enabled = enable;
}
public boolean isCompositionEnabled() {
return enabled;
}
/** Wipe clean */
private void reset() {
buffer.delete(0, buffer.length());
index.contextStart = index.contextLimit = index.start = index.limit = 0;
}
// committed}context-composed|composed
// ^ ^ ^
// cc start ctxLim
private void traceBuffer(String msg, int cc, int off) {
System.out.println("cc: " + cc + " st: " + index.start + " cl: " + index.contextLimit + " len: " + buffer.length() + " off: " + off);
if (TRACE_BUFFER) System.out.println(Utility.escape(msg + ": '" + buffer.substring(0, cc) + '}' +
buffer.substring(cc, index.start) + '-' +
buffer.substring(index.start, index.contextLimit) + '|' +
buffer.substring(index.contextLimit) + '\''));
}
private void update(boolean flush) {
int len = buffer.length();
String text = buffer.toString();
AttributedString as = new AttributedString(text);
int cc, off;
if (flush) {
off = index.contextLimit - len; // will be negative
cc = index.start = index.limit = index.contextLimit = len;
} else {
cc = index.start > desiredContext ? index.start - desiredContext : 0;
off = index.contextLimit - cc;
}
if (index.start < len) {
as.addAttribute(TextAttribute.INPUT_METHOD_HIGHLIGHT,
InputMethodHighlight.SELECTED_RAW_TEXT_HIGHLIGHT,
index.start, len);
}
imc.dispatchInputMethodEvent(InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
as.getIterator(),
cc,
TextHitInfo.leading(off),
null);
traceBuffer("update", cc, off);
if (cc > 0) {
buffer.delete(0, cc);
index.start -= cc;
index.limit -= cc;
index.contextLimit -= cc;
}
}
private void updateCaret() {
imc.dispatchInputMethodEvent(InputMethodEvent.CARET_POSITION_CHANGED,
null,
0,
TextHitInfo.leading(index.contextLimit),
null);
traceBuffer("updateCaret", 0, index.contextLimit);
}
private void caretToStart() {
if (index.contextLimit > index.start) {
index.contextLimit = index.limit = index.start;
updateCaret();
}
}
private void caretToLimit() {
if (index.contextLimit < buffer.length()) {
index.contextLimit = index.limit = buffer.length();
updateCaret();
}
}
private boolean caretTowardsStart() {
int bufpos = index.contextLimit;
if (bufpos > index.start) {
--bufpos;
if (bufpos > index.start &&
UCharacter.isLowSurrogate(buffer.charAt(bufpos)) &&
UCharacter.isHighSurrogate(buffer.charAt(bufpos-1))) {
--bufpos;
}
index.contextLimit = index.limit = bufpos;
updateCaret();
return true;
}
return commitAll();
}
private boolean caretTowardsLimit() {
int bufpos = index.contextLimit;
if (bufpos < buffer.length()) {
++bufpos;
if (bufpos < buffer.length() &&
UCharacter.isLowSurrogate(buffer.charAt(bufpos)) &&
UCharacter.isHighSurrogate(buffer.charAt(bufpos-1))) {
++bufpos;
}
index.contextLimit = index.limit = bufpos;
updateCaret();
return true;
}
return commitAll();
}
private boolean canBackspace() {
return index.contextLimit > 0;
}
private boolean backspace() {
int bufpos = index.contextLimit;
if (bufpos > 0) {
int limit = bufpos;
--bufpos;
if (bufpos > 0 &&
UCharacter.isLowSurrogate(buffer.charAt(bufpos)) &&
UCharacter.isHighSurrogate(buffer.charAt(bufpos-1))) {
--bufpos;
}
if (bufpos < index.start) {
index.start = bufpos;
}
index.contextLimit = index.limit = bufpos;
doDelete(bufpos, limit);
return true;
}
return false;
}
private boolean canDelete() {
return index.contextLimit < buffer.length();
}
private boolean delete() {
int bufpos = index.contextLimit;
if (bufpos < buffer.length()) {
int limit = bufpos + 1;
if (limit < buffer.length() &&
UCharacter.isHighSurrogate(buffer.charAt(limit-1)) &&
UCharacter.isLowSurrogate(buffer.charAt(limit))) {
++limit;
}
doDelete(bufpos, limit);
return true;
}
return false;
}
private void doDelete(int start, int limit) {
buffer.delete(start, limit);
update(false);
}
private boolean commitAll() {
if (buffer.length() > 0) {
boolean atStart = index.start == index.contextLimit;
boolean didConvert = buffer.length() > index.start;
index.contextLimit = index.limit = buffer.length();
transliterator.finishTransliteration(replaceableText, index);
if (atStart) {
index.start = index.limit = index.contextLimit = 0;
}
update(true);
return didConvert;
}
return false;
}
private void clearAll() {
int len = buffer.length();
if (len > 0) {
if (len > index.start) {
buffer.delete(index.start, len);
}
update(true);
}
}
private boolean insert(char c) {
transliterator.transliterate(replaceableText, index, c);
update(false);
return true;
}
private boolean editing() {
return buffer.length() > 0;
}
/**
* The big problem is that from release to release swing changes how it
* handles some characters like tab and backspace. Sometimes it handles
* them as keyTyped events, and sometimes it handles them as keyPressed
* events. If you want to allow the event to go through so swing handles
* it, you have to allow one or the other to go through. If you don't want
* the event to go through so you can handle it, you have to stop the
* event both places.
* @return whether the character was handled
*/
private boolean handleTyped(char ch) {
if (enabled) {
switch (ch) {
case '\b': if (editing()) return backspace(); break;
case '\t': if (editing()) { return commitAll(); } break;
case '\u001b': if (editing()) { clearAll(); return true; } break;
case '\u007f': if (editing()) return delete(); break;
default: return insert(ch);
}
}
return false;
}
/**
* Handle keyPressed events.
*/
private boolean handlePressed(int code) {
if (enabled && editing()) {
switch (code) {
case KeyEvent.VK_PAGE_UP:
case KeyEvent.VK_UP:
case KeyEvent.VK_KP_UP:
case KeyEvent.VK_HOME:
caretToStart(); return true;
case KeyEvent.VK_PAGE_DOWN:
case KeyEvent.VK_DOWN:
case KeyEvent.VK_KP_DOWN:
case KeyEvent.VK_END:
caretToLimit(); return true;
case KeyEvent.VK_LEFT:
case KeyEvent.VK_KP_LEFT:
return caretTowardsStart();
case KeyEvent.VK_RIGHT:
case KeyEvent.VK_KP_RIGHT:
return caretTowardsLimit();
case KeyEvent.VK_BACK_SPACE:
return canBackspace(); // unfortunately, in 1.5 swing handles this in keyPressed instead of keyTyped
case KeyEvent.VK_DELETE:
return canDelete(); // this too?
case KeyEvent.VK_TAB:
case KeyEvent.VK_ENTER:
return commitAll(); // so we'll never handle VK_TAB in keyTyped
case KeyEvent.VK_SHIFT:
case KeyEvent.VK_CONTROL:
case KeyEvent.VK_ALT:
return false; // ignore these unless a key typed event gets generated
default:
// by default, let editor handle it, and we'll assume that it will tell us
// to endComposition if it does anything funky with, e.g., function keys.
return false;
}
}
return false;
}
}
class NameRenderer extends JLabel implements ListCellRenderer {
public Component getListCellRendererComponent(
JList list,
Object value,
int index,
boolean isSelected,
boolean cellHasFocus) {
String s = ((JLabel)value).getText();
setText(s);
if (isSelected) {
setBackground(list.getSelectionBackground());
setForeground(list.getSelectionForeground());
} else {
setBackground(list.getBackground());
setForeground(list.getForeground());
}
setEnabled(list.isEnabled());
setFont(list.getFont());
setOpaque(true);
return this;
}
}
class LabelComparator implements Comparator {
public int compare(Object obj1, Object obj2) {
Collator collator = Collator.getInstance();
return collator.compare(((JLabel)obj1).getText(), ((JLabel)obj2).getText());
}
public boolean equals(Object obj1) {
return this.equals(obj1);
}
}

View File

@ -1,10 +1,11 @@
/* /*
* =========================================================================== *******************************************************************************
* IBM ICU Transliterator IME - TransliteratorInputMethodDescriptor.java version 1.0 * Copyright (C) 2004, International Business Machines Corporation and *
* (C) Copyright IBM Corp. 2004. All Rights Reserved * others. All Rights Reserved. *
* =========================================================================== *******************************************************************************
*/ */
package com.ibm.icu.dev.tool.ime;
package com.ibm.icu.dev.tool.translitime;
import java.awt.Image; import java.awt.Image;
import java.awt.im.spi.InputMethod; import java.awt.im.spi.InputMethod;
@ -67,7 +68,7 @@ public class TransliteratorInputMethodDescriptor implements InputMethodDescripto
String name = null; String name = null;
try { try {
rb = ResourceBundle.getBundle("com.ibm.icu.dev.tool.ime.Transliterator", displayLanguage); rb = ResourceBundle.getBundle("com.ibm.icu.dev.tool.translitime.Transliterator", displayLanguage);
name = rb.getString("name"); name = rb.getString("name");
} }
catch (MissingResourceException m) { catch (MissingResourceException m) {