1*34dd1e25SAndrew Rist /************************************************************** 2*34dd1e25SAndrew Rist * 3*34dd1e25SAndrew Rist * Licensed to the Apache Software Foundation (ASF) under one 4*34dd1e25SAndrew Rist * or more contributor license agreements. See the NOTICE file 5*34dd1e25SAndrew Rist * distributed with this work for additional information 6*34dd1e25SAndrew Rist * regarding copyright ownership. The ASF licenses this file 7*34dd1e25SAndrew Rist * to you under the Apache License, Version 2.0 (the 8*34dd1e25SAndrew Rist * "License"); you may not use this file except in compliance 9*34dd1e25SAndrew Rist * with the License. You may obtain a copy of the License at 10*34dd1e25SAndrew Rist * 11*34dd1e25SAndrew Rist * http://www.apache.org/licenses/LICENSE-2.0 12*34dd1e25SAndrew Rist * 13*34dd1e25SAndrew Rist * Unless required by applicable law or agreed to in writing, 14*34dd1e25SAndrew Rist * software distributed under the License is distributed on an 15*34dd1e25SAndrew Rist * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16*34dd1e25SAndrew Rist * KIND, either express or implied. See the License for the 17*34dd1e25SAndrew Rist * specific language governing permissions and limitations 18*34dd1e25SAndrew Rist * under the License. 19*34dd1e25SAndrew Rist * 20*34dd1e25SAndrew Rist *************************************************************/ 21*34dd1e25SAndrew Rist 22*34dd1e25SAndrew Rist 23cdf0e10cSrcweir 24cdf0e10cSrcweir import java.util.Vector; 25cdf0e10cSrcweir 26cdf0e10cSrcweir import com.sun.star.uno.AnyConverter; 27cdf0e10cSrcweir import com.sun.star.uno.UnoRuntime; 28cdf0e10cSrcweir import com.sun.star.uno.Type; 29cdf0e10cSrcweir import com.sun.star.accessibility.*; 30cdf0e10cSrcweir 31cdf0e10cSrcweir /** Handle all the events send from accessibility objects. The events 32cdf0e10cSrcweir denoting new or removed top windows are handled as well. 33cdf0e10cSrcweir 34cdf0e10cSrcweir It does not implement any listener interface as does the 35cdf0e10cSrcweir EventListenerProxy class because it is interested only in a sub set of 36cdf0e10cSrcweir the event types. 37cdf0e10cSrcweir */ 38cdf0e10cSrcweir public class EventHandler 39cdf0e10cSrcweir { 40cdf0e10cSrcweir public EventHandler () 41cdf0e10cSrcweir { 42cdf0e10cSrcweir mnTopWindowCount = 0; 43cdf0e10cSrcweir maListenerProxy = new EventListenerProxy (this); 44cdf0e10cSrcweir maConnectionTask = new ConnectionTask (maListenerProxy); 45cdf0e10cSrcweir maObjectDisplays = new Vector (); 46cdf0e10cSrcweir } 47cdf0e10cSrcweir 48cdf0e10cSrcweir public synchronized void addObjectDisplay (IAccessibleObjectDisplay aDisplay) 49cdf0e10cSrcweir { 50cdf0e10cSrcweir maObjectDisplays.add (aDisplay); 51cdf0e10cSrcweir } 52cdf0e10cSrcweir 53cdf0e10cSrcweir 54cdf0e10cSrcweir public void finalize () 55cdf0e10cSrcweir { 56cdf0e10cSrcweir // When it is running then cancel the timer that tries to connect to 57cdf0e10cSrcweir // the Office. 58cdf0e10cSrcweir if (maConnectionTask != null) 59cdf0e10cSrcweir maConnectionTask.cancel(); 60cdf0e10cSrcweir } 61cdf0e10cSrcweir 62cdf0e10cSrcweir 63cdf0e10cSrcweir 64cdf0e10cSrcweir public void disposing (com.sun.star.lang.EventObject aEvent) 65cdf0e10cSrcweir { 66cdf0e10cSrcweir // Ignored: We are not holding references to accessibility objects. 67cdf0e10cSrcweir } 68cdf0e10cSrcweir 69cdf0e10cSrcweir 70cdf0e10cSrcweir 71cdf0e10cSrcweir 72cdf0e10cSrcweir /** This method is called back when a new top level window has been opened. 73cdf0e10cSrcweir */ 74cdf0e10cSrcweir public void windowOpened (XAccessible xAccessible) 75cdf0e10cSrcweir { 76cdf0e10cSrcweir if (xAccessible != null) 77cdf0e10cSrcweir { 78cdf0e10cSrcweir // Update the counter of currently open top level windows 79cdf0e10cSrcweir // observed by this object. 80cdf0e10cSrcweir mnTopWindowCount += 1; 81cdf0e10cSrcweir 82cdf0e10cSrcweir XAccessibleContext xContext = xAccessible.getAccessibleContext(); 83cdf0e10cSrcweir if (xContext != null) 84cdf0e10cSrcweir { 85cdf0e10cSrcweir MessageArea.println ("new top level window has accessible name " 86cdf0e10cSrcweir + xContext.getAccessibleName()); 87cdf0e10cSrcweir 88cdf0e10cSrcweir // Register at all accessible objects of the new window. 89cdf0e10cSrcweir new RegistrationThread ( 90cdf0e10cSrcweir maListenerProxy, 91cdf0e10cSrcweir xContext, 92cdf0e10cSrcweir true, 93cdf0e10cSrcweir true); 94cdf0e10cSrcweir } 95cdf0e10cSrcweir else 96cdf0e10cSrcweir MessageArea.println ("new top level window is not accessible."); 97cdf0e10cSrcweir } 98cdf0e10cSrcweir else 99cdf0e10cSrcweir MessageArea.println ("new top level window is not accessible."); 100cdf0e10cSrcweir } 101cdf0e10cSrcweir 102cdf0e10cSrcweir 103cdf0e10cSrcweir 104cdf0e10cSrcweir 105cdf0e10cSrcweir public void windowClosed (XAccessible xAccessible) 106cdf0e10cSrcweir { 107cdf0e10cSrcweir mnTopWindowCount -= 1; 108cdf0e10cSrcweir MessageArea.println ("window closed, " + mnTopWindowCount + " still open"); 109cdf0e10cSrcweir if (mnTopWindowCount == 0) 110cdf0e10cSrcweir { 111cdf0e10cSrcweir // This was the last window. Wait for a new connection. 112cdf0e10cSrcweir MessageArea.println ("lost connection to office"); 113cdf0e10cSrcweir new ConnectionTask (maListenerProxy); 114cdf0e10cSrcweir } 115cdf0e10cSrcweir if (xAccessible != null) 116cdf0e10cSrcweir new RegistrationThread ( 117cdf0e10cSrcweir maListenerProxy, 118cdf0e10cSrcweir xAccessible.getAccessibleContext(), 119cdf0e10cSrcweir false, 120cdf0e10cSrcweir true); 121cdf0e10cSrcweir } 122cdf0e10cSrcweir 123cdf0e10cSrcweir 124cdf0e10cSrcweir 125cdf0e10cSrcweir 126cdf0e10cSrcweir /** Print a message that the given object just received the focus. Call 127cdf0e10cSrcweir all accessible object diplays and tell them to update. 128cdf0e10cSrcweir */ 129cdf0e10cSrcweir private synchronized void focusGained (XAccessibleContext xContext) 130cdf0e10cSrcweir { 131cdf0e10cSrcweir if (xContext != null) 132cdf0e10cSrcweir { 133cdf0e10cSrcweir MessageArea.println ("focusGained: " + xContext.getAccessibleName() 134cdf0e10cSrcweir + " with role " 135cdf0e10cSrcweir + NameProvider.getRoleName (xContext.getAccessibleRole())); 136cdf0e10cSrcweir 137cdf0e10cSrcweir // Tell the object displays to update their views. 138cdf0e10cSrcweir for (int i=0; i<maObjectDisplays.size(); i++) 139cdf0e10cSrcweir { 140cdf0e10cSrcweir IAccessibleObjectDisplay aDisplay = 141cdf0e10cSrcweir (IAccessibleObjectDisplay)maObjectDisplays.get(i); 142cdf0e10cSrcweir if (aDisplay != null) 143cdf0e10cSrcweir aDisplay.setAccessibleObject (xContext); 144cdf0e10cSrcweir } 145cdf0e10cSrcweir 146cdf0e10cSrcweir // Remember the currently focused object. 147cdf0e10cSrcweir mxFocusedObject = xContext; 148cdf0e10cSrcweir } 149cdf0e10cSrcweir else 150cdf0e10cSrcweir MessageArea.println ("focusGained: null"); 151cdf0e10cSrcweir } 152cdf0e10cSrcweir 153cdf0e10cSrcweir 154cdf0e10cSrcweir 155cdf0e10cSrcweir 156cdf0e10cSrcweir /** Print a message that the given object just lost the focus. Call 157cdf0e10cSrcweir all accessible object diplays and tell them to update. 158cdf0e10cSrcweir */ 159cdf0e10cSrcweir private synchronized void focusLost (XAccessibleContext xContext) 160cdf0e10cSrcweir { 161cdf0e10cSrcweir if (xContext != null) 162cdf0e10cSrcweir { 163cdf0e10cSrcweir MessageArea.println ("focusLost: " 164cdf0e10cSrcweir + xContext.getAccessibleName() 165cdf0e10cSrcweir + " with role " 166cdf0e10cSrcweir + NameProvider.getRoleName (xContext.getAccessibleRole())); 167cdf0e10cSrcweir 168cdf0e10cSrcweir // Tell the object displays to update their views. 169cdf0e10cSrcweir for (int i=0; i<maObjectDisplays.size(); i++) 170cdf0e10cSrcweir { 171cdf0e10cSrcweir IAccessibleObjectDisplay aDisplay = 172cdf0e10cSrcweir (IAccessibleObjectDisplay)maObjectDisplays.get(i); 173cdf0e10cSrcweir if (aDisplay != null) 174cdf0e10cSrcweir aDisplay.setAccessibleObject (null); 175cdf0e10cSrcweir } 176cdf0e10cSrcweir mxFocusedObject = null; 177cdf0e10cSrcweir } 178cdf0e10cSrcweir else 179cdf0e10cSrcweir MessageArea.println ("focusLost: null"); 180cdf0e10cSrcweir } 181cdf0e10cSrcweir 182cdf0e10cSrcweir 183cdf0e10cSrcweir 184cdf0e10cSrcweir 185cdf0e10cSrcweir /** Handle a change of the caret position. Ignore this on all objects 186cdf0e10cSrcweir but the one currently focused. 187cdf0e10cSrcweir */ 188cdf0e10cSrcweir private void handleCaretEvent (XAccessibleContext xContext, 189cdf0e10cSrcweir long nOldPosition, long nNewPosition) 190cdf0e10cSrcweir { 191cdf0e10cSrcweir if (xContext == mxFocusedObject) 192cdf0e10cSrcweir MessageArea.println ("caret moved from " + nOldPosition + " to " + nNewPosition); 193cdf0e10cSrcweir } 194cdf0e10cSrcweir 195cdf0e10cSrcweir 196cdf0e10cSrcweir 197cdf0e10cSrcweir 198cdf0e10cSrcweir /** Print a message that a state has been changed. 199cdf0e10cSrcweir @param xContext 200cdf0e10cSrcweir The accessible context of the object whose state has changed. 201cdf0e10cSrcweir @param nOldState 202cdf0e10cSrcweir When not zero then this value describes a state that has been reset. 203cdf0e10cSrcweir @param nNewValue 204cdf0e10cSrcweir When not zero then this value describes a state that has been set. 205cdf0e10cSrcweir */ 206cdf0e10cSrcweir private void handleStateChange (XAccessibleContext xContext, short nOldState, short nNewState) 207cdf0e10cSrcweir { 208cdf0e10cSrcweir // Determine which state has changed and what is its new value. 209cdf0e10cSrcweir short nState; 210cdf0e10cSrcweir boolean aNewValue; 211cdf0e10cSrcweir if (nOldState >= 0) 212cdf0e10cSrcweir { 213cdf0e10cSrcweir nState = nOldState; 214cdf0e10cSrcweir aNewValue = false; 215cdf0e10cSrcweir } 216cdf0e10cSrcweir else 217cdf0e10cSrcweir { 218cdf0e10cSrcweir nState = nNewState; 219cdf0e10cSrcweir aNewValue = true; 220cdf0e10cSrcweir } 221cdf0e10cSrcweir 222cdf0e10cSrcweir // Print a message about the changed state. 223cdf0e10cSrcweir MessageArea.print ("setting state " + NameProvider.getStateName(nState) 224cdf0e10cSrcweir + " to " + aNewValue); 225cdf0e10cSrcweir if (xContext != null) 226cdf0e10cSrcweir { 227cdf0e10cSrcweir MessageArea.println (" at " + xContext.getAccessibleName() + " with role " 228cdf0e10cSrcweir + NameProvider.getRoleName(xContext.getAccessibleRole())); 229cdf0e10cSrcweir } 230cdf0e10cSrcweir else 231cdf0e10cSrcweir MessageArea.println (" at null"); 232cdf0e10cSrcweir 233cdf0e10cSrcweir // Further handling of some states 234cdf0e10cSrcweir switch (nState) 235cdf0e10cSrcweir { 236cdf0e10cSrcweir case AccessibleStateType.FOCUSED: 237cdf0e10cSrcweir if (aNewValue) 238cdf0e10cSrcweir focusGained (xContext); 239cdf0e10cSrcweir else 240cdf0e10cSrcweir focusLost (xContext); 241cdf0e10cSrcweir } 242cdf0e10cSrcweir } 243cdf0e10cSrcweir 244cdf0e10cSrcweir 245cdf0e10cSrcweir 246cdf0e10cSrcweir 247cdf0e10cSrcweir /** Handle a child event that describes the creation of removal of a 248cdf0e10cSrcweir single child. 249cdf0e10cSrcweir */ 250cdf0e10cSrcweir private void handleChildEvent ( 251cdf0e10cSrcweir XAccessibleContext aOldChild, 252cdf0e10cSrcweir XAccessibleContext aNewChild) 253cdf0e10cSrcweir { 254cdf0e10cSrcweir if (aOldChild != null) 255cdf0e10cSrcweir // Remove event listener from the child and all of its descendants. 256cdf0e10cSrcweir new RegistrationThread (maListenerProxy, aOldChild, false, false); 257cdf0e10cSrcweir else if (aNewChild != null) 258cdf0e10cSrcweir // Add event listener to the new child and all of its descendants. 259cdf0e10cSrcweir new RegistrationThread (maListenerProxy, aNewChild, true, false); 260cdf0e10cSrcweir } 261cdf0e10cSrcweir 262cdf0e10cSrcweir 263cdf0e10cSrcweir 264cdf0e10cSrcweir 265cdf0e10cSrcweir /** Handle the change of some visible data of an object. 266cdf0e10cSrcweir */ 267cdf0e10cSrcweir private void handleVisibleDataEvent (XAccessibleContext xContext) 268cdf0e10cSrcweir { 269cdf0e10cSrcweir // The given object may affect the visible appearance of the focused 270cdf0e10cSrcweir // object even when the two are not identical when the given object 271cdf0e10cSrcweir // is an ancestor of the focused object. 272cdf0e10cSrcweir // In order to not check this we simply call an update on the 273cdf0e10cSrcweir // focused object. 274cdf0e10cSrcweir if (mxFocusedObject != null) 275cdf0e10cSrcweir for (int i=0; i<maObjectDisplays.size(); i++) 276cdf0e10cSrcweir { 277cdf0e10cSrcweir IAccessibleObjectDisplay aDisplay = 278cdf0e10cSrcweir (IAccessibleObjectDisplay)maObjectDisplays.get(i); 279cdf0e10cSrcweir if (aDisplay != null) 280cdf0e10cSrcweir aDisplay.updateAccessibleObject (mxFocusedObject); 281cdf0e10cSrcweir } 282cdf0e10cSrcweir } 283cdf0e10cSrcweir 284cdf0e10cSrcweir 285cdf0e10cSrcweir 286cdf0e10cSrcweir 287cdf0e10cSrcweir /** Print some information about an event that is not handled by any 288cdf0e10cSrcweir more specialized handler. 289cdf0e10cSrcweir */ 290cdf0e10cSrcweir private void handleGenericEvent ( 291cdf0e10cSrcweir int nEventId, 292cdf0e10cSrcweir Object aSource, 293cdf0e10cSrcweir Object aOldValue, 294cdf0e10cSrcweir Object aNewValue) 295cdf0e10cSrcweir { 296cdf0e10cSrcweir // Print event to message area. 297cdf0e10cSrcweir MessageArea.print ("received event " 298cdf0e10cSrcweir + NameProvider.getEventName (nEventId) + " from "); 299cdf0e10cSrcweir XAccessibleContext xContext = objectToContext (aSource); 300cdf0e10cSrcweir if (xContext != null) 301cdf0e10cSrcweir MessageArea.print (xContext.getAccessibleName()); 302cdf0e10cSrcweir else 303cdf0e10cSrcweir MessageArea.print ("null"); 304cdf0e10cSrcweir MessageArea.println (" / " 305cdf0e10cSrcweir + NameProvider.getRoleName(xContext.getAccessibleRole())); 306cdf0e10cSrcweir } 307cdf0e10cSrcweir 308cdf0e10cSrcweir 309cdf0e10cSrcweir 310cdf0e10cSrcweir /** This is the main method for handling accessibility events. It is 311cdf0e10cSrcweir assumed that it is not called directly from the Office but from a 312cdf0e10cSrcweir listener proxy that runs in a separate thread so that calls back to 313cdf0e10cSrcweir the Office do not result in dead-locks. 314cdf0e10cSrcweir */ 315cdf0e10cSrcweir public void notifyEvent (com.sun.star.accessibility.AccessibleEventObject aEvent) 316cdf0e10cSrcweir { 317cdf0e10cSrcweir try // Guard against disposed objects. 318cdf0e10cSrcweir { 319cdf0e10cSrcweir switch (aEvent.EventId) 320cdf0e10cSrcweir { 321cdf0e10cSrcweir case AccessibleEventId.CHILD: 322cdf0e10cSrcweir handleChildEvent ( 323cdf0e10cSrcweir objectToContext (aEvent.OldValue), 324cdf0e10cSrcweir objectToContext (aEvent.NewValue)); 325cdf0e10cSrcweir break; 326cdf0e10cSrcweir 327cdf0e10cSrcweir case AccessibleEventId.STATE_CHANGED: 328cdf0e10cSrcweir { 329cdf0e10cSrcweir short nOldState = -1; 330cdf0e10cSrcweir short nNewState = -1; 331cdf0e10cSrcweir try 332cdf0e10cSrcweir { 333cdf0e10cSrcweir if (AnyConverter.isShort (aEvent.NewValue)) 334cdf0e10cSrcweir nNewState = AnyConverter.toShort (aEvent.NewValue); 335cdf0e10cSrcweir if (AnyConverter.isShort (aEvent.OldValue)) 336cdf0e10cSrcweir nOldState = AnyConverter.toShort (aEvent.OldValue); 337cdf0e10cSrcweir } 338cdf0e10cSrcweir catch (com.sun.star.lang.IllegalArgumentException e) 339cdf0e10cSrcweir {} 340cdf0e10cSrcweir handleStateChange ( 341cdf0e10cSrcweir objectToContext (aEvent.Source), 342cdf0e10cSrcweir nOldState, 343cdf0e10cSrcweir nNewState); 344cdf0e10cSrcweir } 345cdf0e10cSrcweir break; 346cdf0e10cSrcweir 347cdf0e10cSrcweir case AccessibleEventId.VISIBLE_DATA_CHANGED: 348cdf0e10cSrcweir case AccessibleEventId.BOUNDRECT_CHANGED: 349cdf0e10cSrcweir handleVisibleDataEvent (objectToContext (aEvent.Source)); 350cdf0e10cSrcweir break; 351cdf0e10cSrcweir 352cdf0e10cSrcweir case AccessibleEventId.CARET_CHANGED: 353cdf0e10cSrcweir try 354cdf0e10cSrcweir { 355cdf0e10cSrcweir handleCaretEvent ( 356cdf0e10cSrcweir objectToContext (aEvent.Source), 357cdf0e10cSrcweir AnyConverter.toLong(aEvent.OldValue), 358cdf0e10cSrcweir AnyConverter.toLong(aEvent.NewValue)); 359cdf0e10cSrcweir } 360cdf0e10cSrcweir catch (com.sun.star.lang.IllegalArgumentException e) 361cdf0e10cSrcweir {} 362cdf0e10cSrcweir break; 363cdf0e10cSrcweir 364cdf0e10cSrcweir default: 365cdf0e10cSrcweir handleGenericEvent (aEvent.EventId, 366cdf0e10cSrcweir aEvent.Source, aEvent.OldValue, aEvent.NewValue); 367cdf0e10cSrcweir break; 368cdf0e10cSrcweir } 369cdf0e10cSrcweir } 370cdf0e10cSrcweir catch (com.sun.star.lang.DisposedException e) 371cdf0e10cSrcweir {} 372cdf0e10cSrcweir } 373cdf0e10cSrcweir 374cdf0e10cSrcweir 375cdf0e10cSrcweir 376cdf0e10cSrcweir 377cdf0e10cSrcweir /** Convert the given object into an accessible context. The object is 378cdf0e10cSrcweir interpreted as UNO Any and may contain either an XAccessible or 379cdf0e10cSrcweir XAccessibleContext reference. 380cdf0e10cSrcweir @return 381cdf0e10cSrcweir The returned value is null when the given object can not be 382cdf0e10cSrcweir converted to an XAccessibleContext reference. 383cdf0e10cSrcweir */ 384cdf0e10cSrcweir private XAccessibleContext objectToContext (Object aObject) 385cdf0e10cSrcweir { 386cdf0e10cSrcweir XAccessibleContext xContext = null; 387cdf0e10cSrcweir XAccessible xAccessible = null; 388cdf0e10cSrcweir try 389cdf0e10cSrcweir { 390cdf0e10cSrcweir xAccessible = (XAccessible)AnyConverter.toObject( 391cdf0e10cSrcweir new Type(XAccessible.class), aObject); 392cdf0e10cSrcweir } 393cdf0e10cSrcweir catch (com.sun.star.lang.IllegalArgumentException e) 394cdf0e10cSrcweir {} 395cdf0e10cSrcweir if (xAccessible != null) 396cdf0e10cSrcweir xContext = xAccessible.getAccessibleContext(); 397cdf0e10cSrcweir else 398cdf0e10cSrcweir try 399cdf0e10cSrcweir { 400cdf0e10cSrcweir xContext = (XAccessibleContext)AnyConverter.toObject( 401cdf0e10cSrcweir new Type(XAccessibleContext.class), aObject); 402cdf0e10cSrcweir } 403cdf0e10cSrcweir catch (com.sun.star.lang.IllegalArgumentException e) 404cdf0e10cSrcweir {} 405cdf0e10cSrcweir return xContext; 406cdf0e10cSrcweir } 407cdf0e10cSrcweir 408cdf0e10cSrcweir 409cdf0e10cSrcweir 410cdf0e10cSrcweir 411cdf0e10cSrcweir /** The proxy that runs in a seperate thread and allows to call back to 412cdf0e10cSrcweir the Office without running into dead-locks. 413cdf0e10cSrcweir */ 414cdf0e10cSrcweir private EventListenerProxy maListenerProxy; 415cdf0e10cSrcweir 416cdf0e10cSrcweir /** The currently focused object. A value of null means that no object 417cdf0e10cSrcweir has the focus. 418cdf0e10cSrcweir */ 419cdf0e10cSrcweir private XAccessibleContext mxFocusedObject; 420cdf0e10cSrcweir 421cdf0e10cSrcweir /** Keep track of the currently open top windows to start a registration 422cdf0e10cSrcweir loop when the last window (and the Office) is closed. 423cdf0e10cSrcweir */ 424cdf0e10cSrcweir private long mnTopWindowCount; 425cdf0e10cSrcweir 426cdf0e10cSrcweir /** A list of objects that can display accessible objects in specific 427cdf0e10cSrcweir ways such as showing a graphical representation or some textual 428cdf0e10cSrcweir descriptions. 429cdf0e10cSrcweir */ 430cdf0e10cSrcweir private Vector maObjectDisplays; 431cdf0e10cSrcweir 432cdf0e10cSrcweir /** The timer task that attempts in regular intervals to connect to a 433cdf0e10cSrcweir running Office application. 434cdf0e10cSrcweir */ 435cdf0e10cSrcweir private ConnectionTask maConnectionTask; 436cdf0e10cSrcweir } 437