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