1 7 8 package javax.swing.text.html; 9 10 import java.awt.*; 11 import java.awt.event.*; 12 import java.beans.*; 13 import java.util.*; 14 import javax.swing.*; 15 import javax.swing.event.*; 16 import javax.swing.text.*; 17 import javax.accessibility.*; 18 import java.text.BreakIterator ; 19 20 27 class AccessibleHTML implements Accessible { 28 29 32 private JEditorPane editor; 33 36 private Document model; 37 40 private DocumentListener docListener; 41 44 private PropertyChangeListener propChangeListener; 45 48 private ElementInfo rootElementInfo; 49 52 private RootHTMLAccessibleContext rootHTMLAccessibleContext; 53 54 public AccessibleHTML(JEditorPane pane) { 55 editor = pane; 56 propChangeListener = new PropertyChangeHandler(); 57 setDocument(editor.getDocument()); 58 59 docListener = new DocumentHandler(); 60 } 61 62 65 private void setDocument(Document document) { 66 if (model != null) { 67 model.removeDocumentListener(docListener); 68 } 69 if (editor != null) { 70 editor.removePropertyChangeListener(propChangeListener); 71 } 72 this.model = document; 73 if (model != null) { 74 if (rootElementInfo != null) { 75 rootElementInfo.invalidate(false); 76 } 77 buildInfo(); 78 model.addDocumentListener(docListener); 79 } 80 else { 81 rootElementInfo = null; 82 } 83 if (editor != null) { 84 editor.addPropertyChangeListener(propChangeListener); 85 } 86 } 87 88 91 private Document getDocument() { 92 return model; 93 } 94 95 98 private JEditorPane getTextComponent() { 99 return editor; 100 } 101 102 105 private ElementInfo getRootInfo() { 106 return rootElementInfo; 107 } 108 109 113 private View getRootView() { 114 return getTextComponent().getUI().getRootView(getTextComponent()); 115 } 116 117 120 private Rectangle getRootEditorRect() { 121 Rectangle alloc = getTextComponent().getBounds(); 122 if ((alloc.width > 0) && (alloc.height > 0)) { 123 alloc.x = alloc.y = 0; 124 Insets insets = editor.getInsets(); 125 alloc.x += insets.left; 126 alloc.y += insets.top; 127 alloc.width -= insets.left + insets.right; 128 alloc.height -= insets.top + insets.bottom; 129 return alloc; 130 } 131 return null; 132 } 133 134 139 private Object lock() { 140 Document document = getDocument(); 141 142 if (document instanceof AbstractDocument) { 143 ((AbstractDocument)document).readLock(); 144 return document; 145 } 146 return null; 147 } 148 149 152 private void unlock(Object key) { 153 if (key != null) { 154 ((AbstractDocument)key).readUnlock(); 155 } 156 } 157 158 161 private void buildInfo() { 162 Object lock = lock(); 163 164 try { 165 Document doc = getDocument(); 166 Element root = doc.getDefaultRootElement(); 167 168 rootElementInfo = new ElementInfo(root); 169 rootElementInfo.validate(); 170 } finally { 171 unlock(lock); 172 } 173 } 174 175 178 ElementInfo createElementInfo(Element e, ElementInfo parent) { 179 AttributeSet attrs = e.getAttributes(); 180 181 if (attrs != null) { 182 Object name = attrs.getAttribute(StyleConstants.NameAttribute); 183 184 if (name == HTML.Tag.IMG) { 185 return new IconElementInfo(e, parent); 186 } 187 else if (name == HTML.Tag.CONTENT || name == HTML.Tag.CAPTION) { 188 return new TextElementInfo(e, parent); 189 } 190 else if (name == HTML.Tag.TABLE) { 191 return new TableElementInfo(e, parent); 192 } 193 } 194 return null; 195 } 196 197 200 public AccessibleContext getAccessibleContext() { 201 if (rootHTMLAccessibleContext == null) { 202 rootHTMLAccessibleContext = 203 new RootHTMLAccessibleContext(rootElementInfo); 204 } 205 return rootHTMLAccessibleContext; 206 } 207 208 211 private class RootHTMLAccessibleContext extends HTMLAccessibleContext { 212 213 public RootHTMLAccessibleContext(ElementInfo elementInfo) { 214 super(elementInfo); 215 } 216 217 232 public String getAccessibleName() { 233 if (model != null) { 234 return (String )model.getProperty(Document.TitleProperty); 235 } else { 236 return null; 237 } 238 } 239 240 250 public String getAccessibleDescription() { 251 return editor.getContentType(); 252 } 253 254 272 public AccessibleRole getAccessibleRole() { 273 return AccessibleRole.TEXT; 274 } 275 } 276 277 280 protected abstract class HTMLAccessibleContext extends AccessibleContext 281 implements Accessible, AccessibleComponent { 282 283 protected ElementInfo elementInfo; 284 285 public HTMLAccessibleContext(ElementInfo elementInfo) { 286 this.elementInfo = elementInfo; 287 } 288 289 public AccessibleContext getAccessibleContext() { 291 return this; 292 } 293 294 301 public AccessibleStateSet getAccessibleStateSet() { 302 AccessibleStateSet states = new AccessibleStateSet(); 303 Component comp = getTextComponent(); 304 305 if (comp.isEnabled()) { 306 states.add(AccessibleState.ENABLED); 307 } 308 if (comp instanceof JTextComponent && 309 ((JTextComponent)comp).isEditable()) { 310 311 states.add(AccessibleState.EDITABLE); 312 states.add(AccessibleState.FOCUSABLE); 313 } 314 if (comp.isVisible()) { 315 states.add(AccessibleState.VISIBLE); 316 } 317 if (comp.isShowing()) { 318 states.add(AccessibleState.SHOWING); 319 } 320 return states; 321 } 322 323 333 public int getAccessibleIndexInParent() { 334 return elementInfo.getIndexInParent(); 335 } 336 337 342 public int getAccessibleChildrenCount() { 343 return elementInfo.getChildCount(); 344 } 345 346 356 public Accessible getAccessibleChild(int i) { 357 ElementInfo childInfo = elementInfo.getChild(i); 358 if (childInfo != null && childInfo instanceof Accessible) { 359 return (Accessible)childInfo; 360 } else { 361 return null; 362 } 363 } 364 365 377 public Locale getLocale() throws IllegalComponentStateException { 378 return editor.getLocale(); 379 } 380 382 public AccessibleComponent getAccessibleComponent() { 384 return this; 385 } 386 387 394 public Color getBackground() { 395 return getTextComponent().getBackground(); 396 } 397 398 404 public void setBackground(Color c) { 405 getTextComponent().setBackground(c); 406 } 407 408 415 public Color getForeground() { 416 return getTextComponent().getForeground(); 417 } 418 419 425 public void setForeground(Color c) { 426 getTextComponent().setForeground(c); 427 } 428 429 435 public Cursor getCursor() { 436 return getTextComponent().getCursor(); 437 } 438 439 445 public void setCursor(Cursor cursor) { 446 getTextComponent().setCursor(cursor); 447 } 448 449 455 public Font getFont() { 456 return getTextComponent().getFont(); 457 } 458 459 465 public void setFont(Font f) { 466 getTextComponent().setFont(f); 467 } 468 469 476 public FontMetrics getFontMetrics(Font f) { 477 return getTextComponent().getFontMetrics(f); 478 } 479 480 491 public boolean isEnabled() { 492 return getTextComponent().isEnabled(); 493 } 494 495 501 public void setEnabled(boolean b) { 502 getTextComponent().setEnabled(b); 503 } 504 505 520 public boolean isVisible() { 521 return getTextComponent().isVisible(); 522 } 523 524 530 public void setVisible(boolean b) { 531 getTextComponent().setVisible(b); 532 } 533 534 543 public boolean isShowing() { 544 return getTextComponent().isShowing(); 545 } 546 547 556 public boolean contains(Point p) { 557 Rectangle r = getBounds(); 558 if (r != null) { 559 return r.contains(p.x, p.y); 560 } else { 561 return false; 562 } 563 } 564 565 573 public Point getLocationOnScreen() { 574 Point editorLocation = getTextComponent().getLocationOnScreen(); 575 Rectangle r = getBounds(); 576 if (r != null) { 577 return new Point(editorLocation.x + r.x, 578 editorLocation.y + r.y); 579 } else { 580 return null; 581 } 582 } 583 584 595 public Point getLocation() { 596 Rectangle r = getBounds(); 597 if (r != null) { 598 return new Point(r.x, r.y); 599 } else { 600 return null; 601 } 602 } 603 604 609 public void setLocation(Point p) { 610 } 611 612 621 public Rectangle getBounds() { 622 return elementInfo.getBounds(); 623 } 624 625 633 public void setBounds(Rectangle r) { 634 } 635 636 646 public Dimension getSize() { 647 Rectangle r = getBounds(); 648 if (r != null) { 649 return new Dimension(r.width, r.height); 650 } else { 651 return null; 652 } 653 } 654 655 661 public void setSize(Dimension d) { 662 Component comp = getTextComponent(); 663 comp.setSize(d); 664 } 665 666 674 public Accessible getAccessibleAt(Point p) { 675 ElementInfo innerMostElement = getElementInfoAt(rootElementInfo, p); 676 if (innerMostElement instanceof Accessible) { 677 return (Accessible)innerMostElement; 678 } else { 679 return null; 680 } 681 } 682 683 private ElementInfo getElementInfoAt(ElementInfo elementInfo, Point p) { 684 if (elementInfo.getBounds() == null) { 685 return null; 686 } 687 if (elementInfo.getChildCount() == 0 && 688 elementInfo.getBounds().contains(p)) { 689 return elementInfo; 690 691 } else { 692 if (elementInfo instanceof TableElementInfo) { 693 ElementInfo captionInfo = 696 ((TableElementInfo)elementInfo).getCaptionInfo(); 697 if (captionInfo != null) { 698 Rectangle bounds = captionInfo.getBounds(); 699 if (bounds != null && bounds.contains(p)) { 700 return captionInfo; 701 } 702 } 703 } 704 for (int i = 0; i < elementInfo.getChildCount(); i++) 705 { 706 ElementInfo childInfo = elementInfo.getChild(i); 707 ElementInfo retValue = getElementInfoAt(childInfo, p); 708 if (retValue != null) { 709 return retValue; 710 } 711 } 712 } 713 return null; 714 } 715 716 727 public boolean isFocusTraversable() { 728 Component comp = getTextComponent(); 729 if (comp instanceof JTextComponent) { 730 if (((JTextComponent)comp).isEditable()) { 731 return true; 732 } 733 } 734 return false; 735 } 736 737 743 public void requestFocus() { 744 if (! isFocusTraversable()) { 746 return; 747 } 748 749 Component comp = getTextComponent(); 750 if (comp instanceof JTextComponent) { 751 752 comp.requestFocusInWindow(); 753 754 try { 755 if (elementInfo.validateIfNecessary()) { 756 Element elem = elementInfo.getElement(); 758 ((JTextComponent)comp).setCaretPosition(elem.getStartOffset()); 759 760 AccessibleContext ac = editor.getAccessibleContext(); 762 PropertyChangeEvent pce = new PropertyChangeEvent(this, 763 AccessibleContext.ACCESSIBLE_STATE_PROPERTY, 764 null, AccessibleState.FOCUSED); 765 ac.firePropertyChange( 766 AccessibleContext.ACCESSIBLE_STATE_PROPERTY, 767 null, pce); 768 } 769 } catch (IllegalArgumentException e) { 770 } 772 } 773 } 774 775 782 public void addFocusListener(FocusListener l) { 783 getTextComponent().addFocusListener(l); 784 } 785 786 793 public void removeFocusListener(FocusListener l) { 794 getTextComponent().removeFocusListener(l); 795 } 796 } 799 800 801 804 class TextElementInfo extends ElementInfo implements Accessible { 805 806 TextElementInfo(Element element, ElementInfo parent) { 807 super(element, parent); 808 } 809 810 private AccessibleContext accessibleContext; 812 813 public AccessibleContext getAccessibleContext() { 814 if (accessibleContext == null) { 815 accessibleContext = new TextAccessibleContext(this); 816 } 817 return accessibleContext; 818 } 819 820 823 public class TextAccessibleContext extends HTMLAccessibleContext 824 implements AccessibleText { 825 826 public TextAccessibleContext(ElementInfo elementInfo) { 827 super(elementInfo); 828 } 829 830 public AccessibleText getAccessibleText() { 831 return this; 832 } 833 834 849 public String getAccessibleName() { 850 if (model != null) { 851 return (String )model.getProperty(Document.TitleProperty); 852 } else { 853 return null; 854 } 855 } 856 857 867 public String getAccessibleDescription() { 868 return editor.getContentType(); 869 } 870 871 889 public AccessibleRole getAccessibleRole() { 890 return AccessibleRole.TEXT; 891 } 892 893 902 public int getIndexAtPoint(Point p) { 903 View v = getView(); 904 if (v != null) { 905 return v.viewToModel(p.x, p.y, getBounds()); 906 } else { 907 return -1; 908 } 909 } 910 911 921 public Rectangle getCharacterBounds(int i) { 922 try { 923 return editor.getUI().modelToView(editor, i); 924 } catch (BadLocationException e) { 925 return null; 926 } 927 } 928 929 934 public int getCharCount() { 935 if (validateIfNecessary()) { 936 Element elem = elementInfo.getElement(); 937 return elem.getEndOffset() - elem.getStartOffset(); 938 } 939 return 0; 940 } 941 942 949 public int getCaretPosition() { 950 View v = getView(); 951 if (v == null) { 952 return -1; 953 } 954 Container c = v.getContainer(); 955 if (c == null) { 956 return -1; 957 } 958 if (c instanceof JTextComponent) { 959 return ((JTextComponent)c).getCaretPosition(); 960 } else { 961 return -1; 962 } 963 } 964 965 969 private class IndexedSegment extends Segment { 970 973 public int modelOffset; 974 } 975 976 public String getAtIndex(int part, int index) { 977 return getAtIndex(part, index, 0); 978 } 979 980 981 public String getAfterIndex(int part, int index) { 982 return getAtIndex(part, index, 1); 983 } 984 985 public String getBeforeIndex(int part, int index) { 986 return getAtIndex(part, index, -1); 987 } 988 989 994 private String getAtIndex(int part, int index, int direction) { 995 if (model instanceof AbstractDocument) { 996 ((AbstractDocument)model).readLock(); 997 } 998 try { 999 if (index < 0 || index >= model.getLength()) { 1000 return null; 1001 } 1002 switch (part) { 1003 case AccessibleText.CHARACTER: 1004 if (index + direction < model.getLength() && 1005 index + direction >= 0) { 1006 return model.getText(index + direction, 1); 1007 } 1008 break; 1009 1010 1011 case AccessibleText.WORD: 1012 case AccessibleText.SENTENCE: 1013 IndexedSegment seg = getSegmentAt(part, index); 1014 if (seg != null) { 1015 if (direction != 0) { 1016 int next; 1017 1018 1019 if (direction < 0) { 1020 next = seg.modelOffset - 1; 1021 } 1022 else { 1023 next = seg.modelOffset + direction * seg.count; 1024 } 1025 if (next >= 0 && next <= model.getLength()) { 1026 seg = getSegmentAt(part, next); 1027 } 1028 else { 1029 seg = null; 1030 } 1031 } 1032 if (seg != null) { 1033 return new String (seg.array, seg.offset, 1034 seg.count); 1035 } 1036 } 1037 break; 1038 1039 default: 1040 break; 1041 } 1042 } catch (BadLocationException e) { 1043 } finally { 1044 if (model instanceof AbstractDocument) { 1045 ((AbstractDocument)model).readUnlock(); 1046 } 1047 } 1048 return null; 1049 } 1050 1051 1054 private Element getParagraphElement(int index) { 1055 if (model instanceof PlainDocument ) { 1056 PlainDocument sdoc = (PlainDocument)model; 1057 return sdoc.getParagraphElement(index); 1058 } else if (model instanceof StyledDocument) { 1059 StyledDocument sdoc = (StyledDocument)model; 1060 return sdoc.getParagraphElement(index); 1061 } else { 1062 Element para = null; 1063 for (para = model.getDefaultRootElement(); ! para.isLeaf(); ) { 1064 int pos = para.getElementIndex(index); 1065 para = para.getElement(pos); 1066 } 1067 if (para == null) { 1068 return null; 1069 } 1070 return para.getParentElement(); 1071 } 1072 } 1073 1074 1079 private IndexedSegment getParagraphElementText(int index) 1080 throws BadLocationException { 1081 Element para = getParagraphElement(index); 1082 1083 1084 if (para != null) { 1085 IndexedSegment segment = new IndexedSegment(); 1086 try { 1087 int length = para.getEndOffset() - para.getStartOffset(); 1088 model.getText(para.getStartOffset(), length, segment); 1089 } catch (BadLocationException e) { 1090 return null; 1091 } 1092 segment.modelOffset = para.getStartOffset(); 1093 return segment; 1094 } 1095 return null; 1096 } 1097 1098 1099 1107 private IndexedSegment getSegmentAt(int part, int index) 1108 throws BadLocationException { 1109 1110 IndexedSegment seg = getParagraphElementText(index); 1111 if (seg == null) { 1112 return null; 1113 } 1114 BreakIterator iterator; 1115 switch (part) { 1116 case AccessibleText.WORD: 1117 iterator = BreakIterator.getWordInstance(getLocale()); 1118 break; 1119 case AccessibleText.SENTENCE: 1120 iterator = BreakIterator.getSentenceInstance(getLocale()); 1121 break; 1122 default: 1123 return null; 1124 } 1125 seg.first(); 1126 iterator.setText(seg); 1127 int end = iterator.following(index - seg.modelOffset + seg.offset); 1128 if (end == BreakIterator.DONE) { 1129 return null; 1130 } 1131 if (end > seg.offset + seg.count) { 1132 return null; 1133 } 1134 int begin = iterator.previous(); 1135 if (begin == BreakIterator.DONE || 1136 begin >= seg.offset + seg.count) { 1137 return null; 1138 } 1139 seg.modelOffset = seg.modelOffset + begin - seg.offset; 1140 seg.offset = begin; 1141 seg.count = end - begin; 1142 return seg; 1143 } 1144 1145 1151 public AttributeSet getCharacterAttribute(int i) { 1152 if (model instanceof StyledDocument) { 1153 StyledDocument doc = (StyledDocument)model; 1154 Element elem = doc.getCharacterElement(i); 1155 if (elem != null) { 1156 return elem.getAttributes(); 1157 } 1158 } 1159 return null; 1160 } 1161 1162 1169 public int getSelectionStart() { 1170 return editor.getSelectionStart(); 1171 } 1172 1173 1180 public int getSelectionEnd() { 1181 return editor.getSelectionEnd(); 1182 } 1183 1184 1189 public String getSelectedText() { 1190 return editor.getSelectedText(); 1191 } 1192 1193 1197 private String getText(int offset, int length) 1198 throws BadLocationException { 1199 1200 if (model != null && model instanceof StyledDocument) { 1201 StyledDocument doc = (StyledDocument)model; 1202 return model.getText(offset, length); 1203 } else { 1204 return null; 1205 } 1206 } 1207 } 1208 } 1209 1210 1213 private class IconElementInfo extends ElementInfo implements Accessible { 1214 1215 private int width = -1; 1216 private int height = -1; 1217 1218 IconElementInfo(Element element, ElementInfo parent) { 1219 super(element, parent); 1220 } 1221 1222 protected void invalidate(boolean first) { 1223 super.invalidate(first); 1224 width = height = -1; 1225 } 1226 1227 private int getImageSize(Object key) { 1228 if (validateIfNecessary()) { 1229 int size = getIntAttr(getAttributes(), key, -1); 1230 1231 if (size == -1) { 1232 View v = getView(); 1233 1234 size = 0; 1235 if (v instanceof ImageView ) { 1236 Image img = ((ImageView )v).getImage(); 1237 if (img != null) { 1238 if (key == HTML.Attribute.WIDTH) { 1239 size = img.getWidth(null); 1240 } 1241 else { 1242 size = img.getHeight(null); 1243 } 1244 } 1245 } 1246 } 1247 return size; 1248 } 1249 return 0; 1250 } 1251 1252 private AccessibleContext accessibleContext; 1254 1255 public AccessibleContext getAccessibleContext() { 1256 if (accessibleContext == null) { 1257 accessibleContext = new IconAccessibleContext(this); 1258 } 1259 return accessibleContext; 1260 } 1261 1262 1265 protected class IconAccessibleContext extends HTMLAccessibleContext 1266 implements AccessibleIcon { 1267 1268 public IconAccessibleContext(ElementInfo elementInfo) { 1269 super(elementInfo); 1270 } 1271 1272 1287 public String getAccessibleName() { 1288 return getAccessibleIconDescription(); 1289 } 1290 1291 1301 public String getAccessibleDescription() { 1302 return editor.getContentType(); 1303 } 1304 1305 1323 public AccessibleRole getAccessibleRole() { 1324 return AccessibleRole.ICON; 1325 } 1326 1327 public AccessibleIcon [] getAccessibleIcon() { 1328 AccessibleIcon [] icons = new AccessibleIcon[1]; 1329 icons[0] = this; 1330 return icons; 1331 } 1332 1333 1341 public String getAccessibleIconDescription() { 1342 return ((ImageView )getView()).getAltText(); 1343 } 1344 1345 1353 public void setAccessibleIconDescription(String description) { 1354 } 1355 1356 1361 public int getAccessibleIconWidth() { 1362 if (width == -1) { 1363 width = getImageSize(HTML.Attribute.WIDTH); 1364 } 1365 return width; 1366 } 1367 1368 1373 public int getAccessibleIconHeight() { 1374 if (height == -1) { 1375 height = getImageSize(HTML.Attribute.HEIGHT); 1376 } 1377 return height; 1378 } 1379 } 1380 } 1382 1383 1384 1393 private class TableElementInfo extends ElementInfo 1394 implements Accessible { 1395 1396 protected ElementInfo caption; 1397 1398 1403 private TableCellElementInfo[][] grid; 1404 1405 1406 TableElementInfo(Element e, ElementInfo parent) { 1407 super(e, parent); 1408 } 1409 1410 public ElementInfo getCaptionInfo() { 1411 return caption; 1412 } 1413 1414 1417 protected void validate() { 1418 super.validate(); 1419 updateGrid(); 1420 } 1421 1422 1425 protected void loadChildren(Element e) { 1426 1427 for (int counter = 0; counter < e.getElementCount(); counter++) { 1428 Element child = e.getElement(counter); 1429 AttributeSet attrs = child.getAttributes(); 1430 1431 if (attrs.getAttribute(StyleConstants.NameAttribute) == 1432 HTML.Tag.TR) { 1433 addChild(new TableRowElementInfo(child, this, counter)); 1434 1435 } else if (attrs.getAttribute(StyleConstants.NameAttribute) == 1436 HTML.Tag.CAPTION) { 1437 caption = createElementInfo(child, this); 1440 } 1441 } 1442 } 1443 1444 1447 private void updateGrid() { 1448 int delta = 0; 1450 int maxCols = 0; 1451 int rows = 0; 1452 for (int counter = 0; counter < getChildCount(); counter++) { 1453 TableRowElementInfo row = getRow(counter); 1454 int prev = 0; 1455 for (int y = 0; y < delta; y++) { 1456 prev = Math.max(prev, getRow(counter - y - 1). 1457 getColumnCount(y + 2)); 1458 } 1459 delta = Math.max(row.getRowCount(), delta); 1460 delta--; 1461 maxCols = Math.max(maxCols, row.getColumnCount() + prev); 1462 } 1463 rows = getChildCount() + delta; 1464 1465 grid = new TableCellElementInfo[rows][]; 1467 for (int counter = 0; counter < rows; counter++) { 1468 grid[counter] = new TableCellElementInfo[maxCols]; 1469 } 1470 for (int counter = 0; counter < rows; counter++) { 1472 getRow(counter).updateGrid(counter); 1473 } 1474 } 1475 1476 1479 public TableRowElementInfo getRow(int index) { 1480 return (TableRowElementInfo)getChild(index); 1481 } 1482 1483 1486 public TableCellElementInfo getCell(int r, int c) { 1487 if (validateIfNecessary() && r < grid.length && 1488 c < grid[0].length) { 1489 return grid[r][c]; 1490 } 1491 return null; 1492 } 1493 1494 1497 public int getRowExtentAt(int r, int c) { 1498 TableCellElementInfo cell = getCell(r, c); 1499 1500 if (cell != null) { 1501 int rows = cell.getRowCount(); 1502 int delta = 1; 1503 1504 while ((r - delta) >= 0 && grid[r - delta][c] == cell) { 1505 delta++; 1506 } 1507 return rows - delta + 1; 1508 } 1509 return 0; 1510 } 1511 1512 1515 public int getColumnExtentAt(int r, int c) { 1516 TableCellElementInfo cell = getCell(r, c); 1517 1518 if (cell != null) { 1519 int cols = cell.getColumnCount(); 1520 int delta = 1; 1521 1522 while ((c - delta) >= 0 && grid[r][c - delta] == cell) { 1523 delta++; 1524 } 1525 return cols - delta + 1; 1526 } 1527 return 0; 1528 } 1529 1530 1533 public int getRowCount() { 1534 if (validateIfNecessary()) { 1535 return grid.length; 1536 } 1537 return 0; 1538 } 1539 1540 1543 public int getColumnCount() { 1544 if (validateIfNecessary() && grid.length > 0) { 1545 return grid[0].length; 1546 } 1547 return 0; 1548 } 1549 1550 private AccessibleContext accessibleContext; 1552 1553 public AccessibleContext getAccessibleContext() { 1554 if (accessibleContext == null) { 1555 accessibleContext = new TableAccessibleContext(this); 1556 } 1557 return accessibleContext; 1558 } 1559 1560 1563 public class TableAccessibleContext extends HTMLAccessibleContext 1564 implements AccessibleTable { 1565 1566 private AccessibleHeadersTable rowHeadersTable; 1567 1568 public TableAccessibleContext(ElementInfo elementInfo) { 1569 super(elementInfo); 1570 } 1571 1572 1587 public String getAccessibleName() { 1588 return getAccessibleRole().toString(); 1590 } 1591 1592 1602 public String getAccessibleDescription() { 1603 return editor.getContentType(); 1604 } 1605 1606 1624 public AccessibleRole getAccessibleRole() { 1625 return AccessibleRole.TABLE; 1626 } 1627 1628 1638 public int getAccessibleIndexInParent() { 1639 return elementInfo.getIndexInParent(); 1640 } 1641 1642 1647 public int getAccessibleChildrenCount() { 1648 return ((TableElementInfo)elementInfo).getRowCount() * 1649 ((TableElementInfo)elementInfo).getColumnCount(); 1650 } 1651 1652 1662 public Accessible getAccessibleChild(int i) { 1663 int rowCount = ((TableElementInfo)elementInfo).getRowCount(); 1664 int columnCount = ((TableElementInfo)elementInfo).getColumnCount(); 1665 int r = i / rowCount; 1666 int c = i % columnCount; 1667 if (r < 0 || r >= rowCount || c < 0 || c >= columnCount) { 1668 return null; 1669 } else { 1670 return getAccessibleAt(r, c); 1671 } 1672 } 1673 1674 public AccessibleTable getAccessibleTable() { 1675 return this; 1676 } 1677 1678 1683 public Accessible getAccessibleCaption() { 1684 ElementInfo captionInfo = getCaptionInfo(); 1685 if (captionInfo instanceof Accessible) { 1686 return (Accessible)caption; 1687 } else { 1688 return null; 1689 } 1690 } 1691 1692 1697 public void setAccessibleCaption(Accessible a) { 1698 } 1699 1700 1705 public Accessible getAccessibleSummary() { 1706 return null; 1707 } 1708 1709 1714 public void setAccessibleSummary(Accessible a) { 1715 } 1716 1717 1722 public int getAccessibleRowCount() { 1723 return ((TableElementInfo)elementInfo).getRowCount(); 1724 } 1725 1726 1731 public int getAccessibleColumnCount() { 1732 return ((TableElementInfo)elementInfo).getColumnCount(); 1733 } 1734 1735 1743 public Accessible getAccessibleAt(int r, int c) { 1744 TableCellElementInfo cellInfo = getCell(r, c); 1745 if (cellInfo != null) { 1746 return cellInfo.getAccessible(); 1747 } else { 1748 return null; 1749 } 1750 } 1751 1752 1759 public int getAccessibleRowExtentAt(int r, int c) { 1760 return ((TableElementInfo)elementInfo).getRowExtentAt(r, c); 1761 } 1762 1763 1770 public int getAccessibleColumnExtentAt(int r, int c) { 1771 return ((TableElementInfo)elementInfo).getColumnExtentAt(r, c); 1772 } 1773 1774 1780 public AccessibleTable getAccessibleRowHeader() { 1781 return rowHeadersTable; 1782 } 1783 1784 1790 public void setAccessibleRowHeader(AccessibleTable table) { 1791 } 1792 1793 1799 public AccessibleTable getAccessibleColumnHeader() { 1800 return null; 1801 } 1802 1803 1809 public void setAccessibleColumnHeader(AccessibleTable table) { 1810 } 1811 1812 1818 public Accessible getAccessibleRowDescription(int r) { 1819 return null; 1820 } 1821 1822 1828 public void setAccessibleRowDescription(int r, Accessible a) { 1829 } 1830 1831 1837 public Accessible getAccessibleColumnDescription(int c) { 1838 return null; 1839 } 1840 1841 1847 public void setAccessibleColumnDescription(int c, Accessible a) { 1848 } 1849 1850 1860 public boolean isAccessibleSelected(int r, int c) { 1861 if (validateIfNecessary()) { 1862 if (r < 0 || r >= getAccessibleRowCount() || 1863 c < 0 || c >= getAccessibleColumnCount()) { 1864 return false; 1865 } 1866 TableCellElementInfo cell = getCell(r, c); 1867 if (cell != null) { 1868 Element elem = cell.getElement(); 1869 int start = elem.getStartOffset(); 1870 int end = elem.getEndOffset(); 1871 return start >= editor.getSelectionStart() && 1872 end <= editor.getSelectionEnd(); 1873 } 1874 } 1875 return false; 1876 } 1877 1878 1886 public boolean isAccessibleRowSelected(int r) { 1887 if (validateIfNecessary()) { 1888 if (r < 0 || r >= getAccessibleRowCount()) { 1889 return false; 1890 } 1891 int nColumns = getAccessibleColumnCount(); 1892 1893 TableCellElementInfo startCell = getCell(r, 0); 1894 if (startCell == null) { 1895 return false; 1896 } 1897 int start = startCell.getElement().getStartOffset(); 1898 1899 TableCellElementInfo endCell = getCell(r, nColumns-1); 1900 if (endCell == null) { 1901 return false; 1902 } 1903 int end = endCell.getElement().getEndOffset(); 1904 1905 return start >= editor.getSelectionStart() && 1906 end <= editor.getSelectionEnd(); 1907 } 1908 return false; 1909 } 1910 1911 1919 public boolean isAccessibleColumnSelected(int c) { 1920 if (validateIfNecessary()) { 1921 if (c < 0 || c >= getAccessibleColumnCount()) { 1922 return false; 1923 } 1924 int nRows = getAccessibleRowCount(); 1925 1926 TableCellElementInfo startCell = getCell(0, c); 1927 if (startCell == null) { 1928 return false; 1929 } 1930 int start = startCell.getElement().getStartOffset(); 1931 1932 TableCellElementInfo endCell = getCell(nRows-1, c); 1933 if (endCell == null) { 1934 return false; 1935 } 1936 int end = endCell.getElement().getEndOffset(); 1937 return start >= editor.getSelectionStart() && 1938 end <= editor.getSelectionEnd(); 1939 } 1940 return false; 1941 } 1942 1943 1949 public int [] getSelectedAccessibleRows() { 1950 if (validateIfNecessary()) { 1951 int nRows = getAccessibleRowCount(); 1952 Vector vec = new Vector(); 1953 1954 for (int i = 0; i < nRows; i++) { 1955 if (isAccessibleRowSelected(i)) { 1956 vec.addElement(new Integer (i)); 1957 } 1958 } 1959 int retval[] = new int[vec.size()]; 1960 for (int i = 0; i < retval.length; i++) { 1961 retval[i] = ((Integer )vec.elementAt(i)).intValue(); 1962 } 1963 return retval; 1964 } 1965 return new int[0]; 1966 } 1967 1968 1974 public int [] getSelectedAccessibleColumns() { 1975 if (validateIfNecessary()) { 1976 int nColumns = getAccessibleRowCount(); 1977 Vector vec = new Vector(); 1978 1979 for (int i = 0; i < nColumns; i++) { 1980 if (isAccessibleColumnSelected(i)) { 1981 vec.addElement(new Integer (i)); 1982 } 1983 } 1984 int retval[] = new int[vec.size()]; 1985 for (int i = 0; i < retval.length; i++) { 1986 retval[i] = ((Integer )vec.elementAt(i)).intValue(); 1987 } 1988 return retval; 1989 } 1990 return new int[0]; 1991 } 1992 1993 1995 2002 public int getAccessibleRow(int index) { 2003 if (validateIfNecessary()) { 2004 int numCells = getAccessibleColumnCount() * 2005 getAccessibleRowCount(); 2006 if (index >= numCells) { 2007 return -1; 2008 } else { 2009 return index / getAccessibleColumnCount(); 2010 } 2011 } 2012 return -1; 2013 } 2014 2015 2022 public int getAccessibleColumn(int index) { 2023 if (validateIfNecessary()) { 2024 int numCells = getAccessibleColumnCount() * 2025 getAccessibleRowCount(); 2026 if (index >= numCells) { 2027 return -1; 2028 } else { 2029 return index % getAccessibleColumnCount(); 2030 } 2031 } 2032 return -1; 2033 } 2034 2035 2043 public int getAccessibleIndex(int r, int c) { 2044 if (validateIfNecessary()) { 2045 if (r >= getAccessibleRowCount() || 2046 c >= getAccessibleColumnCount()) { 2047 return -1; 2048 } else { 2049 return r * getAccessibleColumnCount() + c; 2050 } 2051 } 2052 return -1; 2053 } 2054 2055 2062 public String getAccessibleRowHeader(int r) { 2063 if (validateIfNecessary()) { 2064 TableCellElementInfo cellInfo = getCell(r, 0); 2065 if (cellInfo.isHeaderCell()) { 2066 View v = cellInfo.getView(); 2067 if (v != null && model != null) { 2068 try { 2069 return model.getText(v.getStartOffset(), 2070 v.getEndOffset() - 2071 v.getStartOffset()); 2072 } catch (BadLocationException e) { 2073 return null; 2074 } 2075 } 2076 } 2077 } 2078 return null; 2079 } 2080 2081 2088 public String getAccessibleColumnHeader(int c) { 2089 if (validateIfNecessary()) { 2090 TableCellElementInfo cellInfo = getCell(0, c); 2091 if (cellInfo.isHeaderCell()) { 2092 View v = cellInfo.getView(); 2093 if (v != null && model != null) { 2094 try { 2095 return model.getText(v.getStartOffset(), 2096 v.getEndOffset() - 2097 v.getStartOffset()); 2098 } catch (BadLocationException e) { 2099 return null; 2100 } 2101 } 2102 } 2103 } 2104 return null; 2105 } 2106 2107 public void addRowHeader(TableCellElementInfo cellInfo, int rowNumber) { 2108 if (rowHeadersTable == null) { 2109 rowHeadersTable = new AccessibleHeadersTable(); 2110 } 2111 rowHeadersTable.addHeader(cellInfo, rowNumber); 2112 } 2113 2115 protected class AccessibleHeadersTable implements AccessibleTable { 2116 2117 private Hashtable headers = new Hashtable(); 2121 private int rowCount = 0; 2122 private int columnCount = 0; 2123 2124 public void addHeader(TableCellElementInfo cellInfo, int rowNumber) { 2125 Integer rowInteger = new Integer (rowNumber); 2126 ArrayList list = (ArrayList)headers.get(rowInteger); 2127 if (list == null) { 2128 list = new ArrayList(); 2129 headers.put(rowInteger, list); 2130 } 2131 list.add(cellInfo); 2132 } 2133 2134 2139 public Accessible getAccessibleCaption() { 2140 return null; 2141 } 2142 2143 2148 public void setAccessibleCaption(Accessible a) { 2149 } 2150 2151 2156 public Accessible getAccessibleSummary() { 2157 return null; 2158 } 2159 2160 2165 public void setAccessibleSummary(Accessible a) { 2166 } 2167 2168 2173 public int getAccessibleRowCount() { 2174 return rowCount; 2175 } 2176 2177 2182 public int getAccessibleColumnCount() { 2183 return columnCount; 2184 } 2185 2186 private TableCellElementInfo getElementInfoAt(int r, int c) { 2187 ArrayList list = (ArrayList)headers.get(new Integer (r)); 2188 if (list != null) { 2189 return (TableCellElementInfo)list.get(c); 2190 } else { 2191 return null; 2192 } 2193 } 2194 2195 2203 public Accessible getAccessibleAt(int r, int c) { 2204 ElementInfo elementInfo = getElementInfoAt(r, c); 2205 if (elementInfo instanceof Accessible) { 2206 return (Accessible)elementInfo; 2207 } else { 2208 return null; 2209 } 2210 } 2211 2212 2219 public int getAccessibleRowExtentAt(int r, int c) { 2220 TableCellElementInfo elementInfo = getElementInfoAt(r, c); 2221 if (elementInfo != null) { 2222 return elementInfo.getRowCount(); 2223 } else { 2224 return 0; 2225 } 2226 } 2227 2228 2235 public int getAccessibleColumnExtentAt(int r, int c) { 2236 TableCellElementInfo elementInfo = getElementInfoAt(r, c); 2237 if (elementInfo != null) { 2238 return elementInfo.getRowCount(); 2239 } else { 2240 return 0; 2241 } 2242 } 2243 2244 2250 public AccessibleTable getAccessibleRowHeader() { 2251 return null; 2252 } 2253 2254 2260 public void setAccessibleRowHeader(AccessibleTable table) { 2261 } 2262 2263 2269 public AccessibleTable getAccessibleColumnHeader() { 2270 return null; 2271 } 2272 2273 2279 public void setAccessibleColumnHeader(AccessibleTable table) { 2280 } 2281 2282 2288 public Accessible getAccessibleRowDescription(int r) { 2289 return null; 2290 } 2291 2292 2298 public void setAccessibleRowDescription(int r, Accessible a) { 2299 } 2300 2301 2307 public Accessible getAccessibleColumnDescription(int c) { 2308 return null; 2309 } 2310 2311 2317 public void setAccessibleColumnDescription(int c, Accessible a) { 2318 } 2319 2320 2330 public boolean isAccessibleSelected(int r, int c) { 2331 return false; 2332 } 2333 2334 2342 public boolean isAccessibleRowSelected(int r) { 2343 return false; 2344 } 2345 2346 2354 public boolean isAccessibleColumnSelected(int c) { 2355 return false; 2356 } 2357 2358 2364 public int [] getSelectedAccessibleRows() { 2365 return new int [0]; 2366 } 2367 2368 2374 public int [] getSelectedAccessibleColumns() { 2375 return new int [0]; 2376 } 2377 } 2378 } 2380 2383 private class TableRowElementInfo extends ElementInfo { 2384 2385 private TableElementInfo parent; 2386 private int rowNumber; 2387 2388 TableRowElementInfo(Element e, TableElementInfo parent, int rowNumber) { 2389 super(e, parent); 2390 this.parent = parent; 2391 this.rowNumber = rowNumber; 2392 } 2393 2394 protected void loadChildren(Element e) { 2395 for (int x = 0; x < e.getElementCount(); x++) { 2396 AttributeSet attrs = e.getElement(x).getAttributes(); 2397 2398 if (attrs.getAttribute(StyleConstants.NameAttribute) == 2399 HTML.Tag.TH) { 2400 TableCellElementInfo headerElementInfo = 2401 new TableCellElementInfo(e.getElement(x), this, true); 2402 addChild(headerElementInfo); 2403 2404 AccessibleTable at = 2405 parent.getAccessibleContext().getAccessibleTable(); 2406 TableAccessibleContext tableElement = 2407 (TableAccessibleContext)at; 2408 tableElement.addRowHeader(headerElementInfo, rowNumber); 2409 2410 } else if (attrs.getAttribute(StyleConstants.NameAttribute) == 2411 HTML.Tag.TD) { 2412 addChild(new TableCellElementInfo(e.getElement(x), this, 2413 false)); 2414 } 2415 } 2416 } 2417 2418 2421 public int getRowCount() { 2422 int rowCount = 1; 2423 if (validateIfNecessary()) { 2424 for (int counter = 0; counter < getChildCount(); 2425 counter++) { 2426 2427 TableCellElementInfo cell = (TableCellElementInfo) 2428 getChild(counter); 2429 2430 if (cell.validateIfNecessary()) { 2431 rowCount = Math.max(rowCount, cell.getRowCount()); 2432 } 2433 } 2434 } 2435 return rowCount; 2436 } 2437 2438 2442 public int getColumnCount() { 2443 int colCount = 0; 2444 if (validateIfNecessary()) { 2445 for (int counter = 0; counter < getChildCount(); 2446 counter++) { 2447 TableCellElementInfo cell = (TableCellElementInfo) 2448 getChild(counter); 2449 2450 if (cell.validateIfNecessary()) { 2451 colCount += cell.getColumnCount(); 2452 } 2453 } 2454 } 2455 return colCount; 2456 } 2457 2458 2462 protected void invalidate(boolean first) { 2463 super.invalidate(first); 2464 getParent().invalidate(true); 2465 } 2466 2467 2471 private void updateGrid(int row) { 2472 if (validateIfNecessary()) { 2473 boolean emptyRow = false; 2474 2475 while (!emptyRow) { 2476 for (int counter = 0; counter < grid[row].length; 2477 counter++) { 2478 if (grid[row][counter] == null) { 2479 emptyRow = true; 2480 break; 2481 } 2482 } 2483 if (!emptyRow) { 2484 row++; 2485 } 2486 } 2487 for (int col = 0, counter = 0; counter < getChildCount(); 2488 counter++) { 2489 TableCellElementInfo cell = (TableCellElementInfo) 2490 getChild(counter); 2491 2492 while (grid[row][col] != null) { 2493 col++; 2494 } 2495 for (int rowCount = cell.getRowCount() - 1; 2496 rowCount >= 0; rowCount--) { 2497 for (int colCount = cell.getColumnCount() - 1; 2498 colCount >= 0; colCount--) { 2499 grid[row + rowCount][col + colCount] = cell; 2500 } 2501 } 2502 col += cell.getColumnCount(); 2503 } 2504 } 2505 } 2506 2507 2511 private int getColumnCount(int rowspan) { 2512 if (validateIfNecessary()) { 2513 int cols = 0; 2514 for (int counter = 0; counter < getChildCount(); 2515 counter++) { 2516 TableCellElementInfo cell = (TableCellElementInfo) 2517 getChild(counter); 2518 2519 if (cell.getRowCount() >= rowspan) { 2520 cols += cell.getColumnCount(); 2521 } 2522 } 2523 return cols; 2524 } 2525 return 0; 2526 } 2527 } 2528 2529 2533 private class TableCellElementInfo extends ElementInfo { 2534 2535 private Accessible accessible; 2536 private boolean isHeaderCell; 2537 2538 TableCellElementInfo(Element e, ElementInfo parent) { 2539 super(e, parent); 2540 this.isHeaderCell = false; 2541 } 2542 2543 TableCellElementInfo(Element e, ElementInfo parent, 2544 boolean isHeaderCell) { 2545 super(e, parent); 2546 this.isHeaderCell = isHeaderCell; 2547 } 2548 2549 2552 public boolean isHeaderCell() { 2553 return this.isHeaderCell; 2554 } 2555 2556 2559 public Accessible getAccessible() { 2560 accessible = null; 2561 getAccessible(this); 2562 return accessible; 2563 } 2564 2565 2568 private void getAccessible(ElementInfo elementInfo) { 2569 if (elementInfo instanceof Accessible) { 2570 accessible = (Accessible)elementInfo; 2571 return; 2572 } else { 2573 for (int i = 0; i < elementInfo.getChildCount(); i++) { 2574 getAccessible(elementInfo.getChild(i)); 2575 } 2576 } 2577 } 2578 2579 2582 public int getRowCount() { 2583 if (validateIfNecessary()) { 2584 return Math.max(1, getIntAttr(getAttributes(), 2585 HTML.Attribute.ROWSPAN, 1)); 2586 } 2587 return 0; 2588 } 2589 2590 2593 public int getColumnCount() { 2594 if (validateIfNecessary()) { 2595 return Math.max(1, getIntAttr(getAttributes(), 2596 HTML.Attribute.COLSPAN, 1)); 2597 } 2598 return 0; 2599 } 2600 2601 2605 protected void invalidate(boolean first) { 2606 super.invalidate(first); 2607 getParent().invalidate(true); 2608 } 2609 } 2610 } 2611 2612 2613 2624 private class ElementInfo { 2625 2626 2629 private ArrayList children; 2630 2633 private Element element; 2634 2637 private ElementInfo parent; 2638 2641 private boolean isValid; 2642 2645 private boolean canBeValid; 2646 2647 2648 2651 ElementInfo(Element element) { 2652 this(element, null); 2653 } 2654 2655 2659 ElementInfo(Element element, ElementInfo parent) { 2660 this.element = element; 2661 this.parent = parent; 2662 isValid = false; 2663 canBeValid = true; 2664 } 2665 2666 2671 protected void validate() { 2672 isValid = true; 2673 loadChildren(getElement()); 2674 } 2675 2676 2679 protected void loadChildren(Element parent) { 2680 if (!parent.isLeaf()) { 2681 for (int counter = 0, maxCounter = parent.getElementCount(); 2682 counter < maxCounter; counter++) { 2683 Element e = parent.getElement(counter); 2684 ElementInfo childInfo = createElementInfo(e, this); 2685 2686 if (childInfo != null) { 2687 addChild(childInfo); 2688 } 2689 else { 2690 loadChildren(e); 2691 } 2692 } 2693 } 2694 } 2695 2696 2700 public int getIndexInParent() { 2701 if (parent == null || !parent.isValid()) { 2702 return -1; 2703 } 2704 return parent.indexOf(this); 2705 } 2706 2707 2710 public Element getElement() { 2711 return element; 2712 } 2713 2714 2717 public ElementInfo getParent() { 2718 return parent; 2719 } 2720 2721 2725 public int indexOf(ElementInfo child) { 2726 ArrayList children = this.children; 2727 2728 if (children != null) { 2729 return children.indexOf(child); 2730 } 2731 return -1; 2732 } 2733 2734 2738 public ElementInfo getChild(int index) { 2739 if (validateIfNecessary()) { 2740 ArrayList children = this.children; 2741 2742 if (children != null && index >= 0 && 2743 index < children.size()) { 2744 return (ElementInfo)children.get(index); 2745 } 2746 } 2747 return null; 2748 } 2749 2750 2753 public int getChildCount() { 2754 validateIfNecessary(); 2755 return (children == null) ? 0 : children.size(); 2756 } 2757 2758 2761 protected void addChild(ElementInfo child) { 2762 if (children == null) { 2763 children = new ArrayList(); 2764 } 2765 children.add(child); 2766 } 2767 2768 2772 protected View getView() { 2773 if (!validateIfNecessary()) { 2774 return null; 2775 } 2776 Object lock = lock(); 2777 try { 2778 View rootView = getRootView(); 2779 Element e = getElement(); 2780 int start = e.getStartOffset(); 2781 2782 if (rootView != null) { 2783 return getView(rootView, e, start); 2784 } 2785 return null; 2786 } finally { 2787 unlock(lock); 2788 } 2789 } 2790 2791 2795 public Rectangle getBounds() { 2796 if (!validateIfNecessary()) { 2797 return null; 2798 } 2799 Object lock = lock(); 2800 try { 2801 Rectangle bounds = getRootEditorRect(); 2802 View rootView = getRootView(); 2803 Element e = getElement(); 2804 2805 if (bounds != null && rootView != null) { 2806 try { 2807 return rootView.modelToView(e.getStartOffset(), 2808 Position.Bias.Forward, 2809 e.getEndOffset(), 2810 Position.Bias.Backward, 2811 bounds).getBounds(); 2812 } catch (BadLocationException ble) { } 2813 } 2814 } finally { 2815 unlock(lock); 2816 } 2817 return null; 2818 } 2819 2820 2823 protected boolean isValid() { 2824 return isValid; 2825 } 2826 2827 2831 protected AttributeSet getAttributes() { 2832 if (validateIfNecessary()) { 2833 return getElement().getAttributes(); 2834 } 2835 return null; 2836 } 2837 2838 2843 protected AttributeSet getViewAttributes() { 2844 if (validateIfNecessary()) { 2845 View view = getView(); 2846 2847 if (view != null) { 2848 return view.getElement().getAttributes(); 2849 } 2850 return getElement().getAttributes(); 2851 } 2852 return null; 2853 } 2854 2855 2859 protected int getIntAttr(AttributeSet attrs, Object key, int deflt) { 2860 if (attrs != null && attrs.isDefined(key)) { 2861 int i; 2862 String val = (String )attrs.getAttribute(key); 2863 if (val == null) { 2864 i = deflt; 2865 } 2866 else { 2867 try { 2868 i = Math.max(0, Integer.parseInt(val)); 2869 } catch (NumberFormatException x) { 2870 i = deflt; 2871 } 2872 } 2873 return i; 2874 } 2875 return deflt; 2876 } 2877 2878 2885 protected boolean validateIfNecessary() { 2886 if (!isValid() && canBeValid) { 2887 children = null; 2888 Object lock = lock(); 2889 2890 try { 2891 validate(); 2892 } finally { 2893 unlock(lock); 2894 } 2895 } 2896 return isValid(); 2897 } 2898 2899 2903 protected void invalidate(boolean first) { 2904 if (!isValid()) { 2905 if (canBeValid && !first) { 2906 canBeValid = false; 2907 } 2908 return; 2909 } 2910 isValid = false; 2911 canBeValid = first; 2912 if (children != null) { 2913 for (int counter = 0; counter < children.size(); counter++) { 2914 ((ElementInfo)children.get(counter)).invalidate(false); 2915 } 2916 children = null; 2917 } 2918 } 2919 2920 private View getView(View parent, Element e, int start) { 2921 if (parent.getElement() == e) { 2922 return parent; 2923 } 2924 int index = parent.getViewIndex(start, Position.Bias.Forward); 2925 2926 if (index != -1 && index < parent.getViewCount()) { 2927 return getView(parent.getView(index), e, start); 2928 } 2929 return null; 2930 } 2931 2932 private int getClosestInfoIndex(int index) { 2933 for (int counter = 0; counter < getChildCount(); counter++) { 2934 ElementInfo info = getChild(counter); 2935 2936 if (index < info.getElement().getEndOffset() || 2937 index == info.getElement().getStartOffset()) { 2938 return counter; 2939 } 2940 } 2941 return -1; 2942 } 2943 2944 private void update(DocumentEvent e) { 2945 if (!isValid()) { 2946 return; 2947 } 2948 ElementInfo parent = getParent(); 2949 Element element = getElement(); 2950 2951 do { 2952 DocumentEvent.ElementChange ec = e.getChange(element); 2953 if (ec != null) { 2954 if (element == getElement()) { 2955 invalidate(true); 2957 } 2958 else if (parent != null) { 2959 parent.invalidate(parent == getRootInfo()); 2960 } 2961 return; 2962 } 2963 element = element.getParentElement(); 2964 } while (parent != null && element != null && 2965 element != parent.getElement()); 2966 2967 if (getChildCount() > 0) { 2968 Element elem = getElement(); 2969 int pos = e.getOffset(); 2970 int index0 = getClosestInfoIndex(pos); 2971 if (index0 == -1 && 2972 e.getType() == DocumentEvent.EventType.REMOVE && 2973 pos >= elem.getEndOffset()) { 2974 index0 = getChildCount() - 1; 2979 } 2980 ElementInfo info = (index0 >= 0) ? getChild(index0) : null; 2981 if (info != null && 2982 (info.getElement().getStartOffset() == pos) && (pos > 0)) { 2983 index0 = Math.max(index0 - 1, 0); 2986 } 2987 int index1; 2988 if (e.getType() != DocumentEvent.EventType.REMOVE) { 2989 index1 = getClosestInfoIndex(pos + e.getLength()); 2990 if (index1 < 0) { 2991 index1 = getChildCount() - 1; 2992 } 2993 } 2994 else { 2995 index1 = index0; 2996 while ((index1 + 1) < getChildCount() && 2998 getChild(index1 + 1).getElement().getEndOffset() == 2999 getChild(index1 + 1).getElement().getStartOffset()){ 3000 index1++; 3001 } 3002 } 3003 index0 = Math.max(index0, 0); 3004 for (int i = index0; i <= index1 && isValid(); i++) { 3007 getChild(i).update(e); 3008 } 3009 } 3010 } 3011 } 3012 3013 3018 private class DocumentHandler implements DocumentListener { 3019 public void insertUpdate(DocumentEvent e) { 3020 getRootInfo().update(e); 3021 } 3022 public void removeUpdate(DocumentEvent e) { 3023 getRootInfo().update(e); 3024 } 3025 public void changedUpdate(DocumentEvent e) { 3026 getRootInfo().update(e); 3027 } 3028 } 3029 3030 3033 private class PropertyChangeHandler implements PropertyChangeListener { 3034 public void propertyChange(PropertyChangeEvent evt) { 3035 if (evt.getPropertyName().equals("document")) { 3036 setDocument(editor.getDocument()); 3038 } 3039 } 3040 } 3041} 3042 | Popular Tags |