TextLayout.java 18.8 KB
Newer Older
Tom Tromey committed
1
/* TextLayout.java --
2
   Copyright (C) 2006  Free Software Foundation, Inc.
Tom Tromey committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40

This file is part of GNU Classpath.

GNU Classpath is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

GNU Classpath is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU Classpath; see the file COPYING.  If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA.

Linking this library statically or dynamically with other modules is
making a combined work based on this library.  Thus, the terms and
conditions of the GNU General Public License cover the whole
combination.

As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent
modules, and to copy and distribute the resulting executable under
terms of your choice, provided that you also meet, for each linked
independent module, the terms and conditions of the license of that
module.  An independent module is a module which is not derived from
or based on this library.  If you modify this library, you may extend
this exception to your version of the library, but you are not
obligated to do so.  If you do not wish to do so, delete this
exception statement from your version. */


package java.awt.font;

41
import gnu.classpath.NotImplementedException;
Tom Tromey committed
42 43 44 45 46 47

import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
48 49
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
50
import java.text.CharacterIterator;
Tom Tromey committed
51
import java.text.AttributedCharacterIterator;
52
import java.text.Bidi;
Tom Tromey committed
53 54 55
import java.util.Map;

/**
56
 * @author Sven de Marothy
Tom Tromey committed
57 58 59
 */
public final class TextLayout implements Cloneable
{
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
  private GlyphVector[] runs;
  private Font font;
  private FontRenderContext frc;
  private String string;
  private Rectangle2D boundsCache;
  private LineMetrics lm;

  /**
   * Start and end character indices of the runs.
   * First index is the run number, second is 0 or 1 for the starting 
   * and ending character index of the run, respectively.
   */
  private int[][] runIndices;

  /**
75 76 77 78 79 80
   * Character indices.
   * Fixt index is the glyphvector, second index is the (first) glyph.
   */
  private int[][] charIndices;

  /**
81 82 83 84 85 86 87 88 89 90 91 92
   * Base directionality, determined from the first char.
   */
  private boolean leftToRight;

  /**
   * Whether this layout contains whitespace or not.
   */
  private boolean hasWhitespace = false;

  /**
   * The default caret policy.
   */
93
  public static final TextLayout.CaretPolicy DEFAULT_CARET_POLICY = new CaretPolicy();
94 95 96 97 98

  /**
   * Constructs a TextLayout.
   */
  public TextLayout (String string, Font font, FontRenderContext frc) 
Tom Tromey committed
99
  {
100 101 102 103
    this.font = font;
    this.frc = frc;
    this.string = string;
    lm = font.getLineMetrics(string, frc);
Tom Tromey committed
104

105 106
    // Get base direction and whitespace info
    getStringProperties();
Tom Tromey committed
107

108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
    if( Bidi.requiresBidi( string.toCharArray(), 0, string.length() ) )
      {
	Bidi bidi = new Bidi( string, leftToRight ? 
			      Bidi.DIRECTION_LEFT_TO_RIGHT : 
			      Bidi.DIRECTION_RIGHT_TO_LEFT );
	int rc = bidi.getRunCount();
	byte[] table = new byte[ rc ];
	for(int i = 0; i < table.length; i++)
	  table[i] = (byte)bidi.getRunLevel(i);

	runs = new GlyphVector[ rc ];
	runIndices = new int[rc][2];
	for(int i = 0; i < runs.length; i++)
	  {
	    runIndices[i][0] = bidi.getRunStart( i );
	    runIndices[i][1] = bidi.getRunLimit( i );
	    if( runIndices[i][0] != runIndices[i][1] ) // no empty runs.
	      {
		runs[i] = font.layoutGlyphVector
		  ( frc, string.toCharArray(),
		    runIndices[i][0], runIndices[i][1],
		    ((table[i] & 1) == 0) ? Font.LAYOUT_LEFT_TO_RIGHT :
		    Font.LAYOUT_RIGHT_TO_LEFT );
	      }
	  }
	Bidi.reorderVisually( table, 0, runs, 0, runs.length );
      }
    else
      {
	runs = new GlyphVector[ 1 ];
	runIndices = new int[1][2];
	runIndices[0][0] = 0;
	runIndices[0][1] = string.length();
	runs[ 0 ] = font.layoutGlyphVector( frc, string.toCharArray(), 
					    0, string.length(),
					    leftToRight ?
					    Font.LAYOUT_LEFT_TO_RIGHT :
					    Font.LAYOUT_RIGHT_TO_LEFT );
      }
147
    setCharIndices();
Tom Tromey committed
148 149
  }

150
  public TextLayout (String string, Map attributes, FontRenderContext frc)  
Tom Tromey committed
151
  {
152
    this( string, new Font( attributes ), frc );
Tom Tromey committed
153 154
  }

155 156
  public TextLayout (AttributedCharacterIterator text, FontRenderContext frc)
  {
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
    // FIXME: Very rudimentary.
    this(getText(text), getFont(text), frc);
  }

