KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > swing > text > Utilities


1 /*
2  * @(#)Utilities.java 1.49 06/04/10
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7 package javax.swing.text;
8
9 import java.lang.reflect.Method JavaDoc;
10
11 import java.awt.Component JavaDoc;
12 import java.awt.Rectangle JavaDoc;
13 import java.awt.Graphics JavaDoc;
14 import java.awt.FontMetrics JavaDoc;
15 import java.awt.Shape JavaDoc;
16 import java.awt.Toolkit JavaDoc;
17 import java.awt.Graphics2D JavaDoc;
18 import java.awt.font.FontRenderContext JavaDoc;
19 import java.awt.font.TextLayout JavaDoc;
20 import java.awt.font.TextAttribute JavaDoc;
21
22 import java.text.*;
23 import javax.swing.JComponent JavaDoc;
24 import javax.swing.SwingConstants JavaDoc;
25 import javax.swing.text.ParagraphView.Row JavaDoc;
26 import com.sun.java.swing.SwingUtilities2;
27
28 /**
29  * A collection of methods to deal with various text
30  * related activities.
31  *
32  * @author Timothy Prinzing
33  * @version 1.49 04/10/06
34  */

35 public class Utilities {
36     /**
37      * If <code>view</code>'s container is a <code>JComponent</code> it
38      * is returned, after casting.
39      */

40     static JComponent JavaDoc getJComponent(View JavaDoc view) {
41         if (view != null) {
42             Component JavaDoc component = view.getContainer();
43             if (component instanceof JComponent JavaDoc) {
44                 return (JComponent JavaDoc)component;
45             }
46         }
47         return null;
48     }
49
50     /**
51      * Draws the given text, expanding any tabs that are contained
52      * using the given tab expansion technique. This particular
53      * implementation renders in a 1.1 style coordinate system
54      * where ints are used and 72dpi is assumed.
55      *
56      * @param s the source of the text
57      * @param x the X origin >= 0
58      * @param y the Y origin >= 0
59      * @param g the graphics context
60      * @param e how to expand the tabs. If this value is null,
61      * tabs will be expanded as a space character.
62      * @param startOffset starting offset of the text in the document >= 0
63      * @return the X location at the end of the rendered text
64      */

65     public static final int drawTabbedText(Segment JavaDoc s, int x, int y, Graphics JavaDoc g,
66                        TabExpander JavaDoc e, int startOffset) {
67         return drawTabbedText(null, s, x, y, g, e, startOffset);
68     }
69
70     /**
71      * Draws the given text, expanding any tabs that are contained
72      * using the given tab expansion technique. This particular
73      * implementation renders in a 1.1 style coordinate system
74      * where ints are used and 72dpi is assumed.
75      *
76      * @param view View requesting rendering, may be null.
77      * @param s the source of the text
78      * @param x the X origin >= 0
79      * @param y the Y origin >= 0
80      * @param g the graphics context
81      * @param e how to expand the tabs. If this value is null,
82      * tabs will be expanded as a space character.
83      * @param startOffset starting offset of the text in the document >= 0
84      * @return the X location at the end of the rendered text
85      */

86     static final int drawTabbedText(View JavaDoc view,
87                                 Segment JavaDoc s, int x, int y, Graphics JavaDoc g,
88                 TabExpander JavaDoc e, int startOffset) {
89         return drawTabbedText(view, s, x, y, g, e, startOffset, null);
90     }
91
92     // In addition to the previous method it can extend spaces for
93
// justification.
94
//
95
// all params are the same as in the preious method except the last
96
// one:
97
// @param justificationData justificationData for the row.
98
// if null not justification is needed
99
static final int drawTabbedText(View JavaDoc view,
100                                 Segment JavaDoc s, int x, int y, Graphics JavaDoc g,
101                                 TabExpander JavaDoc e, int startOffset,
102                                 int [] justificationData) {
103         JComponent JavaDoc component = getJComponent(view);
104     FontMetrics JavaDoc metrics = SwingUtilities2.getFontMetrics(component, g);
105     int nextX = x;
106     char[] txt = s.array;
107     int txtOffset = s.offset;
108     int flushLen = 0;
109     int flushIndex = s.offset;
110         int spaceAddon = 0;
111         int spaceAddonLeftoverEnd = -1;
112         int startJustifiableContent = 0;
113         int endJustifiableContent = 0;
114         if (justificationData != null) {
115             int offset = - startOffset + txtOffset;
116             View JavaDoc parent = null;
117             if (view != null
118                   && (parent = view.getParent()) != null) {
119                 offset += parent.getStartOffset();
120             }
121             spaceAddon =
122                 justificationData[Row.SPACE_ADDON];
123             spaceAddonLeftoverEnd =
124                 justificationData[Row.SPACE_ADDON_LEFTOVER_END] + offset;
125             startJustifiableContent =
126                 justificationData[Row.START_JUSTIFIABLE] + offset;
127             endJustifiableContent =
128                 justificationData[Row.END_JUSTIFIABLE] + offset;
129         }
130     int n = s.offset + s.count;
131     for (int i = txtOffset; i < n; i++) {
132             if (txt[i] == '\t'
133                 || ((spaceAddon != 0 || i <= spaceAddonLeftoverEnd)
134                     && (txt[i] == ' ')
135                     && startJustifiableContent <= i
136                     && i <= endJustifiableContent
137                     )) {
138         if (flushLen > 0) {
139             nextX = SwingUtilities2.drawChars(component, g, txt,
140                                                 flushIndex, flushLen, x, y);
141             flushLen = 0;
142         }
143         flushIndex = i + 1;
144                 if (txt[i] == '\t') {
145                     if (e != null) {
146                         nextX = (int) e.nextTabStop((float) nextX, startOffset + i - txtOffset);
147                     } else {
148                         nextX += metrics.charWidth(' ');
149                     }
150                 } else if (txt[i] == ' ') {
151                     nextX += metrics.charWidth(' ') + spaceAddon;
152                     if (i <= spaceAddonLeftoverEnd) {
153                         nextX++;
154                     }
155                 }
156         x = nextX;
157         } else if ((txt[i] == '\n') || (txt[i] == '\r')) {
158         if (flushLen > 0) {
159             nextX = SwingUtilities2.drawChars(component, g, txt,
160                                                 flushIndex, flushLen, x, y);
161             flushLen = 0;
162         }
163         flushIndex = i + 1;
164         x = nextX;
165         } else {
166         flushLen += 1;
167         }
168     }
169     if (flushLen > 0) {
170         nextX = SwingUtilities2.drawChars(component, g,txt, flushIndex,
171                                               flushLen, x, y);
172     }
173     return nextX;
174     }
175
176     /**
177      * Determines the width of the given segment of text taking tabs
178      * into consideration. This is implemented in a 1.1 style coordinate
179      * system where ints are used and 72dpi is assumed.
180      *
181      * @param s the source of the text
182      * @param metrics the font metrics to use for the calculation
183      * @param x the X origin >= 0
184      * @param e how to expand the tabs. If this value is null,
185      * tabs will be expanded as a space character.
186      * @param startOffset starting offset of the text in the document >= 0
187      * @return the width of the text
188      */

189     public static final int getTabbedTextWidth(Segment JavaDoc s, FontMetrics JavaDoc metrics, int x,
190                            TabExpander JavaDoc e, int startOffset) {
191         return getTabbedTextWidth(null, s, metrics, x, e, startOffset, null);
192     }
193
194
195     // In addition to the previous method it can extend spaces for
196
// justification.
197
//
198
// all params are the same as in the preious method except the last
199
// one:
200
// @param justificationData justificationData for the row.
201
// if null not justification is needed
202
static final int getTabbedTextWidth(View JavaDoc view, Segment JavaDoc s, FontMetrics JavaDoc metrics, int x,
203                                         TabExpander JavaDoc e, int startOffset,
204                                         int[] justificationData) {
205     int nextX = x;
206     char[] txt = s.array;
207     int txtOffset = s.offset;
208     int n = s.offset + s.count;
209         int charCount = 0;
210         int spaceAddon = 0;
211         int spaceAddonLeftoverEnd = -1;
212         int startJustifiableContent = 0;
213         int endJustifiableContent = 0;
214         if (justificationData != null) {
215             int offset = - startOffset + txtOffset;
216             View JavaDoc parent = null;
217             if (view != null
218                   && (parent = view.getParent()) != null) {
219                 offset += parent.getStartOffset();
220             }
221             spaceAddon =
222                 justificationData[Row.SPACE_ADDON];
223             spaceAddonLeftoverEnd =
224                 justificationData[Row.SPACE_ADDON_LEFTOVER_END] + offset;
225             startJustifiableContent =
226                 justificationData[Row.START_JUSTIFIABLE] + offset;
227             endJustifiableContent =
228                 justificationData[Row.END_JUSTIFIABLE] + offset;
229         }
230
231     for (int i = txtOffset; i < n; i++) {
232             if (txt[i] == '\t'
233                 || ((spaceAddon != 0 || i <= spaceAddonLeftoverEnd)
234                     && (txt[i] == ' ')
235                     && startJustifiableContent <= i
236                     && i <= endJustifiableContent
237                     )) {
238                 nextX += metrics.charsWidth(txt, i-charCount, charCount);
239                 charCount = 0;
240                 if (txt[i] == '\t') {
241                     if (e != null) {
242                         nextX = (int) e.nextTabStop((float) nextX,
243                                                     startOffset + i - txtOffset);
244                     } else {
245                         nextX += metrics.charWidth(' ');
246                     }
247                 } else if (txt[i] == ' ') {
248                     nextX += metrics.charWidth(' ') + spaceAddon;
249                     if (i <= spaceAddonLeftoverEnd) {
250                         nextX++;
251                     }
252                 }
253         } else if(txt[i] == '\n') {
254         // Ignore newlines, they take up space and we shouldn't be
255
// counting them.
256
nextX += metrics.charsWidth(txt, i - charCount, charCount);
257                 charCount = 0;
258         } else {
259                 charCount++;
260     }
261     }
262         nextX += metrics.charsWidth(txt, n - charCount, charCount);
263     return nextX - x;
264     }
265
266     /**
267      * Determines the relative offset into the given text that
268      * best represents the given span in the view coordinate
269      * system. This is implemented in a 1.1 style coordinate
270      * system where ints are used and 72dpi is assumed.
271      *
272      * @param s the source of the text
273      * @param metrics the font metrics to use for the calculation
274      * @param x0 the starting view location representing the start
275      * of the given text >= 0.
276      * @param x the target view location to translate to an
277      * offset into the text >= 0.
278      * @param e how to expand the tabs. If this value is null,
279      * tabs will be expanded as a space character.
280      * @param startOffset starting offset of the text in the document >= 0
281      * @return the offset into the text >= 0
282      */

283     public static final int getTabbedTextOffset(Segment JavaDoc s, FontMetrics JavaDoc metrics,
284                          int x0, int x, TabExpander JavaDoc e,
285                          int startOffset) {
286     return getTabbedTextOffset(s, metrics, x0, x, e, startOffset, true);
287     }
288
289     static final int getTabbedTextOffset(View JavaDoc view, Segment JavaDoc s, FontMetrics JavaDoc metrics,
290                                          int x0, int x, TabExpander JavaDoc e,
291                                          int startOffset,
292                                          int[] justificationData) {
293         return getTabbedTextOffset(view, s, metrics, x0, x, e, startOffset, true,
294                                    justificationData);
295     }
296
297     public static final int getTabbedTextOffset(Segment JavaDoc s,
298                         FontMetrics JavaDoc metrics,
299                         int x0, int x, TabExpander JavaDoc e,
300                         int startOffset,
301                         boolean round) {
302         return getTabbedTextOffset(null, s, metrics, x0, x, e, startOffset, round, null);
303     }
304
305     // In addition to the previous method it can extend spaces for
306
// justification.
307
//
308
// all params are the same as in the preious method except the last
309
// one:
310
// @param justificationData justificationData for the row.
311
// if null not justification is needed
312
static final int getTabbedTextOffset(View JavaDoc view,
313                                          Segment JavaDoc s,
314                                          FontMetrics JavaDoc metrics,
315                                          int x0, int x, TabExpander JavaDoc e,
316                                          int startOffset,
317                                          boolean round,
318                                          int[] justificationData) {
319         if (x0 >= x) {
320             // x before x0, return.
321
return 0;
322         }
323     int currX = x0;
324     int nextX = currX;
325     // s may be a shared segment, so it is copied prior to calling
326
// the tab expander
327
char[] txt = s.array;
328     int txtOffset = s.offset;
329     int txtCount = s.count;
330         int spaceAddon = 0 ;
331         int spaceAddonLeftoverEnd = -1;
332         int startJustifiableContent = 0 ;
333         int endJustifiableContent = 0;
334         if (justificationData != null) {
335             int offset = - startOffset + txtOffset;
336             View JavaDoc parent = null;
337             if (view != null
338                   && (parent = view.getParent()) != null) {
339                 offset += parent.getStartOffset();
340             }
341             spaceAddon =
342                 justificationData[Row.SPACE_ADDON];
343             spaceAddonLeftoverEnd =
344                 justificationData[Row.SPACE_ADDON_LEFTOVER_END] + offset;
345             startJustifiableContent =
346                 justificationData[Row.START_JUSTIFIABLE] + offset;
347             endJustifiableContent =
348                 justificationData[Row.END_JUSTIFIABLE] + offset;
349         }
350     int n = s.offset + s.count;
351     for (int i = s.offset; i < n; i++) {
352             if (txt[i] == '\t'
353                 || ((spaceAddon != 0 || i <= spaceAddonLeftoverEnd)
354                     && (txt[i] == ' ')
355                     && startJustifiableContent <= i
356                     && i <= endJustifiableContent
357                     )){
358                 if (txt[i] == '\t') {
359                     if (e != null) {
360                         nextX = (int) e.nextTabStop((float) nextX,
361                                                     startOffset + i - txtOffset);
362                     } else {
363                         nextX += metrics.charWidth(' ');
364                     }
365                 } else if (txt[i] == ' ') {
366                     nextX += metrics.charWidth(' ') + spaceAddon;
367                     if (i <= spaceAddonLeftoverEnd) {
368                         nextX++;
369                     }
370                 }
371         } else {
372         nextX += metrics.charWidth(txt[i]);
373         }
374         if ((x >= currX) && (x < nextX)) {
375         // found the hit position... return the appropriate side
376
if ((round == false) || ((x - currX) < (nextX - x))) {
377             return i - txtOffset;
378         } else {
379             return i + 1 - txtOffset;
380         }
381         }
382         currX = nextX;
383     }
384
385     // didn't find, return end offset
386
return txtCount;
387     }
388
389     /**
390      * Determine where to break the given text to fit
391      * within the given span. This tries to find a word boundary.
392      * @param s the source of the text
393      * @param metrics the font metrics to use for the calculation
394      * @param x0 the starting view location representing the start
395      * of the given text.
396      * @param x the target view location to translate to an
397      * offset into the text.
398      * @param e how to expand the tabs. If this value is null,
399      * tabs will be expanded as a space character.
400      * @param startOffset starting offset in the document of the text
401      * @return the offset into the given text
402      */

403     public static final int getBreakLocation(Segment JavaDoc s, FontMetrics JavaDoc metrics,
404                          int x0, int x, TabExpander JavaDoc e,
405                          int startOffset) {
406     char[] txt = s.array;
407     int txtOffset = s.offset;
408     int txtCount = s.count;
409     int index = Utilities.getTabbedTextOffset(s, metrics, x0, x,
410                           e, startOffset, false);
411         
412
413         if (index >= txtCount - 1) {
414             return txtCount;
415         }
416
417     for (int i = txtOffset + index; i >= txtOffset; i--) {
418         char ch = txt[i];
419             if (ch < 256) {
420                 // break on whitespace
421
if (Character.isWhitespace(ch)) {
422                     index = i - txtOffset + 1;
423                     break;
424                 }
425             } else {
426                 // a multibyte char found; use BreakIterator to find line break
427
BreakIterator bit = BreakIterator.getLineInstance();
428                 bit.setText(s);
429                 int breakPos = bit.preceding(i + 1);
430                 if (breakPos > txtOffset) {
431                     index = breakPos - txtOffset;
432                 }
433                 break;
434             }
435     }
436     return index;
437     }
438
439     /**
440      * Determines the starting row model position of the row that contains
441      * the specified model position. The component given must have a
442      * size to compute the result. If the component doesn't have a size
443      * a value of -1 will be returned.
444      *
445      * @param c the editor
446      * @param offs the offset in the document >= 0
447      * @return the position >= 0 if the request can be computed, otherwise
448      * a value of -1 will be returned.
449      * @exception BadLocationException if the offset is out of range
450      */

451     public static final int getRowStart(JTextComponent JavaDoc c, int offs) throws BadLocationException JavaDoc {
452     Rectangle JavaDoc r = c.modelToView(offs);
453     if (r == null) {
454         return -1;
455     }
456     int lastOffs = offs;
457     int y = r.y;
458     while ((r != null) && (y == r.y)) {
459         offs = lastOffs;
460         lastOffs -= 1;
461         r = (lastOffs >= 0) ? c.modelToView(lastOffs) : null;
462     }
463     return offs;
464     }
465
466     /**
467      * Determines the ending row model position of the row that contains
468      * the specified model position. The component given must have a
469      * size to compute the result. If the component doesn't have a size
470      * a value of -1 will be returned.
471      *
472      * @param c the editor
473      * @param offs the offset in the document >= 0
474      * @return the position >= 0 if the request can be computed, otherwise
475      * a value of -1 will be returned.
476      * @exception BadLocationException if the offset is out of range
477      */

478     public static final int getRowEnd(JTextComponent JavaDoc c, int offs) throws BadLocationException JavaDoc {
479     Rectangle JavaDoc r = c.modelToView(offs);
480     if (r == null) {
481         return -1;
482     }
483     int n = c.getDocument().getLength();
484     int lastOffs = offs;
485     int y = r.y;
486     while ((r != null) && (y == r.y)) {
487         offs = lastOffs;
488         lastOffs += 1;
489         r = (lastOffs <= n) ? c.modelToView(lastOffs) : null;
490     }
491     return offs;
492     }
493
494     /**
495      * Determines the position in the model that is closest to the given
496      * view location in the row above. The component given must have a
497      * size to compute the result. If the component doesn't have a size
498      * a value of -1 will be returned.
499      *
500      * @param c the editor
501      * @param offs the offset in the document >= 0
502      * @param x the X coordinate >= 0
503      * @return the position >= 0 if the request can be computed, otherwise
504      * a value of -1 will be returned.
505      * @exception BadLocationException if the offset is out of range
506      */

507     public static final int getPositionAbove(JTextComponent JavaDoc c, int offs, int x) throws BadLocationException JavaDoc {
508     int lastOffs = getRowStart(c, offs) - 1;
509     if (lastOffs < 0) {
510         return -1;
511     }
512     int bestSpan = Integer.MAX_VALUE;
513     int y = 0;
514     Rectangle JavaDoc r = null;
515     if (lastOffs >= 0) {
516         r = c.modelToView(lastOffs);
517         y = r.y;
518     }
519     while ((r != null) && (y == r.y)) {
520         int span = Math.abs(r.x - x);
521         if (span < bestSpan) {
522         offs = lastOffs;
523         bestSpan = span;
524         }
525         lastOffs -= 1;
526         r = (lastOffs >= 0) ? c.modelToView(lastOffs) : null;
527     }
528     return offs;
529     }
530
531     /**
532      * Determines the position in the model that is closest to the given
533      * view location in the row below. The component given must have a
534      * size to compute the result. If the component doesn't have a size
535      * a value of -1 will be returned.
536      *
537      * @param c the editor
538      * @param offs the offset in the document >= 0
539      * @param x the X coordinate >= 0
540      * @return the position >= 0 if the request can be computed, otherwise
541      * a value of -1 will be returned.
542      * @exception BadLocationException if the offset is out of range
543      */

544     public static final int getPositionBelow(JTextComponent JavaDoc c, int offs, int x) throws BadLocationException JavaDoc {
545     int lastOffs = getRowEnd(c, offs) + 1;
546     if (lastOffs <= 0) {
547         return -1;
548     }
549     int bestSpan = Integer.MAX_VALUE;
550     int n = c.getDocument().getLength();
551     int y = 0;
552     Rectangle JavaDoc r = null;
553     if (lastOffs <= n) {
554         r = c.modelToView(lastOffs);
555         y = r.y;
556     }
557     while ((r != null) && (y == r.y)) {
558         int span = Math.abs(x - r.x);
559         if (span < bestSpan) {
560         offs = lastOffs;
561         bestSpan = span;
562         }
563         lastOffs += 1;
564         r = (lastOffs <= n) ? c.modelToView(lastOffs) : null;
565     }
566     return offs;
567     }
568
569     /**
570      * Determines the start of a word for the given model location.
571      * Uses BreakIterator.getWordInstance() to actually get the words.
572      *
573      * @param c the editor
574      * @param offs the offset in the document >= 0
575      * @return the location in the model of the word start >= 0
576      * @exception BadLocationException if the offset is out of range
577      */

578     public static final int getWordStart(JTextComponent JavaDoc c, int offs) throws BadLocationException JavaDoc {
579     Document JavaDoc doc = c.getDocument();
580     Element JavaDoc line = getParagraphElement(c, offs);
581     if (line == null) {
582         throw new BadLocationException JavaDoc("No word at " + offs, offs);
583     }
584     int lineStart = line.getStartOffset();
585     int lineEnd = Math.min(line.getEndOffset(), doc.getLength());
586     
587         Segment JavaDoc seg = SegmentCache.getSharedSegment();
588         doc.getText(lineStart, lineEnd - lineStart, seg);
589         if(seg.count > 0) {
590             BreakIterator words = BreakIterator.getWordInstance(c.getLocale());
591             words.setText(seg);
592             int wordPosition = seg.offset + offs - lineStart;
593             if(wordPosition >= words.last()) {
594                 wordPosition = words.last() - 1;
595             }
596             words.following(wordPosition);
597             offs = lineStart + words.previous() - seg.offset;
598         }
599         SegmentCache.releaseSharedSegment(seg);
600     return offs;
601     }
602
603     /**
604      * Determines the end of a word for the given location.
605      * Uses BreakIterator.getWordInstance() to actually get the words.
606      *
607      * @param c the editor
608      * @param offs the offset in the document >= 0
609      * @return the location in the model of the word end >= 0
610      * @exception BadLocationException if the offset is out of range
611      */

612     public static final int getWordEnd(JTextComponent JavaDoc c, int offs) throws BadLocationException JavaDoc {
613     Document JavaDoc doc = c.getDocument();
614     Element JavaDoc line = getParagraphElement(c, offs);
615     if (line == null) {
616         throw new BadLocationException JavaDoc("No word at " + offs, offs);
617     }
618     int lineStart = line.getStartOffset();
619     int lineEnd = Math.min(line.getEndOffset(), doc.getLength());
620     
621         Segment JavaDoc seg = SegmentCache.getSharedSegment();
622         doc.getText(lineStart, lineEnd - lineStart, seg);
623         if(seg.count > 0) {
624             BreakIterator words = BreakIterator.getWordInstance(c.getLocale());
625         words.setText(seg);
626         int wordPosition = offs - lineStart + seg.offset;
627         if(wordPosition >= words.last()) {
628         wordPosition = words.last() - 1;
629         }
630         offs = lineStart + words.following(wordPosition) - seg.offset;
631     }
632         SegmentCache.releaseSharedSegment(seg);
633     return offs;
634     }
635
636     /**
637      * Determines the start of the next word for the given location.
638      * Uses BreakIterator.getWordInstance() to actually get the words.
639      *
640      * @param c the editor
641      * @param offs the offset in the document >= 0
642      * @return the location in the model of the word start >= 0
643      * @exception BadLocationException if the offset is out of range
644      */

645     public static final int getNextWord(JTextComponent JavaDoc c, int offs) throws BadLocationException JavaDoc {
646     int nextWord;
647     Element JavaDoc line = getParagraphElement(c, offs);
648     for (nextWord = getNextWordInParagraph(c, line, offs, false);
649          nextWord == BreakIterator.DONE;
650          nextWord = getNextWordInParagraph(c, line, offs, true)) {
651
652         // didn't find in this line, try the next line
653
offs = line.getEndOffset();
654         line = getParagraphElement(c, offs);
655     }
656     return nextWord;
657     }
658
659     /**
660      * Finds the next word in the given elements text. The first
661      * parameter allows searching multiple paragraphs where even
662      * the first offset is desired.
663      * Returns the offset of the next word, or BreakIterator.DONE
664      * if there are no more words in the element.
665      */

666     static int getNextWordInParagraph(JTextComponent JavaDoc c, Element JavaDoc line, int offs, boolean first) throws BadLocationException JavaDoc {
667     if (line == null) {
668         throw new BadLocationException JavaDoc("No more words", offs);
669     }
670     Document JavaDoc doc = line.getDocument();
671     int lineStart = line.getStartOffset();
672     int lineEnd = Math.min(line.getEndOffset(), doc.getLength());
673     if ((offs >= lineEnd) || (offs < lineStart)) {
674         throw new BadLocationException JavaDoc("No more words", offs);
675     }
676         Segment JavaDoc seg = SegmentCache.getSharedSegment();
677         doc.getText(lineStart, lineEnd - lineStart, seg);
678         BreakIterator words = BreakIterator.getWordInstance(c.getLocale());
679     words.setText(seg);
680     if ((first && (words.first() == (seg.offset + offs - lineStart))) &&
681         (! Character.isWhitespace(seg.array[words.first()]))) {
682
683         return offs;
684     }
685     int wordPosition = words.following(seg.offset + offs - lineStart);
686     if ((wordPosition == BreakIterator.DONE) ||
687         (wordPosition >= seg.offset + seg.count)) {
688         // there are no more words on this line.
689
return BreakIterator.DONE;
690     }
691     // if we haven't shot past the end... check to
692
// see if the current boundary represents whitespace.
693
// if so, we need to try again
694
char ch = seg.array[wordPosition];
695     if (! Character.isWhitespace(ch)) {
696         return lineStart + wordPosition - seg.offset;
697     }
698
699     // it was whitespace, try again. The assumption
700
// is that it must be a word start if the last
701
// one had whitespace following it.
702
wordPosition = words.next();
703     if (wordPosition != BreakIterator.DONE) {
704         offs = lineStart + wordPosition - seg.offset;
705         if (offs != lineEnd) {
706         return offs;
707         }
708     }
709         SegmentCache.releaseSharedSegment(seg);
710     return BreakIterator.DONE;
711     }
712
713
714     /**
715      * Determine the start of the prev word for the given location.
716      * Uses BreakIterator.getWordInstance() to actually get the words.
717      *
718      * @param c the editor
719      * @param offs the offset in the document >= 0
720      * @return the location in the model of the word start >= 0
721      * @exception BadLocationException if the offset is out of range
722      */

723     public static final int getPreviousWord(JTextComponent JavaDoc c, int offs) throws BadLocationException JavaDoc {
724     int prevWord;
725     Element JavaDoc line = getParagraphElement(c, offs);
726     for (prevWord = getPrevWordInParagraph(c, line, offs);
727          prevWord == BreakIterator.DONE;
728          prevWord = getPrevWordInParagraph(c, line, offs)) {
729
730         // didn't find in this line, try the prev line
731
offs = line.getStartOffset() - 1;
732         line = getParagraphElement(c, offs);
733     }
734     return prevWord;
735     }
736
737     /**
738      * Finds the previous word in the given elements text. The first
739      * parameter allows searching multiple paragraphs where even
740      * the first offset is desired.
741      * Returns the offset of the next word, or BreakIterator.DONE
742      * if there are no more words in the element.
743      */

744     static int getPrevWordInParagraph(JTextComponent JavaDoc c, Element JavaDoc line, int offs) throws BadLocationException JavaDoc {
745     if (line == null) {
746         throw new BadLocationException JavaDoc("No more words", offs);
747     }
748     Document JavaDoc doc = line.getDocument();
749     int lineStart = line.getStartOffset();
750     int lineEnd = line.getEndOffset();
751     if ((offs > lineEnd) || (offs < lineStart)) {
752         throw new BadLocationException JavaDoc("No more words", offs);
753     }
754         Segment JavaDoc seg = SegmentCache.getSharedSegment();
755     doc.getText(lineStart, lineEnd - lineStart, seg);
756         BreakIterator words = BreakIterator.getWordInstance(c.getLocale());
757     words.setText(seg);
758     if (words.following(seg.offset + offs - lineStart) == BreakIterator.DONE) {
759         words.last();
760     }
761     int wordPosition = words.previous();
762     if (wordPosition == (seg.offset + offs - lineStart)) {
763         wordPosition = words.previous();
764     }
765
766     if (wordPosition == BreakIterator.DONE) {
767         // there are no more words on this line.
768
return BreakIterator.DONE;
769     }
770     // if we haven't shot past the end... check to
771
// see if the current boundary represents whitespace.
772
// if so, we need to try again
773
char ch = seg.array[wordPosition];
774     if (! Character.isWhitespace(ch)) {
775         return lineStart + wordPosition - seg.offset;
776     }
777
778     // it was whitespace, try again. The assumption
779
// is that it must be a word start if the last
780
// one had whitespace following it.
781
wordPosition = words.previous();
782     if (wordPosition != BreakIterator.DONE) {
783         return lineStart + wordPosition - seg.offset;
784     }
785         SegmentCache.releaseSharedSegment(seg);
786     return BreakIterator.DONE;
787     }
788
789     /**
790      * Determines the element to use for a paragraph/line.
791      *
792      * @param c the editor
793      * @param offs the starting offset in the document >= 0
794      * @return the element
795      */

796     public static final Element JavaDoc getParagraphElement(JTextComponent JavaDoc c, int offs) {
797     Document JavaDoc doc = c.getDocument();
798     if (doc instanceof StyledDocument JavaDoc) {
799         return ((StyledDocument JavaDoc)doc).getParagraphElement(offs);
800     }
801     Element JavaDoc map = doc.getDefaultRootElement();
802     int index = map.getElementIndex(offs);
803     Element JavaDoc paragraph = map.getElement(index);
804     if ((offs >= paragraph.getStartOffset()) && (offs < paragraph.getEndOffset())) {
805         return paragraph;
806     }
807     return null;
808     }
809
810     static boolean isComposedTextElement(Document JavaDoc doc, int offset) {
811     Element JavaDoc elem = doc.getDefaultRootElement();
812     while (!elem.isLeaf()) {
813         elem = elem.getElement(elem.getElementIndex(offset));
814     }
815     return isComposedTextElement(elem);
816     }
817
818     static boolean isComposedTextElement(Element JavaDoc elem) {
819         AttributeSet JavaDoc as = elem.getAttributes();
820     return isComposedTextAttributeDefined(as);
821     }
822
823     static boolean isComposedTextAttributeDefined(AttributeSet JavaDoc as) {
824     return ((as != null) &&
825             (as.isDefined(StyleConstants.ComposedTextAttribute)));
826     }
827
828     /**
829      * Draws the given composed text passed from an input method.
830      *
831      * @param view View hosting text
832      * @param attr the attributes containing the composed text
833      * @param g the graphics context
834      * @param x the X origin
835      * @param y the Y origin
836      * @param p0 starting offset in the composed text to be rendered
837      * @param p1 ending offset in the composed text to be rendered
838      * @return the new insertion position
839      */

840     static int drawComposedText(View JavaDoc view, AttributeSet JavaDoc attr, Graphics JavaDoc g,
841                                 int x, int y, int p0, int p1)
842                                      throws BadLocationException JavaDoc {
843         Graphics2D JavaDoc g2d = (Graphics2D JavaDoc)g;
844         AttributedString as = (AttributedString)attr.getAttribute(
845         StyleConstants.ComposedTextAttribute);
846     as.addAttribute(TextAttribute.FONT, g.getFont());
847
848     if (p0 >= p1)
849         return x;
850
851     AttributedCharacterIterator aci = as.getIterator(null, p0, p1);
852         return x + (int)SwingUtilities2.drawString(
853                              getJComponent(view), g2d,aci,x,y);
854     }
855
856     /**
857      * Paints the composed text in a GlyphView
858      */

859     static void paintComposedText(Graphics JavaDoc g, Rectangle JavaDoc alloc, GlyphView JavaDoc v) {
860     if (g instanceof Graphics2D JavaDoc) {
861         Graphics2D JavaDoc g2d = (Graphics2D JavaDoc) g;
862         int p0 = v.getStartOffset();
863         int p1 = v.getEndOffset();
864         AttributeSet JavaDoc attrSet = v.getElement().getAttributes();
865         AttributedString as =
866         (AttributedString)attrSet.getAttribute(StyleConstants.ComposedTextAttribute);
867         int start = v.getElement().getStartOffset();
868         int y = alloc.y + alloc.height - (int)v.getGlyphPainter().getDescent(v);
869         int x = alloc.x;
870         
871         //Add text attributes
872
as.addAttribute(TextAttribute.FONT, v.getFont());
873         as.addAttribute(TextAttribute.FOREGROUND, v.getForeground());
874         if (StyleConstants.isBold(v.getAttributes())) {
875         as.addAttribute(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD);
876         }
877         if (StyleConstants.isItalic(v.getAttributes())) {
878         as.addAttribute(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE);
879         }
880         if (v.isUnderline()) {
881         as.addAttribute(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
882         }
883         if (v.isStrikeThrough()) {
884         as.addAttribute(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON);
885         }
886         if (v.isSuperscript()) {
887         as.addAttribute(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUPER);
888         }
889         if (v.isSubscript()) {
890         as.addAttribute(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUB);
891         }
892     
893         // draw
894
AttributedCharacterIterator aci = as.getIterator(null, p0 - start, p1 - start);
895             SwingUtilities2.drawString(getJComponent(v),
896                                        g2d,aci,x,y);
897     }
898     }
899
900     /*
901      * Convenience function for determining ComponentOrientation. Helps us
902      * avoid having Munge directives throughout the code.
903      */

904     static boolean isLeftToRight( java.awt.Component JavaDoc c ) {
905         return c.getComponentOrientation().isLeftToRight();
906     }
907
908
909     /**
910      * Provides a way to determine the next visually represented model
911      * location that one might place a caret. Some views may not be visible,
912      * they might not be in the same order found in the model, or they just
913      * might not allow access to some of the locations in the model.
914      * <p>
915      * This implementation assumes the views are layed out in a logical
916      * manner. That is, that the view at index x + 1 is visually after
917      * the View at index x, and that the View at index x - 1 is visually
918      * before the View at x. There is support for reversing this behavior
919      * only if the passed in <code>View</code> is an instance of
920      * <code>CompositeView</code>. The <code>CompositeView</code>
921      * must then override the <code>flipEastAndWestAtEnds</code> method.
922      *
923      * @param v View to query
924      * @param pos the position to convert >= 0
925      * @param a the allocated region to render into
926      * @param direction the direction from the current position that can
927      * be thought of as the arrow keys typically found on a keyboard;
928      * this may be one of the following:
929      * <ul>
930      * <li><code>SwingConstants.WEST</code>
931      * <li><code>SwingConstants.EAST</code>
932      * <li><code>SwingConstants.NORTH</code>
933      * <li><code>SwingConstants.SOUTH</code>
934      * </ul>
935      * @param biasRet an array contain the bias that was checked
936      * @return the location within the model that best represents the next
937      * location visual position
938      * @exception BadLocationException
939      * @exception IllegalArgumentException if <code>direction</code> is invalid
940      */

941     static int getNextVisualPositionFrom(View JavaDoc v, int pos, Position.Bias JavaDoc b,
942                                           Shape JavaDoc alloc, int direction,
943                                           Position.Bias JavaDoc[] biasRet)
944                              throws BadLocationException JavaDoc {
945         if (v.getViewCount() == 0) {
946             // Nothing to do.
947
return pos;
948         }
949         boolean top = (direction == SwingConstants.NORTH ||
950                        direction == SwingConstants.WEST);
951         int retValue;
952         if (pos == -1) {
953             // Start from the first View.
954
int childIndex = (top) ? v.getViewCount() - 1 : 0;
955             View JavaDoc child = v.getView(childIndex);
956             Shape JavaDoc childBounds = v.getChildAllocation(childIndex, alloc);
957             retValue = child.getNextVisualPositionFrom(pos, b, childBounds,
958                                                        direction, biasRet);
959         if (retValue == -1 && !top && v.getViewCount() > 1) {
960         // Special case that should ONLY happen if first view
961
// isn't valid (can happen when end position is put at
962
// beginning of line.
963
child = v.getView(1);
964                 childBounds = v.getChildAllocation(1, alloc);
965         retValue = child.getNextVisualPositionFrom(-1, biasRet[0],
966                                                            childBounds,
967                                                            direction, biasRet);
968         }
969         }
970         else {
971             int increment = (top) ? -1 : 1;
972             int childIndex;
973             if (b == Position.Bias.Backward && pos > 0) {
974                 childIndex = v.getViewIndex(pos - 1, Position.Bias.Forward);
975             }
976             else {
977                 childIndex = v.getViewIndex(pos, Position.Bias.Forward);
978             }
979             View JavaDoc child = v.getView(childIndex);
980             Shape JavaDoc childBounds = v.getChildAllocation(childIndex, alloc);
981             retValue = child.getNextVisualPositionFrom(pos, b, childBounds,
982                                                        direction, biasRet);
983             if ((direction == SwingConstants.EAST ||
984                  direction == SwingConstants.WEST) &&
985                 (v instanceof CompositeView JavaDoc) &&
986                 ((CompositeView JavaDoc)v).flipEastAndWestAtEnds(pos, b)) {
987                 increment *= -1;
988             }
989             childIndex += increment;
990             if (retValue == -1 && childIndex >= 0 &&
991                                   childIndex < v.getViewCount()) {
992                 child = v.getView(childIndex);
993                 childBounds = v.getChildAllocation(childIndex, alloc);
994                 retValue = child.getNextVisualPositionFrom(
995                                      -1, b, childBounds, direction, biasRet);
996                 // If there is a bias change, it is a fake position
997
// and we should skip it. This is usually the result
998
// of two elements side be side flowing the same way.
999
if (retValue == pos && biasRet[0] != b) {
1000                    return getNextVisualPositionFrom(v, pos, biasRet[0],
1001                                                     alloc, direction,
1002                                                     biasRet);
1003                }
1004            }
1005            else if (retValue != -1 && biasRet[0] != b &&
1006                     ((increment == 1 && child.getEndOffset() == retValue) ||
1007                      (increment == -1 &&
1008                       child.getStartOffset() == retValue)) &&
1009                     childIndex >= 0 && childIndex < v.getViewCount()) {
1010                // Reached the end of a view, make sure the next view
1011
// is a different direction.
1012
child = v.getView(childIndex);
1013                childBounds = v.getChildAllocation(childIndex, alloc);
1014                Position.Bias JavaDoc originalBias = biasRet[0];
1015                int nextPos = child.getNextVisualPositionFrom(
1016                                    -1, b, childBounds, direction, biasRet);
1017                if (biasRet[0] == b) {
1018                    retValue = nextPos;
1019                }
1020                else {
1021                    biasRet[0] = originalBias;
1022                }
1023            }
1024        }
1025        return retValue;
1026    }
1027}
1028
Popular Tags