forked from amazingfate/loongoffice
329 lines
11 KiB
Java
Executable File
329 lines
11 KiB
Java
Executable File
import drafts.com.sun.star.accessibility.XAccessible;
|
|
import drafts.com.sun.star.accessibility.XAccessibleContext;
|
|
import drafts.com.sun.star.accessibility.XAccessibleComponent;
|
|
import drafts.com.sun.star.accessibility.XAccessibleExtendedComponent;
|
|
import drafts.com.sun.star.accessibility.XAccessibleAction;
|
|
import drafts.com.sun.star.accessibility.XAccessibleImage;
|
|
import drafts.com.sun.star.accessibility.XAccessibleRelationSet;
|
|
import drafts.com.sun.star.accessibility.XAccessibleStateSet;
|
|
import drafts.com.sun.star.accessibility.XAccessibleText;
|
|
import drafts.com.sun.star.accessibility.XAccessibleEditableText;
|
|
import drafts.com.sun.star.accessibility.AccessibleTextType;
|
|
import drafts.com.sun.star.accessibility.XAccessibleEventListener;
|
|
import drafts.com.sun.star.accessibility.XAccessibleEventBroadcaster;
|
|
import drafts.com.sun.star.accessibility.AccessibleEventObject;
|
|
import drafts.com.sun.star.accessibility.AccessibleEventId;
|
|
|
|
import com.sun.star.lang.XServiceInfo;
|
|
import com.sun.star.lang.IndexOutOfBoundsException;
|
|
import com.sun.star.uno.UnoRuntime;
|
|
|
|
import java.util.Vector;
|
|
import java.awt.*;
|
|
import java.awt.event.*;
|
|
import javax.swing.*;
|
|
import javax.swing.tree.*;
|
|
import javax.swing.event.*;
|
|
|
|
// JDK 1.4.
|
|
// import java.util.regex.Pattern;
|
|
// import java.util.regex.Matcher;
|
|
|
|
|
|
|
|
/** This class is a start to collect the handling of a JTree and a DefaultTreeModel.
|
|
*/
|
|
public class AccessibilityTree
|
|
extends JTree
|
|
{
|
|
/** Create a new accessibility tree. Use the specified message display
|
|
for displaying messages and the specified canvas to draw the
|
|
graphical representations of accessible objects on.
|
|
*/
|
|
public AccessibilityTree (
|
|
MessageInterface aMessageDisplay,
|
|
Canvas aCanvas)
|
|
{
|
|
maMessageDisplay = aMessageDisplay;
|
|
maCanvas = aCanvas;
|
|
|
|
setModel( new AccessibilityTreeModel( "Please press Update button" ) );
|
|
|
|
maCellRenderer = new AccessibleTreeCellRenderer();
|
|
// setCellRenderer (maCellRenderer);
|
|
|
|
// allow editing of XAccessibleText interfaces
|
|
// setEditable (true);
|
|
// maTreeModel.addTreeModelListener( new TextUpdateListener() );
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Predicate class to determine whether a node should be expanded
|
|
* For use with expandTree method */
|
|
abstract class Expander
|
|
{ abstract public boolean expand(Object aObject); }
|
|
|
|
/** expand all nodes */
|
|
class AllExpander extends Expander
|
|
{
|
|
public boolean expand(Object aObject) { return true; }
|
|
}
|
|
|
|
/** expand all nodes with accessibility roles > 100 */
|
|
class ShapeExpander extends Expander
|
|
{
|
|
public boolean expand(Object aObject)
|
|
{
|
|
aObject = AccessibilityTreeModel.normalize( aObject );
|
|
XAccessibleContext xContext =
|
|
(XAccessibleContext)UnoRuntime.queryInterface(
|
|
XAccessibleContext.class, aObject );
|
|
int nRole = ( xContext == null ) ? -1 :
|
|
xContext.getAccessibleRole();
|
|
return (nRole >= 100);
|
|
}
|
|
}
|
|
|
|
private TreePath expandTree( TreePath aPath, Expander aExpander )
|
|
{
|
|
// return first expanded object
|
|
TreePath aFirst = null;
|
|
|
|
// get 'our' object
|
|
Object aObj = aPath.getLastPathComponent();
|
|
|
|
// expand this object, if the Expander tells us so
|
|
if( aExpander.expand( aObj ) )
|
|
{
|
|
expandPath (aPath);
|
|
if( aFirst == null )
|
|
aFirst = aPath;
|
|
}
|
|
|
|
// visit all children
|
|
if( aObj instanceof AccTreeNode )
|
|
{
|
|
AccTreeNode aNode = (AccTreeNode)aObj;
|
|
int nLength = aNode.getChildCount();
|
|
for( int i = 0; i < nLength; i++ )
|
|
{
|
|
TreePath aRet = expandTree(
|
|
aPath.pathByAddingChild( aNode.getChild( i ) ),
|
|
aExpander );
|
|
if( aFirst == null )
|
|
aFirst = aRet;
|
|
}
|
|
}
|
|
|
|
return aFirst;
|
|
}
|
|
|
|
|
|
/** Expand all nodes and their subtrees that represent shapes. Call
|
|
* this method from the outside. */
|
|
public void expandShapes ()
|
|
{
|
|
message ("Expanding shapes.");
|
|
|
|
TreePath aFirst = expandTree( new TreePath( getModel().getRoot() ),
|
|
new ShapeExpander() );
|
|
if( aFirst != null )
|
|
{
|
|
makeVisible (aFirst);
|
|
}
|
|
}
|
|
|
|
/** Expand all nodes */
|
|
public void expandAll ()
|
|
{
|
|
message ("Expanding complete tree");
|
|
expandTree( new TreePath( getModel().getRoot() ), new AllExpander() );
|
|
}
|
|
|
|
|
|
|
|
protected void message (String message)
|
|
{
|
|
maMessageDisplay.message (message);
|
|
}
|
|
|
|
public void disposing (com.sun.star.lang.EventObject e)
|
|
{
|
|
System.out.println ("disposing " + e);
|
|
}
|
|
|
|
|
|
|
|
|
|
/** listen to tree model changes in order to update XAccessibleText objects
|
|
*/
|
|
class TextUpdateListener implements TreeModelListener
|
|
{
|
|
public void treeNodesChanged(TreeModelEvent e)
|
|
{
|
|
// if the change is to the first child of a DefaultMutableTreeNode
|
|
// with an XAccessibleText child, then we call updateText
|
|
int[] aIndices = e.getChildIndices();
|
|
if( (aIndices != null) &&
|
|
(aIndices.length > 0) )
|
|
{
|
|
// we have a parent... lets check for XAccessibleText then
|
|
DefaultMutableTreeNode aParent = (DefaultMutableTreeNode)
|
|
(e.getTreePath().getLastPathComponent());
|
|
DefaultMutableTreeNode aNode = (DefaultMutableTreeNode)
|
|
(aParent.getChildAt(aIndices[0]));
|
|
if( aParent.getUserObject() instanceof XAccessibleText)
|
|
{
|
|
// aha! we have an xText. So we can now check for
|
|
// the various cases we support
|
|
XAccessibleText xText =
|
|
(XAccessibleText)aParent.getUserObject();
|
|
|
|
if( aIndices[0] == 0 )
|
|
{
|
|
// first child! Then we call updateText
|
|
updateText( xText, aNode.toString() );
|
|
}
|
|
else
|
|
{
|
|
// JDK 1.4:
|
|
// // check for pattern "Selection:"
|
|
// Matcher m = Pattern.compile(
|
|
// "selection: \\[(-?[0-9]+),(-?[0-9]+)\\] \".*" ).
|
|
// matcher( aNode.toString() );
|
|
// if( m.matches() )
|
|
// {
|
|
// try
|
|
// {
|
|
// // aha! Selection:
|
|
// setSelection( xText,
|
|
// Integer.parseInt(m.group(1)),
|
|
// Integer.parseInt(m.group(2)) );
|
|
// }
|
|
// catch( NumberFormatException f )
|
|
// {
|
|
// // ignore
|
|
// }
|
|
// }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// don't care:
|
|
public void treeNodesInserted(TreeModelEvent e) { ; }
|
|
public void treeNodesRemoved(TreeModelEvent e) { ; }
|
|
public void treeStructureChanged(TreeModelEvent e) { ; }
|
|
|
|
/** update the text */
|
|
boolean updateText( XAccessibleText xText, String sNew )
|
|
{
|
|
// is this text editable? if not, fudge you and return
|
|
XAccessibleEditableText xEdit =
|
|
(XAccessibleEditableText) UnoRuntime.queryInterface (
|
|
XAccessibleEditableText.class, xText);
|
|
if (xEdit == null)
|
|
return false;
|
|
|
|
String sOld = xText.getText();
|
|
|
|
// false alarm? Early out if no change was done!
|
|
if( sOld.equals( sNew ) )
|
|
return false;
|
|
|
|
// get the minimum length of both strings
|
|
int nMinLength = sOld.length();
|
|
if( sNew.length() < nMinLength )
|
|
nMinLength = sNew.length();
|
|
|
|
// count equal characters from front and end
|
|
int nFront = 0;
|
|
while( (nFront < nMinLength) &&
|
|
(sNew.charAt(nFront) == sOld.charAt(nFront)) )
|
|
nFront++;
|
|
int nBack = 0;
|
|
while( (nBack < nMinLength) &&
|
|
( sNew.charAt(sNew.length()-nBack-1) ==
|
|
sOld.charAt(sOld.length()-nBack-1) ) )
|
|
nBack++;
|
|
if( nFront + nBack > nMinLength )
|
|
nBack = nMinLength - nFront;
|
|
|
|
// so... the first nFront and the last nBack characters
|
|
// are the same. Change the others!
|
|
String sDel = sOld.substring( nFront, sOld.length() - nBack );
|
|
String sIns = sNew.substring( nFront, sNew.length() - nBack );
|
|
|
|
System.out.println("edit text: " +
|
|
sOld.substring(0, nFront) +
|
|
" [ " + sDel + " -> " + sIns + " ] " +
|
|
sOld.substring(sOld.length() - nBack) );
|
|
|
|
boolean bRet = false;
|
|
try
|
|
{
|
|
// edit the text, and use
|
|
// (set|insert|delete|replace)Text as needed
|
|
if( nFront+nBack == 0 )
|
|
bRet = xEdit.setText( sIns );
|
|
else if( sDel.length() == 0 )
|
|
bRet = xEdit.insertText( sIns, nFront );
|
|
else if( sIns.length() == 0 )
|
|
bRet = xEdit.deleteText( nFront, sOld.length()-nBack );
|
|
else
|
|
bRet = xEdit.replaceText(nFront, sOld.length()-nBack,sIns);
|
|
}
|
|
catch( IndexOutOfBoundsException e )
|
|
{
|
|
bRet = false;
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
boolean setSelection( XAccessibleText xText, int p1, int p2 )
|
|
{
|
|
try
|
|
{
|
|
return xText.setSelection( p1, p2 );
|
|
}
|
|
catch( com.sun.star.lang.IndexOutOfBoundsException f )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// /** replace the given node with a new xText node */
|
|
// void updateNode( XAccessibleText xText,
|
|
// DefaultMutableTreeNode aNode )
|
|
// {
|
|
// // create a new node
|
|
// DefaultMutableTreeNode aNew = newTextTreeNode( xText );
|
|
//
|
|
// // get parent (must be DefaultMutableTreeNode)
|
|
// DefaultMutableTreeNode aParent =
|
|
// (DefaultMutableTreeNode)aNode.getParent();
|
|
// if( aParent != null )
|
|
// {
|
|
// // remove old sub-tree, and insert new one
|
|
// int nIndex = aParent.getIndex( aNode );
|
|
// aParent.remove( nIndex );
|
|
// aParent.insert( aNew, nIndex );
|
|
// }
|
|
// }
|
|
}
|
|
|
|
|
|
protected MessageInterface
|
|
maMessageDisplay;
|
|
protected AccessibleTreeCellRenderer
|
|
maCellRenderer;
|
|
|
|
private Canvas
|
|
maCanvas;
|
|
private boolean
|
|
mbFirstShapeSeen;
|
|
}
|