1. /*
  2. * @(#)Utilities.java 1.31 00/07/26
  3. *
  4. * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package javax.swing.text;
  11. import java.lang.reflect.Method;
  12. import java.awt.Rectangle;
  13. import java.awt.Graphics;
  14. import java.awt.FontMetrics;
  15. import java.awt.Toolkit;
  16. import java.text.*;
  17. import java.awt.Graphics2D;
  18. import java.awt.font.FontRenderContext;
  19. import java.awt.font.TextLayout;
  20. import java.awt.font.TextAttribute;
  21. import java.text.AttributedString;
  22. /**
  23. * A collection of methods to deal with various text
  24. * related activities.
  25. *
  26. * @author Timothy Prinzing
  27. * @version 1.31 07/26/00
  28. */
  29. public class Utilities {
  30. /**
  31. * Draws the given text, expanding any tabs that are contained
  32. * using the given tab expansion technique. This particular
  33. * implementation renders in a 1.1 style coordinate system
  34. * where ints are used and 72dpi is assumed.
  35. *
  36. * @param s the source of the text
  37. * @param x the X origin >= 0
  38. * @param y the Y origin >= 0
  39. * @param g the graphics context
  40. * @param e how to expand the tabs. If this value is null,
  41. * tabs will be expanded as a space character.
  42. * @param startOffset starting offset of the text in the document >= 0
  43. * @returns the X location at the end of the rendered text
  44. */
  45. public static final int drawTabbedText(Segment s, int x, int y, Graphics g,
  46. TabExpander e, int startOffset) {
  47. FontMetrics metrics = g.getFontMetrics();
  48. int nextX = x;
  49. char[] txt = s.array;
  50. int txtOffset = s.offset;
  51. int flushLen = 0;
  52. int flushIndex = s.offset;
  53. int n = s.offset + s.count;
  54. for (int i = txtOffset; i < n; i++) {
  55. if (txt[i] == '\t') {
  56. if (flushLen > 0) {
  57. g.drawChars(txt, flushIndex, flushLen, x, y);
  58. flushLen = 0;
  59. }
  60. flushIndex = i + 1;
  61. if (e != null) {
  62. nextX = (int) e.nextTabStop((float) nextX, startOffset + i - txtOffset);
  63. } else {
  64. nextX += metrics.charWidth(' ');
  65. }
  66. x = nextX;
  67. } else if ((txt[i] == '\n') || (txt[i] == '\r')) {
  68. if (flushLen > 0) {
  69. g.drawChars(txt, flushIndex, flushLen, x, y);
  70. flushLen = 0;
  71. }
  72. flushIndex = i + 1;
  73. x = nextX;
  74. } else {
  75. flushLen += 1;
  76. nextX += metrics.charWidth(txt[i]);
  77. }
  78. }
  79. if (flushLen > 0) {
  80. g.drawChars(txt, flushIndex, flushLen, x, y);
  81. }
  82. return nextX;
  83. }
  84. /**
  85. * Determines the width of the given segment of text taking tabs
  86. * into consideration. This is implemented in a 1.1 style coordinate
  87. * system where ints are used and 72dpi is assumed.
  88. *
  89. * @param s the source of the text
  90. * @param metrics the font metrics to use for the calculation
  91. * @param x the X origin >= 0
  92. * @param e how to expand the tabs. If this value is null,
  93. * tabs will be expanded as a space character.
  94. * @param startOffset starting offset of the text in the document >= 0
  95. * @returns the width of the text
  96. */
  97. public static final int getTabbedTextWidth(Segment s, FontMetrics metrics, int x,
  98. TabExpander e, int startOffset) {
  99. int nextX = x;
  100. char[] txt = s.array;
  101. int txtOffset = s.offset;
  102. int n = s.offset + s.count;
  103. for (int i = txtOffset; i < n; i++) {
  104. if (txt[i] == '\t') {
  105. if (e != null) {
  106. nextX = (int) e.nextTabStop((float) nextX,
  107. startOffset + i - txtOffset);
  108. } else {
  109. nextX += metrics.charWidth(' ');
  110. }
  111. } else if(txt[i] != '\n') {
  112. nextX += metrics.charWidth(txt[i]);
  113. }
  114. // Ignore newlines, they take up space and we shouldn't be
  115. // counting them.
  116. }
  117. return nextX - x;
  118. }
  119. /**
  120. * Determines the relative offset into the given text that
  121. * best represents the given span in the view coordinate
  122. * system. This is implemented in a 1.1 style coordinate
  123. * system where ints are used and 72dpi is assumed.
  124. *
  125. * @param s the source of the text
  126. * @param metrics the font metrics to use for the calculation
  127. * @param x0 the starting view location representing the start
  128. * of the given text >= 0.
  129. * @param x the target view location to translate to an
  130. * offset into the text >= 0.
  131. * @param e how to expand the tabs. If this value is null,
  132. * tabs will be expanded as a space character.
  133. * @param startOffset starting offset of the text in the document >= 0
  134. * @returns the offset into the text >= 0
  135. */
  136. public static final int getTabbedTextOffset(Segment s, FontMetrics metrics,
  137. int x0, int x, TabExpander e,
  138. int startOffset) {
  139. return getTabbedTextOffset(s, metrics, x0, x, e, startOffset, true);
  140. }
  141. public static final int getTabbedTextOffset(Segment s,
  142. FontMetrics metrics,
  143. int x0, int x, TabExpander e,
  144. int startOffset,
  145. boolean round) {
  146. int currX = x0;
  147. int nextX = currX;
  148. // s may be a shared segment, so it is copied prior to calling
  149. // the tab expander
  150. char[] txt = s.array;
  151. int txtOffset = s.offset;
  152. int txtCount = s.count;
  153. int n = s.offset + s.count;
  154. for (int i = s.offset; i < n; i++) {
  155. if (txt[i] == '\t') {
  156. if (e != null) {
  157. nextX = (int) e.nextTabStop((float) nextX,
  158. startOffset + i - txtOffset);
  159. } else {
  160. nextX += metrics.charWidth(' ');
  161. }
  162. } else {
  163. nextX += metrics.charWidth(txt[i]);
  164. }
  165. if ((x >= currX) && (x < nextX)) {
  166. // found the hit position... return the appropriate side
  167. if ((round == false) || ((x - currX) < (nextX - x))) {
  168. return i - txtOffset;
  169. } else {
  170. return i + 1 - txtOffset;
  171. }
  172. }
  173. currX = nextX;
  174. }
  175. // didn't find, return end offset
  176. return txtCount;
  177. }
  178. /**
  179. * Determine where to break the given text to fit
  180. * within the the given span. This tries to find a
  181. * whitespace boundary.
  182. * @param s the source of the text
  183. * @param metrics the font metrics to use for the calculation
  184. * @param x0 the starting view location representing the start
  185. * of the given text.
  186. * @param x the target view location to translate to an
  187. * offset into the text.
  188. * @param e how to expand the tabs. If this value is null,
  189. * tabs will be expanded as a space character.
  190. * @param startOffset starting offset in the document of the text
  191. * @returns the offset into the given text.
  192. */
  193. public static final int getBreakLocation(Segment s, FontMetrics metrics,
  194. int x0, int x, TabExpander e,
  195. int startOffset) {
  196. char[] txt = s.array;
  197. int txtOffset = s.offset;
  198. int txtCount = s.count;
  199. int index = Utilities.getTabbedTextOffset(s, metrics, x0, x,
  200. e, startOffset, false);
  201. for (int i = txtOffset + Math.min(index, txtCount - 1);
  202. i >= txtOffset; i--) {
  203. char ch = txt[i];
  204. if (Character.isWhitespace(ch)) {
  205. // found whitespace, break here
  206. index = i - txtOffset + 1;
  207. break;
  208. }
  209. }
  210. return index;
  211. }
  212. /**
  213. * Determines the starting row model position of the row that contains
  214. * the specified model position. The component given must have a
  215. * size to compute the result. If the component doesn't have a size
  216. * a value of -1 will be returned.
  217. *
  218. * @param c the editor
  219. * @param offs the offset in the document >= 0
  220. * @return the position >= 0 if the request can be computed, otherwise
  221. * a value of -1 will be returned.
  222. * @exception BadLocationException if the offset is out of range
  223. */
  224. public static final int getRowStart(JTextComponent c, int offs) throws BadLocationException {
  225. Rectangle r = c.modelToView(offs);
  226. if (r == null) {
  227. return -1;
  228. }
  229. int lastOffs = offs;
  230. int y = r.y;
  231. while ((r != null) && (y == r.y)) {
  232. offs = lastOffs;
  233. lastOffs -= 1;
  234. r = (lastOffs >= 0) ? c.modelToView(lastOffs) : null;
  235. }
  236. return offs;
  237. }
  238. /**
  239. * Determines the ending row model position of the row that contains
  240. * the specified model position. The component given must have a
  241. * size to compute the result. If the component doesn't have a size
  242. * a value of -1 will be returned.
  243. *
  244. * @param c the editor
  245. * @param offs the offset in the document >= 0
  246. * @return the position >= 0 if the request can be computed, otherwise
  247. * a value of -1 will be returned.
  248. * @exception BadLocationException if the offset is out of range
  249. */
  250. public static final int getRowEnd(JTextComponent c, int offs) throws BadLocationException {
  251. Rectangle r = c.modelToView(offs);
  252. if (r == null) {
  253. return -1;
  254. }
  255. int n = c.getDocument().getLength();
  256. int lastOffs = offs;
  257. int y = r.y;
  258. while ((r != null) && (y == r.y)) {
  259. offs = lastOffs;
  260. lastOffs += 1;
  261. r = (lastOffs <= n) ? c.modelToView(lastOffs) : null;
  262. }
  263. return offs;
  264. }
  265. /**
  266. * Determines the position in the model that is closest to the given
  267. * view location in the row above. The component given must have a
  268. * size to compute the result. If the component doesn't have a size
  269. * a value of -1 will be returned.
  270. *
  271. * @param c the editor
  272. * @param offs the offset in the document >= 0
  273. * @param x the X coordinate >= 0
  274. * @return the position >= 0 if the request can be computed, otherwise
  275. * a value of -1 will be returned.
  276. * @exception BadLocationException if the offset is out of range
  277. */
  278. public static final int getPositionAbove(JTextComponent c, int offs, int x) throws BadLocationException {
  279. int lastOffs = getRowStart(c, offs) - 1;
  280. if (lastOffs < 0) {
  281. return -1;
  282. }
  283. int bestSpan = Short.MAX_VALUE;
  284. int y = 0;
  285. Rectangle r = null;
  286. if (lastOffs >= 0) {
  287. r = c.modelToView(lastOffs);
  288. y = r.y;
  289. }
  290. while ((r != null) && (y == r.y)) {
  291. int span = Math.abs(r.x - x);
  292. if (span < bestSpan) {
  293. offs = lastOffs;
  294. bestSpan = span;
  295. }
  296. lastOffs -= 1;
  297. r = (lastOffs >= 0) ? c.modelToView(lastOffs) : null;
  298. }
  299. return offs;
  300. }
  301. /**
  302. * Determines the position in the model that is closest to the given
  303. * view location in the row below. The component given must have a
  304. * size to compute the result. If the component doesn't have a size
  305. * a value of -1 will be returned.
  306. *
  307. * @param c the editor
  308. * @param offs the offset in the document >= 0
  309. * @param x the X coordinate >= 0
  310. * @return the position >= 0 if the request can be computed, otherwise
  311. * a value of -1 will be returned.
  312. * @exception BadLocationException if the offset is out of range
  313. */
  314. public static final int getPositionBelow(JTextComponent c, int offs, int x) throws BadLocationException {
  315. int lastOffs = getRowEnd(c, offs) + 1;
  316. if (lastOffs <= 0) {
  317. return -1;
  318. }
  319. int bestSpan = Short.MAX_VALUE;
  320. int n = c.getDocument().getLength();
  321. int y = 0;
  322. Rectangle r = null;
  323. if (lastOffs <= n) {
  324. r = c.modelToView(lastOffs);
  325. y = r.y;
  326. }
  327. while ((r != null) && (y == r.y)) {
  328. int span = Math.abs(x - r.x);
  329. if (span < bestSpan) {
  330. offs = lastOffs;
  331. bestSpan = span;
  332. }
  333. lastOffs += 1;
  334. r = (lastOffs <= n) ? c.modelToView(lastOffs) : null;
  335. }
  336. return offs;
  337. }
  338. /**
  339. * Determines the start of a word for the given model location.
  340. * Uses BreakIterator.getWordInstance() to actually get the words.
  341. *
  342. * @param c the editor
  343. * @param offs the offset in the document >= 0
  344. * @returns the location in the model of the word start >= 0.
  345. * @exception BadLocationException if the offset is out of range
  346. */
  347. public static final int getWordStart(JTextComponent c, int offs) throws BadLocationException {
  348. Document doc = c.getDocument();
  349. Element line = getParagraphElement(c, offs);
  350. if (line == null) {
  351. throw new BadLocationException("No word at " + offs, offs);
  352. }
  353. int lineStart = line.getStartOffset();
  354. int lineEnd = Math.min(line.getEndOffset(), doc.getLength());
  355. String s = doc.getText(lineStart, lineEnd - lineStart);
  356. if(s != null && s.length() > 0) {
  357. BreakIterator words = BreakIterator.getWordInstance();
  358. words.setText(s);
  359. int wordPosition = offs - lineStart;
  360. if(wordPosition >= words.last()) {
  361. wordPosition = words.last() - 1;
  362. }
  363. words.following(wordPosition);
  364. offs = lineStart + words.previous();
  365. }
  366. return offs;
  367. }
  368. /**
  369. * Determines the end of a word for the given location.
  370. * Uses BreakIterator.getWordInstance() to actually get the words.
  371. *
  372. * @param c the editor
  373. * @param offs the offset in the document >= 0
  374. * @returns the location in the model of the word end >= 0.
  375. * @exception BadLocationException if the offset is out of range
  376. */
  377. public static final int getWordEnd(JTextComponent c, int offs) throws BadLocationException {
  378. Document doc = c.getDocument();
  379. Element line = getParagraphElement(c, offs);
  380. if (line == null) {
  381. throw new BadLocationException("No word at " + offs, offs);
  382. }
  383. int lineStart = line.getStartOffset();
  384. int lineEnd = Math.min(line.getEndOffset(), doc.getLength());
  385. String s = doc.getText(lineStart, lineEnd - lineStart);
  386. if(s != null && s.length() > 0) {
  387. BreakIterator words = BreakIterator.getWordInstance();
  388. words.setText(s);
  389. int wordPosition = offs - lineStart;
  390. if(wordPosition >= words.last()) {
  391. wordPosition = words.last() - 1;
  392. }
  393. offs = lineStart + words.following(wordPosition);
  394. }
  395. return offs;
  396. }
  397. /**
  398. * Determines the start of the next word for the given location.
  399. * Uses BreakIterator.getWordInstance() to actually get the words.
  400. *
  401. * @param c the editor
  402. * @param offs the offset in the document >= 0
  403. * @returns the location in the model of the word start >= 0.
  404. * @exception BadLocationException if the offset is out of range
  405. */
  406. public static final int getNextWord(JTextComponent c, int offs) throws BadLocationException {
  407. int nextWord;
  408. Element line = getParagraphElement(c, offs);
  409. for (nextWord = getNextWordInParagraph(line, offs, false);
  410. nextWord == BreakIterator.DONE;
  411. nextWord = getNextWordInParagraph(line, offs, true)) {
  412. // didn't find in this line, try the next line
  413. offs = line.getEndOffset();
  414. line = getParagraphElement(c, offs);
  415. }
  416. return nextWord;
  417. }
  418. /**
  419. * Finds the next word in the given elements text. The first
  420. * parameter allows searching multiple paragraphs where even
  421. * the first offset is desired.
  422. * Returns the offset of the next word, or BreakIterator.DONE
  423. * if there are no more words in the element.
  424. */
  425. static int getNextWordInParagraph(Element line, int offs, boolean first) throws BadLocationException {
  426. if (line == null) {
  427. throw new BadLocationException("No more words", offs);
  428. }
  429. Document doc = line.getDocument();
  430. int lineStart = line.getStartOffset();
  431. int lineEnd = Math.min(line.getEndOffset(), doc.getLength());
  432. if ((offs >= lineEnd) || (offs < lineStart)) {
  433. throw new BadLocationException("No more words", offs);
  434. }
  435. String s = doc.getText(lineStart, lineEnd - lineStart);
  436. BreakIterator words = BreakIterator.getWordInstance();
  437. words.setText(s);
  438. if ((first && (words.first() == (offs - lineStart))) &&
  439. (! Character.isWhitespace(s.charAt(words.first())))) {
  440. return offs;
  441. }
  442. int wordPosition = words.following(offs - lineStart);
  443. if ((wordPosition == BreakIterator.DONE) ||
  444. (wordPosition >= s.length())) {
  445. // there are no more words on this line.
  446. return BreakIterator.DONE;
  447. }
  448. // if we haven't shot past the end... check to
  449. // see if the current boundary represents whitespace.
  450. // if so, we need to try again
  451. char ch = s.charAt(wordPosition);
  452. if (! Character.isWhitespace(ch)) {
  453. return lineStart + wordPosition;
  454. }
  455. // it was whitespace, try again. The assumption
  456. // is that it must be a word start if the last
  457. // one had whitespace following it.
  458. wordPosition = words.next();
  459. if (wordPosition != BreakIterator.DONE) {
  460. offs = lineStart + wordPosition;
  461. if (offs != lineEnd) {
  462. return offs;
  463. }
  464. }
  465. return BreakIterator.DONE;
  466. }
  467. /**
  468. * Determine the start of the prev word for the given location.
  469. * Uses BreakIterator.getWordInstance() to actually get the words.
  470. *
  471. * @param c the editor
  472. * @param offs the offset in the document >= 0
  473. * @returns the location in the model of the word start >= 0.
  474. * @exception BadLocationException if the offset is out of range
  475. */
  476. public static final int getPreviousWord(JTextComponent c, int offs) throws BadLocationException {
  477. int prevWord;
  478. Element line = getParagraphElement(c, offs);
  479. for (prevWord = getPrevWordInParagraph(line, offs);
  480. prevWord == BreakIterator.DONE;
  481. prevWord = getPrevWordInParagraph(line, offs)) {
  482. // didn't find in this line, try the prev line
  483. offs = line.getStartOffset() - 1;
  484. line = getParagraphElement(c, offs);
  485. }
  486. return prevWord;
  487. }
  488. /**
  489. * Finds the previous word in the given elements text. The first
  490. * parameter allows searching multiple paragraphs where even
  491. * the first offset is desired.
  492. * Returns the offset of the next word, or BreakIterator.DONE
  493. * if there are no more words in the element.
  494. */
  495. static int getPrevWordInParagraph(Element line, int offs) throws BadLocationException {
  496. if (line == null) {
  497. throw new BadLocationException("No more words", offs);
  498. }
  499. Document doc = line.getDocument();
  500. int lineStart = line.getStartOffset();
  501. int lineEnd = line.getEndOffset();
  502. if ((offs > lineEnd) || (offs < lineStart)) {
  503. throw new BadLocationException("No more words", offs);
  504. }
  505. String s = doc.getText(lineStart, lineEnd - lineStart);
  506. BreakIterator words = BreakIterator.getWordInstance();
  507. words.setText(s);
  508. if (words.following(offs - lineStart) == BreakIterator.DONE) {
  509. words.last();
  510. }
  511. int wordPosition = words.previous();
  512. if (wordPosition == (offs - lineStart)) {
  513. wordPosition = words.previous();
  514. }
  515. if (wordPosition == BreakIterator.DONE) {
  516. // there are no more words on this line.
  517. return BreakIterator.DONE;
  518. }
  519. // if we haven't shot past the end... check to
  520. // see if the current boundary represents whitespace.
  521. // if so, we need to try again
  522. char ch = s.charAt(wordPosition);
  523. if (! Character.isWhitespace(ch)) {
  524. return lineStart + wordPosition;
  525. }
  526. // it was whitespace, try again. The assumption
  527. // is that it must be a word start if the last
  528. // one had whitespace following it.
  529. wordPosition = words.previous();
  530. if (wordPosition != BreakIterator.DONE) {
  531. return lineStart + wordPosition;
  532. }
  533. return BreakIterator.DONE;
  534. }
  535. /**
  536. * Determines the element to use for a paragraph/line.
  537. *
  538. * @param c the editor
  539. * @param offs the starting offset in the document >= 0
  540. * @return the element
  541. */
  542. public static final Element getParagraphElement(JTextComponent c, int offs) {
  543. Document doc = c.getDocument();
  544. if (doc instanceof StyledDocument) {
  545. return ((StyledDocument)doc).getParagraphElement(offs);
  546. }
  547. Element map = doc.getDefaultRootElement();
  548. int index = map.getElementIndex(offs);
  549. Element paragraph = map.getElement(index);
  550. if ((offs >= paragraph.getStartOffset()) && (offs < paragraph.getEndOffset())) {
  551. return paragraph;
  552. }
  553. return null;
  554. }
  555. static boolean isComposedTextElement(Element elem) {
  556. AttributeSet as = elem.getAttributes();
  557. return isComposedTextAttributeDefined(as);
  558. }
  559. static boolean isComposedTextAttributeDefined(AttributeSet as) {
  560. return ((as != null) &&
  561. (as.isDefined(StyleConstants.ComposedTextAttribute)));
  562. }
  563. /**
  564. * Draws the given composed text passed from an input method.
  565. *
  566. * @param attr the attributes containing the composed text
  567. * @param g the graphics context
  568. * @param x the X origin
  569. * @param y the Y origin
  570. * @param p0 starting offset in the composed text to be rendered
  571. * @param p1 ending offset in the composed text to be rendered
  572. * @returns the new insertion position
  573. */
  574. static int drawComposedText(AttributeSet attr, Graphics g, int x, int y,
  575. int p0, int p1) throws BadLocationException {
  576. Graphics2D g2d = (Graphics2D)g;
  577. AttributedString as = (AttributedString)attr.getAttribute(
  578. StyleConstants.ComposedTextAttribute);
  579. as.addAttribute(TextAttribute.FONT, g.getFont());
  580. if (p0 >= p1)
  581. return x;
  582. AttributedCharacterIterator aci = as.getIterator(null, p0, p1);
  583. // Create text layout
  584. TextLayout layout = new TextLayout(aci, g2d.getFontRenderContext());
  585. // draw
  586. layout.draw(g2d, x, y);
  587. return x + (int)layout.getAdvance();
  588. }
  589. /**
  590. * Paints the composed text in a GlyphView
  591. */
  592. static void paintComposedText(Graphics g, Rectangle alloc, GlyphView v) {
  593. if (g instanceof Graphics2D) {
  594. Graphics2D g2d = (Graphics2D) g;
  595. int p0 = v.getStartOffset();
  596. int p1 = v.getEndOffset();
  597. AttributeSet attrSet = v.getElement().getAttributes();
  598. AttributedString as =
  599. (AttributedString)attrSet.getAttribute(StyleConstants.ComposedTextAttribute);
  600. int start = v.getElement().getStartOffset();
  601. int y = alloc.y + (int) v.getGlyphPainter().getAscent(v);
  602. int x = alloc.x;
  603. //Add text attributes
  604. as.addAttribute(TextAttribute.FONT, v.getFont());
  605. as.addAttribute(TextAttribute.FOREGROUND, v.getForeground());
  606. if (StyleConstants.isBold(v.getAttributes())) {
  607. as.addAttribute(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD);
  608. }
  609. if (StyleConstants.isItalic(v.getAttributes())) {
  610. as.addAttribute(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE);
  611. }
  612. if (v.isUnderline()) {
  613. as.addAttribute(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
  614. }
  615. if (v.isStrikeThrough()) {
  616. as.addAttribute(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON);
  617. }
  618. if (v.isSuperscript()) {
  619. as.addAttribute(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUPER);
  620. }
  621. if (v.isSubscript()) {
  622. as.addAttribute(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUB);
  623. }
  624. // draw
  625. AttributedCharacterIterator aci = as.getIterator(null, p0 - start, p1 - start);
  626. TextLayout layout = new TextLayout(aci, g2d.getFontRenderContext());
  627. layout.draw(g2d, x, y);
  628. }
  629. }
  630. /*
  631. * Convenience function for determining ComponentOrientation. Helps us
  632. * avoid having Munge directives throughout the code.
  633. */
  634. static boolean isLeftToRight( java.awt.Component c ) {
  635. return c.getComponentOrientation().isLeftToRight();
  636. }
  637. }