  /**
   * Package-private constructor to make a textlayout from an existing one.
   * This is used by TextMeasurer for returning sub-layouts, and it 
   * saves a lot of time in not having to relayout the text.
   */
  TextLayout(TextLayout t, int startIndex, int endIndex)
  {
    font = t.font;
    frc = t.frc;
    boundsCache = null;
    lm = t.lm;
    leftToRight = t.leftToRight;

    if( endIndex > t.getCharacterCount() )
      endIndex = t.getCharacterCount();
    string = t.string.substring( startIndex, endIndex );

    int startingRun = t.charIndices[startIndex][0];
    int nRuns = 1 + t.charIndices[endIndex - 1][0] - startingRun;
    runIndices = new int[ nRuns ][2];

    runs = new GlyphVector[ nRuns ];
    for( int i = 0; i < nRuns; i++ )
      {
	GlyphVector run = t.runs[ i + startingRun ];
	// Copy only the relevant parts of the first and last runs.
	int beginGlyphIndex = (i > 0) ? 0 : t.charIndices[startIndex][1];
	int numEntries = ( i < nRuns - 1) ? run.getNumGlyphs() : 
	  1 + t.charIndices[endIndex - 1][1] - beginGlyphIndex;
	
	int[] codes = run.getGlyphCodes(beginGlyphIndex, numEntries, null);
	runs[ i ] = font.createGlyphVector( frc, codes );
	runIndices[ i ][0] = t.runIndices[i + startingRun][0] - startIndex;
	runIndices[ i ][1] = t.runIndices[i + startingRun][1] - startIndex;
      }
    runIndices[ nRuns - 1 ][1] = endIndex - 1;

    setCharIndices();
    determineWhiteSpace();
  }

  private void setCharIndices()
  {
    charIndices = new int[ getCharacterCount() ][2];
    int i = 0;
    int currentChar = 0;
    for(int run = 0; run < runs.length; run++)
      {
	currentChar = -1;
	for( int gi = 0; gi < runs[ run ].getNumGlyphs(); gi++)
	  {
	    if( runs[ run ].getGlyphCharIndex( gi ) != currentChar )
	      {
		charIndices[ i ][0] = run;
		charIndices[ i ][1] = gi;
		currentChar = runs[ run ].getGlyphCharIndex( gi );
		i++;
	      }
	  }
      }
  }

  private static String getText(AttributedCharacterIterator iter)
  {
    StringBuffer sb = new StringBuffer();
    int idx = iter.getIndex();
    for(char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) 
      sb.append(c);
    iter.setIndex( idx );
    return sb.toString();
  }

  private static Font getFont(AttributedCharacterIterator iter)
  {
    Font f = (Font)iter.getAttribute(TextAttribute.FONT);
    if( f == null )
      {
	int size;
	Float i = (Float)iter.getAttribute(TextAttribute.SIZE);
	if( i != null )
	  size = (int)i.floatValue();
	else
	  size = 14;
	f = new Font("Dialog", Font.PLAIN, size );
      }
    return f;
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
  }

