44 |
import java.awt.Graphics; |
import java.awt.Graphics; |
45 |
import java.awt.Rectangle; |
import java.awt.Rectangle; |
46 |
import java.awt.Shape; |
import java.awt.Shape; |
47 |
|
import java.text.BreakIterator; |
48 |
|
|
49 |
|
import javax.swing.SwingConstants; |
50 |
|
import javax.swing.event.DocumentEvent; |
51 |
|
import javax.swing.text.Position.Bias; |
52 |
|
|
53 |
/** |
/** |
54 |
* Renders a run of styled text. This {@link View} subclass paints the |
* Renders a run of styled text. This {@link View} subclass paints the |
57 |
* |
* |
58 |
* @author Roman Kennke (roman@kennke.org) |
* @author Roman Kennke (roman@kennke.org) |
59 |
*/ |
*/ |
60 |
public class GlyphView |
public class GlyphView extends View implements TabableView, Cloneable |
|
extends View |
|
|
implements TabableView, Cloneable |
|
61 |
{ |
{ |
62 |
|
|
63 |
/** |
/** |
74 |
} |
} |
75 |
|
|
76 |
/** |
/** |
77 |
|
* Returns the ascent of the font that is used by this glyph painter. |
78 |
|
* |
79 |
|
* @param v the glyph view |
80 |
|
* |
81 |
|
* @return the ascent of the font that is used by this glyph painter |
82 |
|
*/ |
83 |
|
public abstract float getAscent(GlyphView v); |
84 |
|
|
85 |
|
/** |
86 |
|
* Returns the descent of the font that is used by this glyph painter. |
87 |
|
* |
88 |
|
* @param v the glyph view |
89 |
|
* |
90 |
|
* @return the descent of the font that is used by this glyph painter |
91 |
|
*/ |
92 |
|
public abstract float getDescent(GlyphView v); |
93 |
|
|
94 |
|
/** |
95 |
* Returns the full height of the rendered text. |
* Returns the full height of the rendered text. |
96 |
* |
* |
97 |
* @return the full height of the rendered text |
* @return the full height of the rendered text |
98 |
*/ |
*/ |
99 |
public abstract float getHeight(GlyphView view); |
public abstract float getHeight(GlyphView view); |
100 |
|
|
101 |
|
/** |
102 |
|
* Determines the model offset, so that the text between <code>p0</code> |
103 |
|
* and this offset fits within the span starting at <code>x</code> with |
104 |
|
* the length of <code>len</code>. |
105 |
|
* |
106 |
|
* @param v the glyph view |
107 |
|
* @param p0 the starting offset in the model |
108 |
|
* @param x the start location in the view |
109 |
|
* @param len the length of the span in the view |
110 |
|
*/ |
111 |
|
public abstract int getBoundedPosition(GlyphView v, int p0, float x, |
112 |
|
float len); |
113 |
|
|
114 |
/** |
/** |
115 |
* Paints the glyphs. |
* Paints the glyphs. |
116 |
* |
* |
131 |
* @param view the glyph view |
* @param view the glyph view |
132 |
* @param pos the position of the character in the model |
* @param pos the position of the character in the model |
133 |
* @param a the area that is occupied by the view |
* @param a the area that is occupied by the view |
134 |
* @param bias either {@link Position.Bias.Forward} or |
* @param b either {@link Position.Bias#Forward} or |
135 |
* {@link Position.Bias.Backward} depending on the preferred |
* {@link Position.Bias#Backward} depending on the preferred |
136 |
* direction bias. If <code>null</code> this defaults to |
* direction bias. If <code>null</code> this defaults to |
137 |
* <code>Position.Bias.Forward</code> |
* <code>Position.Bias.Forward</code> |
138 |
* |
* |
148 |
throws BadLocationException; |
throws BadLocationException; |
149 |
|
|
150 |
/** |
/** |
151 |
|
* Maps a visual position into a document location. |
152 |
|
* |
153 |
|
* @param v the glyph view |
154 |
|
* @param x the X coordinate of the visual position |
155 |
|
* @param y the Y coordinate of the visual position |
156 |
|
* @param a the allocated region |
157 |
|
* @param biasRet filled with the bias of the model location on method exit |
158 |
|
* |
159 |
|
* @return the model location that represents the specified view location |
160 |
|
*/ |
161 |
|
public abstract int viewToModel(GlyphView v, float x, float y, Shape a, |
162 |
|
Position.Bias[] biasRet); |
163 |
|
|
164 |
|
/** |
165 |
* Determine the span of the glyphs from location <code>p0</code> to |
* Determine the span of the glyphs from location <code>p0</code> to |
166 |
* location <code>p1</code>. If <code>te</code> is not <code>null</code>, |
* location <code>p1</code>. If <code>te</code> is not <code>null</code>, |
167 |
* then TABs are expanded using this <code>TabExpander</code>. |
* then TABs are expanded using this <code>TabExpander</code>. |
170 |
* |
* |
171 |
* @param view the glyph view |
* @param view the glyph view |
172 |
* @param p0 the starting location in the document model |
* @param p0 the starting location in the document model |
173 |
* @param p0 the end location in the document model |
* @param p1 the end location in the document model |
174 |
* @param te the tab expander to use |
* @param te the tab expander to use |
175 |
* @param x the location at which the view is located |
* @param x the location at which the view is located |
176 |
* |
* |
180 |
public abstract float getSpan(GlyphView view, int p0, int p1, |
public abstract float getSpan(GlyphView view, int p0, int p1, |
181 |
TabExpander te, float x); |
TabExpander te, float x); |
182 |
|
|
183 |
|
|
184 |
|
/** |
185 |
|
* Returns the model location that should be used to place a caret when |
186 |
|
* moving the caret through the document. |
187 |
|
* |
188 |
|
* @param v the glyph view |
189 |
|
* @param pos the current model location |
190 |
|
* @param b the bias for <code>p</code> |
191 |
|
* @param a the allocated region for the glyph view |
192 |
|
* @param direction the direction from the current position; Must be one of |
193 |
|
* {@link SwingConstants#EAST}, {@link SwingConstants#WEST}, |
194 |
|
* {@link SwingConstants#NORTH} or {@link SwingConstants#SOUTH} |
195 |
|
* @param biasRet filled with the bias of the resulting location when method |
196 |
|
* returns |
197 |
|
* |
198 |
|
* @return the location within the document that should be used to place the |
199 |
|
* caret when moving the caret around the document |
200 |
|
* |
201 |
|
* @throws BadLocationException if <code>pos</code> is an invalid model |
202 |
|
* location |
203 |
|
* @throws IllegalArgumentException if <code>d</code> is invalid |
204 |
|
*/ |
205 |
|
public int getNextVisualPositionFrom(GlyphView v, int pos, Position.Bias b, |
206 |
|
Shape a, int direction, |
207 |
|
Position.Bias[] biasRet) |
208 |
|
throws BadLocationException |
209 |
|
|
210 |
|
{ |
211 |
|
int result = pos; |
212 |
|
switch (direction) |
213 |
|
{ |
214 |
|
case SwingConstants.EAST: |
215 |
|
result = pos + 1; |
216 |
|
break; |
217 |
|
case SwingConstants.WEST: |
218 |
|
result = pos - 1; |
219 |
|
break; |
220 |
|
case SwingConstants.NORTH: |
221 |
|
case SwingConstants.SOUTH: |
222 |
|
default: |
223 |
|
// This should be handled in enclosing view, since the glyph view |
224 |
|
// does not layout vertically. |
225 |
|
break; |
226 |
|
} |
227 |
|
return result; |
228 |
|
} |
229 |
|
|
230 |
|
/** |
231 |
|
* Returns a painter that can be used to render the specified glyph view. |
232 |
|
* If this glyph painter is stateful, then it should return a new instance. |
233 |
|
* However, if this painter is stateless it should return itself. The |
234 |
|
* default behaviour is to return itself. |
235 |
|
* |
236 |
|
* @param v the glyph view for which to create a painter |
237 |
|
* @param p0 the start offset of the rendered area |
238 |
|
* @param p1 the end offset of the rendered area |
239 |
|
* |
240 |
|
* @return a painter that can be used to render the specified glyph view |
241 |
|
*/ |
242 |
|
public GlyphPainter getPainter(GlyphView v, int p0, int p1) |
243 |
|
{ |
244 |
|
return this; |
245 |
|
} |
246 |
} |
} |
247 |
|
|
248 |
/** |
/** |
284 |
if (parent instanceof TabExpander) |
if (parent instanceof TabExpander) |
285 |
tabEx = (TabExpander) parent; |
tabEx = (TabExpander) parent; |
286 |
|
|
287 |
// FIXME: Set character attributes like font-family, font-size, colors. |
// Fill the background of the text run. |
288 |
Color foreground = view.getForeground(); |
Color background = view.getBackground(); |
289 |
g.setColor(foreground); |
g.setColor(background); |
290 |
Utilities.drawTabbedText(txt, bounds.x, bounds.y, g, tabEx, |
int width = Utilities.getTabbedTextWidth(txt, g.getFontMetrics(), |
291 |
txt.offset); |
bounds.x, tabEx, txt.offset); |
292 |
|
g.fillRect(bounds.x, bounds.y, width, height); |
293 |
|
|
294 |
|
// Draw the actual text. |
295 |
|
g.setColor(view.getForeground()); |
296 |
|
g.setFont(view.getFont()); |
297 |
|
if (view.isSuperscript()) |
298 |
|
// TODO: Adjust font for superscripting. |
299 |
|
Utilities.drawTabbedText(txt, bounds.x, bounds.y - height / 2, g, tabEx, |
300 |
|
txt.offset); |
301 |
|
else if (view.isSubscript()) |
302 |
|
// TODO: Adjust font for subscripting. |
303 |
|
Utilities.drawTabbedText(txt, bounds.x, bounds.y + height / 2, g, tabEx, |
304 |
|
txt.offset); |
305 |
|
else |
306 |
|
Utilities.drawTabbedText(txt, bounds.x, bounds.y, g, tabEx, |
307 |
|
txt.offset); |
308 |
|
|
309 |
|
if (view.isStikeThrough()) |
310 |
|
{ |
311 |
|
int strikeHeight = (int) (getAscent(view) / 2); |
312 |
|
g.drawLine(bounds.x, bounds.y + strikeHeight, bounds.height + width, |
313 |
|
bounds.y + strikeHeight); |
314 |
|
} |
315 |
|
if (view.isUnderline()) |
316 |
|
{ |
317 |
|
int lineHeight = (int) getAscent(view); |
318 |
|
g.drawLine(bounds.x, bounds.y + lineHeight, bounds.height + width, |
319 |
|
bounds.y + lineHeight); |
320 |
|
} |
321 |
} |
} |
322 |
|
|
323 |
/** |
/** |
328 |
* @param view the glyph view |
* @param view the glyph view |
329 |
* @param pos the position of the character in the model |
* @param pos the position of the character in the model |
330 |
* @param a the area that is occupied by the view |
* @param a the area that is occupied by the view |
331 |
* @param bias either {@link Position.Bias.Forward} or |
* @param b either {@link Position.Bias#Forward} or |
332 |
* {@link Position.Bias.Backward} depending on the preferred |
* {@link Position.Bias#Backward} depending on the preferred |
333 |
* direction bias. If <code>null</code> this defaults to |
* direction bias. If <code>null</code> this defaults to |
334 |
* <code>Position.Bias.Forward</code> |
* <code>Position.Bias.Forward</code> |
335 |
* |
* |
365 |
* |
* |
366 |
* @param view the glyph view |
* @param view the glyph view |
367 |
* @param p0 the starting location in the document model |
* @param p0 the starting location in the document model |
368 |
* @param p0 the end location in the document model |
* @param p1 the end location in the document model |
369 |
* @param te the tab expander to use |
* @param te the tab expander to use |
370 |
* @param x the location at which the view is located |
* @param x the location at which the view is located |
371 |
* |
* |
382 |
int span = Utilities.getTabbedTextWidth(txt, fm, (int) x, te, p0); |
int span = Utilities.getTabbedTextWidth(txt, fm, (int) x, te, p0); |
383 |
return span; |
return span; |
384 |
} |
} |
385 |
|
|
386 |
|
/** |
387 |
|
* Returns the ascent of the text run that is rendered by this |
388 |
|
* <code>GlyphPainter</code>. |
389 |
|
* |
390 |
|
* @param v the glyph view |
391 |
|
* |
392 |
|
* @return the ascent of the text run that is rendered by this |
393 |
|
* <code>GlyphPainter</code> |
394 |
|
* |
395 |
|
* @see FontMetrics#getAscent() |
396 |
|
*/ |
397 |
|
public float getAscent(GlyphView v) |
398 |
|
{ |
399 |
|
Font font = v.getFont(); |
400 |
|
FontMetrics fm = v.getContainer().getFontMetrics(font); |
401 |
|
return fm.getAscent(); |
402 |
|
} |
403 |
|
|
404 |
|
/** |
405 |
|
* Returns the descent of the text run that is rendered by this |
406 |
|
* <code>GlyphPainter</code>. |
407 |
|
* |
408 |
|
* @param v the glyph view |
409 |
|
* |
410 |
|
* @return the descent of the text run that is rendered by this |
411 |
|
* <code>GlyphPainter</code> |
412 |
|
* |
413 |
|
* @see FontMetrics#getDescent() |
414 |
|
*/ |
415 |
|
public float getDescent(GlyphView v) |
416 |
|
{ |
417 |
|
Font font = v.getFont(); |
418 |
|
FontMetrics fm = v.getContainer().getFontMetrics(font); |
419 |
|
return fm.getDescent(); |
420 |
|
} |
421 |
|
|
422 |
|
/** |
423 |
|
* Determines the model offset, so that the text between <code>p0</code> |
424 |
|
* and this offset fits within the span starting at <code>x</code> with |
425 |
|
* the length of <code>len</code>. |
426 |
|
* |
427 |
|
* @param v the glyph view |
428 |
|
* @param p0 the starting offset in the model |
429 |
|
* @param x the start location in the view |
430 |
|
* @param len the length of the span in the view |
431 |
|
*/ |
432 |
|
public int getBoundedPosition(GlyphView v, int p0, float x, float len) |
433 |
|
{ |
434 |
|
TabExpander te = v.getTabExpander(); |
435 |
|
Segment txt = v.getText(p0, v.getEndOffset()); |
436 |
|
Font font = v.getFont(); |
437 |
|
FontMetrics fm = v.getContainer().getFontMetrics(font); |
438 |
|
int pos = Utilities.getTabbedTextOffset(txt, fm, (int) x, |
439 |
|
(int) (x + len), te, p0, false); |
440 |
|
return pos; |
441 |
|
} |
442 |
|
|
443 |
|
/** |
444 |
|
* Maps a visual position into a document location. |
445 |
|
* |
446 |
|
* @param v the glyph view |
447 |
|
* @param x the X coordinate of the visual position |
448 |
|
* @param y the Y coordinate of the visual position |
449 |
|
* @param a the allocated region |
450 |
|
* @param biasRet filled with the bias of the model location on method exit |
451 |
|
* |
452 |
|
* @return the model location that represents the specified view location |
453 |
|
*/ |
454 |
|
public int viewToModel(GlyphView v, float x, float y, Shape a, |
455 |
|
Bias[] biasRet) |
456 |
|
{ |
457 |
|
Rectangle b = a.getBounds(); |
458 |
|
assert b.contains(x, y) : "The coordinates are expected to be within the " |
459 |
|
+ "view's bounds: x=" + x + ", y=" + y |
460 |
|
+ "a=" + a; |
461 |
|
int pos = getBoundedPosition(v, v.getStartOffset(), b.x, x - b.x); |
462 |
|
return pos; |
463 |
|
} |
464 |
} |
} |
465 |
|
|
466 |
/** |
/** |
469 |
GlyphPainter glyphPainter; |
GlyphPainter glyphPainter; |
470 |
|
|
471 |
/** |
/** |
472 |
|
* The start offset within the document for this view. |
473 |
|
*/ |
474 |
|
int startOffset; |
475 |
|
|
476 |
|
/** |
477 |
|
* The end offset within the document for this view. |
478 |
|
*/ |
479 |
|
int endOffset; |
480 |
|
|
481 |
|
/** |
482 |
* Creates a new <code>GlyphView</code> for the given <code>Element</code>. |
* Creates a new <code>GlyphView</code> for the given <code>Element</code>. |
483 |
* |
* |
484 |
* @param element the element that is rendered by this GlyphView |
* @param element the element that is rendered by this GlyphView |
486 |
public GlyphView(Element element) |
public GlyphView(Element element) |
487 |
{ |
{ |
488 |
super(element); |
super(element); |
489 |
|
startOffset = element.getStartOffset(); |
490 |
|
endOffset = element.getEndOffset(); |
491 |
} |
} |
492 |
|
|
493 |
/** |
/** |
550 |
*/ |
*/ |
551 |
public float getPreferredSpan(int axis) |
public float getPreferredSpan(int axis) |
552 |
{ |
{ |
553 |
Element el = getElement(); |
float span = 0; |
554 |
checkPainter(); |
checkPainter(); |
555 |
GlyphPainter painter = getGlyphPainter(); |
GlyphPainter painter = getGlyphPainter(); |
556 |
TabExpander tabEx = null; |
if (axis == X_AXIS) |
557 |
View parent = getParent(); |
{ |
558 |
if (parent instanceof TabExpander) |
Element el = getElement(); |
559 |
tabEx = (TabExpander) parent; |
TabExpander tabEx = null; |
560 |
// FIXME: Figure out how to determine the x parameter. |
View parent = getParent(); |
561 |
float span = painter.getSpan(this, el.getStartOffset(), el.getEndOffset(), |
if (parent instanceof TabExpander) |
562 |
tabEx, 0.F); |
tabEx = (TabExpander) parent; |
563 |
|
span = painter.getSpan(this, getStartOffset(), getEndOffset(), |
564 |
|
tabEx, 0.F); |
565 |
|
} |
566 |
|
else |
567 |
|
span = painter.getHeight(this); |
568 |
return span; |
return span; |
569 |
} |
} |
570 |
|
|
608 |
*/ |
*/ |
609 |
public int viewToModel(float x, float y, Shape a, Position.Bias[] b) |
public int viewToModel(float x, float y, Shape a, Position.Bias[] b) |
610 |
{ |
{ |
611 |
// FIXME: not implemented |
checkPainter(); |
612 |
return 0; |
GlyphPainter painter = getGlyphPainter(); |
613 |
|
return painter.viewToModel(this, x, y, a, b); |
614 |
} |
} |
615 |
|
|
616 |
/** |
/** |
620 |
*/ |
*/ |
621 |
public TabExpander getTabExpander() |
public TabExpander getTabExpander() |
622 |
{ |
{ |
|
// TODO: Figure out if this is correct. |
|
623 |
TabExpander te = null; |
TabExpander te = null; |
624 |
View parent = getParent(); |
View parent = getParent(); |
625 |
|
|
626 |
if (parent instanceof ParagraphView) |
if (parent instanceof TabExpander) |
627 |
te = (ParagraphView) parent; |
te = (TabExpander) parent; |
628 |
|
|
629 |
return te; |
return te; |
630 |
} |
} |
672 |
} |
} |
673 |
|
|
674 |
/** |
/** |
675 |
* Returns the starting offset in the document model of the portion |
* Returns the start offset in the document model of the portion |
676 |
* of text that this view is responsible for. |
* of text that this view is responsible for. |
677 |
* |
* |
678 |
* @return the starting offset in the document model of the portion |
* @return the start offset in the document model of the portion |
679 |
* of text that this view is responsible for |
* of text that this view is responsible for |
680 |
*/ |
*/ |
681 |
public int getBeginIndex() |
public int getStartOffset() |
682 |
{ |
{ |
683 |
return getElement().getStartOffset(); |
return startOffset; |
684 |
} |
} |
685 |
|
|
686 |
/** |
/** |
690 |
* @return the end offset in the document model of the portion |
* @return the end offset in the document model of the portion |
691 |
* of text that this view is responsible for |
* of text that this view is responsible for |
692 |
*/ |
*/ |
693 |
public int getEndIndex() |
public int getEndOffset() |
694 |
{ |
{ |
695 |
return getElement().getEndOffset(); |
return endOffset; |
696 |
} |
} |
697 |
|
|
698 |
/** |
/** |
754 |
AttributeSet atts = el.getAttributes(); |
AttributeSet atts = el.getAttributes(); |
755 |
return StyleConstants.getForeground(atts); |
return StyleConstants.getForeground(atts); |
756 |
} |
} |
757 |
|
|
758 |
|
/** |
759 |
|
* Returns the background color which should be used to paint the text. |
760 |
|
* This is fetched from the associated element's text attributes using |
761 |
|
* {@link StyleConstants#getBackground}. |
762 |
|
* |
763 |
|
* @return the background color which should be used to paint the text |
764 |
|
*/ |
765 |
|
public Color getBackground() |
766 |
|
{ |
767 |
|
Element el = getElement(); |
768 |
|
AttributeSet atts = el.getAttributes(); |
769 |
|
return StyleConstants.getBackground(atts); |
770 |
|
} |
771 |
|
|
772 |
|
/** |
773 |
|
* Determines whether the text should be rendered strike-through or not. This |
774 |
|
* is determined using the method |
775 |
|
* {@link StyleConstants#isStrikeThrough(AttributeSet)} on the element of |
776 |
|
* this view. |
777 |
|
* |
778 |
|
* @return whether the text should be rendered strike-through or not |
779 |
|
*/ |
780 |
|
public boolean isStikeThrough() |
781 |
|
{ |
782 |
|
Element el = getElement(); |
783 |
|
AttributeSet atts = el.getAttributes(); |
784 |
|
return StyleConstants.isStrikeThrough(atts); |
785 |
|
} |
786 |
|
|
787 |
|
/** |
788 |
|
* Determines whether the text should be rendered as subscript or not. This |
789 |
|
* is determined using the method |
790 |
|
* {@link StyleConstants#isSubscript(AttributeSet)} on the element of |
791 |
|
* this view. |
792 |
|
* |
793 |
|
* @return whether the text should be rendered as subscript or not |
794 |
|
*/ |
795 |
|
public boolean isSubscript() |
796 |
|
{ |
797 |
|
Element el = getElement(); |
798 |
|
AttributeSet atts = el.getAttributes(); |
799 |
|
return StyleConstants.isSubscript(atts); |
800 |
|
} |
801 |
|
|
802 |
|
/** |
803 |
|
* Determines whether the text should be rendered as superscript or not. This |
804 |
|
* is determined using the method |
805 |
|
* {@link StyleConstants#isSuperscript(AttributeSet)} on the element of |
806 |
|
* this view. |
807 |
|
* |
808 |
|
* @return whether the text should be rendered as superscript or not |
809 |
|
*/ |
810 |
|
public boolean isSuperscript() |
811 |
|
{ |
812 |
|
Element el = getElement(); |
813 |
|
AttributeSet atts = el.getAttributes(); |
814 |
|
return StyleConstants.isSuperscript(atts); |
815 |
|
} |
816 |
|
|
817 |
|
/** |
818 |
|
* Determines whether the text should be rendered as underlined or not. This |
819 |
|
* is determined using the method |
820 |
|
* {@link StyleConstants#isUnderline(AttributeSet)} on the element of |
821 |
|
* this view. |
822 |
|
* |
823 |
|
* @return whether the text should be rendered as underlined or not |
824 |
|
*/ |
825 |
|
public boolean isUnderline() |
826 |
|
{ |
827 |
|
Element el = getElement(); |
828 |
|
AttributeSet atts = el.getAttributes(); |
829 |
|
return StyleConstants.isUnderline(atts); |
830 |
|
} |
831 |
|
|
832 |
|
/** |
833 |
|
* Creates and returns a shallow clone of this GlyphView. This is used by |
834 |
|
* the {@link #createFragment} and {@link #breakView} methods. |
835 |
|
* |
836 |
|
* @return a shallow clone of this GlyphView |
837 |
|
*/ |
838 |
|
protected final Object clone() |
839 |
|
{ |
840 |
|
try |
841 |
|
{ |
842 |
|
return super.clone(); |
843 |
|
} |
844 |
|
catch (CloneNotSupportedException ex) |
845 |
|
{ |
846 |
|
AssertionError err = new AssertionError("CloneNotSupportedException " |
847 |
|
+ "must not be thrown here"); |
848 |
|
err.initCause(ex); |
849 |
|
throw err; |
850 |
|
} |
851 |
|
} |
852 |
|
|
853 |
|
/** |
854 |
|
* Tries to break the view near the specified view span <code>len</code>. |
855 |
|
* The glyph view can only be broken in the X direction. For Y direction it |
856 |
|
* returns itself. |
857 |
|
* |
858 |
|
* @param axis the axis for breaking, may be {@link View#X_AXIS} or |
859 |
|
* {@link View#Y_AXIS} |
860 |
|
* @param p0 the model location where the fragment should start |
861 |
|
* @param pos the view position along the axis where the fragment starts |
862 |
|
* @param len the desired length of the fragment view |
863 |
|
* |
864 |
|
* @return the fragment view, or <code>this</code> if breaking was not |
865 |
|
* possible |
866 |
|
*/ |
867 |
|
public View breakView(int axis, int p0, float pos, float len) |
868 |
|
{ |
869 |
|
if (axis == Y_AXIS) |
870 |
|
return this; |
871 |
|
|
872 |
|
checkPainter(); |
873 |
|
GlyphPainter painter = getGlyphPainter(); |
874 |
|
int breakLocation = painter.getBoundedPosition(this, p0, pos, len); |
875 |
|
// Try to find a suitable line break. |
876 |
|
BreakIterator lineBreaker = BreakIterator.getLineInstance(); |
877 |
|
Segment txt = new Segment(); |
878 |
|
try |
879 |
|
{ |
880 |
|
getDocument().getText(getStartOffset(), getEndOffset(), txt); |
881 |
|
} |
882 |
|
catch (BadLocationException ex) |
883 |
|
{ |
884 |
|
AssertionError err = new AssertionError("BadLocationException must not " |
885 |
|
+ "be thrown here."); |
886 |
|
err.initCause(ex); |
887 |
|
throw err; |
888 |
|
} |
889 |
|
lineBreaker.setText(txt); |
890 |
|
int goodBreakLocation = lineBreaker.previous(); |
891 |
|
if (goodBreakLocation != BreakIterator.DONE) |
892 |
|
breakLocation = goodBreakLocation; |
893 |
|
|
894 |
|
View brokenView = createFragment(p0, breakLocation); |
895 |
|
return brokenView; |
896 |
|
} |
897 |
|
|
898 |
|
/** |
899 |
|
* Determines how well the specified view location is suitable for inserting |
900 |
|
* a line break. If <code>axis</code> is <code>View.Y_AXIS</code>, then |
901 |
|
* this method forwards to the superclass, if <code>axis</code> is |
902 |
|
* <code>View.X_AXIS</code> then this method returns |
903 |
|
* {@link View#ExcellentBreakWeight} if there is a suitable break location |
904 |
|
* (usually whitespace) within the specified view span, or |
905 |
|
* {@link View#GoodBreakWeight} if not. |
906 |
|
* |
907 |
|
* @param axis the axis along which the break weight is requested |
908 |
|
* @param pos the starting view location |
909 |
|
* @param len the length of the span at which the view should be broken |
910 |
|
* |
911 |
|
* @return the break weight |
912 |
|
*/ |
913 |
|
public int getBreakWeight(int axis, float pos, float len) |
914 |
|
{ |
915 |
|
int weight; |
916 |
|
if (axis == Y_AXIS) |
917 |
|
weight = super.getBreakWeight(axis, pos, len); |
918 |
|
else |
919 |
|
{ |
920 |
|
// Determine the model locations at pos and pos + len. |
921 |
|
int spanX = (int) getPreferredSpan(X_AXIS); |
922 |
|
int spanY = (int) getPreferredSpan(Y_AXIS); |
923 |
|
Rectangle dummyAlloc = new Rectangle(0, 0, spanX, spanY); |
924 |
|
Position.Bias[] biasRet = new Position.Bias[1]; |
925 |
|
int offset1 = viewToModel(pos, spanY / 2, dummyAlloc, biasRet); |
926 |
|
int offset2 = viewToModel(pos, spanY / 2, dummyAlloc, biasRet); |
927 |
|
Segment txt = getText(offset1, offset2); |
928 |
|
BreakIterator lineBreaker = BreakIterator.getLineInstance(); |
929 |
|
lineBreaker.setText(txt); |
930 |
|
int breakLoc = lineBreaker.previous(); |
931 |
|
if (breakLoc == offset1) |
932 |
|
weight = View.BadBreakWeight; |
933 |
|
else if(breakLoc == BreakIterator.DONE) |
934 |
|
weight = View.GoodBreakWeight; |
935 |
|
else |
936 |
|
weight = View.ExcellentBreakWeight; |
937 |
|
} |
938 |
|
return weight; |
939 |
|
} |
940 |
|
|
941 |
|
/** |
942 |
|
* Receives notification that some text attributes have changed within the |
943 |
|
* text fragment that this view is responsible for. This calls |
944 |
|
* {@link View#preferenceChanged(View, boolean, boolean)} on the parent for |
945 |
|
* both width and height. |
946 |
|
* |
947 |
|
* @param e the document event describing the change; not used here |
948 |
|
* @param a the view allocation on screen; not used here |
949 |
|
* @param vf the view factory; not used here |
950 |
|
*/ |
951 |
|
public void changedUpdate(DocumentEvent e, Shape a, ViewFactory vf) |
952 |
|
{ |
953 |
|
getParent().preferenceChanged(this, true, true); |
954 |
|
} |
955 |
|
|
956 |
|
/** |
957 |
|
* Receives notification that some text has been inserted within the |
958 |
|
* text fragment that this view is responsible for. This calls |
959 |
|
* {@link View#preferenceChanged(View, boolean, boolean)} on the parent for |
960 |
|
* width. |
961 |
|
* |
962 |
|
* @param e the document event describing the change; not used here |
963 |
|
* @param a the view allocation on screen; not used here |
964 |
|
* @param vf the view factory; not used here |
965 |
|
*/ |
966 |
|
public void insertUpdate(DocumentEvent e, Shape a, ViewFactory vf) |
967 |
|
{ |
968 |
|
getParent().preferenceChanged(this, true, false); |
969 |
|
} |
970 |
|
|
971 |
|
/** |
972 |
|
* Receives notification that some text has been removed within the |
973 |
|
* text fragment that this view is responsible for. This calls |
974 |
|
* {@link View#preferenceChanged(View, boolean, boolean)} on the parent for |
975 |
|
* width. |
976 |
|
* |
977 |
|
* @param e the document event describing the change; not used here |
978 |
|
* @param a the view allocation on screen; not used here |
979 |
|
* @param vf the view factory; not used here |
980 |
|
*/ |
981 |
|
public void removeUpdate(DocumentEvent e, Shape a, ViewFactory vf) |
982 |
|
{ |
983 |
|
getParent().preferenceChanged(this, true, false); |
984 |
|
} |
985 |
|
|
986 |
|
/** |
987 |
|
* Creates a fragment view of this view that starts at <code>p0</code> and |
988 |
|
* ends at <code>p1</code>. |
989 |
|
* |
990 |
|
* @param p0 the start location for the fragment view |
991 |
|
* @param p1 the end location for the fragment view |
992 |
|
* |
993 |
|
* @return the fragment view |
994 |
|
*/ |
995 |
|
public View createFragment(int p0, int p1) |
996 |
|
{ |
997 |
|
GlyphView fragment = (GlyphView) clone(); |
998 |
|
fragment.startOffset = p0; |
999 |
|
fragment.endOffset = p1; |
1000 |
|
return fragment; |
1001 |
|
} |
1002 |
|
|
1003 |
|
/** |
1004 |
|
* Returns the alignment of this view along the specified axis. For the Y |
1005 |
|
* axis this is <code>(height - descent) / height</code> for the used font, |
1006 |
|
* so that it is aligned along the baseline. |
1007 |
|
* For the X axis the superclass is called. |
1008 |
|
*/ |
1009 |
|
public float getAlignment(int axis) |
1010 |
|
{ |
1011 |
|
float align; |
1012 |
|
if (axis == Y_AXIS) |
1013 |
|
{ |
1014 |
|
checkPainter(); |
1015 |
|
GlyphPainter painter = getGlyphPainter(); |
1016 |
|
float height = painter.getHeight(this); |
1017 |
|
float descent = painter.getDescent(this); |
1018 |
|
align = (height - descent) / height; |
1019 |
|
} |
1020 |
|
else |
1021 |
|
align = super.getAlignment(axis); |
1022 |
|
|
1023 |
|
return align; |
1024 |
|
} |
1025 |
|
|
1026 |
|
/** |
1027 |
|
* Returns the model location that should be used to place a caret when |
1028 |
|
* moving the caret through the document. |
1029 |
|
* |
1030 |
|
* @param pos the current model location |
1031 |
|
* @param bias the bias for <code>p</code> |
1032 |
|
* @param a the allocated region for the glyph view |
1033 |
|
* @param direction the direction from the current position; Must be one of |
1034 |
|
* {@link SwingConstants#EAST}, {@link SwingConstants#WEST}, |
1035 |
|
* {@link SwingConstants#NORTH} or {@link SwingConstants#SOUTH} |
1036 |
|
* @param biasRet filled with the bias of the resulting location when method |
1037 |
|
* returns |
1038 |
|
* |
1039 |
|
* @return the location within the document that should be used to place the |
1040 |
|
* caret when moving the caret around the document |
1041 |
|
* |
1042 |
|
* @throws BadLocationException if <code>pos</code> is an invalid model |
1043 |
|
* location |
1044 |
|
* @throws IllegalArgumentException if <code>d</code> is invalid |
1045 |
|
*/ |
1046 |
|
public int getNextVisualPositionFrom(int pos, Position.Bias bias, Shape a, |
1047 |
|
int direction, Position.Bias[] biasRet) |
1048 |
|
throws BadLocationException |
1049 |
|
{ |
1050 |
|
checkPainter(); |
1051 |
|
GlyphPainter painter = getGlyphPainter(); |
1052 |
|
return painter.getNextVisualPositionFrom(this, pos, bias, a, direction, |
1053 |
|
biasRet); |
1054 |
|
} |
1055 |
} |
} |