This implements the missing pieces of AccessibleJTextComponent.

2006-08-05  Roman Kennke  <[EMAIL PROTECTED]>

        * javax/swing/text/JTextComponent.java
        (AccessibleJTextComponent.dot): Renamed field into caretDot.
        (AccessibleJTextComponent.textComp): Removed field
        and replace with JTextComponent.this construct.
        (AccessibleJTextComponent.AccessibleJTextComponent):
        Fetch caret position.
        (caretUpdate): Implemented. Fires property change events and
        updates the caretDot field.
        (changedUpdate): Implemented. Fires property change events.
        (insertUpdate): Implemented. Fires property change events.
        (removeUpdate): Implemented. Fires property change events.
        (cut): Replaced textComp with JTextComponent.this construct.
        (paste): Replaced textComp with JTextComponent.this construct.
        (replaceText): Replaced textComp with JTextComponent.this construct.
        (selectText): Replaced textComp with JTextComponent.this construct.
        (getCaretPosition): Replaced textComp with JTextComponent.this
        construct.
        (getCharCount): Replaced textComp with JTextComponent.this construct.
        (getSelectedText): Replaced textComp with JTextComponent.this
        construct.
        (getSelectionEnd): Replaced textComp with JTextComponent.this
        construct.
        (getSelectionStart): Replaced textComp with JTextComponent.this
        construct.
        (getTextRange): Replaced textComp with JTextComponent.this
        construct.
        (doAccessibleAction): Implemented.
        (getAccessibleActionCount): Implemented.
        (getAccessibleActionDescription): Implemented.
        (getAccessibleStateSet): Implemented.
        (getAfterIndex): Implemented.
        (getBeforeIndex): Implemented.
        (getAtIndex): Implemented.
        (getAtIndexImpl): New helper method.
        (getCharacterAttribute): Implemented.
        (getCharacterBounds): Implemented.
        (getIndexAtPoint): Implemented. 
        (insertTextAtIndex): Implemented.
        (setAttributes): Implemented.
        (setTextContents): Implemented.

/Roman
Index: javax/swing/text/JTextComponent.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/JTextComponent.java,v
retrieving revision 1.60
diff -u -1 -2 -r1.60 JTextComponent.java
--- javax/swing/text/JTextComponent.java	21 Jun 2006 17:04:41 -0000	1.60
+++ javax/swing/text/JTextComponent.java	7 Aug 2006 19:19:14 -0000
@@ -29,53 +29,54 @@
 modules, and to copy and distribute the resulting executable under
 terms of your choice, provided that you also meet, for each linked
 independent module, the terms and conditions of the license of that
 module.  An independent module is a module which is not derived from
 or based on this library.  If you modify this library, you may extend
 this exception to your version of the library, but you are not
 obligated to do so.  If you do not wish to do so, delete this
 exception statement from your version. */
 
 
 package javax.swing.text;
 
-import gnu.classpath.NotImplementedException;
-
 import java.awt.AWTEvent;
 import java.awt.Color;
 import java.awt.Container;
 import java.awt.Dimension;
 import java.awt.Insets;
 import java.awt.Point;
 import java.awt.Rectangle;
+import java.awt.Shape;
 import java.awt.datatransfer.Clipboard;
 import java.awt.datatransfer.DataFlavor;
 import java.awt.datatransfer.StringSelection;
 import java.awt.datatransfer.Transferable;
 import java.awt.datatransfer.UnsupportedFlavorException;
 import java.awt.event.ActionEvent;
 import java.awt.event.InputMethodListener;
 import java.awt.event.KeyEvent;
 import java.awt.event.MouseEvent;
 import java.io.IOException;
 import java.io.Reader;
 import java.io.Writer;
+import java.text.BreakIterator;
 import java.util.Enumeration;
 import java.util.Hashtable;
 
 import javax.accessibility.Accessible;
 import javax.accessibility.AccessibleAction;
 import javax.accessibility.AccessibleContext;
 import javax.accessibility.AccessibleEditableText;
 import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleState;
 import javax.accessibility.AccessibleStateSet;
 import javax.accessibility.AccessibleText;
 import javax.swing.Action;
 import javax.swing.ActionMap;
 import javax.swing.InputMap;
 import javax.swing.JComponent;
 import javax.swing.JViewport;
 import javax.swing.KeyStroke;
 import javax.swing.Scrollable;
 import javax.swing.SwingConstants;
 import javax.swing.TransferHandler;
 import javax.swing.UIManager;
