1. /*
  2. * @(#)AccessibleHTML.java 1.11 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing.text.html;
  8. import java.awt.*;
  9. import java.awt.event.*;
  10. import java.beans.*;
  11. import java.util.*;
  12. import javax.swing.*;
  13. import javax.swing.event.*;
  14. import javax.swing.text.*;
  15. import javax.accessibility.*;
  16. import java.text.BreakIterator;
  17. /*
  18. * The AccessibleHTML class provide information about the contents
  19. * of a HTML document to assistive technologies.
  20. *
  21. * @version 1.11 01/23/03
  22. * @author Lynn Monsanto
  23. */
  24. class AccessibleHTML implements Accessible {
  25. /**
  26. * The editor.
  27. */
  28. private JEditorPane editor;
  29. /**
  30. * Current model.
  31. */
  32. private Document model;
  33. /**
  34. * DocumentListener installed on the current model.
  35. */
  36. private DocumentListener docListener;
  37. /**
  38. * PropertyChangeListener installed on the editor
  39. */
  40. private PropertyChangeListener propChangeListener;
  41. /**
  42. * The root ElementInfo for the document
  43. */
  44. private ElementInfo rootElementInfo;
  45. /*
  46. * The root accessible context for the document
  47. */
  48. private RootHTMLAccessibleContext rootHTMLAccessibleContext;
  49. public AccessibleHTML(JEditorPane pane) {
  50. editor = pane;
  51. propChangeListener = new PropertyChangeHandler();
  52. setDocument(editor.getDocument());
  53. docListener = new DocumentHandler();
  54. }
  55. /**
  56. * Sets the document.
  57. */
  58. private void setDocument(Document document) {
  59. if (model != null) {
  60. model.removeDocumentListener(docListener);
  61. }
  62. if (editor != null) {
  63. editor.removePropertyChangeListener(propChangeListener);
  64. }
  65. this.model = document;
  66. if (model != null) {
  67. if (rootElementInfo != null) {
  68. rootElementInfo.invalidate(false);
  69. }
  70. buildInfo();
  71. model.addDocumentListener(docListener);
  72. }
  73. else {
  74. rootElementInfo = null;
  75. }
  76. if (editor != null) {
  77. editor.addPropertyChangeListener(propChangeListener);
  78. }
  79. }
  80. /**
  81. * Returns the Document currently presenting information for.
  82. */
  83. private Document getDocument() {
  84. return model;
  85. }
  86. /**
  87. * Returns the JEditorPane providing information for.
  88. */
  89. private JEditorPane getTextComponent() {
  90. return editor;
  91. }
  92. /**
  93. * Returns the ElementInfo representing the root Element.
  94. */
  95. private ElementInfo getRootInfo() {
  96. return rootElementInfo;
  97. }
  98. /**
  99. * Returns the root <code>View</code> associated with the current text
  100. * component.
  101. */
  102. private View getRootView() {
  103. return getTextComponent().getUI().getRootView(getTextComponent());
  104. }
  105. /**
  106. * Returns the bounds the root View will be rendered in.
  107. */
  108. private Rectangle getRootEditorRect() {
  109. Rectangle alloc = getTextComponent().getBounds();
  110. if ((alloc.width > 0) && (alloc.height > 0)) {
  111. alloc.x = alloc.y = 0;
  112. Insets insets = editor.getInsets();
  113. alloc.x += insets.left;
  114. alloc.y += insets.top;
  115. alloc.width -= insets.left + insets.right;
  116. alloc.height -= insets.top + insets.bottom;
  117. return alloc;
  118. }
  119. return null;
  120. }
  121. /**
  122. * If possible acquires a lock on the Document. If a lock has been
  123. * obtained a key will be retured that should be passed to
  124. * <code>unlock</code>.
  125. */
  126. private Object lock() {
  127. Document document = getDocument();
  128. if (document instanceof AbstractDocument) {
  129. ((AbstractDocument)document).readLock();
  130. return document;
  131. }
  132. return null;
  133. }
  134. /**
  135. * Releases a lock previously obtained via <code>lock</code>.
  136. */
  137. private void unlock(Object key) {
  138. if (key != null) {
  139. ((AbstractDocument)key).readUnlock();
  140. }
  141. }
  142. /**
  143. * Rebuilds the information from the current info.
  144. */
  145. private void buildInfo() {
  146. Object lock = lock();
  147. try {
  148. Document doc = getDocument();
  149. Element root = doc.getDefaultRootElement();
  150. rootElementInfo = new ElementInfo(root);
  151. rootElementInfo.validate();
  152. } finally {
  153. unlock(lock);
  154. }
  155. }
  156. /*
  157. * Create an ElementInfo subclass based on the passed in Element.
  158. */
  159. ElementInfo createElementInfo(Element e, ElementInfo parent) {
  160. AttributeSet attrs = e.getAttributes();
  161. if (attrs != null) {
  162. Object name = attrs.getAttribute(StyleConstants.NameAttribute);
  163. if (name == HTML.Tag.IMG) {
  164. return new IconElementInfo(e, parent);
  165. }
  166. else if (name == HTML.Tag.CONTENT || name == HTML.Tag.CAPTION) {
  167. return new TextElementInfo(e, parent);
  168. }
  169. else if (name == HTML.Tag.TABLE) {
  170. return new TableElementInfo(e, parent);
  171. }
  172. }
  173. return null;
  174. }
  175. /**
  176. * Returns the root AccessibleContext for the document
  177. */
  178. public AccessibleContext getAccessibleContext() {
  179. if (rootHTMLAccessibleContext == null) {
  180. rootHTMLAccessibleContext =
  181. new RootHTMLAccessibleContext(rootElementInfo);
  182. }
  183. return rootHTMLAccessibleContext;
  184. }
  185. /*
  186. * The roow AccessibleContext for the document
  187. */
  188. private class RootHTMLAccessibleContext extends HTMLAccessibleContext {
  189. public RootHTMLAccessibleContext(ElementInfo elementInfo) {
  190. super(elementInfo);
  191. }
  192. /**
  193. * Gets the accessibleName property of this object. The accessibleName
  194. * property of an object is a localized String that designates the purpose
  195. * of the object. For example, the accessibleName property of a label
  196. * or button might be the text of the label or button itself. In the
  197. * case of an object that doesn't display its name, the accessibleName
  198. * should still be set. For example, in the case of a text field used
  199. * to enter the name of a city, the accessibleName for the en_US locale
  200. * could be 'city.'
  201. *
  202. * @return the localized name of the object; null if this
  203. * object does not have a name
  204. *
  205. * @see #setAccessibleName
  206. */
  207. public String getAccessibleName() {
  208. if (model != null) {
  209. return (String)model.getProperty(Document.TitleProperty);
  210. } else {
  211. return null;
  212. }
  213. }
  214. /**
  215. * Gets the accessibleDescription property of this object. If this
  216. * property isn't set, returns the content type of this
  217. * <code>JEditorPane</code> instead (e.g. "plain/text", "html/text").
  218. *
  219. * @return the localized description of the object; <code>null</code>
  220. * if this object does not have a description
  221. *
  222. * @see #setAccessibleName
  223. */
  224. public String getAccessibleDescription() {
  225. return editor.getContentType();
  226. }
  227. /**
  228. * Gets the role of this object. The role of the object is the generic
  229. * purpose or use of the class of this object. For example, the role
  230. * of a push button is AccessibleRole.PUSH_BUTTON. The roles in
  231. * AccessibleRole are provided so component developers can pick from
  232. * a set of predefined roles. This enables assistive technologies to
  233. * provide a consistent interface to various tweaked subclasses of
  234. * components (e.g., use AccessibleRole.PUSH_BUTTON for all components
  235. * that act like a push button) as well as distinguish between sublasses
  236. * that behave differently (e.g., AccessibleRole.CHECK_BOX for check boxes
  237. * and AccessibleRole.RADIO_BUTTON for radio buttons).
  238. * <p>Note that the AccessibleRole class is also extensible, so
  239. * custom component developers can define their own AccessibleRole's
  240. * if the set of predefined roles is inadequate.
  241. *
  242. * @return an instance of AccessibleRole describing the role of the object
  243. * @see AccessibleRole
  244. */
  245. public AccessibleRole getAccessibleRole() {
  246. return AccessibleRole.TEXT;
  247. }
  248. }
  249. /*
  250. * Base AccessibleContext class for HTML elements
  251. */
  252. protected abstract class HTMLAccessibleContext extends AccessibleContext
  253. implements Accessible, AccessibleComponent {
  254. protected ElementInfo elementInfo;
  255. public HTMLAccessibleContext(ElementInfo elementInfo) {
  256. this.elementInfo = elementInfo;
  257. }
  258. // begin AccessibleContext implementation ...
  259. public AccessibleContext getAccessibleContext() {
  260. return this;
  261. }
  262. /**
  263. * Gets the state set of this object.
  264. *
  265. * @return an instance of AccessibleStateSet describing the states
  266. * of the object
  267. * @see AccessibleStateSet
  268. */
  269. public AccessibleStateSet getAccessibleStateSet() {
  270. AccessibleStateSet states = new AccessibleStateSet();
  271. Component comp = getTextComponent();
  272. if (comp != null && comp.isEnabled()) {
  273. states.add(AccessibleState.ENABLED);
  274. }
  275. if (comp != null && comp.isFocusTraversable()) {
  276. states.add(AccessibleState.FOCUSABLE);
  277. }
  278. if (comp != null && comp.isVisible()) {
  279. states.add(AccessibleState.VISIBLE);
  280. }
  281. if (comp != null && comp.isShowing()) {
  282. states.add(AccessibleState.SHOWING);
  283. }
  284. return states;
  285. }
  286. /**
  287. * Gets the 0-based index of this object in its accessible parent.
  288. *
  289. * @return the 0-based index of this object in its parent; -1 if this
  290. * object does not have an accessible parent.
  291. *
  292. * @see #getAccessibleParent
  293. * @see #getAccessibleChildrenCount
  294. * @see #getAccessibleChild
  295. */
  296. public int getAccessibleIndexInParent() {
  297. return elementInfo.getIndexInParent();
  298. }
  299. /**
  300. * Returns the number of accessible children of the object.
  301. *
  302. * @return the number of accessible children of the object.
  303. */
  304. public int getAccessibleChildrenCount() {
  305. return elementInfo.getChildCount();
  306. }
  307. /**
  308. * Returns the specified Accessible child of the object. The Accessible
  309. * children of an Accessible object are zero-based, so the first child
  310. * of an Accessible child is at index 0, the second child is at index 1,
  311. * and so on.
  312. *
  313. * @param i zero-based index of child
  314. * @return the Accessible child of the object
  315. * @see #getAccessibleChildrenCount
  316. */
  317. public Accessible getAccessibleChild(int i) {
  318. ElementInfo childInfo = elementInfo.getChild(i);
  319. if (childInfo != null && childInfo instanceof Accessible) {
  320. return (Accessible)childInfo;
  321. } else {
  322. return null;
  323. }
  324. }
  325. /**
  326. * Gets the locale of the component. If the component does not have a
  327. * locale, then the locale of its parent is returned.
  328. *
  329. * @return this component's locale. If this component does not have
  330. * a locale, the locale of its parent is returned.
  331. *
  332. * @exception IllegalComponentStateException
  333. * If the Component does not have its own locale and has not yet been
  334. * added to a containment hierarchy such that the locale can be
  335. * determined from the containing parent.
  336. */
  337. public Locale getLocale() throws IllegalComponentStateException {
  338. return editor.getLocale();
  339. }
  340. // ... end AccessibleContext implementation
  341. // begin AccessibleComponent implementation ...
  342. public AccessibleComponent getAccessibleComponent() {
  343. return this;
  344. }
  345. /**
  346. * Gets the background color of this object.
  347. *
  348. * @return the background color, if supported, of the object;
  349. * otherwise, null
  350. * @see #setBackground
  351. */
  352. public Color getBackground() {
  353. return getTextComponent().getBackground();
  354. }
  355. /**
  356. * Sets the background color of this object.
  357. *
  358. * @param c the new Color for the background
  359. * @see #setBackground
  360. */
  361. public void setBackground(Color c) {
  362. getTextComponent().setBackground(c);
  363. }
  364. /**
  365. * Gets the foreground color of this object.
  366. *
  367. * @return the foreground color, if supported, of the object;
  368. * otherwise, null
  369. * @see #setForeground
  370. */
  371. public Color getForeground() {
  372. return getTextComponent().getForeground();
  373. }
  374. /**
  375. * Sets the foreground color of this object.
  376. *
  377. * @param c the new Color for the foreground
  378. * @see #getForeground
  379. */
  380. public void setForeground(Color c) {
  381. getTextComponent().setForeground(c);
  382. }
  383. /**
  384. * Gets the Cursor of this object.
  385. *
  386. * @return the Cursor, if supported, of the object; otherwise, null
  387. * @see #setCursor
  388. */
  389. public Cursor getCursor() {
  390. return getTextComponent().getCursor();
  391. }
  392. /**
  393. * Sets the Cursor of this object.
  394. *
  395. * @param c the new Cursor for the object
  396. * @see #getCursor
  397. */
  398. public void setCursor(Cursor cursor) {
  399. getTextComponent().setCursor(cursor);
  400. }
  401. /**
  402. * Gets the Font of this object.
  403. *
  404. * @return the Font,if supported, for the object; otherwise, null
  405. * @see #setFont
  406. */
  407. public Font getFont() {
  408. return getTextComponent().getFont();
  409. }
  410. /**
  411. * Sets the Font of this object.
  412. *
  413. * @param f the new Font for the object
  414. * @see #getFont
  415. */
  416. public void setFont(Font f) {
  417. getTextComponent().setFont(f);
  418. }
  419. /**
  420. * Gets the FontMetrics of this object.
  421. *
  422. * @param f the Font
  423. * @return the FontMetrics, if supported, the object; otherwise, null
  424. * @see #getFont
  425. */
  426. public FontMetrics getFontMetrics(Font f) {
  427. return getTextComponent().getFontMetrics(f);
  428. }
  429. /**
  430. * Determines if the object is enabled. Objects that are enabled
  431. * will also have the AccessibleState.ENABLED state set in their
  432. * AccessibleStateSets.
  433. *
  434. * @return true if object is enabled; otherwise, false
  435. * @see #setEnabled
  436. * @see AccessibleContext#getAccessibleStateSet
  437. * @see AccessibleState#ENABLED
  438. * @see AccessibleStateSet
  439. */
  440. public boolean isEnabled() {
  441. return getTextComponent().isEnabled();
  442. }
  443. /**
  444. * Sets the enabled state of the object.
  445. *
  446. * @param b if true, enables this object; otherwise, disables it
  447. * @see #isEnabled
  448. */
  449. public void setEnabled(boolean b) {
  450. getTextComponent().setEnabled(b);
  451. }
  452. /**
  453. * Determines if the object is visible. Note: this means that the
  454. * object intends to be visible; however, it may not be
  455. * showing on the screen because one of the objects that this object
  456. * is contained by is currently not visible. To determine if an object
  457. * is showing on the screen, use isShowing().
  458. * <p>Objects that are visible will also have the
  459. * AccessibleState.VISIBLE state set in their AccessibleStateSets.
  460. *
  461. * @return true if object is visible; otherwise, false
  462. * @see #setVisible
  463. * @see AccessibleContext#getAccessibleStateSet
  464. * @see AccessibleState#VISIBLE
  465. * @see AccessibleStateSet
  466. */
  467. public boolean isVisible() {
  468. return getTextComponent().isVisible();
  469. }
  470. /**
  471. * Sets the visible state of the object.
  472. *
  473. * @param b if true, shows this object; otherwise, hides it
  474. * @see #isVisible
  475. */
  476. public void setVisible(boolean b) {
  477. getTextComponent().setVisible(b);
  478. }
  479. /**
  480. * Determines if the object is showing. This is determined by checking
  481. * the visibility of the object and its ancestors.
  482. * Note: this
  483. * will return true even if the object is obscured by another (for
  484. * example, it is underneath a menu that was pulled down).
  485. *
  486. * @return true if object is showing; otherwise, false
  487. */
  488. public boolean isShowing() {
  489. return getTextComponent().isShowing();
  490. }
  491. /**
  492. * Checks whether the specified point is within this object's bounds,
  493. * where the point's x and y coordinates are defined to be relative
  494. * to the coordinate system of the object.
  495. *
  496. * @param p the Point relative to the coordinate system of the object
  497. * @return true if object contains Point; otherwise false
  498. * @see #getBounds
  499. */
  500. public boolean contains(Point p) {
  501. Rectangle r = getBounds();
  502. if (r != null) {
  503. return r.contains(p.x, p.y);
  504. } else {
  505. return false;
  506. }
  507. }
  508. /**
  509. * Returns the location of the object on the screen.
  510. *
  511. * @return the location of the object on screen; null if this object
  512. * is not on the screen
  513. * @see #getBounds
  514. * @see #getLocation
  515. */
  516. public Point getLocationOnScreen() {
  517. Point editorLocation = getTextComponent().getLocationOnScreen();
  518. Rectangle r = getBounds();
  519. if (r != null) {
  520. return new Point(editorLocation.x + r.x,
  521. editorLocation.y + r.y);
  522. } else {
  523. return null;
  524. }
  525. }
  526. /**
  527. * Gets the location of the object relative to the parent in the form
  528. * of a point specifying the object's top-left corner in the screen's
  529. * coordinate space.
  530. *
  531. * @return An instance of Point representing the top-left corner of the
  532. * object's bounds in the coordinate space of the screen; null if
  533. * this object or its parent are not on the screen
  534. * @see #getBounds
  535. * @see #getLocationOnScreen
  536. */
  537. public Point getLocation() {
  538. Rectangle r = getBounds();
  539. if (r != null) {
  540. return new Point(r.x, r.y);
  541. } else {
  542. return null;
  543. }
  544. }
  545. /**
  546. * Sets the location of the object relative to the parent.
  547. * @param p the new position for the top-left corner
  548. * @see #getLocation
  549. */
  550. public void setLocation(Point p) {
  551. }
  552. /**
  553. * Gets the bounds of this object in the form of a Rectangle object.
  554. * The bounds specify this object's width, height, and location
  555. * relative to its parent.
  556. *
  557. * @return A rectangle indicating this component's bounds; null if
  558. * this object is not on the screen.
  559. * @see #contains
  560. */
  561. public Rectangle getBounds() {
  562. return elementInfo.getBounds();
  563. }
  564. /**
  565. * Sets the bounds of this object in the form of a Rectangle object.
  566. * The bounds specify this object's width, height, and location
  567. * relative to its parent.
  568. *
  569. * @param r rectangle indicating this component's bounds
  570. * @see #getBounds
  571. */
  572. public void setBounds(Rectangle r) {
  573. getTextComponent().setBounds(r);
  574. }
  575. /**
  576. * Returns the size of this object in the form of a Dimension object.
  577. * The height field of the Dimension object contains this object's
  578. * height, and the width field of the Dimension object contains this
  579. * object's width.
  580. *
  581. * @return A Dimension object that indicates the size of this component;
  582. * null if this object is not on the screen
  583. * @see #setSize
  584. */
  585. public Dimension getSize() {
  586. Rectangle r = getBounds();
  587. if (r != null) {
  588. return new Dimension(r.width, r.height);
  589. } else {
  590. return null;
  591. }
  592. }
  593. /**
  594. * Resizes this object so that it has width and height.
  595. *
  596. * @param d The dimension specifying the new size of the object.
  597. * @see #getSize
  598. */
  599. public void setSize(Dimension d) {
  600. getTextComponent().setSize(d);
  601. }
  602. /**
  603. * Returns the Accessible child, if one exists, contained at the local
  604. * coordinate Point.
  605. *
  606. * @param p The point relative to the coordinate system of this object.
  607. * @return the Accessible, if it exists, at the specified location;
  608. * otherwise null
  609. */
  610. public Accessible getAccessibleAt(Point p) {
  611. ElementInfo innerMostElement = getElementInfoAt(rootElementInfo, p);
  612. if (innerMostElement instanceof Accessible) {
  613. return (Accessible)innerMostElement;
  614. } else {
  615. return null;
  616. }
  617. }
  618. private ElementInfo getElementInfoAt(ElementInfo elementInfo, Point p) {
  619. if (elementInfo.getBounds() == null) {
  620. return null;
  621. }
  622. if (elementInfo.getChildCount() == 0 &&
  623. elementInfo.getBounds().contains(p)) {
  624. return elementInfo;
  625. } else {
  626. if (elementInfo instanceof TableElementInfo) {
  627. // Handle table caption as a special case since it's the
  628. // only table child that is not a table row.
  629. ElementInfo captionInfo =
  630. ((TableElementInfo)elementInfo).getCaptionInfo();
  631. if (captionInfo != null) {
  632. Rectangle bounds = captionInfo.getBounds();
  633. if (bounds != null && bounds.contains(p)) {
  634. return captionInfo;
  635. }
  636. }
  637. }
  638. for (int i = 0; i < elementInfo.getChildCount(); i++)
  639. {
  640. ElementInfo childInfo = elementInfo.getChild(i);
  641. ElementInfo retValue = getElementInfoAt(childInfo, p);
  642. if (retValue != null) {
  643. return retValue;
  644. }
  645. }
  646. }
  647. return null;
  648. }
  649. /**
  650. * Returns whether this object can accept focus or not. Objects that
  651. * can accept focus will also have the AccessibleState.FOCUSABLE state
  652. * set in their AccessibleStateSets.
  653. *
  654. * @return true if object can accept focus; otherwise false
  655. * @see AccessibleContext#getAccessibleStateSet
  656. * @see AccessibleState#FOCUSABLE
  657. * @see AccessibleState#FOCUSED
  658. * @see AccessibleStateSet
  659. */
  660. public boolean isFocusTraversable() {
  661. return getTextComponent().isFocusTraversable();
  662. }
  663. /**
  664. * Requests focus for this object. If this object cannot accept focus,
  665. * nothing will happen. Otherwise, the object will attempt to take
  666. * focus.
  667. * @see #isFocusTraversable
  668. */
  669. public void requestFocus() {
  670. getTextComponent().requestFocus();
  671. }
  672. /**
  673. * Adds the specified focus listener to receive focus events from this
  674. * component.
  675. *
  676. * @param l the focus listener
  677. * @see #removeFocusListener
  678. */
  679. public void addFocusListener(FocusListener l) {
  680. getTextComponent().addFocusListener(l);
  681. }
  682. /**
  683. * Removes the specified focus listener so it no longer receives focus
  684. * events from this component.
  685. *
  686. * @param l the focus listener
  687. * @see #addFocusListener
  688. */
  689. public void removeFocusListener(FocusListener l) {
  690. getTextComponent().removeFocusListener(l);
  691. }
  692. // ... end AccessibleComponent implementation
  693. } // ... end HTMLAccessibleContext
  694. /*
  695. * ElementInfo for text
  696. */
  697. class TextElementInfo extends ElementInfo implements Accessible {
  698. TextElementInfo(Element element, ElementInfo parent) {
  699. super(element, parent);
  700. }
  701. // begin AccessibleText implementation ...
  702. private AccessibleContext accessibleContext;
  703. public AccessibleContext getAccessibleContext() {
  704. if (accessibleContext == null) {
  705. accessibleContext = new TextAccessibleContext(this);
  706. }
  707. return accessibleContext;
  708. }
  709. /*
  710. * AccessibleContext for text elements
  711. */
  712. public class TextAccessibleContext extends HTMLAccessibleContext
  713. implements AccessibleText {
  714. public TextAccessibleContext(ElementInfo elementInfo) {
  715. super(elementInfo);
  716. }
  717. public AccessibleText getAccessibleText() {
  718. return this;
  719. }
  720. /**
  721. * Gets the accessibleName property of this object. The accessibleName
  722. * property of an object is a localized String that designates the purpose
  723. * of the object. For example, the accessibleName property of a label
  724. * or button might be the text of the label or button itself. In the
  725. * case of an object that doesn't display its name, the accessibleName
  726. * should still be set. For example, in the case of a text field used
  727. * to enter the name of a city, the accessibleName for the en_US locale
  728. * could be 'city.'
  729. *
  730. * @return the localized name of the object; null if this
  731. * object does not have a name
  732. *
  733. * @see #setAccessibleName
  734. */
  735. public String getAccessibleName() {
  736. if (model != null) {
  737. return (String)model.getProperty(Document.TitleProperty);
  738. } else {
  739. return null;
  740. }
  741. }
  742. /**
  743. * Gets the accessibleDescription property of this object. If this
  744. * property isn't set, returns the content type of this
  745. * <code>JEditorPane</code> instead (e.g. "plain/text", "html/text").
  746. *
  747. * @return the localized description of the object; <code>null</code>
  748. * if this object does not have a description
  749. *
  750. * @see #setAccessibleName
  751. */
  752. public String getAccessibleDescription() {
  753. return editor.getContentType();
  754. }
  755. /**
  756. * Gets the role of this object. The role of the object is the generic
  757. * purpose or use of the class of this object. For example, the role
  758. * of a push button is AccessibleRole.PUSH_BUTTON. The roles in
  759. * AccessibleRole are provided so component developers can pick from
  760. * a set of predefined roles. This enables assistive technologies to
  761. * provide a consistent interface to various tweaked subclasses of
  762. * components (e.g., use AccessibleRole.PUSH_BUTTON for all components
  763. * that act like a push button) as well as distinguish between sublasses
  764. * that behave differently (e.g., AccessibleRole.CHECK_BOX for check boxes
  765. * and AccessibleRole.RADIO_BUTTON for radio buttons).
  766. * <p>Note that the AccessibleRole class is also extensible, so
  767. * custom component developers can define their own AccessibleRole's
  768. * if the set of predefined roles is inadequate.
  769. *
  770. * @return an instance of AccessibleRole describing the role of the object
  771. * @see AccessibleRole
  772. */
  773. public AccessibleRole getAccessibleRole() {
  774. return AccessibleRole.TEXT;
  775. }
  776. /**
  777. * Given a point in local coordinates, return the zero-based index
  778. * of the character under that Point. If the point is invalid,
  779. * this method returns -1.
  780. *
  781. * @param p the Point in local coordinates
  782. * @return the zero-based index of the character under Point p; if
  783. * Point is invalid returns -1.
  784. */
  785. public int getIndexAtPoint(Point p) {
  786. View v = getView();
  787. if (v != null) {
  788. return v.viewToModel(p.x, p.y, getBounds());
  789. } else {
  790. return -1;
  791. }
  792. }
  793. /**
  794. * Determine the bounding box of the character at the given
  795. * index into the string. The bounds are returned in local
  796. * coordinates. If the index is invalid an empty rectangle is
  797. * returned.
  798. *
  799. * @param i the index into the String
  800. * @return the screen coordinates of the character's the bounding box,
  801. * if index is invalid returns an empty rectangle.
  802. */
  803. public Rectangle getCharacterBounds(int i) {
  804. try {
  805. return editor.getUI().modelToView(editor, i);
  806. } catch (BadLocationException e) {
  807. return null;
  808. }
  809. }
  810. /**
  811. * Return the number of characters (valid indicies)
  812. *
  813. * @return the number of characters
  814. */
  815. public int getCharCount() {
  816. if (validateIfNecessary()) {
  817. Element elem = elementInfo.getElement();
  818. return elem.getEndOffset() - elem.getStartOffset();
  819. }
  820. return 0;
  821. }
  822. /**
  823. * Return the zero-based offset of the caret.
  824. *
  825. * Note: That to the right of the caret will have the same index
  826. * value as the offset (the caret is between two characters).
  827. * @return the zero-based offset of the caret.
  828. */
  829. public int getCaretPosition() {
  830. View v = getView();
  831. if (v == null) {
  832. return -1;
  833. }
  834. Container c = v.getContainer();
  835. if (c == null) {
  836. return -1;
  837. }
  838. if (c instanceof JTextComponent) {
  839. return ((JTextComponent)c).getCaretPosition();
  840. } else {
  841. return -1;
  842. }
  843. }
  844. /**
  845. * IndexedSegment extends Segment adding the offset into the
  846. * the model the <code>Segment</code> was asked for.
  847. */
  848. private class IndexedSegment extends Segment {
  849. /**
  850. * Offset into the model that the position represents.
  851. */
  852. public int modelOffset;
  853. }
  854. public String getAtIndex(int part, int index) {
  855. return getAtIndex(part, index, 0);
  856. }
  857. public String getAfterIndex(int part, int index) {
  858. return getAtIndex(part, index, 1);
  859. }
  860. public String getBeforeIndex(int part, int index) {
  861. return getAtIndex(part, index, -1);
  862. }
  863. /**
  864. * Gets the word, sentence, or character at <code>index</code>.
  865. * If <code>direction</code> is non-null this will find the
  866. * next/previous word/sentence/character.
  867. */
  868. private String getAtIndex(int part, int index, int direction) {
  869. if (model instanceof AbstractDocument) {
  870. ((AbstractDocument)model).readLock();
  871. }
  872. try {
  873. if (index < 0 || index >= model.getLength()) {
  874. return null;
  875. }
  876. switch (part) {
  877. case AccessibleText.CHARACTER:
  878. if (index + direction < model.getLength() &&
  879. index + direction >= 0) {
  880. return model.getText(index + direction, 1);
  881. }
  882. break;
  883. case AccessibleText.WORD:
  884. case AccessibleText.SENTENCE:
  885. IndexedSegment seg = getSegmentAt(part, index);
  886. if (seg != null) {
  887. if (direction != 0) {
  888. int next;
  889. if (direction < 0) {
  890. next = seg.modelOffset - 1;
  891. }
  892. else {
  893. next = seg.modelOffset + direction * seg.count;
  894. }
  895. if (next >= 0 && next <= model.getLength()) {
  896. seg = getSegmentAt(part, next);
  897. }
  898. else {
  899. seg = null;
  900. }
  901. }
  902. if (seg != null) {
  903. return new String(seg.array, seg.offset,
  904. seg.count);
  905. }
  906. }
  907. break;
  908. default:
  909. break;
  910. }
  911. } catch (BadLocationException e) {
  912. } finally {
  913. if (model instanceof AbstractDocument) {
  914. ((AbstractDocument)model).readUnlock();
  915. }
  916. }
  917. return null;
  918. }
  919. /*
  920. * Returns the paragraph element for the specified index.
  921. */
  922. private Element getParagraphElement(int index) {
  923. if (model instanceof PlainDocument ) {
  924. PlainDocument sdoc = (PlainDocument)model;
  925. return sdoc.getParagraphElement(index);
  926. } else if (model instanceof StyledDocument) {
  927. StyledDocument sdoc = (StyledDocument)model;
  928. return sdoc.getParagraphElement(index);
  929. } else {
  930. Element para = null;
  931. for (para = model.getDefaultRootElement(); ! para.isLeaf(); ) {
  932. int pos = para.getElementIndex(index);
  933. para = para.getElement(pos);
  934. }
  935. if (para == null) {
  936. return null;
  937. }
  938. return para.getParentElement();
  939. }
  940. }
  941. /*
  942. * Returns a <code>Segment</code> containing the paragraph text
  943. * at <code>index</code>, or null if <code>index</code> isn't
  944. * valid.
  945. */
  946. private IndexedSegment getParagraphElementText(int index)
  947. throws BadLocationException {
  948. Element para = getParagraphElement(index);
  949. if (para != null) {
  950. IndexedSegment segment = new IndexedSegment();
  951. try {
  952. int length = para.getEndOffset() - para.getStartOffset();
  953. model.getText(para.getStartOffset(), length, segment);
  954. } catch (BadLocationException e) {
  955. return null;
  956. }
  957. segment.modelOffset = para.getStartOffset();
  958. return segment;
  959. }
  960. return null;
  961. }
  962. /**
  963. * Returns the Segment at <code>index</code> representing either
  964. * the paragraph or sentence as identified by <code>part</code>, or
  965. * null if a valid paragraph/sentence can't be found. The offset
  966. * will point to the start of the word/sentence in the array, and
  967. * the modelOffset will point to the location of the word/sentence
  968. * in the model.
  969. */
  970. private IndexedSegment getSegmentAt(int part, int index)
  971. throws BadLocationException {
  972. IndexedSegment seg = getParagraphElementText(index);
  973. if (seg == null) {
  974. return null;
  975. }
  976. BreakIterator iterator;
  977. switch (part) {
  978. case AccessibleText.WORD:
  979. iterator = BreakIterator.getWordInstance(getLocale());
  980. break;
  981. case AccessibleText.SENTENCE:
  982. iterator = BreakIterator.getSentenceInstance(getLocale());
  983. break;
  984. default:
  985. return null;
  986. }
  987. seg.first();
  988. iterator.setText(seg);
  989. int end = iterator.following(index - seg.modelOffset + seg.offset);
  990. if (end == BreakIterator.DONE) {
  991. return null;
  992. }
  993. if (end > seg.offset + seg.count) {
  994. return null;
  995. }
  996. int begin = iterator.previous();
  997. if (begin == BreakIterator.DONE ||
  998. begin >= seg.offset + seg.count) {
  999. return null;
  1000. }
  1001. seg.modelOffset = seg.modelOffset + begin - seg.offset;
  1002. seg.offset = begin;
  1003. seg.count = end - begin;
  1004. return seg;
  1005. }
  1006. /**
  1007. * Return the AttributeSet for a given character at a given index
  1008. *
  1009. * @param i the zero-based index into the text
  1010. * @return the AttributeSet of the character
  1011. */
  1012. public AttributeSet getCharacterAttribute(int i) {
  1013. if (model instanceof StyledDocument) {
  1014. StyledDocument doc = (StyledDocument)model;
  1015. Element elem = doc.getCharacterElement(i);
  1016. if (elem != null) {
  1017. return elem.getAttributes();
  1018. }
  1019. }
  1020. return null;
  1021. }
  1022. /**
  1023. * Returns the start offset within the selected text.
  1024. * If there is no selection, but there is
  1025. * a caret, the start and end offsets will be the same.
  1026. *
  1027. * @return the index into the text of the start of the selection
  1028. */
  1029. public int getSelectionStart() {
  1030. return editor.getSelectionStart();
  1031. }
  1032. /**
  1033. * Returns the end offset within the selected text.
  1034. * If there is no selection, but there is
  1035. * a caret, the start and end offsets will be the same.
  1036. *
  1037. * @return the index into teh text of the end of the selection
  1038. */
  1039. public int getSelectionEnd() {
  1040. return editor.getSelectionEnd();
  1041. }
  1042. /**
  1043. * Returns the portion of the text that is selected.
  1044. *
  1045. * @return the String portion of the text that is selected
  1046. */
  1047. public String getSelectedText() {
  1048. return editor.getSelectedText();
  1049. }
  1050. /*
  1051. * Returns the text substring starting at the specified
  1052. * offset with the specified length.
  1053. */
  1054. private String getText(int offset, int length)
  1055. throws BadLocationException {
  1056. if (model != null && model instanceof StyledDocument) {
  1057. StyledDocument doc = (StyledDocument)model;
  1058. return model.getText(offset, length);
  1059. } else {
  1060. return null;
  1061. }
  1062. }
  1063. }
  1064. }
  1065. /*
  1066. * ElementInfo for images
  1067. */
  1068. private class IconElementInfo extends ElementInfo implements Accessible {
  1069. private int width = -1;
  1070. private int height = -1;
  1071. IconElementInfo(Element element, ElementInfo parent) {
  1072. super(element, parent);
  1073. }
  1074. protected void invalidate(boolean first) {
  1075. super.invalidate(first);
  1076. width = height = -1;
  1077. }
  1078. private int getImageSize(Object key) {
  1079. if (validateIfNecessary()) {
  1080. int size = getIntAttr(getAttributes(), key, -1);
  1081. if (size == -1) {
  1082. View v = getView();
  1083. size = 0;
  1084. if (v instanceof ImageView) {
  1085. Image img = ((ImageView)v).getImage();
  1086. if (img != null) {
  1087. if (key == HTML.Attribute.WIDTH) {
  1088. size = img.getWidth(null);
  1089. }
  1090. else {
  1091. size = img.getHeight(null);
  1092. }
  1093. }
  1094. }
  1095. }
  1096. return size;
  1097. }
  1098. return 0;
  1099. }
  1100. // begin AccessibleIcon implementation ...
  1101. private AccessibleContext accessibleContext;
  1102. public AccessibleContext getAccessibleContext() {
  1103. if (accessibleContext == null) {
  1104. accessibleContext = new IconAccessibleContext(this);
  1105. }
  1106. return accessibleContext;
  1107. }
  1108. /*
  1109. * AccessibleContext for images
  1110. */
  1111. protected class IconAccessibleContext extends HTMLAccessibleContext
  1112. implements AccessibleIcon {
  1113. public IconAccessibleContext(ElementInfo elementInfo) {
  1114. super(elementInfo);
  1115. }
  1116. /**
  1117. * Gets the accessibleName property of this object. The accessibleName
  1118. * property of an object is a localized String that designates the purpose
  1119. * of the object. For example, the accessibleName property of a label
  1120. * or button might be the text of the label or button itself. In the
  1121. * case of an object that doesn't display its name, the accessibleName
  1122. * should still be set. For example, in the case of a text field used
  1123. * to enter the name of a city, the accessibleName for the en_US locale
  1124. * could be 'city.'
  1125. *
  1126. * @return the localized name of the object; null if this
  1127. * object does not have a name
  1128. *
  1129. * @see #setAccessibleName
  1130. */
  1131. public String getAccessibleName() {
  1132. if (model != null) {
  1133. return (String)model.getProperty(Document.TitleProperty);
  1134. } else {
  1135. return null;
  1136. }
  1137. }
  1138. /**
  1139. * Gets the accessibleDescription property of this object. If this
  1140. * property isn't set, returns the content type of this
  1141. * <code>JEditorPane</code> instead (e.g. "plain/text", "html/text").
  1142. *
  1143. * @return the localized description of the object; <code>null</code>
  1144. * if this object does not have a description
  1145. *
  1146. * @see #setAccessibleName
  1147. */
  1148. public String getAccessibleDescription() {
  1149. return editor.getContentType();
  1150. }
  1151. /**
  1152. * Gets the role of this object. The role of the object is the generic
  1153. * purpose or use of the class of this object. For example, the role
  1154. * of a push button is AccessibleRole.PUSH_BUTTON. The roles in
  1155. * AccessibleRole are provided so component developers can pick from
  1156. * a set of predefined roles. This enables assistive technologies to
  1157. * provide a consistent interface to various tweaked subclasses of
  1158. * components (e.g., use AccessibleRole.PUSH_BUTTON for all components
  1159. * that act like a push button) as well as distinguish between sublasses
  1160. * that behave differently (e.g., AccessibleRole.CHECK_BOX for check boxes
  1161. * and AccessibleRole.RADIO_BUTTON for radio buttons).
  1162. * <p>Note that the AccessibleRole class is also extensible, so
  1163. * custom component developers can define their own AccessibleRole's
  1164. * if the set of predefined roles is inadequate.
  1165. *
  1166. * @return an instance of AccessibleRole describing the role of the object
  1167. * @see AccessibleRole
  1168. */
  1169. public AccessibleRole getAccessibleRole() {
  1170. return AccessibleRole.ICON;
  1171. }
  1172. public AccessibleIcon [] getAccessibleIcon() {
  1173. AccessibleIcon [] icons = new AccessibleIcon[1];
  1174. icons[0] = this;
  1175. return icons;
  1176. }
  1177. /**
  1178. * Gets the description of the icon. This is meant to be a brief
  1179. * textual description of the object. For example, it might be
  1180. * presented to a blind user to give an indication of the purpose
  1181. * of the icon.
  1182. *
  1183. * @return the description of the icon
  1184. */
  1185. public String getAccessibleIconDescription() {
  1186. return ((ImageView)getView()).getAltText();
  1187. }
  1188. /**
  1189. * Sets the description of the icon. This is meant to be a brief
  1190. * textual description of the object. For example, it might be
  1191. * presented to a blind user to give an indication of the purpose
  1192. * of the icon.
  1193. *
  1194. * @param description the description of the icon
  1195. */
  1196. public void setAccessibleIconDescription(String description) {
  1197. }
  1198. /**
  1199. * Gets the width of the icon
  1200. *
  1201. * @return the width of the icon.
  1202. */
  1203. public int getAccessibleIconWidth() {
  1204. if (width == -1) {
  1205. width = getImageSize(HTML.Attribute.WIDTH);
  1206. }
  1207. return width;
  1208. }
  1209. /**
  1210. * Gets the height of the icon
  1211. *
  1212. * @return the height of the icon.
  1213. */
  1214. public int getAccessibleIconHeight() {
  1215. if (height == -1) {
  1216. height = getImageSize(HTML.Attribute.HEIGHT);
  1217. }
  1218. return height;
  1219. }
  1220. }
  1221. // ... end AccessibleIconImplementation
  1222. }
  1223. /**
  1224. * TableElementInfo encapsulates information about a HTML.Tag.TABLE.
  1225. * To make access fast it crates a grid containing the children to
  1226. * allow for access by row, column. TableElementInfo will contain
  1227. * TableRowElementInfos, which will contain TableCellElementInfos.
  1228. * Any time one of the rows or columns becomes invalid the table is
  1229. * invalidated. This is because any time one of the child attributes
  1230. * changes the size of the grid may have changed.
  1231. */
  1232. private class TableElementInfo extends ElementInfo
  1233. implements Accessible {
  1234. protected ElementInfo caption;
  1235. /**
  1236. * Allocation of the table by row x column. There may be holes (eg
  1237. * nulls) depending upon the html, any cell that has a rowspan/colspan
  1238. * > 1 will be contained multiple times in the grid.
  1239. */
  1240. private TableCellElementInfo[][] grid;
  1241. TableElementInfo(Element e, ElementInfo parent) {
  1242. super(e, parent);
  1243. }
  1244. public ElementInfo getCaptionInfo() {
  1245. return caption;
  1246. }
  1247. /**
  1248. * Overriden to update the grid when validating.
  1249. */
  1250. protected void validate() {
  1251. super.validate();
  1252. updateGrid();
  1253. }
  1254. /**
  1255. * Overriden to only alloc instances of TableRowElementInfos.
  1256. */
  1257. protected void loadChildren(Element e) {
  1258. for (int counter = 0; counter < e.getElementCount(); counter++) {
  1259. Element child = e.getElement(counter);
  1260. AttributeSet attrs = child.getAttributes();
  1261. if (attrs.getAttribute(StyleConstants.NameAttribute) ==
  1262. HTML.Tag.TR) {
  1263. addChild(new TableRowElementInfo(child, this, counter));
  1264. } else if (attrs.getAttribute(StyleConstants.NameAttribute) ==
  1265. HTML.Tag.CAPTION) {
  1266. // Handle captions as a special case since all other
  1267. // children are table rows.
  1268. caption = createElementInfo(child, this);
  1269. }
  1270. }
  1271. }
  1272. /**
  1273. * Updates the grid.
  1274. */
  1275. private void updateGrid() {
  1276. // Determine the max row/col count.
  1277. int delta = 0;
  1278. int maxCols = 0;
  1279. int rows = 0;
  1280. for (int counter = 0; counter < getChildCount(); counter++) {
  1281. TableRowElementInfo row = getRow(counter);
  1282. int prev = 0;
  1283. for (int y = 0; y < delta; y++) {
  1284. prev = Math.max(prev, getRow(counter - y - 1).
  1285. getColumnCount(y + 2));
  1286. }
  1287. delta = Math.max(row.getRowCount(), delta);
  1288. delta--;
  1289. maxCols = Math.max(maxCols, row.getColumnCount() + prev);
  1290. }
  1291. rows = getChildCount() + delta;
  1292. // Alloc
  1293. grid = new TableCellElementInfo[rows][];
  1294. for (int counter = 0; counter < rows; counter++) {
  1295. grid[counter] = new TableCellElementInfo[maxCols];
  1296. }
  1297. // Update
  1298. for (int counter = 0; counter < rows; counter++) {
  1299. getRow(counter).updateGrid(counter);
  1300. }
  1301. }
  1302. /**
  1303. * Returns the TableCellElementInfo at the specified index.
  1304. */
  1305. public TableRowElementInfo getRow(int index) {
  1306. return (TableRowElementInfo)getChild(index);
  1307. }
  1308. /**
  1309. * Returns the TableCellElementInfo by row and column.
  1310. */
  1311. public TableCellElementInfo getCell(int r, int c) {
  1312. if (validateIfNecessary() && r < grid.length &&
  1313. c < grid[0].length) {
  1314. return grid[r][c];
  1315. }
  1316. return null;
  1317. }
  1318. /**
  1319. * Returns the rowspan of the specified entry.
  1320. */
  1321. public int getRowExtentAt(int r, int c) {
  1322. TableCellElementInfo cell = getCell(r, c);
  1323. if (cell != null) {
  1324. int rows = cell.getRowCount();
  1325. int delta = 1;
  1326. while ((r - delta) >= 0 && grid[r - delta][c] == cell) {
  1327. delta++;
  1328. }
  1329. return rows - delta + 1;
  1330. }
  1331. return 0;
  1332. }
  1333. /**
  1334. * Returns the colspan of the specified entry.
  1335. */
  1336. public int getColumnExtentAt(int r, int c) {
  1337. TableCellElementInfo cell = getCell(r, c);
  1338. if (cell != null) {
  1339. int cols = cell.getColumnCount();
  1340. int delta = 1;
  1341. while ((c - delta) >= 0 && grid[r][c - delta] == cell) {
  1342. delta++;
  1343. }
  1344. return cols - delta + 1;
  1345. }
  1346. return 0;
  1347. }
  1348. /**
  1349. * Returns the number of rows in the table.
  1350. */
  1351. public int getRowCount() {
  1352. if (validateIfNecessary()) {
  1353. return grid.length;
  1354. }
  1355. return 0;
  1356. }
  1357. /**
  1358. * Returns the number of columns in the table.
  1359. */
  1360. public int getColumnCount() {
  1361. if (validateIfNecessary() && grid.length > 0) {
  1362. return grid[0].length;
  1363. }
  1364. return 0;
  1365. }
  1366. // begin AccessibleTable implementation ...
  1367. private AccessibleContext accessibleContext;
  1368. public AccessibleContext getAccessibleContext() {
  1369. if (accessibleContext == null) {
  1370. accessibleContext = new TableAccessibleContext(this);
  1371. }
  1372. return accessibleContext;
  1373. }
  1374. /*
  1375. * AccessibleContext for tables
  1376. */
  1377. public class TableAccessibleContext extends HTMLAccessibleContext
  1378. implements AccessibleTable {
  1379. private AccessibleHeadersTable rowHeadersTable;
  1380. public TableAccessibleContext(ElementInfo elementInfo) {
  1381. super(elementInfo);
  1382. }
  1383. /**
  1384. * Gets the accessibleName property of this object. The accessibleName
  1385. * property of an object is a localized String that designates the purpose
  1386. * of the object. For example, the accessibleName property of a label
  1387. * or button might be the text of the label or button itself. In the
  1388. * case of an object that doesn't display its name, the accessibleName
  1389. * should still be set. For example, in the case of a text field used
  1390. * to enter the name of a city, the accessibleName for the en_US locale
  1391. * could be 'city.'
  1392. *
  1393. * @return the localized name of the object; null if this
  1394. * object does not have a name
  1395. *
  1396. * @see #setAccessibleName
  1397. */
  1398. public String getAccessibleName() {
  1399. // return the role of the object
  1400. return getAccessibleRole().toString();
  1401. }
  1402. /**
  1403. * Gets the accessibleDescription property of this object. If this
  1404. * property isn't set, returns the content type of this
  1405. * <code>JEditorPane</code> instead (e.g. "plain/text", "html/text").
  1406. *
  1407. * @return the localized description of the object; <code>null</code>
  1408. * if this object does not have a description
  1409. *
  1410. * @see #setAccessibleName
  1411. */
  1412. public String getAccessibleDescription() {
  1413. return editor.getContentType();
  1414. }
  1415. /**
  1416. * Gets the role of this object. The role of the object is the generic
  1417. * purpose or use of the class of this object. For example, the role
  1418. * of a push button is AccessibleRole.PUSH_BUTTON. The roles in
  1419. * AccessibleRole are provided so component developers can pick from
  1420. * a set of predefined roles. This enables assistive technologies to
  1421. * provide a consistent interface to various tweaked subclasses of
  1422. * components (e.g., use AccessibleRole.PUSH_BUTTON for all components
  1423. * that act like a push button) as well as distinguish between sublasses
  1424. * that behave differently (e.g., AccessibleRole.CHECK_BOX for check boxes
  1425. * and AccessibleRole.RADIO_BUTTON for radio buttons).
  1426. * <p>Note that the AccessibleRole class is also extensible, so
  1427. * custom component developers can define their own AccessibleRole's
  1428. * if the set of predefined roles is inadequate.
  1429. *
  1430. * @return an instance of AccessibleRole describing the role of the object
  1431. * @see AccessibleRole
  1432. */
  1433. public AccessibleRole getAccessibleRole() {
  1434. return AccessibleRole.TABLE;
  1435. }
  1436. /**
  1437. * Gets the 0-based index of this object in its accessible parent.
  1438. *
  1439. * @return the 0-based index of this object in its parent; -1 if this
  1440. * object does not have an accessible parent.
  1441. *
  1442. * @see #getAccessibleParent
  1443. * @see #getAccessibleChildrenCount
  1444. * @gsee #getAccessibleChild
  1445. */
  1446. public int getAccessibleIndexInParent() {
  1447. return elementInfo.getIndexInParent();
  1448. }
  1449. /**
  1450. * Returns the number of accessible children of the object.
  1451. *
  1452. * @return the number of accessible children of the object.
  1453. */
  1454. public int getAccessibleChildrenCount() {
  1455. return ((TableElementInfo)elementInfo).getRowCount() *
  1456. ((TableElementInfo)elementInfo).getColumnCount();
  1457. }
  1458. /**
  1459. * Returns the specified Accessible child of the object. The Accessible
  1460. * children of an Accessible object are zero-based, so the first child
  1461. * of an Accessible child is at index 0, the second child is at index 1,
  1462. * and so on.
  1463. *
  1464. * @param i zero-based index of child
  1465. * @return the Accessible child of the object
  1466. * @see #getAccessibleChildrenCount
  1467. */
  1468. public Accessible getAccessibleChild(int i) {
  1469. int rowCount = ((TableElementInfo)elementInfo).getRowCount();
  1470. int columnCount = ((TableElementInfo)elementInfo).getColumnCount();
  1471. int r = i / rowCount;
  1472. int c = i % columnCount;
  1473. if (r < 0 || r >= rowCount || c < 0 || c >= columnCount) {
  1474. return null;
  1475. } else {
  1476. return getAccessibleAt(r, c);
  1477. }
  1478. }
  1479. public AccessibleTable getAccessibleTable() {
  1480. return this;
  1481. }
  1482. /**
  1483. * Returns the caption for the table.
  1484. *
  1485. * @return the caption for the table
  1486. */
  1487. public Accessible getAccessibleCaption() {
  1488. ElementInfo captionInfo = getCaptionInfo();
  1489. if (captionInfo instanceof Accessible) {
  1490. return (Accessible)caption;
  1491. } else {
  1492. return null;
  1493. }
  1494. }
  1495. /**
  1496. * Sets the caption for the table.
  1497. *
  1498. * @param a the caption for the table
  1499. */
  1500. public void setAccessibleCaption(Accessible a) {
  1501. }
  1502. /**
  1503. * Returns the summary description of the table.
  1504. *
  1505. * @return the summary description of the table
  1506. */
  1507. public Accessible getAccessibleSummary() {
  1508. return null;
  1509. }
  1510. /**
  1511. * Sets the summary description of the table
  1512. *
  1513. * @param a the summary description of the table
  1514. */
  1515. public void setAccessibleSummary(Accessible a) {
  1516. }
  1517. /**
  1518. * Returns the number of rows in the table.
  1519. *
  1520. * @return the number of rows in the table
  1521. */
  1522. public int getAccessibleRowCount() {
  1523. return ((TableElementInfo)elementInfo).getRowCount();
  1524. }
  1525. /**
  1526. * Returns the number of columns in the table.
  1527. *
  1528. * @return the number of columns in the table
  1529. */
  1530. public int getAccessibleColumnCount() {
  1531. return ((TableElementInfo)elementInfo).getColumnCount();
  1532. }
  1533. /**
  1534. * Returns the Accessible at a specified row and column
  1535. * in the table.
  1536. *
  1537. * @param r zero-based row of the table
  1538. * @param c zero-based column of the table
  1539. * @return the Accessible at the specified row and column
  1540. */
  1541. public Accessible getAccessibleAt(int r, int c) {
  1542. TableCellElementInfo cellInfo = getCell(r, c);
  1543. if (cellInfo != null) {
  1544. return cellInfo.getAccessible();
  1545. } else {
  1546. return null;
  1547. }
  1548. }
  1549. /**
  1550. * Returns the number of rows occupied by the Accessible at
  1551. * a specified row and column in the table.
  1552. *
  1553. * @return the number of rows occupied by the Accessible at a
  1554. * given specified (row, column)
  1555. */
  1556. public int getAccessibleRowExtentAt(int r, int c) {
  1557. return ((TableElementInfo)elementInfo).getRowExtentAt(r, c);
  1558. }
  1559. /**
  1560. * Returns the number of columns occupied by the Accessible at
  1561. * a specified row and column in the table.
  1562. *
  1563. * @return the number of columns occupied by the Accessible at a
  1564. * given specified row and column
  1565. */
  1566. public int getAccessibleColumnExtentAt(int r, int c) {
  1567. return ((TableElementInfo)elementInfo).getColumnExtentAt(r, c);
  1568. }
  1569. /**
  1570. * Returns the row headers as an AccessibleTable.
  1571. *
  1572. * @return an AccessibleTable representing the row
  1573. * headers
  1574. */
  1575. public AccessibleTable getAccessibleRowHeader() {
  1576. return rowHeadersTable;
  1577. }
  1578. /**
  1579. * Sets the row headers.
  1580. *
  1581. * @param table an AccessibleTable representing the
  1582. * row headers
  1583. */
  1584. public void setAccessibleRowHeader(AccessibleTable table) {
  1585. }
  1586. /**
  1587. * Returns the column headers as an AccessibleTable.
  1588. *
  1589. * @return an AccessibleTable representing the column
  1590. * headers
  1591. */
  1592. public AccessibleTable getAccessibleColumnHeader() {
  1593. return null;
  1594. }
  1595. /**
  1596. * Sets the column headers.
  1597. *
  1598. * @param table an AccessibleTable representing the
  1599. * column headers
  1600. */
  1601. public void setAccessibleColumnHeader(AccessibleTable table) {
  1602. }
  1603. /**
  1604. * Returns the description of the specified row in the table.
  1605. *
  1606. * @param r zero-based row of the table
  1607. * @return the description of the row
  1608. */
  1609. public Accessible getAccessibleRowDescription(int r) {
  1610. return null;
  1611. }
  1612. /**
  1613. * Sets the description text of the specified row of the table.
  1614. *
  1615. * @param r zero-based row of the table
  1616. * @param a the description of the row
  1617. */
  1618. public void setAccessibleRowDescription(int r, Accessible a) {
  1619. }
  1620. /**
  1621. * Returns the description text of the specified column in the table.
  1622. *
  1623. * @param c zero-based column of the table
  1624. * @return the text description of the column
  1625. */
  1626. public Accessible getAccessibleColumnDescription(int c) {
  1627. return null;
  1628. }
  1629. /**
  1630. * Sets the description text of the specified column in the table.
  1631. *
  1632. * @param c zero-based column of the table
  1633. * @param a the text description of the column
  1634. */
  1635. public void setAccessibleColumnDescription(int c, Accessible a) {
  1636. }
  1637. /**
  1638. * Returns a boolean value indicating whether the accessible at
  1639. * a specified row and column is selected.
  1640. *
  1641. * @param r zero-based row of the table
  1642. * @param c zero-based column of the table
  1643. * @return the boolean value true if the accessible at the
  1644. * row and column is selected. Otherwise, the boolean value
  1645. * false
  1646. */
  1647. public boolean isAccessibleSelected(int r, int c) {
  1648. if (validateIfNecessary()) {
  1649. if (r < 0 || r >= getAccessibleRowCount() ||
  1650. c < 0 || c >= getAccessibleColumnCount()) {
  1651. return false;
  1652. }
  1653. TableCellElementInfo cell = getCell(r, c);
  1654. if (cell != null) {
  1655. Element elem = cell.getElement();
  1656. int start = elem.getStartOffset();
  1657. int end = elem.getEndOffset();
  1658. return start >= editor.getSelectionStart() &&
  1659. end <= editor.getSelectionEnd();
  1660. }
  1661. }
  1662. return false;
  1663. }
  1664. /**
  1665. * Returns a boolean value indicating whether the specified row
  1666. * is selected.
  1667. *
  1668. * @param r zero-based row of the table
  1669. * @return the boolean value true if the specified row is selected.
  1670. * Otherwise, false.
  1671. */
  1672. public boolean isAccessibleRowSelected(int r) {
  1673. if (validateIfNecessary()) {
  1674. if (r < 0 || r >= getAccessibleRowCount()) {
  1675. return false;
  1676. }
  1677. int nColumns = getAccessibleColumnCount();
  1678. TableCellElementInfo startCell = getCell(r, 0);
  1679. if (startCell == null) {
  1680. return false;
  1681. }
  1682. int start = startCell.getElement().getStartOffset();
  1683. TableCellElementInfo endCell = getCell(r, nColumns-1);
  1684. if (endCell == null) {
  1685. return false;
  1686. }
  1687. int end = endCell.getElement().getEndOffset();
  1688. return start >= editor.getSelectionStart() &&
  1689. end <= editor.getSelectionEnd();
  1690. }
  1691. return false;
  1692. }
  1693. /**
  1694. * Returns a boolean value indicating whether the specified column
  1695. * is selected.
  1696. *
  1697. * @param r zero-based column of the table
  1698. * @return the boolean value true if the specified column is selected.
  1699. * Otherwise, false.
  1700. */
  1701. public boolean isAccessibleColumnSelected(int c) {
  1702. if (validateIfNecessary()) {
  1703. if (c < 0 || c >= getAccessibleColumnCount()) {
  1704. return false;
  1705. }
  1706. int nRows = getAccessibleRowCount();
  1707. TableCellElementInfo startCell = getCell(0, c);
  1708. if (startCell == null) {
  1709. return false;
  1710. }
  1711. int start = startCell.getElement().getStartOffset();
  1712. TableCellElementInfo endCell = getCell(nRows-1, c);
  1713. if (endCell == null) {
  1714. return false;
  1715. }
  1716. int end = endCell.getElement().getEndOffset();
  1717. return start >= editor.getSelectionStart() &&
  1718. end <= editor.getSelectionEnd();
  1719. }
  1720. return false;
  1721. }
  1722. /**
  1723. * Returns the selected rows in a table.
  1724. *
  1725. * @return an array of selected rows where each element is a
  1726. * zero-based row of the table
  1727. */
  1728. public int [] getSelectedAccessibleRows() {
  1729. if (validateIfNecessary()) {
  1730. int nRows = getAccessibleRowCount();
  1731. Vector vec = new Vector();
  1732. for (int i = 0; i < nRows; i++) {
  1733. if (isAccessibleRowSelected(i)) {
  1734. vec.addElement(new Integer(i));
  1735. }
  1736. }
  1737. int retval[] = new int[vec.size()];
  1738. for (int i = 0; i < retval.length; i++) {
  1739. retval[i] = ((Integer)vec.elementAt(i)).intValue();
  1740. }
  1741. return retval;
  1742. }
  1743. return new int[0];
  1744. }
  1745. /**
  1746. * Returns the selected columns in a table.
  1747. *
  1748. * @return an array of selected columns where each element is a
  1749. * zero-based column of the table
  1750. */
  1751. public int [] getSelectedAccessibleColumns() {
  1752. if (validateIfNecessary()) {
  1753. int nColumns = getAccessibleRowCount();
  1754. Vector vec = new Vector();
  1755. for (int i = 0; i < nColumns; i++) {
  1756. if (isAccessibleColumnSelected(i)) {
  1757. vec.addElement(new Integer(i));
  1758. }
  1759. }
  1760. int retval[] = new int[vec.size()];
  1761. for (int i = 0; i < retval.length; i++) {
  1762. retval[i] = ((Integer)vec.elementAt(i)).intValue();
  1763. }
  1764. return retval;
  1765. }
  1766. return new int[0];
  1767. }
  1768. // begin AccessibleExtendedTable implementation -------------
  1769. /**
  1770. * Returns the row number of an index in the table.
  1771. *
  1772. * @param index the zero-based index in the table
  1773. * @return the zero-based row of the table if one exists;
  1774. * otherwise -1.
  1775. */
  1776. public int getAccessibleRow(int index) {
  1777. if (validateIfNecessary()) {
  1778. int numCells = getAccessibleColumnCount() *
  1779. getAccessibleRowCount();
  1780. if (index >= numCells) {
  1781. return -1;
  1782. } else {
  1783. return index / getAccessibleColumnCount();
  1784. }
  1785. }
  1786. return -1;
  1787. }
  1788. /**
  1789. * Returns the column number of an index in the table.
  1790. *
  1791. * @param index the zero-based index in the table
  1792. * @return the zero-based column of the table if one exists;
  1793. * otherwise -1.
  1794. */
  1795. public int getAccessibleColumn(int index) {
  1796. if (validateIfNecessary()) {
  1797. int numCells = getAccessibleColumnCount() *
  1798. getAccessibleRowCount();
  1799. if (index >= numCells) {
  1800. return -1;
  1801. } else {
  1802. return index % getAccessibleColumnCount();
  1803. }
  1804. }
  1805. return -1;
  1806. }
  1807. /*
  1808. * Returns the index at a row and column in the table.
  1809. *
  1810. * @param r zero-based row of the table
  1811. * @param c zero-based column of the table
  1812. * @return the zero-based index in the table if one exists;
  1813. * otherwise -1.
  1814. */
  1815. public int getAccessibleIndex(int r, int c) {
  1816. if (validateIfNecessary()) {
  1817. if (r >= getAccessibleRowCount() ||
  1818. c >= getAccessibleColumnCount()) {
  1819. return -1;
  1820. } else {
  1821. return r * getAccessibleColumnCount() + c;
  1822. }
  1823. }
  1824. return -1;
  1825. }
  1826. /**
  1827. * Returns the row header at a row in a table.
  1828. * @param r zero-based row of the table
  1829. *
  1830. * @return a String representing the row header
  1831. * if one exists; otherwise null.
  1832. */
  1833. public String getAccessibleRowHeader(int r) {
  1834. if (validateIfNecessary()) {
  1835. TableCellElementInfo cellInfo = getCell(r, 0);
  1836. if (cellInfo.isHeaderCell()) {
  1837. View v = cellInfo.getView();
  1838. if (v != null && model != null) {
  1839. try {
  1840. return model.getText(v.getStartOffset(),
  1841. v.getEndOffset() -
  1842. v.getStartOffset());
  1843. } catch (BadLocationException e) {
  1844. return null;
  1845. }
  1846. }
  1847. }
  1848. }
  1849. return null;
  1850. }
  1851. /**
  1852. * Returns the column header at a column in a table.
  1853. * @param c zero-based column of the table
  1854. *
  1855. * @return a String representing the column header
  1856. * if one exists; otherwise null.
  1857. */
  1858. public String getAccessibleColumnHeader(int c) {
  1859. if (validateIfNecessary()) {
  1860. TableCellElementInfo cellInfo = getCell(0, c);
  1861. if (cellInfo.isHeaderCell()) {
  1862. View v = cellInfo.getView();
  1863. if (v != null && model != null) {
  1864. try {
  1865. return model.getText(v.getStartOffset(),
  1866. v.getEndOffset() -
  1867. v.getStartOffset());
  1868. } catch (BadLocationException e) {
  1869. return null;
  1870. }
  1871. }
  1872. }
  1873. }
  1874. return null;
  1875. }
  1876. public void addRowHeader(TableCellElementInfo cellInfo, int rowNumber) {
  1877. if (rowHeadersTable == null) {
  1878. rowHeadersTable = new AccessibleHeadersTable();
  1879. }
  1880. rowHeadersTable.addHeader(cellInfo, rowNumber);
  1881. }
  1882. // end of AccessibleExtendedTable implementation ------------
  1883. protected class AccessibleHeadersTable implements AccessibleTable {
  1884. // Header information is modeled as a Hashtable of
  1885. // ArrayLists where each Hashtable entry represents
  1886. // a row containing one or more headers.
  1887. private Hashtable headers = new Hashtable();
  1888. private int rowCount = 0;
  1889. private int columnCount = 0;
  1890. public void addHeader(TableCellElementInfo cellInfo, int rowNumber) {
  1891. Integer rowInteger = new Integer(rowNumber);
  1892. ArrayList list = (ArrayList)headers.get(rowInteger);
  1893. if (list == null) {
  1894. list = new ArrayList();
  1895. headers.put(rowInteger, list);
  1896. }
  1897. list.add(cellInfo);
  1898. }
  1899. /**
  1900. * Returns the caption for the table.
  1901. *
  1902. * @return the caption for the table
  1903. */
  1904. public Accessible getAccessibleCaption() {
  1905. return null;
  1906. }
  1907. /**
  1908. * Sets the caption for the table.
  1909. *
  1910. * @param a the caption for the table
  1911. */
  1912. public void setAccessibleCaption(Accessible a) {
  1913. }
  1914. /**
  1915. * Returns the summary description of the table.
  1916. *
  1917. * @return the summary description of the table
  1918. */
  1919. public Accessible getAccessibleSummary() {
  1920. return null;
  1921. }
  1922. /**
  1923. * Sets the summary description of the table
  1924. *
  1925. * @param a the summary description of the table
  1926. */
  1927. public void setAccessibleSummary(Accessible a) {
  1928. }
  1929. /**
  1930. * Returns the number of rows in the table.
  1931. *
  1932. * @return the number of rows in the table
  1933. */
  1934. public int getAccessibleRowCount() {
  1935. return rowCount;
  1936. }
  1937. /**
  1938. * Returns the number of columns in the table.
  1939. *
  1940. * @return the number of columns in the table
  1941. */
  1942. public int getAccessibleColumnCount() {
  1943. return columnCount;
  1944. }
  1945. private TableCellElementInfo getElementInfoAt(int r, int c) {
  1946. ArrayList list = (ArrayList)headers.get(new Integer(r));
  1947. if (list != null) {
  1948. return (TableCellElementInfo)list.get(c);
  1949. } else {
  1950. return null;
  1951. }
  1952. }
  1953. /**
  1954. * Returns the Accessible at a specified row and column
  1955. * in the table.
  1956. *
  1957. * @param r zero-based row of the table
  1958. * @param c zero-based column of the table
  1959. * @return the Accessible at the specified row and column
  1960. */
  1961. public Accessible getAccessibleAt(int r, int c) {
  1962. ElementInfo elementInfo = getElementInfoAt(r, c);
  1963. if (elementInfo instanceof Accessible) {
  1964. return (Accessible)elementInfo;
  1965. } else {
  1966. return null;
  1967. }
  1968. }
  1969. /**
  1970. * Returns the number of rows occupied by the Accessible at
  1971. * a specified row and column in the table.
  1972. *
  1973. * @return the number of rows occupied by the Accessible at a
  1974. * given specified (row, column)
  1975. */
  1976. public int getAccessibleRowExtentAt(int r, int c) {
  1977. TableCellElementInfo elementInfo = getElementInfoAt(r, c);
  1978. if (elementInfo != null) {
  1979. return elementInfo.getRowCount();
  1980. } else {
  1981. return 0;
  1982. }
  1983. }
  1984. /**
  1985. * Returns the number of columns occupied by the Accessible at
  1986. * a specified row and column in the table.
  1987. *
  1988. * @return the number of columns occupied by the Accessible at a
  1989. * given specified row and column
  1990. */
  1991. public int getAccessibleColumnExtentAt(int r, int c) {
  1992. TableCellElementInfo elementInfo = getElementInfoAt(r, c);
  1993. if (elementInfo != null) {
  1994. return elementInfo.getRowCount();
  1995. } else {
  1996. return 0;
  1997. }
  1998. }
  1999. /**
  2000. * Returns the row headers as an AccessibleTable.
  2001. *
  2002. * @return an AccessibleTable representing the row
  2003. * headers
  2004. */
  2005. public AccessibleTable getAccessibleRowHeader() {
  2006. return null;
  2007. }
  2008. /**
  2009. * Sets the row headers.
  2010. *
  2011. * @param table an AccessibleTable representing the
  2012. * row headers
  2013. */
  2014. public void setAccessibleRowHeader(AccessibleTable table) {
  2015. }
  2016. /**
  2017. * Returns the column headers as an AccessibleTable.
  2018. *
  2019. * @return an AccessibleTable representing the column
  2020. * headers
  2021. */
  2022. public AccessibleTable getAccessibleColumnHeader() {
  2023. return null;
  2024. }
  2025. /**
  2026. * Sets the column headers.
  2027. *
  2028. * @param table an AccessibleTable representing the
  2029. * column headers
  2030. */
  2031. public void setAccessibleColumnHeader(AccessibleTable table) {
  2032. }
  2033. /**
  2034. * Returns the description of the specified row in the table.
  2035. *
  2036. * @param r zero-based row of the table
  2037. * @return the description of the row
  2038. */
  2039. public Accessible getAccessibleRowDescription(int r) {
  2040. return null;
  2041. }
  2042. /**
  2043. * Sets the description text of the specified row of the table.
  2044. *
  2045. * @param r zero-based row of the table
  2046. * @param a the description of the row
  2047. */
  2048. public void setAccessibleRowDescription(int r, Accessible a) {
  2049. }
  2050. /**
  2051. * Returns the description text of the specified column in the table.
  2052. *
  2053. * @param c zero-based column of the table
  2054. * @return the text description of the column
  2055. */
  2056. public Accessible getAccessibleColumnDescription(int c) {
  2057. return null;
  2058. }
  2059. /**
  2060. * Sets the description text of the specified column in the table.
  2061. *
  2062. * @param c zero-based column of the table
  2063. * @param a the text description of the column
  2064. */
  2065. public void setAccessibleColumnDescription(int c, Accessible a) {
  2066. }
  2067. /**
  2068. * Returns a boolean value indicating whether the accessible at
  2069. * a specified row and column is selected.
  2070. *
  2071. * @param r zero-based row of the table
  2072. * @param c zero-based column of the table
  2073. * @return the boolean value true if the accessible at the
  2074. * row and column is selected. Otherwise, the boolean value
  2075. * false
  2076. */
  2077. public boolean isAccessibleSelected(int r, int c) {
  2078. return false;
  2079. }
  2080. /**
  2081. * Returns a boolean value indicating whether the specified row
  2082. * is selected.
  2083. *
  2084. * @param r zero-based row of the table
  2085. * @return the boolean value true if the specified row is selected.
  2086. * Otherwise, false.
  2087. */
  2088. public boolean isAccessibleRowSelected(int r) {
  2089. return false;
  2090. }
  2091. /**
  2092. * Returns a boolean value indicating whether the specified column
  2093. * is selected.
  2094. *
  2095. * @param r zero-based column of the table
  2096. * @return the boolean value true if the specified column is selected.
  2097. * Otherwise, false.
  2098. */
  2099. public boolean isAccessibleColumnSelected(int c) {
  2100. return false;
  2101. }
  2102. /**
  2103. * Returns the selected rows in a table.
  2104. *
  2105. * @return an array of selected rows where each element is a
  2106. * zero-based row of the table
  2107. */
  2108. public int [] getSelectedAccessibleRows() {
  2109. return new int [0];
  2110. }
  2111. /**
  2112. * Returns the selected columns in a table.
  2113. *
  2114. * @return an array of selected columns where each element is a
  2115. * zero-based column of the table
  2116. */
  2117. public int [] getSelectedAccessibleColumns() {
  2118. return new int [0];
  2119. }
  2120. }
  2121. } // ... end AccessibleHeadersTable
  2122. /*
  2123. * ElementInfo for table rows
  2124. */
  2125. private class TableRowElementInfo extends ElementInfo {
  2126. private TableElementInfo parent;
  2127. private int rowNumber;
  2128. TableRowElementInfo(Element e, TableElementInfo parent, int rowNumber) {
  2129. super(e, parent);
  2130. this.parent = parent;
  2131. this.rowNumber = rowNumber;
  2132. }
  2133. protected void loadChildren(Element e) {
  2134. for (int x = 0; x < e.getElementCount(); x++) {
  2135. AttributeSet attrs = e.getElement(x).getAttributes();
  2136. if (attrs.getAttribute(StyleConstants.NameAttribute) ==
  2137. HTML.Tag.TH) {
  2138. TableCellElementInfo headerElementInfo =
  2139. new TableCellElementInfo(e.getElement(x), this, true);
  2140. addChild(headerElementInfo);
  2141. AccessibleTable at =
  2142. parent.getAccessibleContext().getAccessibleTable();
  2143. TableAccessibleContext tableElement =
  2144. (TableAccessibleContext)at;
  2145. tableElement.addRowHeader(headerElementInfo, rowNumber);
  2146. } else if (attrs.getAttribute(StyleConstants.NameAttribute) ==
  2147. HTML.Tag.TD) {
  2148. addChild(new TableCellElementInfo(e.getElement(x), this,
  2149. false));
  2150. }
  2151. }
  2152. }
  2153. /**
  2154. * Returns the max of the rowspans of the cells in this row.
  2155. */
  2156. public int getRowCount() {
  2157. int rowCount = 1;
  2158. if (validateIfNecessary()) {
  2159. for (int counter = 0; counter < getChildCount();
  2160. counter++) {
  2161. TableCellElementInfo cell = (TableCellElementInfo)
  2162. getChild(counter);
  2163. if (cell.validateIfNecessary()) {
  2164. rowCount = Math.max(rowCount, cell.getRowCount());
  2165. }
  2166. }
  2167. }
  2168. return rowCount;
  2169. }
  2170. /**
  2171. * Returns the sum of the column spans of the individual
  2172. * cells in this row.
  2173. */
  2174. public int getColumnCount() {
  2175. int colCount = 0;
  2176. if (validateIfNecessary()) {
  2177. for (int counter = 0; counter < getChildCount();
  2178. counter++) {
  2179. TableCellElementInfo cell = (TableCellElementInfo)
  2180. getChild(counter);
  2181. if (cell.validateIfNecessary()) {
  2182. colCount += cell.getColumnCount();
  2183. }
  2184. }
  2185. }
  2186. return colCount;
  2187. }
  2188. /**
  2189. * Overriden to invalidate the table as well as
  2190. * TableRowElementInfo.
  2191. */
  2192. protected void invalidate(boolean first) {
  2193. super.invalidate(first);
  2194. getParent().invalidate(true);
  2195. }
  2196. /**
  2197. * Places the TableCellElementInfos for this element in
  2198. * the grid.
  2199. */
  2200. private void updateGrid(int row) {
  2201. if (validateIfNecessary()) {
  2202. boolean emptyRow = false;
  2203. while (!emptyRow) {
  2204. for (int counter = 0; counter < grid[row].length;
  2205. counter++) {
  2206. if (grid[row][counter] == null) {
  2207. emptyRow = true;
  2208. break;
  2209. }
  2210. }
  2211. if (!emptyRow) {
  2212. row++;
  2213. }
  2214. }
  2215. for (int col = 0, counter = 0; counter < getChildCount();
  2216. counter++) {
  2217. TableCellElementInfo cell = (TableCellElementInfo)
  2218. getChild(counter);
  2219. while (grid[row][col] != null) {
  2220. col++;
  2221. }
  2222. for (int rowCount = cell.getRowCount() - 1;
  2223. rowCount >= 0; rowCount--) {
  2224. for (int colCount = cell.getColumnCount() - 1;
  2225. colCount >= 0; colCount--) {
  2226. grid[row + rowCount][col + colCount] = cell;
  2227. }
  2228. }
  2229. col += cell.getColumnCount();
  2230. }
  2231. }
  2232. }
  2233. /**
  2234. * Returns the column count of the number of columns that have
  2235. * a rowcount >= rowspan.
  2236. */
  2237. private int getColumnCount(int rowspan) {
  2238. if (validateIfNecessary()) {
  2239. int cols = 0;
  2240. for (int counter = 0; counter < getChildCount();
  2241. counter++) {
  2242. TableCellElementInfo cell = (TableCellElementInfo)
  2243. getChild(counter);
  2244. if (cell.getRowCount() >= rowspan) {
  2245. cols += cell.getColumnCount();
  2246. }
  2247. }
  2248. return cols;
  2249. }
  2250. return 0;
  2251. }
  2252. }
  2253. /**
  2254. * TableCellElementInfo is used to represents the cells of
  2255. * the table.
  2256. */
  2257. private class TableCellElementInfo extends ElementInfo {
  2258. private Accessible accessible;
  2259. private boolean isHeaderCell;
  2260. TableCellElementInfo(Element e, ElementInfo parent) {
  2261. super(e, parent);
  2262. this.isHeaderCell = false;
  2263. }
  2264. TableCellElementInfo(Element e, ElementInfo parent,
  2265. boolean isHeaderCell) {
  2266. super(e, parent);
  2267. this.isHeaderCell = isHeaderCell;
  2268. }
  2269. /*
  2270. * Returns whether this table cell is a header
  2271. */
  2272. public boolean isHeaderCell() {
  2273. return this.isHeaderCell;
  2274. }
  2275. /*
  2276. * Returns the Accessible representing this table cell
  2277. */
  2278. public Accessible getAccessible() {
  2279. accessible = null;
  2280. getAccessible(this);
  2281. return accessible;
  2282. }
  2283. /*
  2284. * Gets the outermost Accessible in the table cell
  2285. */
  2286. private void getAccessible(ElementInfo elementInfo) {
  2287. if (elementInfo instanceof Accessible) {
  2288. accessible = (Accessible)elementInfo;
  2289. return;
  2290. } else {
  2291. for (int i = 0; i < elementInfo.getChildCount(); i++) {
  2292. getAccessible(elementInfo.getChild(i));
  2293. }
  2294. }
  2295. }
  2296. /**
  2297. * Returns the rowspan attribute.
  2298. */
  2299. public int getRowCount() {
  2300. if (validateIfNecessary()) {
  2301. return Math.max(1, getIntAttr(getAttributes(),
  2302. HTML.Attribute.ROWSPAN, 1));
  2303. }
  2304. return 0;
  2305. }
  2306. /**
  2307. * Returns the colspan attribute.
  2308. */
  2309. public int getColumnCount() {
  2310. if (validateIfNecessary()) {
  2311. return Math.max(1, getIntAttr(getAttributes(),
  2312. HTML.Attribute.COLSPAN, 1));
  2313. }
  2314. return 0;
  2315. }
  2316. /**
  2317. * Overriden to invalidate the TableRowElementInfo as well as
  2318. * the TableCellElementInfo.
  2319. */
  2320. protected void invalidate(boolean first) {
  2321. super.invalidate(first);
  2322. getParent().invalidate(true);
  2323. }
  2324. }
  2325. }
  2326. /**
  2327. * ElementInfo provides a slim down view of an Element. Each ElementInfo
  2328. * can have any number of child ElementInfos that are not necessarily
  2329. * direct children of the Element. As the Document changes various
  2330. * ElementInfos become invalidated. Before accessing a particular portion
  2331. * of an ElementInfo you should make sure it is valid by invoking
  2332. * <code>validateIfNecessary</code>, this will return true if
  2333. * successful, on the other hand a false return value indicates the
  2334. * ElementInfo is not valid and can never become valid again (usually
  2335. * the result of the Element the ElementInfo encapsulates being removed).
  2336. */
  2337. private class ElementInfo {
  2338. /**
  2339. * The children of this ElementInfo.
  2340. */
  2341. private ArrayList children;
  2342. /**
  2343. * The Element this ElementInfo is providing information for.
  2344. */
  2345. private Element element;
  2346. /**
  2347. * The parent ElementInfo, will be null for the root.
  2348. */
  2349. private ElementInfo parent;
  2350. /**
  2351. * Indicates the validity of the ElementInfo.
  2352. */
  2353. private boolean isValid;
  2354. /**
  2355. * Indicates if the ElementInfo can become valid.
  2356. */
  2357. private boolean canBeValid;
  2358. /**
  2359. * Creates the root ElementInfo.
  2360. */
  2361. ElementInfo(Element element) {
  2362. this(element, null);
  2363. }
  2364. /**
  2365. * Creates an ElementInfo representing <code>element</code> with
  2366. * the specified parent.
  2367. */
  2368. ElementInfo(Element element, ElementInfo parent) {
  2369. this.element = element;
  2370. this.parent = parent;
  2371. isValid = false;
  2372. canBeValid = true;
  2373. }
  2374. /**
  2375. * Validates the receiver. This recreates the children as well. This
  2376. * will be invoked within a <code>readLock</code>. If this is overriden
  2377. * it MUST invoke supers implementation first!
  2378. */
  2379. protected void validate() {
  2380. isValid = true;
  2381. loadChildren(getElement());
  2382. }
  2383. /**
  2384. * Recreates the direct children of <code>info</code>.
  2385. */
  2386. protected void loadChildren(Element parent) {
  2387. if (!parent.isLeaf()) {
  2388. for (int counter = 0, maxCounter = parent.getElementCount();
  2389. counter < maxCounter; counter++) {
  2390. Element e = parent.getElement(counter);
  2391. ElementInfo childInfo = createElementInfo(e, this);
  2392. if (childInfo != null) {
  2393. addChild(childInfo);
  2394. }
  2395. else {
  2396. loadChildren(e);
  2397. }
  2398. }
  2399. }
  2400. }
  2401. /**
  2402. * Returns the index of the child in the parent, or -1 for the
  2403. * root or if the parent isn't valid.
  2404. */
  2405. public int getIndexInParent() {
  2406. if (parent == null || !parent.isValid()) {
  2407. return -1;
  2408. }
  2409. return parent.indexOf(this);
  2410. }
  2411. /**
  2412. * Returns the Element this <code>ElementInfo</code> represents.
  2413. */
  2414. public Element getElement() {
  2415. return element;
  2416. }
  2417. /**
  2418. * Returns the parent of this Element, or null for the root.
  2419. */
  2420. public ElementInfo getParent() {
  2421. return parent;
  2422. }
  2423. /**
  2424. * Returns the index of the specified child, or -1 if
  2425. * <code>child</code> isn't a valid child.
  2426. */
  2427. public int indexOf(ElementInfo child) {
  2428. ArrayList children = this.children;
  2429. if (children != null) {
  2430. return children.indexOf(child);
  2431. }
  2432. return -1;
  2433. }
  2434. /**
  2435. * Returns the child ElementInfo at <code>index</code>, or null
  2436. * if <code>index</code> isn't a valid index.
  2437. */
  2438. public ElementInfo getChild(int index) {
  2439. if (validateIfNecessary()) {
  2440. ArrayList children = this.children;
  2441. if (children != null && index >= 0 &&
  2442. index < children.size()) {
  2443. return (ElementInfo)children.get(index);
  2444. }
  2445. }
  2446. return null;
  2447. }
  2448. /**
  2449. * Returns the number of children the ElementInfo contains.
  2450. */
  2451. public int getChildCount() {
  2452. validateIfNecessary();
  2453. return (children == null) ? 0 : children.size();
  2454. }
  2455. /**
  2456. * Adds a new child to this ElementInfo.
  2457. */
  2458. protected void addChild(ElementInfo child) {
  2459. if (children == null) {
  2460. children = new ArrayList();
  2461. }
  2462. children.add(child);
  2463. }
  2464. /**
  2465. * Returns the View corresponding to this ElementInfo, or null
  2466. * if the ElementInfo can't be validated.
  2467. */
  2468. protected View getView() {
  2469. if (!validateIfNecessary()) {
  2470. return null;
  2471. }
  2472. Object lock = lock();
  2473. try {
  2474. View rootView = getRootView();
  2475. Element e = getElement();
  2476. int start = e.getStartOffset();
  2477. if (rootView != null) {
  2478. return getView(rootView, e, start);
  2479. }
  2480. return null;
  2481. } finally {
  2482. unlock(lock);
  2483. }
  2484. }
  2485. /**
  2486. * Returns the Bounds for this ElementInfo, or null
  2487. * if the ElementInfo can't be validated.
  2488. */
  2489. public Rectangle getBounds() {
  2490. if (!validateIfNecessary()) {
  2491. return null;
  2492. }
  2493. Object lock = lock();
  2494. try {
  2495. Rectangle bounds = getRootEditorRect();
  2496. View rootView = getRootView();
  2497. Element e = getElement();
  2498. if (bounds != null && rootView != null) {
  2499. try {
  2500. return rootView.modelToView(e.getStartOffset(),
  2501. Position.Bias.Forward,
  2502. e.getEndOffset(),
  2503. Position.Bias.Backward,
  2504. bounds).getBounds();
  2505. } catch (BadLocationException ble) { }
  2506. }
  2507. } finally {
  2508. unlock(lock);
  2509. }
  2510. return null;
  2511. }
  2512. /**
  2513. * Returns true if this ElementInfo is valid.
  2514. */
  2515. protected boolean isValid() {
  2516. return isValid;
  2517. }
  2518. /**
  2519. * Returns the AttributeSet associated with the Element, this will
  2520. * return null if the ElementInfo can't be validated.
  2521. */
  2522. protected AttributeSet getAttributes() {
  2523. if (validateIfNecessary()) {
  2524. return getElement().getAttributes();
  2525. }
  2526. return null;
  2527. }
  2528. /**
  2529. * Returns the AttributeSet associated with the View that is
  2530. * representing this Element, this will
  2531. * return null if the ElementInfo can't be validated.
  2532. */
  2533. protected AttributeSet getViewAttributes() {
  2534. if (validateIfNecessary()) {
  2535. View view = getView();
  2536. if (view != null) {
  2537. return view.getElement().getAttributes();
  2538. }
  2539. return getElement().getAttributes();
  2540. }
  2541. return null;
  2542. }
  2543. /**
  2544. * Convenience method for getting an integer attribute from the passed
  2545. * in AttributeSet.
  2546. */
  2547. protected int getIntAttr(AttributeSet attrs, Object key, int deflt) {
  2548. if (attrs != null && attrs.isDefined(key)) {
  2549. int i;
  2550. String val = (String)attrs.getAttribute(key);
  2551. if (val == null) {
  2552. i = deflt;
  2553. }
  2554. else {
  2555. try {
  2556. i = Math.max(0, Integer.parseInt(val));
  2557. } catch (NumberFormatException x) {
  2558. i = deflt;
  2559. }
  2560. }
  2561. return i;
  2562. }
  2563. return deflt;
  2564. }
  2565. /**
  2566. * Validates the ElementInfo if necessary. Some ElementInfos may
  2567. * never be valid again. You should check <code>isValid</code> before
  2568. * using one. This will reload the children and invoke
  2569. * <code>validate</code> if the ElementInfo is invalid and can become
  2570. * valid again. This will return true if the receiver is valid.
  2571. */
  2572. protected boolean validateIfNecessary() {
  2573. if (!isValid() && canBeValid) {
  2574. children = null;
  2575. Object lock = lock();
  2576. try {
  2577. validate();
  2578. } finally {
  2579. unlock(lock);
  2580. }
  2581. }
  2582. return isValid();
  2583. }
  2584. /**
  2585. * Invalidates the ElementInfo. Subclasses should override this
  2586. * if they need to reset state once invalid.
  2587. */
  2588. protected void invalidate(boolean first) {
  2589. if (!isValid()) {
  2590. if (canBeValid && !first) {
  2591. canBeValid = false;
  2592. }
  2593. return;
  2594. }
  2595. isValid = false;
  2596. canBeValid = first;
  2597. if (children != null) {
  2598. for (int counter = 0; counter < children.size(); counter++) {
  2599. ((ElementInfo)children.get(counter)).invalidate(false);
  2600. }
  2601. children = null;
  2602. }
  2603. }
  2604. private View getView(View parent, Element e, int start) {
  2605. if (parent.getElement() == e) {
  2606. return parent;
  2607. }
  2608. int index = parent.getViewIndex(start, Position.Bias.Forward);
  2609. if (index != -1 && index < parent.getViewCount()) {
  2610. return getView(parent.getView(index), e, start);
  2611. }
  2612. return null;
  2613. }
  2614. private int getClosestInfoIndex(int index) {
  2615. for (int counter = 0; counter < getChildCount(); counter++) {
  2616. ElementInfo info = getChild(counter);
  2617. if (index < info.getElement().getEndOffset() ||
  2618. index == info.getElement().getStartOffset()) {
  2619. return counter;
  2620. }
  2621. }
  2622. return -1;
  2623. }
  2624. private void update(DocumentEvent e) {
  2625. if (!isValid()) {
  2626. return;
  2627. }
  2628. ElementInfo parent = getParent();
  2629. Element element = getElement();
  2630. do {
  2631. DocumentEvent.ElementChange ec = e.getChange(element);
  2632. if (ec != null) {
  2633. if (element == getElement()) {
  2634. // One of our children changed.
  2635. invalidate(true);
  2636. }
  2637. else if (parent != null) {
  2638. parent.invalidate(parent == getRootInfo());
  2639. }
  2640. return;
  2641. }
  2642. element = element.getParentElement();
  2643. } while (parent != null && element != null &&
  2644. element != parent.getElement());
  2645. if (getChildCount() > 0) {
  2646. Element elem = getElement();
  2647. int pos = e.getOffset();
  2648. int index0 = getClosestInfoIndex(pos);
  2649. if (index0 == -1 &&
  2650. e.getType() == DocumentEvent.EventType.REMOVE &&
  2651. pos >= elem.getEndOffset()) {
  2652. // Event beyond our offsets. We may have represented this,
  2653. // that is the remove may have removed one of our child
  2654. // Elements that represented this, so, we should foward
  2655. // to last element.
  2656. index0 = getChildCount() - 1;
  2657. }
  2658. ElementInfo info = (index0 >= 0) ? getChild(index0) : null;
  2659. if (info != null &&
  2660. (info.getElement().getStartOffset() == pos) && (pos > 0)) {
  2661. // If at a boundary, forward the event to the previous
  2662. // ElementInfo too.
  2663. index0 = Math.max(index0 - 1, 0);
  2664. }
  2665. int index1;
  2666. if (e.getType() != DocumentEvent.EventType.REMOVE) {
  2667. index1 = getClosestInfoIndex(pos + e.getLength());
  2668. if (index1 < 0) {
  2669. index1 = getChildCount() - 1;
  2670. }
  2671. }
  2672. else {
  2673. index1 = index0;
  2674. // A remove may result in empty elements.
  2675. while ((index1 + 1) < getChildCount() &&
  2676. getChild(index1 + 1).getElement().getEndOffset() ==
  2677. getChild(index1 + 1).getElement().getStartOffset()){
  2678. index1++;
  2679. }
  2680. }
  2681. index0 = Math.max(index0, 0);
  2682. // The check for isValid is here as in the process of
  2683. // forwarding update our child may invalidate us.
  2684. for (int i = index0; i <= index1 && isValid(); i++) {
  2685. getChild(i).update(e);
  2686. }
  2687. }
  2688. }
  2689. }
  2690. /**
  2691. * DocumentListener installed on the current Document. Will invoke
  2692. * <code>update</code> on the <code>RootInfo</code> in response to
  2693. * any event.
  2694. */
  2695. private class DocumentHandler implements DocumentListener {
  2696. public void insertUpdate(DocumentEvent e) {
  2697. getRootInfo().update(e);
  2698. }
  2699. public void removeUpdate(DocumentEvent e) {
  2700. getRootInfo().update(e);
  2701. }
  2702. public void changedUpdate(DocumentEvent e) {
  2703. getRootInfo().update(e);
  2704. }
  2705. }
  2706. /*
  2707. * PropertyChangeListener installed on the editor.
  2708. */
  2709. private class PropertyChangeHandler implements PropertyChangeListener {
  2710. public void propertyChange(PropertyChangeEvent evt) {
  2711. if (evt.getPropertyName().equals("document")) {
  2712. // handle the document change
  2713. setDocument(editor.getDocument());
  2714. }
  2715. }
  2716. }
  2717. }