Skip to content

Commit bda9b0a

Browse files
committed
fix for input method support (bug #854)
1 parent 09f1c34 commit bda9b0a

4 files changed

Lines changed: 10 additions & 423 deletions

File tree

app/src/processing/app/syntax/im/CompositionTextManager.java

Lines changed: 0 additions & 187 deletions
Original file line numberDiff line numberDiff line change
@@ -185,190 +185,3 @@ public int getInsertPositionOffset() {
185185
return textArea.getCaretPosition() * -1;
186186
}
187187
}
188-
package processing.app.syntax.im;
189-
190-
import java.awt.Font;
191-
import java.awt.FontMetrics;
192-
import java.awt.Graphics2D;
193-
import java.awt.Point;
194-
import java.awt.Rectangle;
195-
import java.awt.font.FontRenderContext;
196-
import java.awt.font.TextAttribute;
197-
import java.awt.font.TextLayout;
198-
import java.text.AttributedCharacterIterator;
199-
import java.text.AttributedString;
200-
201-
import javax.swing.text.BadLocationException;
202-
203-
import processing.app.syntax.JEditTextArea;
204-
import processing.app.syntax.TextAreaPainter;
205-
206-
/**
207-
* This class Manage texts from input method
208-
* by begin-process-end steps.
209-
*
210-
* First, if a user start inputing via input method,
211-
* beginCompositionText is called from InputMethodSupport.
212-
* Second, the user continues from input method, processCompositionText is called
213-
* and reflect user inputs to text area.
214-
* Finally the user try to commit text, endCompositionText is called.
215-
*
216-
* @author Takashi Maekawa (takachin@generative.info)
217-
*/
218-
219-
public class CompositionTextManager {
220-
private JEditTextArea textArea;
221-
private String prevComposeString;
222-
private int prevCommittedCount;
223-
private boolean isInputProcess;
224-
private int initialCaretPosition;
225-
public static final int COMPOSING_UNDERBAR_HEIGHT = 5;
226-
227-
/**
228-
* Create text manager class with a textarea.
229-
* @param textArea texarea component for PDE.
230-
*/
231-
public CompositionTextManager(JEditTextArea textArea) {
232-
this.textArea = textArea;
233-
prevComposeString = "";
234-
isInputProcess = false;
235-
prevCommittedCount = 0;
236-
}
237-
238-
/**
239-
* Get this text manager is whether in input process or not.
240-
*/
241-
public boolean getIsInputProcess() {
242-
return isInputProcess;
243-
}
244-
245-
/**
246-
* Called when a user begins input from input method.
247-
* This method initializes text manager.
248-
*
249-
* @param text Text from InputMethodEvent.
250-
* @param commited_count Numbers of committed characters in text.
251-
*/
252-
public void beginCompositionText(AttributedCharacterIterator text, int committed_count) {
253-
isInputProcess = true;
254-
prevComposeString = "";
255-
initialCaretPosition = textArea.getCaretPosition();
256-
processCompositionText(text, committed_count);
257-
}
258-
259-
/**
260-
* Called when a user processing input characters and
261-
* select candidates from input method.
262-
*
263-
* @param text Text from InputMethodEvent.
264-
* @param commited_count Numbers of committed characters in text.
265-
*/
266-
public void processCompositionText(AttributedCharacterIterator text, int committed_count) {
267-
int layoutCaretPosition = initialCaretPosition + committed_count;
268-
CompositionTextPainter compositionPainter = textArea.getPainter().getCompositionTextpainter();
269-
compositionPainter.setComposedTextLayout(getTextLayout(text, committed_count), layoutCaretPosition);
270-
int textLength = text.getEndIndex() - text.getBeginIndex() - committed_count;
271-
StringBuffer unCommitedStringBuf = new StringBuffer(textLength);
272-
char c;
273-
for (c = text.setIndex(committed_count); c != AttributedCharacterIterator.DONE
274-
&& textLength > 0; c = text.next(), --textLength) {
275-
unCommitedStringBuf.append(c);
276-
}
277-
String unCommittedString = unCommitedStringBuf.toString();
278-
try {
279-
if(canRemovePreviousInput(committed_count)){
280-
textArea.getDocument().remove(layoutCaretPosition, prevComposeString.length());
281-
}
282-
textArea.getDocument().insertString(layoutCaretPosition, unCommittedString, null);
283-
if(committed_count > 0){
284-
initialCaretPosition = initialCaretPosition + committed_count;
285-
}
286-
prevComposeString = unCommittedString;
287-
prevCommittedCount = committed_count;
288-
} catch (BadLocationException e) {
289-
e.printStackTrace();
290-
}
291-
}
292-
293-
private boolean canRemovePreviousInput(int committed_count){
294-
return (prevCommittedCount == committed_count || prevCommittedCount > committed_count);
295-
}
296-
297-
/**
298-
* Called when a user fixed text from input method or delete all
299-
* composition text. This method resets CompositionTextPainter.
300-
*
301-
* @param text Text from InputMethodEvent.
302-
* @param commited_count Numbers of committed characters in text.
303-
*/
304-
public void endCompositionText(AttributedCharacterIterator text, int committed_count) {
305-
isInputProcess = false;
306-
/*
307-
* If there are no committed characters, remove it all from textarea.
308-
* This case will happen if a user delete all composing characters by backspace or delete key.
309-
* If it does, these previous characters are needed to be deleted.
310-
*/
311-
if(committed_count == 0){
312-
removeNotCommittedText(text);
313-
}
314-
CompositionTextPainter compositionPainter = textArea.getPainter().getCompositionTextpainter();
315-
compositionPainter.invalidateComposedTextLayout(initialCaretPosition + committed_count);
316-
prevComposeString = "";
317-
isInputProcess = false;
318-
}
319-
320-
private void removeNotCommittedText(AttributedCharacterIterator text){
321-
if (prevComposeString.length() == 0) {
322-
return;
323-
}
324-
try {
325-
textArea.getDocument().remove(initialCaretPosition, prevComposeString.length());
326-
} catch (BadLocationException e) {
327-
e.printStackTrace();
328-
}
329-
}
330-
331-
private TextLayout getTextLayout(AttributedCharacterIterator text, int committed_count) {
332-
AttributedString composed = new AttributedString(text, committed_count, text.getEndIndex());
333-
Font font = textArea.getPainter().getFont();
334-
FontRenderContext context = ((Graphics2D) (textArea.getPainter().getGraphics())).getFontRenderContext();
335-
composed.addAttribute(TextAttribute.FONT, font);
336-
TextLayout layout = new TextLayout(composed.getIterator(), context);
337-
return layout;
338-
}
339-
340-
private Point getCaretLocation() {
341-
Point loc = new Point();
342-
TextAreaPainter painter = textArea.getPainter();
343-
FontMetrics fm = painter.getFontMetrics();
344-
int offsetY = fm.getHeight() - COMPOSING_UNDERBAR_HEIGHT;
345-
int lineIndex = textArea.getCaretLine();
346-
loc.y = lineIndex * fm.getHeight() + offsetY;
347-
int offsetX = textArea.getCaretPosition()
348-
- textArea.getLineStartOffset(lineIndex);
349-
loc.x = textArea.offsetToX(lineIndex, offsetX);
350-
return loc;
351-
}
352-
353-
public Rectangle getTextLocation() {
354-
Point caret = getCaretLocation();
355-
return getCaretRectangle(caret.x, caret.y);
356-
}
357-
358-
private Rectangle getCaretRectangle(int x, int y) {
359-
TextAreaPainter painter = textArea.getPainter();
360-
Point origin = painter.getLocationOnScreen();
361-
int height = painter.getFontMetrics().getHeight();
362-
return new Rectangle(origin.x + x, origin.y + y, 0, height);
363-
}
364-
365-
public AttributedCharacterIterator getCommittedText(int beginIndex, int endIndex) {
366-
int length = endIndex - beginIndex;
367-
String textAreaString = textArea.getText(beginIndex, length);
368-
return new AttributedString(textAreaString).getIterator();
369-
}
370-
371-
public int getInsertPositionOffset() {
372-
return textArea.getCaretPosition() * -1;
373-
}
374-
}

