History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/javax/swing/text/html/AccessibleHTML.java (1 of 18352 files). Show all

Unified · Split

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