| 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; |
| } |
| } |