1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 import com.sun.star.accessibility.*;
23 import com.sun.star.lang.XServiceInfo;
24 import com.sun.star.lang.IndexOutOfBoundsException;
25 import com.sun.star.uno.UnoRuntime;
26 
27 import java.util.Vector;
28 import java.awt.*;
29 import java.awt.event.*;
30 import javax.swing.*;
31 import javax.swing.tree.*;
32 import javax.swing.event.*;
33 
34 
35 
36 /** This is the tree component that is responsible for displaying the
37     contents of the tree model on the screen.
38 */
39 public class AccessibilityTree
40     implements TreeExpansionListener, TreeWillExpandListener
41 {
42     /** Create a new accessibility tree.  Use the specified message display
43         for displaying messages and the specified canvas to draw the
44         graphical representations of accessible objects on.
45     */
46     public AccessibilityTree ()
47     {
48         maTree = new JTree ();
49 
50         AccessibilityTreeModel aModel =
51             new AccessibilityTreeModel (
52                 new StringNode ("Please press Update button", null));
53         maTree.setModel (aModel);
54 
55         maCellRenderer = new AccessibleTreeCellRenderer();
56         //        setCellRenderer (maCellRenderer);
57 
58         // allow editing of XAccessibleText interfaces
59         //        setEditable (true);
60         //        maTreeModel.addTreeModelListener( new TextUpdateListener() );
61 
62         maTree.addMouseListener (new MouseListener (this));
63 
64         // Listen to expansions and collapses to change the mouse cursor.
65         mnExpandLevel = 0;
66         maTree.addTreeWillExpandListener (this);
67         maTree.addTreeExpansionListener (this);
68     }
69 
70     public JTree getComponent ()
71     {
72         return maTree;
73     }
74 
75     // Change cursor during expansions to show the user that this is a
76     // lengthy operation.
77     public void treeWillExpand (TreeExpansionEvent e)
78     {
79         if (mnExpandLevel == 0)
80         {
81             maTree.setCursor (new Cursor (Cursor.WAIT_CURSOR));
82         }
83         mnExpandLevel += 1;
84     }
85     public void treeWillCollapse (TreeExpansionEvent e)
86     {
87         if (mnExpandLevel == 0)
88         {
89             maTree.setCursor (new Cursor (Cursor.WAIT_CURSOR));
90         }
91         mnExpandLevel += 1;
92     }
93     public void treeExpanded (TreeExpansionEvent e)
94     {
95         mnExpandLevel -= 1;
96         if (mnExpandLevel == 0)
97         {
98             maTree.setCursor (new Cursor (Cursor.DEFAULT_CURSOR));
99         }
100     }
101     public void treeCollapsed (TreeExpansionEvent e)
102     {
103         mnExpandLevel -= 1;
104         if (mnExpandLevel == 0)
105         {
106             maTree.setCursor (new Cursor (Cursor.DEFAULT_CURSOR));
107         }
108     }
109 
110 
111 
112     public void SetCanvas (Canvas aCanvas)
113     {
114         maCanvas = aCanvas;
115         ((AccessibilityTreeModel)maTree.getModel()).setCanvas (maCanvas);
116     }
117 
118     /** Expand the nodes in the subtree rooted in aNode according to the the
119         specified expander.  The tree is locked during the expansion.
120     */
121     protected void expandTree (AccessibleTreeNode aNode, Expander aExpander)
122     {
123         if (mnExpandLevel == 0)
124         {
125             maTree.setEnabled (false);
126         }
127         mnExpandLevel += 1;
128 
129         ((AccessibilityTreeModel)maTree.getModel()).lock ();
130 
131         try
132         {
133             expandTree (new TreePath (aNode.createPath()), aExpander);
134         }
135         catch (Exception e)
136         {
137             // Ignore
138         }
139 
140         mnExpandLevel -= 1;
141         if (mnExpandLevel == 0)
142         {
143             maTree.setEnabled (true);
144             ((AccessibilityTreeModel)maTree.getModel()).unlock (aNode);
145         }
146     }
147 
148     private TreePath expandTree( TreePath aPath, Expander aExpander )
149     {
150         // return first expanded object
151         TreePath aFirst = null;
152 
153         //        System.out.print ("e");
154 
155         try
156         {
157             // get 'our' object
158             Object aObj = aPath.getLastPathComponent();
159 
160             // expand this object, if the Expander tells us so
161             if( aExpander.expand( aObj ) )
162             {
163                 maTree.expandPath (aPath);
164                 if( aFirst == null )
165                     aFirst = aPath;
166             }
167 
168             // visit all children
169             if (aObj instanceof AccessibleTreeNode)
170             {
171                 AccessibleTreeNode aNode = (AccessibleTreeNode)aObj;
172                 int nLength = aNode.getChildCount();
173                 for( int i = 0; i < nLength; i++ )
174                 {
175                     TreePath aRet = expandTree(
176                         aPath.pathByAddingChild( aNode.getChild( i ) ),
177                         aExpander );
178                     if( aFirst == null )
179                         aFirst = aRet;
180                 }
181             }
182         }
183         catch (Exception e)
184         {
185             System.out.println ("caught exception while expanding tree path "
186                 + aPath + ": " + e);
187             e.printStackTrace ();
188         }
189 
190         return aFirst;
191     }
192 
193 
194     /** Expand all nodes and their subtrees that represent shapes.  Call
195      *  this method from the outside. */
196     public void expandShapes ()
197     {
198         expandShapes ((AccessibleTreeNode)maTree.getModel().getRoot());
199     }
200     public void expandShapes (AccessibleTreeNode aNode)
201     {
202         expandTree (aNode, new ShapeExpander());
203     }
204 
205     /** Expand all nodes */
206     public void expandAll ()
207     {
208         expandAll ((AccessibleTreeNode)maTree.getModel().getRoot());
209     }
210     public void expandAll (AccessibleTreeNode aNode)
211     {
212         expandTree (aNode, new AllExpander());
213     }
214 
215 
216 
217     public void disposing (com.sun.star.lang.EventObject e)
218     {
219         System.out.println ("disposing " + e);
220     }
221 
222     /*
223     public Dimension getPreferredSize ()
224     {
225         Dimension aPreferredSize = super.getPreferredSize();
226         Dimension aMinimumSize = super.getMinimumSize();
227         if (aPreferredSize.width < aMinimumSize.width)
228             aPreferredSize.width = aMinimumSize.width;
229         return aPreferredSize;
230     }
231     */
232 
233     class MouseListener extends MouseAdapter
234     {
235         public MouseListener (AccessibilityTree aTree)
236         {
237             maTree=aTree;
238         }
239         public void mousePressed(MouseEvent e) { popupTrigger(e); }
240         public void mouseClicked(MouseEvent e) { popupTrigger(e); }
241         public void mouseEntered(MouseEvent e) { popupTrigger(e); }
242         public void mouseExited(MouseEvent e) { popupTrigger(e); }
243         public void mouseReleased(MouseEvent e) { popupTrigger(e); }
244 
245         public boolean popupTrigger( MouseEvent e )
246         {
247             boolean bIsPopup = e.isPopupTrigger();
248             if( bIsPopup )
249             {
250                 int selRow = maTree.getComponent().getRowForLocation(e.getX(), e.getY());
251                 if (selRow != -1)
252                 {
253                     TreePath aPath = maTree.getComponent().getPathForLocation(e.getX(), e.getY());
254 
255                     // check for actions
256                     Object aObject = aPath.getLastPathComponent();
257                     JPopupMenu aMenu = new JPopupMenu();
258                     if( aObject instanceof AccTreeNode )
259                     {
260                         AccTreeNode aNode = (AccTreeNode)aObject;
261 
262                         Vector aActions = new Vector();
263                         aMenu.add (new AccessibilityTree.ShapeExpandAction(maTree, aNode));
264                         aMenu.add (new AccessibilityTree.SubtreeExpandAction(maTree, aNode));
265 
266                         aNode.getActions(aActions);
267                         for( int i = 0; i < aActions.size(); i++ )
268                         {
269                             aMenu.add( new NodeAction(
270                                            aActions.elementAt(i).toString(),
271                                            aNode, i ) );
272                         }
273                     }
274                     else if (aObject instanceof AccessibleTreeNode)
275                     {
276                         AccessibleTreeNode aNode = (AccessibleTreeNode)aObject;
277                         String[] aActionNames = aNode.getActions();
278                         int nCount=aActionNames.length;
279                         if (nCount > 0)
280                         {
281                             for (int i=0; i<nCount; i++)
282                                 aMenu.add( new NodeAction(
283                                     aActionNames[i],
284                                     aNode,
285                                     i));
286                         }
287                         else
288                             aMenu = null;
289                     }
290                     if (aMenu != null)
291                         aMenu.show (maTree.getComponent(),
292                             e.getX(), e.getY());
293                 }
294             }
295 
296             return bIsPopup;
297         }
298 
299         private AccessibilityTree maTree;
300     }
301 
302     class NodeAction extends AbstractAction
303     {
304         private int mnIndex;
305         private AccessibleTreeNode maNode;
306 
307         public NodeAction( String aName, AccessibleTreeNode aNode, int nIndex )
308         {
309             super( aName );
310             maNode = aNode;
311             mnIndex = nIndex;
312         }
313 
314         public void actionPerformed(ActionEvent e)
315         {
316             maNode.performAction(mnIndex);
317         }
318     }
319 
320     // This action expands all shapes in the subtree rooted in the specified node.
321     class ShapeExpandAction extends AbstractAction
322     {
323         private AccessibilityTree maTree;
324         private AccTreeNode maNode;
325         public ShapeExpandAction (AccessibilityTree aTree, AccTreeNode aNode)
326         {
327             super ("Expand Shapes");
328             maTree = aTree;
329             maNode = aNode;
330         }
331         public void actionPerformed (ActionEvent e)
332         {
333             maTree.expandShapes (maNode);
334         }
335     }
336 
337     // This action expands all nodes in the subtree rooted in the specified node.
338     class SubtreeExpandAction extends AbstractAction
339     {
340         private AccessibilityTree maTree;
341         private AccTreeNode maNode;
342         public SubtreeExpandAction (AccessibilityTree aTree, AccTreeNode aNode)
343         {
344             super ("Expand Subtree");
345             maTree = aTree;
346             maNode = aNode;
347         }
348         public void actionPerformed (ActionEvent e)
349         {
350             maTree.expandAll (maNode);
351         }
352     }
353 
354     /** Predicate class to determine whether a node should be expanded
355      * For use with expandTree method */
356     abstract class Expander
357     {
358         abstract public boolean expand (Object aObject);
359     }
360 
361     /** expand all nodes */
362     class AllExpander extends Expander
363     {
364         public boolean expand(Object aObject) { return true; }
365     }
366 
367     /** expand all nodes with accessibility roles > 100 */
368     class ShapeExpander extends Expander
369     {
370         public boolean expand (Object aObject)
371         {
372             if (aObject instanceof AccTreeNode)
373             {
374                 AccTreeNode aNode = (AccTreeNode)aObject;
375                 XAccessibleContext xContext = aNode.getContext();
376                 if (xContext != null)
377                     if (xContext.getAccessibleRole() >= 100)
378                         return true;
379             }
380             return false;
381         }
382     }
383 
384 
385 
386     protected AccessibleTreeCellRenderer
387         maCellRenderer;
388 
389 
390     private JTree
391         maTree;
392     private Canvas
393         maCanvas;
394     private boolean
395         mbFirstShapeSeen;
396     private int
397         mnExpandLevel;
398 }
399