app/src/processing/app/syntax/im/CompositionTextPainter.java

Lines changed: 5 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -11,139 +11,13 @@
1111
import processing.app.syntax.TextAreaPainter;
1212

1313
/**
14-
* Paint texts from input method.
15-
* Text via input method are transmitted by AttributedCaharacterIterator.
16-
* But current PDE's TextAreaPainter can't treat AttributedCaharacterIterator directly.
17-
* So this class helps to treat it and paint text.
14+
* Paint texts from input method. Text via input method are transmitted by
15+
* AttributedCaharacterIterator. This class helps the PDE's TextAreaPainter
16+
* to handle AttributedCaharacterIterator.
1817
*
1918
* For practical purposes, paint to textarea is done by TextLayout class.
20-
* Because TextLayout class is easy to draw composing texts.
21-
* (For example, draw underline composing texts, focus when select from candidates text.)
22-
*
23-
* @author Takashi Maekawa (takachin@generative.info)
24-
*/
25-
public class CompositionTextPainter {
26-
private TextLayout composedTextLayout;
27-
private int composedBeginCaretPosition = 0;
28-
private JEditTextArea textArea;
29-
30-
/**
31-
* Constructor for painter.
32-
* @param textarea textarea used by PDE.
33-
*/
34-
public CompositionTextPainter(JEditTextArea textArea) {
35-
this.textArea = textArea;
36-
composedTextLayout = null;
37-
}
38-
39-
/**
40-
* Check the painter has TextLayout.
41-
* If a user input via InputMethod, this result will return true.
42-
* @param textarea textarea used by PDE.
43-
*/
44-
public boolean hasComposedTextLayout() {
45-
return (composedTextLayout != null);
46-
}
47-
48-
/**
49-
* Set TextLayout to the painter.
50-
* TextLayout will be created and set by CompositionTextManager.
51-
*
52-
* @see CompositionTextManager
53-
* @param textarea textarea used by PDE.
54-
*/
55-
public void setComposedTextLayout(TextLayout composedTextLayout, int composedStartCaretPosition) {
56-
this.composedTextLayout = composedTextLayout;
57-
this.composedBeginCaretPosition = composedStartCaretPosition;
58-
}
59-
60-
/**
61-
* Invalidate this TextLayout to set null.
62-
* If a user end input via InputMethod, this method will called from CompositionTextManager.endCompositionText
63-
*/
64-
public void invalidateComposedTextLayout(int composedEndCaretPosition) {
65-
this.composedTextLayout = null;
66-
this.composedBeginCaretPosition = composedEndCaretPosition;
67-
//this.composedBeginCaretPosition = textArea.getCaretPosition();
68-
}
69-
70-
/**
71-
* Draw text via input method with composed text information.
72-
* This method can draw texts with some underlines to illustrate converting characters.
73-
*
74-
* This method is workaround for TextAreaPainter.
75-
* Because, TextAreaPainter can't treat AttributedCharacterIterator directly.
76-
* AttributedCharacterIterator has very important information when composing text.
77-
* It has a map where are converted characters and committed characters.
78-
* Ideally, changing TextAreaPainter method can treat AttributedCharacterIterator is better. But it's very tough!!
79-
* So I choose to write some code as a workaround.
80-
*
81-
* This draw method is proceeded with the following steps.
82-
* 1. Original TextAreaPainter draws characters.
83-
* 2. This refillComposedArea method erase previous paint characters by textarea's background color.
84-
* The refill area is only square that width and height defined by characters with input method.
85-
* 3. CompositionTextPainter.draw method paints composed text. It was actually drawn by TextLayout.
86-
*
87-
* @param gfx set TextAreaPainter's Graphics object.
88-
* @param fillBackGroundColor set textarea's background.
89-
*/
90-
public void draw(Graphics gfx, Color fillBackGroundColor) {
91-
assert(composedTextLayout != null);
92-
Point composedLoc = getCaretLocation();
93-
refillComposedArea(fillBackGroundColor, composedLoc.x, composedLoc.y);
94-
composedTextLayout.draw((Graphics2D) gfx, composedLoc.x, composedLoc.y);
95-
}
96-
97-
/**
98-
* Fill color to erase characters drawn by original TextAreaPainter.
99-
*
100-
* @param fillColor fill color to erase characters drawn by original TextAreaPainter method.
101-
* @param x x-coordinate where to fill.
102-
* @param y y-coordinate where to fill.
103-
*/
104-
private void refillComposedArea(Color fillColor, int x, int y) {
105-
Graphics gfx = textArea.getPainter().getGraphics();
106-
gfx.setColor(fillColor);
107-
FontMetrics fm = textArea.getPainter().getFontMetrics();
108-
int newY = y - (fm.getHeight() - CompositionTextManager.COMPOSING_UNDERBAR_HEIGHT);
109-
int paintHeight = fm.getHeight();
110-
int paintWidth = (int) composedTextLayout.getBounds().getWidth();
111-
gfx.fillRect(x, newY, paintWidth, paintHeight);
112-
}
113-
114-
private Point getCaretLocation() {
115-
Point loc = new Point();
116-
TextAreaPainter painter = textArea.getPainter();
117-
FontMetrics fm = painter.getFontMetrics();
118-
int offsetY = fm.getHeight() - CompositionTextManager.COMPOSING_UNDERBAR_HEIGHT;
119-
int lineIndex = textArea.getCaretLine();
120-
loc.y = lineIndex * fm.getHeight() + offsetY;
121-
int offsetX = composedBeginCaretPosition - textArea.getLineStartOffset(lineIndex);
122-
loc.x = textArea.offsetToX(lineIndex, offsetX);
123-
return loc;
124-
}
125-
}
126-
package processing.app.syntax.im;
127-
128-
import java.awt.Color;
129-
import java.awt.FontMetrics;
130-
import java.awt.Graphics;
131-
import java.awt.Graphics2D;
132-
import java.awt.Point;
133-
import java.awt.font.TextLayout;
134-
135-
import processing.app.syntax.JEditTextArea;
136-
import processing.app.syntax.TextAreaPainter;
137-
138-
/**
139-
* Paint texts from input method.
140-
* Text via input method are transmitted by AttributedCaharacterIterator.
141-
* But current PDE's TextAreaPainter can't treat AttributedCaharacterIterator directly.
142-
* So this class helps to treat it and paint text.
143-
*
144-
* For practical purposes, paint to textarea is done by TextLayout class.
145-
* Because TextLayout class is easy to draw composing texts.
146-
* (For example, draw underline composing texts, focus when select from candidates text.)
19+
* Because TextLayout class is easy to draw composing texts. (For example,
20+
* draw underline composing texts, focus when select from candidates text.)
14721
*
14822
* @author Takashi Maekawa (takachin@generative.info)
14923
*/

0 commit comments

Comments
 (0)