/plugins/VoxSpell/tags/release-1.0.2/voxspellcheck/VoxSpellPainter.java

# · Java · 324 lines · 256 code · 42 blank · 26 comment · 44 complexity · f32d58f5b43552e8a88573b1d9cbaff3 MD5 · raw file

  1. /*
  2. Copyright (C) 2008 Matthew Gilbert
  3. This program is free software; you can redistribute it and/or
  4. modify it under the terms of the GNU General Public License
  5. as published by the Free Software Foundation; either version 2
  6. of the License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program; if not, write to the Free Software
  13. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  14. */
  15. package voxspellcheck;
  16. import java.lang.StringBuffer;
  17. import java.util.Vector;
  18. import java.util.Enumeration;
  19. import org.gjt.sp.util.Log;
  20. import org.gjt.sp.jedit.textarea.TextArea;
  21. import org.gjt.sp.jedit.textarea.TextAreaExtension;
  22. import org.gjt.sp.jedit.textarea.TextAreaPainter;
  23. import org.gjt.sp.jedit.textarea.Selection;
  24. import org.gjt.sp.jedit.buffer.JEditBuffer;
  25. import org.gjt.sp.jedit.Buffer;
  26. import org.gjt.sp.jedit.View;
  27. import org.gjt.sp.jedit.jEdit;
  28. import org.gjt.sp.jedit.syntax.*;
  29. import org.gjt.sp.jedit.TextUtilities;
  30. import org.gjt.sp.jedit.textarea.JEditTextArea;
  31. import java.awt.geom.*;
  32. import java.awt.Rectangle;
  33. import java.awt.FontMetrics;
  34. import java.awt.Point;
  35. import java.awt.EventQueue;
  36. import java.awt.Color;
  37. public class VoxSpellPainter extends TextAreaExtension
  38. {
  39. private enum MarkupMode {ALL_TEXT, NON_MARKUP, COMMENT_OR_LITERAL};
  40. private SpellCheck checker;
  41. private SpellCheck user_checker;
  42. private SpellCheck ignore_checker;
  43. private TextArea textarea;
  44. private String mode;
  45. private MarkupMode markup_mode;
  46. // non-letter chars that should not indicate the end of the word.
  47. static private String no_word_sep = "'";
  48. static public java.awt.Color getUnderlineColor()
  49. {
  50. String s;
  51. boolean b;
  52. s = jEdit.getProperty("options.voxspellcheck.use_custom_color");
  53. b = s.equals("true");
  54. Integer bg;
  55. if (b) {
  56. bg = jEdit.getColorProperty("options.voxspellcheck.custom_color").getRGB();
  57. } else {
  58. s = jEdit.getProperty("view.status.background");
  59. bg = Integer.decode(s);
  60. // Default underline color is the inverse of the bg color
  61. bg ^= ~1;
  62. }
  63. bg = (bg & 0xffffff) | 0x88000000;
  64. return new java.awt.Color(bg, true);
  65. }
  66. public void setMode(String mode_)
  67. {
  68. mode = mode_;
  69. String all_text_modes = jEdit.getProperty("options.voxspellcheck.all_text_modes");
  70. String non_markup_modes = jEdit.getProperty("options.voxspellcheck.non_markup_modes");
  71. String[] at_array = all_text_modes.split(" ");
  72. String[] nm_array = non_markup_modes.split(" ");
  73. java.util.Arrays.sort(at_array);
  74. java.util.Arrays.sort(nm_array);
  75. markup_mode = MarkupMode.COMMENT_OR_LITERAL;
  76. if (java.util.Arrays.binarySearch(at_array, mode) >= 0) {
  77. markup_mode = MarkupMode.ALL_TEXT;
  78. } else if (java.util.Arrays.binarySearch(nm_array, mode) >= 0) {
  79. markup_mode = MarkupMode.NON_MARKUP;
  80. }
  81. }
  82. public String getMode()
  83. {
  84. return mode;
  85. }
  86. public void setIgnoreChecker(SpellCheck ignore_checker_)
  87. {
  88. ignore_checker = ignore_checker_;
  89. }
  90. public SpellCheck getIgnoreChecker()
  91. {
  92. return ignore_checker;
  93. }
  94. private int getWordExtent(final String text, int offset)
  95. {
  96. int num_chars = 0;
  97. try {
  98. Character c = text.charAt(offset + num_chars++);
  99. if (Character.isSpaceChar(c)) {
  100. while (Character.isSpaceChar(c) || c.equals('\t')) {
  101. c = text.charAt(offset + num_chars++);
  102. }
  103. } else if (Character.isLetterOrDigit(c)) {
  104. while (Character.isLetterOrDigit(c) ||
  105. (no_word_sep.indexOf(c) != -1))
  106. {
  107. c = text.charAt(offset + num_chars++);
  108. }
  109. } else {
  110. while (!Character.isLetterOrDigit(c)) {
  111. c = text.charAt(offset + num_chars++);
  112. }
  113. }
  114. } catch (java.lang.IndexOutOfBoundsException ex) {
  115. ;
  116. }
  117. return --num_chars;
  118. }
  119. private Vector<String> getWords(TextArea textarea, int buf_start, int buf_end)
  120. {
  121. Vector<String> words = new Vector<String>();
  122. if (buf_start == textarea.getBufferLength())
  123. return words;
  124. // The last line includes the cursor position after the last char. This
  125. // compensates for that (otherwise getText throws an index exception).
  126. // FIXME: Not so sure about that.
  127. int len = java.lang.Math.min(buf_end - buf_start,
  128. textarea.getBufferLength() - buf_start);
  129. final String text = textarea.getText(buf_start, len);
  130. int start = 0;
  131. int end = 0;
  132. while (true) {
  133. end = getWordExtent(text, start);
  134. if (end == 0)
  135. break;
  136. words.add(text.substring(start, start + end));
  137. start += end;
  138. }
  139. return words;
  140. }
  141. public VoxSpellPainter(TextArea textarea_,
  142. SpellCheck checker_,
  143. SpellCheck user_checker_,
  144. SpellCheck ignore_checker_)
  145. {
  146. super();
  147. this.textarea = textarea_;
  148. this.checker = checker_;
  149. this.user_checker = user_checker_;
  150. this.ignore_checker = ignore_checker_;
  151. setMode("text");
  152. }
  153. protected boolean check(String word, int line_offset,
  154. DefaultTokenHandler tokenHandler,
  155. boolean user_only)
  156. {
  157. String trim_word = word.trim();
  158. String low_word = trim_word.toLowerCase();
  159. if (trim_word.length() == 0)
  160. return true;
  161. if (low_word.length() == 1)
  162. return true;
  163. if (trim_word.endsWith("'") && trim_word.length() == 2)
  164. return true;
  165. Character c = trim_word.charAt(0);
  166. if (!Character.isLetter(c))
  167. return true;
  168. // FIXME: Hack!
  169. if (!user_only) {
  170. Token token;
  171. try {
  172. // FIXME: why am I getting null tokens (ArrayIndexOutOfBoundsException)?
  173. token = TextUtilities.getTokenAtOffset(tokenHandler.getTokens(), line_offset);
  174. } catch (ArrayIndexOutOfBoundsException ex) {
  175. return true;
  176. }
  177. switch (markup_mode) {
  178. case COMMENT_OR_LITERAL:
  179. if ((token.id < Token.COMMENT1 || token.id > Token.COMMENT4) &&
  180. (token.id < Token.LITERAL1 || token.id > Token.LITERAL4))
  181. {
  182. return true;
  183. }
  184. break;
  185. case NON_MARKUP:
  186. if (token.id != Token.NULL) {
  187. return true;
  188. }
  189. break;
  190. }
  191. }
  192. if (user_only) {
  193. return user_checker.find(low_word) ||
  194. user_checker.find(trim_word) ||
  195. ignore_checker.find(low_word) ||
  196. ignore_checker.find(trim_word);
  197. }
  198. return checker.find(low_word) ||
  199. checker.find(trim_word) ||
  200. user_checker.find(low_word) ||
  201. user_checker.find(trim_word) ||
  202. ignore_checker.find(low_word) ||
  203. ignore_checker.find(trim_word);
  204. }
  205. public boolean check(String word, int line_offset,
  206. DefaultTokenHandler tokenHandler)
  207. {
  208. return check(word, line_offset, tokenHandler, false);
  209. }
  210. public boolean check(JEditTextArea ta, int pos, StringBuffer word_checked,
  211. boolean user_only)
  212. {
  213. int line;
  214. int start;
  215. int end;
  216. try {
  217. line = ta.getLineOfOffset(pos);
  218. start = ta.getLineStartOffset(line);
  219. end = ta.getLineEndOffset(line);
  220. } catch (java.lang.NullPointerException ex) {
  221. // Getting NPE's on splits
  222. return true;
  223. }
  224. JEditBuffer buffer = ta.getBuffer();
  225. DefaultTokenHandler tokenHandler = new DefaultTokenHandler();
  226. buffer.markTokens(line, tokenHandler);
  227. Vector<String> words = getWords(ta, start, end);
  228. Enumeration<String> iter = words.elements();
  229. int char_count = 0;
  230. String word = null;
  231. while (iter.hasMoreElements()) {
  232. word = iter.nextElement();
  233. if ((start + char_count + word.length()) >= pos)
  234. break;
  235. char_count += word.length();
  236. }
  237. if (word == null)
  238. return true;
  239. word_checked.replace(0, word_checked.length(), word);
  240. return check(word, char_count, tokenHandler, user_only);
  241. }
  242. public boolean check(JEditTextArea ta, int pos, StringBuffer word_checked)
  243. {
  244. return check(ta, pos, word_checked, false);
  245. }
  246. public void paintValidLine(java.awt.Graphics2D gfx, int screenLine,
  247. int physicalLine, int start, int end, int y)
  248. {
  249. // Make sure tokens are valid for this line
  250. JEditBuffer buffer = textarea.getBuffer();
  251. DefaultTokenHandler tokenHandler = new DefaultTokenHandler();
  252. buffer.markTokens(textarea.getLineOfOffset(start), tokenHandler);
  253. gfx.setColor(getUnderlineColor());
  254. FontMetrics metrics = this.textarea.getPainter().getFontMetrics();
  255. int char_height = metrics.getHeight() - metrics.getLeading();
  256. Vector<String> words = getWords(textarea, start, end);
  257. int char_count = 0;
  258. for (String word : words) {
  259. skip: {
  260. if (check(word, char_count, tokenHandler)) {
  261. break skip;
  262. }
  263. int x = textarea.offsetToXY(start + char_count).x;
  264. Point p;
  265. try {
  266. p = textarea.offsetToXY(start + char_count + word.length());
  267. } catch (java.lang.ArrayIndexOutOfBoundsException ex) {
  268. break skip;
  269. }
  270. if (p == null)
  271. break skip;
  272. int width = p.x - x;
  273. Rectangle r = new Rectangle(x, y + char_height - 1, width, 1);
  274. gfx.fill(r);
  275. }
  276. char_count += word.length();
  277. }
  278. }
  279. }