1 7 package javax.swing.text; 8 9 import java.awt.*; 10 import java.text.BreakIterator ; 11 import javax.swing.event.*; 12 import java.util.BitSet ; 13 14 import com.sun.java.swing.SwingUtilities2; 15 16 45 public class GlyphView extends View implements TabableView , Cloneable { 46 47 52 public GlyphView(Element elem) { 53 super(elem); 54 offset = 0; 55 length = 0; 56 Element parent = elem.getParentElement(); 57 AttributeSet attr = elem.getAttributes(); 58 59 impliedCR = (attr != null && attr.getAttribute(IMPLIED_CR) != null && 61 parent != null && parent.getElementCount() > 1); 63 skipWidth = elem.getName().equals("br"); 64 } 65 66 72 protected final Object clone() { 73 Object o; 74 try { 75 o = super.clone(); 76 } catch (CloneNotSupportedException cnse) { 77 o = null; 78 } 79 return o; 80 } 81 82 87 public GlyphPainter getGlyphPainter() { 88 return painter; 89 } 90 91 94 public void setGlyphPainter(GlyphPainter p) { 95 painter = p; 96 } 97 98 108 public Segment getText(int p0, int p1) { 109 Segment text = SegmentCache.getSharedSegment(); 113 try { 114 Document doc = getDocument(); 115 doc.getText(p0, p1 - p0, text); 116 } catch (BadLocationException bl) { 117 throw new StateInvariantError ("GlyphView: Stale view: " + bl); 118 } 119 return text; 120 } 121 122 129 public Color getBackground() { 130 Document doc = getDocument(); 131 if (doc instanceof StyledDocument ) { 132 AttributeSet attr = getAttributes(); 133 if (attr.isDefined(StyleConstants.Background)) { 134 return ((StyledDocument )doc).getBackground(attr); 135 } 136 } 137 return null; 138 } 139 140 150 public Color getForeground() { 151 Document doc = getDocument(); 152 if (doc instanceof StyledDocument ) { 153 AttributeSet attr = getAttributes(); 154 return ((StyledDocument )doc).getForeground(attr); 155 } 156 Component c = getContainer(); 157 if (c != null) { 158 return c.getForeground(); 159 } 160 return null; 161 } 162 163 172 public Font getFont() { 173 Document doc = getDocument(); 174 if (doc instanceof StyledDocument ) { 175 AttributeSet attr = getAttributes(); 176 return ((StyledDocument )doc).getFont(attr); 177 } 178 Component c = getContainer(); 179 if (c != null) { 180 return c.getFont(); 181 } 182 return null; 183 } 184 185 189 public boolean isUnderline() { 190 AttributeSet attr = getAttributes(); 191 return StyleConstants.isUnderline(attr); 192 } 193 194 199 public boolean isStrikeThrough() { 200 AttributeSet attr = getAttributes(); 201 return StyleConstants.isStrikeThrough(attr); 202 } 203 204 207 public boolean isSubscript() { 208 AttributeSet attr = getAttributes(); 209 return StyleConstants.isSubscript(attr); 210 } 211 212 215 public boolean isSuperscript() { 216 AttributeSet attr = getAttributes(); 217 return StyleConstants.isSuperscript(attr); 218 } 219 220 223 public TabExpander getTabExpander() { 224 return expander; 225 } 226 227 231 protected void checkPainter() { 232 if (painter == null) { 233 if (defaultPainter == null) { 234 String classname = "javax.swing.text.GlyphPainter1"; 236 try { 237 Class c; 238 ClassLoader loader = getClass().getClassLoader(); 239 if (loader != null) { 240 c = loader.loadClass(classname); 241 } else { 242 c = Class.forName(classname); 243 } 244 Object o = c.newInstance(); 245 if (o instanceof GlyphPainter) { 246 defaultPainter = (GlyphPainter) o; 247 } 248 } catch (Throwable e) { 249 throw new StateInvariantError ("GlyphView: Can't load glyph painter: " 250 + classname); 251 } 252 } 253 setGlyphPainter(defaultPainter.getPainter(this, getStartOffset(), 254 getEndOffset())); 255 } 256 } 257 258 260 270 public float getTabbedSpan(float x, TabExpander e) { 271 checkPainter(); 272 273 TabExpander old = expander; 274 expander = e; 275 276 if (expander != old) { 277 preferenceChanged(null, true, false); 280 } 281 282 this.x = (int) x; 283 int p0 = getStartOffset(); 284 int p1 = getEndOffset(); 285 float width = painter.getSpan(this, p0, p1, expander, x); 286 return width; 287 } 288 289 308 public float getPartialSpan(int p0, int p1) { 309 checkPainter(); 310 float width = painter.getSpan(this, p0, p1, expander, x); 311 return width; 312 } 313 314 316 322 public int getStartOffset() { 323 Element e = getElement(); 324 return (length > 0) ? e.getStartOffset() + offset : e.getStartOffset(); 325 } 326 327 333 public int getEndOffset() { 334 Element e = getElement(); 335 return (length > 0) ? e.getStartOffset() + offset + length : e.getEndOffset(); 336 } 337 338 341 private void initSelections(int p0, int p1) { 342 int viewPosCount = p1 - p0 + 1; 343 if (selections == null || viewPosCount > selections.length) { 344 selections = new byte[viewPosCount]; 345 return; 346 } 347 for (int i = 0; i < viewPosCount; selections[i++] = 0); 348 } 349 350 356 public void paint(Graphics g, Shape a) { 357 checkPainter(); 358 359 boolean paintedText = false; 360 Component c = getContainer(); 361 int p0 = getStartOffset(); 362 int p1 = getEndOffset(); 363 Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a : a.getBounds(); 364 Color bg = getBackground(); 365 Color fg = getForeground(); 366 367 if (c instanceof JTextComponent ) { 368 JTextComponent tc = (JTextComponent ) c; 369 if (!tc.isEnabled()) { 370 fg = tc.getDisabledTextColor(); 371 } 372 } 373 if (bg != null) { 374 g.setColor(bg); 375 g.fillRect(alloc.x, alloc.y, alloc.width, alloc.height); 376 } 377 if (c instanceof JTextComponent ) { 378 JTextComponent tc = (JTextComponent ) c; 379 Highlighter h = tc.getHighlighter(); 380 if (h instanceof LayeredHighlighter ) { 381 ((LayeredHighlighter )h).paintLayeredHighlights 382 (g, p0, p1, a, tc, this); 383 } 384 } 385 386 if (Utilities.isComposedTextElement(getElement())) { 387 Utilities.paintComposedText(g, a.getBounds(), this); 388 paintedText = true; 389 } else if(c instanceof JTextComponent ) { 390 JTextComponent tc = (JTextComponent ) c; 391 Color selFG = tc.getSelectedTextColor(); 392 393 if ( (tc.getHighlighter() != null) && 395 (selFG != null) && !selFG.equals(fg)) { 397 398 Highlighter.Highlight [] h = tc.getHighlighter().getHighlights(); 399 if(h.length != 0) { 400 boolean initialized = false; 401 int viewSelectionCount = 0; 402 for (int i = 0; i < h.length; i++) { 403 Highlighter.Highlight highlight = h[i]; 404 int hStart = highlight.getStartOffset(); 405 int hEnd = highlight.getEndOffset(); 406 if (hStart > p1 || hEnd < p0) { 407 continue; 409 } 410 if (!SwingUtilities2.useSelectedTextColor(highlight, tc)) { 411 continue; 412 } 413 if (hStart <= p0 && hEnd >= p1){ 414 paintTextUsingColor(g, a, selFG, p0, p1); 416 paintedText = true; 417 break; 418 } 419 if (!initialized) { 422 initSelections(p0, p1); 423 initialized = true; 424 } 425 hStart = Math.max(p0, hStart); 426 hEnd = Math.min(p1, hEnd); 427 paintTextUsingColor(g, a, selFG, hStart, hEnd); 428 selections[hStart-p0]++; 432 selections[hEnd-p0]--; 433 434 viewSelectionCount++; 435 } 436 437 if (!paintedText && viewSelectionCount > 0) { 438 int curPos = -1; 440 int startPos = 0; 441 int viewLen = p1 - p0; 442 while (curPos++ < viewLen) { 443 while(curPos < viewLen && 445 selections[curPos] == 0) curPos++; 446 if (startPos != curPos) { 447 paintTextUsingColor(g, a, fg, 449 p0 + startPos, p0 + curPos); 450 } 451 int checkSum = 0; 452 while (curPos < viewLen && 454 (checkSum += selections[curPos]) != 0) curPos++; 455 startPos = curPos; 456 } 457 paintedText = true; 458 } 459 } 460 } 461 } 462 if(!paintedText) 463 paintTextUsingColor(g, a, fg, p0, p1); 464 } 465 466 469 final void paintTextUsingColor(Graphics g, Shape a, Color c, int p0, int p1) { 470 g.setColor(c); 472 painter.paint(this, g, a, p0, p1); 473 474 boolean underline = isUnderline(); 476 boolean strike = isStrikeThrough(); 477 if (underline || strike) { 478 Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a : a.getBounds(); 480 View parent = getParent(); 481 if ((parent != null) && (parent.getEndOffset() == p1)) { 482 Segment s = getText(p0, p1); 484 while ((s.count > 0) && (Character.isWhitespace(s.array[s.count-1]))) { 485 p1 -= 1; 486 s.count -= 1; 487 } 488 SegmentCache.releaseSharedSegment(s); 489 } 490 int x0 = alloc.x; 491 int p = getStartOffset(); 492 if (p != p0) { 493 x0 += (int) painter.getSpan(this, p, p0, getTabExpander(), x0); 494 } 495 int x1 = x0 + (int) painter.getSpan(this, p0, p1, getTabExpander(), x0); 496 497 int d = (int) painter.getDescent(this); 499 int y = alloc.y + alloc.height - (int) painter.getDescent(this); 500 if (underline) { 501 int yTmp = y; 502 yTmp += 1; 503 g.drawLine(x0, yTmp, x1, yTmp); 504 } 505 if (strike) { 506 int yTmp = y; 507 yTmp -= (int) (painter.getAscent(this) * 0.3f); 509 g.drawLine(x0, yTmp, x1, yTmp); 510 } 511 512 } 513 } 514 515 525 public float getPreferredSpan(int axis) { 526 if (impliedCR) { 527 return 0; 528 } 529 checkPainter(); 530 int p0 = getStartOffset(); 531 int p1 = getEndOffset(); 532 switch (axis) { 533 case View.X_AXIS: 534 if (skipWidth) { 535 return 0; 536 } 537 return painter.getSpan(this, p0, p1, expander, this.x); 538 case View.Y_AXIS: 539 float h = painter.getHeight(this); 540 if (isSuperscript()) { 541 h += h/3; 542 } 543 return h; 544 default: 545 throw new IllegalArgumentException ("Invalid axis: " + axis); 546 } 547 } 548 549 562 public float getAlignment(int axis) { 563 checkPainter(); 564 if (axis == View.Y_AXIS) { 565 boolean sup = isSuperscript(); 566 boolean sub = isSubscript(); 567 float h = painter.getHeight(this); 568 float d = painter.getDescent(this); 569 float a = painter.getAscent(this); 570 float align; 571 if (sup) { 572 align = 1.0f; 573 } else if (sub) { 574 align = (h > 0) ? (h - (d + (a / 2))) / h : 0; 575 } else { 576 align = (h > 0) ? (h - d) / h : 0; 577 } 578 return align; 579 } 580 return super.getAlignment(axis); 581 } 582 583 596 public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException { 597 checkPainter(); 598 return painter.modelToView(this, pos, b, a); 599 } 600 601 615 public int viewToModel(float x, float y, Shape a, Position.Bias [] biasReturn) { 616 checkPainter(); 617 return painter.viewToModel(this, x, y, a, biasReturn); 618 } 619 620 663 public int getBreakWeight(int axis, float pos, float len) { 664 if (axis == View.X_AXIS) { 665 checkPainter(); 666 int p0 = getStartOffset(); 667 int p1 = painter.getBoundedPosition(this, p0, pos, len); 668 if (p1 == p0) { 669 return View.BadBreakWeight; 671 } 672 if (getBreakSpot(p0, p1) != -1) { 673 return View.ExcellentBreakWeight; 674 } 675 if (p1 == getEndOffset()) { 678 return View.GoodBreakWeight; 679 } else { 680 return View.GoodBreakWeight - 1; 681 } 682 } 683 return super.getBreakWeight(axis, pos, len); 684 } 685 686 707 public View breakView(int axis, int p0, float pos, float len) { 708 if (axis == View.X_AXIS) { 709 checkPainter(); 710 int p1 = painter.getBoundedPosition(this, p0, pos, len); 711 int breakSpot = getBreakSpot(p0, p1); 712 713 if (breakSpot != -1) { 714 p1 = breakSpot; 715 } 716 if (p0 == getStartOffset() && p1 == getEndOffset()) { 719 return this; 720 } 721 GlyphView v = (GlyphView ) createFragment(p0, p1); 722 v.x = (int) pos; 723 return v; 724 } 725 return this; 726 } 727 728 732 private int getBreakSpot(int p0, int p1) { 733 Document doc = getDocument(); 734 735 if (doc != null && Boolean.TRUE.equals(doc.getProperty( 736 AbstractDocument.MultiByteProperty))) { 737 return getBreakSpotUseBreakIterator(p0, p1); 738 } 739 return getBreakSpotUseWhitespace(p0, p1); 740 } 741 742 746 private int getBreakSpotUseWhitespace(int p0, int p1) { 747 Segment s = getText(p0, p1); 748 749 for (char ch = s.last(); ch != Segment.DONE; ch = s.previous()) { 750 if (Character.isWhitespace(ch)) { 751 SegmentCache.releaseSharedSegment(s); 753 return s.getIndex() - s.getBeginIndex() + 1 + p0; 754 } 755 } 756 SegmentCache.releaseSharedSegment(s); 757 return -1; 758 } 759 760 763 private int getBreakSpotUseBreakIterator(int p0, int p1) { 764 Element parent = getElement().getParentElement(); 767 int parent0; 768 int parent1; 769 Container c = getContainer(); 770 BreakIterator breaker; 771 772 if (parent == null) { 773 parent0 = p0; 774 parent1 = p1; 775 } 776 else { 777 parent0 = parent.getStartOffset(); 778 parent1 = parent.getEndOffset(); 779 } 780 if (c != null) { 781 breaker = BreakIterator.getLineInstance(c.getLocale()); 782 } 783 else { 784 breaker = BreakIterator.getLineInstance(); 785 } 786 787 Segment s = getText(parent0, parent1); 788 int breakPoint; 789 790 s.first(); 792 breaker.setText(s); 793 794 if (p1 == parent1) { 795 breakPoint = breaker.last(); 799 } 800 else if (p1 + 1 == parent1) { 801 breakPoint = breaker.next(s.offset + s.count - 2); 803 if (breakPoint >= s.count + s.offset) { 804 breakPoint = breaker.preceding(s.offset + s.count - 1); 805 } 806 } 807 else { 808 breakPoint = breaker.preceding(p1 - parent0 + s.offset + 1); 809 } 810 811 int retValue = -1; 812 813 if (breakPoint != BreakIterator.DONE) { 814 breakPoint = breakPoint - s.offset + parent0; 815 if (breakPoint > p0) { 816 if (p0 == parent0 && breakPoint == p0) { 817 retValue = -1; 818 } 819 else if (breakPoint <= p1) { 820 retValue = breakPoint; 821 } 822 } 823 } 824 SegmentCache.releaseSharedSegment(s); 825 return retValue; 826 } 827 828 849 public View createFragment(int p0, int p1) { 850 checkPainter(); 851 Element elem = getElement(); 852 GlyphView v = (GlyphView ) clone(); 853 v.offset = p0 - elem.getStartOffset(); 854 v.length = p1 - p0; 855 v.painter = painter.getPainter(v, p0, p1); 856 v.justificationInfo = null; 857 return v; 858 } 859 860 878 public int getNextVisualPositionFrom(int pos, Position.Bias b, Shape a, 879 int direction, 880 Position.Bias [] biasRet) 881 throws BadLocationException { 882 883 return painter.getNextVisualPositionFrom(this, pos, b, a, direction, biasRet); 884 } 885 886 897 public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f) { 898 justificationInfo = null; 899 syncCR(); 900 preferenceChanged(null, true, false); 901 } 902 903 914 public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) { 915 justificationInfo = null; 916 syncCR(); 917 preferenceChanged(null, true, false); 918 } 919 920 931 public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) { 932 syncCR(); 933 preferenceChanged(null, true, true); 934 } 935 936 private void syncCR() { 939 if (impliedCR) { 940 Element parent = getElement().getParentElement(); 941 impliedCR = (parent != null && parent.getElementCount() > 1); 942 } 943 } 944 945 948 static class JustificationInfo { 949 final int start; 951 final int end; 953 final int leadingSpaces; 954 final int contentSpaces; 955 final int trailingSpaces; 956 final boolean hasTab; 957 final BitSet spaceMap; 958 JustificationInfo(int start, int end, 959 int leadingSpaces, 960 int contentSpaces, 961 int trailingSpaces, 962 boolean hasTab, 963 BitSet spaceMap) { 964 this.start = start; 965 this.end = end; 966 this.leadingSpaces = leadingSpaces; 967 this.contentSpaces = contentSpaces; 968 this.trailingSpaces = trailingSpaces; 969 this.hasTab = hasTab; 970 this.spaceMap = spaceMap; 971 } 972 } 973 974 975 976 JustificationInfo getJustificationInfo(int rowStartOffset) { 977 if (justificationInfo != null) { 978 return justificationInfo; 979 } 980 final int TRAILING = 0; 982 final int CONTENT = 1; 983 final int SPACES = 2; 984 int startOffset = getStartOffset(); 985 int endOffset = getEndOffset(); 986 Segment segment = getText(startOffset, endOffset); 987 int txtOffset = segment.offset; 988 int txtEnd = segment.offset + segment.count - 1; 989 int startContentPosition = txtEnd + 1; 990 int endContentPosition = txtOffset - 1; 991 int lastTabPosition = txtOffset - 1; 992 int trailingSpaces = 0; 993 int contentSpaces = 0; 994 int leadingSpaces = 0; 995 boolean hasTab = false; 996 BitSet spaceMap = new BitSet (endOffset - startOffset + 1); 997 998 for (int i = txtEnd, state = TRAILING; i >= txtOffset; i--) { 1003 if (' ' == segment.array[i]) { 1004 spaceMap.set(i - txtOffset); 1005 if (state == TRAILING) { 1006 trailingSpaces++; 1007 } else if (state == CONTENT) { 1008 state = SPACES; 1009 leadingSpaces = 1; 1010 } else if (state == SPACES) { 1011 leadingSpaces++; 1012 } 1013 } else if ('\t' == segment.array[i]) { 1014 hasTab = true; 1015 break; 1016 } else { 1017 if (state == TRAILING) { 1018 if ('\n' != segment.array[i] 1019 && '\r' != segment.array[i]) { 1020 state = CONTENT; 1021 endContentPosition = i; 1022 } 1023 } else if (state == CONTENT) { 1024 } else if (state == SPACES) { 1026 contentSpaces += leadingSpaces; 1027 leadingSpaces = 0; 1028 } 1029 startContentPosition = i; 1030 } 1031 } 1032 1033 SegmentCache.releaseSharedSegment(segment); 1034 1035 int startJustifiableContent = -1; 1036 if (startContentPosition < txtEnd) { 1037 startJustifiableContent = 1038 startContentPosition - txtOffset; 1039 } 1040 int endJustifiableContent = -1; 1041 if (endContentPosition > txtOffset) { 1042 endJustifiableContent = 1043 endContentPosition - txtOffset; 1044 } 1045 justificationInfo = 1046 new JustificationInfo(startJustifiableContent, 1047 endJustifiableContent, 1048 leadingSpaces, 1049 contentSpaces, 1050 trailingSpaces, 1051 hasTab, 1052 spaceMap); 1053 return justificationInfo; 1054 } 1055 1056 1058 1061 private byte[] selections = null; 1062 1063 int offset; 1064 int length; 1065 boolean impliedCR; 1067 private static final String IMPLIED_CR = "CR"; 1068 boolean skipWidth; 1069 1070 1073 TabExpander expander; 1074 1075 1078 int x; 1079 1080 1083 GlyphPainter painter; 1084 1085 1088 static GlyphPainter defaultPainter; 1089 1090 private JustificationInfo justificationInfo = null; 1091 1092 1105 public static abstract class GlyphPainter { 1106 1107 1111 public abstract float getSpan(GlyphView v, int p0, int p1, TabExpander e, float x); 1112 1113 public abstract float getHeight(GlyphView v); 1114 1115 public abstract float getAscent(GlyphView v); 1116 1117 public abstract float getDescent(GlyphView v); 1118 1119 1122 public abstract void paint(GlyphView v, Graphics g, Shape a, int p0, int p1); 1123 1124 1140 public abstract Shape modelToView(GlyphView v, 1141 int pos, Position.Bias bias, 1142 Shape a) throws BadLocationException ; 1143 1144 1159 public abstract int viewToModel(GlyphView v, 1160 float x, float y, Shape a, 1161 Position.Bias [] biasReturn); 1162 1163 1182 public abstract int getBoundedPosition(GlyphView v, int p0, float x, float len); 1183 1184 1194 public GlyphPainter getPainter(GlyphView v, int p0, int p1) { 1195 return this; 1196 } 1197 1198 1222 public int getNextVisualPositionFrom(GlyphView v, int pos, Position.Bias b, Shape a, 1223 int direction, 1224 Position.Bias [] biasRet) 1225 throws BadLocationException { 1226 1227 int startOffset = v.getStartOffset(); 1228 int endOffset = v.getEndOffset(); 1229 Segment text; 1230 1231 switch (direction) { 1232 case View.NORTH: 1233 case View.SOUTH: 1234 if (pos != -1) { 1235 return -1; 1239 } 1240 Container container = v.getContainer(); 1241 1242 if (container instanceof JTextComponent ) { 1243 Caret c = ((JTextComponent )container).getCaret(); 1244 Point magicPoint; 1245 magicPoint = (c != null) ? c.getMagicCaretPosition() :null; 1246 1247 if (magicPoint == null) { 1248 biasRet[0] = Position.Bias.Forward; 1249 return startOffset; 1250 } 1251 int value = v.viewToModel(magicPoint.x, 0f, a, biasRet); 1252 return value; 1253 } 1254 break; 1255 case View.EAST: 1256 if(startOffset == v.getDocument().getLength()) { 1257 if(pos == -1) { 1258 biasRet[0] = Position.Bias.Forward; 1259 return startOffset; 1260 } 1261 return -1; 1264 } 1265 if(pos == -1) { 1266 biasRet[0] = Position.Bias.Forward; 1267 return startOffset; 1268 } 1269 if(pos == endOffset) { 1270 return -1; 1271 } 1272 if(++pos == endOffset) { 1273 return -1; 1276 } 1277 else { 1278 biasRet[0] = Position.Bias.Forward; 1279 } 1280 return pos; 1281 case View.WEST: 1282 if(startOffset == v.getDocument().getLength()) { 1283 if(pos == -1) { 1284 biasRet[0] = Position.Bias.Forward; 1285 return startOffset; 1286 } 1287 return -1; 1290 } 1291 if(pos == -1) { 1292 biasRet[0] = Position.Bias.Forward; 1295 return endOffset - 1; 1296 } 1297 if(pos == startOffset) { 1298 return -1; 1299 } 1300 biasRet[0] = Position.Bias.Forward; 1301 return (pos - 1); 1302 default: 1303 throw new IllegalArgumentException ("Bad direction: " + direction); 1304 } 1305 return pos; 1306 1307 } 1308 } 1309} 1310 1311 | Popular Tags |