OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libjava/] [classpath/] [javax/] [swing/] [text/] [GlyphView.java] - Blame information for rev 772

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 772 jeremybenn
/* GlyphView.java -- A view to render styled text
2
   Copyright (C) 2005  Free Software Foundation, Inc.
3
 
4
This file is part of GNU Classpath.
5
 
6
GNU Classpath is free software; you can redistribute it and/or modify
7
it under the terms of the GNU General Public License as published by
8
the Free Software Foundation; either version 2, or (at your option)
9
any later version.
10
 
11
GNU Classpath is distributed in the hope that it will be useful, but
12
WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
General Public License for more details.
15
 
16
You should have received a copy of the GNU General Public License
17
along with GNU Classpath; see the file COPYING.  If not, write to the
18
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
02110-1301 USA.
20
 
21
Linking this library statically or dynamically with other modules is
22
making a combined work based on this library.  Thus, the terms and
23
conditions of the GNU General Public License cover the whole
24
combination.
25
 
26
As a special exception, the copyright holders of this library give you
27
permission to link this library with independent modules to produce an
28
executable, regardless of the license terms of these independent
29
modules, and to copy and distribute the resulting executable under
30
terms of your choice, provided that you also meet, for each linked
31
independent module, the terms and conditions of the license of that
32
module.  An independent module is a module which is not derived from
33
or based on this library.  If you modify this library, you may extend
34
this exception to your version of the library, but you are not
35
obligated to do so.  If you do not wish to do so, delete this
36
exception statement from your version. */
37
 
38
 
39
package javax.swing.text;
40
 
41
import gnu.classpath.SystemProperties;
42
 
43
import java.awt.Color;
44
import java.awt.Container;
45
import java.awt.Font;
46
import java.awt.FontMetrics;
47
import java.awt.Graphics;
48
import java.awt.Graphics2D;
49
import java.awt.Rectangle;
50
import java.awt.Shape;
51
import java.awt.Toolkit;
52
import java.awt.font.FontRenderContext;
53
import java.awt.font.TextHitInfo;
54
import java.awt.font.TextLayout;
55
import java.awt.geom.Rectangle2D;
56
 
57
import javax.swing.SwingConstants;
58
import javax.swing.event.DocumentEvent;
59
import javax.swing.text.Position.Bias;
60
 
61
/**
62
 * Renders a run of styled text. This {@link View} subclass paints the
63
 * characters of the <code>Element</code> it is responsible for using
64
 * the style information from that <code>Element</code>.
65
 *
66
 * @author Roman Kennke (roman@kennke.org)
67
 */
