PageRenderTime 48ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/jEdit/tags/jedit-4-5-pre1/org/gjt/sp/jedit/textarea/InputMethodSupport.java

#
Java | 290 lines | 208 code | 21 blank | 61 comment | 33 complexity | 975c49a9a3dbb18f710a1e4ee5e05f3c MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
  1. /*
  2. * InputMethodSupport.java - Input method support for JEditTextArea
  3. *
  4. * :tabSize=8:indentSize=8:noTabs=false:
  5. * :folding=explicit:collapseFolds=1:
  6. *
  7. * Copyright (C) 2006 Kazutoshi Satoda
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public License
  11. * as published by the Free Software Foundation; either version 2
  12. * of the License, or any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program; if not, write to the Free Software
  21. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  22. */
  23. package org.gjt.sp.jedit.textarea;
  24. // {{{ Imports
  25. import java.text.AttributedString;
  26. import java.text.AttributedCharacterIterator;
  27. import java.awt.Point;
  28. import java.awt.Rectangle;
  29. import java.awt.Graphics2D;
  30. import java.awt.FontMetrics;
  31. import java.awt.im.InputMethodRequests;
  32. import java.awt.event.InputMethodListener;
  33. import java.awt.event.InputMethodEvent;
  34. import java.awt.font.TextLayout;
  35. import java.awt.font.TextAttribute;
  36. import java.awt.font.TextHitInfo;
  37. // }}}
  38. /**
  39. * Input method support for JEditTextArea
  40. *
  41. * @author Kazutoshi Satoda
  42. * @since jEdit 4.3pre7
  43. */
  44. class InputMethodSupport
  45. extends TextAreaExtension
  46. implements InputMethodRequests, InputMethodListener
  47. {
  48. // The owner.
  49. private TextArea owner;
  50. // The composed text layout which was built from last InputMethodEvent.
  51. private TextLayout composedTextLayout = null;
  52. // The X offset to the caret in the composed text.
  53. private int composedCaretX = 0;
  54. // Last committed information to support cancelLatestCommittedText()
  55. private int lastCommittedAt = 0;
  56. private String lastCommittedText = null;
  57. public InputMethodSupport(TextArea owner)
  58. {
  59. this.owner = owner;
  60. owner.addInputMethodListener(this);
  61. owner.getPainter().addExtension(TextAreaPainter.HIGHEST_LAYER, this);
  62. }
  63. // {{{ Private utilities
  64. // Compute return value of getTextLocation() from (x, y).
  65. private Rectangle getCaretRectangle(int x, int y)
  66. {
  67. TextAreaPainter painter = owner.getPainter();
  68. Point origin = painter.getLocationOnScreen();
  69. int height = painter.getLineHeight();
  70. return new Rectangle(origin.x + x, origin.y + y, 0, height);
  71. }
  72. // }}}
  73. // {{{ extends TextAreaExtension
  74. public void paintValidLine(Graphics2D gfx, int screenLine,
  75. int physicalLine, int start, int end, int y)
  76. {
  77. if(composedTextLayout != null)
  78. {
  79. int caret = owner.getCaretPosition();
  80. if(start <= caret && caret < end)
  81. {
  82. TextAreaPainter painter = owner.getPainter();
  83. // The hight and baseline are taken from
  84. // painter's FontMetrics instead of TextLayout
  85. // so that the composed text is rendered at
  86. // the same position with text in the TextArea.
  87. FontMetrics fm = painter.getFontMetrics();
  88. int x = owner.offsetToXY(caret).x;
  89. int width = Math.round(composedTextLayout.getAdvance());
  90. int height = painter.getLineHeight();
  91. int offset_to_baseline = height
  92. - (fm.getLeading()+1) - fm.getDescent();
  93. int caret_x = x + composedCaretX;
  94. gfx.setColor(painter.getBackground());
  95. gfx.fillRect(x, y, width, height);
  96. gfx.setColor(painter.getForeground());
  97. composedTextLayout.draw(gfx, x, y + offset_to_baseline);
  98. gfx.setColor(painter.getCaretColor());
  99. gfx.drawLine(caret_x, y, caret_x, y + height - 1);
  100. }
  101. }
  102. }
  103. // }}}
  104. // {{{ implements InputMethodRequests
  105. public Rectangle getTextLocation(TextHitInfo offset)
  106. {
  107. if(composedTextLayout != null)
  108. {
  109. // return location of composed text.
  110. Point caret = owner.offsetToXY(owner.getCaretPosition());
  111. return getCaretRectangle(caret.x + composedCaretX, caret.y);
  112. }
  113. else
  114. {
  115. // return location of selected text.
  116. Selection selection_on_caret = owner.getSelectionAtOffset(owner.getCaretPosition());
  117. if(selection_on_caret != null)
  118. {
  119. Point selection_start = owner.offsetToXY(selection_on_caret.getStart());
  120. return getCaretRectangle(selection_start.x, selection_start.y);
  121. }
  122. }
  123. return null;
  124. }
  125. public TextHitInfo getLocationOffset(int x, int y)
  126. {
  127. if(composedTextLayout != null)
  128. {
  129. Point origin = owner.getPainter().getLocationOnScreen();
  130. Point caret = owner.offsetToXY(owner.getCaretPosition());
  131. float local_x = x - origin.x - caret.x;
  132. float local_y = y - origin.y - caret.y
  133. - (composedTextLayout.getLeading()+1)
  134. - composedTextLayout.getAscent();
  135. return composedTextLayout.hitTestChar(local_x, local_y);
  136. }
  137. return null;
  138. }
  139. public int getInsertPositionOffset()
  140. {
  141. return owner.getCaretPosition();
  142. }
  143. public AttributedCharacterIterator getCommittedText(int beginIndex , int endIndex
  144. , AttributedCharacterIterator.Attribute[] attributes)
  145. {
  146. return (new AttributedString(owner.getText(beginIndex, endIndex - beginIndex))).getIterator();
  147. }
  148. public int getCommittedTextLength()
  149. {
  150. return owner.getBufferLength();
  151. }
  152. public AttributedCharacterIterator cancelLatestCommittedText(AttributedCharacterIterator.Attribute[] attributes)
  153. {
  154. if(lastCommittedText != null)
  155. {
  156. int offset = lastCommittedAt;
  157. int length = lastCommittedText.length();
  158. String sample = owner.getText(offset, length);
  159. if(sample != null && sample.equals(lastCommittedText))
  160. {
  161. AttributedCharacterIterator canceled = (new AttributedString(sample)).getIterator();
  162. owner.getBuffer().remove(offset, length);
  163. owner.setCaretPosition(offset);
  164. lastCommittedText = null;
  165. return canceled;
  166. }
  167. // Cleare last committed information to prevent
  168. // accidental match.
  169. lastCommittedText = null;
  170. }
  171. return null;
  172. }
  173. public AttributedCharacterIterator getSelectedText(AttributedCharacterIterator.Attribute[] attributes)
  174. {
  175. Selection selection_on_caret = owner.getSelectionAtOffset(owner.getCaretPosition());
  176. if(selection_on_caret != null)
  177. {
  178. return (new AttributedString(owner.getSelectedText(selection_on_caret))).getIterator();
  179. }
  180. return null;
  181. }
  182. // }}}
  183. // {{{ implements InputMethodListener
  184. public void inputMethodTextChanged(InputMethodEvent event)
  185. {
  186. composedTextLayout = null;
  187. AttributedCharacterIterator text = event.getText();
  188. if(text != null)
  189. {
  190. int committed_count = event.getCommittedCharacterCount();
  191. if(committed_count > 0)
  192. {
  193. lastCommittedText = null;
  194. lastCommittedAt = owner.getCaretPosition();
  195. StringBuilder committed = new StringBuilder(committed_count);
  196. char c;
  197. int count;
  198. for(c = text.first(), count = committed_count
  199. ; c != AttributedCharacterIterator.DONE && count > 0
  200. ; c = text.next(), --count)
  201. {
  202. owner.userInput(c);
  203. committed.append(c);
  204. }
  205. lastCommittedText = committed.toString();
  206. }
  207. int end_index = text.getEndIndex();
  208. if(committed_count < end_index)
  209. {
  210. AttributedString composed = new AttributedString(text, committed_count, end_index);
  211. TextAreaPainter painter = owner.getPainter();
  212. composed.addAttribute(TextAttribute.FONT, painter.getFont());
  213. composedTextLayout = new TextLayout(composed.getIterator()
  214. , painter.getFontRenderContext());
  215. }
  216. }
  217. // Also updates caret.
  218. caretPositionChanged(event);
  219. }
  220. public void caretPositionChanged(InputMethodEvent event)
  221. {
  222. composedCaretX = 0;
  223. if(composedTextLayout != null)
  224. {
  225. TextHitInfo caret = event.getCaret();
  226. if(caret != null)
  227. {
  228. composedCaretX = Math.round(composedTextLayout.getCaretInfo(caret)[0]);
  229. }
  230. // Adjust visiblity.
  231. int insertion_x = owner.offsetToXY(owner.getCaretPosition()).x;
  232. TextHitInfo visible = event.getVisiblePosition();
  233. int composed_visible_x = (visible != null)
  234. ? Math.round(composedTextLayout.getCaretInfo(visible)[0])
  235. : composedCaretX;
  236. int visible_x = insertion_x + composed_visible_x;
  237. int painter_width = owner.getPainter().getWidth();
  238. int adjustment = 0;
  239. if(visible_x < 0)
  240. {
  241. adjustment = visible_x;
  242. }
  243. if(visible_x >= painter_width)
  244. {
  245. adjustment = visible_x - (painter_width - 1);
  246. }
  247. if(adjustment != 0)
  248. {
  249. owner.setHorizontalOffset(owner.getHorizontalOffset() - adjustment);
  250. }
  251. }
  252. else
  253. {
  254. /* Cancel horizontal adjustment for composed text.
  255. FIXME:
  256. The horizontal offset may be beyond the max
  257. value of owner's horizontal scroll bar.
  258. */
  259. owner.scrollToCaret(false);
  260. }
  261. /* Invalidate one more line below the caret because
  262. the underline for composed text goes beyond the caret
  263. line in some font settings. */
  264. int caret_line = owner.getCaretLine();
  265. owner.invalidateLineRange(caret_line, caret_line + 1);
  266. event.consume();
  267. }
  268. // }}}
  269. }