1 7 package javax.swing.text; 8 9 import java.util.Arrays ; 10 import java.awt.*; 11 import java.awt.font.TextAttribute ; 12 import javax.swing.event.*; 13 import javax.swing.SizeRequirements ; 14 15 33 public class ParagraphView extends FlowView implements TabExpander { 34 35 40 public ParagraphView(Element elem) { 41 super(elem, View.Y_AXIS); 42 setPropertiesFromAttributes(); 43 Document doc = elem.getDocument(); 44 Object i18nFlag = doc.getProperty(AbstractDocument.I18NProperty); 45 if ((i18nFlag != null) && i18nFlag.equals(Boolean.TRUE)) { 46 try { 47 if (i18nStrategy == null) { 48 String classname = "javax.swing.text.TextLayoutStrategy"; 50 ClassLoader loader = getClass().getClassLoader(); 51 if (loader != null) { 52 i18nStrategy = loader.loadClass(classname); 53 } else { 54 i18nStrategy = Class.forName(classname); 55 } 56 } 57 Object o = i18nStrategy.newInstance(); 58 if (o instanceof FlowStrategy) { 59 strategy = (FlowStrategy) o; 60 } 61 } catch (Throwable e) { 62 throw new StateInvariantError ("ParagraphView: Can't create i18n strategy: " 63 + e.getMessage()); 64 } 65 } 66 } 67 68 78 protected void setJustification(int j) { 79 justification = j; 80 } 81 82 87 protected void setLineSpacing(float ls) { 88 lineSpacing = ls; 89 } 90 91 96 protected void setFirstLineIndent(float fi) { 97 firstLineIndent = (int) fi; 98 } 99 100 103 protected void setPropertiesFromAttributes() { 104 AttributeSet attr = getAttributes(); 105 if (attr != null) { 106 setParagraphInsets(attr); 107 Integer a = (Integer )attr.getAttribute(StyleConstants.Alignment); 108 int alignment; 109 if (a == null) { 110 Document doc = getElement().getDocument(); 111 Object o = doc.getProperty(TextAttribute.RUN_DIRECTION); 112 if ((o != null) && o.equals(TextAttribute.RUN_DIRECTION_RTL)) { 113 alignment = StyleConstants.ALIGN_RIGHT; 114 } else { 115 alignment = StyleConstants.ALIGN_LEFT; 116 } 117 } else { 118 alignment = a.intValue(); 119 } 120 setJustification(alignment); 121 setLineSpacing(StyleConstants.getLineSpacing(attr)); 122 setFirstLineIndent(StyleConstants.getFirstLineIndent(attr)); 123 } 124 } 125 126 139 protected int getLayoutViewCount() { 140 return layoutPool.getViewCount(); 141 } 142 143 156 protected View getLayoutView(int index) { 157 return layoutPool.getView(index); 158 } 159 160 174 protected void adjustRow(Row r, int desiredSpan, int x) { 175 } 176 177 192 protected int getNextNorthSouthVisualPositionFrom(int pos, Position.Bias b, 193 Shape a, int direction, 194 Position.Bias [] biasRet) 195 throws BadLocationException { 196 int vIndex; 197 if(pos == -1) { 198 vIndex = (direction == NORTH) ? 199 getViewCount() - 1 : 0; 200 } 201 else { 202 if(b == Position.Bias.Backward && pos > 0) { 203 vIndex = getViewIndexAtPosition(pos - 1); 204 } 205 else { 206 vIndex = getViewIndexAtPosition(pos); 207 } 208 if(direction == NORTH) { 209 if(vIndex == 0) { 210 return -1; 211 } 212 vIndex--; 213 } 214 else if(++vIndex >= getViewCount()) { 215 return -1; 216 } 217 } 218 JTextComponent text = (JTextComponent )getContainer(); 220 Caret c = text.getCaret(); 221 Point magicPoint; 222 magicPoint = (c != null) ? c.getMagicCaretPosition() : null; 223 int x; 224 if(magicPoint == null) { 225 Shape posBounds; 226 try { 227 posBounds = text.getUI().modelToView(text, pos, b); 228 } catch (BadLocationException exc) { 229 posBounds = null; 230 } 231 if(posBounds == null) { 232 x = 0; 233 } 234 else { 235 x = posBounds.getBounds().x; 236 } 237 } 238 else { 239 x = magicPoint.x; 240 } 241 return getClosestPositionTo(pos, b, a, direction, biasRet, vIndex, x); 242 } 243 244 261 protected int getClosestPositionTo(int pos, Position.Bias b, Shape a, 265 int direction, Position.Bias [] biasRet, 266 int rowIndex, int x) 267 throws BadLocationException { 268 JTextComponent text = (JTextComponent )getContainer(); 269 Document doc = getDocument(); 270 AbstractDocument aDoc = (doc instanceof AbstractDocument ) ? 271 (AbstractDocument )doc : null; 272 View row = getView(rowIndex); 273 int lastPos = -1; 274 biasRet[0] = Position.Bias.Forward; 276 for(int vc = 0, numViews = row.getViewCount(); vc < numViews; vc++) { 277 View v = row.getView(vc); 278 int start = v.getStartOffset(); 279 boolean ltr = (aDoc != null) ? aDoc.isLeftToRight 280 (start, start + 1) : true; 281 if(ltr) { 282 lastPos = start; 283 for(int end = v.getEndOffset(); lastPos < end; lastPos++) { 284 float xx = text.modelToView(lastPos).getBounds().x; 285 if(xx >= x) { 286 while (++lastPos < end && 287 text.modelToView(lastPos).getBounds().x == xx) { 288 } 289 return --lastPos; 290 } 291 } 292 lastPos--; 293 } 294 else { 295 for(lastPos = v.getEndOffset() - 1; lastPos >= start; 296 lastPos--) { 297 float xx = text.modelToView(lastPos).getBounds().x; 298 if(xx >= x) { 299 while (--lastPos >= start && 300 text.modelToView(lastPos).getBounds().x == xx) { 301 } 302 return ++lastPos; 303 } 304 } 305 lastPos++; 306 } 307 } 308 if(lastPos == -1) { 309 return getStartOffset(); 310 } 311 return lastPos; 312 } 313 314 338 protected boolean flipEastAndWestAtEnds(int position, 339 Position.Bias bias) { 340 Document doc = getDocument(); 341 if(doc instanceof AbstractDocument && 342 !((AbstractDocument )doc).isLeftToRight(getStartOffset(), 343 getStartOffset() + 1)) { 344 return true; 345 } 346 return false; 347 } 348 349 351 358 public int getFlowSpan(int index) { 359 View child = getView(index); 360 int adjust = 0; 361 if (child instanceof Row) { 362 Row row = (Row) child; 363 adjust = row.getLeftInset() + row.getRightInset(); 364 } 365 int span = layoutSpan - adjust; 366 return span; 367 } 368 369 376 public int getFlowStart(int index) { 377 View child = getView(index); 378 int adjust = 0; 379 if (child instanceof Row) { 380 Row row = (Row) child; 381 adjust = row.getLeftInset(); 382 } 383 return tabBase + adjust; 384 } 385 386 391 protected View createRow() { 392 return new Row(getElement()); 393 } 394 395 397 422 public float nextTabStop(float x, int tabOffset) { 423 if(justification != StyleConstants.ALIGN_LEFT) 425 return x + 10.0f; 426 x -= tabBase; 427 TabSet tabs = getTabSet(); 428 if(tabs == null) { 429 return (float)(tabBase + (((int)x / 72 + 1) * 72)); 431 } 432 TabStop tab = tabs.getTabAfter(x + .01f); 433 if(tab == null) { 434 return tabBase + x + 5.0f; 437 } 438 int alignment = tab.getAlignment(); 439 int offset; 440 switch(alignment) { 441 default: 442 case TabStop.ALIGN_LEFT: 443 return tabBase + tab.getPosition(); 445 case TabStop.ALIGN_BAR: 446 return tabBase + tab.getPosition(); 448 case TabStop.ALIGN_RIGHT: 449 case TabStop.ALIGN_CENTER: 450 offset = findOffsetToCharactersInString(tabChars, 451 tabOffset + 1); 452 break; 453 case TabStop.ALIGN_DECIMAL: 454 offset = findOffsetToCharactersInString(tabDecimalChars, 455 tabOffset + 1); 456 break; 457 } 458 if (offset == -1) { 459 offset = getEndOffset(); 460 } 461 float charsSize = getPartialSize(tabOffset + 1, offset); 462 switch(alignment) { 463 case TabStop.ALIGN_RIGHT: 464 case TabStop.ALIGN_DECIMAL: 465 return tabBase + Math.max(x, tab.getPosition() - charsSize); 469 case TabStop.ALIGN_CENTER: 470 return tabBase + Math.max(x, tab.getPosition() - charsSize / 2.0f); 472 } 473 return x; 475 } 476 477 482 protected TabSet getTabSet() { 483 return StyleConstants.getTabSet(getElement().getAttributes()); 484 } 485 486 500 protected float getPartialSize(int startOffset, int endOffset) { 501 float size = 0.0f; 502 int viewIndex; 503 int numViews = getViewCount(); 504 View view; 505 int viewEnd; 506 int tempEnd; 507 508 viewIndex = getElement().getElementIndex(startOffset); 512 numViews = layoutPool.getViewCount(); 513 while(startOffset < endOffset && viewIndex < numViews) { 514 view = layoutPool.getView(viewIndex++); 515 viewEnd = view.getEndOffset(); 516 tempEnd = Math.min(endOffset, viewEnd); 517 if(view instanceof TabableView ) 518 size += ((TabableView )view).getPartialSpan(startOffset, tempEnd); 519 else if(startOffset == view.getStartOffset() && 520 tempEnd == view.getEndOffset()) 521 size += view.getPreferredSpan(View.X_AXIS); 522 else 523 return 0.0f; 525 startOffset = viewEnd; 526 } 527 return size; 528 } 529 530 539 protected int findOffsetToCharactersInString(char[] string, 540 int start) { 541 int stringLength = string.length; 542 int end = getEndOffset(); 543 Segment seg = new Segment (); 544 try { 545 getDocument().getText(start, end - start, seg); 546 } catch (BadLocationException ble) { 547 return -1; 548 } 549 for(int counter = seg.offset, maxCounter = seg.offset + seg.count; 550 counter < maxCounter; counter++) { 551 char currentChar = seg.array[counter]; 552 for(int subCounter = 0; subCounter < stringLength; 553 subCounter++) { 554 if(currentChar == string[subCounter]) 555 return counter - seg.offset + start; 556 } 557 } 558 return -1; 560 } 561 562 566 protected float getTabBase() { 567 return (float)tabBase; 568 } 569 570 572 581 public void paint(Graphics g, Shape a) { 582 Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a : a.getBounds(); 583 tabBase = alloc.x + getLeftInset(); 584 super.paint(g, a); 585 586 if (firstLineIndent < 0) { 589 Shape sh = getChildAllocation(0, a); 590 if ((sh != null) && sh.intersects(alloc)) { 591 int x = alloc.x + getLeftInset() + firstLineIndent; 592 int y = alloc.y + getTopInset(); 593 594 Rectangle clip = g.getClipBounds(); 595 tempRect.x = x + getOffset(X_AXIS, 0); 596 tempRect.y = y + getOffset(Y_AXIS, 0); 597 tempRect.width = getSpan(X_AXIS, 0) - firstLineIndent; 598 tempRect.height = getSpan(Y_AXIS, 0); 599 if (tempRect.intersects(clip)) { 600 tempRect.x = tempRect.x - firstLineIndent; 601 paintChild(g, tempRect, 0); 602 } 603 } 604 } 605 } 606 607 621 public float getAlignment(int axis) { 622 switch (axis) { 623 case Y_AXIS: 624 float a = 0.5f; 625 if (getViewCount() != 0) { 626 int paragraphSpan = (int) getPreferredSpan(View.Y_AXIS); 627 View v = getView(0); 628 int rowSpan = (int) v.getPreferredSpan(View.Y_AXIS); 629 a = (paragraphSpan != 0) ? ((float)(rowSpan / 2)) / paragraphSpan : 0; 630 } 631 return a; 632 case X_AXIS: 633 return 0.5f; 634 default: 635 throw new IllegalArgumentException ("Invalid axis: " + axis); 636 } 637 } 638 639 657 public View breakView(int axis, float len, Shape a) { 658 if(axis == View.Y_AXIS) { 659 if(a != null) { 660 Rectangle alloc = a.getBounds(); 661 setSize(alloc.width, alloc.height); 662 } 663 665 return this; 667 } 668 return this; 669 } 670 671 687 public int getBreakWeight(int axis, float len) { 688 if(axis == View.Y_AXIS) { 689 return BadBreakWeight; 695 } 696 return BadBreakWeight; 697 } 698 699 709 public void changedUpdate(DocumentEvent changes, Shape a, ViewFactory f) { 710 setPropertiesFromAttributes(); 713 layoutChanged(X_AXIS); 714 layoutChanged(Y_AXIS); 715 super.changedUpdate(changes, a, f); 716 } 717 718 719 721 private int justification; 722 private float lineSpacing; 723 724 protected int firstLineIndent = 0; 725 726 731 private int tabBase; 732 733 736 static Class i18nStrategy; 737 738 739 static char[] tabChars; 740 741 static char[] tabDecimalChars; 742 743 static { 744 tabChars = new char[1]; 745 tabChars[0] = '\t'; 746 tabDecimalChars = new char[2]; 747 tabDecimalChars[0] = '\t'; 748 tabDecimalChars[1] = '.'; 749 } 750 751 756 class Row extends BoxView { 757 758 Row(Element elem) { 759 super(elem, View.X_AXIS); 760 } 761 762 767 protected void loadChildren(ViewFactory f) { 768 } 769 770 775 public AttributeSet getAttributes() { 776 View p = getParent(); 777 return (p != null) ? p.getAttributes() : null; 778 } 779 780 public float getAlignment(int axis) { 781 if (axis == View.X_AXIS) { 782 switch (justification) { 783 case StyleConstants.ALIGN_LEFT: 784 return 0; 785 case StyleConstants.ALIGN_RIGHT: 786 return 1; 787 case StyleConstants.ALIGN_CENTER: 788 return 0.5f; 789 case StyleConstants.ALIGN_JUSTIFIED: 790 float rv = 0.5f; 791 if (isJustifiableDocument()) { 794 rv = 0f; 795 } 796 return rv; 797 } 798 } 799 return super.getAlignment(axis); 800 } 801 802 817 public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException { 818 Rectangle r = a.getBounds(); 819 View v = getViewAtPosition(pos, r); 820 if ((v != null) && (!v.getElement().isLeaf())) { 821 return super.modelToView(pos, a, b); 823 } 824 r = a.getBounds(); 825 int height = r.height; 826 int y = r.y; 827 Shape loc = super.modelToView(pos, a, b); 828 r = loc.getBounds(); 829 r.height = height; 830 r.y = y; 831 return r; 832 } 833 834 839 public int getStartOffset() { 840 int offs = Integer.MAX_VALUE; 841 int n = getViewCount(); 842 for (int i = 0; i < n; i++) { 843 View v = getView(i); 844 offs = Math.min(offs, v.getStartOffset()); 845 } 846 return offs; 847 } 848 849 public int getEndOffset() { 850 int offs = 0; 851 int n = getViewCount(); 852 for (int i = 0; i < n; i++) { 853 View v = getView(i); 854 offs = Math.max(offs, v.getEndOffset()); 855 } 856 return offs; 857 } 858 859 879 protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) { 880 baselineLayout(targetSpan, axis, offsets, spans); 881 } 882 883 protected SizeRequirements calculateMinorAxisRequirements(int axis, 884 SizeRequirements r) { 885 return baselineRequirements(axis, r); 886 } 887 888 889 private boolean isLastRow() { 890 View parent; 891 return ((parent = getParent()) == null 892 || this == parent.getView(parent.getViewCount() - 1)); 893 } 894 895 private boolean isBrokenRow() { 896 boolean rv = false; 897 int viewsCount = getViewCount(); 898 if (viewsCount > 0) { 899 View lastView = getView(viewsCount - 1); 900 if (lastView.getBreakWeight(X_AXIS, 0, 0) >= 901 ForcedBreakWeight) { 902 rv = true; 903 } 904 } 905 return rv; 906 } 907 908 private boolean isJustifiableDocument() { 909 return (! Boolean.TRUE.equals(getDocument().getProperty( 910 AbstractDocument.I18NProperty))); 911 } 912 913 920 private boolean isJustifyEnabled() { 921 boolean ret = (justification == StyleConstants.ALIGN_JUSTIFIED); 922 923 ret = ret && isJustifiableDocument(); 925 926 ret = ret && ! isLastRow(); 928 929 ret = ret && ! isBrokenRow(); 931 932 return ret; 933 } 934 935 936 @Override 939 protected SizeRequirements calculateMajorAxisRequirements(int axis, 940 SizeRequirements r) { 941 int oldJustficationData[] = justificationData; 942 justificationData = null; 943 SizeRequirements ret = super.calculateMajorAxisRequirements(axis, r); 944 if (isJustifyEnabled()) { 945 justificationData = oldJustficationData; 946 } 947 return ret; 948 } 949 950 @Override 951 protected void layoutMajorAxis(int targetSpan, int axis, 952 int[] offsets, int[] spans) { 953 int oldJustficationData[] = justificationData; 954 justificationData = null; 955 super.layoutMajorAxis(targetSpan, axis, offsets, spans); 956 if (! isJustifyEnabled()) { 957 return; 958 } 959 960 int currentSpan = 0; 961 for (int span : spans) { 962 currentSpan += span; 963 } 964 if (currentSpan == targetSpan) { 965 return; 967 } 968 969 977 int extendableSpaces = 0; 978 int startJustifiableContent = -1; 979 int endJustifiableContent = -1; 980 int lastLeadingSpaces = 0; 981 982 int rowStartOffset = getStartOffset(); 983 int rowEndOffset = getEndOffset(); 984 int spaceMap[] = new int[rowEndOffset - rowStartOffset]; 985 Arrays.fill(spaceMap, 0); 986 for (int i = getViewCount() - 1; i >= 0 ; i--) { 987 View view = getView(i); 988 if (view instanceof GlyphView ) { 989 GlyphView.JustificationInfo justificationInfo = 990 ((GlyphView ) view).getJustificationInfo(rowStartOffset); 991 final int viewStartOffset = view.getStartOffset(); 992 final int offset = viewStartOffset - rowStartOffset; 993 for (int j = 0; j < justificationInfo.spaceMap.length(); j++) { 994 if (justificationInfo.spaceMap.get(j)) { 995 spaceMap[j + offset] = 1; 996 } 997 } 998 if (startJustifiableContent > 0) { 999 if (justificationInfo.end >= 0) { 1000 extendableSpaces += justificationInfo.trailingSpaces; 1001 } else { 1002 lastLeadingSpaces += justificationInfo.trailingSpaces; 1003 } 1004 } 1005 if (justificationInfo.start >= 0) { 1006 startJustifiableContent = 1007 justificationInfo.start + viewStartOffset; 1008 extendableSpaces += lastLeadingSpaces; 1009 } 1010 if (justificationInfo.end >= 0 1011 && endJustifiableContent < 0) { 1012 endJustifiableContent = 1013 justificationInfo.end + viewStartOffset; 1014 } 1015 extendableSpaces += justificationInfo.contentSpaces; 1016 lastLeadingSpaces = justificationInfo.leadingSpaces; 1017 if (justificationInfo.hasTab) { 1018 break; 1019 } 1020 } 1021 } 1022 if (extendableSpaces <= 0) { 1023 return; 1025 } 1026 int adjustment = (targetSpan - currentSpan); 1027 int spaceAddon = (extendableSpaces > 0) 1028 ? adjustment / extendableSpaces 1029 : 0; 1030 int spaceAddonLeftoverEnd = -1; 1031 for (int i = startJustifiableContent - rowStartOffset, 1032 leftover = adjustment - spaceAddon * extendableSpaces; 1033 leftover > 0; 1034 leftover -= spaceMap[i], 1035 i++) { 1036 spaceAddonLeftoverEnd = i; 1037 } 1038 if (spaceAddon > 0 || spaceAddonLeftoverEnd >= 0) { 1039 justificationData = (oldJustficationData != null) 1040 ? oldJustficationData 1041 : new int[END_JUSTIFIABLE + 1]; 1042 justificationData[SPACE_ADDON] = spaceAddon; 1043 justificationData[SPACE_ADDON_LEFTOVER_END] = 1044 spaceAddonLeftoverEnd; 1045 justificationData[START_JUSTIFIABLE] = 1046 startJustifiableContent - rowStartOffset; 1047 justificationData[END_JUSTIFIABLE] = 1048 endJustifiableContent - rowStartOffset; 1049 super.layoutMajorAxis(targetSpan, axis, offsets, spans); 1050 } 1051 } 1052 1053 @Override 1056 public float getMaximumSpan(int axis) { 1057 float ret; 1058 if (View.X_AXIS == axis 1059 && isJustifyEnabled()) { 1060 ret = Float.MAX_VALUE; 1061 } else { 1062 ret = super.getMaximumSpan(axis); 1063 } 1064 return ret; 1065 } 1066 1067 1075 protected int getViewIndexAtPosition(int pos) { 1076 if(pos < getStartOffset() || pos >= getEndOffset()) 1079 return -1; 1080 for(int counter = getViewCount() - 1; counter >= 0; counter--) { 1081 View v = getView(counter); 1082 if(pos >= v.getStartOffset() && 1083 pos < v.getEndOffset()) { 1084 return counter; 1085 } 1086 } 1087 return -1; 1088 } 1089 1090 1095 protected short getLeftInset() { 1096 View parentView; 1097 int adjustment = 0; 1098 if ((parentView = getParent()) != null) { if (this == parentView.getView(0)) { 1100 adjustment = firstLineIndent; 1101 } 1102 } 1103 return (short)(super.getLeftInset() + adjustment); 1104 } 1105 1106 protected short getBottomInset() { 1107 return (short)(super.getBottomInset() + 1108 ((minorRequest != null) ? minorRequest.preferred : 0) * 1109 lineSpacing); 1110 } 1111 1112 final static int SPACE_ADDON = 0; 1113 final static int SPACE_ADDON_LEFTOVER_END = 1; 1114 final static int START_JUSTIFIABLE = 2; 1115 final static int END_JUSTIFIABLE = 3; 1117 1118 int justificationData[] = null; 1119 } 1120 1121} 1122 | Popular Tags |