@@ -96,120 +97,124 @@
    * manipulate the text component's contents as well as update UI
    * elements such as the caret.
    */
   public class AccessibleJTextComponent extends AccessibleJComponent implements
       AccessibleText, CaretListener, DocumentListener, AccessibleAction,
       AccessibleEditableText
   {
     private static final long serialVersionUID = 7664188944091413696L;
 
     /**
      * The caret's offset.
      */
-    int dot = 0;
-
-    /**
-     * The current JTextComponent.
-     */
-    JTextComponent textComp = JTextComponent.this;
+    private int caretDot;
 
     /**
      * Construct an AccessibleJTextComponent.
      */
     public AccessibleJTextComponent()
     {
       super();
-      textComp.addCaretListener(this);
+      JTextComponent.this.addCaretListener(this);
+      caretDot = getCaretPosition();
     }
 
     /**
      * Retrieve the current caret position.  The index of the first
      * caret position is 0.
      *
      * @return caret position
      */
     public int getCaretPosition()
     {
-      dot = textComp.getCaretPosition();
-      return dot;
+      return JTextComponent.this.getCaretPosition();
     }
 
     /**
      * Retrieve the current text selection.  If no text is selected
      * this method returns null.
      *
      * @return the currently selected text or null
      */
     public String getSelectedText()
     {
-      return textComp.getSelectedText();
+      return JTextComponent.this.getSelectedText();
     }
 
     /**
      * Retrieve the index of the first character in the current text
      * selection.  If there is no text in the text component, this
      * method returns 0.  If there is text in the text component, but
      * there is no selection, this method returns the current caret
      * position.
      *
      * @return the index of the first character in the selection, the
      * current caret position or 0
      */
     public int getSelectionStart()
     {
-      if (getSelectedText() == null || (textComp.getText().equals("")))
+      if (getSelectedText() == null
+          || (JTextComponent.this.getText().equals("")))
         return 0;
-      return textComp.getSelectionStart();
+      return JTextComponent.this.getSelectionStart();
     }
 
     /**
      * Retrieve the index of the last character in the current text
      * selection.  If there is no text in the text component, this
      * method returns 0.  If there is text in the text component, but
      * there is no selection, this method returns the current caret
      * position.
      *
      * @return the index of the last character in the selection, the
      * current caret position or 0
      */
     public int getSelectionEnd()
     {
-      if (getSelectedText() == null || (textComp.getText().equals("")))
-        return 0;
-      return textComp.getSelectionEnd();
+      return JTextComponent.this.getSelectionEnd();
     }
 
     /**
      * Handle a change in the caret position and fire any applicable
      * property change events.
      *
      * @param e - the caret update event
      */
     public void caretUpdate(CaretEvent e)
-      throws NotImplementedException
     {
-      // TODO: fire appropriate event.
-      dot = e.getDot();
+      int dot = e.getDot();
+      int mark = e.getMark();
+      if (caretDot != dot)
+        {
+          firePropertyChange(ACCESSIBLE_CARET_PROPERTY, new Integer(caretDot),
+                             new Integer(dot));
+          caretDot = dot;
+        }
+      if (mark != dot)
+        {
+          firePropertyChange(ACCESSIBLE_SELECTION_PROPERTY, null,
+                             getSelectedText());
+        }
     }
 
     /**
      * Retreive the accessible state set of this component.
      *
      * @return the accessible state set of this component
      */
     public AccessibleStateSet getAccessibleStateSet()
-      throws NotImplementedException
     {
       AccessibleStateSet state = super.getAccessibleStateSet();
-      // TODO: Figure out what state must be added here to the super's state.
+      if (isEditable())
+        state.add(AccessibleState.EDITABLE);
       return state;
     }
 
     /**
      * Retrieve the accessible role of this component.
      *
      * @return the accessible role of this component
      *
      * @see AccessibleRole
      */
     public AccessibleRole getAccessibleRole()
     {
@@ -239,230 +244,374 @@
     {
       return this;
     }
     
     /**
      * Handle a text insertion event and fire an
      * AccessibleContext.ACCESSIBLE_TEXT_PROPERTY property change
      * event.
      *
      * @param e - the insertion event
      */
     public void insertUpdate(DocumentEvent e)
-      throws NotImplementedException
     {
-      // TODO
+      firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null,
+                         new Integer(e.getOffset()));
     }
 
     /**
      * Handle a text removal event and fire an
      * AccessibleContext.ACCESSIBLE_TEXT_PROPERTY property change
      * event.
      *
      * @param e - the removal event
      */
     public void removeUpdate(DocumentEvent e)
-      throws NotImplementedException
     {
-      // TODO
+      firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null,
+                         new Integer(e.getOffset()));
     }
 
     /**
      * Handle a text change event and fire an
      * AccessibleContext.ACCESSIBLE_TEXT_PROPERTY property change
      * event.
      *
      * @param e - text change event
      */
     public void changedUpdate(DocumentEvent e)
