Source for javax.swing.text.ParagraphView

   1: /* ParagraphView.java -- A composite View
   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 java.awt.Shape;
  42: 
  43: import javax.swing.event.DocumentEvent;
  44: 
  45: /**
  46:  * A {@link FlowView} that flows it's children horizontally and boxes the rows
  47:  * vertically.
  48:  *
  49:  * @author Roman Kennke (roman@kennke.org)
  50:  */
  51: public class ParagraphView extends FlowView implements TabExpander
  52: {
  53:   /**
  54:    * A specialized horizontal <code>BoxView</code> that represents exactly
  55:    * one row in a <code>ParagraphView</code>.
  56:    */
  57:   class Row extends BoxView
  58:   {
  59:     /**
  60:      * Creates a new instance of <code>Row</code>.
  61:      */
  62:     Row(Element el)
  63:     {
  64:       super(el, X_AXIS);
  65:     }
  66: 
  67:     public float getAlignment(int axis)
  68:     {
  69:       float align;
  70:       if (axis == X_AXIS)
  71:         align = 0.0F; // TODO: Implement according to justification
  72:       else
  73:         align = super.getAlignment(axis);
  74:       return align;
  75:     }
  76: 
  77:     /**
  78:      * Allows rows to span the whole parent view.
  79:      */
  80:     public float getMaximumSpan(int axis)
  81:     {
  82:       float max;
  83:       if (axis == X_AXIS)
  84:         max = Float.MAX_VALUE;
  85:       else
  86:         max = super.getMaximumSpan(axis);
  87:       return max;
  88:     }
  89: 
  90:     /**
  91:      * Overridden because child views are not necessarily laid out in model
  92:      * order.
  93:      */
  94:     protected int getViewIndexAtPosition(int pos)
  95:     {
  96:       int index = -1;
  97:       if (pos >= getStartOffset() && pos < getEndOffset())
  98:         {
  99:           int nviews = getViewCount();
 100:           for (int i = 0; i < nviews && index == -1; i++)
 101:             {
 102:               View child = getView(i);
 103:               if (pos >= child.getStartOffset() && pos < child.getEndOffset())
 104:                 index = i;
 105:             }
 106:         }
 107:       return index;
 108:     }
 109: 
 110:     protected void loadChildren(ViewFactory vf)
 111:     {
 112:       // Do nothing here. The children are added while layouting.
 113:     }
 114:   }
 115: 
 116:   /**
 117:    * The indentation of the first line of the paragraph.
 118:    */
 119:   protected int firstLineIndent;
 120: 
 121:   /**
 122:    * The justification of the paragraph.
 123:    */
 124:   private int justification;
 125: 
 126:   /**
 127:    * The line spacing of this paragraph.
 128:    */
 129:   private float lineSpacing;
 130: 
 131:   /**
 132:    * The TabSet of this paragraph.
 133:    */
 134:   private TabSet tabSet;
 135: 
 136:   /**
 137:    * Creates a new <code>ParagraphView</code> for the given
 138:    * <code>Element</code>.
 139:    *
 140:    * @param element the element that is rendered by this ParagraphView
 141:    */
 142:   public ParagraphView(Element element)
 143:   {
 144:     super(element, Y_AXIS);
 145:   }
 146: 
 147:   public float nextTabStop(float x, int tabOffset)
 148:   {
 149:     throw new InternalError("Not implemented yet");
 150:   }
 151: 
 152:   /**
 153:    * Creates a new view that represents a row within a flow.
 154:    *
 155:    * @return a view for a new row
 156:    */
 157:   protected View createRow()
 158:   {
 159:     return new Row(getElement());
 160:   }
 161: 
 162:   /**
 163:    * Returns the alignment for this paragraph view for the specified axis.
 164:    * For the X_AXIS the paragraph view will be aligned at it's left edge
 165:    * (0.0F). For the Y_AXIS the paragraph view will be aligned at the
 166:    * center of it's first row.
 167:    *
 168:    * @param axis the axis which is examined
 169:    *
 170:    * @return the alignment for this paragraph view for the specified axis
 171:    */
 172:   public float getAlignment(int axis)
 173:   {
 174:     float align;
 175:     if (axis == X_AXIS)
 176:       align = 0.5F;
 177:     else if (getViewCount() > 0)
 178:       {
 179:         float prefHeight = getPreferredSpan(Y_AXIS);
 180:         float firstRowHeight = getView(0).getPreferredSpan(Y_AXIS);
 181:         align = (firstRowHeight / 2.F) / prefHeight;
 182:       }
 183:     else
 184:       align = 0.5F;
 185:     return align;
 186:   }
 187: 
 188:   /**
 189:    * Receives notification when some attributes of the displayed element
 190:    * changes. This triggers a refresh of the cached attributes of this
 191:    * paragraph.
 192:    *
 193:    * @param ev the document event
 194:    * @param a the allocation of this view
 195:    * @param fv the view factory to use for creating new child views
 196:    */
 197:   public void changedUpdate(DocumentEvent ev, Shape a, ViewFactory fv)
 198:   {
 199:     setPropertiesFromAttributes();
 200:   }
 201: 
 202:   /**
 203:    * Fetches the cached properties from the element's attributes.
 204:    */
 205:   protected void setPropertiesFromAttributes()
 206:   {
 207:     Element el = getElement();
 208:     AttributeSet atts = el.getAttributes();
 209:     setFirstLineIndent(StyleConstants.getFirstLineIndent(atts));
 210:     setLineSpacing(StyleConstants.getLineSpacing(atts));
 211:     setJustification(StyleConstants.getAlignment(atts));
 212:     tabSet = StyleConstants.getTabSet(atts);
 213:   }
 214: 
 215:   /**
 216:    * Sets the indentation of the first line of the paragraph.
 217:    *
 218:    * @param i the indentation to set
 219:    */
 220:   protected void setFirstLineIndent(float i)
 221:   {
 222:     firstLineIndent = (int) i;
 223:   }
 224: 
 225:   /**
 226:    * Sets the justification of the paragraph.
 227:    *
 228:    * @param j the justification to set 
 229:    */
 230:   protected void setJustification(int j)
 231:   {
 232:     justification = j;
 233:   }
 234: 
 235:   /**
 236:    * Sets the line spacing for this paragraph.
 237:    *
 238:    * @param s the line spacing to set
 239:    */
 240:   protected void setLineSpacing(float s)
 241:   {
 242:     lineSpacing = s;
 243:   }
 244: 
 245:   /**
 246:    * Returns the i-th view from the logical views, before breaking into rows.
 247:    *
 248:    * @param i the index of the logical view to return
 249:    *
 250:    * @return the i-th view from the logical views, before breaking into rows
 251:    */
 252:   protected View getLayoutView(int i)
 253:   {
 254:     return layoutPool.getView(i);
 255:   }
 256: 
 257:   /**
 258:    * Returns the number of logical child views.
 259:    *
 260:    * @return the number of logical child views
 261:    */
 262:   protected int getLayoutViewCount()
 263:   {
 264:     return layoutPool.getViewCount();
 265:   }
 266: 
 267:   /**
 268:    * Returns the TabSet used by this ParagraphView.
 269:    *
 270:    * @return the TabSet used by this ParagraphView
 271:    */
 272:   protected TabSet getTabSet()
 273:   {
 274:     return tabSet;
 275:   }
 276: 
 277:   /**
 278:    * Finds the next offset in the document that has one of the characters
 279:    * specified in <code>string</code>. If there is no such character found,
 280:    * this returns -1.
 281:    *
 282:    * @param string the characters to search for
 283:    * @param start the start offset
 284:    *
 285:    * @return the next offset in the document that has one of the characters
 286:    *         specified in <code>string</code>
 287:    */
 288:   protected int findOffsetToCharactersInString(char[] string, int start)
 289:   {
 290:     int offset = -1;
 291:     Document doc = getDocument();
 292:     Segment text = new Segment();
 293:     try
 294:       {
 295:         doc.getText(start, doc.getLength() - start, text);
 296:         int index = start;
 297: 
 298:         searchLoop:
 299:         while (true)
 300:           {
 301:             char ch = text.next();
 302:             if (ch == Segment.DONE)
 303:               break;
 304: 
 305:             for (int j = 0; j < string.length; ++j)
 306:               {
 307:                 if (string[j] == ch)
 308:                   {
 309:                     offset = index;
 310:                     break searchLoop;
 311:                   }
 312:               }
 313:             index++;
 314:           }
 315:       }
 316:     catch (BadLocationException ex)
 317:       {
 318:         // Ignore this and return -1.
 319:       }
 320:     return offset;
 321:   }
 322: 
 323:   protected int getClosestPositionTo(int pos, Position.Bias bias, Shape a,
 324:                                      int direction, Position.Bias[] biasRet,
 325:                                      int rowIndex, int x)
 326:     throws BadLocationException
 327:   {
 328:     // FIXME: Implement this properly. However, this looks like it might
 329:     // have been replaced by viewToModel.
 330:     return pos;
 331:   }
 332: 
 333:   /**
 334:    * Returns the size that is used by this view (or it's child views) between
 335:    * <code>startOffset</code> and <code>endOffset</code>. If the child views
 336:    * implement the {@link TabableView} interface, then this is used to
 337:    * determine the span, otherwise we use the preferred span of the child
 338:    * views.
 339:    *
 340:    * @param startOffset the start offset
 341:    * @param endOffset the end offset
 342:    *
 343:    * @return the span used by the view between <code>startOffset</code> and
 344:    *         <code>endOffset</cod>
 345:    */
 346:   protected float getPartialSize(int startOffset, int endOffset)
 347:   {
 348:     int startIndex = getViewIndex(startOffset, Position.Bias.Backward);
 349:     int endIndex = getViewIndex(endOffset, Position.Bias.Forward);
 350:     float span;
 351:     if (startIndex == endIndex)
 352:       {
 353:         View child = getView(startIndex);
 354:         if (child instanceof TabableView)
 355:           {
 356:             TabableView tabable = (TabableView) child;
 357:             span = tabable.getPartialSpan(startOffset, endOffset);
 358:           }
 359:         else
 360:           span = child.getPreferredSpan(X_AXIS);
 361:       }
 362:     else if (endIndex - startIndex == 1)
 363:       {
 364:         View child1 = getView(startIndex);
 365:         if (child1 instanceof TabableView)
 366:           {
 367:             TabableView tabable = (TabableView) child1;
 368:             span = tabable.getPartialSpan(startOffset, child1.getEndOffset());
 369:           }
 370:         else
 371:           span = child1.getPreferredSpan(X_AXIS);
 372:         View child2 = getView(endIndex);
 373:         if (child2 instanceof TabableView)
 374:           {
 375:             TabableView tabable = (TabableView) child2;
 376:             span += tabable.getPartialSpan(child2.getStartOffset(), endOffset);
 377:           }
 378:         else
 379:           span += child2.getPreferredSpan(X_AXIS);
 380:       }
 381:     else
 382:       {
 383:         // Start with the first view.
 384:         View child1 = getView(startIndex);
 385:         if (child1 instanceof TabableView)
 386:           {
 387:             TabableView tabable = (TabableView) child1;
 388:             span = tabable.getPartialSpan(startOffset, child1.getEndOffset());
 389:           }
 390:         else
 391:           span = child1.getPreferredSpan(X_AXIS);
 392: 
 393:         // Add up the view spans between the start and the end view.
 394:         for (int i = startIndex + 1; i < endIndex; i++)
 395:           {
 396:             View child = getView(i);
 397:             span += child.getPreferredSpan(X_AXIS);
 398:           }
 399: 
 400:         // Add the span of the last view.
 401:         View child2 = getView(endIndex);
 402:         if (child2 instanceof TabableView)
 403:           {
 404:             TabableView tabable = (TabableView) child2;
 405:             span += tabable.getPartialSpan(child2.getStartOffset(), endOffset);
 406:           }
 407:         else
 408:           span += child2.getPreferredSpan(X_AXIS);
 409:       }
 410:     return span;
 411:   }
 412: 
 413:   /**
 414:    * Returns the location where the tabs are calculated from. This returns
 415:    * <code>0.0F</code> by default.
 416:    *
 417:    * @return the location where the tabs are calculated from
 418:    */
 419:   protected float getTabBase()
 420:   {
 421:     return 0.0F;
 422:   }
 423: 
 424:   /**
 425:    * @specnote This method is specified to take a Row parameter, which is a
 426:    *           private inner class of that class, which makes it unusable from
 427:    *           application code. Also, this method seems to be replaced by
 428:    *           {@link FlowStrategy#adjustRow(FlowView, int, int, int)}.
 429:    *
 430:    */
 431:   protected void adjustRow(Row r, int desiredSpan, int x)
 432:   {
 433:   }
 434: 
 435:   /**
 436:    * @specnote This method's signature differs from the one defined in
 437:    *           {@link View} and is therefore never called. It is probably there
 438:    *           for historical reasons.
 439:    */
 440:   public View breakView(int axis, float len, Shape a)
 441:   {
 442:     // This method is not used.
 443:     return null;
 444:   }
 445: 
 446:   /**
 447:    * @specnote This method's signature differs from the one defined in
 448:    *           {@link View} and is therefore never called. It is probably there
 449:    *           for historical reasons.
 450:    */
 451:   public int getBreakWeight(int axis, float len)
 452:   {
 453:     // This method is not used.
 454:     return 0;
 455:   }
 456: }