blob: 3e350287392a380566264136ccc463d97b887d88 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
package org.openoffice.accessibility.awb.view;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JTree;
import javax.swing.tree.TreeNode;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.MutableTreeNode;
import com.sun.star.accessibility.AccessibleEventId;
import com.sun.star.accessibility.AccessibleEventObject;
import com.sun.star.accessibility.AccessibleTextType;
import com.sun.star.accessibility.AccessibleStateType;
import com.sun.star.accessibility.TextSegment;
import com.sun.star.accessibility.XAccessibleText;
import com.sun.star.accessibility.XAccessibleContext;
import com.sun.star.accessibility.XAccessibleMultiLineText;
import com.sun.star.accessibility.XAccessibleStateSet;
import com.sun.star.awt.Point;
import com.sun.star.awt.Rectangle;
import com.sun.star.beans.PropertyValue;
import com.sun.star.lang.IndexOutOfBoundsException;
import com.sun.star.lang.IllegalArgumentException;
import com.sun.star.uno.UnoRuntime;
import org.openoffice.accessibility.awb.view.text.CaretSpinnerModel;
import org.openoffice.accessibility.awb.view.text.TextDialogFactory;
public class TextView
extends ObjectView
implements ActionListener
{
/** Create a TextView when the given object supports the
XAccessibleText interface.
*/
static public ObjectView Create (
ObjectViewContainer aContainer,
XAccessibleContext xContext)
{
XAccessibleText xText = (XAccessibleText)UnoRuntime.queryInterface(
XAccessibleText.class, xContext);
if (xText != null)
return new TextView (aContainer);
else
return null;
}
public TextView (ObjectViewContainer aContainer)
{
super (aContainer);
ViewGridLayout aLayout = new ViewGridLayout (this);
maTextLabel = aLayout.AddLabeledString ("Text: ");
maCharacterArrayLabel = aLayout.AddLabeledEntry ("Characters: ");
maCharacterCountLabel = aLayout.AddLabeledEntry ("Character Count: ");
maSelectionLabel = aLayout.AddLabeledEntry ("Selection: ");
maBoundsLabel = aLayout.AddLabeledEntry ("Bounds Test: ");
maCaretPositionSpinner = (JSpinner)aLayout.AddLabeledComponent (
"Caret position:", new JSpinner());
Dimension aSize = maCaretPositionSpinner.getSize();
maCaretPositionSpinner.setPreferredSize (new Dimension (100,20));
maCaretLineNoLabel = aLayout.AddLabeledEntry ("Line number at caret: ");
maCaretLineTextLabel = aLayout.AddLabeledEntry ("Text of line at caret: ");
maLineNoFromCaretPosLabel = aLayout.AddLabeledEntry ("Line number at index of caret: ");
maLineTextFromCaretPosLabel = aLayout.AddLabeledEntry ("Text of line at index of caret: ");
JPanel aButtonPanel = new JPanel ();
aLayout.AddComponent (aButtonPanel);
JButton aButton = new JButton ("select...");
aButton.setFont (aLayout.GetFont());
aButton.addActionListener (this);
aButtonPanel.add (aButton);
aButton = new JButton ("copy...");
aButton.setFont (aLayout.GetFont());
aButton.addActionListener (this);
aButtonPanel.add (aButton);
// A tree that holds the text broken down into various segments.
maTree = new JTree ();
aLayout.AddComponent (new JScrollPane (
maTree,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED));
}
/** Additionally to the context store a reference to the
XAccessibleText interface.
*/
public void SetObject (XAccessibleContext xObject)
{
mxText = (XAccessibleText)UnoRuntime.queryInterface(
XAccessibleText.class, xObject);
maCaretSpinnerModel = new CaretSpinnerModel(mxText);
maCaretPositionSpinner.setModel (maCaretSpinnerModel);
super.SetObject (xObject);
}
synchronized public void Destroy ()
{
mxText = null;
super.Destroy();
}
synchronized public void Update ()
{
maCaretPositionSpinner.setEnabled (mxText != null);
DefaultMutableTreeNode aRoot = new DefaultMutableTreeNode ("Text Segments");
if (mxText == null)
{
maTextLabel.setText ("<null object>");
maCharacterArrayLabel.setText ("<null object>");
maCharacterCountLabel.setText ("<null object>");
maSelectionLabel.setText ("<null object>");
maBoundsLabel.setText ("<null object>");
maCaretLineNoLabel.setText ("<null object>");
maCaretLineTextLabel.setText ("<null object>");
maLineNoFromCaretPosLabel.setText ("<null object>");
maLineTextFromCaretPosLabel.setText ("<null object>");
}
else
{
maTextLabel.setText (mxText.getText());
maCharacterArrayLabel.setText (GetCharacterArray());
maCharacterCountLabel.setText (
Integer.toString(mxText.getCharacterCount()));
// Selection.
maSelectionLabel.setText (
"[" + mxText.getSelectionStart()
+ "," + mxText.getSelectionEnd()
+ "] \"" + mxText.getSelectedText() + "\"");
// Character bounds.
maBoundsLabel.setText (GetTextBoundsString());
// Caret position.
maCaretPositionSpinner.setValue (new Integer (mxText.getCaretPosition()));
// Multi line methods.
XAccessibleMultiLineText xMultiText = (XAccessibleMultiLineText)
UnoRuntime.queryInterface( XAccessibleMultiLineText.class, mxText );
if( null != xMultiText ) {
try {
maCaretLineNoLabel.setText ( Integer.toString( xMultiText.getNumberOfLineWithCaret() ) );
TextSegment ts = xMultiText.getTextAtLineWithCaret();
maCaretLineTextLabel.setText ( "[" + ts.SegmentStart
+ "," + ts.SegmentEnd
+ "] \"" + ts.SegmentText + "\"");
maLineNoFromCaretPosLabel.setText ( Integer.toString( xMultiText.getLineNumberAtIndex( mxText.getCaretPosition() ) ) );
ts = xMultiText.getTextAtLineNumber(xMultiText.getLineNumberAtIndex( mxText.getCaretPosition() ) );
maLineTextFromCaretPosLabel.setText ( "[" + ts.SegmentStart
+ "," + ts.SegmentEnd
+ "] \"" + ts.SegmentText + "\"");
} catch( IndexOutOfBoundsException e) {
}
}
// Text segments.
aRoot.add (CreateNode ("Character", AccessibleTextType.CHARACTER));
aRoot.add (CreateNode ("Word", AccessibleTextType.WORD));
aRoot.add (CreateNode ("Sentence", AccessibleTextType.SENTENCE));
aRoot.add (CreateNode ("Paragraph", AccessibleTextType.PARAGRAPH));
aRoot.add (CreateNode ("Line", AccessibleTextType.LINE));
aRoot.add (CreateNode ("Attribute", AccessibleTextType.ATTRIBUTE_RUN));
aRoot.add (CreateNode ("Glyph", AccessibleTextType.GLYPH));
}
((DefaultTreeModel)maTree.getModel()).setRoot (aRoot);
}
public String GetTitle ()
{
return ("Text");
}
public void notifyEvent (AccessibleEventObject aEvent)
{
System.out.println (aEvent);
switch (aEvent.EventId)
{
case AccessibleEventId.CARET_CHANGED :
maCaretSpinnerModel.Update();
Update ();
break;
case AccessibleEventId.TEXT_CHANGED :
case AccessibleEventId.TEXT_SELECTION_CHANGED:
Update ();
break;
}
}
public void actionPerformed (ActionEvent aEvent)
{
String sCommand = aEvent.getActionCommand();
if (sCommand.equals ("select..."))
TextDialogFactory.CreateSelectionDialog (mxContext);
else if (sCommand.equals ("copy..."))
TextDialogFactory.CreateCopyDialog (mxContext);
}
/** Create a string that is a list of all characters returned by the
getCharacter() method.
*/
private String GetCharacterArray ()
{
// Do not show more than 30 characters.
int nCharacterCount = mxText.getCharacterCount();
int nMaxDisplayCount = 30;
// build up string
StringBuffer aCharacterArray = new StringBuffer();
int nIndex = 0;
try
{
while (nIndex<nCharacterCount && nIndex<nMaxDisplayCount)
{
aCharacterArray.append (mxText.getCharacter (nIndex));
if (nIndex < nCharacterCount-1)
aCharacterArray.append (",");
nIndex ++;
}
if (nMaxDisplayCount < nCharacterCount)
aCharacterArray.append (", ...");
}
catch (IndexOutOfBoundsException e)
{
aCharacterArray.append ("; Index Out Of Bounds at index " + nIndex);
}
return aCharacterArray.toString();
}
/** Iterate over all characters and translate their positions
back and forth.
*/
private String GetTextBoundsString ()
{
StringBuffer aBuffer = new StringBuffer ();
try
{
// Iterate over all characters in the text.
int nCount = mxText.getCharacterCount();
for (int i=0; i<nCount; i++)
{
// Get bounds for this character.
Rectangle aBBox = mxText.getCharacterBounds (i);
// get the character by 'clicking' into the middle of
// the bounds
Point aMiddle = new Point();
aMiddle.X = aBBox.X + (aBBox.Width / 2) - 1;
aMiddle.Y = aBBox.Y + (aBBox.Height / 2) - 1;
int nIndex = mxText.getIndexAtPoint (aMiddle);
// get the character, or a '#' for an illegal index
if ((nIndex >= 0) && (nIndex < mxText.getCharacter(i)))
aBuffer.append (mxText.getCharacter(nIndex));
else
aBuffer.append ('#');
}
}
catch (IndexOutOfBoundsException aEvent)
{
// Ignore errors.
}
return aBuffer.toString();
}
private final static int BEFORE = -1;
private final static int AT = 0;
private final static int BEHIND = +1;
private MutableTreeNode CreateNode (String sTitle, short nTextType)
{
DefaultMutableTreeNode aNode = new DefaultMutableTreeNode (sTitle);
aNode.add (CreateSegmentNode ("Before", nTextType, BEFORE));
aNode.add (CreateSegmentNode ("At", nTextType, AT));
aNode.add (CreateSegmentNode ("Behind", nTextType, BEHIND));
return aNode;
}
private MutableTreeNode CreateSegmentNode (String sTitle, short nTextType, int nWhere)
{
TextSegment aSegment;
int nTextLength = mxText.getCharacterCount();
DefaultMutableTreeNode aNode = new DefaultMutableTreeNode (sTitle);
for (int nIndex=0; nIndex<=nTextLength; /* empty */)
{
aSegment = GetTextSegment (nIndex, nTextType, nWhere);
DefaultMutableTreeNode aSegmentNode = new DefaultMutableTreeNode (
new StringBuffer (
Integer.toString (nIndex) + " -> "
+ Integer.toString (aSegment.SegmentStart) + " - "
+ Integer.toString (aSegment.SegmentEnd) + " : "
+ aSegment.SegmentText.toString()));
aNode.add (aSegmentNode);
if (nTextType == AccessibleTextType.ATTRIBUTE_RUN)
AddAttributeNodes (aSegmentNode, aSegment);
if (aSegment.SegmentEnd > nIndex)
nIndex = aSegment.SegmentEnd;
else
nIndex ++;
}
return aNode;
}
private TextSegment GetTextSegment (int nIndex, short nTextType, int nWhere)
{
TextSegment aSegment;
try
{
switch (nWhere)
{
case BEFORE:
aSegment = mxText.getTextBeforeIndex (nIndex, nTextType);
break;
case AT:
aSegment = mxText.getTextAtIndex (nIndex, nTextType);
break;
case BEHIND:
aSegment = mxText.getTextBehindIndex (nIndex, nTextType);
break;
default:
aSegment = new TextSegment();
aSegment.SegmentText = new String ("unknown position " + nWhere);
aSegment.SegmentStart = nIndex;
aSegment.SegmentStart = nIndex+1;
break;
}
}
catch (IndexOutOfBoundsException aException)
{
aSegment = new TextSegment ();
aSegment.SegmentText = new String ("Invalid index at ") + nIndex + " : "
+ aException.toString();
aSegment.SegmentStart = nIndex;
aSegment.SegmentEnd = nIndex+1;
}
catch (IllegalArgumentException aException)
{
aSegment = new TextSegment ();
aSegment.SegmentText = new String ("Illegal argument at ") + nIndex + " : "
+ aException.toString();
aSegment.SegmentStart = nIndex;
aSegment.SegmentEnd = nIndex+1;
}
return aSegment;
}
/** Add to the given node one node for every attribute of the given segment.
*/
private void AddAttributeNodes (
DefaultMutableTreeNode aNode,
TextSegment aSegment)
{
try
{
PropertyValue[] aValues = mxText.getCharacterAttributes (
aSegment.SegmentStart, aAttributeList);
for (int i=0; i<aValues.length; i++)
aNode.add (new DefaultMutableTreeNode (
aValues[i].Name + ": " + aValues[i].Value));
}
catch (IndexOutOfBoundsException aException)
{
aNode.add (new DefaultMutableTreeNode (
"caught IndexOutOfBoundsException while retrieveing attributes"));
}
}
private XAccessibleText mxText;
private JLabel
maTextLabel,
maCharacterArrayLabel,
maCharacterCountLabel,
maSelectionLabel,
maBoundsLabel,
maCaretLineNoLabel,
maCaretLineTextLabel,
maLineNoFromCaretPosLabel,
maLineTextFromCaretPosLabel;
private JSpinner maCaretPositionSpinner;
private JTree maTree;
private CaretSpinnerModel maCaretSpinnerModel;
private static String[] aAttributeList = new String[] {
"CharBackColor",
"CharColor",
"CharEscapement",
"CharHeight",
"CharPosture",
"CharStrikeout",
"CharUnderline",
"CharWeight",
"ParaAdjust",
"ParaBottomMargin",
"ParaFirstLineIndent",
"ParaLeftMargin",
"ParaLineSpacing",
"ParaRightMargin",
"ParaTabStops"};
}