-      throws NotImplementedException
     {
-      // TODO
+      firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null,
+                         new Integer(e.getOffset()));
     }
 
     /**
      * Get the index of the character at the given point, in component
      * pixel co-ordinates.  If the point argument is invalid this
      * method returns -1.
      *
      * @param p - a point in component pixel co-ordinates
      *
      * @return a character index, or -1
      */
     public int getIndexAtPoint(Point p)
-      throws NotImplementedException
     {
-      return 0; // TODO
+      return viewToModel(p);
     }
 
     /**
      * Calculate the bounding box of the character at the given index.
      * The returned x and y co-ordinates are relative to this text
      * component's top-left corner.  If the index is invalid this
      * method returns null.
      *
      * @param index - the character index
      *
      * @return a character's bounding box, or null
      */
     public Rectangle getCharacterBounds(int index)
-      throws NotImplementedException
     {
-      return null; // TODO
+      // This is basically the same as BasicTextUI.modelToView().
+      
+      Rectangle bounds = null;
+      if (index >= 0 && index < doc.getLength() - 1)
+        {
+          if (doc instanceof AbstractDocument)
+            ((AbstractDocument) doc).readLock();
+          try
+            {
+              TextUI ui = getUI();
+              if (ui != null)
+                {
+                  // Get editor rectangle.
+                  Rectangle rect = new Rectangle();
+                  Insets insets = getInsets();
+                  rect.x = insets.left;
+                  rect.y = insets.top;
+                  rect.width = getWidth() - insets.left - insets.right;
+                  rect.height = getHeight() - insets.top - insets.bottom;
+                  View rootView = ui.getRootView(JTextComponent.this);
+                  if (rootView != null)
+                    {
+                      rootView.setSize(rect.width, rect.height);
+                      Shape s = rootView.modelToView(index,
+                                                     Position.Bias.Forward,
+                                                     index + 1,
+                                                     Position.Bias.Backward,
+                                                     rect);
+                      if (s != null)
+                        bounds = s.getBounds();
+                    }
+                }
+            }
+          catch (BadLocationException ex)
+            {
+              // Ignore (return null).
+            }
+          finally
+            {
+              if (doc instanceof AbstractDocument)
+                ((AbstractDocument) doc).readUnlock();
+            }
+        }
+      return bounds;
     }
 
     /**
      * Return the length of the text in this text component.
      *
      * @return a character length
      */
     public int getCharCount()
     {
-      return textComp.getText().length();
+      return JTextComponent.this.getText().length();
     }
 
    /** 
     * Gets the character attributes of the character at index. If
     * the index is out of bounds, null is returned.
     *
     * @param index - index of the character
     * 
     * @return the character's attributes
     */
     public AttributeSet getCharacterAttribute(int index)
-      throws NotImplementedException
     {
-      return null; // TODO
+      AttributeSet atts;
+      if (doc instanceof AbstractDocument)
+        ((AbstractDocument) doc).readLock();
+      try
+        {
+          Element el = doc.getDefaultRootElement();
+          while (! el.isLeaf())
+            {
+              int i = el.getElementIndex(index);
+              el = el.getElement(i);
+            }
+          atts = el.getAttributes();
+        }
+      finally
+        {
+          if (doc instanceof AbstractDocument)
+            ((AbstractDocument) doc).readUnlock();
+        }
+      return atts;
     }
 
     /**
      * Gets the text located at index. null is returned if the index
      * or part is invalid.
      * 
      * @param part - [EMAIL PROTECTED] #CHARACTER}, [EMAIL PROTECTED] #WORD}, or [EMAIL PROTECTED] #SENTENCE}
      * @param index - index of the part
      * 
      * @return the part of text at that index, or null
      */
     public String getAtIndex(int part, int index)
