Skip to content

Commit fb3b691

Browse files
committed
updates for input method support (bug processing#854)
1 parent b4f700d commit fb3b691

5 files changed

Lines changed: 792 additions & 107 deletions

File tree

app/src/processing/app/syntax/InputMethodSupport.java

Lines changed: 139 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,51 @@
1-
package processing.app.syntax;
1+
package processing.app.syntax.im;
22

3-
import java.awt.Font;
4-
import java.awt.FontMetrics;
5-
import java.awt.Graphics;
6-
import java.awt.Graphics2D;
7-
import java.awt.Point;
83
import java.awt.Rectangle;
94
import java.awt.event.InputMethodEvent;
105
import java.awt.event.InputMethodListener;
11-
import java.awt.font.FontRenderContext;
12-
import java.awt.font.TextAttribute;
136
import java.awt.font.TextHitInfo;
14-
import java.awt.font.TextLayout;
157
import java.awt.im.InputMethodRequests;
168
import java.text.AttributedCharacterIterator;
17-
import java.text.AttributedString;
18-
19-
import javax.swing.text.BadLocationException;
209

10+
import processing.app.syntax.JEditTextArea;
11+
12+
/**
13+
* Support in-line Japanese input for PDE. (Maybe Chinese, Korean and more)
14+
* This class is implemented by Java Input Method Framework and handles
15+
* If you would like to know more about Java Input Method Framework,
16+
* Please see http://java.sun.com/j2se/1.5.0/docs/guide/imf/
17+
*
18+
* This class is implemented to fix Bug 854.
19+
*
20+
* @author Takashi Maekawa (takachin@generative.info)
21+
*/
2122
public class InputMethodSupport implements InputMethodRequests,
2223
InputMethodListener {
2324

24-
private JEditTextArea textArea;
25-
26-
private TextLayout composedTextLayout = null;
27-
2825
private int committed_count = 0;
29-
30-
private static final int COMPOSING_UNDERBAR_HEIGHT = 5;
31-
32-
private boolean isComposing;
26+
private CompositionTextManager textManager;
3327

3428
public InputMethodSupport(JEditTextArea textArea) {
35-
this.textArea = textArea;
29+
textManager = new CompositionTextManager(textArea);
3630
textArea.enableInputMethods(true);
3731
textArea.addInputMethodListener(this);
38-
isComposing = false;
39-
}
40-
41-
public boolean getIsComposing() {
42-
return isComposing;
43-
}
44-
45-
private Point getCaretLocation() {
46-
Point loc = new Point();
47-
TextAreaPainter painter = textArea.getPainter();
48-
FontMetrics fm = painter.getFontMetrics();
49-
int offsetY = fm.getHeight() - 5;
50-
int lineIndex = textArea.getCaretLine();
51-
loc.y = lineIndex * fm.getHeight() + offsetY;
52-
int offsetX = textArea.getCaretPosition()
53-
- textArea.getLineStartOffset(lineIndex);
54-
loc.x = textArea.offsetToX(lineIndex, offsetX);
55-
return loc;
5632
}
5733

5834
public Rectangle getTextLocation(TextHitInfo offset) {
59-
Point caret = getCaretLocation();
60-
return getCaretRectangle(caret.x, caret.y);
61-
}
62-
63-
private Rectangle getCaretRectangle(int x, int y) {
64-
TextAreaPainter painter = textArea.getPainter();
65-
Point origin = painter.getLocationOnScreen();
66-
int height = painter.getFontMetrics().getHeight();
67-
return new Rectangle(origin.x + x, origin.y + y, 0, height);
35+
return textManager.getTextLocation();
6836
}
6937

7038
public TextHitInfo getLocationOffset(int x, int y) {
7139
return null;
7240
}
7341

7442
public int getInsertPositionOffset() {
75-
if (isComposing) {
76-
isComposing = false;
77-
}
78-
return textArea.getCaretPosition();
43+
return textManager.getInsertPositionOffset();
7944
}
8045

8146
public AttributedCharacterIterator getCommittedText(int beginIndex,
8247
int endIndex, AttributedCharacterIterator.Attribute[] attributes) {
83-
return (new AttributedString(textArea.getText(beginIndex, endIndex
84-
- beginIndex))).getIterator();
48+
return textManager.getCommittedText(beginIndex, endIndex);
8549
}
8650

8751
public int getCommittedTextLength() {
@@ -98,73 +62,144 @@ public AttributedCharacterIterator getSelectedText(
9862
return null;
9963
}
10064

65+
/**
66+
* Handles events from InputMethod.
67+
* This method judges whether beginning of input or
68+
* progress of input or end and call related method.
69+
*
70+
* @param event event from Input Method.
71+
*/
10172
public void inputMethodTextChanged(InputMethodEvent event) {
102-
composedTextLayout = null;
10373
AttributedCharacterIterator text = event.getText();
10474
committed_count = event.getCommittedCharacterCount();
105-
if (committed_count == 0) {
106-
if (text.getEndIndex() == 0) {
107-
caretPositionChanged(event);
108-
return;
109-
}
110-
if (text.getEndIndex() < text.getBeginIndex()) {
111-
caretPositionChanged(event);
112-
return;
113-
}
114-
isComposing = true;
115-
drawComposingText(text, committed_count);
75+
if(isBeginInputProcess(text, textManager)){
76+
textManager.beginCompositionText(text, committed_count);
77+
caretPositionChanged(event);
78+
return;
79+
}
80+
if (isInputProcess(text)){
81+
textManager.processCompositionText(text, committed_count);
11682
caretPositionChanged(event);
11783
return;
11884
}
119-
commitText(text, committed_count);
120-
isComposing = false;
85+
textManager.endCompositionText(text, committed_count);
12186
caretPositionChanged(event);
12287
}
12388

124-
private void drawComposingText(AttributedCharacterIterator text, int count) {
125-
assert ((count == 0 && text.getEndIndex() > 0));
126-
Point textLocation = getCaretLocation();
127-
invalidateComposingLine(textArea.getPainter(), textLocation.x,
128-
textLocation.y);
129-
composedTextLayout = getTextLayout(text, count);
130-
composedTextLayout.draw((Graphics2D) (textArea.getPainter().getGraphics()),
131-
textLocation.x, textLocation.y);
89+
private boolean isBeginInputProcess(AttributedCharacterIterator text, CompositionTextManager textManager){
90+
if(text == null)
91+
return false;
92+
return (isInputProcess(text) && !textManager.getIsInputProcess());
93+
}
94+
95+
private boolean isInputProcess(AttributedCharacterIterator text){
96+
if(text == null)
97+
return false;
98+
return (text.getEndIndex() - (text.getBeginIndex() + committed_count) > 0);
99+
}
100+
101+
public void caretPositionChanged(InputMethodEvent event) {
102+
event.consume();
132103
}
104+
}
105+
package processing.app.syntax.im;
106+
107+
import java.awt.Rectangle;
108+
import java.awt.event.InputMethodEvent;
109+
import java.awt.event.InputMethodListener;
110+
import java.awt.font.TextHitInfo;
111+
import java.awt.im.InputMethodRequests;
112+
import java.text.AttributedCharacterIterator;
133113

134-
private void invalidateComposingLine(TextAreaPainter painter, int x, int y) {
135-
Graphics gfx = painter.getGraphics();
136-
gfx.setColor(painter.lineHighlightColor);
137-
gfx.fillRect(x, y
138-
- (painter.getFontMetrics().getHeight() - COMPOSING_UNDERBAR_HEIGHT), //
139-
painter.getWidth(), painter.getFontMetrics().getHeight());
114+
import processing.app.syntax.JEditTextArea;
115+
116+
/**
117+
* Support in-line Japanese input for PDE. (Maybe Chinese, Korean and more)
118+
* This class is implemented by Java Input Method Framework and handles
119+
* If you would like to know more about Java Input Method Framework,
120+
* Please see http://java.sun.com/j2se/1.5.0/docs/guide/imf/
121+
*
122+
* This class is implemented to fix Bug 854.
123+
*
124+
* @author Takashi Maekawa (takachin@generative.info)
125+
*/
126+
public class InputMethodSupport implements InputMethodRequests,
127+
InputMethodListener {
128+
129+
private int committed_count = 0;
130+
private CompositionTextManager textManager;
131+
132+
public InputMethodSupport(JEditTextArea textArea) {
133+
textManager = new CompositionTextManager(textArea);
134+
textArea.enableInputMethods(true);
135+
textArea.addInputMethodListener(this);
140136
}
141137

142-
private void commitText(AttributedCharacterIterator text, int count) {
143-
char c;
144-
StringBuffer committing = new StringBuffer(count);
145-
for (c = text.first(); c != AttributedCharacterIterator.DONE && count > 0; c = text
146-
.next(), --count) {
147-
committing.append(c);
138+
public Rectangle getTextLocation(TextHitInfo offset) {
139+
return textManager.getTextLocation();
140+
}
141+
142+
public TextHitInfo getLocationOffset(int x, int y) {
143+
return null;
144+
}
145+
146+
public int getInsertPositionOffset() {
147+
return textManager.getInsertPositionOffset();
148+
}
149+
150+
public AttributedCharacterIterator getCommittedText(int beginIndex,
151+
int endIndex, AttributedCharacterIterator.Attribute[] attributes) {
152+
return textManager.getCommittedText(beginIndex, endIndex);
153+
}
154+
155+
public int getCommittedTextLength() {
156+
return committed_count;
157+
}
158+
159+
public AttributedCharacterIterator cancelLatestCommittedText(
160+
AttributedCharacterIterator.Attribute[] attributes) {
161+
return null;
162+
}
163+
164+
public AttributedCharacterIterator getSelectedText(
165+
AttributedCharacterIterator.Attribute[] attributes) {
166+
return null;
167+
}
168+
169+
/**
170+
* Handles events from InputMethod.
171+
* This method judges whether beginning of input or
172+
* progress of input or end and call related method.
173+
*
174+
* @param event event from Input Method.
175+
*/
176+
public void inputMethodTextChanged(InputMethodEvent event) {
177+
AttributedCharacterIterator text = event.getText();
178+
committed_count = event.getCommittedCharacterCount();
179+
if(isBeginInputProcess(text, textManager)){
180+
textManager.beginCompositionText(text, committed_count);
181+
caretPositionChanged(event);
182+
return;
148183
}
149-
int caret = textArea.getCaretPosition();
150-
String committing_text = committing.toString();
151-
try {
152-
textArea.getDocument().insertString(caret, committing_text, null);
153-
} catch (BadLocationException e) {
154-
e.printStackTrace();
184+
if (isInputProcess(text)){
185+
textManager.processCompositionText(text, committed_count);
186+
caretPositionChanged(event);
187+
return;
155188
}
189+
textManager.endCompositionText(text, committed_count);
190+
caretPositionChanged(event);
191+
}
192+
193+
private boolean isBeginInputProcess(AttributedCharacterIterator text, CompositionTextManager textManager){
194+
if(text == null)
195+
return false;
196+
return (isInputProcess(text) && !textManager.getIsInputProcess());
156197
}
157198

158-
private TextLayout getTextLayout(AttributedCharacterIterator text,
159-
int committed_count) {
160-
AttributedString composed = new AttributedString(text, committed_count,
161-
text.getEndIndex());
162-
Font font = textArea.getPainter().getFont();
163-
FontRenderContext context = ((Graphics2D) (textArea.getPainter()
164-
.getGraphics())).getFontRenderContext();
165-
composed.addAttribute(TextAttribute.FONT, font);
166-
TextLayout layout = new TextLayout(composed.getIterator(), context);
167-
return layout;
199+
private boolean isInputProcess(AttributedCharacterIterator text){
200+
if(text == null)
201+
return false;
202+
return (text.getEndIndex() - (text.getBeginIndex() + committed_count) > 0);
168203
}
169204

170205
public void caretPositionChanged(InputMethodEvent event) {

app/src/processing/app/syntax/JEditTextArea.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import java.util.Vector;
2525
import java.awt.im.InputMethodRequests;
2626

27+
import processing.app.syntax.im.InputMethodSupport;
28+
2729
/**
2830
* jEdit's text area component. It is more suited for editing program
2931
* source code than JEditorPane, because it drops the unnecessary features
@@ -227,7 +229,7 @@ public void setCaretVisible(boolean caretVisible) {
227229
* Blinks the caret.
228230
*/
229231
public final void blinkCaret() {
230-
if (caretBlinks && !((InputMethodSupport)getInputMethodRequests()).getIsComposing()) {
232+
if (caretBlinks) {
231233
blink = !blink;
232234
painter.invalidateSelectedLines();
233235
} else {

app/src/processing/app/syntax/TextAreaPainter.java

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
package processing.app.syntax;
1313

1414
import processing.app.*;
15+
import processing.app.syntax.im.CompositionTextPainter;
1516

1617
import javax.swing.ToolTipManager;
1718
import javax.swing.text.*;
@@ -33,6 +34,9 @@ public class TextAreaPainter extends JComponent
3334
/** Current setting for editor.antialias preference */
3435
boolean antialias;
3536

37+
/** A specific painter composed by the InputMethod.*/
38+
protected CompositionTextPainter compositionTextPainter;
39+
3640
/**
3741
* Creates a new repaint manager. This should be not be called
3842
* directly.
@@ -72,6 +76,16 @@ public TextAreaPainter(JEditTextArea textArea, TextAreaDefaults defaults)
7276
eolMarkerColor = defaults.eolMarkerColor;
7377
eolMarkers = defaults.eolMarkers;
7478
}
79+
80+
/**
81+
* Get CompositionTextPainter. if CompositionTextPainter is not created, create it.
82+
*/
83+
public CompositionTextPainter getCompositionTextpainter(){
84+
if(compositionTextPainter == null){
85+
compositionTextPainter = new CompositionTextPainter(textArea);
86+
}
87+
return compositionTextPainter;
88+
}
7589

7690
/**
7791
* Returns if this component can be traversed by pressing the
@@ -602,7 +616,12 @@ protected void paintPlainLine(Graphics gfx, int line, Font defaultFont,
602616

603617
y += fm.getHeight();
604618
x = Utilities.drawTabbedText(currentLine,x,y,gfx,this,0);
605-
619+
/*
620+
* Draw characters via input method.
621+
*/
622+
if (compositionTextPainter != null && compositionTextPainter.hasComposedTextLayout()) {
623+
compositionTextPainter.draw(gfx, lineHighlightColor);
624+
}
606625
if (eolMarkers) {
607626
gfx.setColor(eolMarkerColor);
608627
gfx.drawString(".",x,y);
@@ -625,7 +644,12 @@ protected void paintSyntaxLine(Graphics gfx, TokenMarker tokenMarker,
625644
x = SyntaxUtilities.paintSyntaxLine(currentLine,
626645
currentLineTokens,
627646
styles, this, gfx, x, y);
628-
647+
/*
648+
* Draw characters via input method.
649+
*/
650+
if (compositionTextPainter != null && compositionTextPainter.hasComposedTextLayout()) {
651+
compositionTextPainter.draw(gfx, lineHighlightColor);
652+
}
629653
if (eolMarkers) {
630654
gfx.setColor(eolMarkerColor);
631655
gfx.drawString(".",x,y);

0 commit comments

Comments
 (0)