  /**
   * Scan the character run for the first strongly directional character,
   * which in turn defines the base directionality of the whole layout.
   */
  private void getStringProperties()
  {
    boolean gotDirection = false;
    int i = 0;

    leftToRight = true;
    while( i < string.length() && !gotDirection )
      switch( Character.getDirectionality( string.charAt( i++ ) ) )
	{
	case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
	case Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING:
	case Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE:
	  gotDirection = true;
	  break;
	  
	case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
	case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
	case Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING:
	case Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE:
	  leftToRight = false;
	  gotDirection = true;
	  break;
	}
276 277
    determineWhiteSpace();
  }
278

279 280
  private void determineWhiteSpace()
  {
281 282
    // Determine if there's whitespace in the thing.
    // Ignore trailing chars.
283
    int i = string.length() - 1; 
284 285 286 287 288 289 290
    hasWhitespace = false;
    while( i >= 0 && Character.isWhitespace( string.charAt(i) ) )
      i--;
    // Check the remaining chars
    while( i >= 0 )
      if( Character.isWhitespace( string.charAt(i--) ) )
	hasWhitespace = true;
Tom Tromey committed
291 292 293 294
  }

  protected Object clone ()
  {
295
    return new TextLayout( string, font, frc );
Tom Tromey committed
296 297 298
  }

  public void draw (Graphics2D g2, float x, float y) 
299 300 301 302 303 304 305
  {    
    for(int i = 0; i < runs.length; i++)
      {
	g2.drawGlyphVector(runs[i], x, y);
	Rectangle2D r = runs[i].getLogicalBounds();
	x += r.getWidth();
      }
Tom Tromey committed
306 307 308 309
  }

  public boolean equals (Object obj)
  {
310
    if( !( obj instanceof TextLayout) )
Tom Tromey committed
311 312
      return false;

313
    return equals( (TextLayout) obj );
Tom Tromey committed
314 315 316 317
  }

  public boolean equals (TextLayout tl)
  {
318 319 320 321 322 323 324
    if( runs.length != tl.runs.length )
      return false;
    // Compare all glyph vectors.
    for( int i = 0; i < runs.length; i++ )
      if( !runs[i].equals( tl.runs[i] ) )
	return false;
    return true;
Tom Tromey committed
325 326 327 328
  }

  public float getAdvance ()
  {
329 330 331 332
    float totalAdvance = 0f;
    for(int i = 0; i < runs.length; i++)
      totalAdvance += runs[i].getLogicalBounds().getWidth();
    return totalAdvance;
Tom Tromey committed
333 334 335 336
  }

  public float getAscent ()
  {
337
    return lm.getAscent();
Tom Tromey committed
338 339 340 341
  }

  public byte getBaseline ()
  {
342
    return (byte)lm.getBaselineIndex();
Tom Tromey committed
343 344 345 346
  }

  public float[] getBaselineOffsets ()
  {
347
    return lm.getBaselineOffsets();
Tom Tromey committed
348 349 350 351
  }

  public Shape getBlackBoxBounds (int firstEndpoint, int secondEndpoint)
  {
352 353 354 355
    if( secondEndpoint - firstEndpoint <= 0 )
      return new Rectangle2D.Float(); // Hmm? 

    if( firstEndpoint < 0 || secondEndpoint > getCharacterCount())
356 357 358
      return new Rectangle2D.Float();

    GeneralPath gp = new GeneralPath();
359 360 361
    
    int ri = charIndices[ firstEndpoint ][0];
    int gi = charIndices[ firstEndpoint ][1];
362

363 364 365 366 367 368
    double advance = 0;
   
    for( int i = 0; i < ri; i++ )
      advance += runs[i].getLogicalBounds().getWidth();
    
    for( int i = ri; i <= charIndices[ secondEndpoint - 1 ][0]; i++ )
369
      {
370 371 372 373 374
	int dg;
	if( i == charIndices[ secondEndpoint - 1 ][0] )
	  dg = charIndices[ secondEndpoint - 1][1];
	else
	  dg = runs[i].getNumGlyphs() - 1;
375

376
	for( int j = 0; j <= dg; j++ )
377 378 379 380
	  {
	    Rectangle2D r2 = (runs[i].getGlyphVisualBounds( j )).
	      getBounds2D();
	    Point2D p = runs[i].getGlyphPosition( j );
381
	    r2.setRect( advance + r2.getX(), r2.getY(), 
382 383 384 385
			r2.getWidth(), r2.getHeight() );
	    gp.append(r2, false);
	  }

386
	advance += runs[i].getLogicalBounds().getWidth();
387 388
      }
    return gp;
Tom Tromey committed
389 390 391 392
  }