-      throws NotImplementedException
     {
-      return null; // TODO
+      return getAtIndexImpl(part, index, 0);
     }
     
     /**
      * Gets the text located after index. null is returned if the index
      * or part is invalid.
      * 
      * @param part - [EMAIL PROTECTED] #CHARACTER}, [EMAIL PROTECTED] #WORD}, or [EMAIL PROTECTED] #SENTENCE}
      * @param index - index after the part
      * 
      * @return the part of text after that index, or null
      */
     public String getAfterIndex(int part, int index)
-      throws NotImplementedException
     {
-      return null; // TODO
+      return getAtIndexImpl(part, index, 1);
     }
 
     /**
      * Gets the text located before index. null is returned if the index
      * or part is invalid.
      * 
      * @param part - [EMAIL PROTECTED] #CHARACTER}, [EMAIL PROTECTED] #WORD}, or [EMAIL PROTECTED] #SENTENCE}
      * @param index - index before the part
      * 
      * @return the part of text before that index, or null
      */
     public String getBeforeIndex(int part, int index)
-      throws NotImplementedException
     {
-      return null; // TODO
+      return getAtIndexImpl(part, index, -1);
     }
-    
+
+    /**
+     * Implements getAtIndex(), getBeforeIndex() and getAfterIndex().
+     *
+     * @param part the part to return, either CHARACTER, WORD or SENTENCE
+     * @param index the index
+     * @param dir the direction, -1 for backwards, 0 for here, +1 for forwards
+     *
+     * @return the resulting string
+     */
+    private String getAtIndexImpl(int part, int index, int dir)
+    {
+      String ret = null;
+      if (doc instanceof AbstractDocument)
+        ((AbstractDocument) doc).readLock();
+      try
+        {
+          BreakIterator iter = null;
+          switch (part)
+          {
+            case CHARACTER:
+              iter = BreakIterator.getCharacterInstance(getLocale());
+              break;
+            case WORD:
+              iter = BreakIterator.getWordInstance(getLocale());
+              break;
+            case SENTENCE:
+              iter = BreakIterator.getSentenceInstance(getLocale());
+              break;
+            default:
+              break;
+          }
+          String text = doc.getText(0, doc.getLength() - 1);
+          iter.setText(text);
+          int start = index;
+          int end = index;
+          switch (dir)
+          {
+          case 0:
+            if (iter.isBoundary(index))
+              {
+                start = index;
+                end = iter.following(index);
+              }
+            else
+              {
+                start = iter.preceding(index);
+                end = iter.next();
+              }
+            break;
+          case 1:
+            start = iter.following(index);
+            end = iter.next();
+            break;
+          case -1:
+            end = iter.preceding(index);
+            start = iter.previous();
+            break;
+          default:
+            assert false;
+          }
+          ret = text.substring(start, end);
+        }
+      catch (BadLocationException ex)
+        {
+          // Ignore (return null).
+        }
+      finally
+        {
+          if (doc instanceof AbstractDocument)
+            ((AbstractDocument) doc).readUnlock();
+        }
+      return ret;
+    }
+
     /**
      * Returns the number of actions for this object. The zero-th
      * object represents the default action.
      * 
      * @return the number of actions (0-based).
      */
     public int getAccessibleActionCount()
-      throws NotImplementedException
     {
-      return 0; // TODO
+      return getActions().length;
     }
     
     /**
      * Returns the description of the i-th action. Null is returned if
      * i is out of bounds.
      * 
      * @param i - the action to get the description for
      * 
      * @return description of the i-th action
      */
     public String getAccessibleActionDescription(int i)
