Frames | No Frames |
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: }