Source for gnu.java.awt.peer.gtk.GdkTextLayout

   1: /* GdkTextLayout.java
   2:    Copyright (C) 2003, 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 gnu.java.awt.peer.gtk;
  40: 
  41: import gnu.classpath.Configuration;
  42: import gnu.java.awt.peer.ClasspathTextLayoutPeer;
  43: 
  44: import java.awt.Font;
  45: import java.awt.Graphics2D;
  46: import java.awt.Shape;
  47: import java.awt.font.FontRenderContext;
  48: import java.awt.font.GlyphMetrics;
  49: import java.awt.font.GlyphVector;
  50: import java.awt.font.TextAttribute;
  51: import java.awt.font.TextHitInfo;
  52: import java.awt.font.TextLayout;
  53: import java.awt.geom.AffineTransform;
  54: import java.awt.geom.GeneralPath;
  55: import java.awt.geom.Rectangle2D;
  56: import java.text.AttributedCharacterIterator;
  57: import java.text.AttributedString;
  58: import java.text.CharacterIterator;
  59: 
  60: /**
  61:  * This is an implementation of the text layout peer interface which
  62:  * delegates all the hard work to pango.
  63:  *
  64:  * @author Graydon Hoare
  65:  */
  66: 
  67: public class GdkTextLayout 
  68:   implements ClasspathTextLayoutPeer
  69: {
  70:   // native side, plumbing, etc.
  71:   static 
  72:   {
  73:     if (Configuration.INIT_LOAD_LIBRARY)
  74:       {
  75:         System.loadLibrary("gtkpeer");
  76:       }
  77:     initStaticState ();
  78:   }
  79:   private native void setText(String str);
  80:   private native void getExtents(double[] inkExtents,
  81:                                  double[] logExtents);
  82:   private native void indexToPos(int idx, double[] pos);
  83:   private native void initState ();
  84:   private native void dispose ();
  85:   static native void initStaticState();
  86:   private final int native_state = GtkGenericPeer.getUniqueInteger ();
  87:   protected void finalize ()
  88:   {
  89:     dispose ();
  90:   }
  91: 
  92:   // we hold on to these to make sure we can render when presented
  93:   // with non-GdkGraphics2D paint targets
  94:   private AttributedString attributedString;
  95:   private FontRenderContext fontRenderContext;
  96: 
  97:   public GdkTextLayout(AttributedString str, FontRenderContext frc)
  98:   {
  99:     initState();
 100:     attributedString = str;
 101:     fontRenderContext = frc;
 102:   }
 103: 
 104:   protected class CharacterIteratorProxy 
 105:     implements CharacterIterator
 106:   {
 107:     public CharacterIterator target;
 108:     public int begin;
 109:     public int limit;
 110:     public int index;
 111: 
 112:     public CharacterIteratorProxy (CharacterIterator ci)
 113:     {
 114:       target = ci;
 115:     }
 116: 
 117:     public int getBeginIndex ()
 118:     {
 119:       return begin;
 120:     }
 121: 
 122:     public int getEndIndex ()
 123:     {
 124:       return limit;
 125:     }
 126: 
 127:     public int getIndex ()
 128:     {
 129:       return index;
 130:     }
 131: 
 132:     public char setIndex (int idx) 
 133:       throws IllegalArgumentException
 134:     {
 135:       if (idx < begin || idx >= limit)
 136:         throw new IllegalArgumentException ();
 137:       char ch = target.setIndex (idx);
 138:       index = idx;
 139:       return ch;
 140:     }
 141: 
 142:     public char first ()
 143:     {
 144:       int save = target.getIndex ();
 145:       char ch = target.setIndex (begin);
 146:       target.setIndex (save);
 147:       return ch;
 148:     }
 149: 
 150:     public char last ()
 151:     {
 152:       if (begin == limit)
 153:         return this.first ();
 154: 
 155:       int save = target.getIndex ();
 156:       char ch = target.setIndex (limit - 1);
 157:       target.setIndex (save);
 158:       return ch;
 159:     }
 160: 
 161:     public char current ()
 162:     {
 163:       return target.current();
 164:     }
 165: 
 166:     public char next ()
 167:     {
 168:       if (index >= limit - 1)
 169:         return CharacterIterator.DONE;
 170:       else
 171:         {
 172:           index++;
 173:           return target.next();
 174:         }
 175:     }
 176: 
 177:     public char previous ()
 178:     {
 179:       if (index <= begin)
 180:         return CharacterIterator.DONE;
 181:       else
 182:         {
 183:           index--;
 184:           return target.previous ();
 185:         }
 186:     }
 187: 
 188:     public Object clone ()
 189:     {
 190:       CharacterIteratorProxy cip = new CharacterIteratorProxy (this.target);
 191:       cip.begin = this.begin;
 192:       cip.limit = this.limit;
 193:       cip.index = this.index;
 194:       return cip;
 195:     }
 196:     
 197:   }
 198: 
 199: 
 200:   // public side
 201: 
 202:   public void draw (Graphics2D g2, float x, float y)
 203:   {
 204:     if (g2 instanceof GdkGraphics2D)
 205:       {
 206:         // we share pango structures directly with GdkGraphics2D 
 207:         // when legal
 208:         GdkGraphics2D gg2 = (GdkGraphics2D) g2;
 209:         gg2.drawGdkTextLayout(this, x, y);
 210:       }
 211:     else 
 212:       {
 213:         // falling back to a rather tedious layout algorithm when
 214:         // not legal
 215:         AttributedCharacterIterator ci = attributedString.getIterator ();
 216:         CharacterIteratorProxy proxy = new CharacterIteratorProxy (ci);
 217:         Font defFont = g2.getFont ();
 218: 
 219:         /* Note: this implementation currently only interprets FONT text
 220:          * attributes. There is a reasonable argument to be made for some
 221:          * attributes being interpreted out here, where we have control of the
 222:          * Graphics2D and can construct or derive new fonts, and some
 223:          * attributes being interpreted by the GlyphVector itself. So far, for
 224:          * all attributes except FONT we do neither.
 225:          */
 226: 
 227:         for (char c = ci.first ();
 228:              c != CharacterIterator.DONE;
 229:              c = ci.next ())
 230:           {                
 231:             proxy.begin = ci.getIndex ();
 232:             proxy.limit = ci.getRunLimit(TextAttribute.FONT);
 233:             if (proxy.limit <= proxy.begin)
 234:               continue;
 235: 
 236:             proxy.index = proxy.begin;
 237: 
 238:             Object fnt = ci.getAttribute(TextAttribute.FONT);
 239:             GlyphVector gv;
 240:             if (fnt instanceof Font)
 241:               gv = ((Font)fnt).createGlyphVector (fontRenderContext, proxy);
 242:             else
 243:               gv = defFont.createGlyphVector (fontRenderContext, proxy);
 244: 
 245:             g2.drawGlyphVector (gv, x, y);
 246: 
 247:             int n = gv.getNumGlyphs ();
 248:             for (int i = 0; i < n; ++i)
 249:               {
 250:                 GlyphMetrics gm = gv.getGlyphMetrics (i);
 251:                 if (gm.getAdvanceX() == gm.getAdvance ())
 252:                   x += gm.getAdvanceX ();
 253:                 else
 254:                   y += gm.getAdvanceY ();
 255:               }
 256:           }
 257:       }
 258:   }
 259: 
 260:   public TextHitInfo getStrongCaret (TextHitInfo hit1, 
 261:                                      TextHitInfo hit2)
 262:   {
 263:     throw new Error("not implemented");
 264:   }
 265: 
 266:   public byte getBaseline ()
 267:   {
 268:     throw new Error("not implemented");
 269:   }
 270: 
 271:   public boolean isLeftToRight ()
 272:   {
 273:     throw new Error("not implemented");
 274:   }
 275: 
 276:   public boolean isVertical ()
 277:   {
 278:     throw new Error("not implemented");
 279:   }
 280: 
 281:   public float getAdvance ()
 282:   {
 283:     throw new Error("not implemented");
 284:   }
 285: 
 286:   public float getAscent ()
 287:   {
 288:     throw new Error("not implemented");
 289:   }
 290: 
 291:   public float getDescent ()
 292:   {
 293:     throw new Error("not implemented");
 294:   }
 295: 
 296:   public float getLeading ()
 297:   {
 298:     throw new Error("not implemented");
 299:   }
 300: 
 301:   public int getCharacterCount ()
 302:   {
 303:     throw new Error("not implemented");
 304:   }
 305: 
 306:   public byte getCharacterLevel (int index)
 307:   {
 308:     throw new Error("not implemented");
 309:   }
 310: 
 311:   public float[] getBaselineOffsets ()
 312:   {
 313:     throw new Error("not implemented");
 314:   }
 315: 
 316:   public Shape getBlackBoxBounds (int firstEndpoint, int secondEndpoint)
 317:   {
 318:     throw new Error("not implemented");
 319:   }
 320: 
 321:   public Rectangle2D getBounds ()
 322:   {
 323:     double[] inkExtents = new double[4];
 324:     double[] logExtents = new double[4];
 325:     getExtents(inkExtents, logExtents);
 326:     return new Rectangle2D.Double(logExtents[0], logExtents[1],
 327:                                   logExtents[2], logExtents[3]);
 328:   }
 329: 
 330:   public float[] getCaretInfo (TextHitInfo hit, Rectangle2D bounds)
 331:   {
 332:     throw new Error("not implemented");
 333:   }
 334: 
 335:   public Shape getCaretShape (TextHitInfo hit, Rectangle2D bounds)
 336:   {
 337:     throw new Error("not implemented");
 338:   }
 339: 
 340:   public Shape[] getCaretShapes (int offset, Rectangle2D bounds,
 341:                                  TextLayout.CaretPolicy policy)
 342:   {
 343:     throw new Error("not implemented");
 344:   }
 345: 
 346:   public Shape getLogicalHighlightShape (int firstEndpoint, int secondEndpoint,
 347:                                          Rectangle2D bounds)
 348:   {
 349:     AffineTransform at = new AffineTransform();
 350:     GeneralPath gp = new GeneralPath();
 351:     double [] rect = new double[4];
 352:     Rectangle2D tmp = new Rectangle2D.Double();
 353:     for (int i = firstEndpoint; i <= secondEndpoint; ++i)
 354:       {
 355:         indexToPos(i, rect);
 356:         tmp.setRect(rect[0], rect[1], rect[2], rect[3]);
 357:         Rectangle2D.intersect(tmp, bounds, tmp);
 358:         gp.append(tmp.getPathIterator(at), false);
 359:       }
 360:     return gp;
 361:   }
 362: 
 363:   public int[] getLogicalRangesForVisualSelection (TextHitInfo firstEndpoint,
 364:                                                    TextHitInfo secondEndpoint)
 365:   {
 366:     throw new Error("not implemented");
 367:   }
 368:   
 369:   public TextHitInfo getNextLeftHit (int offset, TextLayout.CaretPolicy policy)
 370:   {
 371:     throw new Error("not implemented");
 372:   }
 373:   public TextHitInfo getNextRightHit (int offset, TextLayout.CaretPolicy policy)
 374:   {
 375:     throw new Error("not implemented");
 376:   }
 377:   public TextHitInfo hitTestChar (float x, float y, Rectangle2D bounds)
 378:   {
 379:     throw new Error("not implemented");
 380:   }
 381:   public TextHitInfo getVisualOtherHit (TextHitInfo hit)
 382:   {
 383:     throw new Error("not implemented");
 384:   }
 385: 
 386:   public float getVisibleAdvance ()
 387:   {
 388:     throw new Error("not implemented");
 389:   }
 390: 
 391:   public native Shape getOutline (AffineTransform tx);
 392: 
 393:   public Shape getVisualHighlightShape (TextHitInfo firstEndpoint,
 394:                                         TextHitInfo secondEndpoint,
 395:                                         Rectangle2D bounds)
 396:   {
 397:     throw new Error("not implemented");
 398:   }
 399: 
 400:   
 401:   public TextLayout getJustifiedLayout (float justificationWidth)
 402:   {
 403:     throw new Error("not implemented");
 404:   }
 405: 
 406:   public void handleJustify (float justificationWidth)
 407:   {
 408:     throw new Error("not implemented");
 409:   }
 410: 
 411:   public Object clone ()
 412:   {
 413:     throw new Error("not implemented");
 414:   }
 415: 
 416:   public int hashCode ()
 417:   {
 418:     throw new Error("not implemented");
 419:   }
 420: 
 421:   public boolean equals (ClasspathTextLayoutPeer tl)
 422:   {
 423:     throw new Error("not implemented");
 424:   }
 425: 
 426:   public String toString ()
 427:   {
 428:     throw new Error("not implemented");
 429:   }
 430: 
 431: }