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 23 24 package org.openoffice.java.accessibility; 25 26 import java.lang.ref.WeakReference; 27 import javax.accessibility.Accessible; 28 import javax.accessibility.AccessibleStateSet; 29 30 import com.sun.star.uno.*; 31 import com.sun.star.accessibility.*; 32 import org.openoffice.java.accessibility.logging.XAccessibleEventLog; 33 34 /** 35 */ 36 public class AccessibleObjectFactory { 37 // This type is needed for conversions from/to uno Any 38 public static final Type XAccessibleType = new Type(XAccessible.class); 39 40 private static java.util.Hashtable objectList = new java.util.Hashtable(); 41 private static java.awt.FocusTraversalPolicy focusTraversalPolicy = new FocusTraversalPolicy(); 42 43 private static java.awt.EventQueue theEventQueue = java.awt.Toolkit.getDefaultToolkit(). 44 getSystemEventQueue(); 45 getEventQueue()46 public static java.awt.EventQueue getEventQueue() { 47 return theEventQueue; 48 } 49 postFocusGained(java.awt.Component c)50 public static void postFocusGained(java.awt.Component c) { 51 getEventQueue().postEvent(new java.awt.event.FocusEvent(c, java.awt.event.FocusEvent.FOCUS_GAINED)); 52 } 53 postWindowGainedFocus(java.awt.Window w)54 public static void postWindowGainedFocus(java.awt.Window w) { 55 postWindowEvent(w, java.awt.event.WindowEvent.WINDOW_GAINED_FOCUS); 56 } 57 postWindowLostFocus(java.awt.Window w)58 public static void postWindowLostFocus(java.awt.Window w) { 59 postWindowEvent(w, java.awt.event.WindowEvent.WINDOW_LOST_FOCUS); 60 } 61 postWindowActivated(java.awt.Window w)62 public static void postWindowActivated(java.awt.Window w) { 63 postWindowEvent(w, java.awt.event.WindowEvent.WINDOW_ACTIVATED); 64 } 65 postWindowDeactivated(java.awt.Window w)66 public static void postWindowDeactivated(java.awt.Window w) { 67 postWindowEvent(w, java.awt.event.WindowEvent.WINDOW_DEACTIVATED); 68 } 69 postWindowOpened(java.awt.Window w)70 public static void postWindowOpened(java.awt.Window w) { 71 postWindowEvent(w, java.awt.event.WindowEvent.WINDOW_OPENED); 72 } 73 postWindowClosed(java.awt.Window w)74 public static void postWindowClosed(java.awt.Window w) { 75 postWindowEvent(w, java.awt.event.WindowEvent.WINDOW_CLOSED); 76 } 77 invokeAndWait()78 public static void invokeAndWait() { 79 try { 80 theEventQueue.invokeAndWait( new java.lang.Runnable () { 81 public void run() { 82 } 83 }); 84 } catch (java.lang.reflect.InvocationTargetException e) { 85 } catch (java.lang.InterruptedException e) { 86 } 87 } 88 postWindowEvent(java.awt.Window w, int i)89 private static void postWindowEvent(java.awt.Window w, int i) { 90 theEventQueue.postEvent(new java.awt.event.WindowEvent(w, i)); 91 } 92 getAccessibleComponent(XAccessible xAccessible)93 public static java.awt.Component getAccessibleComponent(XAccessible xAccessible) { 94 java.awt.Component c = null; 95 if (xAccessible != null) { 96 // Retrieve unique id for the original UNO object to be used as a hash key 97 String oid = UnoRuntime.generateOid(xAccessible); 98 99 // Check if we already have a wrapper object for this context 100 synchronized (objectList) { 101 WeakReference r = (WeakReference) objectList.get(oid); 102 if(r != null) { 103 c = (java.awt.Component) r.get(); 104 } 105 } 106 } 107 return c; 108 } 109 addChild(java.awt.Container parent, Object any)110 public static void addChild(java.awt.Container parent, Object any) { 111 try { 112 addChild(parent, (XAccessible) AnyConverter.toObject(XAccessibleType, any)); 113 } catch (com.sun.star.lang.IllegalArgumentException e) { 114 System.err.println(e.getClass().getName() + " caught: " + e.getMessage()); 115 } 116 } 117 addChild(java.awt.Container parent, XAccessible child)118 public static void addChild(java.awt.Container parent, XAccessible child) { 119 try { 120 if (child != null) { 121 XAccessibleContext childAC = child.getAccessibleContext(); 122 if (childAC != null) { 123 XAccessibleStateSet stateSet = childAC.getAccessibleStateSet(); 124 if (stateSet != null) { 125 java.awt.Component c = getAccessibleComponent(child); 126 127 // Re-use existing wrapper if possible, create a new one otherwise 128 if (c != null) { 129 // Seems to be already in child list 130 if (parent.equals(c.getParent())) 131 return; 132 // Update general component states 133 c.setEnabled(stateSet.contains(AccessibleStateType.ENABLED)); 134 c.setVisible(stateSet.contains(AccessibleStateType.VISIBLE)); 135 } else { 136 c = createAccessibleComponentImpl(child, childAC, stateSet); 137 } 138 139 if (c != null) { 140 if (c instanceof java.awt.Container) { 141 populateContainer((java.awt.Container) c, childAC); 142 } 143 parent.add(c); 144 // Simulate focus gained event for new child 145 if (stateSet.contains(AccessibleStateType.FOCUSED)) { 146 postFocusGained(c); 147 } 148 } 149 } 150 } 151 } 152 } catch (com.sun.star.uno.RuntimeException e) { 153 System.err.println(e.getClass().getName() + " caught: " + e.getMessage()); 154 e.printStackTrace(); 155 } 156 } 157 removeChild(java.awt.Container parent, Object any)158 protected static void removeChild(java.awt.Container parent, Object any) { 159 try { 160 XAccessible xAccessible = (XAccessible) AnyConverter.toObject(XAccessibleType, any); 161 java.awt.Component c = getAccessibleComponent(xAccessible); 162 163 if (c != null) { 164 parent.remove(c); 165 166 if (c instanceof java.awt.Container) { 167 clearContainer((java.awt.Container) c); 168 } 169 } 170 } catch (com.sun.star.lang.IllegalArgumentException e) { 171 System.err.println(e.getClass().getName() + " caught: " + e.getMessage()); 172 } 173 } 174 175 176 /** 177 * Removes all children from the container parent 178 */ 179 clearContainer(java.awt.Container parent)180 protected static void clearContainer(java.awt.Container parent) { 181 // Purge all children from this container 182 int count = parent.getComponentCount(); 183 for (int i = 0; i < count; i++) { 184 java.awt.Component c = parent.getComponent(i); 185 if (c instanceof java.awt.Container) { 186 clearContainer((java.awt.Container) c); 187 } 188 } 189 parent.removeAll(); 190 } 191 192 193 /** 194 * Populates the given Container parent with wrapper objects for all children of parentAC. This method is 195 * intended to be called when a container is added using a CHILDREN_CHANGED event. 196 */ 197 populateContainer(java.awt.Container parent, XAccessibleContext parentAC)198 protected static void populateContainer(java.awt.Container parent, XAccessibleContext parentAC) { 199 if (parentAC != null) { 200 try { 201 int childCount = parentAC.getAccessibleChildCount(); 202 for (int i=0; i<childCount; i++) { 203 addChild(parent, parentAC.getAccessibleChild(i)); 204 } 205 } catch (java.lang.Exception e) { 206 System.err.println(e.getClass().getName() + " caught: " + e.getMessage()); 207 e.printStackTrace(); 208 } 209 } 210 } 211 212 /** 213 * Populates the given Container parent with wrapper objects for all children of parentAC. This method is 214 * intended to be called when a new window has been opened. 215 */ populateContainer(java.awt.Container parent, XAccessibleContext parentAC, java.awt.Window frame)216 protected static void populateContainer(java.awt.Container parent, XAccessibleContext parentAC, java.awt.Window frame) { 217 if (parentAC != null) { 218 try { 219 int childCount = parentAC.getAccessibleChildCount(); 220 for (int i=0; i<childCount; i++) { 221 XAccessible child = parentAC.getAccessibleChild(i); 222 if (child != null) { 223 XAccessibleContext childAC = child.getAccessibleContext(); 224 java.awt.Component c = createAccessibleComponent(child, childAC, frame); 225 if (c != null) { 226 if (c instanceof java.awt.Container) { 227 populateContainer((java.awt.Container) c, childAC, frame); 228 } 229 parent.add(c); 230 } 231 } else if (Build.DEBUG) { 232 System.err.println("ignoring not accessible child " + i); 233 } 234 } 235 } 236 237 catch (java.lang.Exception e) { 238 System.err.println(e.getClass().getName() + " caught: " + e.getMessage()); 239 e.printStackTrace(); 240 } 241 } 242 } 243 createAccessibleComponent(XAccessible xAccessible)244 protected static java.awt.Component createAccessibleComponent(XAccessible xAccessible) { 245 try { 246 XAccessibleContext xAccessibleContext = xAccessible.getAccessibleContext(); 247 if (xAccessibleContext != null) { 248 return createAccessibleComponentImpl(xAccessible, xAccessibleContext, xAccessibleContext.getAccessibleStateSet()); 249 } 250 } catch (com.sun.star.uno.RuntimeException e) { 251 System.err.println(e.getClass().getName() + " caught: " + e.getMessage()); 252 e.printStackTrace(); 253 } 254 return null; 255 } 256 createAccessibleComponent(XAccessible xAccessible, XAccessibleContext xAccessibleContext, java.awt.Window frame)257 protected static java.awt.Component createAccessibleComponent(XAccessible xAccessible, XAccessibleContext xAccessibleContext, 258 java.awt.Window frame) { 259 if (xAccessibleContext != null) { 260 try { 261 XAccessibleStateSet xAccessibleStateSet = xAccessibleContext.getAccessibleStateSet(); 262 java.awt.Component c = createAccessibleComponentImpl(xAccessible, xAccessibleContext, xAccessibleStateSet); 263 if (c != null) { 264 // Set this component as initial component 265 if (xAccessibleStateSet.contains(AccessibleStateType.FOCUSED)) { 266 if (frame instanceof NativeFrame) { 267 ((NativeFrame) frame).setInitialComponent(c); 268 } 269 } 270 return c; 271 } 272 } catch (com.sun.star.uno.RuntimeException e) { 273 System.err.println(e.getClass().getName() + " caught: " + e.getMessage()); 274 e.printStackTrace(); 275 } 276 } 277 return null; 278 } 279 createAccessibleComponentImpl(XAccessible xAccessible, XAccessibleContext xAccessibleContext, XAccessibleStateSet xAccessibleStateSet)280 protected static java.awt.Component createAccessibleComponentImpl(XAccessible xAccessible, XAccessibleContext xAccessibleContext, 281 XAccessibleStateSet xAccessibleStateSet) { 282 java.awt.Component c = null; 283 short role = xAccessibleContext.getAccessibleRole(); 284 switch (role) { 285 case AccessibleRole.CANVAS: 286 c = new Container(javax.accessibility.AccessibleRole.CANVAS, 287 xAccessible, xAccessibleContext); 288 break; 289 case AccessibleRole.CHECK_BOX: 290 c = new CheckBox(xAccessible, xAccessibleContext); 291 break; 292 case AccessibleRole.COMBO_BOX: 293 c = new ComboBox(xAccessible, xAccessibleContext); 294 break; 295 case AccessibleRole.DOCUMENT: 296 c = new Container(javax.accessibility.AccessibleRole.CANVAS, 297 xAccessible, xAccessibleContext); 298 break; 299 case AccessibleRole.EMBEDDED_OBJECT: 300 c = new Container(javax.accessibility.AccessibleRole.PANEL, 301 xAccessible, xAccessibleContext); 302 break; 303 case AccessibleRole.END_NOTE: 304 c = new Container(javax.accessibility.AccessibleRole.PANEL, 305 xAccessible, xAccessibleContext); 306 break; 307 case AccessibleRole.FILLER: 308 c = new Container(javax.accessibility.AccessibleRole.FILLER, 309 xAccessible, xAccessibleContext); 310 break; 311 case AccessibleRole.FOOTNOTE: 312 c = new Container(javax.accessibility.AccessibleRole.PANEL, 313 xAccessible, xAccessibleContext); 314 break; 315 case AccessibleRole.FOOTER: 316 c = new Container(javax.accessibility.AccessibleRole.PANEL, 317 xAccessible, xAccessibleContext); 318 break; 319 case AccessibleRole.GRAPHIC: 320 c = new Container(javax.accessibility.AccessibleRole.PANEL, 321 xAccessible, xAccessibleContext); 322 break; 323 case AccessibleRole.HEADER: 324 c = new Container(javax.accessibility.AccessibleRole.PANEL, 325 xAccessible, xAccessibleContext); 326 break; 327 case AccessibleRole.ICON: 328 c = new Icon(xAccessible, xAccessibleContext); 329 break; 330 case AccessibleRole.LABEL: 331 c = new Label(xAccessible, xAccessibleContext); 332 break; 333 case AccessibleRole.LAYERED_PANE: 334 c = new Container(javax.accessibility.AccessibleRole.LAYERED_PANE, 335 xAccessible, xAccessibleContext); 336 break; 337 case AccessibleRole.LIST: 338 if (xAccessibleStateSet.contains(AccessibleStateType.MANAGES_DESCENDANTS)) { 339 c = new List(xAccessible, xAccessibleContext); 340 } else { 341 c = new Container(javax.accessibility.AccessibleRole.LIST, 342 xAccessible, xAccessibleContext); 343 } 344 break; 345 case AccessibleRole.MENU: 346 c = new Menu(xAccessible, xAccessibleContext); 347 break; 348 case AccessibleRole.MENU_BAR: 349 c = new MenuContainer(javax.accessibility.AccessibleRole.MENU_BAR, xAccessible, xAccessibleContext); 350 break; 351 case AccessibleRole.MENU_ITEM: 352 c = new MenuItem(xAccessible, xAccessibleContext); 353 break; 354 case AccessibleRole.POPUP_MENU: 355 c = new MenuContainer(javax.accessibility.AccessibleRole.POPUP_MENU, xAccessible, xAccessibleContext); 356 break; 357 case AccessibleRole.OPTION_PANE: 358 c = new Container(javax.accessibility.AccessibleRole.OPTION_PANE, 359 xAccessible, xAccessibleContext); 360 break; 361 case AccessibleRole.PAGE_TAB: 362 c = new Container(javax.accessibility.AccessibleRole.PAGE_TAB, xAccessible, xAccessibleContext); 363 break; 364 case AccessibleRole.PAGE_TAB_LIST: 365 c = new Container(javax.accessibility.AccessibleRole.PAGE_TAB_LIST, xAccessible, xAccessibleContext); 366 break; 367 case AccessibleRole.PARAGRAPH: 368 case AccessibleRole.HEADING: 369 c = new Paragraph(xAccessible, xAccessibleContext); 370 break; 371 case AccessibleRole.PANEL: 372 c = new Container(javax.accessibility.AccessibleRole.PANEL, 373 xAccessible, xAccessibleContext); 374 break; 375 case AccessibleRole.PUSH_BUTTON: 376 c = new Button(xAccessible, xAccessibleContext); 377 break; 378 case AccessibleRole.RADIO_BUTTON: 379 c = new RadioButton(xAccessible, xAccessibleContext); 380 break; 381 case AccessibleRole.ROOT_PANE: 382 c = new Container(javax.accessibility.AccessibleRole.ROOT_PANE, 383 xAccessible, xAccessibleContext); 384 break; 385 case AccessibleRole.SCROLL_BAR: 386 c = new ScrollBar(xAccessible, xAccessibleContext); 387 break; 388 case AccessibleRole.SCROLL_PANE: 389 c = new Container(javax.accessibility.AccessibleRole.SCROLL_PANE, 390 xAccessible, xAccessibleContext); 391 break; 392 case AccessibleRole.SEPARATOR: 393 c = new Separator(xAccessible, xAccessibleContext); 394 break; 395 case AccessibleRole.SHAPE: 396 c = new Container(javax.accessibility.AccessibleRole.CANVAS, 397 xAccessible, xAccessibleContext); 398 break; 399 case AccessibleRole.SPLIT_PANE: 400 c = new Container(javax.accessibility.AccessibleRole.SPLIT_PANE, 401 xAccessible, xAccessibleContext); 402 break; 403 case AccessibleRole.STATUS_BAR: 404 c = new Container(javax.accessibility.AccessibleRole.STATUS_BAR, 405 xAccessible, xAccessibleContext); 406 break; 407 case AccessibleRole.COLUMN_HEADER: 408 case AccessibleRole.TABLE: 409 if (xAccessibleStateSet.contains(AccessibleStateType.MANAGES_DESCENDANTS)) { 410 c = new Table(xAccessible, xAccessibleContext, 411 xAccessibleStateSet.contains(AccessibleStateType.MULTI_SELECTABLE)); 412 } else { 413 c = new Container(javax.accessibility.AccessibleRole.TABLE, 414 xAccessible, xAccessibleContext); 415 } 416 break; 417 case AccessibleRole.TABLE_CELL: 418 if( xAccessibleContext.getAccessibleChildCount() > 0 ) 419 c = new Container(javax.accessibility.AccessibleRole.PANEL, 420 xAccessible, xAccessibleContext); 421 else 422 c = new Label(xAccessible, xAccessibleContext); 423 break; 424 case AccessibleRole.TEXT: 425 c = new TextComponent(xAccessible, xAccessibleContext); 426 break; 427 case AccessibleRole.TEXT_FRAME: 428 c = new Container(javax.accessibility.AccessibleRole.PANEL, 429 xAccessible, xAccessibleContext); 430 break; 431 case AccessibleRole.TOGGLE_BUTTON: 432 c = new ToggleButton(xAccessible, xAccessibleContext); 433 break; 434 case AccessibleRole.TOOL_BAR: 435 c = new Container(javax.accessibility.AccessibleRole.TOOL_BAR, 436 xAccessible, xAccessibleContext); 437 break; 438 case AccessibleRole.TOOL_TIP: 439 c = new ToolTip(xAccessible, xAccessibleContext); 440 break; 441 case AccessibleRole.TREE: 442 c = new Tree(xAccessible, xAccessibleContext); 443 break; 444 case AccessibleRole.VIEW_PORT: 445 c = new Container(javax.accessibility.AccessibleRole.VIEWPORT, 446 xAccessible, xAccessibleContext); 447 break; 448 default: 449 System.err.println("Unmapped accessible object " + role); 450 System.err.println("usually mapped to " + AccessibleRoleAdapter.getAccessibleRole(role)); 451 c = new Container(AccessibleRoleAdapter.getAccessibleRole(role), 452 xAccessible, xAccessibleContext); 453 break; 454 } 455 if (c != null) { 456 // Add the newly created object to the cache list 457 synchronized (objectList) { 458 objectList.put(c.toString(), new WeakReference(c)); 459 if (Build.DEBUG) { 460 // System.out.println("Object cache now contains " + objectList.size() + " objects."); 461 } 462 } 463 464 AccessibleStateAdapter.setComponentState(c, xAccessibleStateSet); 465 466 if (! Build.PRODUCT) { 467 String property = System.getProperty("AccessBridgeLogging"); 468 if ((property != null) && (property.indexOf("event") != -1)) { 469 XAccessibleEventLog.addEventListener(xAccessibleContext, c); 470 } 471 } 472 } 473 474 return c; 475 } 476 disposing(java.awt.Component c)477 protected static void disposing(java.awt.Component c) { 478 if (c != null) { 479 synchronized (objectList) { 480 objectList.remove(c.toString()); 481 } 482 } 483 } 484 getTopWindow(XAccessible xAccessible)485 public static java.awt.Window getTopWindow(XAccessible xAccessible) { 486 XAccessibleContext xAccessibleContext = xAccessible.getAccessibleContext(); 487 488 if (xAccessibleContext != null) { 489 short role = xAccessibleContext.getAccessibleRole(); 490 XAccessibleStateSet xAccessibleStateSet = xAccessibleContext.getAccessibleStateSet(); 491 XAccessibleComponent xAccessibleComponent = (XAccessibleComponent) 492 UnoRuntime.queryInterface(XAccessibleComponent.class, xAccessibleContext); 493 494 java.awt.Window w; 495 if (role == AccessibleRole.DIALOG) { 496 w = new Dialog(new Application(), 497 xAccessibleContext.getAccessibleName(), 498 xAccessibleStateSet.contains(AccessibleStateType.MODAL), 499 xAccessibleComponent); 500 } else if (role == AccessibleRole.ALERT) { 501 w = new Alert(new Application(), 502 xAccessibleContext.getAccessibleName(), 503 xAccessibleStateSet.contains(AccessibleStateType.MODAL), 504 xAccessibleComponent); 505 } else if (role == AccessibleRole.FRAME) { 506 w = new Frame(xAccessibleContext.getAccessibleName(), 507 xAccessibleComponent); 508 } else if (role == AccessibleRole.WINDOW) { 509 java.awt.Window activeWindow = 510 java.awt.KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow(); 511 if (activeWindow != null) { 512 w = new Window(activeWindow, xAccessibleComponent); 513 } else { 514 if (Build.DEBUG) { 515 System.err.println("no active frame found for Window: " + role); 516 } 517 return null; 518 } 519 } else { 520 if (Build.DEBUG) { 521 System.err.println("invalid role for toplevel window: " + role); 522 } 523 return null; 524 } 525 populateContainer(w, xAccessibleContext, w); 526 w.setFocusTraversalPolicy(focusTraversalPolicy); 527 w.setVisible(true); 528 529 // Make the new window the focused one if it has an initialy focused object set. 530 java.awt.Component c = ((NativeFrame) w).getInitialComponent(); 531 if (c != null) { 532 postWindowGainedFocus(w); 533 } 534 return w; 535 } 536 537 return null; 538 } 539 } 540 541 542