blob: ba9ee155f7fbaa8ef8daf3644c11faf4e610cf97 [file] [log] [blame]
package processing.app.syntax.im;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.font.FontRenderContext;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;
import javax.swing.text.BadLocationException;
import processing.app.syntax.JEditTextArea;
import processing.app.syntax.TextAreaPainter;
/**
* This class Manage texts from input method
* by begin-process-end steps.
*
* First, if a user start inputing via input method,
* beginCompositionText is called from InputMethodSupport.
* Second, the user continues from input method, processCompositionText is called
* and reflect user inputs to text area.
* Finally the user try to commit text, endCompositionText is called.
*
* @author Takashi Maekawa (takachin@generative.info)
*/
public class CompositionTextManager {
private JEditTextArea textArea;
private String prevComposeString;
private int prevCommittedCount;
private boolean isInputProcess;
private int initialCaretPosition;
public static final int COMPOSING_UNDERBAR_HEIGHT = 5;
/**
* Create text manager class with a textarea.
* @param textArea texarea component for PDE.
*/
public CompositionTextManager(JEditTextArea textArea) {
this.textArea = textArea;
prevComposeString = "";
isInputProcess = false;
prevCommittedCount = 0;
}
/**
* Get this text manager is whether in input process or not.
*/
public boolean getIsInputProcess() {
return isInputProcess;
}
/**
* Insert full width space
*/
public void insertFullWidthSpace() {
initialCaretPosition = textArea.getCaretPosition();
int layoutCaretPosition = initialCaretPosition;
try {
textArea.getDocument().insertString(layoutCaretPosition, "\u3000", null);
} catch (BadLocationException e) {
e.printStackTrace();
}
}
/**
* Called when a user begins input from input method.
* This method initializes text manager.
*
* @param text Text from InputMethodEvent.
* @param commited_count Numbers of committed characters in text.
*/
public void beginCompositionText(AttributedCharacterIterator text, int committed_count) {
isInputProcess = true;
prevComposeString = "";
initialCaretPosition = textArea.getCaretPosition();
processCompositionText(text, committed_count);
}
/**
* Called when a user processing input characters and
* select candidates from input method.
*
* @param text Text from InputMethodEvent.
* @param commited_count Numbers of committed characters in text.
*/
public void processCompositionText(AttributedCharacterIterator text, int committed_count) {
int layoutCaretPosition = initialCaretPosition + committed_count;
CompositionTextPainter compositionPainter = textArea.getPainter().getCompositionTextpainter();
compositionPainter.setComposedTextLayout(getTextLayout(text, committed_count), layoutCaretPosition);
int textLength = text.getEndIndex() - text.getBeginIndex() - committed_count;
StringBuffer unCommitedStringBuf = new StringBuffer(textLength);
char c;
for (c = text.setIndex(committed_count); c != AttributedCharacterIterator.DONE
&& textLength > 0; c = text.next(), --textLength) {
unCommitedStringBuf.append(c);
}
String unCommittedString = unCommitedStringBuf.toString();
try {
if(canRemovePreviousInput(committed_count)){
textArea.getDocument().remove(layoutCaretPosition, prevComposeString.length());
}
textArea.getDocument().insertString(layoutCaretPosition, unCommittedString, null);
if(committed_count > 0){
initialCaretPosition = initialCaretPosition + committed_count;
}
prevComposeString = unCommittedString;
prevCommittedCount = committed_count;
} catch (BadLocationException e) {
e.printStackTrace();
}
}
private boolean canRemovePreviousInput(int committed_count){
return (prevCommittedCount == committed_count || prevCommittedCount > committed_count);
}
/**
* Called when a user fixed text from input method or delete all
* composition text. This method resets CompositionTextPainter.
*
* @param text Text from InputMethodEvent.
* @param commited_count Numbers of committed characters in text.
*/
public void endCompositionText(AttributedCharacterIterator text, int committed_count) {
/*
* If there are no committed characters, remove it all from textarea.
* This case will happen if a user delete all composing characters by backspace or delete key.
* If it does, these previous characters are needed to be deleted.
*/
if(committed_count == 0){
removeNotCommittedText(text);
}
CompositionTextPainter compositionPainter = textArea.getPainter().getCompositionTextpainter();
compositionPainter.invalidateComposedTextLayout(initialCaretPosition + committed_count);
prevComposeString = "";
isInputProcess = false;
}
private void removeNotCommittedText(AttributedCharacterIterator text){
if (prevComposeString.length() == 0) {
return;
}
try {
textArea.getDocument().remove(initialCaretPosition, prevComposeString.length());
} catch (BadLocationException e) {
e.printStackTrace();
}
}
private TextLayout getTextLayout(AttributedCharacterIterator text, int committed_count) {
AttributedString composed = new AttributedString(text, committed_count, text.getEndIndex());
Font font = textArea.getPainter().getFont();
FontRenderContext context = ((Graphics2D) (textArea.getPainter().getGraphics())).getFontRenderContext();
composed.addAttribute(TextAttribute.FONT, font);
TextLayout layout = new TextLayout(composed.getIterator(), context);
return layout;
}
private Point getCaretLocation() {
Point loc = new Point();
TextAreaPainter painter = textArea.getPainter();
FontMetrics fm = painter.getFontMetrics();
int offsetY = fm.getHeight() - COMPOSING_UNDERBAR_HEIGHT;
int lineIndex = textArea.getCaretLine();
loc.y = lineIndex * fm.getHeight() + offsetY;
int offsetX = textArea.getCaretPosition()
- textArea.getLineStartOffset(lineIndex);
loc.x = textArea.offsetToX(lineIndex, offsetX);
return loc;
}
public Rectangle getTextLocation() {
Point caret = getCaretLocation();
return getCaretRectangle(caret.x, caret.y);
}
private Rectangle getCaretRectangle(int x, int y) {
TextAreaPainter painter = textArea.getPainter();
Point origin = painter.getLocationOnScreen();
int height = painter.getFontMetrics().getHeight();
return new Rectangle(origin.x + x, origin.y + y, 0, height);
}
public AttributedCharacterIterator getCommittedText(int beginIndex, int endIndex) {
int length = endIndex - beginIndex;
String textAreaString = textArea.getText(beginIndex, length);
return new AttributedString(textAreaString).getIterator();
}
public int getInsertPositionOffset() {
return textArea.getCaretPosition() * -1;
}
}