1 import java.util.*;
2 import java.awt.*;
3 import java.awt.event.*;
4 import javax.swing.*;
5 import javax.swing.tree.*;
6 import javax.swing.event.TreeSelectionListener;
7 import javax.swing.event.TreeSelectionEvent;
8 import java.awt.geom.Rectangle2D;
9 
10 import com.sun.star.accessibility.XAccessible;
11 import com.sun.star.accessibility.XAccessibleContext;
12 import com.sun.star.accessibility.XAccessibleComponent;
13 
14 /** This canvas displays accessible objects graphically.  Each accessible
15     object with graphical representation is represented by an
16     CanvasShape object and has to be added by the
17     <member>addAccessible</member> member function.
18 
19     <p>The canvas listens to selection events of the associated JTree and
20     highlights the first selected node of that tree.</p>
21 */
22 class Canvas
23     extends JPanel
24     implements MouseListener, MouseMotionListener, TreeSelectionListener//, Scrollable
25 {
26     // This constant can be passed to SetZoomMode to always show the whole screen.
27     public static final int WHOLE_SCREEN = -1;
28 
29     public Canvas ()
30     {
31         super (true);
32         maObjects = new java.util.HashMap ();
33         maNodes = new Vector ();
34         maObjectList = new Vector ();
35         maContexts = new Vector ();
36         addMouseListener (this);
37         addMouseMotionListener (this);
38         maBoundingBox = new Rectangle (0,0,100,100);
39         maTree = null;
40         mnHOffset = 0;
41         mnVOffset = 0;
42         mnScale = 1;
43         setShowText(false);
44         setShowDescriptions (true);
45         setShowNames (true);
46         setAntialiasing (true);
47         maLastWidgetSize = new Dimension (0,0);
48     }
49 
50     /** Tell the canvas which tree view to use to highlight accessible
51         objects.
52     */
53     public void setTree (JTree aTree)
54     {
55         if (maTree != null)
56             maTree.removeTreeSelectionListener (this);
57         maTree = aTree;
58         if (maTree != null)
59             maTree.addTreeSelectionListener (this);
60     }
61 
62 
63 
64 
65     public void addNode (AccTreeNode aNode)
66     {
67         if (maNodes.indexOf (aNode) == -1)
68         {
69             maNodes.add (aNode);
70 
71             CanvasShape aObject = (CanvasShape) maObjects.get (aNode);
72             if (aObject == null)
73             {
74                 aObject = new CanvasShape (aNode);
75                 // Update bounding box that includes all objects.
76                 if (maObjects.size() == 0)
77                     maBoundingBox = aObject.getBBox();
78                 else
79                     maBoundingBox = maBoundingBox.union (aObject.getBBox());
80 
81                 maObjects.put (aNode, aObject);
82                 maObjectList.add (aObject);
83 
84             }
85             repaint ();
86         }
87     }
88 
89     public void removeNode (AccTreeNode aNode)
90     {
91         int i = maNodes.indexOf (aNode);
92         if( i != -1 )
93         {
94             Object aObject = maObjects.get(aNode);
95             maObjectList.remove (aObject);
96             maObjects.remove (aObject);
97             maNodes.remove (aNode);
98             repaint ();
99         }
100     }
101 
102     public void updateNode (AccTreeNode aNode)
103     {
104         int i = maNodes.indexOf (aNode);
105         if (i != -1)
106         {
107             CanvasShape aObject = (CanvasShape)maObjects.get(aNode);
108             if (aObject != null)
109                 aObject.update();
110         }
111     }
112 
113     public void updateNodeGeometry (AccTreeNode aNode)
114     {
115         CanvasShape aObject = (CanvasShape)maObjects.get(aNode);
116         if (aObject != null)
117             aObject.updateGeometry();
118     }
119 
120     public void clear ()
121     {
122         while (maNodes.size() > 0)
123             removeNode ((AccTreeNode)maNodes.elementAt(0));
124 
125         maNodes.clear();
126         maObjects.clear();
127         maObjectList.clear();
128     }
129 
130     public boolean getShowDescriptions ()
131     {
132         return Options.GetBoolean ("ShowDescriptions");
133     }
134 
135     public void setShowDescriptions (boolean bNewValue)
136     {
137         Options.SetBoolean ("ShowDescriptions", bNewValue);
138         repaint ();
139     }
140 
141     public boolean getShowNames ()
142     {
143         return Options.GetBoolean ("ShowNames");
144     }
145 
146     public void setShowNames (boolean bNewValue)
147     {
148         Options.SetBoolean ("ShowNames", bNewValue);
149         repaint ();
150     }
151 
152     public boolean getAntialiasing ()
153     {
154         return Options.GetBoolean ("Antialiasing");
155     }
156 
157     public void setAntialiasing (boolean bNewValue)
158     {
159         Options.SetBoolean ("Antialiasing", bNewValue);
160         repaint ();
161     }
162 
163     public boolean getShowText ()
164     {
165         return Options.GetBoolean ("ShowText");
166     }
167 
168     public void setShowText (boolean bNewValue)
169     {
170         Options.SetBoolean ("ShowText", bNewValue);
171         repaint ();
172     }
173 
174     public void setZoomMode (int nZoomMode)
175     {
176         Options.SetInteger ("ZoomMode", nZoomMode);
177         repaint ();
178     }
179 
180     public int getZoomMode ()
181     {
182         return Options.GetInteger ("ZoomMode", WHOLE_SCREEN);
183     }
184 
185 
186     public void paintComponent (Graphics g)
187     {
188         synchronized (g)
189         {
190             super.paintComponent (g);
191 
192             Graphics2D g2 = (Graphics2D)g;
193             if (getAntialiasing())
194                 g2.setRenderingHint (RenderingHints.KEY_ANTIALIASING,
195                     RenderingHints.VALUE_ANTIALIAS_ON);
196             else
197                 g2.setRenderingHint (RenderingHints.KEY_ANTIALIASING,
198                     RenderingHints.VALUE_ANTIALIAS_OFF);
199 
200             setupTransformation ();
201 
202             // Draw the screen representation to give a hint of the location of the
203             // accessible object on the screen.
204             Dimension aScreenSize = Toolkit.getDefaultToolkit().getScreenSize();
205             Rectangle2D.Double aScreen = new Rectangle2D.Double (
206                 mnHOffset,
207                 mnVOffset,
208                 mnScale*aScreenSize.getWidth(),
209                 mnScale*aScreenSize.getHeight());
210             // Fill the screen rectangle and draw a frame arround it to increase its visibility.
211             g2.setColor (new Color (250,240,230));
212             g2.fill (aScreen);
213             g2.setColor (Color.BLACK);
214             g2.draw (aScreen);
215 
216             synchronized (maObjectList)
217             {
218                 int nCount = maObjectList.size();
219                 boolean bShowDescriptions = getShowDescriptions();
220                 boolean bShowNames = getShowNames();
221                 boolean bShowText = getShowText();
222                 for (int i=0; i<nCount; i++)
223                 {
224                     CanvasShape aCanvasShape = (CanvasShape)maObjectList.elementAt(i);
225                     aCanvasShape.paint (
226                         g2,
227                         mnHOffset, mnVOffset, mnScale,
228                         bShowDescriptions, bShowNames, bShowText);
229                 }
230             }
231 
232             // Paint highlighted frame around active object as the last thing.
233             if (maActiveObject != null)
234                 maActiveObject.paint_highlight (
235                     g2,
236                     mnHOffset, mnVOffset, mnScale);
237         }
238     }
239 
240 
241 
242 
243     /** Set up the transformation so that the graphical display can show a
244         centered representation of the whole screen.
245     */
246     private void setupTransformation ()
247     {
248         // Turn off scrollbars when showing the whole screen.  Otherwise show them when needed.
249         JViewport aViewport = (JViewport)getParent();
250         JScrollPane aScrollPane = (JScrollPane)aViewport.getParent();
251         int nZoomMode = getZoomMode();
252         if (nZoomMode == WHOLE_SCREEN)
253         {
254             if (aScrollPane.getHorizontalScrollBarPolicy()
255                 != JScrollPane.HORIZONTAL_SCROLLBAR_NEVER)
256                 aScrollPane.setHorizontalScrollBarPolicy (JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
257             if (aScrollPane.getVerticalScrollBarPolicy()
258                 != JScrollPane.VERTICAL_SCROLLBAR_NEVER)
259                 aScrollPane.setVerticalScrollBarPolicy (JScrollPane.VERTICAL_SCROLLBAR_NEVER);
260         }
261         else
262         {
263             if (aScrollPane.getHorizontalScrollBarPolicy()
264                 != JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED)
265                 aScrollPane.setHorizontalScrollBarPolicy (JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
266             if (aScrollPane.getVerticalScrollBarPolicy()
267                 != JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED)
268                 aScrollPane.setVerticalScrollBarPolicy (JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
269         }
270 
271         Dimension aScreenSize = Toolkit.getDefaultToolkit().getScreenSize();
272         Dimension aWidgetSize = aViewport.getSize();
273         {
274             if ((aScreenSize.getWidth() > 0) && (aScreenSize.getHeight() > 0))
275             {
276                 if (nZoomMode == WHOLE_SCREEN)
277                 {
278                     // Calculate the scales that would map the screen onto the
279                     // widget in both of the coordinate axes and select the
280                     // smaller
281                     // of the two: it maps the screen onto the widget in both
282                     // axes at the same time.
283                     double nHScale = (aWidgetSize.getWidth() - 10) / aScreenSize.getWidth();
284                     double nVScale = (aWidgetSize.getHeight() - 10) / aScreenSize.getHeight();
285                     if (nHScale < nVScale)
286                         mnScale = nHScale;
287                     else
288                         mnScale = nVScale;
289                 }
290                 else
291                 {
292                     mnScale = nZoomMode / 100.0;
293                 }
294 
295                 // Calculate offsets that center the scaled screen inside the widget.
296                 mnHOffset = (aWidgetSize.getWidth() - mnScale*aScreenSize.getWidth()) / 2.0;
297                 mnVOffset = (aWidgetSize.getHeight() - mnScale*aScreenSize.getHeight()) / 2.0;
298                 if (mnHOffset < 0)
299                     mnHOffset = 0;
300                 if (mnVOffset < 0)
301                     mnVOffset = 0;
302 
303                 setPreferredSize (new Dimension (
304                     (int)(2*mnHOffset + mnScale * aScreenSize.getWidth()),
305                     (int)(2*mnVOffset + mnScale * aScreenSize.getHeight())));
306                 revalidate ();
307             }
308             else
309             {
310                 // In case of a degenerate (not yet initialized?) screen size
311                 // use some meaningless default values.
312                 mnScale = 1;
313                 mnHOffset = 0;
314                 mnVOffset = 0;
315             }
316         }
317         maLastWidgetSize = aWidgetSize;
318     }
319 
320 
321 
322     /**  Call getAccessibleAt to determine accessible object under mouse.
323     */
324     public void mouseClicked (MouseEvent e)
325     {
326     }
327 
328     public void mousePressed (MouseEvent e)
329     {
330         CanvasShape aObjectUnderMouse = FindCanvasShapeUnderMouse (e);
331         highlightObject (aObjectUnderMouse);
332         if ((e.getModifiers() & InputEvent.CTRL_MASK) != 0)
333         {
334             maTree.expandPath (aObjectUnderMouse.getPath());
335         }
336     }
337 
338     public void mouseReleased (MouseEvent e)
339     {
340     }
341 
342     public void mouseEntered (MouseEvent e)
343     {
344     }
345 
346     public void mouseExited (MouseEvent e)
347     {
348         // Deselect currently active object.
349         if (maActiveObject != null)
350         {
351             maActiveObject.unhighlight ();
352             maActiveObject = null;
353             repaint ();
354         }
355     }
356 
357     public void mouseDragged (MouseEvent e)
358     {
359     }
360 
361     public void mouseMoved (MouseEvent e)
362     {
363         if ((e.getModifiers() & InputEvent.SHIFT_MASK) != 0)
364             highlightObject (FindCanvasShapeUnderMouse (e));
365     }
366 
367     protected CanvasShape FindCanvasShapeUnderMouse (MouseEvent e)
368     {
369         int nObjects = maObjects.size();
370         CanvasShape aObjectUnderMouse = null;
371         int nCount = maObjectList.size();
372         for (int i=nCount-1; i>=0; --i)
373         {
374             CanvasShape aObject = (CanvasShape)maObjectList.elementAt(i);
375             if (aObject != null)
376                 if (aObject.contains (e.getX(),e.getY()))
377                 {
378                     aObjectUnderMouse = aObject;
379                     break;
380                 }
381         }
382         return aObjectUnderMouse;
383     }
384 
385     protected boolean highlightObject (CanvasShape aNewActiveObject)
386     {
387         if (aNewActiveObject != maActiveObject)
388         {
389             if (maActiveObject != null)
390                 maActiveObject.unhighlight();
391 
392             maActiveObject = aNewActiveObject;
393             if (maActiveObject != null)
394             {
395                 if (maTree != null)
396                 {
397                     maTree.scrollPathToVisible (maActiveObject.getPath());
398                     maTree.setSelectionPath (maActiveObject.getPath());
399                     maTree.repaint ();
400                 }
401                 maActiveObject.highlight ();
402                 repaint ();
403             }
404             return true;
405         }
406         else
407             return false;
408     }
409 
410     /** Called when the selection of the tree changes.  Highlight the
411         corresponding graphical representation of the first selected object.
412     */
413     public void valueChanged (javax.swing.event.TreeSelectionEvent event)
414     {
415         TreePath aPath = event.getPath();
416         Object aObject = aPath.getLastPathComponent();
417         if (aObject instanceof AccTreeNode)
418         {
419             CanvasShape aCanvasShape = (CanvasShape)maObjects.get ((AccTreeNode)aObject);
420             if (highlightObject (aCanvasShape))
421                 repaint();
422         }
423     }
424 
425     private int
426         mnXAnchor,
427         mnYAnchor,
428         maResizeFlag;
429     private double
430         mnHOffset,
431         mnVOffset,
432         mnScale;
433     private CanvasShape
434         maActiveObject;
435     private java.util.HashMap
436         maObjects;
437     private Vector
438         maObjectList,
439         maContexts,
440         maNodes;
441     private Rectangle
442         maBoundingBox;
443     private JTree
444         maTree;
445     // The size of the widget at the last call of setupTransformation()
446     private Dimension
447         maLastWidgetSize;
448 }
449