Update ooo320-m1
[ooovba.git] / vcl / unx / gtk / a11y / atklistener.cxx
blobf083e7cc584ec036bea72ec3eb7af52fb1daef6a
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: atklistener.cxx,v $
10 * $Revision: 1.9 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_vcl.hxx"
34 #include <com/sun/star/accessibility/TextSegment.hpp>
35 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
36 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
37 #include <com/sun/star/accessibility/AccessibleTableModelChange.hpp>
38 #include <com/sun/star/accessibility/AccessibleTableModelChangeType.hpp>
39 #include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp>
41 #include "atklistener.hxx"
42 #include "atkwrapper.hxx"
44 #include <rtl/ref.hxx>
45 #include <stdio.h>
47 using namespace com::sun::star;
50 #define CSTRING_FROM_ANY(i) rtl::OUStringToOString( i.get< rtl::OUString >(), RTL_TEXTENCODING_UTF8 ).getStr()
52 AtkListener::AtkListener( AtkObjectWrapper* pWrapper ) : mpWrapper( pWrapper )
54 if( mpWrapper )
56 g_object_ref( mpWrapper );
57 updateChildList( mpWrapper->mpContext );
61 AtkListener::~AtkListener()
63 if( mpWrapper )
64 g_object_unref( mpWrapper );
67 /*****************************************************************************/
69 AtkStateType mapState( const uno::Any &rAny )
71 sal_Int16 nState = accessibility::AccessibleStateType::INVALID;
72 rAny >>= nState;
73 return mapAtkState( nState );
76 /*****************************************************************************/
78 // XEventListener implementation
79 void AtkListener::disposing( const lang::EventObject& ) throw (uno::RuntimeException)
81 if( mpWrapper )
83 AtkObject *atk_obj = ATK_OBJECT( mpWrapper );
85 // Release all interface references to avoid shutdown problems with
86 // global mutex
87 atk_object_wrapper_dispose( mpWrapper );
89 // This is an equivalent to a state change to DEFUNC(T).
90 atk_object_notify_state_change( atk_obj, ATK_STATE_DEFUNCT, TRUE );
92 if( atk_get_focus_object() == atk_obj )
93 atk_focus_tracker_notify( NULL );
95 // Release the wrapper object so that it can vanish ..
96 g_object_unref( mpWrapper );
97 mpWrapper = NULL;
101 /*****************************************************************************/
103 static AtkObject *getObjFromAny( const uno::Any &rAny )
105 uno::Reference< accessibility::XAccessible > xAccessible;
106 rAny >>= xAccessible;
107 return xAccessible.is() ? atk_object_wrapper_ref( xAccessible ) : NULL;
110 /*****************************************************************************/
112 // Updates the child list held to provide the old IndexInParent on children_changed::remove
113 void AtkListener::updateChildList(accessibility::XAccessibleContext* pContext)
115 m_aChildList.clear();
117 uno::Reference< accessibility::XAccessibleStateSet > xStateSet = pContext->getAccessibleStateSet();
118 if( xStateSet.is()
119 && !xStateSet->contains(accessibility::AccessibleStateType::DEFUNC)
120 && !xStateSet->contains(accessibility::AccessibleStateType::MANAGES_DESCENDANTS) )
122 sal_Int32 nChildren = pContext->getAccessibleChildCount();
123 m_aChildList.resize(nChildren);
124 for(sal_Int32 n = 0; n < nChildren; n++)
126 m_aChildList[n] = pContext->getAccessibleChild(n);
127 OSL_ASSERT(m_aChildList[n].is());
132 /*****************************************************************************/
134 void AtkListener::handleChildAdded(
135 const uno::Reference< accessibility::XAccessibleContext >& rxParent,
136 const uno::Reference< accessibility::XAccessible>& rxAccessible)
138 AtkObject * pChild = atk_object_wrapper_ref( rxAccessible );
140 if( pChild )
142 updateChildList(rxParent.get());
144 atk_object_wrapper_add_child( mpWrapper, pChild,
145 atk_object_get_index_in_parent( pChild ));
147 g_object_unref( pChild );
151 /*****************************************************************************/
153 void AtkListener::handleChildRemoved(
154 const uno::Reference< accessibility::XAccessibleContext >& rxParent,
155 const uno::Reference< accessibility::XAccessible>& rxChild)
157 sal_Int32 nIndex = -1;
159 // Locate the child in the children list
160 size_t n, nmax = m_aChildList.size();
161 for( n = 0; n < nmax; ++n )
163 if( rxChild == m_aChildList[n] )
165 nIndex = n;
166 break;
170 // FIXME: two problems here:
171 // a) we get child-removed events for objects that are no real childs
172 // in the accessibility hierarchy or have been removed before due to
173 // some child removing batch.
174 // b) spi_atk_bridge_signal_listener ignores the given parameters
175 // for children_changed events and always asks the parent for the
176 // 0. child, which breaks somehow on vanishing list boxes.
177 // Ignoring "remove" events for objects not in the m_aChildList
178 // for now.
179 if( nIndex >= 0 )
181 updateChildList(rxParent.get());
183 AtkObject * pChild = atk_object_wrapper_ref( rxChild, false );
184 if( pChild )
186 atk_object_wrapper_remove_child( mpWrapper, pChild, nIndex );
187 g_object_unref( pChild );
192 /*****************************************************************************/
194 void AtkListener::handleInvalidateChildren(
195 const uno::Reference< accessibility::XAccessibleContext >& rxParent)
197 // Send notifications for all previous children
198 size_t n = m_aChildList.size();
199 while( n-- > 0 )
201 if( m_aChildList[n].is() )
203 AtkObject * pChild = atk_object_wrapper_ref( m_aChildList[n], false );
204 if( pChild )
206 atk_object_wrapper_remove_child( mpWrapper, pChild, n );
207 g_object_unref( pChild );
212 updateChildList(rxParent.get());
214 // Send notifications for all new children
215 size_t nmax = m_aChildList.size();
216 for( n = 0; n < nmax; ++n )
218 if( m_aChildList[n].is() )
220 AtkObject * pChild = atk_object_wrapper_ref( m_aChildList[n] );
222 if( pChild )
224 atk_object_wrapper_add_child( mpWrapper, pChild, n );
225 g_object_unref( pChild );
231 /*****************************************************************************/
233 static uno::Reference< accessibility::XAccessibleContext >
234 getAccessibleContextFromSource( const uno::Reference< uno::XInterface >& rxSource )
236 uno::Reference< accessibility::XAccessibleContext > xContext(rxSource, uno::UNO_QUERY);
237 if( ! xContext.is() )
239 g_warning( "ERROR: Event source does not implement XAccessibleContext" );
241 // Second try - query for XAccessible, which should give us access to
242 // XAccessibleContext.
243 uno::Reference< accessibility::XAccessible > xAccessible(rxSource, uno::UNO_QUERY);
244 if( xAccessible.is() )
245 xContext = xAccessible->getAccessibleContext();
248 return xContext;
251 /*****************************************************************************/
253 // XAccessibleEventListener
254 void AtkListener::notifyEvent( const accessibility::AccessibleEventObject& aEvent ) throw( uno::RuntimeException )
256 if( !mpWrapper )
257 return;
259 AtkObject *atk_obj = ATK_OBJECT( mpWrapper );
261 switch( aEvent.EventId )
263 // AtkObject signals:
264 // Hierarchy signals
265 case accessibility::AccessibleEventId::CHILD:
267 uno::Reference< accessibility::XAccessibleContext > xParent;
268 uno::Reference< accessibility::XAccessible > xChild;
270 xParent = getAccessibleContextFromSource(aEvent.Source);
271 g_return_if_fail( xParent.is() );
273 if( aEvent.OldValue >>= xChild )
274 handleChildRemoved(xParent, xChild);
276 if( aEvent.NewValue >>= xChild )
277 handleChildAdded(xParent, xChild);
279 break;
281 case accessibility::AccessibleEventId::INVALIDATE_ALL_CHILDREN:
283 uno::Reference< accessibility::XAccessibleContext > xParent;
285 xParent = getAccessibleContextFromSource(aEvent.Source);
286 g_return_if_fail( xParent.is() );
288 handleInvalidateChildren(xParent);
290 break;
292 case accessibility::AccessibleEventId::NAME_CHANGED:
294 rtl::OUString aName;
295 if( aEvent.NewValue >>= aName )
297 atk_object_set_name(atk_obj,
298 rtl::OUStringToOString(aName, RTL_TEXTENCODING_UTF8).getStr());
301 break;
303 case accessibility::AccessibleEventId::DESCRIPTION_CHANGED:
305 rtl::OUString aDescription;
306 if( aEvent.NewValue >>= aDescription )
308 atk_object_set_description(atk_obj,
309 rtl::OUStringToOString(aDescription, RTL_TEXTENCODING_UTF8).getStr());
312 break;
314 case accessibility::AccessibleEventId::STATE_CHANGED:
316 AtkStateType eOldState = mapState( aEvent.OldValue );
317 AtkStateType eNewState = mapState( aEvent.NewValue );
319 gboolean bState = eNewState != ATK_STATE_INVALID;
320 AtkStateType eRealState = bState ? eNewState : eOldState;
322 atk_object_notify_state_change( atk_obj, eRealState, bState );
323 break;
326 case accessibility::AccessibleEventId::BOUNDRECT_CHANGED:
328 #ifdef HAS_ATKRECTANGLE
329 if( ATK_IS_COMPONENT( atk_obj ) )
331 AtkRectangle rect;
333 atk_component_get_extents( ATK_COMPONENT( atk_obj ),
334 &rect.x,
335 &rect.y,
336 &rect.width,
337 &rect.height,
338 ATK_XY_SCREEN );
340 g_signal_emit_by_name( atk_obj, "bounds_changed", &rect );
342 else
343 g_warning( "bounds_changed event for object not implementing AtkComponent\n");
344 #endif
346 break;
348 case accessibility::AccessibleEventId::VISIBLE_DATA_CHANGED:
349 g_signal_emit_by_name( atk_obj, "visible-data-changed" );
350 break;
352 case accessibility::AccessibleEventId::ACTIVE_DESCENDANT_CHANGED:
354 AtkObject *pChild = getObjFromAny( aEvent.NewValue );
355 if( pChild )
357 g_signal_emit_by_name( atk_obj, "active-descendant-changed", pChild );
358 g_object_unref( pChild );
360 break;
363 // --> OD 2009-05-26 #i92103#
364 case accessibility::AccessibleEventId::LISTBOX_ENTRY_EXPANDED:
366 AtkObject *pChild = getObjFromAny( aEvent.NewValue );
367 if( pChild )
369 AtkStateType eExpandedState = ATK_STATE_EXPANDED;
370 atk_object_notify_state_change( pChild, eExpandedState, true );
371 g_object_unref( pChild );
373 break;
376 case accessibility::AccessibleEventId::LISTBOX_ENTRY_COLLAPSED:
378 AtkObject *pChild = getObjFromAny( aEvent.NewValue );
379 if( pChild )
381 AtkStateType eExpandedState = ATK_STATE_EXPANDED;
382 atk_object_notify_state_change( pChild, eExpandedState, false );
383 g_object_unref( pChild );
385 break;
387 // <--
389 // AtkAction signals ...
390 case accessibility::AccessibleEventId::ACTION_CHANGED:
391 g_signal_emit_by_name( G_OBJECT( atk_obj ), "property_change::accessible-actions");
392 break;
394 // AtkText
395 case accessibility::AccessibleEventId::CARET_CHANGED:
397 sal_Int32 nPos=0;
398 aEvent.NewValue >>= nPos;
399 g_signal_emit_by_name( atk_obj, "text_caret_moved", nPos );
400 break;
402 case accessibility::AccessibleEventId::TEXT_CHANGED:
404 // TESTME: and remove this comment:
405 // cf. comphelper/source/misc/accessibletexthelper.cxx (implInitTextChangedEvent)
406 accessibility::TextSegment aDeletedText;
407 accessibility::TextSegment aInsertedText;
409 // TODO: when GNOME starts to send "update" kind of events, change
410 // we need to re-think this implementation as well
411 if( aEvent.OldValue >>= aDeletedText )
413 /* Remember the text segment here to be able to return removed text in get_text().
414 * This is clearly a hack to be used until appropriate API exists in atk to pass
415 * the string value directly or we find a compelling reason to start caching the
416 * UTF-8 converted strings in the atk wrapper object.
419 g_object_set_data( G_OBJECT(atk_obj), "ooo::text_changed::delete", &aDeletedText);
421 g_signal_emit_by_name( atk_obj, "text_changed::delete",
422 (gint) aDeletedText.SegmentStart,
423 (gint)( aDeletedText.SegmentEnd - aDeletedText.SegmentStart ) );
425 g_object_steal_data( G_OBJECT(atk_obj), "ooo::text_changed::delete" );
428 if( aEvent.NewValue >>= aInsertedText )
429 g_signal_emit_by_name( atk_obj, "text_changed::insert",
430 (gint) aInsertedText.SegmentStart,
431 (gint)( aInsertedText.SegmentEnd - aInsertedText.SegmentStart ) );
432 break;
435 case accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED:
437 g_signal_emit_by_name( atk_obj, "text-selection-changed" );
438 break;
441 case accessibility::AccessibleEventId::TEXT_ATTRIBUTE_CHANGED:
442 g_signal_emit_by_name( atk_obj, "text-attributes-changed" );
443 break;
445 // AtkValue
446 case accessibility::AccessibleEventId::VALUE_CHANGED:
447 g_object_notify( G_OBJECT( atk_obj ), "accessible-value" );
448 break;
450 case accessibility::AccessibleEventId::CONTENT_FLOWS_FROM_RELATION_CHANGED:
451 case accessibility::AccessibleEventId::CONTENT_FLOWS_TO_RELATION_CHANGED:
452 case accessibility::AccessibleEventId::CONTROLLED_BY_RELATION_CHANGED:
453 case accessibility::AccessibleEventId::CONTROLLER_FOR_RELATION_CHANGED:
454 case accessibility::AccessibleEventId::LABEL_FOR_RELATION_CHANGED:
455 case accessibility::AccessibleEventId::LABELED_BY_RELATION_CHANGED:
456 case accessibility::AccessibleEventId::MEMBER_OF_RELATION_CHANGED:
457 case accessibility::AccessibleEventId::SUB_WINDOW_OF_RELATION_CHANGED:
458 // FIXME: ask Bill how Atk copes with this little lot ...
459 break;
461 // AtkTable
462 case accessibility::AccessibleEventId::TABLE_MODEL_CHANGED:
464 accessibility::AccessibleTableModelChange aChange;
465 aEvent.NewValue >>= aChange;
467 sal_Int32 nRowsChanged = aChange.LastRow - aChange.FirstRow + 1;
468 sal_Int32 nColumnsChanged = aChange.LastColumn - aChange.FirstColumn + 1;
470 static const struct {
471 const char *row;
472 const char *col;
473 } aSignalNames[] =
475 { NULL, NULL }, // dummy
476 { "row_inserted", "column_inserted" }, // INSERT = 1
477 { "row_deleted", "column_deleted" } // DELETE = 2
479 switch( aChange.Type )
481 case accessibility::AccessibleTableModelChangeType::INSERT:
482 case accessibility::AccessibleTableModelChangeType::DELETE:
483 if( nRowsChanged > 0 )
484 g_signal_emit_by_name( G_OBJECT( atk_obj ),
485 aSignalNames[aChange.Type].row,
486 aChange.FirstRow, nRowsChanged );
487 if( nColumnsChanged > 0 )
488 g_signal_emit_by_name( G_OBJECT( atk_obj ),
489 aSignalNames[aChange.Type].col,
490 aChange.FirstColumn, nColumnsChanged );
491 break;
493 case accessibility::AccessibleTableModelChangeType::UPDATE:
494 // This is not really a model change, is it ?
495 break;
496 default:
497 g_warning( "TESTME: unusual table model change %d\n", aChange.Type );
498 break;
500 g_signal_emit_by_name( G_OBJECT( atk_obj ), "model-changed" );
501 break;
504 case accessibility::AccessibleEventId::TABLE_COLUMN_HEADER_CHANGED:
505 g_signal_emit_by_name( G_OBJECT( atk_obj ), "property_change::accessible-table-column-header");
506 break;
508 case accessibility::AccessibleEventId::TABLE_CAPTION_CHANGED:
509 g_signal_emit_by_name( G_OBJECT( atk_obj ), "property_change::accessible-table-caption");
510 break;
512 case accessibility::AccessibleEventId::TABLE_COLUMN_DESCRIPTION_CHANGED:
513 g_signal_emit_by_name( G_OBJECT( atk_obj ), "property_change::accessible-table-column-description");
514 break;
516 case accessibility::AccessibleEventId::TABLE_ROW_DESCRIPTION_CHANGED:
517 g_signal_emit_by_name( G_OBJECT( atk_obj ), "property_change::accessible-table-row-description");
518 break;
520 case accessibility::AccessibleEventId::TABLE_ROW_HEADER_CHANGED:
521 g_signal_emit_by_name( G_OBJECT( atk_obj ), "property_change::accessible-table-row-header");
522 break;
524 case accessibility::AccessibleEventId::TABLE_SUMMARY_CHANGED:
525 g_signal_emit_by_name( G_OBJECT( atk_obj ), "property_change::accessible-table-summary");
526 break;
528 case accessibility::AccessibleEventId::SELECTION_CHANGED:
529 g_signal_emit_by_name( G_OBJECT( atk_obj ), "selection_changed");
530 break;
532 case accessibility::AccessibleEventId::HYPERTEXT_CHANGED:
533 g_signal_emit_by_name( G_OBJECT( atk_obj ), "property_change::accessible-hypertext-offset");
534 break;
536 default:
537 g_warning( "Unknown event notification %d", aEvent.EventId );
538 break;