| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| import java.util.Vector; |
| |
| import com.sun.star.uno.AnyConverter; |
| import com.sun.star.uno.UnoRuntime; |
| import com.sun.star.uno.Type; |
| import com.sun.star.accessibility.*; |
| |
| /** Handle all the events send from accessibility objects. The events |
| denoting new or removed top windows are handled as well. |
| |
| It does not implement any listener interface as does the |
| EventListenerProxy class because it is interested only in a sub set of |
| the event types. |
| */ |
| public class EventHandler |
| { |
| public EventHandler () |
| { |
| mnTopWindowCount = 0; |
| maListenerProxy = new EventListenerProxy (this); |
| maConnectionTask = new ConnectionTask (maListenerProxy); |
| maObjectDisplays = new Vector (); |
| } |
| |
| public synchronized void addObjectDisplay (IAccessibleObjectDisplay aDisplay) |
| { |
| maObjectDisplays.add (aDisplay); |
| } |
| |
| |
| public void finalize () |
| { |
| // When it is running then cancel the timer that tries to connect to |
| // the Office. |
| if (maConnectionTask != null) |
| maConnectionTask.cancel(); |
| } |
| |
| |
| |
| public void disposing (com.sun.star.lang.EventObject aEvent) |
| { |
| // Ignored: We are not holding references to accessibility objects. |
| } |
| |
| |
| |
| |
| /** This method is called back when a new top level window has been opened. |
| */ |
| public void windowOpened (XAccessible xAccessible) |
| { |
| if (xAccessible != null) |
| { |
| // Update the counter of currently open top level windows |
| // observed by this object. |
| mnTopWindowCount += 1; |
| |
| XAccessibleContext xContext = xAccessible.getAccessibleContext(); |
| if (xContext != null) |
| { |
| MessageArea.println ("new top level window has accessible name " |
| + xContext.getAccessibleName()); |
| |
| // Register at all accessible objects of the new window. |
| new RegistrationThread ( |
| maListenerProxy, |
| xContext, |
| true, |
| true); |
| } |
| else |
| MessageArea.println ("new top level window is not accessible."); |
| } |
| else |
| MessageArea.println ("new top level window is not accessible."); |
| } |
| |
| |
| |
| |
| public void windowClosed (XAccessible xAccessible) |
| { |
| mnTopWindowCount -= 1; |
| MessageArea.println ("window closed, " + mnTopWindowCount + " still open"); |
| if (mnTopWindowCount == 0) |
| { |
| // This was the last window. Wait for a new connection. |
| MessageArea.println ("lost connection to office"); |
| new ConnectionTask (maListenerProxy); |
| } |
| if (xAccessible != null) |
| new RegistrationThread ( |
| maListenerProxy, |
| xAccessible.getAccessibleContext(), |
| false, |
| true); |
| } |
| |
| |
| |
| |
| /** Print a message that the given object just received the focus. Call |
| all accessible object diplays and tell them to update. |
| */ |
| private synchronized void focusGained (XAccessibleContext xContext) |
| { |
| if (xContext != null) |
| { |
| MessageArea.println ("focusGained: " + xContext.getAccessibleName() |
| + " with role " |
| + NameProvider.getRoleName (xContext.getAccessibleRole())); |
| |
| // Tell the object displays to update their views. |
| for (int i=0; i<maObjectDisplays.size(); i++) |
| { |
| IAccessibleObjectDisplay aDisplay = |
| (IAccessibleObjectDisplay)maObjectDisplays.get(i); |
| if (aDisplay != null) |
| aDisplay.setAccessibleObject (xContext); |
| } |
| |
| // Remember the currently focused object. |
| mxFocusedObject = xContext; |
| } |
| else |
| MessageArea.println ("focusGained: null"); |
| } |
| |
| |
| |
| |
| /** Print a message that the given object just lost the focus. Call |
| all accessible object diplays and tell them to update. |
| */ |
| private synchronized void focusLost (XAccessibleContext xContext) |
| { |
| if (xContext != null) |
| { |
| MessageArea.println ("focusLost: " |
| + xContext.getAccessibleName() |
| + " with role " |
| + NameProvider.getRoleName (xContext.getAccessibleRole())); |
| |
| // Tell the object displays to update their views. |
| for (int i=0; i<maObjectDisplays.size(); i++) |
| { |
| IAccessibleObjectDisplay aDisplay = |
| (IAccessibleObjectDisplay)maObjectDisplays.get(i); |
| if (aDisplay != null) |
| aDisplay.setAccessibleObject (null); |
| } |
| mxFocusedObject = null; |
| } |
| else |
| MessageArea.println ("focusLost: null"); |
| } |
| |
| |
| |
| |
| /** Handle a change of the caret position. Ignore this on all objects |
| but the one currently focused. |
| */ |
| private void handleCaretEvent (XAccessibleContext xContext, |
| long nOldPosition, long nNewPosition) |
| { |
| if (xContext == mxFocusedObject) |
| MessageArea.println ("caret moved from " + nOldPosition + " to " + nNewPosition); |
| } |
| |
| |
| |
| |
| /** Print a message that a state has been changed. |
| @param xContext |
| The accessible context of the object whose state has changed. |
| @param nOldState |
| When not zero then this value describes a state that has been reset. |
| @param nNewValue |
| When not zero then this value describes a state that has been set. |
| */ |
| private void handleStateChange (XAccessibleContext xContext, short nOldState, short nNewState) |
| { |
| // Determine which state has changed and what is its new value. |
| short nState; |
| boolean aNewValue; |
| if (nOldState >= 0) |
| { |
| nState = nOldState; |
| aNewValue = false; |
| } |
| else |
| { |
| nState = nNewState; |
| aNewValue = true; |
| } |
| |
| // Print a message about the changed state. |
| MessageArea.print ("setting state " + NameProvider.getStateName(nState) |
| + " to " + aNewValue); |
| if (xContext != null) |
| { |
| MessageArea.println (" at " + xContext.getAccessibleName() + " with role " |
| + NameProvider.getRoleName(xContext.getAccessibleRole())); |
| } |
| else |
| MessageArea.println (" at null"); |
| |
| // Further handling of some states |
| switch (nState) |
| { |
| case AccessibleStateType.FOCUSED: |
| if (aNewValue) |
| focusGained (xContext); |
| else |
| focusLost (xContext); |
| } |
| } |
| |
| |
| |
| |
| /** Handle a child event that describes the creation of removal of a |
| single child. |
| */ |
| private void handleChildEvent ( |
| XAccessibleContext aOldChild, |
| XAccessibleContext aNewChild) |
| { |
| if (aOldChild != null) |
| // Remove event listener from the child and all of its descendants. |
| new RegistrationThread (maListenerProxy, aOldChild, false, false); |
| else if (aNewChild != null) |
| // Add event listener to the new child and all of its descendants. |
| new RegistrationThread (maListenerProxy, aNewChild, true, false); |
| } |
| |
| |
| |
| |
| /** Handle the change of some visible data of an object. |
| */ |
| private void handleVisibleDataEvent (XAccessibleContext xContext) |
| { |
| // The given object may affect the visible appearance of the focused |
| // object even when the two are not identical when the given object |
| // is an ancestor of the focused object. |
| // In order to not check this we simply call an update on the |
| // focused object. |
| if (mxFocusedObject != null) |
| for (int i=0; i<maObjectDisplays.size(); i++) |
| { |
| IAccessibleObjectDisplay aDisplay = |
| (IAccessibleObjectDisplay)maObjectDisplays.get(i); |
| if (aDisplay != null) |
| aDisplay.updateAccessibleObject (mxFocusedObject); |
| } |
| } |
| |
| |
| |
| |
| /** Print some information about an event that is not handled by any |
| more specialized handler. |
| */ |
| private void handleGenericEvent ( |
| int nEventId, |
| Object aSource, |
| Object aOldValue, |
| Object aNewValue) |
| { |
| // Print event to message area. |
| MessageArea.print ("received event " |
| + NameProvider.getEventName (nEventId) + " from "); |
| XAccessibleContext xContext = objectToContext (aSource); |
| if (xContext != null) |
| MessageArea.print (xContext.getAccessibleName()); |
| else |
| MessageArea.print ("null"); |
| MessageArea.println (" / " |
| + NameProvider.getRoleName(xContext.getAccessibleRole())); |
| } |
| |
| |
| |
| /** This is the main method for handling accessibility events. It is |
| assumed that it is not called directly from the Office but from a |
| listener proxy that runs in a separate thread so that calls back to |
| the Office do not result in dead-locks. |
| */ |
| public void notifyEvent (com.sun.star.accessibility.AccessibleEventObject aEvent) |
| { |
| try // Guard against disposed objects. |
| { |
| switch (aEvent.EventId) |
| { |
| case AccessibleEventId.CHILD: |
| handleChildEvent ( |
| objectToContext (aEvent.OldValue), |
| objectToContext (aEvent.NewValue)); |
| break; |
| |
| case AccessibleEventId.STATE_CHANGED: |
| { |
| short nOldState = -1; |
| short nNewState = -1; |
| try |
| { |
| if (AnyConverter.isShort (aEvent.NewValue)) |
| nNewState = AnyConverter.toShort (aEvent.NewValue); |
| if (AnyConverter.isShort (aEvent.OldValue)) |
| nOldState = AnyConverter.toShort (aEvent.OldValue); |
| } |
| catch (com.sun.star.lang.IllegalArgumentException e) |
| {} |
| handleStateChange ( |
| objectToContext (aEvent.Source), |
| nOldState, |
| nNewState); |
| } |
| break; |
| |
| case AccessibleEventId.VISIBLE_DATA_CHANGED: |
| case AccessibleEventId.BOUNDRECT_CHANGED: |
| handleVisibleDataEvent (objectToContext (aEvent.Source)); |
| break; |
| |
| case AccessibleEventId.CARET_CHANGED: |
| try |
| { |
| handleCaretEvent ( |
| objectToContext (aEvent.Source), |
| AnyConverter.toLong(aEvent.OldValue), |
| AnyConverter.toLong(aEvent.NewValue)); |
| } |
| catch (com.sun.star.lang.IllegalArgumentException e) |
| {} |
| break; |
| |
| default: |
| handleGenericEvent (aEvent.EventId, |
| aEvent.Source, aEvent.OldValue, aEvent.NewValue); |
| break; |
| } |
| } |
| catch (com.sun.star.lang.DisposedException e) |
| {} |
| } |
| |
| |
| |
| |
| /** Convert the given object into an accessible context. The object is |
| interpreted as UNO Any and may contain either an XAccessible or |
| XAccessibleContext reference. |
| @return |
| The returned value is null when the given object can not be |
| converted to an XAccessibleContext reference. |
| */ |
| private XAccessibleContext objectToContext (Object aObject) |
| { |
| XAccessibleContext xContext = null; |
| XAccessible xAccessible = null; |
| try |
| { |
| xAccessible = (XAccessible)AnyConverter.toObject( |
| new Type(XAccessible.class), aObject); |
| } |
| catch (com.sun.star.lang.IllegalArgumentException e) |
| {} |
| if (xAccessible != null) |
| xContext = xAccessible.getAccessibleContext(); |
| else |
| try |
| { |
| xContext = (XAccessibleContext)AnyConverter.toObject( |
| new Type(XAccessibleContext.class), aObject); |
| } |
| catch (com.sun.star.lang.IllegalArgumentException e) |
| {} |
| return xContext; |
| } |
| |
| |
| |
| |
| /** The proxy that runs in a separate thread and allows to call back to |
| the Office without running into dead-locks. |
| */ |
| private EventListenerProxy maListenerProxy; |
| |
| /** The currently focused object. A value of null means that no object |
| has the focus. |
| */ |
| private XAccessibleContext mxFocusedObject; |
| |
| /** Keep track of the currently open top windows to start a registration |
| loop when the last window (and the Office) is closed. |
| */ |
| private long mnTopWindowCount; |
| |
| /** A list of objects that can display accessible objects in specific |
| ways such as showing a graphical representation or some textual |
| descriptions. |
| */ |
| private Vector maObjectDisplays; |
| |
| /** The timer task that attempts in regular intervals to connect to a |
| running Office application. |
| */ |
| private ConnectionTask maConnectionTask; |
| } |