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