  public Rectangle2D getBounds()
  {
393 394 395
    if( boundsCache == null )
      boundsCache = getOutline(new AffineTransform()).getBounds();
    return boundsCache;
Tom Tromey committed
396 397 398 399 400 401 402 403
  }

  public float[] getCaretInfo (TextHitInfo hit)
  {
    return getCaretInfo(hit, getBounds());
  }

  public float[] getCaretInfo (TextHitInfo hit, Rectangle2D bounds)
404
    throws NotImplementedException
Tom Tromey committed
405
  {
406
    throw new Error ("not implemented");
Tom Tromey committed
407 408 409 410
  }

  public Shape getCaretShape (TextHitInfo hit)
  {
411
    return getCaretShape( hit, getBounds() );
Tom Tromey committed
412 413 414
  }

  public Shape getCaretShape (TextHitInfo hit, Rectangle2D bounds)
415
    throws NotImplementedException
Tom Tromey committed
416
  {
417
    throw new Error ("not implemented");
Tom Tromey committed
418 419 420 421
  }

  public Shape[] getCaretShapes (int offset)
  {
422
    return getCaretShapes( offset, getBounds() );
Tom Tromey committed
423 424 425
  }

  public Shape[] getCaretShapes (int offset, Rectangle2D bounds)
426
    throws NotImplementedException
Tom Tromey committed
427
  {
428
    throw new Error ("not implemented");
Tom Tromey committed
429 430 431 432
  }

  public int getCharacterCount ()
  {
433
    return string.length();
Tom Tromey committed
434 435 436
  }

  public byte getCharacterLevel (int index)
437
    throws NotImplementedException
Tom Tromey committed
438
  {
439
    throw new Error ("not implemented");
Tom Tromey committed
440 441 442 443
  }

  public float getDescent ()
  {
444
    return lm.getDescent();
Tom Tromey committed
445 446 447 448
  }

  public TextLayout getJustifiedLayout (float justificationWidth)
  {
449 450 451 452 453 454
    TextLayout newLayout = (TextLayout)clone();

    if( hasWhitespace )
      newLayout.handleJustify( justificationWidth );

    return newLayout;
Tom Tromey committed
455 456 457 458
  }

  public float getLeading ()
  {
459
    return lm.getLeading();
Tom Tromey committed
460 461 462 463
  }

  public Shape getLogicalHighlightShape (int firstEndpoint, int secondEndpoint)
  {
464 465
    return getLogicalHighlightShape( firstEndpoint, secondEndpoint, 
				     getBounds() );
Tom Tromey committed
466 467 468 469 470
  }