-      throws NotImplementedException
     {
-      // TODO: Not implemented fully
-      return super.getAccessibleDescription();
+      String desc = null;
+      Action[] actions = getActions();
+      if (i >= 0 && i < actions.length)
+        desc = (String) actions[i].getValue(Action.NAME);
+      return desc;
     }
     
     /**
      * Performs the i-th action. Nothing happens if i is 
      * out of bounds.
      *
      * @param i - the action to perform
      * 
      * @return true if the action was performed successfully
      */
     public boolean doAccessibleAction(int i)
-      throws NotImplementedException
     {
-      return false; // TODO
+      boolean ret = false;
+      Action[] actions = getActions();
+      if (i >= 0 && i < actions.length)
+        {
+          ActionEvent ev = new ActionEvent(JTextComponent.this,
+                                           ActionEvent.ACTION_PERFORMED, null);
+          actions[i].actionPerformed(ev);
+          ret = true;
+        }
+      return ret;
     }
     
     /**
      * Sets the text contents.
      *
      * @param s - the new text contents.
      */
     public void setTextContents(String s)
-      throws NotImplementedException
     {
-      // TODO
+      setText(s);
     }
 
     /**
      * Inserts the text at the given index.
      *
      * @param index - the index to insert the new text at.
      * @param s - the new text
      */
     public void insertTextAtIndex(int index, String s)
-      throws NotImplementedException
     {
-      replaceText(index, index, s);
+      try
+        {
+          doc.insertString(index, s, null);
+        }
+      catch (BadLocationException ex)
+        {
+          // What should we do with this?
+          ex.printStackTrace();
+        }
     }
 
     /**
      * Gets the text between two indexes.
      *
      * @param start - the starting index (inclusive)
      * @param end - the ending index (exclusive)
      */
     public String getTextRange(int start, int end)
     {
       try
       {
-        return textComp.getText(start, end - start);
+        return JTextComponent.this.getText(start, end - start);
       }
       catch (BadLocationException ble)
       {
         return "";
       }
     }
 
     /**
      * Deletes the text between two indexes.
      *
      * @param start - the starting index (inclusive)
      * @param end - the ending index (exclusive)
@@ -472,75 +621,78 @@
       replaceText(start, end, "");
     }
 
     /**
      * Cuts the text between two indexes. The text is put
      * into the system clipboard.
      *
      * @param start - the starting index (inclusive)
      * @param end - the ending index (exclusive)
      */
     public void cut(int start, int end)
     {
-      textComp.select(start, end);
-      textComp.cut();
+      JTextComponent.this.select(start, end);
+      JTextComponent.this.cut();
     }
 
     /**
      * Pastes the text from the system clipboard to the given index.
      *
      * @param start - the starting index
      */
     public void paste(int start)
     {
-      textComp.setCaretPosition(start);
-      textComp.paste();
+      JTextComponent.this.setCaretPosition(start);
+      JTextComponent.this.paste();
     }
 
     /**
      * Replaces the text between two indexes with the given text.
      *
      *
      * @param start - the starting index (inclusive)
      * @param end - the ending index (exclusive)
      * @param s - the text to paste
      */
     public void replaceText(int start, int end, String s)
     {
-      textComp.select(start, end);
-      textComp.replaceSelection(s);
+      JTextComponent.this.select(start, end);
+      JTextComponent.this.replaceSelection(s);
     }
 
     /**
      * Selects the text between two indexes.
      *
      * @param start - the starting index (inclusive)
      * @param end - the ending index (exclusive)
      */
     public void selectText(int start, int end)
     {
-      textComp.select(start, end);
+      JTextComponent.this.select(start, end);
     }
 
     /**
      * Sets the attributes of all the text between two indexes.
      *
      * @param start - the starting index (inclusive)
      * @param end - the ending index (exclusive)
      * @param s - the new attribute set for the text in the range
      */
     public void setAttributes(int start, int end, AttributeSet s)
-      throws NotImplementedException
     {
-      // TODO
+      if (doc instanceof StyledDocument)
+        {
+          StyledDocument sdoc = (StyledDocument) doc;
+          sdoc.setCharacterAttributes(start, end - start, s, true);
+        }
     }
   }
 
   public static class KeyBinding
   {
     public KeyStroke key;
     public String actionName;
 
     /**
      * Creates a new <code>KeyBinding</code> instance.
      *
      * @param key a <code>KeyStroke</code> value

Reply via email to