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