68
public class GlyphView extends View implements TabableView, Cloneable
69
{
70
 
71
  /**
72
   * An abstract base implementation for a glyph painter for
73
   * <code>GlyphView</code>.
74
   */
75
  public abstract static class GlyphPainter
76
  {
77
    /**
78
     * Creates a new <code>GlyphPainer</code>.
79
     */
80
    public GlyphPainter()
81
    {
82
      // Nothing to do here.
83
    }
84
 
85
    /**
86
     * Returns the ascent of the font that is used by this glyph painter.
87
     *
88
     * @param v the glyph view
89
     *
90
     * @return the ascent of the font that is used by this glyph painter
91
     */
92
    public abstract float getAscent(GlyphView v);
93
 
94
    /**
95
     * Returns the descent of the font that is used by this glyph painter.
96
     *
97
     * @param v the glyph view
98
     *
99
     * @return the descent of the font that is used by this glyph painter
100
     */
101
    public abstract float getDescent(GlyphView v);
102
 
103
    /**
104
     * Returns the full height of the rendered text.
105
     *
106
     * @return the full height of the rendered text
107
     */
108
    public abstract float getHeight(GlyphView view);
109
 
110
    /**
111
     * Determines the model offset, so that the text between <code>p0</code>
112
     * and this offset fits within the span starting at <code>x</code> with
113
     * the length of <code>len</code>.
114
     *
115
     * @param v the glyph view
116
     * @param p0 the starting offset in the model
117
     * @param x the start location in the view
118
     * @param len the length of the span in the view
119
     */
120
    public abstract int getBoundedPosition(GlyphView v, int p0, float x,
121
                                           float len);
122
 
123
    /**
124
     * Paints the glyphs.
125
     *
126
     * @param view the glyph view to paint
127
     * @param g the graphics context to use for painting
128
     * @param a the allocation of the glyph view
129
     * @param p0 the start position (in the model) from which to paint
130
     * @param p1 the end position (in the model) to which to paint
131
     */
132
    public abstract void paint(GlyphView view, Graphics g, Shape a, int p0,
133
                               int p1);
134
 
135
    /**
136
     * Maps a position in the document into the coordinate space of the View.
137
     * The output rectangle usually reflects the font height but has a width
138
     * of zero.
139
     *
140
     * @param view the glyph view
141
     * @param pos the position of the character in the model
142
     * @param a the area that is occupied by the view
143
     * @param b either {@link Position.Bias#Forward} or
144
     *        {@link Position.Bias#Backward} depending on the preferred
145
     *        direction bias. If <code>null</code> this defaults to
146
     *        <code>Position.Bias.Forward</code>
147
     *
148
     * @return a rectangle that gives the location of the document position
149
     *         inside the view coordinate space
150
     *
151
     * @throws BadLocationException if <code>pos</code> is invalid
152
     * @throws IllegalArgumentException if b is not one of the above listed
153
     *         valid values
154
     */
155
    public abstract Shape modelToView(GlyphView view, int pos, Position.Bias b,
156
                                      Shape a)
157
      throws BadLocationException;
158
 
159
    /**
160
     * Maps a visual position into a document location.
161
     *
162
     * @param v the glyph view
163
     * @param x the X coordinate of the visual position
164
     * @param y the Y coordinate of the visual position
165
     * @param a the allocated region
166
     * @param biasRet filled with the bias of the model location on method exit
167
     *
168
     * @return the model location that represents the specified view location
169
     */
170
    public abstract int viewToModel(GlyphView v, float x, float y, Shape a,
171
                                    Position.Bias[] biasRet);
172
 
173
    /**
174
     * Determine the span of the glyphs from location <code>p0</code> to
175
     * location <code>p1</code>. If <code>te</code> is not <code>null</code>,
176
     * then TABs are expanded using this <code>TabExpander</code>.
177
     * The parameter <code>x</code> is the location at which the view is
178
     * located (this is important when using TAB expansion).
179
     *
180
     * @param view the glyph view
181
     * @param p0 the starting location in the document model
182
     * @param p1 the end location in the document model
183
     * @param te the tab expander to use
184
     * @param x the location at which the view is located
185
     *
186
     * @return the span of the glyphs from location <code>p0</code> to
187
     *         location <code>p1</code>, possibly using TAB expansion
188
     */
189
    public abstract float getSpan(GlyphView view, int p0, int p1,
190
                                  TabExpander te, float x);
191
 
192
 
193
    /**
194
     * Returns the model location that should be used to place a caret when
195
     * moving the caret through the document.
196
     *
197
     * @param v the glyph view
198
     * @param pos the current model location
199
     * @param b the bias for <code>p</code>
200
     * @param a the allocated region for the glyph view
201
     * @param direction the direction from the current position; Must be one of
202
     *        {@link SwingConstants#EAST}, {@link SwingConstants#WEST},
203
     *        {@link SwingConstants#NORTH} or {@link SwingConstants#SOUTH}
204
     * @param biasRet filled with the bias of the resulting location when method
205
     *        returns
206
     *
207
     * @return the location within the document that should be used to place the
208
     *         caret when moving the caret around the document
209
     *
210
     * @throws BadLocationException if <code>pos</code> is an invalid model
211
     *         location
212
     * @throws IllegalArgumentException if <code>d</code> is invalid
213
     */
214
    public int getNextVisualPositionFrom(GlyphView v, int pos, Position.Bias b,
215
                                         Shape a, int direction,
216
                                         Position.Bias[] biasRet)
217
      throws BadLocationException
218
 
219
    {
220
      int result = pos;
221
      switch (direction)
222
      {
223
        case SwingConstants.EAST:
224
          result = pos + 1;
225
          break;
226
        case SwingConstants.WEST:
227
          result = pos - 1;
228
          break;
229
        case SwingConstants.NORTH:
230
        case SwingConstants.SOUTH:
231
        default:
232
          // This should be handled in enclosing view, since the glyph view
233
          // does not layout vertically.
234
          break;
235
      }
236
      return result;
237
    }
238
 
239
    /**
240
     * Returns a painter that can be used to render the specified glyph view.
241
     * If this glyph painter is stateful, then it should return a new instance.
242
     * However, if this painter is stateless it should return itself. The
243
     * default behaviour is to return itself.
244
     *
245
     * @param v the glyph view for which to create a painter
246
     * @param p0 the start offset of the rendered area
247
     * @param p1 the end offset of the rendered area
248
     *
249
     * @return a painter that can be used to render the specified glyph view
250
     */
251
    public GlyphPainter getPainter(GlyphView v, int p0, int p1)
252
    {
253
      return this;
254
    }
255
  }
256
 
257
  /**
258
   * A GlyphPainter implementation based on TextLayout. This should give
259
   * better performance in Java2D environments.
260
   */
261
  private static class J2DGlyphPainter
262
    extends GlyphPainter
263
  {
264
 
265
    /**
266
     * The text layout.
267
     */
268
    TextLayout textLayout;
269
 
270
    /**
271
     * Creates a new J2DGlyphPainter.
272
     *
273
     * @param str the string
274
     * @param font the font
275
     * @param frc the font render context
276
     */
277
    J2DGlyphPainter(String str, Font font, FontRenderContext frc)
278
    {
279
      textLayout = new TextLayout(str, font, frc);
280
    }
281
 
282
    /**
283
     * Returns null so that GlyphView.checkPainter() creates a new instance.
284
     */
285
    public GlyphPainter getPainter(GlyphView v, int p0, int p1)
286
    {
287
      return null;
288
    }
289
 
290
    /**
291
     * Delegates to the text layout.
292
     */
293
    public float getAscent(GlyphView v)
294
    {
295
      return textLayout.getAscent();
296
    }
297
 
298
    /**
299
     * Delegates to the text layout.
300
     */
301
    public int getBoundedPosition(GlyphView v, int p0, float x, float len)
302
    {
303
      int pos;
304
      TextHitInfo hit = textLayout.hitTestChar(len, 0);
305
      if (hit.getCharIndex() == -1 && ! textLayout.isLeftToRight())
306
        pos = v.getEndOffset();
307
      else
308
        {
309
          pos = hit.isLeadingEdge() ? hit.getInsertionIndex()
310
                                    : hit.getInsertionIndex() - 1;
311
          pos += v.getStartOffset();
312
        }
313
      return pos;
314
    }
315
 
316
    /**
317
     * Delegates to the text layout.
318
     */
319
    public float getDescent(GlyphView v)
320
    {
321
      return textLayout.getDescent();
322
    }
323
 
324
    /**
325
     * Delegates to the text layout.
326
     */
327
    public float getHeight(GlyphView view)
328
    {
329
      return textLayout.getAscent() + textLayout.getDescent()
330
             + textLayout.getLeading();
331
    }
332
 
333
    /**
334
     * Delegates to the text layout.
335
     */
336
    public float getSpan(GlyphView v, int p0, int p1, TabExpander te, float x)
337
    {
338
      float span;
339
      if (p0 == v.getStartOffset() && p1 == v.getEndOffset())
340
        span = textLayout.getAdvance();
341
      else
342
        {
343
          int start = v.getStartOffset();
344
          int i0 = p0 - start;
345
          int i1 = p1 - start;
346
          TextHitInfo hit0 = TextHitInfo.afterOffset(i0);
347
          TextHitInfo hit1 = TextHitInfo.afterOffset(i1);
348
          float x0 = textLayout.getCaretInfo(hit0)[0];
349
          float x1 = textLayout.getCaretInfo(hit1)[0];
350
          span = Math.abs(x1 - x0);
351
        }
352
      return span;
353
    }
354
 
355
    /**
356
     * Delegates to the text layout.
357
     */
358
    public Shape modelToView(GlyphView v, int pos, Bias b, Shape a)
359
      throws BadLocationException
360
    {
361
      int offs = pos - v.getStartOffset();
362
      // Create copy here to protect original shape.
363
      Rectangle2D bounds = a.getBounds2D();
364
      TextHitInfo hit =
365
        b == Position.Bias.Forward ? TextHitInfo.afterOffset(offs)
366
                                   : TextHitInfo.beforeOffset(offs);
367
      float[] loc = textLayout.getCaretInfo(hit);
368
      bounds.setRect(bounds.getX() + loc[0], bounds.getY(), 1,
369
                     bounds.getHeight());
370
      return bounds;
371
    }
372
 
373
    /**
374
     * Delegates to the text layout.
375
     */
376
    public void paint(GlyphView view, Graphics g, Shape a, int p0, int p1)
377
    {
378
      // Can't paint this with plain graphics.
379
      if (g instanceof Graphics2D)
380
        {
381
          Graphics2D g2d = (Graphics2D) g;
382
          Rectangle2D b = a instanceof Rectangle2D ? (Rectangle2D) a
383
                                                   : a.getBounds2D();
384
          float x = (float) b.getX();
385
          float y = (float) b.getY() + textLayout.getAscent()
386
                    + textLayout.getLeading();
387
          // TODO: Try if clipping makes things faster for narrow views.
388
          textLayout.draw(g2d, x, y);
389
        }
390
    }
391
 
392
    /**
393
     * Delegates to the text layout.
394
     */
395
    public int viewToModel(GlyphView v, float x, float y, Shape a,
396
                           Bias[] biasRet)
397
    {
398
      Rectangle2D bounds = a instanceof Rectangle2D ? (Rectangle2D) a
399
                                                    : a.getBounds2D();
400
      TextHitInfo hit = textLayout.hitTestChar(x - (float) bounds.getX(), 0);
401
      int pos = hit.getInsertionIndex();
402
      biasRet[0] = hit.isLeadingEdge() ? Position.Bias.Forward
403
                                       : Position.Bias.Backward;
404
      return pos + v.getStartOffset();
405
    }
406
 
407
  }
408
 
409
  /**
410
   * The default <code>GlyphPainter</code> used in <code>GlyphView</code>.
411
   */
412
  static class DefaultGlyphPainter extends GlyphPainter
413
  {
414
    FontMetrics fontMetrics;
415
 
416
    /**
417
     * Returns the full height of the rendered text.
418
     *
419
     * @return the full height of the rendered text
420
     */
421
    public float getHeight(GlyphView view)
422
    {
423
      updateFontMetrics(view);
424
      float height = fontMetrics.getHeight();
425
      return height;
426
    }
427
 
428
    /**
429
     * Paints the glyphs.
430
     *
431
     * @param view the glyph view to paint
432
     * @param g the graphics context to use for painting
433
     * @param a the allocation of the glyph view
434
     * @param p0 the start position (in the model) from which to paint
435
     * @param p1 the end position (in the model) to which to paint
436
     */
437
    public void paint(GlyphView view, Graphics g, Shape a, int p0,
438
                      int p1)
439
    {
440
      updateFontMetrics(view);
441
      Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
442
      TabExpander tabEx = view.getTabExpander();
443
      Segment txt = view.getText(p0, p1);
444
 
445
      // Find out the X location at which we have to paint.
446
      int x = r.x;
447
      int p = view.getStartOffset();
448
      if (p != p0)
449
        {
450
          int width = Utilities.getTabbedTextWidth(txt, fontMetrics,x, tabEx,
451
                                                   p);
452
          x += width;
453
        }
454
      // Find out Y location.
455
      int y = r.y + fontMetrics.getHeight() - fontMetrics.getDescent();
456
 
457
      // Render the thing.
458
      g.setFont(fontMetrics.getFont());
459
      Utilities.drawTabbedText(txt, x, y, g, tabEx, p0);
460
 
461
    }
462
 
463
    /**
464
     * Maps a position in the document into the coordinate space of the View.
465
     * The output rectangle usually reflects the font height but has a width
466
     * of zero.
467
     *
468
     * @param view the glyph view
469
     * @param pos the position of the character in the model
470
     * @param a the area that is occupied by the view
471
     * @param b either {@link Position.Bias#Forward} or
472
     *        {@link Position.Bias#Backward} depending on the preferred
473
     *        direction bias. If <code>null</code> this defaults to
474
     *        <code>Position.Bias.Forward</code>
475
     *
476
     * @return a rectangle that gives the location of the document position
477
     *         inside the view coordinate space
478
     *
479
     * @throws BadLocationException if <code>pos</code> is invalid
480
     * @throws IllegalArgumentException if b is not one of the above listed
481
     *         valid values
482
     */
483
    public Shape modelToView(GlyphView view, int pos, Position.Bias b,
484
                             Shape a)
485
      throws BadLocationException
486
    {
487
      updateFontMetrics(view);
488
      Element el = view.getElement();
489
      Segment txt = view.getText(el.getStartOffset(), pos);
490
      Rectangle bounds = a instanceof Rectangle ? (Rectangle) a
491
                                                : a.getBounds();
492
      TabExpander expander = view.getTabExpander();
493
      int width = Utilities.getTabbedTextWidth(txt, fontMetrics, bounds.x,
494
                                               expander,
495
                                               view.getStartOffset());
496
      int height = fontMetrics.getHeight();
497
      Rectangle result = new Rectangle(bounds.x + width, bounds.y,
498
                                       0, height);
499
      return result;
500
    }
501
 
502
    /**
503
     * Determine the span of the glyphs from location <code>p0</code> to
504
     * location <code>p1</code>. If <code>te</code> is not <code>null</code>,
505
     * then TABs are expanded using this <code>TabExpander</code>.
506
     * The parameter <code>x</code> is the location at which the view is
507
     * located (this is important when using TAB expansion).
508
     *
509
     * @param view the glyph view
510
     * @param p0 the starting location in the document model
511
     * @param p1 the end location in the document model
512
     * @param te the tab expander to use
513
     * @param x the location at which the view is located
514
     *
515
     * @return the span of the glyphs from location <code>p0</code> to
516
     *         location <code>p1</code>, possibly using TAB expansion
517
     */
518
    public float getSpan(GlyphView view, int p0, int p1,
519
                         TabExpander te, float x)
520
    {
521
      updateFontMetrics(view);
522
      Segment txt = view.getText(p0, p1);
523
      int span = Utilities.getTabbedTextWidth(txt, fontMetrics, (int) x, te,
524
                                              p0);
525
      return span;
526
    }
527
 
528
    /**
529
     * Returns the ascent of the text run that is rendered by this
530
     * <code>GlyphPainter</code>.
531
     *
532
     * @param v the glyph view
533
     *
534
     * @return the ascent of the text run that is rendered by this
535
     *         <code>GlyphPainter</code>
536
     *
537
     * @see FontMetrics#getAscent()
538
     */
539
    public float getAscent(GlyphView v)
540
    {
541
      updateFontMetrics(v);
542
      return fontMetrics.getAscent();
543
    }
544
 
545
    /**
546
     * Returns the descent of the text run that is rendered by this
547
     * <code>GlyphPainter</code>.
548
     *
549
     * @param v the glyph view
550
     *
551
     * @return the descent of the text run that is rendered by this
552
     *         <code>GlyphPainter</code>
553
     *
554
     * @see FontMetrics#getDescent()
555
     */
556
    public float getDescent(GlyphView v)
557
    {
558
      updateFontMetrics(v);
559
      return fontMetrics.getDescent();
560
    }
561
 
562
    /**
563
     * Determines the model offset, so that the text between <code>p0</code>
564
     * and this offset fits within the span starting at <code>x</code> with
565
     * the length of <code>len</code>.
566
     *
567
     * @param v the glyph view
568
     * @param p0 the starting offset in the model
569
     * @param x the start location in the view
570
     * @param len the length of the span in the view
571
     */
572
    public int getBoundedPosition(GlyphView v, int p0, float x, float len)
573
    {
574
      updateFontMetrics(v);
575
      TabExpander te = v.getTabExpander();
576
      Segment txt = v.getText(p0, v.getEndOffset());
577
      int pos = Utilities.getTabbedTextOffset(txt, fontMetrics, (int) x,
578
                                              (int) (x + len), te, p0, false);
579
      return pos + p0;
580
    }
581
 
582
    /**
583
     * Maps a visual position into a document location.
584
     *
585
     * @param v the glyph view
586
     * @param x the X coordinate of the visual position
587
     * @param y the Y coordinate of the visual position
588
     * @param a the allocated region
589
     * @param biasRet filled with the bias of the model location on method exit
590
     *
591
     * @return the model location that represents the specified view location
592
     */
593
    public int viewToModel(GlyphView v, float x, float y, Shape a,
594
                           Bias[] biasRet)
595
    {
596
      Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
597
      int p0 = v.getStartOffset();
598
      int p1 = v.getEndOffset();
599
      TabExpander te = v.getTabExpander();
600
      Segment s = v.getText(p0, p1);
601
      int offset = Utilities.getTabbedTextOffset(s, fontMetrics, r.x, (int) x,
602
                                                 te, p0);
603
      int ret = p0 + offset;
604
      if (ret == p1)
605
        ret--;
606
      biasRet[0] = Position.Bias.Forward;
607
      return ret;
608
    }
609
 
610
    private void updateFontMetrics(GlyphView v)
611
    {
612
      Font font = v.getFont();
613
      if (fontMetrics == null || ! font.equals(fontMetrics.getFont()))
614
        {
615
          Container c = v.getContainer();
616
          FontMetrics fm;
617
          if (c != null)
618
            fm = c.getFontMetrics(font);
619
          else
620
            fm = Toolkit.getDefaultToolkit().getFontMetrics(font);
621
          fontMetrics = fm;
622
        }
623
    }
624
  }
625
 
626
  /**
627
   * The GlyphPainer used for painting the glyphs.
628
   */
629
  GlyphPainter glyphPainter;
630
 
631
  /**
632
   * The start offset within the document for this view.
633
   */
634
  private int offset;
635
 
636
  /**
637
   * The end offset within the document for this view.
638
   */
639
  private int length;
640
 
641
  /**
642
   * The x location against which the tab expansion is done.
643
   */
644
  private float tabX;
645
 
646
  /**
647
   * The tab expander that is used in this view.
648
   */
649
  private TabExpander tabExpander;
650
 
651
  /**
652
   * Creates a new <code>GlyphView</code> for the given <code>Element</code>.
653
   *
654
   * @param element the element that is rendered by this GlyphView
655
   */
656
  public GlyphView(Element element)
657
  {
658
    super(element);
659
    offset = 0;
660
    length = 0;
661
  }
662
 
663
  /**
664
   * Returns the <code>GlyphPainter</code> that is used by this
665
   * <code>GlyphView</code>. If no <code>GlyphPainer</code> has been installed
666
   * <code>null</code> is returned.
667
   *
668
   * @return the glyph painter that is used by this
669
   *         glyph view or <code>null</code> if no glyph painter has been
670
   *         installed
671
   */
672
  public GlyphPainter getGlyphPainter()
673
  {
674
    return glyphPainter;
675
  }
676
 
677
  /**
678
   * Sets the {@link GlyphPainter} to be used for this <code>GlyphView</code>.
679
   *
680
   * @param painter the glyph painter to be used for this glyph view
681
   */
682
  public void setGlyphPainter(GlyphPainter painter)
683
  {
684
    glyphPainter = painter;
685
  }
686
 
687
  /**
688
   * Checks if a <code>GlyphPainer</code> is installed. If this is not the
689
   * case, a default painter is installed.
690
   */
691
  protected void checkPainter()
692
  {
693
    if (glyphPainter == null)
694
      {
695
        if ("true".equals(
696
                 SystemProperties.getProperty("gnu.javax.swing.noGraphics2D")))
697
          {
698
            glyphPainter = new DefaultGlyphPainter();
699
          }
700
        else
701
          {
702
            Segment s = getText(getStartOffset(), getEndOffset());
703
            glyphPainter = new J2DGlyphPainter(s.toString(), getFont(),
704
                                               new FontRenderContext(null,
705
                                                                     false,
706
                                                                     false));
707
          }
708
      }
709
  }
710
 
711
  /**
712
   * Renders the <code>Element</code> that is associated with this
713
   * <code>View</code>.
714
   *
715
   * @param g the <code>Graphics</code> context to render to
716
   * @param a the allocated region for the <code>Element</code>
717
   */
718
  public void paint(Graphics g, Shape a)
719
  {
720
    checkPainter();
721
    int p0 = getStartOffset();
722
    int p1 = getEndOffset();
723
 
724
    Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
725
    Container c = getContainer();
726
 
727
    Color fg = getForeground();
728
    JTextComponent tc = null;
729
    if (c instanceof JTextComponent)
730
      {
731
        tc = (JTextComponent) c;
732
        if (! tc.isEnabled())
733
          fg = tc.getDisabledTextColor();
734
      }
735
    Color bg = getBackground();
736
    if (bg != null)
737
      {
738
        g.setColor(bg);
739
        g.fillRect(r.x, r.y, r.width, r.height);
740
      }
741
 
742
 
743
    // Paint layered highlights if there are any.
744
    if (tc != null)
745
      {
746
        Highlighter h = tc.getHighlighter();
747
        if (h instanceof LayeredHighlighter)
748
          {
749
            LayeredHighlighter lh = (LayeredHighlighter) h;
750
            lh.paintLayeredHighlights(g, p0, p1, a, tc, this);
751
          }
752
      }
753
 
754
    g.setColor(fg);
755
    glyphPainter.paint(this, g, a, p0, p1);
756
    boolean underline = isUnderline();
757
    boolean striked = isStrikeThrough();
758
    if (underline || striked)
759
      {
760
        View parent = getParent();
761
        // X coordinate.
762
        if (parent != null && parent.getEndOffset() == p1)
763
          {
764
            // Strip whitespace.
765
            Segment s = getText(p0, p1);
766
            while (s.count > 0 && Character.isWhitespace(s.array[s.count - 1]))
767
              {
768
                p1--;
769
                s.count--;
770
              }
771
          }
772
        int x0 = r.x;
773
        int p = getStartOffset();
774
        TabExpander tabEx = getTabExpander();
775
        if (p != p0)
776
          x0 += (int) glyphPainter.getSpan(this, p, p0, tabEx, x0);
777
        int x1 = x0 + (int) glyphPainter.getSpan(this, p0, p1, tabEx, x0);
778
        // Y coordinate.
779
        int y = r.y + r.height - (int) glyphPainter.getDescent(this);
780
        if (underline)
781
          {
782
            int yTmp = y;
783
            yTmp += 1;
784
            g.drawLine(x0, yTmp, x1, yTmp);
785
          }
786
        if (striked)
787
          {
788
            int yTmp = y;
789
            yTmp -= (int) glyphPainter.getAscent(this);
790
            g.drawLine(x0, yTmp, x1, yTmp);
791
          }
792
      }
793
  }
794
 
795
 
796
  /**
797
   * Returns the preferred span of the content managed by this
798
   * <code>View</code> along the specified <code>axis</code>.
799
   *
800
   * @param axis the axis
801
   *
802
   * @return the preferred span of this <code>View</code>.
803
   */
804
  public float getPreferredSpan(int axis)
805
  {
806
    float span = 0;
807
    checkPainter();
808
    GlyphPainter painter = getGlyphPainter();
809
    switch (axis)
810
      {
811
      case X_AXIS:
812
        TabExpander tabEx = null;
813
        View parent = getParent();
814
        if (parent instanceof TabExpander)
815
          tabEx = (TabExpander) parent;
816
        span = painter.getSpan(this, getStartOffset(), getEndOffset(),
817
                               tabEx, 0.F);
818
        break;
819
      case Y_AXIS:
820
        span = painter.getHeight(this);
821
        if (isSuperscript())
822
          span += span / 3;
823
        break;
824
      default:
825
        throw new IllegalArgumentException("Illegal axis");
826
      }
827
    return span;
828
  }
829
 
830
  /**
831
   * Maps a position in the document into the coordinate space of the View.
832
   * The output rectangle usually reflects the font height but has a width
833
   * of zero.
834
   *
835
   * @param pos the position of the character in the model
836
   * @param a the area that is occupied by the view
837
   * @param b either {@link Position.Bias#Forward} or
838
   *        {@link Position.Bias#Backward} depending on the preferred
839
   *        direction bias. If <code>null</code> this defaults to
840
   *        <code>Position.Bias.Forward</code>
841
   *
842
   * @return a rectangle that gives the location of the document position
843
   *         inside the view coordinate space
844
   *
845
   * @throws BadLocationException if <code>pos</code> is invalid
846
   * @throws IllegalArgumentException if b is not one of the above listed
847
   *         valid values
848
   */
849
  public Shape modelToView(int pos, Shape a, Position.Bias b)
850
    throws BadLocationException
851
  {
852
    GlyphPainter p = getGlyphPainter();
853
    return p.modelToView(this, pos, b, a);
854
  }
855
 
856
  /**
857
   * Maps coordinates from the <code>View</code>'s space into a position
858
   * in the document model.
859
   *
860
   * @param x the x coordinate in the view space
861
   * @param y the y coordinate in the view space
862
   * @param a the allocation of this <code>View</code>
863
   * @param b the bias to use
864
   *
865
   * @return the position in the document that corresponds to the screen
866
   *         coordinates <code>x, y</code>
867
   */
868
  public int viewToModel(float x, float y, Shape a, Position.Bias[] b)
869
  {
870
    checkPainter();
871
    GlyphPainter painter = getGlyphPainter();
872
    return painter.viewToModel(this, x, y, a, b);
873
  }
874
 
875
  /**
876
   * Return the {@link TabExpander} to use.
877
   *
878
   * @return the {@link TabExpander} to use
879
   */
880
  public TabExpander getTabExpander()
881
  {
882
    return tabExpander;
883
  }
884
 
885
  /**
886
   * Returns the preferred span of this view for tab expansion.
887
   *
888
   * @param x the location of the view
889
   * @param te the tab expander to use
890
   *
891
   * @return the preferred span of this view for tab expansion
892
   */
893
  public float getTabbedSpan(float x, TabExpander te)
894
  {
895
    checkPainter();
896
    TabExpander old = tabExpander;
897
    tabExpander = te;
898
    if (tabExpander != old)
899
      {
900
        // Changing the tab expander will lead to a relayout in the X_AXIS.
901
        preferenceChanged(null, true, false);
902
      }
903
    tabX = x;
904
    return getGlyphPainter().getSpan(this, getStartOffset(),
905
                                     getEndOffset(), tabExpander, x);
906
  }
907
 
908
  /**
909
   * Returns the span of a portion of the view. This is used in TAB expansion
910
   * for fragments that don't contain TABs.
911
   *
912
   * @param p0 the start index
913
   * @param p1 the end index
914
   *
915
   * @return the span of the specified portion of the view
916
   */
917
  public float getPartialSpan(int p0, int p1)
918
  {
919
    checkPainter();
920
    return glyphPainter.getSpan(this, p0, p1, tabExpander, tabX);
921
  }
922
 
923
  /**
924
   * Returns the start offset in the document model of the portion
925
   * of text that this view is responsible for.
926
   *
927
   * @return the start offset in the document model of the portion
928
   *         of text that this view is responsible for
929
   */
930
  public int getStartOffset()
931
  {
932
    Element el = getElement();
933
    int offs = el.getStartOffset();
934
    if (length > 0)
935
      offs += offset;
936
    return offs;
937
  }
938
 
939
  /**
940
   * Returns the end offset in the document model of the portion
941
   * of text that this view is responsible for.
942
   *
943
   * @return the end offset in the document model of the portion
944
   *         of text that this view is responsible for
945
   */
946
  public int getEndOffset()
947
  {
948
    Element el = getElement();
949
    int offs;
950
    if (length > 0)
951
      offs = el.getStartOffset() + offset + length;
952
    else
953
      offs = el.getEndOffset();
954
    return offs;
955
  }
956
 
957
  private Segment cached = new Segment();
958
 
959
  /**
960
   * Returns the text segment that this view is responsible for.
961
   *
962
   * @param p0 the start index in the document model
963
   * @param p1 the end index in the document model
964
   *
965
   * @return the text segment that this view is responsible for
966
   */
967
  public Segment getText(int p0, int p1)
968
  {
969
    try
970
      {
971
        getDocument().getText(p0, p1 - p0, cached);
972
      }
973
    catch (BadLocationException ex)
974
      {
975
        AssertionError ae;
976
        ae = new AssertionError("BadLocationException should not be "
977
                                + "thrown here. p0 = " + p0 + ", p1 = " + p1);
978
        ae.initCause(ex);
979
        throw ae;
980
      }
981
 
982
    return cached;
983
  }
984
 
985
  /**
986
   * Returns the font for the text run for which this <code>GlyphView</code>
987
   * is responsible.
988
   *
989
   * @return the font for the text run for which this <code>GlyphView</code>
990
   *         is responsible
991
   */
992
  public Font getFont()
993
  {
994
    Document doc = getDocument();
995
    Font font = null;
996
    if (doc instanceof StyledDocument)
997
      {
998
        StyledDocument styledDoc = (StyledDocument) doc;
999
        font = styledDoc.getFont(getAttributes());
1000
      }
1001
    else
1002
      {
1003
        Container c = getContainer();
1004
        if (c != null)
1005
          font = c.getFont();
1006
      }
1007
    return font;
1008
  }
1009
 
1010
  /**
1011
   * Returns the foreground color which should be used to paint the text.
1012
   * This is fetched from the associated element's text attributes using
1013
   * {@link StyleConstants#getForeground}.
1014
   *
1015
   * @return the foreground color which should be used to paint the text
1016
   */
1017
  public Color getForeground()
1018
  {
1019
    Element el = getElement();
1020
    AttributeSet atts = el.getAttributes();
1021
    return StyleConstants.getForeground(atts);
1022
  }
1023
 
1024
  /**
1025
   * Returns the background color which should be used to paint the text.
1026
   * This is fetched from the associated element's text attributes using
1027
   * {@link StyleConstants#getBackground}.
1028
   *
1029
   * @return the background color which should be used to paint the text
1030
   */
1031
  public Color getBackground()
1032
  {
1033
    Element el = getElement();
1034
    AttributeSet atts = el.getAttributes();
1035
    // We cannot use StyleConstants.getBackground() here, because that returns
1036
    // BLACK as default (when background == null). What we need is the
1037
    // background setting of the text component instead, which is what we get
1038
    // when background == null anyway.
1039
    return (Color) atts.getAttribute(StyleConstants.Background);
1040
  }
1041
 
1042
  /**
1043
   * Determines whether the text should be rendered strike-through or not. This
1044
   * is determined using the method
1045
   * {@link StyleConstants#isStrikeThrough(AttributeSet)} on the element of
1046
   * this view.
1047
   *
1048
   * @return whether the text should be rendered strike-through or not
1049
   */
1050
  public boolean isStrikeThrough()
1051
  {
1052
    Element el = getElement();
1053
    AttributeSet atts = el.getAttributes();
1054
    return StyleConstants.isStrikeThrough(atts);
1055
  }
1056
 
1057
  /**
1058
   * Determines whether the text should be rendered as subscript or not. This
1059
   * is determined using the method
1060
   * {@link StyleConstants#isSubscript(AttributeSet)} on the element of
1061
   * this view.
1062
   *
1063
   * @return whether the text should be rendered as subscript or not
1064
   */
1065
  public boolean isSubscript()
1066
  {
1067
    Element el = getElement();
1068
    AttributeSet atts = el.getAttributes();
1069
    return StyleConstants.isSubscript(atts);
1070
  }
1071
 
1072
  /**
1073
   * Determines whether the text should be rendered as superscript or not. This
1074
   * is determined using the method
1075
   * {@link StyleConstants#isSuperscript(AttributeSet)} on the element of
1076
   * this view.
1077
   *
1078
   * @return whether the text should be rendered as superscript or not
1079
   */
1080
  public boolean isSuperscript()
1081
  {
1082
    Element el = getElement();
1083
    AttributeSet atts = el.getAttributes();
1084
    return StyleConstants.isSuperscript(atts);
1085
  }
1086
 
1087
  /**
1088
   * Determines whether the text should be rendered as underlined or not. This
1089
   * is determined using the method
1090
   * {@link StyleConstants#isUnderline(AttributeSet)} on the element of
1091
   * this view.
1092
   *
1093
   * @return whether the text should be rendered as underlined or not
1094
   */
1095
  public boolean isUnderline()
1096
  {
1097
    Element el = getElement();
1098
    AttributeSet atts = el.getAttributes();
1099
    return StyleConstants.isUnderline(atts);
1100
  }
1101
 
1102
  /**
1103
   * Creates and returns a shallow clone of this GlyphView. This is used by
1104
   * the {@link #createFragment} and {@link #breakView} methods.
1105
   *
1106
   * @return a shallow clone of this GlyphView
1107
   */
1108
  protected final Object clone()
1109
  {
1110
    try
1111
      {
1112
        return super.clone();
1113
      }
1114
    catch (CloneNotSupportedException ex)
1115
      {
1116
        AssertionError err = new AssertionError("CloneNotSupportedException "
1117
                                                + "must not be thrown here");
1118
        err.initCause(ex);
1119
        throw err;
1120
      }
1121
  }
1122
 
1123
  /**
1124
   * Tries to break the view near the specified view span <code>len</code>.
1125
   * The glyph view can only be broken in the X direction. For Y direction it
1126
   * returns itself.
1127
   *
1128
   * @param axis the axis for breaking, may be {@link View#X_AXIS} or
1129
   *        {@link View#Y_AXIS}
1130
   * @param p0 the model location where the fragment should start
1131
   * @param pos the view position along the axis where the fragment starts
1132
   * @param len the desired length of the fragment view
1133
   *
1134
   * @return the fragment view, or <code>this</code> if breaking was not
1135
   *         possible
1136
   */
1137
  public View breakView(int axis, int p0, float pos, float len)
1138
  {
1139
    View brokenView = this;
1140
    if (axis == X_AXIS)
1141
      {
1142
        checkPainter();
1143
        int end = glyphPainter.getBoundedPosition(this, p0, pos, len);
1144
        int breakLoc = getBreakLocation(p0, end);
1145
        if (breakLoc != -1)
1146
          end = breakLoc;
1147
        if (p0 != getStartOffset() || end != getEndOffset())
1148
          {
1149
            brokenView = createFragment(p0, end);
1150
            if (brokenView instanceof GlyphView)
1151
              ((GlyphView) brokenView).tabX = pos;
1152
          }
1153
      }
1154
    return brokenView;
1155
  }
1156
 
1157
  /**
1158
   * Determines how well the specified view location is suitable for inserting
1159
   * a line break. If <code>axis</code> is <code>View.Y_AXIS</code>, then
1160
   * this method forwards to the superclass, if <code>axis</code> is
1161
   * <code>View.X_AXIS</code> then this method returns
1162
   * {@link View#ExcellentBreakWeight} if there is a suitable break location
1163
   * (usually whitespace) within the specified view span, or
1164
   * {@link View#GoodBreakWeight} if not.
1165
   *
1166
   * @param axis the axis along which the break weight is requested
1167
   * @param pos the starting view location
1168
   * @param len the length of the span at which the view should be broken
1169
   *
1170
   * @return the break weight
1171
   */
1172
  public int getBreakWeight(int axis, float pos, float len)
1173
  {
1174
    int weight;
1175
    if (axis == Y_AXIS)
1176
      weight = super.getBreakWeight(axis, pos, len);
1177
    else
1178
      {
1179
        checkPainter();
1180
        int start = getStartOffset();
1181
        int end = glyphPainter.getBoundedPosition(this, start, pos, len);
1182
        if (end == 0)
1183
          weight = BadBreakWeight;
1184
        else
1185
          {
1186
            if (getBreakLocation(start, end) != -1)
1187
              weight = ExcellentBreakWeight;
1188
            else
1189
              weight = GoodBreakWeight;
1190
          }
1191
      }
1192
    return weight;
1193
  }
1194
 
1195
  private int getBreakLocation(int start, int end)
1196
  {
1197
    int loc = -1;
1198
    Segment s = getText(start, end);
1199
    for (char c = s.last(); c != Segment.DONE && loc == -1; c = s.previous())
1200
      {
1201
        if (Character.isWhitespace(c))
1202
          {
1203
            loc = s.getIndex() - s.getBeginIndex() + 1 + start;
1204
          }
1205
      }
1206
    return loc;
1207
  }
1208
 
1209
  /**
1210
   * Receives notification that some text attributes have changed within the
1211
   * text fragment that this view is responsible for. This calls
1212
   * {@link View#preferenceChanged(View, boolean, boolean)} on the parent for
1213
   * both width and height.
1214
   *
1215
   * @param e the document event describing the change; not used here
1216
   * @param a the view allocation on screen; not used here
1217
   * @param vf the view factory; not used here
1218
   */
1219
  public void changedUpdate(DocumentEvent e, Shape a, ViewFactory vf)
1220
  {
1221
    preferenceChanged(null, true, true);
1222
  }
1223
 
1224
  /**
1225
   * Receives notification that some text has been inserted within the
1226
   * text fragment that this view is responsible for. This calls
1227
   * {@link View#preferenceChanged(View, boolean, boolean)} for the
1228
   * direction in which the glyphs are rendered.
1229
   *
1230
   * @param e the document event describing the change; not used here
1231
   * @param a the view allocation on screen; not used here
1232
   * @param vf the view factory; not used here
1233
   */
1234
  public void insertUpdate(DocumentEvent e, Shape a, ViewFactory vf)
1235
  {
1236
    preferenceChanged(null, true, false);
1237
  }
1238
 
1239
  /**
1240
   * Receives notification that some text has been removed within the
1241
   * text fragment that this view is responsible for. This calls
1242
   * {@link View#preferenceChanged(View, boolean, boolean)} on the parent for
1243
   * width.
1244
   *
1245
   * @param e the document event describing the change; not used here
1246
   * @param a the view allocation on screen; not used here
1247
   * @param vf the view factory; not used here
1248
   */
1249
  public void removeUpdate(DocumentEvent e, Shape a, ViewFactory vf)
1250
  {
1251
    preferenceChanged(null, true, false);
1252
  }
1253
 
1254
  /**
1255
   * Creates a fragment view of this view that starts at <code>p0</code> and
1256
   * ends at <code>p1</code>.
1257
   *
1258
   * @param p0 the start location for the fragment view
1259
   * @param p1 the end location for the fragment view
1260
   *
1261
   * @return the fragment view
1262
   */
1263
  public View createFragment(int p0, int p1)
1264
  {
1265
    checkPainter();
1266
    Element el = getElement();
1267
    GlyphView fragment = (GlyphView) clone();
1268
    fragment.offset = p0 - el.getStartOffset();
1269
    fragment.length = p1 - p0;
1270
    fragment.glyphPainter = glyphPainter.getPainter(fragment, p0, p1);
1271
    return fragment;
1272
  }
1273
 
1274
  /**
1275
   * Returns the alignment of this view along the specified axis. For the Y
1276
   * axis this is <code>(height - descent) / height</code> for the used font,
1277
   * so that it is aligned along the baseline.
1278
   * For the X axis the superclass is called.
1279
   */
1280
  public float getAlignment(int axis)
1281
  {
1282
    checkPainter();
1283
    float align;
1284
    if (axis == Y_AXIS)
1285
      {
1286
        GlyphPainter painter = getGlyphPainter();
1287
        float height = painter.getHeight(this);
1288
        float descent = painter.getDescent(this);
1289
        float ascent = painter.getAscent(this);
1290
        if (isSuperscript())
1291
          align = 1.0F;
1292
        else if (isSubscript())
1293
          align = height > 0 ? (height - (descent + (ascent / 2))) / height
1294
                             : 0;
1295
        else
1296
          align = height > 0 ? (height - descent) / height : 0;
1297
      }
1298
    else
1299
      align = super.getAlignment(axis);
1300
 
1301
    return align;
1302
  }
1303
 
1304
  /**
1305
   * Returns the model location that should be used to place a caret when
1306
   * moving the caret through the document.
1307
   *
1308
   * @param pos the current model location
1309
   * @param bias the bias for <code>p</code>
1310
   * @param a the allocated region for the glyph view
1311
   * @param direction the direction from the current position; Must be one of
1312
   *        {@link SwingConstants#EAST}, {@link SwingConstants#WEST},
1313
   *        {@link SwingConstants#NORTH} or {@link SwingConstants#SOUTH}
1314
   * @param biasRet filled with the bias of the resulting location when method
1315
   *        returns
1316
   *
1317
   * @return the location within the document that should be used to place the
1318
   *         caret when moving the caret around the document
1319
   *
1320
   * @throws BadLocationException if <code>pos</code> is an invalid model
1321
   *         location
1322
   * @throws IllegalArgumentException if <code>d</code> is invalid
1323
   */
1324
  public int getNextVisualPositionFrom(int pos, Position.Bias bias, Shape a,
1325
                                       int direction, Position.Bias[] biasRet)
1326
    throws BadLocationException
1327
  {
1328
    checkPainter();
1329
    GlyphPainter painter = getGlyphPainter();
1330
    return painter.getNextVisualPositionFrom(this, pos, bias, a, direction,
1331
                                             biasRet);
1332
  }
1333
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.