diff options
Diffstat (limited to '')
57 files changed, 8551 insertions, 0 deletions
diff --git a/toolkit/test/accessibility/AWB.sxw b/toolkit/test/accessibility/AWB.sxw Binary files differnew file mode 100644 index 000000000..57931aed1 --- /dev/null +++ b/toolkit/test/accessibility/AWB.sxw diff --git a/toolkit/test/accessibility/AccTreeNode.java b/toolkit/test/accessibility/AccTreeNode.java new file mode 100644 index 000000000..1ce467684 --- /dev/null +++ b/toolkit/test/accessibility/AccTreeNode.java @@ -0,0 +1,346 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import java.util.ArrayList; +import java.util.Arrays; + +import com.sun.star.accessibility.XAccessible; +import com.sun.star.accessibility.XAccessibleComponent; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.XAccessibleEditableText; +import com.sun.star.accessibility.XAccessibleSelection; +import com.sun.star.accessibility.XAccessibleTable; +import com.sun.star.accessibility.XAccessibleText; +import com.sun.star.uno.UnoRuntime; + +/** + * The node type for the AccessibleTreeModel. + * This implements all the child-handling based on the appropriate + * NodeHandlers. Trivial nodes can be implemented by any Object + * type. + */ +class AccTreeNode + extends AccessibleTreeNode +{ + private class HandlerDescriptor + { + private HandlerDescriptor (NodeHandler aHandler) + { + maHandler = aHandler; + mnChildCount = -1; + } + private NodeHandler maHandler; + private int mnChildCount; + } + /// NodeHandlers for this node + private ArrayList<HandlerDescriptor> maHandlers; + + // The accessible context of this node. + private XAccessibleContext mxContext; + private XAccessibleComponent mxComponent; + private XAccessibleText mxText; + private XAccessibleTable mxTable; + + public AccTreeNode (XAccessibleContext xContext, Object aDisplay, AccessibleTreeNode aParent) + { + super (aDisplay, aParent); + + maHandlers = new ArrayList<HandlerDescriptor>(5); + mxContext = xContext; + } + + /** Update the internal data extracted from the corresponding accessible + object. This is done by replacing every handler by a new one. An + update method at each handler would be better of course. + */ + @Override + public void update () + { + for (int i=0; i<maHandlers.size(); i++) + { + System.out.println ("replacing handler " + i); + HandlerDescriptor aDescriptor = maHandlers.get(i); + aDescriptor.maHandler = aDescriptor.maHandler.createHandler (mxContext); + aDescriptor.mnChildCount = + aDescriptor.maHandler.getChildCount (); + } + } + + public XAccessibleContext getContext () + { + return mxContext; + } + + public XAccessibleComponent getComponent () + { + if (mxComponent == null && mxContext != null) + mxComponent = UnoRuntime.queryInterface( + XAccessibleComponent.class, mxContext); + return mxComponent; + } + + public XAccessibleText getText () + { + if (mxText == null && mxContext != null) + mxText = UnoRuntime.queryInterface( + XAccessibleText.class, mxContext); + return mxText; + } + + public XAccessibleEditableText getEditText () + { + return UnoRuntime.queryInterface( + XAccessibleEditableText.class, mxContext); + } + + public XAccessibleTable getTable () + { + if (mxTable == null && mxContext != null) + mxTable = UnoRuntime.queryInterface( + XAccessibleTable.class, mxContext); + return mxTable; + } + + + public XAccessibleSelection getSelection () + { + return UnoRuntime.queryInterface( + XAccessibleSelection.class, mxContext); + } + + public void addHandler( NodeHandler aHandler ) + { + if (aHandler != null) + maHandlers.add (new HandlerDescriptor (aHandler)); + } + + + /** iterate over handlers and return child sum */ + private HandlerDescriptor getHandlerDescriptor (int i) + { + HandlerDescriptor aDescriptor = maHandlers.get(i); + if (aDescriptor.mnChildCount < 0) + aDescriptor.mnChildCount = + aDescriptor.maHandler.getChildCount (); + return aDescriptor; + } + + @Override + public int getChildCount() + { + int nChildCount = 0; + for (int i = 0; i < maHandlers.size(); i++) + { + HandlerDescriptor aDescriptor = getHandlerDescriptor (i); + nChildCount += aDescriptor.mnChildCount; + } + return nChildCount; + } + + /** iterate over handlers until the child is found */ + @Override + public AccessibleTreeNode getChild (int nIndex) + throws IndexOutOfBoundsException + { + if( nIndex >= 0 ) + { + for(int i = 0; i < maHandlers.size(); i++) + { + // check if this handler has the child, and if not + // search with next handler + HandlerDescriptor aDescriptor = getHandlerDescriptor (i); + if (nIndex < aDescriptor.mnChildCount) + return aDescriptor.maHandler.getChild (this, nIndex); + else + nIndex -= aDescriptor.mnChildCount; + } + } + else + throw new IndexOutOfBoundsException(); + + // nothing found? + return null; + } + + @Override + public AccessibleTreeNode getChildNoCreate (int nIndex) + throws IndexOutOfBoundsException + { + if( nIndex >= 0 ) + { + for(int i = 0; i < maHandlers.size(); i++) + { + // check if this handler has the child, and if not + // search with next handler + HandlerDescriptor aDescriptor = getHandlerDescriptor (i); + if (nIndex < aDescriptor.mnChildCount) + return aDescriptor.maHandler.getChildNoCreate (nIndex); + else + nIndex -= aDescriptor.mnChildCount; + } + } + else + throw new IndexOutOfBoundsException(); + + // nothing found? + return null; + } + + @Override + public boolean removeChild (int nIndex) + throws IndexOutOfBoundsException + { + boolean bStatus = false; + if (nIndex >= 0) + { + for (int i=0; i<maHandlers.size(); i++) + { + // check if this handler has the child, and if not + // search with next handler + HandlerDescriptor aDescriptor = getHandlerDescriptor (i); + if (nIndex < aDescriptor.mnChildCount) + { + bStatus = aDescriptor.maHandler.removeChild (nIndex); + aDescriptor.mnChildCount = aDescriptor.maHandler.getChildCount (); + break; + } + else + nIndex -= aDescriptor.mnChildCount; + } + } + else + throw new IndexOutOfBoundsException(); + + return bStatus; + } + + + @Override + public int indexOf (AccessibleTreeNode aNode) + { + int nBaseIndex = 0; + if (aNode != null) + { + for (int i=0; i<maHandlers.size(); i++) + { + HandlerDescriptor aDescriptor = getHandlerDescriptor (i); + int nIndex = aDescriptor.maHandler.indexOf (aNode); + if (nIndex >= 0) + return nBaseIndex + nIndex; + else + nBaseIndex += aDescriptor.mnChildCount; + } + } + + return -1; + } + + /** this node is a leaf if have no handlers, or is those + handlers show no children */ + @Override + public boolean isLeaf() + { + return maHandlers.isEmpty(); + } + + @Override + public boolean equals (Object aOther) + { + return (this == aOther) || (aOther!=null && aOther.equals(mxContext)); + } + + + /** iterate over handlers until the child is found */ + public void getActions(java.util.List<String> aActions) + { + for(int i = 0; i < maHandlers.size(); i++) + { + HandlerDescriptor aDescriptor = getHandlerDescriptor (i); + NodeHandler aHandler = aDescriptor.maHandler; + String[] aHandlerActions = aHandler.getActions (this); + aActions.addAll(Arrays.asList(aHandlerActions)); + } + } + + @Override + public void performAction( int nIndex ) + { + if( nIndex >= 0 ) + { + for(int i = 0; i < maHandlers.size(); i++) + { + // check if this handler has the child, and if not + // search with next handler + HandlerDescriptor aDescriptor = getHandlerDescriptor (i); + NodeHandler aHandler = aDescriptor.maHandler; + int nCount = aHandler.getActions(this).length; + if( nCount > nIndex ) + { + aHandler.performAction(this, nIndex ); + return; + } + else + nIndex -= nCount; + } + } + } + + /** Try to add the specified accessible object as new accessible child of the + AccessibleTreeHandler. + Note that child is used in another context than + it is used in the other methods of this class. + */ + public AccessibleTreeNode addAccessibleChild (XAccessible xChild) + { + for(int i = 0; i < maHandlers.size(); i++) + { + HandlerDescriptor aDescriptor = getHandlerDescriptor (i); + if (aDescriptor.maHandler instanceof AccessibleTreeHandler) + { + AccessibleTreeHandler aHandler = (AccessibleTreeHandler)aDescriptor.maHandler; + AccessibleTreeNode aNode = aHandler.addAccessibleChild (this, xChild); + aDescriptor.mnChildCount = aHandler.getChildCount (); + return aNode; + } + } + return null; + } + + public java.util.List<Integer> updateChildren (java.lang.Class class1, java.lang.Class<AccessibleExtendedComponentHandler> class2) + { + ArrayList<Integer> aChildIndices = new ArrayList<Integer>(); + int nOffset = 0; + for(int i=0; i < maHandlers.size(); i++) + { + HandlerDescriptor aDescriptor = getHandlerDescriptor (i); + if ((class1.isInstance(aDescriptor.maHandler)) + || (class2 !=null && class2.isInstance(aDescriptor.maHandler))) + { + aDescriptor.maHandler.update(this); + // Get updated number of children. + int nChildCount = aDescriptor.maHandler.getChildCount (); + aDescriptor.mnChildCount = nChildCount; + // Fill in the indices of the updated children. + for (int j=0; j<nChildCount; j++) + aChildIndices.add(j+nOffset); + } + nOffset += aDescriptor.mnChildCount; + } + return aChildIndices; + } +} diff --git a/toolkit/test/accessibility/AccessibilityTree.java b/toolkit/test/accessibility/AccessibilityTree.java new file mode 100644 index 000000000..fdf44177a --- /dev/null +++ b/toolkit/test/accessibility/AccessibilityTree.java @@ -0,0 +1,379 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import java.awt.Cursor; +import java.awt.event.ActionEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; + +import javax.swing.AbstractAction; +import javax.swing.JPopupMenu; +import javax.swing.JTree; +import javax.swing.event.TreeExpansionEvent; +import javax.swing.event.TreeExpansionListener; +import javax.swing.event.TreeWillExpandListener; +import javax.swing.tree.TreePath; + +import com.sun.star.accessibility.XAccessibleContext; + + + +/** This is the tree component that is responsible for displaying the + contents of the tree model on the screen. +*/ +public class AccessibilityTree + implements TreeExpansionListener, TreeWillExpandListener +{ + /** 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 () + { + maTree = new JTree (); + + AccessibilityTreeModel aModel = + new AccessibilityTreeModel ( + new StringNode ("Please press Update button", null)); + maTree.setModel (aModel); + + maTree.addMouseListener (new MouseListener (this)); + + // Listen to expansions and collapses to change the mouse cursor. + mnExpandLevel = 0; + maTree.addTreeWillExpandListener (this); + maTree.addTreeExpansionListener (this); + } + + public JTree getComponent () + { + return maTree; + } + + // Change cursor during expansions to show the user that this is a + // lengthy operation. + public void treeWillExpand (TreeExpansionEvent e) + { + if (mnExpandLevel == 0) + { + maTree.setCursor (new Cursor (Cursor.WAIT_CURSOR)); + } + mnExpandLevel += 1; + } + public void treeWillCollapse (TreeExpansionEvent e) + { + if (mnExpandLevel == 0) + { + maTree.setCursor (new Cursor (Cursor.WAIT_CURSOR)); + } + mnExpandLevel += 1; + } + public void treeExpanded (TreeExpansionEvent e) + { + mnExpandLevel -= 1; + if (mnExpandLevel == 0) + { + maTree.setCursor (new Cursor (Cursor.DEFAULT_CURSOR)); + } + } + public void treeCollapsed (TreeExpansionEvent e) + { + mnExpandLevel -= 1; + if (mnExpandLevel == 0) + { + maTree.setCursor (new Cursor (Cursor.DEFAULT_CURSOR)); + } + } + + + + public void SetCanvas (Canvas aCanvas) + { + ((AccessibilityTreeModel)maTree.getModel()).setCanvas(aCanvas); + } + + /** Expand the nodes in the subtree rooted in aNode according to the + specified expander. The tree is locked during the expansion. + */ + private void expandTree (AccessibleTreeNode aNode, Expander aExpander) + { + if (mnExpandLevel == 0) + { + maTree.setEnabled (false); + } + mnExpandLevel += 1; + + ((AccessibilityTreeModel)maTree.getModel()).lock (); + + try + { + expandTree (new TreePath (aNode.createPath()), aExpander); + } + catch (Exception e) + { + // Ignore + } + + mnExpandLevel -= 1; + if (mnExpandLevel == 0) + { + maTree.setEnabled (true); + ((AccessibilityTreeModel)maTree.getModel()).unlock (aNode); + } + } + + private TreePath expandTree( TreePath aPath, Expander aExpander ) + { + // return first expanded object + TreePath aFirst = null; + + try + { + // get 'our' object + Object aObj = aPath.getLastPathComponent(); + + // expand this object, if the Expander tells us so + if( aExpander.expand( aObj ) ) + { + maTree.expandPath (aPath); + aFirst = aPath; + } + + // visit all children + if (aObj instanceof AccessibleTreeNode) + { + AccessibleTreeNode aNode = (AccessibleTreeNode)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; + } + } + } + catch (Exception e) + { + System.out.println ("caught exception while expanding tree path " + + aPath + ": " + e); + e.printStackTrace (); + } + + return aFirst; + } + + + /** Expand all nodes and their subtrees that represent shapes. Call + * this method from the outside. */ + public void expandShapes () + { + expandShapes ((AccessibleTreeNode)maTree.getModel().getRoot()); + } + private void expandShapes (AccessibleTreeNode aNode) + { + expandTree (aNode, new ShapeExpander()); + } + + /** Expand all nodes */ + public void expandAll () + { + expandAll ((AccessibleTreeNode)maTree.getModel().getRoot()); + } + private void expandAll (AccessibleTreeNode aNode) + { + expandTree (aNode, new AllExpander()); + } + + + + /* + public Dimension getPreferredSize () + { + Dimension aPreferredSize = super.getPreferredSize(); + Dimension aMinimumSize = super.getMinimumSize(); + if (aPreferredSize.width < aMinimumSize.width) + aPreferredSize.width = aMinimumSize.width; + return aPreferredSize; + } + */ + + private class MouseListener extends MouseAdapter + { + public MouseListener (AccessibilityTree aTree) + { + maTree=aTree; + } + @Override + public void mousePressed(MouseEvent e) { popupTrigger(e); } + @Override + public void mouseClicked(MouseEvent e) { popupTrigger(e); } + @Override + public void mouseEntered(MouseEvent e) { popupTrigger(e); } + @Override + public void mouseExited(MouseEvent e) { popupTrigger(e); } + @Override + public void mouseReleased(MouseEvent e) { popupTrigger(e); } + + private boolean popupTrigger( MouseEvent e ) + { + boolean bIsPopup = e.isPopupTrigger(); + if( !bIsPopup ) + return false; + + int selRow = maTree.getComponent().getRowForLocation(e.getX(), e.getY()); + if (selRow == -1) + return bIsPopup; + + TreePath aPath = maTree.getComponent().getPathForLocation(e.getX(), e.getY()); + + // check for actions + Object aObject = aPath.getLastPathComponent(); + JPopupMenu aMenu = new JPopupMenu(); + if( aObject instanceof AccTreeNode ) + { + AccTreeNode aNode = (AccTreeNode)aObject; + + ArrayList<String> aActions = new ArrayList<String>(); + aMenu.add (new AccessibilityTree.ShapeExpandAction(maTree, aNode)); + aMenu.add (new AccessibilityTree.SubtreeExpandAction(maTree, aNode)); + + aNode.getActions(aActions); + for( int i = 0; i < aActions.size(); i++ ) + { + aMenu.add( new NodeAction( + aActions.get(i), + aNode, i ) ); + } + } + else if (aObject instanceof AccessibleTreeNode) + { + AccessibleTreeNode aNode = (AccessibleTreeNode)aObject; + String[] aActionNames = aNode.getActions(); + int nCount=aActionNames.length; + if (nCount > 0) + { + for (int i=0; i<nCount; i++) + aMenu.add( new NodeAction( + aActionNames[i], + aNode, + i)); + } + else + aMenu = null; + } + if (aMenu != null) + aMenu.show (maTree.getComponent(), + e.getX(), e.getY()); + + return bIsPopup; + } + + private final AccessibilityTree maTree; + } + + private class NodeAction extends AbstractAction + { + private final int mnIndex; + private final AccessibleTreeNode maNode; + + private NodeAction( String aName, AccessibleTreeNode aNode, int nIndex ) + { + super( aName ); + maNode = aNode; + mnIndex = nIndex; + } + + public void actionPerformed(ActionEvent e) + { + maNode.performAction(mnIndex); + } + } + + // This action expands all shapes in the subtree rooted in the specified node. + private class ShapeExpandAction extends AbstractAction + { + private final AccessibilityTree maTree; + private final AccTreeNode maNode; + public ShapeExpandAction (AccessibilityTree aTree, AccTreeNode aNode) + { + super ("Expand Shapes"); + maTree = aTree; + maNode = aNode; + } + public void actionPerformed (ActionEvent e) + { + maTree.expandShapes (maNode); + } + } + + // This action expands all nodes in the subtree rooted in the specified node. + private class SubtreeExpandAction extends AbstractAction + { + private final AccessibilityTree maTree; + private final AccTreeNode maNode; + public SubtreeExpandAction (AccessibilityTree aTree, AccTreeNode aNode) + { + super ("Expand Subtree"); + maTree = aTree; + maNode = aNode; + } + public void actionPerformed (ActionEvent e) + { + maTree.expandAll (maNode); + } + } + + /** Predicate class to determine whether a node should be expanded + * For use with expandTree method */ + private abstract class Expander + { + abstract public boolean expand (Object aObject); + } + + /** expand all nodes */ + private class AllExpander extends Expander + { + @Override + public boolean expand(Object aObject) { return true; } + } + + /** expand all nodes with accessibility roles > 100 */ + private class ShapeExpander extends Expander + { + @Override + public boolean expand (Object aObject) + { + if (aObject instanceof AccTreeNode) + { + AccTreeNode aNode = (AccTreeNode)aObject; + XAccessibleContext xContext = aNode.getContext(); + if (xContext != null && xContext.getAccessibleRole() >= 100) + return true; + } + return false; + } + } + + + + private final JTree maTree; + private int mnExpandLevel; +} diff --git a/toolkit/test/accessibility/AccessibilityTreeModel.java b/toolkit/test/accessibility/AccessibilityTreeModel.java new file mode 100644 index 000000000..b021746bc --- /dev/null +++ b/toolkit/test/accessibility/AccessibilityTreeModel.java @@ -0,0 +1,471 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import java.util.ArrayList; + +import javax.swing.event.TreeModelEvent; +import javax.swing.tree.TreePath; + +import com.sun.star.accessibility.XAccessible; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.XAccessibleEventBroadcaster; +import com.sun.star.accessibility.XAccessibleEventListener; +import com.sun.star.uno.UnoRuntime; + +public class AccessibilityTreeModel + extends AccessibilityTreeModelBase +{ + private boolean mbVerbose = false; + + public AccessibilityTreeModel (AccessibleTreeNode aRoot) + { + // create default node (unless we have a 'proper' node) + setRoot (aRoot); + + maNodeMap = new NodeMap(); + + mxListener = new QueuedListener(new EventListener (this)); + } + + public void clear () + { + maNodeMap.Clear(); + } + + /** Lock the tree. While the tree is locked, events from the outside are + not processed. Lock the tree when you change its internal structure. + */ + public void lock () + { + mnLockCount += 1; + } + + /** Unlock the tree. After unlocking the tree as many times as locking + it, a treeStructureChange event is sent to the event listeners. + @param aNodeHint + If not null and treeStructureChange events are thrown then this + node is used as root of the modified subtree. + */ + public void unlock (AccessibleTreeNode aNodeHint) + { + mnLockCount -= 1; + if (mnLockCount == 0) + fireTreeStructureChanged ( + new TreeModelEvent (this, + new TreePath (aNodeHint.createPath()))); + } + + + @Override + public synchronized void setRoot (AccessibleTreeNode aRoot) + { + if (getRoot() == null) + super.setRoot (aRoot); + else + { + lock (); + maNodeMap.ForEach (new NodeMapCallback () { + @Override + public void Apply (AccTreeNode aNode) + { + if (maCanvas != null) + maCanvas.removeNode (aNode); + removeAccListener (aNode); + } + }); + maNodeMap.Clear (); + + setRoot (aRoot); + unlock (aRoot); + } + } + + + + // child management: + + + + + /** Delegate the request to the parent and then register listeners at + the child and add the child to the canvas. + */ + @Override + public synchronized Object getChild (Object aParent, int nIndex) + { + AccessibleTreeNode aChild = (AccessibleTreeNode)super.getChild (aParent, nIndex); + + if (aChild == null) + System.out.println ("getChild: child not found"); + else + // Keep translation table up-to-date. + addNode (aChild); + + return aChild; + } + + /** Remove a node (and all children) from the tree model. + */ + private boolean removeChild (AccessibleTreeNode aNode) + { + try + { + if( aNode == null ) + { + System.out.println ("can't remove null node"); + return false; + } + else + { + // depth-first removal of children + while (aNode.getChildCount() > 0) { + if ( ! removeChild (aNode.getChildNoCreate (0))) + break; + } + + // Remove node from its parent. + AccessibleTreeNode aParent = aNode.getParent(); + if (aParent != null) + { + int nIndex = aParent.indexOf(aNode); + aParent.removeChild (nIndex); + } + + maNodeMap.RemoveNode (aNode); + } + } + catch (Exception e) + { + System.out.println ("caught exception while removing child " + + aNode + " : " + e); + e.printStackTrace (); + return false; + } + return true; + } + + public void removeNode (XAccessibleContext xNode) + { + if (xNode != null) + { + AccessibleTreeNode aNode = maNodeMap.GetNode (xNode); + AccessibleTreeNode aRootNode = (AccessibleTreeNode)getRoot(); + TreeModelEvent aEvent = createEvent (aRootNode, aNode); + removeChild (aNode); + if (mbVerbose) + System.out.println (aNode); + fireTreeNodesRemoved (aEvent); + maCanvas.repaint (); + } + } + + + /** Add a new child to a parent. + @return + Returns the new or existing representation of the specified + accessible object. + */ + private AccessibleTreeNode addChild (AccTreeNode aParentNode, XAccessible xNewChild) + { + AccessibleTreeNode aChildNode = null; + try + { + // First make sure that the accessible object does not already have + // a representation. + aChildNode = maNodeMap.GetNode(xNewChild); + if (aChildNode == null) + aChildNode = aParentNode.addAccessibleChild (xNewChild); + else + System.out.println ("node already present"); + } + catch (Exception e) + { + System.out.println ("caught exception while adding child " + + xNewChild + " to parent " + aParentNode + ": " + e); + e.printStackTrace (); + } + return aChildNode; + } + + public void addChild (XAccessibleContext xParent, XAccessible xChild) + { + AccessibleTreeNode aParentNode = maNodeMap.GetNode (xParent); + if (aParentNode instanceof AccTreeNode) + { + AccessibleTreeNode aChild = addChild ((AccTreeNode)aParentNode, xChild); + if (addNode (aChild)) + { + if (maCanvas != null) + maCanvas.updateNode ((AccTreeNode)aParentNode); + + // A call to fireTreeNodesInserted for xNew + // should be sufficient but at least the + // StringNode object that contains the number of + // children also changes and we do not know its + // index relative to its parent. Therefore the + // more expensive fireTreeStructureChanged is + // necessary. + fireTreeNodesInserted (createChildUpdateEvent (xParent)); + updateNode (xParent, AccessibleTreeHandler.class); + } + maCanvas.repaint (); + } + } + + + /** Add the child node to the internal tree structure. + @param aNode + The node to insert into the internal tree structure. + */ + private boolean addNode (AccessibleTreeNode aNode) + { + boolean bRet = false; + try + { + if ( ! maNodeMap.ValueIsMember (aNode)) + { + if (aNode instanceof AccTreeNode) + { + AccTreeNode aChild = (AccTreeNode)aNode; + XAccessibleContext xChild = aChild.getContext(); + registerAccListener (aChild); + if (maCanvas != null) + maCanvas.addNode (aChild); + maNodeMap.InsertNode (xChild, aChild); + } + bRet = true; + } + + } + catch (Exception e) + { + System.out.println ("caught exception while adding node " + + aNode + ": " + e); + e.printStackTrace (); + } + return bRet; + } + + + + + /** create path to node, suitable for TreeModelEvent constructor + * @see javax.swing.event.TreeModelEvent#TreeModelEvent + */ + private Object[] createPath (AccessibleTreeNode aNode) + { + ArrayList<AccessibleTreeNode> aPath = new ArrayList<AccessibleTreeNode>(); + aNode.createPath (aPath); + return aPath.toArray(); + } + + + // listeners (and helper methods) + + // We are registered with listeners as soon as objects are in the + // tree cache, and we should get removed as soon as they are out. + + + private void fireTreeNodesChanged(TreeModelEvent e) + { + for(int i = 0; i < maTMListeners.size(); i++) + { + maTMListeners.get(i).treeNodesChanged(e); + } + } + + protected void fireTreeNodesInserted(final TreeModelEvent e) + { + for(int i = 0; i < maTMListeners.size(); i++) + { + maTMListeners.get(i).treeNodesInserted(e); + } + } + + private void fireTreeNodesRemoved(final TreeModelEvent e) + { + for(int i = 0; i < maTMListeners.size(); i++) + { + maTMListeners.get(i).treeNodesRemoved(e); + } + } + + private void fireTreeStructureChanged(final TreeModelEvent e) + { + for(int i = 0; i < maTMListeners.size(); i++) + { + maTMListeners.get(i).treeStructureChanged(e); + } + } + + /** Create a TreeModelEvent object that informs listeners that one child + has been removed from or inserted into its parent. + */ + private TreeModelEvent createChildUpdateEvent (XAccessibleContext xParent) + { + AccessibleTreeNode aParentNode = maNodeMap.GetNode (xParent); + return createEvent (aParentNode, xParent); + } + + private TreeModelEvent createEvent (AccessibleTreeNode aParentNode, XAccessibleContext xChild) + { + AccessibleTreeNode aChildNode = null; + if (xChild != null) + aChildNode = maNodeMap.GetNode (xChild); + return createEvent (aParentNode, aChildNode); + } + + + + protected TreeModelEvent createEvent ( + AccessibleTreeNode aParentNode, + AccessibleTreeNode aChildNode) + { + Object[] aPathToParent = createPath (aParentNode); + + int nIndexInParent = -1; + if (aChildNode != null) + nIndexInParent = aParentNode.indexOf (aChildNode); + if (mbVerbose) + System.out.println (aChildNode + " " + nIndexInParent); + + if (nIndexInParent == -1) + // This event may be passed only to treeStructureChanged of the listeners. + return new TreeModelEvent (this, + aPathToParent); + else + // General purpose event for removing or inserting known nodes. + return new TreeModelEvent (this, + aPathToParent, + new int[] {nIndexInParent}, + new Object[] {aChildNode} ); + } + + + + + /** Create a TreeModelEvent that indicates changes at those children of + the specified node with the specified indices. + */ + private TreeModelEvent createChangeEvent (AccTreeNode aNode, java.util.List<Integer> aChildIndices) + { + // Build a list of child objects that are indicated by the given indices. + int nCount = aChildIndices.size(); + Object aChildObjects[] = new Object[nCount]; + int nChildIndices[] = new int[nCount]; + for (int i=0; i<nCount; i++) + { + int nIndex = aChildIndices.get(i); + aChildObjects[i] = aNode.getChild (nIndex); + nChildIndices[i] = nIndex; + } + + return new TreeModelEvent (this, + createPath(aNode), + nChildIndices, + aChildObjects); + } + + + + private XAccessibleEventBroadcaster getBroadcaster (Object aObject) + { + if (aObject instanceof AccTreeNode) + return UnoRuntime.queryInterface ( + XAccessibleEventBroadcaster.class, ((AccTreeNode)aObject).getContext()); + else + return null; + } + + private void registerAccListener( Object aObject ) + { + // register this as listener for XAccessibleEventBroadcaster + // implementations + XAccessibleEventBroadcaster xBroadcaster = getBroadcaster( aObject ); + if (xBroadcaster != null) + { + xBroadcaster.addAccessibleEventListener( mxListener ); + } + } + + private void removeAccListener( Object aObject ) + { + XAccessibleEventBroadcaster xBroadcaster = getBroadcaster( aObject ); + if (xBroadcaster != null) + { + xBroadcaster.removeAccessibleEventListener( mxListener ); + } + } + + + + public void setCanvas (Canvas aCanvas) + { + maCanvas = aCanvas; + } + + public Canvas getCanvas () + { + return maCanvas; + } + + public void updateNode (XAccessibleContext xSource, java.lang.Class class1) + { + updateNode (xSource, class1,null); + } + + /** Get a list of children of the node associated with xSource that are + affected by the given handlers. Fire events that these children may + have changed in the tree view. Update the canvas representation of + xSource. + */ + public AccTreeNode updateNode (XAccessibleContext xSource, + java.lang.Class class1, java.lang.Class<AccessibleExtendedComponentHandler> class2) + { + AccessibleTreeNode aTreeNode = maNodeMap.GetNode (xSource); + AccTreeNode aNode = null; + if (mbVerbose) + System.out.println ("updating node " + xSource + " " + aTreeNode); + if (aTreeNode instanceof AccTreeNode) + { + aNode = (AccTreeNode) aTreeNode; + // Get list of affected children. + java.util.List<Integer> aChildIndices = aNode.updateChildren ( + class1, class2); + // Fire events that these children may have changed. + fireTreeNodesChanged ( + createChangeEvent (aNode, aChildIndices)); + } + return aNode; + } + + /** The listener to be registered with the accessible objects. + * Could be set to 'this' for same-thread event delivery, or to an + * instance of QueuedListener for multi-threaded delivery. May + * not be changed, since this would trip the + * register/removeAccListener logic. */ + private final XAccessibleEventListener mxListener; + + // Map to translate from accessible object to corresponding tree node. + private final NodeMap maNodeMap; + + // If the lock count is higher then zero, then no events are processed. + private int mnLockCount; + + private Canvas maCanvas; +} diff --git a/toolkit/test/accessibility/AccessibilityTreeModelBase.java b/toolkit/test/accessibility/AccessibilityTreeModelBase.java new file mode 100644 index 000000000..5ff2d96c9 --- /dev/null +++ b/toolkit/test/accessibility/AccessibilityTreeModelBase.java @@ -0,0 +1,140 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import java.util.ArrayList; + +import javax.swing.event.TreeModelListener; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreePath; + +public class AccessibilityTreeModelBase + implements TreeModel +{ + public AccessibilityTreeModelBase () + { + setRoot (null); + maTMListeners = new ArrayList<TreeModelListener>(); + } + + public synchronized void addTreeModelListener(TreeModelListener l) + { + maTMListeners.add(l); + } + + public synchronized void removeTreeModelListener(TreeModelListener l) + { + maTMListeners.remove(l); + } + + public synchronized int getChildCount(Object aParent) + { + return (aParent instanceof AccessibleTreeNode) ? + ((AccessibleTreeNode)aParent).getChildCount() : 0; + } + + public synchronized Object getChild (Object aParent, int nIndex) + { + Object aChild = null; + try + { + if (aParent instanceof AccessibleTreeNode) + aChild = ((AccessibleTreeNode)aParent).getChild(nIndex); + else + System.out.println ("getChild called for unknown parent node"); + } + catch (com.sun.star.lang.IndexOutOfBoundsException e) + { + aChild = ("no child " + nIndex + " from " + aParent + ": " + e); + } + return aChild; + } + + public synchronized Object getChildNoCreate (Object aParent, int nIndex) + { + Object aChild = null; + try + { + if (aParent instanceof AccessibleTreeNode) + aChild = ((AccessibleTreeNode)aParent).getChildNoCreate(nIndex); + else + System.out.println ("getChild called for unknown parent node"); + } + catch (com.sun.star.lang.IndexOutOfBoundsException e) + { } + return aChild; + } + + /** iterate over all children and look for child */ + public synchronized int getIndexOfChild (Object aParent, Object aChild) + { + int nIndex = -1; + try + { + if ((aParent instanceof AccessibleTreeNode) && (aChild instanceof AccessibleTreeNode)) + { + AccessibleTreeNode aParentNode = (AccessibleTreeNode) aParent; + AccessibleTreeNode aChildNode = (AccessibleTreeNode) aChild; + + int nChildCount = aParentNode.getChildCount(); + for( int i = 0; i < nChildCount; i++ ) + { + if (aChildNode.equals (aParentNode.getChild (i))) + { + nIndex = i; + break; + } + } + } + } + catch (com.sun.star.lang.IndexOutOfBoundsException e) + { + // Return -1 by falling through. + } + + // not found? + return nIndex; + } + + public boolean isLeaf (Object aNode) + { + return (aNode instanceof AccessibleTreeNode) ? + ((AccessibleTreeNode)aNode).isLeaf() : true; + } + + + + public synchronized Object getRoot() + { + return maRoot; + } + + public void valueForPathChanged(TreePath path, Object newValue) + { } + + protected synchronized void setRoot (AccessibleTreeNode aRoot) + { + maRoot = aRoot; + } + + + // The list of TreeModelListener objects. + protected ArrayList<TreeModelListener> maTMListeners; + + // The root node of the tree. Use setRoot to change it. + private AccessibleTreeNode maRoot = null; +} diff --git a/toolkit/test/accessibility/AccessibilityWorkBench.java b/toolkit/test/accessibility/AccessibilityWorkBench.java new file mode 100644 index 000000000..918b8cf6d --- /dev/null +++ b/toolkit/test/accessibility/AccessibilityWorkBench.java @@ -0,0 +1,591 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import com.sun.star.frame.XFrame; +import com.sun.star.frame.XTerminateListener; +import com.sun.star.uno.UnoRuntime; + +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.awt.XExtendedToolkit; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.tree.*; +import javax.swing.event.TreeSelectionListener; +import javax.swing.event.TreeSelectionEvent; +import ov.ObjectViewContainer; + +/** This class manages the GUI of the work bench. + @see AccessibilityTreeModel + for the implementation of the tree view on the left side which also + manages the registration of accessibility listeners. + @see Canvas + for the graphical view of the accessible objects. +*/ +public class AccessibilityWorkBench + extends JFrame + implements ActionListener, XTerminateListener, TreeSelectionListener + +{ + private static final String msVersion = "v1.7.2"; + private String msOptionsFileName = ".AWBrc"; + + public static void main (String args[]) + { + int nPortNumber = 5678; + + for (int i=0; i<args.length; i++) + { + if (args[i].equals ("-h") || args[i].equals ("--help") || args[i].equals ("-?")) + { + System.out.println ("usage: AccessibilityWorkBench <option>*"); + System.out.println ("options:"); + System.out.println (" -p <port-number> Port on which to connect to StarOffice."); + System.out.println (" Defaults to 5678."); + System.exit (0); + } + else if (args[i].equals ("-p")) + { + nPortNumber = Integer.parseInt (args[++i]); + } + } + + saWorkBench = new AccessibilityWorkBench (nPortNumber); + } + + + + + /** Return the one instance of the AccessibilityWorkBench + @return + Returns null when the AccessibilityWorkBench could not be + created successfully. + */ + public static AccessibilityWorkBench Instance () + { + return saWorkBench; + } + + + + /** Create an accessibility work bench that listens at the specified + port to Office applications. + */ + private AccessibilityWorkBench (int nPortNumber) + { + mbInitialized = false; + + Layout (); + + MessageArea.println (System.getProperty ("os.name") + " / " + + System.getProperty ("os.arch") + " / " + + System.getProperty ("os.version")); + MessageArea.println ("Using port " + nPortNumber); + office = new SimpleOffice (nPortNumber); + + maAccessibilityTree.getComponent().addTreeSelectionListener (this); + + addWindowListener (new WindowAdapter () + { @Override + public void windowClosing (WindowEvent e) + { System.exit(0); } + }); + + initialize (); + } + + + + + /** Create and arrange the widgets of the GUI. + */ + private void Layout () + { + setSize (new Dimension (8000,600)); + + // Create new layout. + GridBagLayout aLayout = new GridBagLayout (); + getContentPane().setLayout (aLayout); + + // Accessible Tree. + maAccessibilityTree = new AccessibilityTree (); + JScrollPane aTreeScrollPane = new JScrollPane( + maAccessibilityTree.getComponent(), + JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, + JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); + aTreeScrollPane.setPreferredSize (new Dimension (400,300)); + + // Object view shows details about the currently selected accessible + // object. + maObjectViewContainer = new ObjectViewContainer (); + JScrollPane aObjectViewContainerScrollPane = new JScrollPane( + maObjectViewContainer, + JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, + JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + aObjectViewContainerScrollPane.setPreferredSize (new Dimension (400,300)); + + // Split pane for tree view and object view. + JSplitPane aLeftViewSplitPane = new JSplitPane ( + JSplitPane.VERTICAL_SPLIT, + aTreeScrollPane, + aObjectViewContainerScrollPane + ); + aLeftViewSplitPane.setDividerLocation (300); + + // Canvas. + maCanvas = new Canvas (); + maCanvas.setTree (maAccessibilityTree.getComponent()); + maAccessibilityTree.SetCanvas (maCanvas); + JScrollPane aScrolledCanvas = new JScrollPane(maCanvas, + JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, + JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + aScrolledCanvas.getViewport().setBackground (Color.RED); + aScrolledCanvas.setPreferredSize (new Dimension(600,400)); + + // Split pane for tree view and canvas. + JSplitPane aViewSplitPane = new JSplitPane ( + JSplitPane.HORIZONTAL_SPLIT, + aLeftViewSplitPane, + aScrolledCanvas + ); + aViewSplitPane.setOneTouchExpandable(true); + aViewSplitPane.setDividerLocation (400); + + // Text output area. + MessageArea aMessageArea = MessageArea.Instance (); + + // Split pane for the two views and the message area. + JSplitPane aSplitPane = new JSplitPane (JSplitPane.VERTICAL_SPLIT, + aViewSplitPane, aMessageArea); + aSplitPane.setOneTouchExpandable(true); + addGridElement (aViewSplitPane, 0,0, 2,1, 3,3, + GridBagConstraints.CENTER, GridBagConstraints.BOTH); + + // Button bar. + maButtonBar = new JPanel(); + maButtonBar.setLayout (new FlowLayout()); + addGridElement (maButtonBar, 0,3, 2,1, 1,0, + GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL); + + // Buttons. + aConnectButton = createButton ("Connect", "connect"); + aUpdateButton = createButton ("Update", "update"); + aShapesButton = createButton ("Expand Shapes", "shapes"); + aExpandButton = createButton ("Expand All", "expand"); + aQuitButton = createButton ("Quit", "quit"); + UpdateButtonStates (); + + Options.Instance().Load (msOptionsFileName); + + setJMenuBar (CreateMenuBar ()); + + setTitle("Accessibility Workbench " + msVersion); + + pack (); + setVisible (true); + validate (); + repaint(); + } + + + + + /** Shortcut method for adding an object to a GridBagLayout. + */ + private void addGridElement (JComponent object, + int x, int y, int width, int height, int weightx, int weighty, + int anchor, int fill) + { + GridBagConstraints constraints = new GridBagConstraints (); + constraints.gridx = x; + constraints.gridy = y; + constraints.gridwidth = width; + constraints.gridheight = height; + constraints.weightx = weightx; + constraints.weighty = weighty; + constraints.anchor = anchor; + constraints.fill = fill; + getContentPane().add (object, constraints); + } + + + + + /** Create a new button and place at the right most position into the + button bar. + */ + private JButton createButton (String title, String command) + { + JButton aButton = new JButton (title); + aButton.setEnabled (false); + aButton.setActionCommand (command); + aButton.addActionListener (this); + + maButtonBar.add (aButton); + return aButton; + } + + + + + /** Create a menu bar for the application. + @return + Returns the new menu bar. The returned reference is also + remembered in the data member <member>maMenuBar</member>. + */ + private JMenuBar CreateMenuBar () + { + // Menu bar. + JMenuBar aMenuBar = new JMenuBar (); + + // File menu. + JMenu aFileMenu = new JMenu ("File"); + aMenuBar.add (aFileMenu); + JMenuItem aItem; + aItem = new JMenuItem ("Quit"); + aFileMenu.add (aItem); + aItem.addActionListener (this); + + // View menu. + JMenu aViewMenu = new JMenu ("View"); + aMenuBar.add (aViewMenu); + ButtonGroup aGroup = new ButtonGroup (); + JRadioButtonMenuItem aRadioButton = new JRadioButtonMenuItem ("Whole Screen"); + aGroup.add (aRadioButton); + aViewMenu.add (aRadioButton); + aRadioButton.addActionListener (this); + aRadioButton = new JRadioButtonMenuItem ("200%"); + aGroup.add (aRadioButton); + aViewMenu.add (aRadioButton); + aRadioButton.addActionListener (this); + aRadioButton = new JRadioButtonMenuItem ("100%"); + aGroup.add (aRadioButton); + aViewMenu.add (aRadioButton); + aRadioButton.addActionListener (this); + aRadioButton = new JRadioButtonMenuItem ("50%"); + aGroup.add (aRadioButton); + aViewMenu.add (aRadioButton); + aRadioButton.addActionListener (this); + aRadioButton = new JRadioButtonMenuItem ("25%"); + aGroup.add (aRadioButton); + aViewMenu.add (aRadioButton); + aRadioButton.addActionListener (this); + aRadioButton = new JRadioButtonMenuItem ("10%"); + aGroup.add (aRadioButton); + aViewMenu.add (aRadioButton); + aRadioButton.addActionListener (this); + + // Options menu. + JMenu aOptionsMenu = new JMenu ("Options"); + aMenuBar.add (aOptionsMenu); + JCheckBoxMenuItem aCBItem; + aCBItem = new JCheckBoxMenuItem ("Show Descriptions", maCanvas.getShowDescriptions()); + aOptionsMenu.add (aCBItem); + aCBItem.addActionListener (this); + + aCBItem = new JCheckBoxMenuItem ("Show Names", maCanvas.getShowNames()); + aOptionsMenu.add (aCBItem); + aCBItem.addActionListener (this); + + aCBItem = new JCheckBoxMenuItem ("Show Text", maCanvas.getShowText()); + aOptionsMenu.add (aCBItem); + aCBItem.addActionListener (this); + + aCBItem = new JCheckBoxMenuItem ("Antialiased Rendering", maCanvas.getAntialiasing()); + aOptionsMenu.add (aCBItem); + aCBItem.addActionListener (this); + + // Help menu. + JMenu aHelpMenu = new JMenu ("Help"); + aMenuBar.add (aHelpMenu); + + aItem = new JMenuItem ("Help"); + aHelpMenu.add (aItem); + aItem.addActionListener (this); + + aItem = new JMenuItem ("News"); + aHelpMenu.add (aItem); + aItem.addActionListener (this); + + aItem = new JMenuItem ("About"); + aHelpMenu.add (aItem); + aItem.addActionListener (this); + + return aMenuBar; + } + + + + + /** Initialize the AWB. This includes clearing the canvas, add + listeners, creation of a new tree model for the tree list box and + the update of the button states. + + This method may be called any number of times. Note that all + actions will be carried out every time. The main purpose of a + second call is that of a re-initialization after a reconnect. + */ + private void initialize () + { + maCanvas.clear(); + + AccessibilityTreeModel aModel = null; + aModel = new AccessibilityTreeModel (createTreeModelRoot()); + + aModel.setCanvas (maCanvas); + maAccessibilityTree.getComponent().setModel (aModel); + + if (office != null) + { + // Add terminate listener. + if (office.getDesktop() != null) + office.getDesktop().addTerminateListener (this); + + XExtendedToolkit xToolkit = office.getExtendedToolkit(); + // Remove old top window listener. + if (maTopWindowListener != null) + xToolkit.removeTopWindowListener (maQueuedTopWindowListener); + // Add top window listener. + if (xToolkit != null) + { + MessageArea.println ("registering at extended toolkit"); + maTopWindowListener = new TopWindowListener (aModel, office); + maQueuedTopWindowListener = new QueuedTopWindowListener (maTopWindowListener); + xToolkit.addTopWindowListener (maQueuedTopWindowListener); + maTopWindowListener.Initialize (); + } + else + maTopWindowListener = null; + } + + mbInitialized = true; + UpdateButtonStates (); + } + + + + + /** Update the states of the buttons according to the internal state of + the AWB. + */ + private void UpdateButtonStates () + { + aConnectButton.setEnabled (mbInitialized); + aQuitButton.setEnabled (mbInitialized); + aUpdateButton.setEnabled (mbInitialized); + aExpandButton.setEnabled (mbInitialized); + aShapesButton.setEnabled (mbInitialized); + } + + + + /** Callback for GUI actions from the buttons. + */ + public void actionPerformed (java.awt.event.ActionEvent e) + { + if (e.getActionCommand().equals("connect")) + { + office.connect(); + initialize (); + } + else if (e.getActionCommand().equals("quit")) + { + AccessibilityTreeModel aModel = (AccessibilityTreeModel)maAccessibilityTree.getComponent().getModel(); + aModel.clear(); + System.exit (0); + } + else if (e.getActionCommand().equals("update")) + { + initialize (); + } + else if (e.getActionCommand().equals("shapes")) + { + Cursor aCursor = getCursor(); + setCursor (new Cursor (Cursor.WAIT_CURSOR)); + maAccessibilityTree.expandShapes(); + setCursor (aCursor); + } + else if (e.getActionCommand().equals("expand")) + { + Cursor aCursor = getCursor(); + setCursor (new Cursor (Cursor.WAIT_CURSOR)); + maAccessibilityTree.expandAll(); + setCursor (aCursor); + } + else if (e.getActionCommand().equals ("Quit")) + { + System.out.println ("exiting"); + System.exit (0); + } + else if (e.getActionCommand().equals ("Show Descriptions")) + { + maCanvas.setShowDescriptions ( ! maCanvas.getShowDescriptions()); + Options.Instance().Save (msOptionsFileName); + } + else if (e.getActionCommand().equals ("Show Names")) + { + maCanvas.setShowNames ( ! maCanvas.getShowNames()); + Options.Instance().Save (msOptionsFileName); + } + else if (e.getActionCommand().equals ("Antialiased Rendering")) + { + maCanvas.setAntialiasing ( ! maCanvas.getAntialiasing()); + Options.Instance().Save (msOptionsFileName); + } + else if (e.getActionCommand().equals ("Help")) + { + HelpWindow.Instance().loadFile ("help.html"); + } + else if (e.getActionCommand().equals ("News")) + { + try{ + HelpWindow.Instance().loadFile ("news.html"); + } catch (Exception ex) {} + } + else if (e.getActionCommand().equals ("About")) + { + HelpWindow.Instance().loadFile ("about.html"); + } + else if (e.getActionCommand().equals ("Whole Screen")) + { + maCanvas.setZoomMode (Canvas.WHOLE_SCREEN); + Options.Instance().Save (msOptionsFileName); + } + else if (e.getActionCommand().equals ("200%")) + { + maCanvas.setZoomMode (200); + Options.Instance().Save (msOptionsFileName); + } + else if (e.getActionCommand().equals ("100%")) + { + maCanvas.setZoomMode (100); + Options.Instance().Save (msOptionsFileName); + } + else if (e.getActionCommand().equals ("50%")) + { + maCanvas.setZoomMode (50); + Options.Instance().Save (msOptionsFileName); + } + else if (e.getActionCommand().equals ("25%")) + { + maCanvas.setZoomMode (25); + Options.Instance().Save (msOptionsFileName); + } + else if (e.getActionCommand().equals ("10%")) + { + maCanvas.setZoomMode (10); + Options.Instance().Save (msOptionsFileName); + } + else + { + System.err.println("unknown command " + e.getActionCommand()); + } + } + + + + + /** Create an AccessibilityTreeModel root which contains the documents + (top windows) that are present at the moment. + */ + private AccessibleTreeNode createTreeModelRoot() + { + // create root node + VectorNode aRoot = new VectorNode ("Accessibility Tree", null); + if (maTopWindowListener != null) + maTopWindowListener.Initialize (); + return aRoot; + } + + + // TreeSelectionListener + public void valueChanged (TreeSelectionEvent aEvent) + { + TreePath aPath = aEvent.getPath(); + Object aObject = aPath.getLastPathComponent(); + if (aObject instanceof AccTreeNode) + { + AccTreeNode aNode = (AccTreeNode) aObject; + XAccessibleContext xContext = aNode.getContext(); + maObjectViewContainer.SetObject (xContext); + } + } + + + + + // XEventListener + public void disposing( com.sun.star.lang.EventObject aSourceObj ) + { + XFrame xFrame = UnoRuntime.queryInterface(XFrame.class, aSourceObj.Source); + + if( xFrame != null ) + System.out.println("frame disposed"); + else + System.out.println("controller disposed"); + } + + + + + // XTerminateListener + public void queryTermination (final com.sun.star.lang.EventObject aEvent) throws RuntimeException + { + System.out.println ("Terminate Event : " + aEvent); + } + + + + + // XTerminateListener + public void notifyTermination (final com.sun.star.lang.EventObject aEvent) throws RuntimeException + { + System.out.println ("Notify Termination Event : " + aEvent); + } + + + + /// The Singleton Workbench object. + private static AccessibilityWorkBench + saWorkBench = null; + + private SimpleOffice + office; + + private JPanel + maButtonBar; + private Canvas + maCanvas; + private AccessibilityTree + maAccessibilityTree; + private ObjectViewContainer + maObjectViewContainer; + private JButton + aConnectButton, + aQuitButton, + aUpdateButton, + aExpandButton, + aShapesButton; + private boolean + mbInitialized; + private TopWindowListener + maTopWindowListener; + private QueuedTopWindowListener + maQueuedTopWindowListener; +} diff --git a/toolkit/test/accessibility/AccessibleActionHandler.java b/toolkit/test/accessibility/AccessibleActionHandler.java new file mode 100644 index 000000000..868ea1105 --- /dev/null +++ b/toolkit/test/accessibility/AccessibleActionHandler.java @@ -0,0 +1,91 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.XAccessibleAction; +import com.sun.star.lang.IndexOutOfBoundsException; + +class AccessibleActionHandler + extends NodeHandler +{ + @Override + public NodeHandler createHandler (XAccessibleContext xContext) + { + XAccessibleAction xEComponent = + UnoRuntime.queryInterface ( + XAccessibleAction.class, xContext); + if (xEComponent != null) + return new AccessibleActionHandler (xEComponent); + else + return null; + } + + public AccessibleActionHandler () + { + } + + private AccessibleActionHandler (XAccessibleAction xAction) + { + if (xAction != null) + maChildList.setSize (1 + xAction.getAccessibleActionCount()); + } + + protected static XAccessibleAction getAction (AccTreeNode aParent) + { + return UnoRuntime.queryInterface ( + XAccessibleAction.class, aParent.getContext()); + } + + @Override + public AccessibleTreeNode createChild ( + AccessibleTreeNode aParent, + int nIndex) + { + AccessibleTreeNode aChild = null; + + if (aParent instanceof AccTreeNode) + { + XAccessibleAction xAction = getAction ((AccTreeNode)aParent); + if( xAction != null ) + { + if (nIndex == 0) + aChild = new StringNode ("Number of actions: " + xAction.getAccessibleActionCount(), + aParent); + else + { + nIndex -= 1; + try + { + aChild = new AccessibleActionNode ( + "Action " + nIndex + " : " + + xAction.getAccessibleActionDescription (nIndex), + aParent, + nIndex); + } + catch( IndexOutOfBoundsException e ) + { + aChild = new StringNode ("ERROR", aParent); + } + } + } + } + + return aChild; + } +} diff --git a/toolkit/test/accessibility/AccessibleActionNode.java b/toolkit/test/accessibility/AccessibleActionNode.java new file mode 100644 index 000000000..c1c927574 --- /dev/null +++ b/toolkit/test/accessibility/AccessibleActionNode.java @@ -0,0 +1,67 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import javax.swing.JOptionPane; + +/** + Base class for all tree nodes. + */ +class AccessibleActionNode + extends StringNode +{ + public AccessibleActionNode (String aDisplayObject, + AccessibleTreeNode aParent, + int nActionIndex) + { + super (aDisplayObject, aParent); + mnActionIndex = nActionIndex; + } + + @Override + public String[] getActions () + { + return new String[] {"Perform Action"}; + } + + /** perform action */ + @Override + public void performAction (int nIndex) + { + if (nIndex != 0) + return; + boolean bResult = false; + if (getParent() instanceof AccTreeNode) + try + { + bResult = AccessibleActionHandler.getAction( + (AccTreeNode)getParent()).doAccessibleAction ( + mnActionIndex); + } + catch (com.sun.star.lang.IndexOutOfBoundsException e) + { + } + + JOptionPane.showMessageDialog (null, + "performed action " + mnActionIndex + + (bResult?" with":" without") + " success", + "Action " + mnActionIndex, + JOptionPane.INFORMATION_MESSAGE); + } + + private final int mnActionIndex; +} diff --git a/toolkit/test/accessibility/AccessibleCellHandler.java b/toolkit/test/accessibility/AccessibleCellHandler.java new file mode 100644 index 000000000..c251f6790 --- /dev/null +++ b/toolkit/test/accessibility/AccessibleCellHandler.java @@ -0,0 +1,168 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.XAccessibleTable; +import com.sun.star.accessibility.XAccessible; + + +class AccessibleCellHandler extends NodeHandler +{ + @Override + public NodeHandler createHandler (XAccessibleContext xContext) + { + if (xContext == null) + return null; + + AccessibleCellHandler aCellHandler = null; + XAccessible xParent = xContext.getAccessibleParent(); + if (xParent != null) + { + XAccessibleTable xTable = + UnoRuntime.queryInterface ( + XAccessibleTable.class, xParent.getAccessibleContext()); + if (xTable != null) + aCellHandler = new AccessibleCellHandler (xTable); + } + return aCellHandler; + } + + public AccessibleCellHandler () + { + } + + private AccessibleCellHandler (XAccessibleTable xTable) + { + if (xTable != null) + maChildList.setSize (8); + } + + @Override + public AccessibleTreeNode createChild (AccessibleTreeNode aParent, int nIndex) + { + AccessibleTreeNode aChild = null; + XAccessibleTable xTable = null; + XAccessibleContext xContext = null; + AccessibleTreeNode aGrandParent = aParent.getParent(); + if (aGrandParent instanceof AccTreeNode) + { + xTable = ((AccTreeNode)aGrandParent).getTable(); + xContext = ((AccTreeNode)aGrandParent).getContext(); + } + if (aParent instanceof AccTreeNode) + { + xContext = ((AccTreeNode)aParent).getContext(); + } + try + { + if( xTable != null && xContext != null ) + { + switch( nIndex ) + { + case 0: + { + int nChild = xContext.getAccessibleIndexInParent(); + int nRow = xTable.getAccessibleRow( nChild ); + + aChild = new StringNode ("# table row: " + nRow, aParent); + } + break; + case 1: + { + int nChild = xContext.getAccessibleIndexInParent(); + int nCol = xTable.getAccessibleColumn( nChild ); + + aChild = new StringNode ("# table column: " + nCol, aParent); + } + break; + case 2: + { + int nChild = xContext.getAccessibleIndexInParent(); + int nRow = xTable.getAccessibleRow( nChild ); + int nCol = xTable.getAccessibleColumn( nChild ); + int nExt = xTable.getAccessibleRowExtentAt( nRow, nCol ); + + aChild = new StringNode ("# table row extend: " + nExt, aParent); + } + break; + case 3: + { + int nChild = xContext.getAccessibleIndexInParent(); + int nRow = xTable.getAccessibleRow( nChild ); + int nCol = xTable.getAccessibleColumn( nChild ); + int nExt = xTable.getAccessibleColumnExtentAt( nRow, nCol ); + + aChild = new StringNode ("# table column extend: " + nExt, aParent); + } + break; + case 4: + { + int nChild = xContext.getAccessibleIndexInParent(); + int nRow = xTable.getAccessibleRow( nChild ); + int nCol = xTable.getAccessibleColumn( nChild ); + XAccessible xChild = + xTable.getAccessibleCellAt( nRow, nCol ); + + aChild = new StringNode ("# cell name retrieved from table: " + xChild.getAccessibleContext().getAccessibleName(), aParent); + } + break; + case 5: + { + int nChild = xContext.getAccessibleIndexInParent(); + int nRow = xTable.getAccessibleRow( nChild ); + int nCol = xTable.getAccessibleColumn( nChild ); + boolean bSelected = + xTable.isAccessibleSelected( nRow, nCol ); + + aChild = new StringNode ("cell is selected: " + bSelected, aParent); + } + break; + case 6: + { + int nChild = xContext.getAccessibleIndexInParent(); + int nRow = xTable.getAccessibleRow( nChild ); + boolean bSelected = + xTable.isAccessibleRowSelected( nRow ); + + aChild = new StringNode ("table row is selected: " + bSelected, aParent); + } + break; + case 7: + { + int nChild = xContext.getAccessibleIndexInParent(); + int nCol = xTable.getAccessibleColumn( nChild ); + boolean bSelected = + xTable.isAccessibleColumnSelected( nCol ); + + aChild = new StringNode ("table column is selected: " + bSelected, aParent); + } + break; + default: + aChild = new StringNode ("unknown child index " + nIndex, aParent); + } + } + } + catch (Exception e) + { + // Return empty child. + } + + return aChild; + } +} diff --git a/toolkit/test/accessibility/AccessibleComponentHandler.java b/toolkit/test/accessibility/AccessibleComponentHandler.java new file mode 100644 index 000000000..229612b8d --- /dev/null +++ b/toolkit/test/accessibility/AccessibleComponentHandler.java @@ -0,0 +1,121 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.XAccessibleComponent; + + +class AccessibleComponentHandler + extends NodeHandler +{ + + @Override + public NodeHandler createHandler (XAccessibleContext xContext) + { + XAccessibleComponent xComponent = + UnoRuntime.queryInterface ( + XAccessibleComponent.class, xContext); + if (xComponent != null) + return new AccessibleComponentHandler (xComponent); + else + return null; + + } + + public AccessibleComponentHandler () + { + } + + private AccessibleComponentHandler (XAccessibleComponent xComponent) + { + if (xComponent != null) + maChildList.setSize (6); + } + + @Override + public AccessibleTreeNode createChild (AccessibleTreeNode aParent, int nIndex) + { + AccessibleTreeNode aChild = null; + if (aParent instanceof AccTreeNode) + { + XAccessibleComponent xComponent = + ((AccTreeNode)aParent).getComponent(); + + if (xComponent != null) + { + int nColor; + switch (nIndex) + { + case 0: + com.sun.star.awt.Point aLocation = xComponent.getLocation(); + aChild = new StringNode ( + "Location: " + aLocation.X + ", " + aLocation.Y, + aParent); + break; + case 1: + com.sun.star.awt.Point aScreenLocation = xComponent.getLocationOnScreen(); + aChild = new StringNode ( + "Location on Screen: " + aScreenLocation.X + ", " + aScreenLocation.Y, + aParent); + break; + case 2: + com.sun.star.awt.Size aSize = xComponent.getSize(); + aChild = new StringNode ( + "Size: "+ aSize.Width + ", " + aSize.Height, + aParent); + break; + case 3: + com.sun.star.awt.Rectangle aBBox = xComponent.getBounds(); + aChild = new StringNode ( + "Bounding Box: "+ aBBox.X + ", " + aBBox.Y + "," + + aBBox.Width + ", " + aBBox.Height, + aParent); + break; + case 4: + nColor = xComponent.getForeground(); + aChild = new StringNode ("Foreground color: R" + + (nColor>>16&0xff) + + "G" + (nColor>>8&0xff) + + "B" + (nColor&0xff) + + "A" + (nColor>>24&0xff), + aParent); + break; + case 5: + nColor = xComponent.getBackground(); + aChild = new StringNode ("Background color: R" + + (nColor>>16&0xff) + + "G" + (nColor>>8&0xff) + + "B" + (nColor&0xff) + + "A" + (nColor>>24&0xff), + aParent); + break; + } + } + } + return aChild; + } + + @Override + public void update (AccessibleTreeNode aNode) + { + maChildList.clear(); + if (aNode instanceof AccTreeNode && ((AccTreeNode)aNode).getComponent() != null) + maChildList.setSize (4); + } +} diff --git a/toolkit/test/accessibility/AccessibleContextHandler.java b/toolkit/test/accessibility/AccessibleContextHandler.java new file mode 100644 index 000000000..3b3d35108 --- /dev/null +++ b/toolkit/test/accessibility/AccessibleContextHandler.java @@ -0,0 +1,95 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import com.sun.star.accessibility.XAccessible; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.XAccessibleStateSet; +import tools.NameProvider; + +class AccessibleContextHandler + extends NodeHandler +{ + @Override + public NodeHandler createHandler (XAccessibleContext xContext) + { + if (xContext != null) + return new AccessibleContextHandler (xContext); + else + return null; + } + + public AccessibleContextHandler () + { + super (); + } + + private AccessibleContextHandler (XAccessibleContext xContext) + { + super(); + if (xContext != null) + maChildList.setSize (4); + } + + @Override + public AccessibleTreeNode createChild (AccessibleTreeNode aParent, int nIndex) + { + XAccessibleContext xContext = null; + if (aParent instanceof AccTreeNode) + xContext = ((AccTreeNode)aParent).getContext(); + + String sChild = ""; + if (xContext != null) + { + switch( nIndex ) + { + case 0: + sChild = "Description: " + + xContext.getAccessibleDescription(); + break; + case 1: + int nRole = xContext.getAccessibleRole(); + sChild = "Role: " + nRole + " (" + NameProvider.getRoleName(nRole) + ")"; + break; + case 2: + XAccessible xParent = xContext.getAccessibleParent(); + sChild = "Has parent: " + (xParent!=null ? "yes" : "no"); + break; + case 3: + sChild = ""; + XAccessibleStateSet xStateSet = + xContext.getAccessibleStateSet(); + if (xStateSet != null) + { + for (short i=0; i<=30; i++) + { + if (xStateSet.contains (i)) + { + if (sChild.compareTo ("") != 0) + sChild += ", "; + sChild += NameProvider.getStateName(i); + } + } + } + else + sChild += "no state set"; + sChild = "State set: " + sChild; + } + } + return new StringNode (sChild, aParent); + } +} diff --git a/toolkit/test/accessibility/AccessibleEditableTextHandler.java b/toolkit/test/accessibility/AccessibleEditableTextHandler.java new file mode 100644 index 000000000..ae5df1461 --- /dev/null +++ b/toolkit/test/accessibility/AccessibleEditableTextHandler.java @@ -0,0 +1,53 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.XAccessibleEditableText; + + +class AccessibleEditableTextHandler extends NodeHandler +{ + @Override + public NodeHandler createHandler (XAccessibleContext xContext) + { + XAccessibleEditableText xText = + UnoRuntime.queryInterface ( + XAccessibleEditableText.class, xContext); + if (xText != null) + return new AccessibleEditableTextHandler (xText); + else + return null; + } + + public AccessibleEditableTextHandler () + { + } + + private AccessibleEditableTextHandler (XAccessibleEditableText xText) + { + if (xText != null) + maChildList.setSize (1); + } + + @Override + public AccessibleTreeNode createChild (AccessibleTreeNode aParent, int nIndex) + { + return new StringNode ("XAccessibleEditableText is supported", aParent); + } +} diff --git a/toolkit/test/accessibility/AccessibleExtendedComponentHandler.java b/toolkit/test/accessibility/AccessibleExtendedComponentHandler.java new file mode 100644 index 000000000..f34ed1b5f --- /dev/null +++ b/toolkit/test/accessibility/AccessibleExtendedComponentHandler.java @@ -0,0 +1,93 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.XAccessibleExtendedComponent; + + +class AccessibleExtendedComponentHandler + extends NodeHandler +{ + @Override + public NodeHandler createHandler (XAccessibleContext xContext) + { + XAccessibleExtendedComponent xEComponent = + UnoRuntime.queryInterface ( + XAccessibleExtendedComponent.class, xContext); + if (xEComponent != null) + return new AccessibleExtendedComponentHandler (xEComponent); + else + return null; + } + + public AccessibleExtendedComponentHandler () + { + } + + private AccessibleExtendedComponentHandler (XAccessibleExtendedComponent xEComponent) + { + if (xEComponent != null) + maChildList.setSize (0); + } + + private static XAccessibleExtendedComponent getComponent (AccTreeNode aNode) + { + return UnoRuntime.queryInterface ( + XAccessibleExtendedComponent.class, + aNode.getContext()); + } + + + @Override + public AccessibleTreeNode createChild (AccessibleTreeNode aParent, int nIndex) + { + AccessibleTreeNode aChild = null; + if (aParent instanceof AccTreeNode) + { + XAccessibleExtendedComponent xEComponent = getComponent ((AccTreeNode)aParent); + + if (xEComponent != null) + { + int nColor; + switch( nIndex ) + { + case 0: + nColor = xEComponent.getForeground(); + aChild = new StringNode ("Deprecated Foreground color: R" + + (nColor>>16&0xff) + + "G" + (nColor>>8&0xff) + + "B" + (nColor&0xff) + + "A" + (nColor>>24&0xff), + aParent); + break; + case 1: + nColor = xEComponent.getBackground(); + aChild = new StringNode ("Deprecated Background color: R" + + (nColor>>16&0xff) + + "G" + (nColor>>8&0xff) + + "B" + (nColor&0xff) + + "A" + (nColor>>24&0xff), + aParent); + break; + } + } + } + return aChild; + } +} diff --git a/toolkit/test/accessibility/AccessibleHyperlinkHandler.java b/toolkit/test/accessibility/AccessibleHyperlinkHandler.java new file mode 100644 index 000000000..41a161ac5 --- /dev/null +++ b/toolkit/test/accessibility/AccessibleHyperlinkHandler.java @@ -0,0 +1,53 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.XAccessibleHyperlink; + + +class AccessibleHyperlinkHandler extends AccessibleTreeHandler +{ + @Override + public NodeHandler createHandler (XAccessibleContext xContext) + { + XAccessibleHyperlink xLink = + UnoRuntime.queryInterface ( + XAccessibleHyperlink.class, xContext); + if (xLink != null) + return new AccessibleHyperlinkHandler (xLink); + else + return null; + } + + public AccessibleHyperlinkHandler () + { + } + + private AccessibleHyperlinkHandler (XAccessibleHyperlink xLink) + { + if (xLink != null) + maChildList.setSize (1); + } + + @Override + public AccessibleTreeNode getChild (AccessibleTreeNode aParent, int nIndex) + { + return new StringNode ("interface XAccessibleHyperlink is supported", aParent); + } +} diff --git a/toolkit/test/accessibility/AccessibleHypertextHandler.java b/toolkit/test/accessibility/AccessibleHypertextHandler.java new file mode 100644 index 000000000..603d68997 --- /dev/null +++ b/toolkit/test/accessibility/AccessibleHypertextHandler.java @@ -0,0 +1,53 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.XAccessibleHypertext; + + +class AccessibleHypertextHandler extends AccessibleTreeHandler +{ + @Override + public NodeHandler createHandler (XAccessibleContext xContext) + { + XAccessibleHypertext xText = + UnoRuntime.queryInterface ( + XAccessibleHypertext.class, xContext); + if (xText != null) + return new AccessibleHypertextHandler (xText); + else + return null; + } + + public AccessibleHypertextHandler () + { + } + + private AccessibleHypertextHandler (XAccessibleHypertext xText) + { + if (xText != null) + maChildList.setSize (1); + } + + @Override + public AccessibleTreeNode getChild (AccessibleTreeNode aParent, int nIndex) + { + return new StringNode ("interface XAccessibleHypertext is supported", aParent); + } +} diff --git a/toolkit/test/accessibility/AccessibleImageHandler.java b/toolkit/test/accessibility/AccessibleImageHandler.java new file mode 100644 index 000000000..a02077b7d --- /dev/null +++ b/toolkit/test/accessibility/AccessibleImageHandler.java @@ -0,0 +1,70 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.XAccessibleImage; + + +class AccessibleImageHandler extends NodeHandler +{ + @Override + public NodeHandler createHandler (XAccessibleContext xContext) + { + XAccessibleImage xImage = + UnoRuntime.queryInterface ( + XAccessibleImage.class, xContext); + if (xImage != null) + return new AccessibleImageHandler (xImage); + else + return null; + } + + public AccessibleImageHandler () + { + } + + private AccessibleImageHandler (XAccessibleImage xImage) + { + if (xImage != null) + maChildList.setSize (1); + } + + private static XAccessibleImage getImage (AccTreeNode aNode) + { + return UnoRuntime.queryInterface ( + XAccessibleImage.class, aNode.getContext()); + } + + @Override + public AccessibleTreeNode createChild (AccessibleTreeNode aParent, int nIndex) + { + if (aParent instanceof AccTreeNode) + { + XAccessibleImage xImage = getImage ((AccTreeNode)aParent); + if (xImage != null) + return new StringNode ( + "Image: " + + xImage.getAccessibleImageDescription() + " (" + + xImage.getAccessibleImageWidth() + "x" + + xImage.getAccessibleImageHeight() + ")", + aParent); + } + return null; + } +} diff --git a/toolkit/test/accessibility/AccessibleRelationHandler.java b/toolkit/test/accessibility/AccessibleRelationHandler.java new file mode 100644 index 000000000..1f7eebada --- /dev/null +++ b/toolkit/test/accessibility/AccessibleRelationHandler.java @@ -0,0 +1,115 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.accessibility.XAccessible; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.AccessibleRelation; +import com.sun.star.accessibility.XAccessibleRelationSet; +import com.sun.star.lang.IndexOutOfBoundsException; + +import tools.NameProvider; + +class AccessibleRelationHandler + extends NodeHandler +{ + @Override + public NodeHandler createHandler( XAccessibleContext xContext ) + { + AccessibleRelationHandler aHandler = null; + if (xContext != null) + { + XAccessibleRelationSet xRelation = xContext.getAccessibleRelationSet(); + if (xRelation != null) + aHandler = new AccessibleRelationHandler(xContext); + } + return aHandler; + } + + public AccessibleRelationHandler() + { + } + + private AccessibleRelationHandler( XAccessibleContext xContext ) + { + XAccessibleRelationSet xRelation = xContext.getAccessibleRelationSet(); + if (xRelation != null) + maChildList.setSize( 1 ); + } + + @Override + public AccessibleTreeNode createChild( AccessibleTreeNode aParent, + int nIndex ) + { + XAccessibleRelationSet xRelation = null; + AccessibleTreeNode aChild = null; + + if( aParent instanceof AccTreeNode ) + { + xRelation = + ((AccTreeNode)aParent).getContext().getAccessibleRelationSet(); + } + if( xRelation == null ) + return aChild; + + + VectorNode aVNode = new VectorNode( "RelationSet", aParent); + int nCount = xRelation.getRelationCount(); + try + { + for( int i = 0; i < nCount; i++ ) + { + AccessibleRelation aRelation = xRelation.getRelation( i ); + + StringBuffer aBuffer = new StringBuffer(); + aBuffer.append (NameProvider.getRelationName (aRelation.RelationType)); + aBuffer.append( ": " ); + + for( int j = 0; j < aRelation.TargetSet.length; j++ ) + { + Object aTarget = aRelation.TargetSet[j]; + XAccessible xAccTarget = + UnoRuntime.queryInterface( + XAccessible.class, aTarget ); + if( xAccTarget == null ) + { + aBuffer.append( aTarget.toString() ); + } + else + { + aBuffer.append( xAccTarget.getAccessibleContext(). + getAccessibleName() ); + } + aBuffer.append( ", " ); + } + aBuffer.delete( aBuffer.length() - 2, aBuffer.length() ); + + aVNode.addChild( new StringNode( aBuffer.toString(), + aParent ) ); + } + + aChild = aVNode; + } + catch( IndexOutOfBoundsException e ) + { + aChild = new StringNode( "IndexOutOfBounds", aParent ); + } + + return aChild; + } +} diff --git a/toolkit/test/accessibility/AccessibleSelectionHandler.java b/toolkit/test/accessibility/AccessibleSelectionHandler.java new file mode 100644 index 000000000..4a8cd24ba --- /dev/null +++ b/toolkit/test/accessibility/AccessibleSelectionHandler.java @@ -0,0 +1,145 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.accessibility.XAccessible; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.XAccessibleSelection; +import com.sun.star.lang.IndexOutOfBoundsException; + + + +class AccessibleSelectionHandler + extends NodeHandler +{ + @Override + public NodeHandler createHandler( XAccessibleContext xContext ) + { + XAccessibleSelection xSelection = + UnoRuntime.queryInterface( + XAccessibleSelection.class, xContext); + return (xSelection == null) ? null : + new AccessibleSelectionHandler(xSelection); + } + + public AccessibleSelectionHandler() + { + } + + private AccessibleSelectionHandler( XAccessibleSelection xSelection ) + { + if (xSelection != null) + maChildList.setSize( 2 ); + } + + @Override + public AccessibleTreeNode createChild( AccessibleTreeNode aParent, + int nIndex ) + { + if( !(aParent instanceof AccTreeNode) ) + return null; + + XAccessibleSelection xSelection = ((AccTreeNode)aParent).getSelection(); + if( xSelection == null ) + return null; + + AccessibleTreeNode aChild = null; + + switch( nIndex ) + { + case 0: + aChild = new StringNode( + "getSelectedAccessibleChildCount: " + + xSelection.getSelectedAccessibleChildCount(), + aParent ); + break; + case 1: + { + VectorNode aVNode = + new VectorNode( "Selected Children", aParent); + int nSelected = 0; + int nCount = ((AccTreeNode)aParent).getContext(). + getAccessibleChildCount(); + try + { + for( int i = 0; i < nCount; i++ ) + { + try + { + if( xSelection.isAccessibleChildSelected( i ) ) + { + XAccessible xSelChild = xSelection. + getSelectedAccessibleChild(nSelected); + XAccessible xNChild = + ((AccTreeNode)aParent). + getContext().getAccessibleChild( i ); + aVNode.addChild( new StringNode( + i + ": " + + xNChild.getAccessibleContext(). + getAccessibleDescription() + " (" + + (xSelChild.equals(xNChild) ? "OK" : "XXX") + + ")", aParent ) ); + } + } + catch (com.sun.star.lang.DisposedException e) + { + aVNode.addChild( new StringNode( + i + ": caught DisposedException while creating", + aParent )); + } + } + aChild = aVNode; + } + catch( IndexOutOfBoundsException e ) + { + aChild = new StringNode( "IndexOutOfBounds", + aParent ); + } + } + break; + default: + aChild = new StringNode( "ERROR", aParent ); + break; + } + + return aChild; + } + + + @Override + public String[] getActions (AccessibleTreeNode aNode) + { + if( aNode instanceof AccTreeNode ) + { + XAccessibleSelection xSelection = + ((AccTreeNode)aNode).getSelection(); + if( xSelection != null ) + { + return new String[] { "Select..." }; + } + } + return new String[0]; + } + + @Override + public void performAction (AccessibleTreeNode aNode, int nIndex) + { + SelectionDialog selectionDialog = new SelectionDialog( (AccTreeNode)aNode ); + selectionDialog.setVisible(true); + } +} diff --git a/toolkit/test/accessibility/AccessibleTableHandler.java b/toolkit/test/accessibility/AccessibleTableHandler.java new file mode 100644 index 000000000..a72fe61a3 --- /dev/null +++ b/toolkit/test/accessibility/AccessibleTableHandler.java @@ -0,0 +1,103 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.XAccessibleTable; + + +class AccessibleTableHandler extends NodeHandler +{ + @Override + public NodeHandler createHandler (XAccessibleContext xContext) + { + XAccessibleTable xTable = + UnoRuntime.queryInterface ( + XAccessibleTable.class, xContext); + if (xTable != null) + return new AccessibleTableHandler (xTable); + else + return null; + } + + public AccessibleTableHandler () + { + } + + private AccessibleTableHandler (XAccessibleTable xTable) + { + if (xTable != null) + maChildList.setSize (4); + } + + @Override + public AccessibleTreeNode createChild (AccessibleTreeNode aParent, int nIndex) + { + AccessibleTreeNode aChild = null; + XAccessibleTable xTable = null; + if (aParent instanceof AccTreeNode) + xTable = ((AccTreeNode)aParent).getTable(); + try + { + if( xTable != null ) + { + switch( nIndex ) + { + case 0: + aChild = new StringNode ("# table rows: " + xTable.getAccessibleRowCount(), aParent); + break; + case 1: + aChild = new StringNode ("# table columns: " + xTable.getAccessibleColumnCount(), aParent); + break; + case 2: + { + String sText = "selected rows: "; + int[] aSelected = xTable.getSelectedAccessibleRows(); + for( int i=0; i < aSelected.length; i++ ) + { + sText += aSelected[i]; + sText += " "; + } + aChild = new StringNode (sText, aParent); + } + break; + case 3: + { + String sText = "selected columns: "; + int[] aSelected = xTable.getSelectedAccessibleColumns(); + for( int i=0; i < aSelected.length; i++ ) + { + sText += aSelected[i]; + sText += " "; + } + aChild = new StringNode (sText, aParent); + } + break; + default: + aChild = new StringNode ("unknown child index " + nIndex, aParent); + } + } + } + catch (Exception e) + { + // Return empty child. + } + + return aChild; + } +} diff --git a/toolkit/test/accessibility/AccessibleTextHandler.java b/toolkit/test/accessibility/AccessibleTextHandler.java new file mode 100644 index 000000000..714bcb591 --- /dev/null +++ b/toolkit/test/accessibility/AccessibleTextHandler.java @@ -0,0 +1,821 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.FlowLayout; +import java.awt.Graphics; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.BoxLayout; +import javax.swing.Icon; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JColorChooser; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JTextArea; +import javax.swing.text.JTextComponent; + +import com.sun.star.accessibility.AccessibleTextType; +import com.sun.star.accessibility.TextSegment; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.XAccessibleEditableText; +import com.sun.star.accessibility.XAccessibleText; +import com.sun.star.awt.Point; +import com.sun.star.awt.Rectangle; +import com.sun.star.beans.PropertyValue; +import com.sun.star.beans.UnknownPropertyException; +import com.sun.star.lang.IndexOutOfBoundsException; +import com.sun.star.uno.UnoRuntime; + + +class AccessibleTextHandler extends NodeHandler +{ + @Override + public NodeHandler createHandler (XAccessibleContext xContext) + { + XAccessibleText xText = UnoRuntime.queryInterface ( + XAccessibleText.class, xContext); + if (xText != null) + return new AccessibleTextHandler (xText); + else + return null; + } + + public AccessibleTextHandler () + { + } + + private AccessibleTextHandler (XAccessibleText xText) + { + if (xText != null) + maChildList.setSize (8); + } + + @Override + public AccessibleTreeNode createChild (AccessibleTreeNode aParent, int nIndex) + { + AccessibleTreeNode aChild = null; + XAccessibleText xText = null; + if (aParent instanceof AccTreeNode) + xText = ((AccTreeNode)aParent).getText(); + + try + { + if( xText != null ) + { + switch( nIndex ) + { + case 0: + aChild = new StringNode (xText.getText(), aParent); + break; + case 1: + aChild = new StringNode ("# chars: " + xText.getCharacterCount(), aParent); + break; + case 2: + aChild = new StringNode (characters( xText ), aParent); + break; + case 3: + aChild = new StringNode ("selection: " + + "[" + xText.getSelectionStart() + + "," + xText.getSelectionEnd() + + "] \"" + xText.getSelectedText() + "\"", + aParent); + break; + case 4: + aChild = new StringNode ("getCaretPosition: " + xText.getCaretPosition(), aParent); + break; + case 5: + { + VectorNode aVec = new VectorNode("portions", aParent); + aChild = aVec; + aVec.addChild( + textAtIndexNode( xText, "Character", + AccessibleTextType.CHARACTER, + aParent ) ); + aVec.addChild( + textAtIndexNode( xText, "Word", + AccessibleTextType.WORD, + aParent ) ); + aVec.addChild( + textAtIndexNode( xText, "Sentence", + AccessibleTextType.SENTENCE, + aParent ) ); + aVec.addChild( + textAtIndexNode( xText, "Paragraph", + AccessibleTextType.PARAGRAPH, + aParent ) ); + aVec.addChild( + textAtIndexNode( xText, "Line", + AccessibleTextType.LINE, + aParent ) ); + aVec.addChild( + textAtIndexNode( xText, "Attribute", + AccessibleTextType.ATTRIBUTE_RUN, + aParent ) ); + aVec.addChild( + textAtIndexNode( xText, "Glyph", + AccessibleTextType.GLYPH, + aParent ) ); + } + break; + case 6: + aChild = new StringNode (bounds( xText ), aParent); + break; + case 7: + aChild = getAttributes( xText, aParent ); + break; + default: + aChild = new StringNode ("unknown child index " + nIndex, aParent); + } + } + } + catch (Exception e) + { + // Return empty child. + } + + return aChild; + } + + + private String textAtIndexNodeString( + int nStart, int nEnd, + String sWord, String sBefore, String sBehind) + { + return "[" + nStart + "," + nEnd + "] " + + "\"" + sWord + "\" \t" + + "(" + sBefore + "," + sBehind + ")"; + } + + /** Create a text node that lists all strings of a particular text type + */ + private AccessibleTreeNode textAtIndexNode( + XAccessibleText xText, + String sName, + short nTextType, + AccessibleTreeNode aParent) + { + VectorNode aNode = new VectorNode (sName, aParent); + + // get word at all positions; + // for nicer display, compare current word to previous one and + // make a new node for every interval, not for every word + int nLength = xText.getCharacterCount(); + if( nLength > 0 ) + { + try + { + // sWord + nStart mark the current word + // make a node as soon as a new one is found; close the last + // one at the end + TextSegment sWord = xText.getTextAtIndex(0, nTextType); + TextSegment sBefore = xText.getTextBeforeIndex(0, nTextType); + TextSegment sBehind = xText.getTextBehindIndex(0, nTextType); + int nStart = 0; + for(int i = 1; i < nLength; i++) + { + TextSegment sTmp = xText.getTextAtIndex(i, nTextType); + TextSegment sTBef = xText.getTextBeforeIndex(i, nTextType); + TextSegment sTBeh = xText.getTextBehindIndex(i, nTextType); + if( ! ( sTmp.equals( sWord ) && sTBef.equals( sBefore ) && + sTBeh.equals( sBehind ) ) ) + { + aNode.addChild (new StringNode (textAtIndexNodeString( + nStart, i, + sWord.SegmentText, sBefore.SegmentText, sBehind.SegmentText), aNode)); + sWord = sTmp; + sBefore = sTBef; + sBehind = sTBeh; + nStart = i; + } + + // don't generate more than 50 children. + if (aNode.getChildCount() > 50) + { + sWord.SegmentText = "..."; + break; + } + } + aNode.addChild (new StringNode (textAtIndexNodeString( + nStart, nLength, + sWord.SegmentText, sBefore.SegmentText, sBehind.SegmentText), aNode)); + } + catch( IndexOutOfBoundsException e ) + { + aNode.addChild (new StringNode (e.toString(), aNode)); + } + catch (com.sun.star.lang.IllegalArgumentException e) + { + aNode.addChild (new StringNode (e.toString(), aNode)); + } + } + + return aNode; + } + + + + /** getCharacter (display as array string) */ + private String characters(XAccessibleText xText) + { + // get count (max. 30) + int nChars = xText.getCharacterCount(); + if( nChars > 30 ) + nChars = 30; + + // build up string + StringBuffer aChars = new StringBuffer(); + try + { + aChars.append( '[' ); + for( int i = 0; i < nChars; i++) + { + aChars.append( xText.getCharacter(i) ); + aChars.append( ',' ); + } + if( nChars > 0) + { + if( nChars == xText.getCharacterCount() ) + aChars.deleteCharAt( aChars.length() - 1 ); + else + aChars.append( "..." ); + } + aChars.append( ']' ); + } + catch( IndexOutOfBoundsException e ) + { + aChars.append( " ERROR " ); + } + + // return result + return "getCharacters: " + aChars; + } + + + /** iterate over characters, and translate their positions + * back and forth */ + private String bounds( XAccessibleText xText ) + { + StringBuffer aBuffer = new StringBuffer( "bounds: " ); + try + { + // iterate over characters + int nCount = xText.getCharacterCount(); + for(int i = 0; i < nCount; i++ ) + { + // get bounds for this character + Rectangle aRect = xText.getCharacterBounds( i ); + + // get the character by 'clicking' into the middle of + // the bounds + Point aMiddle = new Point(); + aMiddle.X = aRect.X + (aRect.Width / 2) - 1; + aMiddle.Y = aRect.Y + (aRect.Height / 2 ) - 1; + int nIndex = xText.getIndexAtPoint( aMiddle ); + + // get the character, or a '#' for an illegal index + if( (nIndex >= 0) && (nIndex < xText.getCharacter(i)) ) + aBuffer.append( xText.getCharacter(nIndex) ); + else + aBuffer.append( '#' ); + } + } + catch( IndexOutOfBoundsException e ) + { } // ignore errors + + return aBuffer.toString(); + } + + + private AccessibleTreeNode getAttributes( XAccessibleText xText, + AccessibleTreeNode aParent) + { + String[] aAttributeList = new String[] { + "CharBackColor", + "CharColor", + "CharEscapement", + "CharHeight", + "CharPosture", + "CharStrikeout", + "CharUnderline", + "CharWeight", + "ParaAdjust", + "ParaBottomMargin", + "ParaFirstLineIndent", + "ParaLeftMargin", + "ParaLineSpacing", + "ParaRightMargin", + "ParaTabStops"}; + + AccessibleTreeNode aRet; + + try + { + VectorNode aPortions = new VectorNode ("getAttributes", aParent); + + int nIndex = 0; + int nLength = xText.getCharacterCount(); + while( nIndex < nLength ) + { + // get attribute run + String aPortion = null; + try + { + aPortion = xText.getTextAtIndex( + nIndex, AccessibleTextType.ATTRIBUTE_RUN).SegmentText; + } + catch(com.sun.star.lang.IllegalArgumentException e) + { + aPortion = ""; + } + + // get attributes and make node with attribute children + PropertyValue[] aValues = xText.getCharacterAttributes(nIndex, aAttributeList); + VectorNode aAttrs = new VectorNode (aPortion, aPortions); + for( int i = 0; i < aValues.length; i++ ) + { + new StringNode( aValues[i].Name + ": " + aValues[i].Value, + aAttrs ); + } + + // get next portion, but advance at least one + nIndex += (aPortion.length() > 0) ? aPortion.length() : 1; + } + + aRet = aPortions; + } + catch( UnknownPropertyException e ) + { + aRet = new StringNode( "Exception caught:" + e, aParent ); + } + catch( IndexOutOfBoundsException e ) + { + aRet = new StringNode( "Exception caught:" + e, aParent ); + } + + return aRet; + } + + + private static String[] aTextActions = + new String[] { "select...", "copy..." }; + private static String[] aEditableTextActions = + new String[] { "select...", "copy...", + "cut...", "paste...", "edit...", "format..." }; + + @Override + public String[] getActions (AccessibleTreeNode aNode) + { + XAccessibleEditableText xEText = null; + if (aNode instanceof AccTreeNode) + xEText = ((AccTreeNode)aNode).getEditText (); + + return (xEText == null) ? aTextActions : aEditableTextActions; + } + + @Override + public void performAction (AccessibleTreeNode aNode, int nIndex) + { + if ( ! (aNode instanceof AccTreeNode)) + return; + + AccTreeNode aATNode = (AccTreeNode)aNode; + TextActionDialog aDialog = null; + + // create proper dialog + switch( nIndex ) + { + case 0: + aDialog = new TextActionDialog( aATNode, + "Select range:", + "select" ) + { + @Override + boolean action( + JTextComponent aText, AccTreeNode aNode ) + throws IndexOutOfBoundsException + { + return aNode.getText().setSelection( + getSelectionStart(), + getSelectionEnd() ); + } + }; + break; + case 1: + aDialog = new TextActionDialog( aATNode, + "Select range and copy:", + "copy" ) + { + @Override + boolean action( + JTextComponent aText, AccTreeNode aNode ) + throws IndexOutOfBoundsException + { + return aNode.getText().copyText( + getSelectionStart(), + getSelectionEnd() ); + } + }; + break; + case 2: + aDialog = new TextActionDialog( aATNode, + "Select range and cut:", + "cut" ) + { + @Override + boolean action( + JTextComponent aText, AccTreeNode aNode ) + throws IndexOutOfBoundsException + { + return aNode.getEditText().cutText( + getSelectionStart(), + getSelectionEnd() ); + } + }; + break; + case 3: + aDialog = new TextActionDialog( aATNode, + "Place Caret and paste:", + "paste" ) + { + @Override + boolean action( + JTextComponent aText, AccTreeNode aNode ) + throws IndexOutOfBoundsException + { + return aNode.getEditText().pasteText( + aText.getCaretPosition() ); + } + }; + break; + case 4: + aDialog = new TextEditDialog( aATNode, "Edit text:", + "edit" ); + break; + case 5: + aDialog = new TextAttributeDialog( aATNode ); + break; + } + + if( aDialog != null ) + aDialog.setVisible(true); + } + +} + +/** + * Display a dialog with a text field and a pair of cancel/do-it buttons + */ +abstract class TextActionDialog extends JDialog + implements ActionListener +{ + private AccTreeNode aNode; + JTextArea aText; + private String sName; + private JCheckBox aIndexToggle; + + public TextActionDialog( AccTreeNode aNd, + String sExplanation, + String sButtonText ) + { + super( AccessibilityWorkBench.Instance() ); + + aNode = aNd; + sName = sButtonText; + init( sExplanation, aNode.getText().getText(), sButtonText ); + setSize( 350, 225 ); + } + + /** build dialog */ + protected void init( String sExplanation, + String sText, + String sButtonText ) + { + setTitle( sName ); + + // vertical stacking of the elements + Container aContent = getContentPane(); + + // label with explanation + if( sExplanation.length() > 0 ) + aContent.add( new JLabel( sExplanation ), BorderLayout.NORTH ); + + // the text field + aText = new JTextArea(); + aText.setText( sText ); + aText.setColumns( Math.min( Math.max( 40, sText.length() ), 20 ) ); + aText.setRows( sText.length() / 40 + 1 ); + aText.setLineWrap( true ); + aText.setEditable( false ); + aContent.add( aText, BorderLayout.CENTER ); + + JPanel aButtons = new JPanel(); + aButtons.setLayout( new FlowLayout() ); + aIndexToggle = new JCheckBox( "reverse selection" ); + aButtons.add( aIndexToggle ); + JButton aActionButton = new JButton( sButtonText ); + aActionButton.setActionCommand( "Action" ); + aActionButton.addActionListener( this ); + aButtons.add( aActionButton ); + JButton aCancelButton = new JButton( "cancel" ); + aCancelButton.setActionCommand( "Cancel" ); + aCancelButton.addActionListener( this ); + aButtons.add( aCancelButton ); + + // add Panel with buttons + aContent.add( aButtons, BorderLayout.SOUTH ); + } + + private void cancel() + { + setVisible(false); + dispose(); + } + + private void action() + { + String sError = null; + try + { + boolean bSuccess = action( aText, aNode ); + if( !bSuccess ) + sError = "Can't execute"; + } + catch( IndexOutOfBoundsException e ) + { + sError = "Index out of bounds"; + } + + if( sError != null ) + JOptionPane.showMessageDialog( AccessibilityWorkBench.Instance(), + sError, sName, + JOptionPane.ERROR_MESSAGE); + + cancel(); + } + + public void actionPerformed(ActionEvent e) + { + String sCommand = e.getActionCommand(); + + if( "Cancel".equals( sCommand ) ) + cancel(); + else if( "Action".equals( sCommand ) ) + action(); + } + + + int getSelectionStart() { return getSelection(true); } + int getSelectionEnd() { return getSelection(false); } + private int getSelection(boolean bStart) + { + return ( bStart ^ aIndexToggle.isSelected() ) + ? aText.getSelectionStart() : aText.getSelectionEnd(); + } + + + + /** override this for dialog-specific action */ + abstract boolean action( JTextComponent aText, AccTreeNode aNode ) + throws IndexOutOfBoundsException; +} + + +class TextEditDialog extends TextActionDialog +{ + public TextEditDialog( AccTreeNode aNode, + String sExplanation, + String sButtonText ) + { + super( aNode, sExplanation, sButtonText ); + } + + @Override + protected void init( String sExplanation, + String sText, + String sButtonText ) + { + super.init( sExplanation, sText, sButtonText ); + aText.setEditable( true ); + } + + + /** edit the text */ + @Override + boolean action( JTextComponent aText, AccTreeNode aNode ) + { + // is this text editable? if not, fudge you and return + XAccessibleEditableText xEdit = aNode.getEditText(); + return ( xEdit == null ) ? false : + updateText( xEdit, aText.getText() ); + } + + + /** update the text */ + private boolean updateText( XAccessibleEditableText xEdit, String sNew ) + { + String sOld = xEdit.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; + } +} + + +class TextAttributeDialog extends TextActionDialog +{ + public TextAttributeDialog( + AccTreeNode aNode ) + { + super( aNode, "Choose attributes, select text, and press 'Set':", + "set" ); + } + + private JCheckBox aBold, aUnderline, aItalics; + private Color aForeground, aBackground; + + @Override + protected void init( String sExplanation, + String sText, + String sButtonText ) + { + super.init( sExplanation, sText, sButtonText ); + + aForeground = Color.black; + aBackground = Color.white; + + JPanel aAttr = new JPanel(); + aAttr.setLayout( new BoxLayout( aAttr, BoxLayout.Y_AXIS ) ); + + aBold = new JCheckBox( "bold" ); + aUnderline = new JCheckBox( "underline" ); + aItalics = new JCheckBox( "italics" ); + + JButton aForeButton = new JButton("Foreground", new ColorIcon(true)); + aForeButton.addActionListener( new ActionListener() { + public void actionPerformed(ActionEvent e) + { + aForeground = JColorChooser.showDialog( + TextAttributeDialog.this, + "Select Foreground Color", + aForeground); + } + } ); + + JButton aBackButton = new JButton("Background", new ColorIcon(false)); + aBackButton.addActionListener( new ActionListener() { + public void actionPerformed(ActionEvent e) + { + aBackground = JColorChooser.showDialog( + TextAttributeDialog.this, + "Select Background Color", + aBackground); + } + } ); + + aAttr.add( aBold ); + aAttr.add( aUnderline ); + aAttr.add( aItalics ); + aAttr.add( aForeButton ); + aAttr.add( aBackButton ); + + getContentPane().add( aAttr, BorderLayout.WEST ); + } + + + private class ColorIcon implements Icon + { + private boolean bForeground; + private static final int nHeight = 16; + private static final int nWidth = 16; + + public ColorIcon(boolean bWhich) { bForeground = bWhich; } + public int getIconHeight() { return nHeight; } + public int getIconWidth() { return nWidth; } + public void paintIcon(Component c, Graphics g, int x, int y) + { + g.setColor( getColor() ); + g.fillRect( x, y, nHeight, nWidth ); + g.setColor( c.getForeground() ); + g.drawRect( x, y, nHeight, nWidth ); + } + Color getColor() + { + return bForeground ? aForeground : aBackground; + } + } + + + + /** edit the text */ + @Override + boolean action( JTextComponent aText, AccTreeNode aNode ) + throws IndexOutOfBoundsException + { + // is this text editable? if not, fudge you and return + XAccessibleEditableText xEdit = aNode.getEditText(); + boolean bSuccess = false; + if( xEdit != null ) + { + PropertyValue[] aSequence = new PropertyValue[6]; + aSequence[0] = new PropertyValue(); + aSequence[0].Name = "CharWeight"; + aSequence[0].Value = Integer.valueOf( aBold.isSelected() ? 150 : 100 ); + aSequence[1] = new PropertyValue(); + aSequence[1].Name = "CharUnderline"; + aSequence[1].Value = Integer.valueOf( aUnderline.isSelected() ? 1 : 0 ); + aSequence[2] = new PropertyValue(); + aSequence[2].Name = "CharBackColor"; + aSequence[2].Value = Integer.valueOf( aBackground.getRGB() ); + aSequence[3] = new PropertyValue(); + aSequence[3].Name = "CharColor"; + aSequence[3].Value = Integer.valueOf( aForeground.getRGB() ); + aSequence[4] = new PropertyValue(); + aSequence[4].Name = "CharPosture"; + aSequence[4].Value = Integer.valueOf( aItalics.isSelected() ? 1 : 0 ); + aSequence[5] = new PropertyValue(); + aSequence[5].Name = "CharBackTransparent"; + aSequence[5].Value = Boolean.FALSE; + + bSuccess = xEdit.setAttributes( getSelectionStart(), + getSelectionEnd(), + aSequence ); + } + return bSuccess; + } + +} diff --git a/toolkit/test/accessibility/AccessibleTreeHandler.java b/toolkit/test/accessibility/AccessibleTreeHandler.java new file mode 100644 index 000000000..e022dbae8 --- /dev/null +++ b/toolkit/test/accessibility/AccessibleTreeHandler.java @@ -0,0 +1,130 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import com.sun.star.accessibility.XAccessible; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.lang.IndexOutOfBoundsException; + + +/** + * Map the tree of accessibility objects into their + * AccessibilityTreeModel counterparts. + */ +class AccessibleTreeHandler + extends NodeHandler +{ + private XAccessibleContext mxContext; + + @Override + public NodeHandler createHandler (XAccessibleContext xContext) + { + if (xContext != null) + return new AccessibleTreeHandler (xContext); + else + return null; + } + + public AccessibleTreeHandler () + { + super(); + mxContext = null; + } + + private AccessibleTreeHandler (XAccessibleContext xContext) + { + super(); + mxContext = xContext; + if (mxContext != null) + // Add one to the number of children to include the string node + // that tells you how many children there are. + synchronized (maChildList) + { + maChildList.setSize (1 + mxContext.getAccessibleChildCount()); + } + } + + @Override + public AccessibleTreeNode createChild (AccessibleTreeNode aParent, int nIndex) + { + AccessibleTreeNode aChild = null; + if (mxContext != null) + { + if (nIndex == 0) + aChild = new StringNode ("Child count: " + mxContext.getAccessibleChildCount(), + aParent); + else + { + // Lower index to skip the string node. + nIndex -= 1; + try + { + XAccessible xChild = mxContext.getAccessibleChild (nIndex); + aChild = NodeFactory.Instance().createDefaultNode ( + xChild, aParent); + } + catch( IndexOutOfBoundsException e ) + { + aChild = new StringNode ("ERROR: no child with index " + nIndex, aParent); + } + } + } + else + aChild = new StringNode ("XAccessibleContext interface not supported", aParent); + return aChild; + } + + /** Try to add the specified accessible child into the lists of + children. The insertion position is determined from the + getIndexInParent method of the child. + */ + public AccessibleTreeNode addAccessibleChild (AccessibleTreeNode aParent, XAccessible xChild) + { + AccessibleTreeNode aChild = null; + + if (xChild != null) + { + XAccessibleContext xContext = xChild.getAccessibleContext(); + if (xContext != null) + { + int nIndex = xContext.getAccessibleIndexInParent() + 1; + synchronized (maChildList) + { + if ((nIndex >= 0) || (nIndex <= maChildList.size())) + { + aChild = NodeFactory.Instance().createDefaultNode (xChild, aParent); + maChildList.add (nIndex, aChild); + } + } + } + } + return aChild; + } + + + /** Update only the child count node. Trust on other ways to update the + accessible children. + */ + @Override + public void update (AccessibleTreeNode aNode) + { + synchronized (maChildList) + { + maChildList.set (0, null); + } + } +} diff --git a/toolkit/test/accessibility/AccessibleTreeNode.java b/toolkit/test/accessibility/AccessibleTreeNode.java new file mode 100644 index 000000000..22fa035f9 --- /dev/null +++ b/toolkit/test/accessibility/AccessibleTreeNode.java @@ -0,0 +1,113 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import java.util.ArrayList; + +import com.sun.star.lang.IndexOutOfBoundsException; + +/** + Base class for all tree nodes. + */ +abstract class AccessibleTreeNode +{ + /// The parent node. It is null for the root node. + private AccessibleTreeNode maParent; + + /// The object to be displayed. + private final Object maDisplayObject; + + public AccessibleTreeNode (Object aDisplayObject, AccessibleTreeNode aParent) + { + maDisplayObject = aDisplayObject; + maParent = aParent; + } + + public void update () + { + // Empty + } + + public AccessibleTreeNode getParent () + { + return maParent; + } + + public int getChildCount () + { + return 0; + } + + public AccessibleTreeNode getChild (int nIndex) + throws IndexOutOfBoundsException + { + throw new IndexOutOfBoundsException(); + } + + public AccessibleTreeNode getChildNoCreate (int nIndex) + throws IndexOutOfBoundsException + { + throw new IndexOutOfBoundsException(); + } + + public boolean removeChild (int nIndex) + throws IndexOutOfBoundsException + { + throw new IndexOutOfBoundsException(); + } + + public abstract int indexOf (AccessibleTreeNode aNode); + + /** Create a path to this node by first asking the parent for its path + and then appending this object. + */ + public void createPath (java.util.List<AccessibleTreeNode> aPath) + { + if (maParent != null) + maParent.createPath (aPath); + aPath.add (this); + } + + public Object[] createPath () + { + ArrayList<AccessibleTreeNode> aPath = new ArrayList<AccessibleTreeNode> (1); + createPath (aPath); + return aPath.toArray(); + } + + public boolean isLeaf() + { + return true; + } + + @Override + public String toString() + { + return maDisplayObject.toString(); + } + + /** get names of supported actions */ + public String[] getActions () + { + return new String[] {}; + } + + /** perform action */ + public void performAction (int nIndex) + { + } +} diff --git a/toolkit/test/accessibility/AccessibleUNOHandler.java b/toolkit/test/accessibility/AccessibleUNOHandler.java new file mode 100644 index 000000000..1ee522b27 --- /dev/null +++ b/toolkit/test/accessibility/AccessibleUNOHandler.java @@ -0,0 +1,126 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import com.sun.star.uno.UnoRuntime; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.lang.XServiceInfo; +import com.sun.star.lang.XTypeProvider; +import com.sun.star.uno.Type; + + +/** This handler displays lower level UNO information. These are the + supported services, interfaces, and the implementation name. +*/ +class AccessibleUNOHandler + extends NodeHandler +{ + @Override + public NodeHandler createHandler (XAccessibleContext xContext) + { + if (xContext == null) + return null; + else + { + AccessibleUNOHandler h = new AccessibleUNOHandler(); + h.maChildList.setSize (3); + return h; + } + } + + private XServiceInfo GetServiceInfo (AccessibleTreeNode aNode) + { + XServiceInfo xServiceInfo = null; + if (aNode instanceof AccTreeNode) + xServiceInfo = UnoRuntime.queryInterface( + XServiceInfo.class, ((AccTreeNode)aNode).getContext()); + return xServiceInfo; + } + private XTypeProvider GetTypeProvider (AccessibleTreeNode aNode) + { + XTypeProvider xTypeProvider = null; + if (aNode instanceof AccTreeNode) + xTypeProvider = UnoRuntime.queryInterface( + XTypeProvider.class, ((AccTreeNode)aNode).getContext()); + return xTypeProvider; + } + + @Override + public AccessibleTreeNode createChild (AccessibleTreeNode aParent, + int nIndex) + { + AccessibleTreeNode aChild = null; + XServiceInfo xServiceInfo; + switch (nIndex) + { + case 0 : // Implementation name. + xServiceInfo = GetServiceInfo (aParent); + aChild = new StringNode ("Implementation name: " + + (xServiceInfo!=null ? xServiceInfo.getImplementationName() + : "<XServiceInfo not supported>"), + aParent); + break; + case 1 : + xServiceInfo = GetServiceInfo (aParent); + if (xServiceInfo == null) + aChild = new StringNode ( + "Supported services: <XServiceInfo not supported>", + aParent); + else + aChild = CreateServiceTree (aParent, xServiceInfo); + break; + case 2 : + XTypeProvider xTypeProvider = GetTypeProvider (aParent); + if (xTypeProvider == null) + aChild = new StringNode ( + "Supported interfaces: <XTypeProvider not supported>", + aParent); + else + aChild = CreateInterfaceTree (aParent, xTypeProvider); + break; + } + + return aChild; + } + + + private AccessibleTreeNode CreateServiceTree (AccessibleTreeNode aParent, + XServiceInfo xServiceInfo) + { + String[] aServiceNames = xServiceInfo.getSupportedServiceNames(); + VectorNode aNode = new VectorNode ("Supported Services", aParent); + + int nCount = aServiceNames.length; + for (int i=0; i<nCount; i++) + aNode.addChild (new StringNode (aServiceNames[i], aParent)); + + return aNode; + } + + private AccessibleTreeNode CreateInterfaceTree (AccessibleTreeNode aParent, + XTypeProvider xTypeProvider) + { + Type[] aTypes = xTypeProvider.getTypes(); + VectorNode aNode = new VectorNode ("Supported Interfaces", aParent); + + int nCount = aTypes.length; + for (int i=0; i<nCount; i++) + aNode.addChild (new StringNode (aTypes[i].getTypeName(), aParent)); + + return aNode; + } +} diff --git a/toolkit/test/accessibility/Canvas.java b/toolkit/test/accessibility/Canvas.java new file mode 100644 index 000000000..e9149ff82 --- /dev/null +++ b/toolkit/test/accessibility/Canvas.java @@ -0,0 +1,463 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.Toolkit; +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.geom.Rectangle2D; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTree; +import javax.swing.JViewport; +import javax.swing.event.TreeSelectionListener; +import javax.swing.tree.TreePath; + +/** This canvas displays accessible objects graphically. Each accessible + object with graphical representation is represented by an + CanvasShape object and has to be added by the + <member>addAccessible</member> member function. + + <p>The canvas listens to selection events of the associated JTree and + highlights the first selected node of that tree.</p> +*/ +class Canvas + extends JPanel + implements MouseListener, MouseMotionListener, TreeSelectionListener//, Scrollable +{ + // This constant can be passed to SetZoomMode to always show the whole screen. + public static final int WHOLE_SCREEN = -1; + + public Canvas () + { + super (true); + maObjects = new java.util.HashMap<AccTreeNode, CanvasShape> (); + maNodes = new ArrayList<AccTreeNode> (); + maObjectList = new ArrayList<CanvasShape> (); + addMouseListener (this); + addMouseMotionListener (this); + maBoundingBox = new Rectangle (0,0,100,100); + maTree = null; + mnHOffset = 0; + mnVOffset = 0; + mnScale = 1; + setShowText(false); + setShowDescriptions (true); + setShowNames (true); + setAntialiasing (true); + } + + /** Tell the canvas which tree view to use to highlight accessible + objects. + */ + public void setTree (JTree aTree) + { + if (maTree != null) + maTree.removeTreeSelectionListener (this); + maTree = aTree; + if (maTree != null) + maTree.addTreeSelectionListener (this); + } + + + + + public void addNode (AccTreeNode aNode) + { + if (maNodes.indexOf (aNode) == -1) + { + maNodes.add (aNode); + + CanvasShape aObject = maObjects.get (aNode); + if (aObject == null) + { + aObject = new CanvasShape (aNode); + // Update bounding box that includes all objects. + if (maObjects.isEmpty()) + maBoundingBox = aObject.getBBox(); + else + maBoundingBox = maBoundingBox.union (aObject.getBBox()); + + maObjects.put (aNode, aObject); + maObjectList.add (aObject); + + } + repaint (); + } + } + + public void removeNode (AccTreeNode aNode) + { + int i = maNodes.indexOf (aNode); + if( i != -1 ) + { + CanvasShape aObject = maObjects.get(aNode); + maObjectList.remove (aObject); + maObjects.remove (aNode); + maNodes.remove (aNode); + repaint (); + } + } + + public void updateNode (AccTreeNode aNode) + { + int i = maNodes.indexOf (aNode); + if (i != -1) + { + CanvasShape aObject = maObjects.get(aNode); + if (aObject != null) + aObject.update(); + } + } + + public void updateNodeGeometry (AccTreeNode aNode) + { + CanvasShape aObject = maObjects.get(aNode); + if (aObject != null) + aObject.updateGeometry(); + } + + public void clear () + { + while (maNodes.size() > 0) { + removeNode (maNodes.get(0)); + } + maNodes.clear(); + maObjects.clear(); + maObjectList.clear(); + } + + public boolean getShowDescriptions () + { + return Options.GetBoolean ("ShowDescriptions"); + } + + public void setShowDescriptions (boolean bNewValue) + { + Options.SetBoolean ("ShowDescriptions", bNewValue); + repaint (); + } + + public boolean getShowNames () + { + return Options.GetBoolean ("ShowNames"); + } + + public void setShowNames (boolean bNewValue) + { + Options.SetBoolean ("ShowNames", bNewValue); + repaint (); + } + + public boolean getAntialiasing () + { + return Options.GetBoolean ("Antialiasing"); + } + + public void setAntialiasing (boolean bNewValue) + { + Options.SetBoolean ("Antialiasing", bNewValue); + repaint (); + } + + public boolean getShowText () + { + return Options.GetBoolean ("ShowText"); + } + + private void setShowText (boolean bNewValue) + { + Options.SetBoolean ("ShowText", bNewValue); + repaint (); + } + + public void setZoomMode (int nZoomMode) + { + Options.SetInteger ("ZoomMode", nZoomMode); + repaint (); + } + + private int getZoomMode () + { + return Options.GetInteger ("ZoomMode", WHOLE_SCREEN); + } + + + @Override + public void paintComponent (Graphics g) + { + synchronized (g) + { + super.paintComponent (g); + + Graphics2D g2 = (Graphics2D)g; + if (getAntialiasing()) + g2.setRenderingHint (RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + else + g2.setRenderingHint (RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_OFF); + + setupTransformation (); + + // Draw the screen representation to give a hint of the location of the + // accessible object on the screen. + Dimension aScreenSize = Toolkit.getDefaultToolkit().getScreenSize(); + Rectangle2D.Double aScreen = new Rectangle2D.Double ( + mnHOffset, + mnVOffset, + mnScale*aScreenSize.getWidth(), + mnScale*aScreenSize.getHeight()); + // Fill the screen rectangle and draw a frame around it to increase its visibility. + g2.setColor (new Color (250,240,230)); + g2.fill (aScreen); + g2.setColor (Color.BLACK); + g2.draw (aScreen); + + synchronized (maObjectList) + { + int nCount = maObjectList.size(); + boolean bShowDescriptions = getShowDescriptions(); + boolean bShowNames = getShowNames(); + boolean bShowText = getShowText(); + for (int i=0; i<nCount; i++) + { + CanvasShape aCanvasShape = maObjectList.get(i); + aCanvasShape.paint ( + g2, + mnHOffset, mnVOffset, mnScale, + bShowDescriptions, bShowNames, bShowText); + } + } + + // Paint highlighted frame around active object as the last thing. + if (maActiveObject != null) + maActiveObject.paint_highlight ( + g2); + } + } + + + + + /** Set up the transformation so that the graphical display can show a + centered representation of the whole screen. + */ + private void setupTransformation () + { + // Turn off scrollbars when showing the whole screen. Otherwise show them when needed. + JViewport aViewport = (JViewport)getParent(); + JScrollPane aScrollPane = (JScrollPane)aViewport.getParent(); + int nZoomMode = getZoomMode(); + if (nZoomMode == WHOLE_SCREEN) + { + if (aScrollPane.getHorizontalScrollBarPolicy() + != JScrollPane.HORIZONTAL_SCROLLBAR_NEVER) + aScrollPane.setHorizontalScrollBarPolicy (JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + if (aScrollPane.getVerticalScrollBarPolicy() + != JScrollPane.VERTICAL_SCROLLBAR_NEVER) + aScrollPane.setVerticalScrollBarPolicy (JScrollPane.VERTICAL_SCROLLBAR_NEVER); + } + else + { + if (aScrollPane.getHorizontalScrollBarPolicy() + != JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED) + aScrollPane.setHorizontalScrollBarPolicy (JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + if (aScrollPane.getVerticalScrollBarPolicy() + != JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED) + aScrollPane.setVerticalScrollBarPolicy (JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); + } + + Dimension aScreenSize = Toolkit.getDefaultToolkit().getScreenSize(); + Dimension aWidgetSize = aViewport.getSize(); + { + if ((aScreenSize.getWidth() > 0) && (aScreenSize.getHeight() > 0)) + { + if (nZoomMode == WHOLE_SCREEN) + { + // Calculate the scales that would map the screen onto the + // widget in both of the coordinate axes and select the + // smaller + // of the two: it maps the screen onto the widget in both + // axes at the same time. + double nHScale = (aWidgetSize.getWidth() - 10) / aScreenSize.getWidth(); + double nVScale = (aWidgetSize.getHeight() - 10) / aScreenSize.getHeight(); + if (nHScale < nVScale) + mnScale = nHScale; + else + mnScale = nVScale; + } + else + { + mnScale = nZoomMode / 100.0; + } + + // Calculate offsets that center the scaled screen inside the widget. + mnHOffset = (aWidgetSize.getWidth() - mnScale*aScreenSize.getWidth()) / 2.0; + mnVOffset = (aWidgetSize.getHeight() - mnScale*aScreenSize.getHeight()) / 2.0; + if (mnHOffset < 0) + mnHOffset = 0; + if (mnVOffset < 0) + mnVOffset = 0; + + setPreferredSize (new Dimension ( + (int)(2*mnHOffset + mnScale * aScreenSize.getWidth()), + (int)(2*mnVOffset + mnScale * aScreenSize.getHeight()))); + revalidate (); + } + else + { + // In case of a degenerate (not yet initialized?) screen size + // use some meaningless default values. + mnScale = 1; + mnHOffset = 0; + mnVOffset = 0; + } + } + } + + + + /** Call getAccessibleAt to determine accessible object under mouse. + */ + public void mouseClicked (MouseEvent e) + { + } + + public void mousePressed (MouseEvent e) + { + CanvasShape aObjectUnderMouse = FindCanvasShapeUnderMouse (e); + highlightObject (aObjectUnderMouse); + if ((e.getModifiers() & InputEvent.CTRL_MASK) != 0) + { + maTree.expandPath (aObjectUnderMouse.getPath()); + } + } + + public void mouseReleased (MouseEvent e) + { + } + + public void mouseEntered (MouseEvent e) + { + } + + public void mouseExited (MouseEvent e) + { + // Deselect currently active object. + if (maActiveObject != null) + { + maActiveObject.unhighlight (); + maActiveObject = null; + repaint (); + } + } + + public void mouseDragged (MouseEvent e) + { + } + + public void mouseMoved (MouseEvent e) + { + if ((e.getModifiers() & InputEvent.SHIFT_MASK) != 0) + highlightObject (FindCanvasShapeUnderMouse (e)); + } + + private CanvasShape FindCanvasShapeUnderMouse (MouseEvent e) + { + CanvasShape aObjectUnderMouse = null; + int nCount = maObjectList.size(); + for (int i=nCount-1; i>=0; --i) + { + CanvasShape aObject = maObjectList.get(i); + if (aObject != null && aObject.contains (e.getX(),e.getY())) + { + aObjectUnderMouse = aObject; + break; + } + } + return aObjectUnderMouse; + } + + private boolean highlightObject (CanvasShape aNewActiveObject) + { + if (aNewActiveObject != maActiveObject) + { + if (maActiveObject != null) + maActiveObject.unhighlight(); + + maActiveObject = aNewActiveObject; + if (maActiveObject != null) + { + if (maTree != null) + { + maTree.scrollPathToVisible (maActiveObject.getPath()); + maTree.setSelectionPath (maActiveObject.getPath()); + maTree.repaint (); + } + maActiveObject.highlight (); + repaint (); + } + return true; + } + else + return false; + } + + /** Called when the selection of the tree changes. Highlight the + corresponding graphical representation of the first selected object. + */ + public void valueChanged (javax.swing.event.TreeSelectionEvent event) + { + TreePath aPath = event.getPath(); + Object aObject = aPath.getLastPathComponent(); + if (aObject instanceof AccTreeNode) + { + CanvasShape aCanvasShape = maObjects.get (aObject); + if (highlightObject (aCanvasShape)) + repaint(); + } + } + + private double + mnHOffset, + mnVOffset, + mnScale; + private CanvasShape + maActiveObject; + private final java.util.HashMap<AccTreeNode, CanvasShape> + maObjects; + private final List<CanvasShape> + maObjectList; + private final List<AccTreeNode> + maNodes; + private Rectangle + maBoundingBox; + private JTree + maTree; +} diff --git a/toolkit/test/accessibility/CanvasShape.java b/toolkit/test/accessibility/CanvasShape.java new file mode 100644 index 000000000..19aa983de --- /dev/null +++ b/toolkit/test/accessibility/CanvasShape.java @@ -0,0 +1,290 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import java.awt.*; +import javax.swing.tree.*; +import java.awt.geom.Rectangle2D; + +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.XAccessibleComponent; +import com.sun.star.accessibility.XAccessibleText; +import com.sun.star.accessibility.XAccessibleStateSet; +import com.sun.star.accessibility.AccessibleStateType; + +class CanvasShape +{ + private final Color maHighlightColor = Color.red; + private final Color maSelectionColor = Color.green; + private final Color maFocusColor = Color.blue; + + public CanvasShape (AccTreeNode aNode) + { + maNode = aNode; + mxContext = aNode.getContext(); + msName = "name unknown"; + msDescription = "description unknown"; + maShape = new Rectangle2D.Double (-10,-10,10,10); + maPosition = new Point (-10,-10); + maSize = new Dimension (10,10); + maFgColor = java.awt.Color.black; + maBgColor = Color.blue; + mbHighlighted = false; + mbSelected = false; + mbFocused = false; + mxComponent = aNode.getComponent(); + + update (); + } + + + + /** Update the data obtained from the xAccessible. + */ + public void update () + { + if (mxContext != null) + { + msName = mxContext.getAccessibleName(); + msDescription = mxContext.getAccessibleDescription(); + + // Extract the selected and focused flag. + XAccessibleStateSet xStateSet = mxContext.getAccessibleStateSet (); + if (xStateSet != null) + { + mbSelected = xStateSet.contains (AccessibleStateType.SELECTED); + mbFocused = xStateSet.contains (AccessibleStateType.FOCUSED); + } + } + + updateGeometry (); + if (mxComponent != null) + { + // Note: alpha values in office 0..255 have to be mapped to + // 255..0 in Java + Color aCol = new Color (mxComponent.getForeground(), true); + maFgColor = new Color (aCol.getRed (), + aCol.getGreen (), + aCol.getBlue (), + 0xff - aCol.getAlpha ()); + aCol = new Color (mxComponent.getBackground(), true); + maBgColor = new Color (aCol.getRed (), + aCol.getGreen (), + aCol.getBlue (), + 0xff - aCol.getAlpha ()); + } + } + + public void updateGeometry () + { + if (mxComponent != null) + { + com.sun.star.awt.Point aLocationOnScreen = mxComponent.getLocationOnScreen(); + com.sun.star.awt.Size aSizeOnScreen = mxComponent.getSize(); + maPosition = new Point ( + aLocationOnScreen.X, + aLocationOnScreen.Y); + maSize = new Dimension ( + aSizeOnScreen.Width, + aSizeOnScreen.Height); + } + } + + + /** Paint the object into the specified canvas. It is transformed + according to the specified offset and scale. + */ + public void paint (Graphics2D g, + double nXOffset, double nYOffset, double nScaleFactor, + boolean bShowDescription, boolean bShowName, boolean bShowText) + { + try{ + // Transform the object's position and size according to the + // specified offset and scale. + maShape = new Rectangle2D.Double ( + maPosition.x * nScaleFactor + nXOffset, + maPosition.y * nScaleFactor + nYOffset, + maSize.width * nScaleFactor, + maSize.height * nScaleFactor); + + // Fill the object's bounding box with its background color if it + // has no children. + if (mxContext.getAccessibleChildCount() == 0) + { + g.setColor (maBgColor); + g.fill (maShape); + } + + // Remove alpha channel from color before drawing the frame. + Color color = maFgColor; + if (maFgColor.getAlpha()<128) + color = new Color (maFgColor.getRed(), maFgColor.getGreen(), maFgColor.getBlue()); + g.setColor (color); + g.draw (maShape); + + if (mbFocused) + { + g.setColor (maFocusColor); + for (int x=0; x<=2; x++) + for (int y=0; y<=2; y++) + g.fill ( + new Rectangle2D.Double ( + maShape.x + x/2.0 * maShape.width-3, + maShape.y + y/2.0 * maShape.height-3, + 6, + 6)); + } + if (mbSelected) + { + g.setColor (maSelectionColor); + for (int x=0; x<=2; x++) + for (int y=0; y<=2; y++) + g.draw ( + new Rectangle2D.Double ( + maShape.x + x/2.0 * maShape.width-2, + maShape.y + y/2.0 * maShape.height-2, + 4, + 4)); + } + + // Write the object's text OR name and description. + g.setColor (maFgColor); + if (bShowName) + paintName (g); + if (bShowDescription) + paintDescription (g); + if (bShowText) + paintText (g); + } + catch (Exception e) + { // don't care + } + } + + public void paint_highlight (Graphics2D g) + { + if (mbHighlighted) + g.setColor (maHighlightColor); + else + g.setColor (maFgColor); + g.draw (maShape); + } + + + + + private void paintName (Graphics2D g) + { + g.drawString ("Name: " + msName, + (float)maShape.x+5, + (float)maShape.y+15); + } + + + + private void paintDescription (Graphics2D g) + { + g.drawString ("Description: " + msDescription, + (float)maShape.x+5, + (float)maShape.y+35); + } + + + + + private void paintText (Graphics2D g) + { + XAccessibleText xText = null; + // get XAccessibleText + xText = maNode.getText(); + + // Draw every character in the text string. + if (xText != null) + { + String sText = xText.getText(); + try + { + for(int i = 0; i < sText.length(); i++) + { + com.sun.star.awt.Rectangle aRect = + xText.getCharacterBounds(i); + + double x = maShape.x + aRect.X; + double y = maShape.y + aRect.Y + aRect.Height; + + g.drawString(sText.substring(i, i+1), (float)x, (float)y); + } + } + catch (com.sun.star.lang.IndexOutOfBoundsException e) + {} + } + } + + + + + /** Compute whether the specified point lies inside the object's + bounding box. + */ + public boolean contains (int x, int y) + { + return maShape.contains (x,y); + } + + public void highlight () + { + mbHighlighted = true; + } + + public void unhighlight () + { + mbHighlighted = false; + } + + public Rectangle getBBox () + { + return new Rectangle (maPosition, maSize); + } + + public TreePath getPath () + { + return new TreePath (maNode.createPath()); + } + + @Override + public String toString () + { + return ">"+msName+", "+msDescription+" +"+maPosition.x+"+"+maPosition.y + +"x"+maSize.width+"x"+maSize.height+"<"; + } + + private final AccTreeNode maNode; + private final XAccessibleContext mxContext; + private final XAccessibleComponent mxComponent; + private String msDescription, msName; + private Rectangle2D.Double maShape; + private Point maPosition; + private Dimension maSize; + private Color maFgColor, maBgColor; + private boolean + // Highlighting objects is an internal concept. Corresponds to selection in the tree view. + mbHighlighted, + // Set when the accessible object is selected. + mbSelected, + // Set when the accessible object is focused. + mbFocused; +} diff --git a/toolkit/test/accessibility/ChildEventHandler.java b/toolkit/test/accessibility/ChildEventHandler.java new file mode 100644 index 000000000..e36f3502a --- /dev/null +++ b/toolkit/test/accessibility/ChildEventHandler.java @@ -0,0 +1,66 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import com.sun.star.accessibility.XAccessible; +import com.sun.star.accessibility.AccessibleEventObject; +import com.sun.star.uno.UnoRuntime; + +import java.io.PrintStream; + +class ChildEventHandler + extends EventHandler +{ + public ChildEventHandler (AccessibleEventObject aEvent, AccessibilityTreeModel aTreeModel) + { + super (aEvent, aTreeModel); + mxOldChild = UnoRuntime.queryInterface( + XAccessible.class, aEvent.OldValue); + mxNewChild = UnoRuntime.queryInterface( + XAccessible.class, aEvent.NewValue); + } + + @Override + public void PrintOldAndNew (PrintStream out) + { + if (mxOldChild != null) + out.println (" removing child " + mxOldChild); + if (mxNewChild != null) + out.println (" adding child " + mxNewChild); + } + + @Override + public void Process () + { + // Insertion and removal of children should be mutually exclusive. + // But this is a test tool and should take everything into account. + if (mxOldChild != null) + { + maTreeModel.removeNode (mxOldChild.getAccessibleContext()); + maTreeModel.updateNode (mxEventSource, AccessibleTreeHandler.class); + } + + if (mxNewChild != null) + { + maTreeModel.addChild (mxEventSource, mxNewChild); + } + } + + + private final XAccessible mxOldChild; + private final XAccessible mxNewChild; +} diff --git a/toolkit/test/accessibility/ContextEventHandler.java b/toolkit/test/accessibility/ContextEventHandler.java new file mode 100644 index 000000000..12ffd0648 --- /dev/null +++ b/toolkit/test/accessibility/ContextEventHandler.java @@ -0,0 +1,70 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import com.sun.star.accessibility.AccessibleEventObject; +import com.sun.star.accessibility.AccessibleEventId; +import com.sun.star.uno.AnyConverter; + +import java.io.PrintStream; + +import tools.NameProvider; + +class ContextEventHandler + extends EventHandler +{ + public ContextEventHandler (AccessibleEventObject aEvent, AccessibilityTreeModel aTreeModel) + { + super (aEvent, aTreeModel); + } + + @Override + public void PrintOldAndNew (PrintStream out) + { + switch (mnEventId) + { + case AccessibleEventId.STATE_CHANGED: + try + { + int nOldValue = AnyConverter.toInt (maEvent.OldValue); + out.println (" turning off state " + nOldValue + " (" + + NameProvider.getStateName (nOldValue) + ")"); + } + catch (com.sun.star.lang.IllegalArgumentException e) + {} + try + { + int nNewValue = AnyConverter.toInt (maEvent.NewValue); + out.println (" turning on state " + nNewValue + " (" + + NameProvider.getStateName (nNewValue) + ")"); + } + catch (com.sun.star.lang.IllegalArgumentException e) + {} + break; + + default: + super.PrintOldAndNew (out); + } + + } + + @Override + public void Process () + { + maTreeModel.updateNode (mxEventSource, AccessibleContextHandler.class); + } +} diff --git a/toolkit/test/accessibility/EventHandler.java b/toolkit/test/accessibility/EventHandler.java new file mode 100644 index 000000000..ed1a7adb4 --- /dev/null +++ b/toolkit/test/accessibility/EventHandler.java @@ -0,0 +1,75 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import com.sun.star.accessibility.XAccessible; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.AccessibleEventObject; +import com.sun.star.uno.UnoRuntime; + +import java.io.PrintStream; + +import tools.NameProvider; + +/** Base class for handling of accessibility events. +*/ +class EventHandler +{ + public EventHandler (AccessibleEventObject aEvent, AccessibilityTreeModel aTreeModel) + { + maEvent = aEvent; + maTreeModel = aTreeModel; + + mnEventId = aEvent.EventId; + + mxEventSource = UnoRuntime.queryInterface( + XAccessibleContext.class, aEvent.Source); + if (mxEventSource == null) + { + XAccessible xAccessible = UnoRuntime.queryInterface( + XAccessible.class, aEvent.Source); + if (xAccessible != null) + mxEventSource = xAccessible.getAccessibleContext(); + } + } + + public void Print (PrintStream out) + { + out.println ("Event id is " + mnEventId + + " (" + NameProvider.getEventName(mnEventId)+")" + + " for " + mxEventSource.getAccessibleName() + " / " + + NameProvider.getRoleName (mxEventSource.getAccessibleRole())); + PrintOldAndNew (out); + } + + public void PrintOldAndNew (PrintStream out) + { + out.println (" old value is " + maEvent.OldValue); + out.println (" new value is " + maEvent.NewValue); + } + + public void Process () + { + System.out.println ("processing of event " + maEvent + " not implemented"); + } + + protected AccessibleEventObject maEvent; + protected AccessibilityTreeModel maTreeModel; + + protected int mnEventId; + protected XAccessibleContext mxEventSource; +} diff --git a/toolkit/test/accessibility/EventListener.java b/toolkit/test/accessibility/EventListener.java new file mode 100644 index 000000000..5d9141953 --- /dev/null +++ b/toolkit/test/accessibility/EventListener.java @@ -0,0 +1,108 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import com.sun.star.accessibility.*; + +/** Objects of this class (usually one, singleton?) listen to accessible + events of all objects in all trees. +*/ +public class EventListener +{ + private boolean mbVerbose = false; + + public EventListener (AccessibilityTreeModel aTreeModel) + { + maTreeModel = aTreeModel; + } + + + /** This method handles accessibility objects that are being disposed. + */ + public void disposing (XAccessibleContext xContext) + { + if (mbVerbose) + System.out.println("disposing " + xContext); + maTreeModel.removeNode (xContext); + } + + /** This method is called from accessible objects that broadcast + modifications of themselves or from their children. The event is + processed only, except printing some messages, if the tree is not + locked. It should be locked during changes to its internal + structure like expanding nodes. + */ + public void notifyEvent (AccessibleEventObject aEvent) + { + EventHandler aHandler; + + switch (aEvent.EventId) + { + case AccessibleEventId.CHILD: + aHandler = new ChildEventHandler (aEvent, maTreeModel); + break; + + case AccessibleEventId.BOUNDRECT_CHANGED: + case AccessibleEventId.VISIBLE_DATA_CHANGED: + aHandler = new GeometryEventHandler (aEvent, maTreeModel); + break; + + + case AccessibleEventId.NAME_CHANGED: + case AccessibleEventId.DESCRIPTION_CHANGED: + case AccessibleEventId.STATE_CHANGED: + case AccessibleEventId.SELECTION_CHANGED: + aHandler = new ContextEventHandler (aEvent, maTreeModel); + break; + + case AccessibleEventId.TABLE_MODEL_CHANGED: + case AccessibleEventId.TABLE_CAPTION_CHANGED: + case AccessibleEventId.TABLE_COLUMN_DESCRIPTION_CHANGED: + case AccessibleEventId.TABLE_COLUMN_HEADER_CHANGED: + case AccessibleEventId.TABLE_ROW_DESCRIPTION_CHANGED: + case AccessibleEventId.TABLE_ROW_HEADER_CHANGED: + case AccessibleEventId.TABLE_SUMMARY_CHANGED: + aHandler = new TableEventHandler (aEvent, maTreeModel); + break; + + case AccessibleEventId.ACTION_CHANGED: + case AccessibleEventId.HYPERTEXT_CHANGED: + case AccessibleEventId.ACTIVE_DESCENDANT_CHANGED: + case AccessibleEventId.CARET_CHANGED: + case AccessibleEventId.TEXT_CHANGED: + case AccessibleEventId.VALUE_CHANGED: + aHandler = new EventHandler (aEvent, maTreeModel); + break; + + default: + aHandler = null; + break; + } + + if (aHandler == null) + System.out.println (" unhandled event"); + else + { + if (mbVerbose) + aHandler.Print (System.out); + aHandler.Process (); + } + } + + + private final AccessibilityTreeModel maTreeModel; +} diff --git a/toolkit/test/accessibility/EventQueue.java b/toolkit/test/accessibility/EventQueue.java new file mode 100644 index 000000000..b6f6c6415 --- /dev/null +++ b/toolkit/test/accessibility/EventQueue.java @@ -0,0 +1,140 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import java.util.LinkedList; + +/** The event queue singleton dispatches events received from OpenOffice.org + applications in a thread separate from the AWB main thread. + + The queue of event objects, LinkedList<Runnable> The queue object will + also serve as lock for the consumer/producer type synchronization. +*/ +class EventQueue + implements Runnable +{ + private boolean mbVerbose = false; + private boolean mbHandleDisposingEventsSynchronous = true; + + public synchronized static EventQueue Instance () + { + if (maInstance == null) + maInstance = new EventQueue (); + return maInstance; + } + + public void addEvent (Runnable aEvent) + { + synchronized (maMonitor) + { + if (mbVerbose) + System.out.println ("queueing regular event " + aEvent); + maRegularQueue.addLast (aEvent); + maMonitor.notify (); + } + } + + + public void addDisposingEvent (Runnable aEvent) + { + if (mbHandleDisposingEventsSynchronous) + aEvent.run (); + else + synchronized (maMonitor) + { + if (mbVerbose) + System.out.println ("queueing disposing event " + aEvent); + maDisposingQueue.addLast (aEvent); + maMonitor.notify (); + } + } + + + private EventQueue () + { + maRegularQueue = new LinkedList<Runnable>(); + maDisposingQueue = new LinkedList<Runnable>(); + new Thread(this, "AWB.EventQueue").start(); + } + + + /// This thread's main method: deliver all events + public void run() + { + // in an infinite loop, check for events to deliver, then + // wait on lock (which will be notified when new events arrive) + while( true ) + { + Runnable aEvent = null; + do + { + synchronized (maMonitor) + { + if (maDisposingQueue.size() > 0) + { + aEvent = maDisposingQueue.removeFirst(); + if (mbVerbose) + System.out.println ("delivering disposing event " + aEvent); + } + else if (maRegularQueue.size() > 0) + { + aEvent = maRegularQueue.removeFirst(); + if (mbVerbose) + System.out.println ("delivering regular event " + aEvent); + } + else + aEvent = null; + } + if (aEvent != null) + { + try + { + aEvent.run(); + } + catch( Throwable e ) + { + System.out.println( + "caught exception during event delivery: " + e ); + e.printStackTrace(); + } + } + } + while( aEvent != null ); + + try + { + synchronized (maMonitor) + { + maMonitor.wait(); + } + } + catch (Exception e) + { + // can't wait? odd! + System.err.println("Can't wait!"); + e.printStackTrace(); + } + } + } + + private static EventQueue maInstance = null; + private final Object maMonitor = new Object(); + private final LinkedList<Runnable> maRegularQueue; + private final LinkedList<Runnable> maDisposingQueue; +} + + diff --git a/toolkit/test/accessibility/GeometryEventHandler.java b/toolkit/test/accessibility/GeometryEventHandler.java new file mode 100644 index 000000000..c55ee3acc --- /dev/null +++ b/toolkit/test/accessibility/GeometryEventHandler.java @@ -0,0 +1,71 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import com.sun.star.accessibility.AccessibleEventObject; +import java.io.PrintStream; +import java.util.LinkedList; + +class GeometryEventHandler + extends EventHandler +{ + public GeometryEventHandler (AccessibleEventObject aEvent, AccessibilityTreeModel aTreeModel) + { + super (aEvent, aTreeModel); + } + + @Override + public void PrintOldAndNew (PrintStream out) + { + out.println (" children not relevant"); + } + + @Override + public void Process () + { + AccTreeNode aNode = maTreeModel.updateNode (mxEventSource, + AccessibleComponentHandler.class, + AccessibleExtendedComponentHandler.class); + + // Update the graphical representation of aNode in the Canvas. + Canvas aCanvas = maTreeModel.getCanvas(); + if (aCanvas != null) + { + // Iterate over all nodes in the sub-tree rooted in aNode. + LinkedList<Object> aShapeQueue = new LinkedList<Object>(); + aShapeQueue.addLast (aNode); + while (aShapeQueue.size() > 0) + { + // Remove the first node from the queue and update its + // graphical representation. + AccTreeNode aShapeNode = (AccTreeNode) aShapeQueue.getFirst(); + aShapeQueue.removeFirst(); + aCanvas.updateNodeGeometry (aShapeNode); + + // Add the node's children to the queue. + int nChildCount = maTreeModel.getChildCount (aShapeNode); + for (int i=0; i<nChildCount; i++) + { + Object aTreeNode = maTreeModel.getChildNoCreate (aShapeNode, i); + if (aTreeNode instanceof AccTreeNode) + aShapeQueue.addLast (aTreeNode); + } + } + aCanvas.repaint (); + } + } +} diff --git a/toolkit/test/accessibility/HelpWindow.java b/toolkit/test/accessibility/HelpWindow.java new file mode 100644 index 000000000..00c934067 --- /dev/null +++ b/toolkit/test/accessibility/HelpWindow.java @@ -0,0 +1,189 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.JEditorPane; +import javax.swing.JButton; +import java.net.URL; +import javax.swing.event.HyperlinkListener; +import javax.swing.event.HyperlinkEvent; +import java.net.MalformedURLException; +import java.io.File; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.GridBagLayout; +import java.awt.GridBagConstraints; +import java.awt.event.ActionListener; +import java.util.LinkedList; + +class HelpWindow + implements ActionListener +{ + public static synchronized HelpWindow Instance () + { + if (maInstance == null) + maInstance = new HelpWindow(); + return maInstance; + } + + public void loadFile (String sFilename) + { + File aFile = new File (sFilename); + try + { + loadURL (aFile.toURI().toURL()); + } + catch (MalformedURLException e) + { + e.printStackTrace (System.err); + } + } + + private void loadURL (URL aURL) + { + maHistory.addLast (aURL); + selectHistoryPage (maHistory.size()-1); + maFrame.toFront (); + } + + + + + private HelpWindow () + { + try + { + maCurrentHistoryEntry = -1; + maHistory = new LinkedList<URL>(); + + maFrame = new JFrame (); + maFrame.addWindowListener (new WindowAdapter () + { + @Override + public void windowClosing (WindowEvent e) + { + maInstance = null; + } + }); + maContent = createContentWidget(); + + maFrame.getContentPane().setLayout (new GridBagLayout()); + GridBagConstraints aConstraints = new GridBagConstraints (); + aConstraints.gridx = 0; + aConstraints.gridy = 0; + aConstraints.gridwidth = 3; + aConstraints.weightx = 1; + aConstraints.weighty = 1; + aConstraints.fill = GridBagConstraints.BOTH; + maFrame.getContentPane().add (new JScrollPane (maContent), aConstraints); + + aConstraints = new GridBagConstraints(); + aConstraints.gridx = 0; + aConstraints.gridy = 1; + maPrevButton = new JButton ("Prev"); + maFrame.getContentPane().add (maPrevButton, aConstraints); + maPrevButton.addActionListener (this); + + aConstraints = new GridBagConstraints(); + aConstraints.gridx = 1; + aConstraints.gridy = 1; + maNextButton = new JButton ("Next"); + maFrame.getContentPane().add (maNextButton, aConstraints); + maNextButton.addActionListener (this); + + aConstraints = new GridBagConstraints(); + aConstraints.gridx = 2; + aConstraints.gridy = 1; + aConstraints.anchor = GridBagConstraints.EAST; + JButton aButton = new JButton ("Close"); + maFrame.getContentPane().add (aButton, aConstraints); + aButton.addActionListener (this); + + maFrame.setSize (600,400); + maFrame.setVisible (true); + } + catch (Exception e) + {} + } + + public void actionPerformed (java.awt.event.ActionEvent e) + { + if (e.getActionCommand().equals("Prev")) + { + selectHistoryPage (maCurrentHistoryEntry - 1); + } + else if (e.getActionCommand().equals("Next")) + { + selectHistoryPage (maCurrentHistoryEntry + 1); + } + else if (e.getActionCommand().equals("Close")) + { + maFrame.dispose (); + maInstance = null; + } + } + + private JEditorPane createContentWidget () + { + JEditorPane aContent = new JEditorPane (); + aContent.setEditable (false); + aContent.addHyperlinkListener (new HyperlinkListener() + { + public void hyperlinkUpdate (HyperlinkEvent e) + { + if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) + HelpWindow.Instance().loadURL (e.getURL()); + } + }); + return aContent; + } + + private void selectHistoryPage (int i) + { + if (i < 0) + i = 0; + else if (i >= maHistory.size()-1) + i = maHistory.size()-1; + if (i != maCurrentHistoryEntry) + { + URL aURL = maHistory.get (i); + try + { + maContent.setPage (aURL); + } + catch (java.io.IOException ex) + { + ex.printStackTrace(System.err); + } + + maCurrentHistoryEntry = i; + } + + maPrevButton.setEnabled (maCurrentHistoryEntry > 0); + maNextButton.setEnabled (maCurrentHistoryEntry < maHistory.size()-1); + } + + private static HelpWindow maInstance = null; + private JFrame maFrame; + private JEditorPane maContent; + private LinkedList<URL> maHistory; + private int maCurrentHistoryEntry; + private JButton maPrevButton; + private JButton maNextButton; +} diff --git a/toolkit/test/accessibility/MessageArea.java b/toolkit/test/accessibility/MessageArea.java new file mode 100644 index 000000000..512cfaf73 --- /dev/null +++ b/toolkit/test/accessibility/MessageArea.java @@ -0,0 +1,131 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import java.awt.Font; +import java.awt.Color; +import java.awt.Graphics; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.JScrollBar; + + + +/** A message area displays text in a scrollable text widget. It is a + singleton. Other objects can access it directly to display messages. +*/ +public class MessageArea + extends JScrollPane +{ + public static synchronized MessageArea Instance () + { + if (saInstance == null) + saInstance = new MessageArea (); + return saInstance; + } + + + + + /** Create a new message area. This method is private because the class is + a singleton and may therefore not be instantiated from the outside. + */ + private MessageArea () + { + maText = new JTextArea(); + maText.setBackground (new Color (255,250,240)); + maText.setFont (new Font ("Helvetica", Font.PLAIN, 9)); + setViewportView (maText); + setVerticalScrollBarPolicy (JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); + setHorizontalScrollBarPolicy (JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); + + printMessage ( + "class path is " + System.getProperty ("java.class.path") + "\n"); + } + + + + + /** Show the given string at the end of the message area and scroll to make + it visible. Indent the string as requested. + */ + private static synchronized void print (int nIndentation, String aMessage) + { + while (nIndentation-- > 0) { + aMessage = " " + aMessage; + } + Instance().printMessage(aMessage); + } + + + + + /** Show the given string at the end of the message area and scroll to make + it visible. + */ + public static void println (String aMessage) + { + println (0, aMessage); + } + + + + + /** Show the given string at the end of the message area and scroll to make + it visible. + */ + private static void println (int nIndentation, String aMessage) + { + print (nIndentation, aMessage+"\n"); + } + + + + + @Override + public void paintComponent (Graphics g) + { + synchronized (g) + { + JScrollBar sb = getVerticalScrollBar(); + if (sb != null) + { + int nScrollBarValue = sb.getMaximum() - sb.getVisibleAmount() - 1; + sb.setValue (nScrollBarValue); + } + super.paintComponent (g); + } + } + + + + + /** Append the given string to the end of the text and scroll so that it + becomes visible. This is an internal method. Use one of the static + and public ones. + */ + private synchronized void printMessage (String aMessage) + { + maText.append (aMessage); + } + + + + + private static MessageArea saInstance = null; + private final JTextArea maText; +} diff --git a/toolkit/test/accessibility/NodeFactory.java b/toolkit/test/accessibility/NodeFactory.java new file mode 100644 index 000000000..7e1d9f585 --- /dev/null +++ b/toolkit/test/accessibility/NodeFactory.java @@ -0,0 +1,142 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import com.sun.star.accessibility.*; +import tools.NameProvider; + +/** This singleton class creates nodes for given accessible objects. +*/ +class NodeFactory +{ + public synchronized static NodeFactory Instance () + { + if (maInstance == null) + { + maInstance = new NodeFactory(); + } + return maInstance; + } + + private NodeFactory () + { + } + + + /** add default handlers based on the supported interfaces */ + private void addDefaultHandlers (AccTreeNode aNode, XAccessibleContext xContext) + { + if (false) + { + // Slow but complete version: try each handler type separately. + aNode.addHandler (maContextHandler.createHandler (xContext)); + aNode.addHandler (maTextHandler.createHandler (xContext)); + aNode.addHandler (maEditableTextHandler.createHandler (xContext)); + aNode.addHandler (maComponentHandler.createHandler (xContext)); + aNode.addHandler (maExtendedComponentHandler.createHandler (xContext)); + aNode.addHandler (maActionHandler.createHandler (xContext)); + aNode.addHandler (maImageHandler.createHandler (xContext)); + aNode.addHandler (maTableHandler.createHandler (xContext)); + aNode.addHandler (maCellHandler.createHandler (xContext)); + aNode.addHandler (maHypertextHandler.createHandler (xContext)); + aNode.addHandler (maHyperlinkHandler.createHandler (xContext)); + aNode.addHandler (maSelectionHandler.createHandler (xContext)); + aNode.addHandler (maRelationHandler.createHandler (xContext)); + aNode.addHandler (maUNOHandler.createHandler (xContext)); + aNode.addHandler (maTreeHandler.createHandler (xContext)); + } + else + { + // Exploit dependencies between interfaces. + NodeHandler aHandler; + aNode.addHandler (maContextHandler.createHandler (xContext)); + + aHandler = maTextHandler.createHandler (xContext); + if (aHandler != null) + { + aNode.addHandler (aHandler); + aNode.addHandler (maEditableTextHandler.createHandler (xContext)); + aNode.addHandler (maHypertextHandler.createHandler (xContext)); + aNode.addHandler (maHyperlinkHandler.createHandler (xContext)); + } + aHandler = maComponentHandler.createHandler (xContext); + if (aHandler != null) + { + aNode.addHandler (aHandler); + aNode.addHandler (maExtendedComponentHandler.createHandler (xContext)); + } + aNode.addHandler (maActionHandler.createHandler (xContext)); + aNode.addHandler (maImageHandler.createHandler (xContext)); + aNode.addHandler (maTableHandler.createHandler (xContext)); + aNode.addHandler (maRelationHandler.createHandler (xContext)); + aNode.addHandler (maCellHandler.createHandler (xContext)); + aNode.addHandler (maSelectionHandler.createHandler (xContext)); + aNode.addHandler (maUNOHandler.createHandler (xContext)); + aNode.addHandler (maTreeHandler.createHandler (xContext)); + } + } + + /** create a node with the default handlers */ + public AccTreeNode createDefaultNode (XAccessible xAccessible, AccessibleTreeNode aParent) + { + // default: aObject + aDisplay + String sDisplay; + + // if we are accessible, we use the context + name instead + XAccessibleContext xContext = null; + if (xAccessible != null) + xContext = xAccessible.getAccessibleContext(); + if (xContext != null) + { + sDisplay = xContext.getAccessibleName(); + if (sDisplay.length()==0) + { + sDisplay = "<no name> Role: " + + NameProvider.getRoleName ( + xContext.getAccessibleRole()); + } + } + else + sDisplay = "not accessible"; + + + // create node, and add default handlers + AccTreeNode aNode = new AccTreeNode (xContext, sDisplay, aParent); + addDefaultHandlers (aNode, xContext); + + return aNode; + } + + private static NodeFactory maInstance = null; + + // default handlers + private final NodeHandler maContextHandler = new AccessibleContextHandler(); + private final NodeHandler maTextHandler = new AccessibleTextHandler(); + private final NodeHandler maEditableTextHandler = new AccessibleEditableTextHandler(); + private final NodeHandler maComponentHandler = new AccessibleComponentHandler(); + private final NodeHandler maExtendedComponentHandler = new AccessibleExtendedComponentHandler(); + private final NodeHandler maActionHandler = new AccessibleActionHandler(); + private final NodeHandler maImageHandler = new AccessibleImageHandler(); + private final NodeHandler maTableHandler = new AccessibleTableHandler(); + private final NodeHandler maCellHandler = new AccessibleCellHandler(); + private final NodeHandler maHypertextHandler = new AccessibleHypertextHandler(); + private final NodeHandler maHyperlinkHandler = new AccessibleHyperlinkHandler(); + private final NodeHandler maSelectionHandler = new AccessibleSelectionHandler(); + private final NodeHandler maRelationHandler = new AccessibleRelationHandler(); + private final NodeHandler maTreeHandler = new AccessibleTreeHandler(); + private final NodeHandler maUNOHandler = new AccessibleUNOHandler(); +} diff --git a/toolkit/test/accessibility/NodeHandler.java b/toolkit/test/accessibility/NodeHandler.java new file mode 100644 index 000000000..69cff2c13 --- /dev/null +++ b/toolkit/test/accessibility/NodeHandler.java @@ -0,0 +1,133 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import java.util.Vector; + + +/** + * Map an arbitrary object into parts of a tree node. + */ +abstract class NodeHandler +{ + /** This vector is used as cache for the child objects. + */ + protected Vector<AccessibleTreeNode> maChildList; + + + public abstract NodeHandler createHandler ( + com.sun.star.accessibility.XAccessibleContext xContext); + + public NodeHandler () + { + maChildList = new Vector<AccessibleTreeNode> (); + } + + /** return the number of children this object has */ + public int getChildCount() + { + synchronized (maChildList) + { + return maChildList.size(); + } + } + + /** + * return a child object. Complex + * children have to be AccTreeNode instances. + * @see AccTreeNode + */ + public AccessibleTreeNode getChild (AccessibleTreeNode aParent, int nIndex) + { + synchronized (maChildList) + { + AccessibleTreeNode aChild = maChildList.get(nIndex); + if (aChild == null) + { + aChild = createChild (aParent, nIndex); + if (aChild == null) + aChild = new StringNode ("could not create child", aParent); + maChildList.set (nIndex, aChild); + } + return aChild; + } + } + + public AccessibleTreeNode getChildNoCreate (int nIndex) + { + synchronized (maChildList) + { + return maChildList.get(nIndex); + } + } + + /** Remove the specified child from the list of children. + */ + public boolean removeChild (int nIndex) + { + try + { + synchronized (maChildList) + { + System.out.println (" removing child at position " + nIndex + ": " + + maChildList.get (nIndex)); + maChildList.remove (nIndex); + } + } + catch (Exception e) + { + return false; + } + return true; + } + + public int indexOf (AccessibleTreeNode aNode) + { + synchronized (maChildList) + { + return maChildList.indexOf (aNode); + } + } + + /** Create a child object for the specified data. This method is called + usually from getChild and put there into the cache. + */ + public abstract AccessibleTreeNode createChild ( + AccessibleTreeNode aParent, int nIndex); + + + // The following methods support editing of children and actions. + // They have default implementations for no actions and read-only. + + + /** get names of supported actions */ + public String[] getActions (AccessibleTreeNode aNode) + { + return new String[] {}; + } + + /** perform action */ + public void performAction (AccessibleTreeNode aNode, int nIndex) + { + } + + /** Update all children. + */ + public void update (AccessibleTreeNode aNode) + { + } +} diff --git a/toolkit/test/accessibility/NodeMap.java b/toolkit/test/accessibility/NodeMap.java new file mode 100644 index 000000000..045c93371 --- /dev/null +++ b/toolkit/test/accessibility/NodeMap.java @@ -0,0 +1,117 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import com.sun.star.accessibility.XAccessibleContext; + +import java.util.HashMap; + +abstract class NodeMapCallback +{ + public abstract void Apply (AccTreeNode aNode); +} + +/** This map translates from XAccessible objects to our internal + representations. +*/ +class NodeMap +{ + public NodeMap () + { + maXAccessibleToNode = new HashMap<XAccessibleContext, AccessibleTreeNode> (); + } + + /** Clear the whole map. + */ + public void Clear () + { + maXAccessibleToNode.clear(); + } + + /** @return + whether the new node was different from a previous one + respectively was the first one set. + */ + public boolean InsertNode (XAccessibleContext xContext, AccessibleTreeNode aNode) + { + AccessibleTreeNode aPreviousNode = maXAccessibleToNode.put ( + xContext, + aNode); + return aPreviousNode != aNode; + } + + protected void RemoveNode (AccessibleTreeNode aNode) + { + try + { + if ((aNode != null) && (aNode instanceof AccTreeNode)) + { + maXAccessibleToNode.remove (((AccTreeNode)aNode).getContext()); + } + } + catch (Exception e) + { + System.out.println ("caught exception while removing node " + + aNode + " : " + e); + e.printStackTrace(); + } + } + + + public void ForEach (NodeMapCallback aFunctor) + { + Object[] aNodes = maXAccessibleToNode.values().toArray(); + for (int i=0; i<aNodes.length; i++) + { + if (aNodes[i] != null && (aNodes[i] instanceof AccTreeNode)) + { + try + { + aFunctor.Apply ((AccTreeNode)aNodes[i]); + } + catch (Exception e) + { + System.out.println ("caught exception applying functor to " + + i + "th node " + aNodes[i] + " : " + e); + e.printStackTrace(); + } + } + } + } + + AccessibleTreeNode GetNode (XAccessibleContext xContext) + { + return maXAccessibleToNode.get (xContext); + } + + AccessibleTreeNode GetNode (Object aObject) + { + if (aObject instanceof XAccessibleContext) + return GetNode ((XAccessibleContext)aObject); + else + return null; + } + + boolean ValueIsMember (AccessibleTreeNode aNode) + { + return maXAccessibleToNode.containsValue(aNode); + } + + + + private final HashMap<XAccessibleContext, AccessibleTreeNode> maXAccessibleToNode; +} diff --git a/toolkit/test/accessibility/OfficeConnection.java b/toolkit/test/accessibility/OfficeConnection.java new file mode 100644 index 000000000..357c7c6d1 --- /dev/null +++ b/toolkit/test/accessibility/OfficeConnection.java @@ -0,0 +1,98 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +// base classes +import com.sun.star.uno.UnoRuntime; + +import com.sun.star.bridge.XUnoUrlResolver; +import com.sun.star.lang.XMultiServiceFactory; + + +/** @descr This class establishes a connection to a LibreOffice application. + */ +public class OfficeConnection +{ + public OfficeConnection (int nPortNumber) + { + mnDefaultPort = nPortNumber; + connect (); + } + + /** @descr Return the service manager that represents the connected + LibreOffice application + */ + public XMultiServiceFactory getServiceManager () + { + if ( ! mbInitialized) + connect (); + return maServiceManager; + } + + /** @descr Connect to an already running LibreOffice application. + */ + private void connect () + { + connect (msDefaultHost, mnDefaultPort); + } + + /** @descr Connect to an already running LibreOffice application that has + been started with a command line argument like + "--accept=socket,host=localhost,port=5678;urp;" + */ + private void connect (String hostname, int portnumber) + { + mbInitialized = true; + // Set up connection string. + String sConnectString = "uno:socket,host=" + hostname + ",port=" + portnumber + + ";urp;StarOffice.ServiceManager"; + + + // connect to a running office and get the ServiceManager + try + { + // Create a URL Resolver. + XMultiServiceFactory aLocalServiceManager = + com.sun.star.comp.helper.Bootstrap.createSimpleServiceManager(); + XUnoUrlResolver aURLResolver = UnoRuntime.queryInterface ( + XUnoUrlResolver.class, + aLocalServiceManager.createInstance ("com.sun.star.bridge.UnoUrlResolver") + ); + + maServiceManager = UnoRuntime.queryInterface ( + XMultiServiceFactory.class, + aURLResolver.resolve (sConnectString) + ); + } + + catch (Exception e) + { + MessageArea.println ("Could not connect with " + sConnectString + " : " + e); + MessageArea.println ("Please start LibreOffice with " + + "\"--accept=socket,host=localhost,port=5678;urp;\""); + } + } + + private final int mnDefaultPort; + private static final String msDefaultHost = "localhost"; + private XMultiServiceFactory maServiceManager = null; + + /** A value of true just indicates that it has been tried to establish a connection, + not that has been successful. + */ + private boolean mbInitialized = false; +} diff --git a/toolkit/test/accessibility/Options.java b/toolkit/test/accessibility/Options.java new file mode 100644 index 000000000..6cf6e48f2 --- /dev/null +++ b/toolkit/test/accessibility/Options.java @@ -0,0 +1,122 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Properties; + +/** Load from and save options into a file. +*/ +class Options + extends Properties +{ + public static Options Instance () + { + if (saOptions == null) + saOptions = new Options (); + return saOptions; + } + + public static void SetBoolean (String sName, boolean bValue) + { + Instance().setProperty (sName, Boolean.toString(bValue)); + } + + public static boolean GetBoolean (String sName) + { + return Boolean.getBoolean(Instance().getProperty (sName)); + } + + public static void SetInteger (String sName, int nValue) + { + Instance().setProperty (sName, Integer.toString(nValue)); + } + + public static int GetInteger (String sName, int nDefault) + { + String sValue = Instance().getProperty (sName); + if (sValue == null) + return nDefault; + else + return Integer.parseInt (sValue); + } + + public void Load (String sBaseName) + { + FileInputStream fis = null; + try + { + fis = new FileInputStream (ProvideFile(sBaseName)); + load (fis); + } + catch (IOException e) + { + // Ignore a non-existing options file. + } + finally + { + try + { + if (fis != null) + fis.close(); + } + catch (IOException ex) + { + } + } + } + + public void Save (String sBaseName) + { + FileOutputStream fos = null; + try + { + fos = new FileOutputStream (ProvideFile(sBaseName)); + store (fos, null); + } + catch (IOException e) + { + } + finally + { + try + { + if (fos != null) + fos.close(); + } + catch (IOException ex) + { + } + } + } + + private Options () + { + } + + private File ProvideFile (String sBaseName) + { + return new File ( + System.getProperty ("user.home"), + sBaseName); + } + + private static Options saOptions = null; +} diff --git a/toolkit/test/accessibility/QueuedListener.java b/toolkit/test/accessibility/QueuedListener.java new file mode 100644 index 000000000..a34b33549 --- /dev/null +++ b/toolkit/test/accessibility/QueuedListener.java @@ -0,0 +1,70 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import com.sun.star.accessibility.*; +import com.sun.star.lang.EventObject; +import com.sun.star.uno.*; + +class QueuedListener + implements XAccessibleEventListener +{ + public QueuedListener (EventListener aListener) + { + maListener = aListener; + } + + + public void disposing( final EventObject aEvent) + { + XAccessibleContext xContext = UnoRuntime.queryInterface( + XAccessibleContext.class, aEvent.Source); + if (xContext == null) + { + XAccessible xAccessible = UnoRuntime.queryInterface( + XAccessible.class, aEvent.Source); + if (xAccessible != null) + xContext = xAccessible.getAccessibleContext(); + } + final XAccessibleContext xSource = xContext; + EventQueue.Instance().addDisposingEvent (new Runnable() + { + public void run() + { + if (QueuedListener.this.maListener != null) + QueuedListener.this.maListener.disposing (xSource); + } + } + ); + } + + public void notifyEvent( final AccessibleEventObject aEvent ) + { + EventQueue.Instance().addEvent (new Runnable() + { + public void run() + { + QueuedListener.this.maListener.notifyEvent( aEvent ); + } + } + ); + } + + private final EventListener maListener; +} + + diff --git a/toolkit/test/accessibility/QueuedTopWindowListener.java b/toolkit/test/accessibility/QueuedTopWindowListener.java new file mode 100644 index 000000000..2ac5c7849 --- /dev/null +++ b/toolkit/test/accessibility/QueuedTopWindowListener.java @@ -0,0 +1,97 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import com.sun.star.awt.XTopWindowListener; +import com.sun.star.lang.EventObject; + +class QueuedTopWindowListener + implements XTopWindowListener +{ + public QueuedTopWindowListener (TopWindowListener aListener) + { + maListener = aListener; + } + + public void windowOpened (final com.sun.star.lang.EventObject aEvent) throws RuntimeException + { + EventQueue.Instance().addEvent (new Runnable() + { + public void run() + { + QueuedTopWindowListener.this.maListener.windowOpened (aEvent); + } + } + ); + } + + public void windowClosing (final com.sun.star.lang.EventObject aEvent) throws RuntimeException + { + // Ignored. + } + + public void windowClosed (final com.sun.star.lang.EventObject aEvent) throws RuntimeException + { + EventQueue.Instance().addEvent (new Runnable() + { + public void run() + { + QueuedTopWindowListener.this.maListener.windowClosed (aEvent); + } + } + ); + } + + public void windowMinimized (final com.sun.star.lang.EventObject aEvent) + throws RuntimeException + { + System.out.println ("QueuedTopWindowListener: Top window minimized: " + aEvent); + } + + public void windowNormalized (final com.sun.star.lang.EventObject aEvent) + throws RuntimeException + { + System.out.println ("QueuedTopWindowListener: Top window normalized: " + aEvent); + } + + public void windowActivated (final com.sun.star.lang.EventObject aEvent) + throws RuntimeException + { + System.out.println ("QueuedTopWindowListener: Top window activated: " + aEvent); + } + + public void windowDeactivated (final com.sun.star.lang.EventObject aEvent) + throws RuntimeException + { + System.out.println ("QueuedTopWindowListener: Top window deactivated: " + aEvent); + } + + public void disposing( final EventObject aEvent) + { + EventQueue.Instance().addDisposingEvent (new Runnable() + { + public void run() + { + if (QueuedTopWindowListener.this.maListener != null) + QueuedTopWindowListener.this.maListener.disposing (aEvent); + } + } + ); + } + + private final TopWindowListener maListener; +} diff --git a/toolkit/test/accessibility/SelectionDialog.java b/toolkit/test/accessibility/SelectionDialog.java new file mode 100644 index 000000000..2f0c6d543 --- /dev/null +++ b/toolkit/test/accessibility/SelectionDialog.java @@ -0,0 +1,203 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Vector; + +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.ListSelectionModel; + +import com.sun.star.accessibility.XAccessible; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.XAccessibleSelection; +import com.sun.star.lang.IndexOutOfBoundsException; + +/** + * Display a dialog with a list-box of children and select/deselect buttons + */ +class SelectionDialog extends JDialog + implements ActionListener +{ + public SelectionDialog (AccTreeNode aNode) + { + super (AccessibilityWorkBench.Instance()); + + maNode = aNode; + + Layout(); + } + + /** build dialog */ + private void Layout () + { + setTitle( "Select" ); + + // vertical stacking of the elements + Container aContent = getContentPane(); + + // label with explanation + aContent.add( new JLabel( "Select/Deselect child elements" ), + BorderLayout.NORTH ); + + // the JListBox + maChildrenSelector = new JList (GetChildrenList()); + maChildrenSelector.setPreferredSize (new Dimension (500,300)); + aContent.add (maChildrenSelector, BorderLayout.CENTER); + maChildrenSelector.setSelectionMode (ListSelectionModel.SINGLE_SELECTION); + + JPanel aButtons = new JPanel(); + aButtons.setLayout( new FlowLayout() ); + + JButton aButton; + + aButton = new JButton( "Select" ); + aButton.setActionCommand( "Select" ); + aButton.addActionListener( this ); + aButtons.add( aButton ); + + aButton = new JButton( "Deselect" ); + aButton.setActionCommand( "Deselect" ); + aButton.addActionListener( this ); + aButtons.add( aButton ); + + aButton = new JButton( "Select all" ); + aButton.setActionCommand( "Select all" ); + aButton.addActionListener( this ); + aButtons.add( aButton ); + + aButton = new JButton( "Clear Selection" ); + aButton.setActionCommand( "Clear Selection" ); + aButton.addActionListener( this ); + aButtons.add( aButton ); + + aButton = new JButton( "Close" ); + aButton.setActionCommand( "Close" ); + aButton.addActionListener( this ); + aButtons.add( aButton ); + + // add Panel with buttons + aContent.add( aButtons, BorderLayout.SOUTH ); + + setSize( getPreferredSize() ); + } + + /** Get a list of all children + */ + private Vector<String> GetChildrenList () + { + mxSelection = maNode.getSelection(); + + XAccessibleContext xContext = maNode.getContext(); + int nCount = xContext.getAccessibleChildCount(); + Vector<String> aChildVector = new Vector<String>(); + for(int i = 0; i < nCount; i++) + { + try + { + XAccessible xChild = xContext.getAccessibleChild(i); + XAccessibleContext xChildContext = xChild.getAccessibleContext(); + aChildVector.add( i + " " + xChildContext.getAccessibleName()); + } + catch( IndexOutOfBoundsException e ) + { + aChildVector.add( "ERROR: IndexOutOfBoundsException" ); + } + } + return aChildVector; + } + + + private void close () + { + setVisible(false); + dispose(); + } + + private void select() + { + try + { + mxSelection.selectAccessibleChild (maChildrenSelector.getSelectedIndex()); + } + catch( IndexOutOfBoundsException e ) + { + JOptionPane.showMessageDialog( AccessibilityWorkBench.Instance(), + "Can't select: IndexOutofBounds", + "Error in selectAccessibleChild", + JOptionPane.ERROR_MESSAGE); + } + } + + private void deselect() + { + try + { + mxSelection.deselectAccessibleChild( + maChildrenSelector.getSelectedIndex()); + } + catch( IndexOutOfBoundsException e ) + { + JOptionPane.showMessageDialog( AccessibilityWorkBench.Instance(), + "Can't deselect: IndexOutofBounds", + "Error in deselectAccessibleChild", + JOptionPane.ERROR_MESSAGE); + } + } + + private void selectAll() + { + mxSelection.selectAllAccessibleChildren(); + } + + private void clearSelection() + { + mxSelection.clearAccessibleSelection(); + } + + + + public void actionPerformed(ActionEvent e) + { + String sCommand = e.getActionCommand(); + + if( "Close".equals( sCommand ) ) + close(); + else if ( "Select".equals( sCommand ) ) + select(); + else if ( "Deselect".equals( sCommand ) ) + deselect(); + else if ( "Clear Selection".equals( sCommand ) ) + clearSelection(); + else if ( "Select all".equals( sCommand ) ) + selectAll(); + } + + private JList maChildrenSelector; + private XAccessibleSelection mxSelection; + private final AccTreeNode maNode; +} diff --git a/toolkit/test/accessibility/SimpleOffice.java b/toolkit/test/accessibility/SimpleOffice.java new file mode 100644 index 000000000..ecb16a6e3 --- /dev/null +++ b/toolkit/test/accessibility/SimpleOffice.java @@ -0,0 +1,139 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import com.sun.star.frame.XDesktop; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XInterface; +import com.sun.star.accessibility.XAccessible; +import com.sun.star.awt.XExtendedToolkit; + + +/** This class tries to simplify some tasks like loading a document or + getting various objects. +*/ +public class SimpleOffice +{ + private XDesktop mxDesktop = null; + private OfficeConnection aConnection; + private int mnPortNumber; + + public SimpleOffice (int nPortNumber) + { + mnPortNumber = nPortNumber; + connect (); + getDesktop (); + } + + public void connect () + { + aConnection = new OfficeConnection (mnPortNumber); + mxDesktop = null; + getDesktop (); + } + + + + + + + + + + + + public XDesktop getDesktop () + { + if (mxDesktop != null) + return mxDesktop; + try + { + // Get the factory of the connected office. + XMultiServiceFactory xMSF = aConnection.getServiceManager (); + if (xMSF == null) + { + MessageArea.println ("can't connect to office"); + return null; + } + else + MessageArea.println ("Connected successfully."); + + // Create a new desktop. + mxDesktop = UnoRuntime.queryInterface( + XDesktop.class, + xMSF.createInstance ("com.sun.star.frame.Desktop") + ); + } + catch (Exception e) + { + MessageArea.println ("caught exception while creating desktop: " + + e); + } + + return mxDesktop; + } + + + /** Return a reference to the extended toolkit which is a broadcaster of + top window, key, and focus events. + */ + public XExtendedToolkit getExtendedToolkit () + { + XExtendedToolkit xToolkit = null; + try + { + // Get the factory of the connected office. + XMultiServiceFactory xMSF = aConnection.getServiceManager (); + if (xMSF != null) + { + xToolkit = UnoRuntime.queryInterface( + XExtendedToolkit.class, + xMSF.createInstance ("stardiv.Toolkit.VCLXToolkit") + ); + } + } + catch (Exception e) + { + MessageArea.println ("caught exception while creating extended toolkit: " + e); + } + + return xToolkit; + } + + + + public XAccessible getAccessibleObject (XInterface xObject) + { + XAccessible xAccessible = null; + try + { + xAccessible = UnoRuntime.queryInterface( + XAccessible.class, xObject); + } + catch (Exception e) + { + MessageArea.println ( + "caught exception while getting accessible object" + e); + e.printStackTrace(); + } + return xAccessible; + } + + + +} diff --git a/toolkit/test/accessibility/StringNode.java b/toolkit/test/accessibility/StringNode.java new file mode 100644 index 000000000..f6660e44a --- /dev/null +++ b/toolkit/test/accessibility/StringNode.java @@ -0,0 +1,36 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + + + +/** + Base class for all tree nodes. + */ +class StringNode + extends AccessibleTreeNode +{ + public StringNode (String aDisplayObject, AccessibleTreeNode aParent) + { + super (aDisplayObject, aParent); + } + + @Override + public int indexOf(AccessibleTreeNode aNode) { + return -1; + } +} diff --git a/toolkit/test/accessibility/TableEventHandler.java b/toolkit/test/accessibility/TableEventHandler.java new file mode 100644 index 000000000..09a3adbbc --- /dev/null +++ b/toolkit/test/accessibility/TableEventHandler.java @@ -0,0 +1,58 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import com.sun.star.accessibility.AccessibleEventObject; +import com.sun.star.accessibility.AccessibleEventId; +import com.sun.star.accessibility.AccessibleTableModelChange; +import java.io.PrintStream; + +class TableEventHandler + extends EventHandler +{ + public TableEventHandler (AccessibleEventObject aEvent, AccessibilityTreeModel aTreeModel) + { + super (aEvent, aTreeModel); + } + + @Override + public void PrintOldAndNew (PrintStream out) + { + switch (mnEventId) + { + case AccessibleEventId.TABLE_MODEL_CHANGED: + AccessibleTableModelChange aModelChange = + (AccessibleTableModelChange)maEvent.NewValue; + out.println( "Range: StartRow " + aModelChange.FirstRow + + " StartColumn " + aModelChange.FirstColumn + + " EndRow " + aModelChange.LastRow + + " EndColumn " + aModelChange.LastColumn + + " Id " + aModelChange.Type); + break; + default: + super.PrintOldAndNew (out); + } + } + + @Override + public void Process () + { + maTreeModel.updateNode (mxEventSource, AccessibleTableHandler.class); + } + + +} diff --git a/toolkit/test/accessibility/TopWindowListener.java b/toolkit/test/accessibility/TopWindowListener.java new file mode 100644 index 000000000..a35b51a5f --- /dev/null +++ b/toolkit/test/accessibility/TopWindowListener.java @@ -0,0 +1,175 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import com.sun.star.awt.XWindow; +import com.sun.star.awt.XExtendedToolkit; +import com.sun.star.accessibility.XAccessible; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.uno.UnoRuntime; + +/** Listen for top window events and create or delete children of the tree + model accordingly. +*/ +class TopWindowListener +{ + TopWindowListener (AccessibilityTreeModel aModel, SimpleOffice aOffice) + { + maModel = aModel; + maOffice = aOffice; + } + + + + + /** Use this function to initially fill the accessibility object tree + view with nodes for top level windows. + */ + public void Initialize () + { + XExtendedToolkit xToolkit = maOffice.getExtendedToolkit(); + if (xToolkit != null) + { + maModel.lock (); + int nTopWindowCount = xToolkit.getTopWindowCount(); + MessageArea.println ("There are " + nTopWindowCount + " top windows."); + for (int i=0; i<nTopWindowCount; i++) + { + try + { + XAccessible xAccessible = maOffice.getAccessibleObject( + xToolkit.getTopWindow (i)); + // Uncomment the following line to get the real root of + // the accessible tree that xAccessible belongs to. + // xAccessible = maOffice.getAccessibleRoot(xAccessible); + AddTopLevelNode (xAccessible); + } + catch (Exception e) + { + System.out.println ("caught exception: " + e); + e.printStackTrace(); + } + } + maModel.unlock ((AccessibleTreeNode)maModel.getRoot()); + } + } + + + + /** Add a new top level node which, to be exact, will be placed on the + second layer of the tree. + @param xNewTopLevelObject + The accessible object of the new top level window. + */ + private void AddTopLevelNode (XAccessible xNewTopLevelObject) + { + System.out.println ("adding top level window"); + if (xNewTopLevelObject != null) + { + XAccessibleContext xContext = xNewTopLevelObject.getAccessibleContext(); + if (xContext == null) + System.out.println ("top level window not accessible"); + else + { + Object aRootObject = maModel.getRoot(); + if (aRootObject instanceof VectorNode) + { + VectorNode aRoot = (VectorNode) aRootObject; + AccessibleTreeNode aNode = + NodeFactory.Instance().createDefaultNode (xNewTopLevelObject, aRoot); + aRoot.addChild (aNode); + maModel.fireTreeNodesInserted (maModel.createEvent (aRoot, aNode)); + } + } + } + } + + /** Remove an existing top level node from the tree. + @param xNewTopLevelObject + The accessible object to remove. + */ + private void RemoveTopLevelNode (XAccessible xTopLevelObject) + { + Object aObject = maModel.getRoot(); + if (aObject instanceof VectorNode && xTopLevelObject != null) + { + System.out.println ("removing node " + xTopLevelObject); + maModel.removeNode (xTopLevelObject.getAccessibleContext()); + } + } + + + + + + // XTopWindowListener + public void windowOpened (final com.sun.star.lang.EventObject aEvent) + throws RuntimeException + { + if (maModel != null) + { + XWindow xWindow = UnoRuntime.queryInterface( + XWindow.class, aEvent.Source); + if (xWindow == null) + System.out.println ("event source is no XWindow"); + else + { + XAccessible xAccessible = maOffice.getAccessibleObject(xWindow); + if (xAccessible == null) + System.out.println ("event source is no XAccessible"); + else + AddTopLevelNode (xAccessible); + } + } + } + + + + + public void windowClosed (final com.sun.star.lang.EventObject aEvent) + throws RuntimeException + { + if (maModel != null) + { + XWindow xWindow = UnoRuntime.queryInterface( + XWindow.class, aEvent.Source); + if (xWindow == null) + System.out.println ("event source is no XWindow"); + else + { + XAccessible xAccessible = maOffice.getAccessibleObject(xWindow); + if (xAccessible == null) + System.out.println ("event source is no XAccessible"); + else + RemoveTopLevelNode (xAccessible); + } + } + } + + public void disposing (final com.sun.star.lang.EventObject aEvent) + { + System.out.println ("Top window disposed: " + aEvent); + } + + + + + private final AccessibilityTreeModel + maModel; + private final SimpleOffice + maOffice; +} diff --git a/toolkit/test/accessibility/VectorNode.java b/toolkit/test/accessibility/VectorNode.java new file mode 100644 index 000000000..c1ffcd650 --- /dev/null +++ b/toolkit/test/accessibility/VectorNode.java @@ -0,0 +1,74 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +import java.util.ArrayList; + +import com.sun.star.lang.IndexOutOfBoundsException; + +/** The VectorNode class is a simple container whose list of children is + managed entirely by its owner. +*/ +class VectorNode + extends StringNode +{ + private final ArrayList<AccessibleTreeNode> maChildren; + + public VectorNode (String sDisplayObject, AccessibleTreeNode aParent) + { + super (sDisplayObject, aParent); + + maChildren = new ArrayList<AccessibleTreeNode> (); + } + + public void addChild (AccessibleTreeNode aChild) + { + maChildren.add (aChild); + } + + @Override + public int getChildCount () + { + return maChildren.size(); + } + + @Override + public AccessibleTreeNode getChild (int nIndex) + throws IndexOutOfBoundsException + { + return maChildren.get(nIndex); + } + + @Override + public boolean removeChild (int nIndex) + throws IndexOutOfBoundsException + { + return maChildren.remove (nIndex) != null; + } + + @Override + public int indexOf (AccessibleTreeNode aNode) + { + return maChildren.indexOf (aNode); + } + + @Override + public boolean isLeaf() + { + return maChildren.isEmpty(); + } +} diff --git a/toolkit/test/accessibility/about.html b/toolkit/test/accessibility/about.html new file mode 100644 index 000000000..ff24ed058 --- /dev/null +++ b/toolkit/test/accessibility/about.html @@ -0,0 +1,8 @@ +<html> +<body> +<center><h1>About AWB</h1> +<p>AWB, the <em>A</em>ccessibility <em>W</em>ork <em>B</em>ench.</p> +<p>Version 1.7</p> +</center> +</body> +</html>
\ No newline at end of file diff --git a/toolkit/test/accessibility/help.html b/toolkit/test/accessibility/help.html new file mode 100644 index 000000000..681fcead4 --- /dev/null +++ b/toolkit/test/accessibility/help.html @@ -0,0 +1,91 @@ +<html><body bgcolor="#fffaf0"> +<h1>Help for the AWB v1.7</h1> + +<p>The AWB, or <em>A</em>ccessibility <em>W</em>ork <em>B</em>ench, is a tool +for testing the implementation UNO Accessibility API.</p> + +<p>The main window is roughly divided into three areas: +<ul> +<li>The <a href="#treeview">tree view</a> on the left shows a part of the +accessibility tree of one or more StarOffice/OpenOffice applications.</li> +<li>The <a href="#graphicalview">graphical view</a> on the right side shows +a graphical representation of the accessibility objects in the tree. To +make objects missing in the graphical view visible expand the corresponding +nodes in the tree view.</li> + +<li>The text window at the bottom logs important messages.</li> +</ul> +</p> + +<h2><a name="treeview">Tree View</a></h2> +<p>The tree view has a top-level node for every open + document window of StarOffice/OpenOffice. Expand those nodes to make them + visible in the <a href="#graphicalview">graphical view</a>.</p> + +<h3>Nodes</h3> +<p>The nodes in the tree view belong to different classes, some of which + have children others do not: +<ul> +<li><b>Accessible Object</b><br> + The node represents an accessible object and has corresponding shape in + the graphical view. Only this kind of node gets highlighted by clicking + on those shapes.</li> +<li><b>Simple Property</b><br> + These leaves represent simple properties of their parent nodes. Examples + are the position, size, and color of an accessible object.</li> +<li><b>Complex Properties</b><br> + These nodes have children that are not accessible objects. Examples are + the lists of interfaces or services supported by an accessible object.</li> +</ul> +</p> + +<h3>Actions</h3> +<p>The tree view supports the following actions: +<ul><li>Left double click expands the node under the mouse pointer.</li> +<li>Right click shows a context menu. Entries, when supported, are: +<ul> +<li><b>Expand Shapes</b><br> +Expands all nodes in the sub-tree that lie on a path from the root to a shape.</li> +<li><b>Expand Subtree</b><br> +Expands all nodes in the sub-tree.</li> +<li><b>Select...</b><br> +Show a dialog that gives access to the XAccessibleSelection interface.</li> +<li><b>select...</b>, <b>copy...</b>, <b>cut...</b>, <b>paste...</b>, + <b>edit...</b>, <b>format...</b><br> +Show dialogs that give access to the XAccessibleText and +XAccessibleEditableText interfaces. +</ul></li> +</ul> +</p> + + + +<h2><a name="graphicalview">Graphical View</a></h2> +<p>The graphical view shows several properties of accessibility objects: +<ul> +<li>The bounding box is represented as a rectangle.</li> +<li>The background color of the accessible object is taken to draw the + bounding box rectangle.</li> +<li>The foreground color is used to fill the bounding box.</li> +<li>The accessible name is shown when the menu checkbox Options->Show Name + is check.</li> +<li>The accessible description is shown when the menu checkbox Options->Show + Descriptions is checked.</li> +<li>If the XAccessibleText interface is supported and the menu checkbox + Options->Show Text is checked then the text of the accessible object is + shown.</li> +</ul> +</p> + +<h3>Actions</h3> +<p>The graphical view supports three the following actions: +<ul> +<li>Left click (no modifiers) highlights the object under the mouse as well + as the corresponding node in the tree view.</li> +<li>Left click with Control modifier expands the object under the + mouse.</li> +<li>Mouse motion with Shift modifier highlights the object under the mouse + as well as the corresponding node in the tree view.</li> +</ul> + +</body></html> diff --git a/toolkit/test/accessibility/jawb.mf b/toolkit/test/accessibility/jawb.mf new file mode 100644 index 000000000..88aa2cc76 --- /dev/null +++ b/toolkit/test/accessibility/jawb.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: AccessibilityWorkBench +Class-Path: classes.jar libreoffice.jar java_uno.jar xt.jar xml-apis.jar diff --git a/toolkit/test/accessibility/news.html b/toolkit/test/accessibility/news.html new file mode 100644 index 000000000..7b5336ea4 --- /dev/null +++ b/toolkit/test/accessibility/news.html @@ -0,0 +1,36 @@ +<html><body bgcolor="#fffaf0"> +<h1>News for AWB v1.7.2</h1> +<p>Adaptation to recent UAA changes: +<ul> +<li>Introduction of +com.sun.star.accessibility.TextSegment structure that is returned by some +functions of the XAccessibleText interface.</li> +<li>Removal of the COLLAPSED state.</li> +<li>Renaming of XAccessibleSelectioni::selectAllAccessible to +selectAllAccessibleChildren.</li> +</ul> + +<h1>News for AWB v1.7</h1> + +<ul> + +<li>This help window.</li> + +<li>Integrated relocation of UAA files from drafts to final.</li> +<li>Control left click in the graphical view expands the object that has + been clicked at.</li> + +<li>Introduction of split panes for easily changing the size of the three +main widgets.</li> + +<li>The graphical view visualizes the screen size.</li> + +<li>Removed the "Load" button.</li> +<li>Removed the status line.</li> +<li>The tree view shows top level nodes only for document windows.</li> +<li>New "views" menu that allows you to select the zoom scale of the + graphical view.</li> + +</ul> + +</body></html>
\ No newline at end of file diff --git a/toolkit/test/accessibility/ov/ContextView.java b/toolkit/test/accessibility/ov/ContextView.java new file mode 100644 index 000000000..0dc97a67a --- /dev/null +++ b/toolkit/test/accessibility/ov/ContextView.java @@ -0,0 +1,140 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package ov; + +import java.awt.Color; +import java.awt.GridBagLayout; +import java.awt.GridBagConstraints; + +import java.awt.event.ActionListener; +import java.awt.event.ActionEvent; + +import javax.swing.JLabel; +import com.sun.star.accessibility.AccessibleEventId; +import com.sun.star.accessibility.AccessibleEventObject; +import com.sun.star.accessibility.XAccessibleContext; + +import tools.NameProvider; + +public class ContextView + extends ListeningObjectView + implements ActionListener +{ + public static ObjectView Create ( + ObjectViewContainer aContainer, + XAccessibleContext xContext) + { + System.out.println ("ContextView.CreateView"); + if (xContext != null) + return new ContextView (aContainer); + else + return null; + } + + private ContextView (ObjectViewContainer aContainer) + { + super (aContainer); + JLabel aNameLabel = new JLabel ("Name: "); + maName = new JLabel (""); + JLabel aDescriptionLabel = new JLabel ("Description: "); + maDescription = new JLabel (""); + JLabel maRoleLabel = new JLabel ("Role: "); + maRole = new JLabel (""); + + // Make the background of name and description white and opaque so + // that leading and trailing spaces become visible. + maName.setOpaque (true); + maName.setBackground (Color.WHITE); + maDescription.setOpaque (true); + maDescription.setBackground (Color.WHITE); + maRole.setOpaque (true); + maRole.setBackground (Color.WHITE); + + GridBagLayout aLayout = new GridBagLayout(); + setLayout (aLayout); + GridBagConstraints constraints = new GridBagConstraints (); + constraints.gridx = 0; + constraints.gridy = 0; + constraints.gridwidth = 1; + constraints.gridheight = 1; + constraints.weightx = 0; + constraints.weighty = 1; + constraints.anchor = GridBagConstraints.WEST; + constraints.fill = GridBagConstraints.NONE; + add (aNameLabel, constraints); + constraints.gridy = 1; + add (aDescriptionLabel, constraints); + constraints.gridy = 2; + add (maRoleLabel, constraints); + constraints.gridy = 0; + constraints.gridx = 1; + constraints.weightx = 2; + add (maName, constraints); + constraints.gridy = 1; + add (maDescription, constraints); + constraints.gridy = 2; + add (maRole, constraints); + } + + @Override + public void Update () + { + if (mxContext == null) + { + maName.setText ("<null object>"); + maDescription.setText ("<null object>"); + maRole.setText ("<null object>"); + } + else + { + maName.setText (mxContext.getAccessibleName()); + maDescription.setText (mxContext.getAccessibleDescription()); + maRole.setText (NameProvider.getRoleName (mxContext.getAccessibleRole())); + } + } + + @Override + public String GetTitle () + { + return "Context"; + } + + /** Listen for changes regarding displayed values. + */ + @Override + public void notifyEvent (AccessibleEventObject aEvent) + { + switch (aEvent.EventId) + { + case AccessibleEventId.NAME_CHANGED : + case AccessibleEventId.DESCRIPTION_CHANGED : + Update (); + } + } + + public void actionPerformed (ActionEvent aEvent) + { + } + + + private final JLabel + maName, + maDescription, + maRole; +} diff --git a/toolkit/test/accessibility/ov/FocusView.java b/toolkit/test/accessibility/ov/FocusView.java new file mode 100644 index 000000000..cca1a6a0c --- /dev/null +++ b/toolkit/test/accessibility/ov/FocusView.java @@ -0,0 +1,141 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package ov; + +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.JLabel; + +import com.sun.star.accessibility.AccessibleEventId; +import com.sun.star.accessibility.AccessibleEventObject; +import com.sun.star.accessibility.AccessibleStateType; +import com.sun.star.accessibility.XAccessibleComponent; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.XAccessibleStateSet; +import com.sun.star.uno.UnoRuntime; + +public class FocusView + extends ListeningObjectView + implements ActionListener +{ + /** Create a FocusView when the given object supports the + XAccessibleComponent interface. + */ + public static ObjectView Create ( + ObjectViewContainer aContainer, + XAccessibleContext xContext) + { + XAccessibleComponent xComponent = UnoRuntime.queryInterface( + XAccessibleComponent.class, xContext); + if (xComponent != null) + return new FocusView (aContainer); + else + return null; + } + + private FocusView (ObjectViewContainer aContainer) + { + super (aContainer); + + setLayout (new GridBagLayout()); + GridBagConstraints aConstraints = new GridBagConstraints (); + + maFocused = new JLabel (); + aConstraints.gridy = 0; + aConstraints.weightx = 1; + aConstraints.fill = GridBagConstraints.HORIZONTAL; + add (maFocused, aConstraints); + + maGrabFocus = new JButton ("grabFocus"); + aConstraints.gridy = 1; + aConstraints.fill = GridBagConstraints.NONE; + aConstraints.anchor = GridBagConstraints.WEST; + add (maGrabFocus, aConstraints); + + maGrabFocus.addActionListener (this); + } + + /** Additionally to the context store a reference to the + XAccessibleComponent interface. + */ + @Override + public void SetObject (XAccessibleContext xObject) + { + mxComponent = UnoRuntime.queryInterface( + XAccessibleComponent.class, xObject); + super.SetObject (xObject); + } + + @Override + synchronized public void Destroy () + { + super.Destroy(); + maGrabFocus.removeActionListener (this); + } + + @Override + synchronized public void Update () + { + if (mxContext == null) + { + maFocused.setText ("<null object>"); + maGrabFocus.setEnabled (false); + } + else + { + XAccessibleStateSet aStateSet = mxContext.getAccessibleStateSet(); + if (aStateSet.contains(AccessibleStateType.FOCUSED)) + maFocused.setText ("focused"); + else + maFocused.setText ("not focused"); + if (maGrabFocus != null) + maGrabFocus.setEnabled (true); + } + } + + @Override + public String GetTitle () + { + return "Focus"; + } + + synchronized public void actionPerformed (ActionEvent aEvent) + { + if (aEvent.getActionCommand().equals("grabFocus")) + { + mxComponent.grabFocus(); + } + } + + @Override + public void notifyEvent (AccessibleEventObject aEvent) + { + System.out.println (aEvent); + if (aEvent.EventId == AccessibleEventId.STATE_CHANGED) + Update (); + } + + private final JLabel maFocused; + private final JButton maGrabFocus; + private XAccessibleComponent mxComponent; +} diff --git a/toolkit/test/accessibility/ov/ListeningObjectView.java b/toolkit/test/accessibility/ov/ListeningObjectView.java new file mode 100644 index 000000000..63b5f8ff7 --- /dev/null +++ b/toolkit/test/accessibility/ov/ListeningObjectView.java @@ -0,0 +1,80 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package ov; + +import com.sun.star.accessibility.AccessibleEventObject; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.accessibility.XAccessibleEventBroadcaster; +import com.sun.star.accessibility.XAccessibleEventListener; +import com.sun.star.lang.EventObject; +import com.sun.star.uno.UnoRuntime; + +/** Base class for object views that registers as accessibility event + listener. +*/ +abstract class ListeningObjectView + extends ObjectView + implements XAccessibleEventListener +{ + public ListeningObjectView (ObjectViewContainer aContainer) + { + super (aContainer); + } + + /** Add this object as event listener at the broadcasting + accessible object. + */ + @Override + public void SetObject (XAccessibleContext xContext) + { + super.SetObject (xContext); + XAccessibleEventBroadcaster xBroadcaster = + UnoRuntime.queryInterface( + XAccessibleEventBroadcaster.class, xContext); + if (xBroadcaster != null) + xBroadcaster.addAccessibleEventListener (this); + } + + + /** Remove this object as event listener from the broadcasting + accessible object. + */ + @Override + public void Destroy () + { + super.Destroy (); + XAccessibleEventBroadcaster xBroadcaster = + UnoRuntime.queryInterface( + XAccessibleEventBroadcaster.class, mxContext); + if (xBroadcaster != null) + xBroadcaster.removeAccessibleEventListener (this); + } + + /** Derived classes have to implement this method to handle incoming + events. + */ + abstract public void notifyEvent (AccessibleEventObject aEvent); + + /** The disposing event is ignored per default. If a derived class is + interested it can overwrite this method. + */ + public void disposing (EventObject aEvent) + { + } +} diff --git a/toolkit/test/accessibility/ov/ObjectView.java b/toolkit/test/accessibility/ov/ObjectView.java new file mode 100644 index 000000000..7e1570b62 --- /dev/null +++ b/toolkit/test/accessibility/ov/ObjectView.java @@ -0,0 +1,80 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package ov; + +import javax.swing.JPanel; + +import com.sun.star.accessibility.XAccessibleContext; + +/** This is the base class for all object views that can be placed inside an + object view container. + + <p>When provided with a new accessible object the container will call + the Create method to create a new instance when certain conditions are + met. It then calls SetObject to pass the object to the instance. + Finally it calls Update.</p> + + <p>The SetObject and Update methods may be called for a new object + without calling Create first. In this way an existing instance is + recycled.</p> +*/ +abstract public class ObjectView + extends JPanel +{ + public ObjectView (ObjectViewContainer aContainer) + { + mxContext = null; + } + + /** Call this when you want the object to be destroyed. Release all + resources when called. + */ + public void Destroy () + { + } + + /** Tell the view to display information for a new accessible object. + @param xContext + The given object may be null. A typical behaviour in this case + would be to display a blank area. But is also possible to show + information about the last object. + */ + public void SetObject (XAccessibleContext xContext) + { + mxContext = xContext; + Update (); + } + + + /** This is a request of a repaint with the current state of the current + object. The current object may or may not be the same as the one + when Update() was called the last time. + */ + public void Update () + { + } + + + /** Return a string that is used as a title of an enclosing frame. + */ + abstract public String GetTitle (); + + /// Reference to the current object to display information about. + protected XAccessibleContext mxContext; +} diff --git a/toolkit/test/accessibility/ov/ObjectViewContainer.java b/toolkit/test/accessibility/ov/ObjectViewContainer.java new file mode 100644 index 000000000..c7e647d67 --- /dev/null +++ b/toolkit/test/accessibility/ov/ObjectViewContainer.java @@ -0,0 +1,152 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package ov; + +import java.awt.Component; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.util.ArrayList; + +import javax.swing.BorderFactory; +import javax.swing.JPanel; +import javax.swing.border.BevelBorder; +import javax.swing.border.Border; + +import com.sun.star.accessibility.XAccessibleContext; + + +public class ObjectViewContainer + extends JPanel +{ + private static interface IViewFactory { + ObjectView Create ( + ObjectViewContainer aContainer, + XAccessibleContext xContext); + } + + public ObjectViewContainer () + { + maViewTemplates = new ArrayList<IViewFactory>(); + maViewBorder = BorderFactory.createBevelBorder (BevelBorder.RAISED); + setLayout (new GridBagLayout ()); + + maViewTemplates.add(new IViewFactory() { + public ObjectView Create(ObjectViewContainer aContainer, + XAccessibleContext xContext) { + return ContextView.Create(aContainer, xContext); + } + }); + maViewTemplates.add(new IViewFactory() { + public ObjectView Create(ObjectViewContainer aContainer, + XAccessibleContext xContext) { + return FocusView.Create(aContainer, xContext); + } + }); + maViewTemplates.add(new IViewFactory() { + public ObjectView Create(ObjectViewContainer aContainer, + XAccessibleContext xContext) { + return TextView.Create(aContainer, xContext); + } + }); + } + + /** Remove all existing views and create new ones according to the + interfaces supported by the given object. + */ + public void SetObject (XAccessibleContext xContext) + { + // Call Destroy at all views to give them a chance to release their + // resources. + int n = getComponentCount(); + for (int i=0; i<n; i++) + ((ObjectView)getComponent(i)).Destroy(); + // Remove existing views. + removeAll (); + + // Add new views. + for (int i=0; i<maViewTemplates.size(); i++) + { + IViewFactory aViewFactory = maViewTemplates.get(i); + ObjectView aView = aViewFactory.Create(this, xContext); + Add (aView); + } + + UpdateLayoutManager (); + + // Now set the object at all views. + n = getComponentCount(); + for (int i=0; i<n; i++) + ((ObjectView)getComponent(i)).SetObject (xContext); + + setPreferredSize (getLayout().preferredLayoutSize (this)); + } + + + /** Add an object view and place it below all previously added views. + @param aView + This argument may be null. In this case nothing happens. + */ + private void Add (ObjectView aView) + { + if (aView != null) + { + GridBagConstraints constraints = new GridBagConstraints (); + constraints.gridx = 0; + constraints.gridy = getComponentCount(); + constraints.gridwidth = 1; + constraints.gridheight = 1; + constraints.weightx = 1; + constraints.weighty = 0; + constraints.ipadx = 2; + constraints.ipady = 5; + constraints.insets = new Insets (5,5,5,5); + constraints.anchor = GridBagConstraints.NORTH; + constraints.fill = GridBagConstraints.HORIZONTAL; + + aView.setBorder ( + BorderFactory.createTitledBorder ( + maViewBorder, aView.GetTitle())); + + add (aView, constraints); + } + } + + /** Update the layout manager by setting the vertical weight of the + bottom entry to 1 and so make it stretch to over the available + space. + + */ + private void UpdateLayoutManager () + { + // Adapt the layout manager. + if (getComponentCount() > 0) + { + Component aComponent = getComponent (getComponentCount()-1); + GridBagLayout aLayout = (GridBagLayout)getLayout(); + GridBagConstraints aConstraints = aLayout.getConstraints (aComponent); + aConstraints.weighty = 1; + aLayout.setConstraints (aComponent, aConstraints); + } + } + + private final Border maViewBorder; + /// List of view templates which are instantiated when new object is set. + private final ArrayList<IViewFactory> maViewTemplates; +} diff --git a/toolkit/test/accessibility/ov/TextView.java b/toolkit/test/accessibility/ov/TextView.java new file mode 100644 index 000000000..518d5ff50 --- /dev/null +++ b/toolkit/test/accessibility/ov/TextView.java @@ -0,0 +1,133 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package ov; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import javax.swing.JLabel; + +import com.sun.star.accessibility.AccessibleEventId; +import com.sun.star.accessibility.AccessibleEventObject; +import com.sun.star.accessibility.XAccessibleText; +import com.sun.star.accessibility.XAccessibleContext; +import com.sun.star.uno.UnoRuntime; + +public class TextView + extends ListeningObjectView +{ + /** Create a TextView when the given object supports the + XAccessibleText interface. + */ + public static ObjectView Create ( + ObjectViewContainer aContainer, + XAccessibleContext xContext) + { + XAccessibleText xText = UnoRuntime.queryInterface( + XAccessibleText.class, xContext); + if (xText != null) + return new TextView (aContainer); + else + return null; + } + + + private TextView (ObjectViewContainer aContainer) + { + super (aContainer); + + setLayout (new GridBagLayout()); + GridBagConstraints aConstraints = new GridBagConstraints (); + + JLabel aLabel = new JLabel ("Text:"); + aConstraints.gridy = 0; + aConstraints.weightx = 1; + aConstraints.fill = GridBagConstraints.HORIZONTAL; + add (aLabel, aConstraints); + + maTextLabel = new JLabel (""); + aConstraints.gridx = 1; + aConstraints.fill = GridBagConstraints.NONE; + aConstraints.anchor = GridBagConstraints.WEST; + add (maTextLabel, aConstraints); + + aLabel = new JLabel ("Caret position:"); + aConstraints.gridx = 0; + aConstraints.gridy = 1; + aConstraints.weightx = 1; + aConstraints.fill = GridBagConstraints.HORIZONTAL; + add (aLabel, aConstraints); + + maCaretPositionLabel = new JLabel (""); + aConstraints.gridx = 1; + aConstraints.fill = GridBagConstraints.NONE; + aConstraints.anchor = GridBagConstraints.WEST; + add (maCaretPositionLabel, aConstraints); + } + + + /** Additionally to the context store a reference to the + XAccessibleText interface. + */ + @Override + public void SetObject (XAccessibleContext xObject) + { + mxText = UnoRuntime.queryInterface( + XAccessibleText.class, xObject); + super.SetObject (xObject); + } + + @Override + synchronized public void Update () + { + if (mxText == null) + { + maTextLabel.setText ("<null object>"); + maCaretPositionLabel.setText ("<null object>"); + } + else + { + maTextLabel.setText (mxText.getText()); + maCaretPositionLabel.setText (Integer.toString(mxText.getCaretPosition())); + } + } + + @Override + public String GetTitle () + { + return "Text"; + } + + @Override + public void notifyEvent (AccessibleEventObject aEvent) + { + System.out.println (aEvent); + switch (aEvent.EventId) + { + case AccessibleEventId.TEXT_CHANGED : + case AccessibleEventId.CARET_CHANGED : + Update (); + break; + } + } + + private final JLabel + maTextLabel, + maCaretPositionLabel; + private XAccessibleText mxText; +} diff --git a/toolkit/test/accessibility/tools/NameProvider.java b/toolkit/test/accessibility/tools/NameProvider.java new file mode 100644 index 000000000..cc5ae469e --- /dev/null +++ b/toolkit/test/accessibility/tools/NameProvider.java @@ -0,0 +1,276 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +package tools; + +import java.util.HashMap; +import com.sun.star.accessibility.AccessibleStateType; +import com.sun.star.accessibility.AccessibleEventId; +import com.sun.star.accessibility.AccessibleRole; +import com.sun.star.accessibility.AccessibleRelationType; + + +/** Provide names for several accessibility constants groups. +*/ +public class NameProvider +{ + /** Return the name of the specified state. + @param nStateId + Id of the state for which to return its name. This is one of + the ids listed in the <type>AccessibleStateType</const> + constants group. + @return + Returns the name of the specified state or an empty string if an + invalid / unknown state id was given. + */ + public static String getStateName (int nStateId) + { + return maStateMap.get (Integer.valueOf(nStateId)); + } + + + /** Return the name of the specified event. + @param nEventId + Id of the event type for which to return its name. This is one + of the ids listed in the <type>AccessibleEventId</const> + constants group. + @return + Returns the name of the specified event type or an empty string + if an invalid / unknown event id was given. + */ + public static String getEventName (int nEventId) + { + return maEventMap.get (Integer.valueOf(nEventId)); + } + + + /** Return the name of the specified role. + @param nRole + Id of the role for which to return its name. This is one of + the ids listed in the <type>AccessibleRole</const> + constants group. + @return + Returns the name of the specified role or an empty string if an + invalid / unknown role id was given. + */ + public static String getRoleName (int nRole) + { + return maRoleMap.get (Integer.valueOf(nRole)); + } + + + /** Return the name of the specified relation. + @param nRelation + Id of the relation for which to return its name. This is one of + the ids listed in the <type>AccessibleRelationType</const> + constants group. + @return + Returns the name of the specified relation type or an empty + string if an invalid / unknown role id was given. + */ + public static String getRelationName (int nRelation) + { + return maRelationMap.get (Integer.valueOf(nRelation)); + } + + + private static HashMap<Integer, String> maStateMap = new HashMap<Integer, String>(); + private static HashMap<Integer, String> maEventMap = new HashMap<Integer, String>(); + private static HashMap<Integer, String> maRoleMap = new HashMap<Integer, String>(); + private static HashMap<Integer, String> maRelationMap = new HashMap<Integer, String>(); + + static { + maStateMap.put (Integer.valueOf(AccessibleStateType.INVALID), "INVALID"); + maStateMap.put (Integer.valueOf(AccessibleStateType.ACTIVE), "ACTIVE"); + maStateMap.put (Integer.valueOf(AccessibleStateType.ARMED), "ARMED"); + maStateMap.put (Integer.valueOf(AccessibleStateType.BUSY), "BUSY"); + maStateMap.put (Integer.valueOf(AccessibleStateType.CHECKED), "CHECKED"); + maStateMap.put (Integer.valueOf(AccessibleStateType.DEFUNC), "DEFUNC"); + maStateMap.put (Integer.valueOf(AccessibleStateType.EDITABLE), "EDITABLE"); + maStateMap.put (Integer.valueOf(AccessibleStateType.ENABLED), "ENABLED"); + maStateMap.put (Integer.valueOf(AccessibleStateType.EXPANDABLE), "EXPANDABLE"); + maStateMap.put (Integer.valueOf(AccessibleStateType.EXPANDED), "EXPANDED"); + maStateMap.put (Integer.valueOf(AccessibleStateType.FOCUSABLE), "FOCUSABLE"); + maStateMap.put (Integer.valueOf(AccessibleStateType.FOCUSED), "FOCUSED"); + maStateMap.put (Integer.valueOf(AccessibleStateType.HORIZONTAL), "HORIZONTAL"); + maStateMap.put (Integer.valueOf(AccessibleStateType.ICONIFIED), "ICONIFIED"); + maStateMap.put (Integer.valueOf(AccessibleStateType.MODAL), "MODAL"); + maStateMap.put (Integer.valueOf(AccessibleStateType.MULTI_LINE), "MULTI_LINE"); + maStateMap.put (Integer.valueOf(AccessibleStateType.MULTI_SELECTABLE), "MULTI_SELECTABLE"); + maStateMap.put (Integer.valueOf(AccessibleStateType.OPAQUE), "OPAQUE"); + maStateMap.put (Integer.valueOf(AccessibleStateType.PRESSED), "PRESSED"); + maStateMap.put (Integer.valueOf(AccessibleStateType.RESIZABLE), "RESIZABLE"); + maStateMap.put (Integer.valueOf(AccessibleStateType.SELECTABLE), "SELECTABLE"); + maStateMap.put (Integer.valueOf(AccessibleStateType.SELECTED), "SELECTED"); + maStateMap.put (Integer.valueOf(AccessibleStateType.SENSITIVE), "SENSITIVE"); + maStateMap.put (Integer.valueOf(AccessibleStateType.SHOWING), "SHOWING"); + maStateMap.put (Integer.valueOf(AccessibleStateType.SINGLE_LINE), "SINGLE_LINE"); + maStateMap.put (Integer.valueOf(AccessibleStateType.STALE), "STALE"); + maStateMap.put (Integer.valueOf(AccessibleStateType.TRANSIENT), "TRANSIENT"); + maStateMap.put (Integer.valueOf(AccessibleStateType.VERTICAL), "VERTICAL"); + maStateMap.put (Integer.valueOf(AccessibleStateType.VISIBLE), "VISIBLE"); + maStateMap.put (Integer.valueOf(AccessibleStateType.MANAGES_DESCENDANTS), + "MANAGES_DESCENDANTS"); + + + maEventMap.put (Integer.valueOf(0), + "[UNKNOWN]"); + maEventMap.put (Integer.valueOf(AccessibleEventId.NAME_CHANGED), + "NAME_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.DESCRIPTION_CHANGED), + "DESCRIPTION_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.ACTION_CHANGED), + "ACTION_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.STATE_CHANGED), + "STATE_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.ACTIVE_DESCENDANT_CHANGED), + "ACTIVE_DESCENDANT_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.BOUNDRECT_CHANGED), + "BOUNDRECT_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.CHILD), + "CHILD"); + maEventMap.put (Integer.valueOf(AccessibleEventId.INVALIDATE_ALL_CHILDREN), + "INVALIDATE_ALL_CHILDREN"); + maEventMap.put (Integer.valueOf(AccessibleEventId.SELECTION_CHANGED), + "SELECTION_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.VISIBLE_DATA_CHANGED), + "VISIBLE_DATA_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.VALUE_CHANGED), + "VALUE_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.CONTENT_FLOWS_FROM_RELATION_CHANGED), + "CONTENT_FLOWS_FROM_RELATION_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.CONTENT_FLOWS_TO_RELATION_CHANGED), + "CONTENT_FLOWS_TO_RELATION_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.CONTROLLED_BY_RELATION_CHANGED), + "CONTROLLED_BY_RELATION_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.CONTROLLER_FOR_RELATION_CHANGED), + "CONTROLLER_FOR_RELATION_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.LABEL_FOR_RELATION_CHANGED), + "LABEL_FOR_RELATION_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.LABELED_BY_RELATION_CHANGED), + "LABELED_BY_RELATION_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.MEMBER_OF_RELATION_CHANGED), + "MEMBER_OF_RELATION_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.SUB_WINDOW_OF_RELATION_CHANGED), + "SUB_WINDOW_OF_RELATION_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.CARET_CHANGED), + "CARET_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.TEXT_SELECTION_CHANGED), + "TEXT_SELECTION_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.TEXT_CHANGED), + "TEXT_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.TEXT_ATTRIBUTE_CHANGED), + "TEXT_ATTRIBUTE_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.HYPERTEXT_CHANGED), + "HYPERTEXT_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.TABLE_CAPTION_CHANGED), + "TABLE_CAPTION_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.TABLE_COLUMN_DESCRIPTION_CHANGED), + "TABLE_COLUMN_DESCRIPTION_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.TABLE_COLUMN_HEADER_CHANGED), + "TABLE_COLUMN_HEADER_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.TABLE_MODEL_CHANGED), + "TABLE_MODEL_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.TABLE_ROW_DESCRIPTION_CHANGED), + "TABLE_ROW_DESCRIPTION_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.TABLE_ROW_HEADER_CHANGED), + "TABLE_ROW_HEADER_CHANGED"); + maEventMap.put (Integer.valueOf(AccessibleEventId.TABLE_SUMMARY_CHANGED), + "TABLE_SUMMARY_CHANGED"); + + maRoleMap.put (Integer.valueOf(AccessibleRole.UNKNOWN), "UNKNOWN"); + maRoleMap.put (Integer.valueOf(AccessibleRole.UNKNOWN), "UNKNOWN"); + maRoleMap.put (Integer.valueOf(AccessibleRole.ALERT), "ALERT"); + maRoleMap.put (Integer.valueOf(AccessibleRole.COLUMN_HEADER), "COLUMN_HEADER"); + maRoleMap.put (Integer.valueOf(AccessibleRole.CANVAS), "CANVAS"); + maRoleMap.put (Integer.valueOf(AccessibleRole.CHECK_BOX), "CHECK_BOX"); + maRoleMap.put (Integer.valueOf(AccessibleRole.CHECK_MENU_ITEM), "CHECK_MENU_ITEM"); + maRoleMap.put (Integer.valueOf(AccessibleRole.COLOR_CHOOSER), "COLOR_CHOOSER"); + maRoleMap.put (Integer.valueOf(AccessibleRole.COMBO_BOX), "COMBO_BOX"); + maRoleMap.put (Integer.valueOf(AccessibleRole.DESKTOP_ICON), "DESKTOP_ICON"); + maRoleMap.put (Integer.valueOf(AccessibleRole.DESKTOP_PANE), "DESKTOP_PANE"); + maRoleMap.put (Integer.valueOf(AccessibleRole.DIRECTORY_PANE), "DIRECTORY_PANE"); + maRoleMap.put (Integer.valueOf(AccessibleRole.DIALOG), "DIALOG"); + maRoleMap.put (Integer.valueOf(AccessibleRole.DOCUMENT), "DOCUMENT"); + maRoleMap.put (Integer.valueOf(AccessibleRole.EMBEDDED_OBJECT), "EMBEDDED_OBJECT"); + maRoleMap.put (Integer.valueOf(AccessibleRole.END_NOTE), "END_NOTE"); + maRoleMap.put (Integer.valueOf(AccessibleRole.FILE_CHOOSER), "FILE_CHOOSER"); + maRoleMap.put (Integer.valueOf(AccessibleRole.FILLER), "FILLER"); + maRoleMap.put (Integer.valueOf(AccessibleRole.FONT_CHOOSER), "FONT_CHOOSER"); + maRoleMap.put (Integer.valueOf(AccessibleRole.FOOTER), "FOOTER"); + maRoleMap.put (Integer.valueOf(AccessibleRole.FOOTNOTE), "FOOTNOTE"); + maRoleMap.put (Integer.valueOf(AccessibleRole.FRAME), "FRAME"); + maRoleMap.put (Integer.valueOf(AccessibleRole.GLASS_PANE), "GLASS_PANE"); + maRoleMap.put (Integer.valueOf(AccessibleRole.GRAPHIC), "GRAPHIC"); + maRoleMap.put (Integer.valueOf(AccessibleRole.GROUP_BOX), "GROUP_BOX"); + maRoleMap.put (Integer.valueOf(AccessibleRole.HEADER), "HEADER"); + maRoleMap.put (Integer.valueOf(AccessibleRole.HEADING), "HEADING"); + maRoleMap.put (Integer.valueOf(AccessibleRole.HYPER_LINK), "HYPER_LINK"); + maRoleMap.put (Integer.valueOf(AccessibleRole.ICON), "ICON"); + maRoleMap.put (Integer.valueOf(AccessibleRole.INTERNAL_FRAME), "INTERNAL_FRAME"); + maRoleMap.put (Integer.valueOf(AccessibleRole.LABEL), "LABEL"); + maRoleMap.put (Integer.valueOf(AccessibleRole.LAYERED_PANE), "LAYERED_PANE"); + maRoleMap.put (Integer.valueOf(AccessibleRole.LIST), "LIST"); + maRoleMap.put (Integer.valueOf(AccessibleRole.LIST_ITEM), "LIST_ITEM"); + maRoleMap.put (Integer.valueOf(AccessibleRole.MENU), "MENU"); + maRoleMap.put (Integer.valueOf(AccessibleRole.MENU_BAR), "MENU_BAR"); + maRoleMap.put (Integer.valueOf(AccessibleRole.MENU_ITEM), "MENU_ITEM"); + maRoleMap.put (Integer.valueOf(AccessibleRole.OPTION_PANE), "OPTION_PANE"); + maRoleMap.put (Integer.valueOf(AccessibleRole.PAGE_TAB), "PAGE_TAB"); + maRoleMap.put (Integer.valueOf(AccessibleRole.PAGE_TAB_LIST), "PAGE_TAB_LIST"); + maRoleMap.put (Integer.valueOf(AccessibleRole.PANEL), "PANEL"); + maRoleMap.put (Integer.valueOf(AccessibleRole.PARAGRAPH), "PARAGRAPH"); + maRoleMap.put (Integer.valueOf(AccessibleRole.PASSWORD_TEXT), "PASSWORD_TEXT"); + maRoleMap.put (Integer.valueOf(AccessibleRole.POPUP_MENU), "POPUP_MENU"); + maRoleMap.put (Integer.valueOf(AccessibleRole.PUSH_BUTTON), "PUSH_BUTTON"); + maRoleMap.put (Integer.valueOf(AccessibleRole.PROGRESS_BAR), "PROGRESS_BAR"); + maRoleMap.put (Integer.valueOf(AccessibleRole.RADIO_BUTTON), "RADIO_BUTTON"); + maRoleMap.put (Integer.valueOf(AccessibleRole.RADIO_MENU_ITEM), "RADIO_MENU_ITEM"); + maRoleMap.put (Integer.valueOf(AccessibleRole.ROW_HEADER), "ROW_HEADER"); + maRoleMap.put (Integer.valueOf(AccessibleRole.ROOT_PANE), "ROOT_PANE"); + maRoleMap.put (Integer.valueOf(AccessibleRole.SCROLL_BAR), "SCROLL_BAR"); + maRoleMap.put (Integer.valueOf(AccessibleRole.SCROLL_PANE), "SCROLL_PANE"); + maRoleMap.put (Integer.valueOf(AccessibleRole.SHAPE), "SHAPE"); + maRoleMap.put (Integer.valueOf(AccessibleRole.SEPARATOR), "SEPARATOR"); + maRoleMap.put (Integer.valueOf(AccessibleRole.SLIDER), "SLIDER"); + maRoleMap.put (Integer.valueOf(AccessibleRole.SPIN_BOX), "SPIN_BOX"); + maRoleMap.put (Integer.valueOf(AccessibleRole.SPLIT_PANE), "SPLIT_PANE"); + maRoleMap.put (Integer.valueOf(AccessibleRole.STATIC), "STATIC"); + maRoleMap.put (Integer.valueOf(AccessibleRole.STATUS_BAR), "STATUS_BAR"); + maRoleMap.put (Integer.valueOf(AccessibleRole.TABLE), "TABLE"); + maRoleMap.put (Integer.valueOf(AccessibleRole.TABLE_CELL), "TABLE_CELL"); + maRoleMap.put (Integer.valueOf(AccessibleRole.TEXT), "TEXT"); + maRoleMap.put (Integer.valueOf(AccessibleRole.TEXT_FRAME), "TEXT_FRAME"); + maRoleMap.put (Integer.valueOf(AccessibleRole.TOGGLE_BUTTON), "TOGGLE_BUTTON"); + maRoleMap.put (Integer.valueOf(AccessibleRole.TOOL_BAR), "TOOL_BAR"); + maRoleMap.put (Integer.valueOf(AccessibleRole.TOOL_TIP), "TOOL_TIP"); + maRoleMap.put (Integer.valueOf(AccessibleRole.TREE), "TREE"); + maRoleMap.put (Integer.valueOf(AccessibleRole.VIEW_PORT), "VIEW_PORT"); + maRoleMap.put (Integer.valueOf(AccessibleRole.WINDOW), "WINDOW"); + + maRelationMap.put (Integer.valueOf(AccessibleRelationType.INVALID), "INVALID"); + maRelationMap.put (Integer.valueOf(AccessibleRelationType.CONTENT_FLOWS_FROM), "CONTENT_FLOWS_FROM"); + maRelationMap.put (Integer.valueOf(AccessibleRelationType.CONTENT_FLOWS_TO), "CONTENT_FLOWS_TO"); + maRelationMap.put (Integer.valueOf(AccessibleRelationType.CONTROLLED_BY), "CONTROLLED_BY"); + maRelationMap.put (Integer.valueOf(AccessibleRelationType.CONTROLLER_FOR), "CONTROLLER_FOR"); + maRelationMap.put (Integer.valueOf(AccessibleRelationType.LABEL_FOR), "LABEL_FOR"); + maRelationMap.put (Integer.valueOf(AccessibleRelationType.LABELED_BY), "LABELED_BY"); + maRelationMap.put (Integer.valueOf(AccessibleRelationType.MEMBER_OF), "MEMBER_OF"); + maRelationMap.put (Integer.valueOf(AccessibleRelationType.SUB_WINDOW_OF), "SUB_WINDOW_OF"); + } +} |