  public Shape getLogicalHighlightShape (int firstEndpoint, int secondEndpoint,
                                         Rectangle2D bounds)
  {
471 472 473 474
    if( secondEndpoint - firstEndpoint <= 0 )
      return new Rectangle2D.Float(); // Hmm? 

    if( firstEndpoint < 0 || secondEndpoint > getCharacterCount())
475 476
      return new Rectangle2D.Float();

477 478 479
    Rectangle2D r = null;
    int ri = charIndices[ firstEndpoint ][0];
    int gi = charIndices[ firstEndpoint ][1];
480

481 482 483 484
    double advance = 0;
   
    for( int i = 0; i < ri; i++ )
      advance += runs[i].getLogicalBounds().getWidth();
485

486
    for( int i = ri; i <= charIndices[ secondEndpoint - 1 ][0]; i++ )
487
      {
488 489 490 491 492
	int dg; // last index in this run to use.
	if( i == charIndices[ secondEndpoint - 1 ][0] )
	  dg = charIndices[ secondEndpoint - 1][1];
	else
	  dg = runs[i].getNumGlyphs() - 1;
493

494
	for(; gi <= dg; gi++ )
495
	  {
496
	    Rectangle2D r2 = (runs[i].getGlyphLogicalBounds( gi )).
497
	      getBounds2D();
498 499 500 501
	    if( r == null )
	      r = r2;
	    else
	      r = r.createUnion(r2);
502
	  }
503
	gi = 0; // reset glyph index into run for next run.
504

505
	advance += runs[i].getLogicalBounds().getWidth();
506 507 508
      }

    return r;
Tom Tromey committed
509 510 511 512
  }

  public int[] getLogicalRangesForVisualSelection (TextHitInfo firstEndpoint,
                                                   TextHitInfo secondEndpoint)
513
    throws NotImplementedException
Tom Tromey committed
514
  {
515
    throw new Error ("not implemented");
Tom Tromey committed
516 517 518
  }

  public TextHitInfo getNextLeftHit (int offset)
519
    throws NotImplementedException
Tom Tromey committed
520
  {
521
    throw new Error ("not implemented");
Tom Tromey committed
522 523 524
  }

  public TextHitInfo getNextLeftHit (TextHitInfo hit)
525
    throws NotImplementedException
Tom Tromey committed
526
  {
527
    throw new Error ("not implemented");
Tom Tromey committed
528 529 530
  }

  public TextHitInfo getNextRightHit (int offset)
531
    throws NotImplementedException
Tom Tromey committed
532
  {
533
    throw new Error ("not implemented");
Tom Tromey committed
534 535 536
  }

  public TextHitInfo getNextRightHit (TextHitInfo hit)
537
    throws NotImplementedException
Tom Tromey committed
538
  {
539
    throw new Error ("not implemented");
Tom Tromey committed
540 541 542 543
  }

  public Shape getOutline (AffineTransform tx)
  {
544 545 546 547 548 549 550 551 552 553 554
    float x = 0f;
    GeneralPath gp = new GeneralPath();
    for(int i = 0; i < runs.length; i++)
      {
	gp.append( runs[i].getOutline( x, 0f ), false );
	Rectangle2D r = runs[i].getLogicalBounds();
	x += r.getWidth();
      }
    if( tx != null )
      gp.transform( tx );
    return gp;
Tom Tromey committed
555 556 557 558
  }

  public float getVisibleAdvance ()
  {
559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
    float totalAdvance = 0f;

    if( runs.length <= 0 )
      return 0f;

    // No trailing whitespace
    if( !Character.isWhitespace( string.charAt( string.length() -1 ) ) )
      return getAdvance();

    // Get length of all runs up to the last
    for(int i = 0; i < runs.length - 1; i++)
      totalAdvance += runs[i].getLogicalBounds().getWidth();

    int lastRun = runIndices[ runs.length - 1 ][0];
    int j = string.length() - 1;
    while( j >= lastRun && Character.isWhitespace( string.charAt( j ) ) ) j--;

    if( j < lastRun )
      return totalAdvance; // entire last run is whitespace

    int lastNonWSChar = j - lastRun;
    j = 0;
    while( runs[ runs.length - 1 ].getGlyphCharIndex( j )
	   <= lastNonWSChar )
      {
	totalAdvance += runs[ runs.length - 1 ].getGlyphLogicalBounds( j ).
	  getBounds2D().getWidth();
	j ++;
      }
    
    return totalAdvance;
Tom Tromey committed
590 591 592 593 594
  }

  public Shape getVisualHighlightShape (TextHitInfo firstEndpoint,
                                        TextHitInfo secondEndpoint)
  {
595 596
    return getVisualHighlightShape( firstEndpoint, secondEndpoint, 
				    getBounds() );
Tom Tromey committed
597 598 599 600 601
  }

  public Shape getVisualHighlightShape (TextHitInfo firstEndpoint,
                                        TextHitInfo secondEndpoint,
                                        Rectangle2D bounds)
602
    throws NotImplementedException
Tom Tromey committed
603
  {
604
    throw new Error ("not implemented");
Tom Tromey committed
605 606 607
  }

  public TextHitInfo getVisualOtherHit (TextHitInfo hit)
608
    throws NotImplementedException
Tom Tromey committed
609
  {
610
    throw new Error ("not implemented");
Tom Tromey committed
611 612
  }

613 614 615 616
  /**
   * This is a protected method of a <code>final</code> class, meaning
   * it exists only to taunt you.
   */
Tom Tromey committed
617 618
  protected void handleJustify (float justificationWidth)
  {
619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658
    // We assume that the text has non-trailing whitespace.
    // First get the change in width to insert into the whitespaces.
    double deltaW = justificationWidth - getVisibleAdvance();
    int nglyphs = 0; // # of whitespace chars

    // determine last non-whitespace char.
    int lastNWS = string.length() - 1;
    while( Character.isWhitespace( string.charAt( lastNWS ) ) ) lastNWS--;

    // locations of the glyphs.
    int[] wsglyphs = new int[string.length() * 10];
    for(int run = 0; run < runs.length; run++ )
      for(int i = 0; i < runs[run].getNumGlyphs(); i++ )
	{
	  int cindex = runIndices[run][0] + runs[run].getGlyphCharIndex( i );
	  if( Character.isWhitespace( string.charAt( cindex ) ) )
	    //	      && cindex < lastNWS )
	    {
	      wsglyphs[ nglyphs * 2 ] = run;
	      wsglyphs[ nglyphs * 2 + 1] = i;
	      nglyphs++;
	    }
	}

    deltaW = deltaW / nglyphs; // Change in width per whitespace glyph
    double w = 0;
    int cws = 0;
    // Shift all characters
    for(int run = 0; run < runs.length; run++ )
      for(int i = 0; i < runs[ run ].getNumGlyphs(); i++ )
	{
	  if( wsglyphs[ cws * 2 ] == run && wsglyphs[ cws * 2 + 1 ] == i )
	    {
	      cws++; // update 'current whitespace'
	      w += deltaW; // increment the shift
	    }
	  Point2D p = runs[ run ].getGlyphPosition( i );
	  p.setLocation( p.getX() + w, p.getY() );
	  runs[ run ].setGlyphPosition( i, p );
	}
Tom Tromey committed
659 660 661 662 663 664 665 666
  }

  public TextHitInfo hitTestChar (float x, float y)
  {
    return hitTestChar(x, y, getBounds());
  }

  public TextHitInfo hitTestChar (float x, float y, Rectangle2D bounds)
667
    throws NotImplementedException
Tom Tromey committed
668
  {
669
    throw new Error ("not implemented");
Tom Tromey committed
670 671 672 673
  }

  public boolean isLeftToRight ()
  {
674
    return leftToRight;
Tom Tromey committed
675 676 677 678
  }

  public boolean isVertical ()
  {
679 680 681 682 683 684 685
    return false; // FIXME: How do you create a vertical layout?
  }

  public int hashCode ()
    throws NotImplementedException
  {
    throw new Error ("not implemented");
Tom Tromey committed
686 687 688 689
  }

  public String toString ()
  {
690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709
    return "TextLayout [string:"+string+", Font:"+font+" Rendercontext:"+
      frc+"]";
  }

  /**
   * Inner class describing a caret policy
   */
  public static class CaretPolicy
  {
    public CaretPolicy()
    {
    }

    public TextHitInfo getStrongCaret(TextHitInfo hit1,
				      TextHitInfo hit2,
				      TextLayout layout)
      throws NotImplementedException
    {
      throw new Error ("not implemented");
    }
Tom Tromey committed
710 711
  }
}
712 713