1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20 #include <com/sun/star/uno/Any.hxx>
21 #include <com/sun/star/uno/Type.hxx>
22 #include <com/sun/star/uno/Sequence.hxx>
23 #include <com/sun/star/accessibility/AccessibleRole.hpp>
24 #include <com/sun/star/accessibility/AccessibleRelation.hpp>
25 #include <com/sun/star/accessibility/AccessibleRelationType.hpp>
26 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
27 #include <com/sun/star/accessibility/XAccessible.hpp>
28 #include <com/sun/star/accessibility/XAccessibleText.hpp>
29 #include <com/sun/star/accessibility/XAccessibleValue.hpp>
30 #include <com/sun/star/accessibility/XAccessibleAction.hpp>
31 #include <com/sun/star/accessibility/XAccessibleContext.hpp>
32 #include <com/sun/star/accessibility/XAccessibleContext2.hpp>
33 #include <com/sun/star/accessibility/XAccessibleComponent.hpp>
34 #include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp>
35 #include <com/sun/star/accessibility/XAccessibleStateSet.hpp>
36 #include <com/sun/star/accessibility/XAccessibleRelationSet.hpp>
37 #include <com/sun/star/accessibility/XAccessibleTable.hpp>
38 #include <com/sun/star/accessibility/XAccessibleEditableText.hpp>
39 #include <com/sun/star/accessibility/XAccessibleExtendedAttributes.hpp>
40 #include <com/sun/star/accessibility/XAccessibleImage.hpp>
41 #include <com/sun/star/accessibility/XAccessibleHypertext.hpp>
42 #include <com/sun/star/accessibility/XAccessibleSelection.hpp>
43 #include <com/sun/star/awt/XWindow.hpp>
44
45 #include <rtl/strbuf.hxx>
46 #include <osl/diagnose.h>
47 #include <tools/diagnose_ex.h>
48 #include <vcl/syschild.hxx>
49 #include <vcl/sysdata.hxx>
50 #include <vcl/toolkit/unowrap.hxx>
51
52 #include "atkwrapper.hxx"
53 #include "atkregistry.hxx"
54 #include "atklistener.hxx"
55 #include "atktextattributes.hxx"
56
57 #include <vector>
58 #include <dlfcn.h>
59
60 using namespace ::com::sun::star;
61
62 static GObjectClass *parent_class = nullptr;
63
mapRelationType(sal_Int16 nRelation)64 static AtkRelationType mapRelationType( sal_Int16 nRelation )
65 {
66 AtkRelationType type = ATK_RELATION_NULL;
67
68 switch( nRelation )
69 {
70 case accessibility::AccessibleRelationType::CONTENT_FLOWS_FROM:
71 type = ATK_RELATION_FLOWS_FROM;
72 break;
73
74 case accessibility::AccessibleRelationType::CONTENT_FLOWS_TO:
75 type = ATK_RELATION_FLOWS_TO;
76 break;
77
78 case accessibility::AccessibleRelationType::CONTROLLED_BY:
79 type = ATK_RELATION_CONTROLLED_BY;
80 break;
81
82 case accessibility::AccessibleRelationType::CONTROLLER_FOR:
83 type = ATK_RELATION_CONTROLLER_FOR;
84 break;
85
86 case accessibility::AccessibleRelationType::LABEL_FOR:
87 type = ATK_RELATION_LABEL_FOR;
88 break;
89
90 case accessibility::AccessibleRelationType::LABELED_BY:
91 type = ATK_RELATION_LABELLED_BY;
92 break;
93
94 case accessibility::AccessibleRelationType::MEMBER_OF:
95 type = ATK_RELATION_MEMBER_OF;
96 break;
97
98 case accessibility::AccessibleRelationType::SUB_WINDOW_OF:
99 type = ATK_RELATION_SUBWINDOW_OF;
100 break;
101
102 case accessibility::AccessibleRelationType::NODE_CHILD_OF:
103 type = ATK_RELATION_NODE_CHILD_OF;
104 break;
105
106 default:
107 break;
108 }
109
110 return type;
111 }
112
mapAtkState(sal_Int16 nState)113 AtkStateType mapAtkState( sal_Int16 nState )
114 {
115 AtkStateType type = ATK_STATE_INVALID;
116
117 // A perfect / complete mapping ...
118 switch( nState )
119 {
120 #define MAP_DIRECT( a ) \
121 case accessibility::AccessibleStateType::a: \
122 type = ATK_STATE_##a; break
123
124 MAP_DIRECT( INVALID );
125 MAP_DIRECT( ACTIVE );
126 MAP_DIRECT( ARMED );
127 MAP_DIRECT( BUSY );
128 MAP_DIRECT( CHECKED );
129 MAP_DIRECT( EDITABLE );
130 MAP_DIRECT( ENABLED );
131 MAP_DIRECT( EXPANDABLE );
132 MAP_DIRECT( EXPANDED );
133 MAP_DIRECT( FOCUSABLE );
134 MAP_DIRECT( FOCUSED );
135 MAP_DIRECT( HORIZONTAL );
136 MAP_DIRECT( ICONIFIED );
137 MAP_DIRECT( INDETERMINATE );
138 MAP_DIRECT( MANAGES_DESCENDANTS );
139 MAP_DIRECT( MODAL );
140 MAP_DIRECT( MULTI_LINE );
141 MAP_DIRECT( OPAQUE );
142 MAP_DIRECT( PRESSED );
143 MAP_DIRECT( RESIZABLE );
144 MAP_DIRECT( SELECTABLE );
145 MAP_DIRECT( SELECTED );
146 MAP_DIRECT( SENSITIVE );
147 MAP_DIRECT( SHOWING );
148 MAP_DIRECT( SINGLE_LINE );
149 MAP_DIRECT( STALE );
150 MAP_DIRECT( TRANSIENT );
151 MAP_DIRECT( VERTICAL );
152 MAP_DIRECT( VISIBLE );
153 MAP_DIRECT( DEFAULT );
154 // a spelling error ...
155 case accessibility::AccessibleStateType::DEFUNC:
156 type = ATK_STATE_DEFUNCT; break;
157 case accessibility::AccessibleStateType::MULTI_SELECTABLE:
158 type = ATK_STATE_MULTISELECTABLE; break;
159 default:
160 //Mis-use ATK_STATE_LAST_DEFINED to check if a state is unmapped
161 //NOTE! Do not report it
162 type = ATK_STATE_LAST_DEFINED;
163 break;
164 }
165
166 return type;
167 }
168
getRoleForName(const gchar * name)169 static AtkRole getRoleForName( const gchar * name )
170 {
171 AtkRole ret = atk_role_for_name( name );
172 if( ATK_ROLE_INVALID == ret )
173 {
174 // this should only happen in old ATK versions
175 SAL_WNODEPRECATED_DECLARATIONS_PUSH
176 ret = atk_role_register( name );
177 SAL_WNODEPRECATED_DECLARATIONS_POP
178 }
179
180 return ret;
181 }
182
mapToAtkRole(sal_Int16 nRole)183 static AtkRole mapToAtkRole( sal_Int16 nRole )
184 {
185 AtkRole role = ATK_ROLE_UNKNOWN;
186
187 static AtkRole roleMap[] = {
188 ATK_ROLE_UNKNOWN,
189 ATK_ROLE_ALERT,
190 ATK_ROLE_COLUMN_HEADER,
191 ATK_ROLE_CANVAS,
192 ATK_ROLE_CHECK_BOX,
193 ATK_ROLE_CHECK_MENU_ITEM,
194 ATK_ROLE_COLOR_CHOOSER,
195 ATK_ROLE_COMBO_BOX,
196 ATK_ROLE_DATE_EDITOR,
197 ATK_ROLE_DESKTOP_ICON,
198 ATK_ROLE_DESKTOP_FRAME, // ? pane
199 ATK_ROLE_DIRECTORY_PANE,
200 ATK_ROLE_DIALOG,
201 ATK_ROLE_UNKNOWN, // DOCUMENT - registered below
202 ATK_ROLE_UNKNOWN, // EMBEDDED_OBJECT - registered below
203 ATK_ROLE_UNKNOWN, // END_NOTE - registered below
204 ATK_ROLE_FILE_CHOOSER,
205 ATK_ROLE_FILLER,
206 ATK_ROLE_FONT_CHOOSER,
207 ATK_ROLE_FOOTER,
208 ATK_ROLE_UNKNOWN, // FOOTNOTE - registered below
209 ATK_ROLE_FRAME,
210 ATK_ROLE_GLASS_PANE,
211 ATK_ROLE_IMAGE, // GRAPHIC
212 ATK_ROLE_UNKNOWN, // GROUP_BOX - registered below
213 ATK_ROLE_HEADER,
214 ATK_ROLE_HEADING,
215 ATK_ROLE_TEXT, // HYPER_LINK - registered below
216 ATK_ROLE_ICON,
217 ATK_ROLE_INTERNAL_FRAME,
218 ATK_ROLE_LABEL,
219 ATK_ROLE_LAYERED_PANE,
220 ATK_ROLE_LIST,
221 ATK_ROLE_LIST_ITEM,
222 ATK_ROLE_MENU,
223 ATK_ROLE_MENU_BAR,
224 ATK_ROLE_MENU_ITEM,
225 ATK_ROLE_OPTION_PANE,
226 ATK_ROLE_PAGE_TAB,
227 ATK_ROLE_PAGE_TAB_LIST,
228 ATK_ROLE_PANEL,
229 ATK_ROLE_PARAGRAPH,
230 ATK_ROLE_PASSWORD_TEXT,
231 ATK_ROLE_POPUP_MENU,
232 ATK_ROLE_PUSH_BUTTON,
233 ATK_ROLE_PROGRESS_BAR,
234 ATK_ROLE_RADIO_BUTTON,
235 ATK_ROLE_RADIO_MENU_ITEM,
236 ATK_ROLE_ROW_HEADER,
237 ATK_ROLE_ROOT_PANE,
238 ATK_ROLE_SCROLL_BAR,
239 ATK_ROLE_SCROLL_PANE,
240 ATK_ROLE_PANEL, // SHAPE
241 ATK_ROLE_SEPARATOR,
242 ATK_ROLE_SLIDER,
243 ATK_ROLE_SPIN_BUTTON, // SPIN_BOX ?
244 ATK_ROLE_SPLIT_PANE,
245 ATK_ROLE_STATUSBAR,
246 ATK_ROLE_TABLE,
247 ATK_ROLE_TABLE_CELL,
248 ATK_ROLE_TEXT,
249 ATK_ROLE_PANEL, // TEXT_FRAME
250 ATK_ROLE_TOGGLE_BUTTON,
251 ATK_ROLE_TOOL_BAR,
252 ATK_ROLE_TOOL_TIP,
253 ATK_ROLE_TREE,
254 ATK_ROLE_VIEWPORT,
255 ATK_ROLE_WINDOW,
256 ATK_ROLE_PUSH_BUTTON, // BUTTON_DROPDOWN
257 ATK_ROLE_PUSH_BUTTON, // BUTTON_MENU
258 ATK_ROLE_UNKNOWN, // CAPTION - registered below
259 ATK_ROLE_UNKNOWN, // CHART - registered below
260 ATK_ROLE_UNKNOWN, // EDIT_BAR - registered below
261 ATK_ROLE_UNKNOWN, // FORM - registered below
262 ATK_ROLE_UNKNOWN, // IMAGE_MAP - registered below
263 ATK_ROLE_UNKNOWN, // NOTE - registered below
264 ATK_ROLE_UNKNOWN, // PAGE - registered below
265 ATK_ROLE_RULER,
266 ATK_ROLE_UNKNOWN, // SECTION - registered below
267 ATK_ROLE_UNKNOWN, // TREE_ITEM - registered below
268 ATK_ROLE_TREE_TABLE,
269 ATK_ROLE_SCROLL_PANE, // COMMENT - mapped to atk_role_scroll_pane
270 ATK_ROLE_UNKNOWN // COMMENT_END - mapped to atk_role_unknown
271 #if defined(ATK_CHECK_VERSION)
272 //older ver that doesn't define ATK_CHECK_VERSION doesn't have the following
273 , ATK_ROLE_DOCUMENT_PRESENTATION
274 , ATK_ROLE_DOCUMENT_SPREADSHEET
275 , ATK_ROLE_DOCUMENT_TEXT
276 #if ATK_CHECK_VERSION(2,15,2)
277 , ATK_ROLE_STATIC
278 #else
279 , ATK_ROLE_LABEL
280 #endif
281 #else
282 //older version should fallback to DOCUMENT_FRAME role
283 , ATK_ROLE_DOCUMENT_FRAME
284 , ATK_ROLE_DOCUMENT_FRAME
285 , ATK_ROLE_DOCUMENT_FRAME
286 , ATK_ROLE_LABEL
287 #endif
288 };
289
290 static bool initialized = false;
291
292 if( ! initialized )
293 {
294 // the accessible roles below were added to ATK in later versions,
295 // with role_for_name we will know if they exist in runtime.
296 roleMap[accessibility::AccessibleRole::EDIT_BAR] = getRoleForName("edit bar");
297 roleMap[accessibility::AccessibleRole::EMBEDDED_OBJECT] = getRoleForName("embedded");
298 roleMap[accessibility::AccessibleRole::CHART] = getRoleForName("chart");
299 roleMap[accessibility::AccessibleRole::CAPTION] = getRoleForName("caption");
300 roleMap[accessibility::AccessibleRole::DOCUMENT] = getRoleForName("document frame");
301 roleMap[accessibility::AccessibleRole::PAGE] = getRoleForName("page");
302 roleMap[accessibility::AccessibleRole::SECTION] = getRoleForName("section");
303 roleMap[accessibility::AccessibleRole::FORM] = getRoleForName("form");
304 roleMap[accessibility::AccessibleRole::GROUP_BOX] = getRoleForName("grouping");
305 roleMap[accessibility::AccessibleRole::COMMENT] = getRoleForName("comment");
306 roleMap[accessibility::AccessibleRole::IMAGE_MAP] = getRoleForName("image map");
307 roleMap[accessibility::AccessibleRole::TREE_ITEM] = getRoleForName("tree item");
308 roleMap[accessibility::AccessibleRole::HYPER_LINK] = getRoleForName("link");
309 roleMap[accessibility::AccessibleRole::END_NOTE] = getRoleForName("footnote");
310 roleMap[accessibility::AccessibleRole::FOOTNOTE] = getRoleForName("footnote");
311 roleMap[accessibility::AccessibleRole::NOTE] = getRoleForName("comment");
312
313 initialized = true;
314 }
315
316 static const sal_Int32 nMapSize = SAL_N_ELEMENTS(roleMap);
317 if( 0 <= nRole && nMapSize > nRole )
318 role = roleMap[nRole];
319
320 return role;
321 }
322
323 /*****************************************************************************/
324
325 extern "C" {
326
327 /*****************************************************************************/
328
329 static const gchar*
wrapper_get_name(AtkObject * atk_obj)330 wrapper_get_name( AtkObject *atk_obj )
331 {
332 AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj);
333
334 if( obj->mpContext.is() )
335 {
336 try {
337 OString aName =
338 OUStringToOString(
339 obj->mpContext->getAccessibleName(),
340 RTL_TEXTENCODING_UTF8);
341
342 int nCmp = atk_obj->name ? rtl_str_compare( atk_obj->name, aName.getStr() ) : -1;
343 if( nCmp != 0 )
344 {
345 if( atk_obj->name )
346 g_free(atk_obj->name);
347 atk_obj->name = g_strdup(aName.getStr());
348 }
349 }
350 catch(const uno::Exception&) {
351 g_warning( "Exception in getAccessibleName()" );
352 }
353 }
354
355 return ATK_OBJECT_CLASS (parent_class)->get_name(atk_obj);
356 }
357
358 /*****************************************************************************/
359
360 static const gchar*
wrapper_get_description(AtkObject * atk_obj)361 wrapper_get_description( AtkObject *atk_obj )
362 {
363 AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj);
364
365 if( obj->mpContext.is() )
366 {
367 try {
368 OString aDescription =
369 OUStringToOString(
370 obj->mpContext->getAccessibleDescription(),
371 RTL_TEXTENCODING_UTF8);
372
373 g_free(atk_obj->description);
374 atk_obj->description = g_strdup(aDescription.getStr());
375 }
376 catch(const uno::Exception&) {
377 g_warning( "Exception in getAccessibleDescription()" );
378 }
379 }
380
381 return ATK_OBJECT_CLASS (parent_class)->get_description(atk_obj);
382
383 }
384
385 /*****************************************************************************/
386
387 static AtkAttributeSet *
wrapper_get_attributes(AtkObject * atk_obj)388 wrapper_get_attributes( AtkObject *atk_obj )
389 {
390 AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER( atk_obj );
391 AtkAttributeSet *pSet = nullptr;
392
393 try
394 {
395 uno::Reference< accessibility::XAccessibleExtendedAttributes >
396 xExtendedAttrs( obj->mpContext, uno::UNO_QUERY );
397 if( xExtendedAttrs.is() )
398 pSet = attribute_set_new_from_extended_attributes( xExtendedAttrs );
399 }
400 catch(const uno::Exception&)
401 {
402 g_warning( "Exception in getAccessibleAttributes()" );
403 }
404
405 return pSet;
406 }
407
408 /*****************************************************************************/
409
410 static gint
wrapper_get_n_children(AtkObject * atk_obj)411 wrapper_get_n_children( AtkObject *atk_obj )
412 {
413 AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj);
414
415 if (obj->mpSysObjChild)
416 return 1;
417
418 gint n = 0;
419
420 if( obj->mpContext.is() )
421 {
422 try {
423 n = obj->mpContext->getAccessibleChildCount();
424 }
425 catch(const uno::Exception&) {
426 TOOLS_WARN_EXCEPTION( "vcl", "Exception" );
427 }
428 }
429
430 return n;
431 }
432
433 /*****************************************************************************/
434
435 static AtkObject *
wrapper_ref_child(AtkObject * atk_obj,gint i)436 wrapper_ref_child( AtkObject *atk_obj,
437 gint i )
438 {
439 AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj);
440
441 if (obj->mpSysObjChild)
442 {
443 g_object_ref(obj->mpSysObjChild);
444 return obj->mpSysObjChild;
445 }
446
447 AtkObject* child = nullptr;
448
449 // see comments above atk_object_wrapper_remove_child
450 if( -1 < i && obj->index_of_child_about_to_be_removed == i )
451 {
452 g_object_ref( obj->child_about_to_be_removed );
453 return obj->child_about_to_be_removed;
454 }
455
456 if( obj->mpContext.is() )
457 {
458 try {
459 uno::Reference< accessibility::XAccessible > xAccessible =
460 obj->mpContext->getAccessibleChild( i );
461
462 child = atk_object_wrapper_ref( xAccessible );
463 }
464 catch(const uno::Exception&) {
465 TOOLS_WARN_EXCEPTION( "vcl", "getAccessibleChild");
466 }
467 }
468
469 return child;
470 }
471
472 /*****************************************************************************/
473
474 static gint
wrapper_get_index_in_parent(AtkObject * atk_obj)475 wrapper_get_index_in_parent( AtkObject *atk_obj )
476 {
477 AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj);
478
479 //if we're a native GtkDrawingArea with custom a11y, use the default toolkit a11y
480 if (obj->mpOrig)
481 return atk_object_get_index_in_parent(obj->mpOrig);
482
483 gint i = -1;
484
485 if( obj->mpContext.is() )
486 {
487 try {
488 i = obj->mpContext->getAccessibleIndexInParent();
489 }
490 catch(const uno::Exception&) {
491 g_warning( "Exception in getAccessibleIndexInParent()" );
492 }
493 }
494 return i;
495 }
496
497 /*****************************************************************************/
498
499 AtkRelation*
atk_object_wrapper_relation_new(const accessibility::AccessibleRelation & rRelation)500 atk_object_wrapper_relation_new(const accessibility::AccessibleRelation& rRelation)
501 {
502 sal_uInt32 nTargetCount = rRelation.TargetSet.getLength();
503
504 std::vector<AtkObject*> aTargets;
505
506 for (const auto& rTarget : rRelation.TargetSet)
507 {
508 uno::Reference< accessibility::XAccessible > xAccessible( rTarget, uno::UNO_QUERY );
509 aTargets.push_back(atk_object_wrapper_ref(xAccessible));
510 }
511
512 AtkRelation *pRel =
513 atk_relation_new(
514 aTargets.data(), nTargetCount,
515 mapRelationType( rRelation.RelationType )
516 );
517
518 return pRel;
519 }
520
521 static AtkRelationSet *
wrapper_ref_relation_set(AtkObject * atk_obj)522 wrapper_ref_relation_set( AtkObject *atk_obj )
523 {
524 AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj);
525
526 //if we're a native GtkDrawingArea with custom a11y, use the default toolkit relation set impl
527 if (obj->mpOrig)
528 return atk_object_ref_relation_set(obj->mpOrig);
529
530 AtkRelationSet *pSet = atk_relation_set_new();
531
532 if( obj->mpContext.is() )
533 {
534 try {
535 uno::Reference< accessibility::XAccessibleRelationSet > xRelationSet(
536 obj->mpContext->getAccessibleRelationSet()
537 );
538
539 sal_Int32 nRelations = xRelationSet.is() ? xRelationSet->getRelationCount() : 0;
540 for( sal_Int32 n = 0; n < nRelations; n++ )
541 {
542 AtkRelation *pRel = atk_object_wrapper_relation_new(xRelationSet->getRelation(n));
543 atk_relation_set_add(pSet, pRel);
544 g_object_unref(pRel);
545 }
546 }
547 catch(const uno::Exception &) {
548 g_object_unref( G_OBJECT( pSet ) );
549 pSet = nullptr;
550 }
551 }
552
553 return pSet;
554 }
555
556 static AtkStateSet *
wrapper_ref_state_set(AtkObject * atk_obj)557 wrapper_ref_state_set( AtkObject *atk_obj )
558 {
559 AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj);
560 AtkStateSet *pSet = atk_state_set_new();
561
562 if( obj->mpContext.is() )
563 {
564 try {
565 uno::Reference< accessibility::XAccessibleStateSet > xStateSet(
566 obj->mpContext->getAccessibleStateSet());
567
568 if( xStateSet.is() )
569 {
570 uno::Sequence< sal_Int16 > aStates = xStateSet->getStates();
571
572 for( const auto nState : aStates )
573 {
574 // ATK_STATE_LAST_DEFINED is used to check if the state
575 // is unmapped, do not report it to Atk
576 if ( mapAtkState( nState ) != ATK_STATE_LAST_DEFINED )
577 atk_state_set_add_state( pSet, mapAtkState( nState ) );
578 }
579
580 // We need to emulate FOCUS state for menus, menu-items etc.
581 if( atk_obj == atk_get_focus_object() )
582 atk_state_set_add_state( pSet, ATK_STATE_FOCUSED );
583 /* FIXME - should we do this ?
584 else
585 atk_state_set_remove_state( pSet, ATK_STATE_FOCUSED );
586 */
587 }
588 }
589
590 catch(const uno::Exception &) {
591 g_warning( "Exception in wrapper_ref_state_set" );
592 atk_state_set_add_state( pSet, ATK_STATE_DEFUNCT );
593 }
594 }
595 else
596 atk_state_set_add_state( pSet, ATK_STATE_DEFUNCT );
597
598 return pSet;
599 }
600
601 /*****************************************************************************/
602
603 static void
atk_object_wrapper_finalize(GObject * obj)604 atk_object_wrapper_finalize (GObject *obj)
605 {
606 AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER (obj);
607
608 if( pWrap->mpAccessible.is() )
609 {
610 ooo_wrapper_registry_remove( pWrap->mpAccessible );
611 pWrap->mpAccessible.clear();
612 }
613
614 atk_object_wrapper_dispose( pWrap );
615
616 parent_class->finalize( obj );
617 }
618
619 static void
atk_object_wrapper_class_init(AtkObjectWrapperClass * klass)620 atk_object_wrapper_class_init (AtkObjectWrapperClass *klass)
621 {
622 GObjectClass *gobject_class = G_OBJECT_CLASS( klass );
623 AtkObjectClass *atk_class = ATK_OBJECT_CLASS( klass );
624
625 parent_class = static_cast<GObjectClass *>(g_type_class_peek_parent (klass));
626
627 // GObject methods
628 gobject_class->finalize = atk_object_wrapper_finalize;
629
630 // AtkObject methods
631 atk_class->get_name = wrapper_get_name;
632 atk_class->get_description = wrapper_get_description;
633 atk_class->get_attributes = wrapper_get_attributes;
634 atk_class->get_n_children = wrapper_get_n_children;
635 atk_class->ref_child = wrapper_ref_child;
636 atk_class->get_index_in_parent = wrapper_get_index_in_parent;
637 atk_class->ref_relation_set = wrapper_ref_relation_set;
638 atk_class->ref_state_set = wrapper_ref_state_set;
639 }
640
641 static void
atk_object_wrapper_init(AtkObjectWrapper * wrapper,AtkObjectWrapperClass *)642 atk_object_wrapper_init (AtkObjectWrapper *wrapper,
643 AtkObjectWrapperClass*)
644 {
645 wrapper->mpAction = nullptr;
646 wrapper->mpComponent = nullptr;
647 wrapper->mpEditableText = nullptr;
648 wrapper->mpHypertext = nullptr;
649 wrapper->mpImage = nullptr;
650 wrapper->mpSelection = nullptr;
651 wrapper->mpTable = nullptr;
652 wrapper->mpText = nullptr;
653 wrapper->mpValue = nullptr;
654 }
655
656 } // extern "C"
657
658 GType
atk_object_wrapper_get_type()659 atk_object_wrapper_get_type()
660 {
661 static GType type = 0;
662
663 if (!type)
664 {
665 static const GTypeInfo typeInfo =
666 {
667 sizeof (AtkObjectWrapperClass),
668 nullptr,
669 nullptr,
670 reinterpret_cast<GClassInitFunc>(atk_object_wrapper_class_init),
671 nullptr,
672 nullptr,
673 sizeof (AtkObjectWrapper),
674 0,
675 reinterpret_cast<GInstanceInitFunc>(atk_object_wrapper_init),
676 nullptr
677 } ;
678 type = g_type_register_static (ATK_TYPE_OBJECT,
679 "OOoAtkObj",
680 &typeInfo, GTypeFlags(0)) ;
681 }
682 return type;
683 }
684
685 static bool
isOfType(uno::XInterface * pInterface,const uno::Type & rType)686 isOfType( uno::XInterface *pInterface, const uno::Type & rType )
687 {
688 g_return_val_if_fail( pInterface != nullptr, false );
689
690 bool bIs = false;
691 try {
692 uno::Any aRet = pInterface->queryInterface( rType );
693
694 bIs = ( ( typelib_TypeClass_INTERFACE == aRet.pType->eTypeClass ) &&
695 ( aRet.pReserved != nullptr ) );
696 } catch( const uno::Exception &) { }
697
698 return bIs;
699 }
700
701 extern "C" {
702 typedef GType (* GetGIfaceType ) ();
703 }
704 const struct {
705 const char *name;
706 GInterfaceInitFunc const aInit;
707 GetGIfaceType const aGetGIfaceType;
708 const uno::Type & (*aGetUnoType) ();
709 } aTypeTable[] = {
710 // re-location heaven:
711 {
712 "Comp", reinterpret_cast<GInterfaceInitFunc>(componentIfaceInit),
713 atk_component_get_type,
714 cppu::UnoType<accessibility::XAccessibleComponent>::get
715 },
716 {
717 "Act", reinterpret_cast<GInterfaceInitFunc>(actionIfaceInit),
718 atk_action_get_type,
719 cppu::UnoType<accessibility::XAccessibleAction>::get
720 },
721 {
722 "Txt", reinterpret_cast<GInterfaceInitFunc>(textIfaceInit),
723 atk_text_get_type,
724 cppu::UnoType<accessibility::XAccessibleText>::get
725 },
726 {
727 "Val", reinterpret_cast<GInterfaceInitFunc>(valueIfaceInit),
728 atk_value_get_type,
729 cppu::UnoType<accessibility::XAccessibleValue>::get
730 },
731 {
732 "Tab", reinterpret_cast<GInterfaceInitFunc>(tableIfaceInit),
733 atk_table_get_type,
734 cppu::UnoType<accessibility::XAccessibleTable>::get
735 },
736 {
737 "Edt", reinterpret_cast<GInterfaceInitFunc>(editableTextIfaceInit),
738 atk_editable_text_get_type,
739 cppu::UnoType<accessibility::XAccessibleEditableText>::get
740 },
741 {
742 "Img", reinterpret_cast<GInterfaceInitFunc>(imageIfaceInit),
743 atk_image_get_type,
744 cppu::UnoType<accessibility::XAccessibleImage>::get
745 },
746 {
747 "Hyp", reinterpret_cast<GInterfaceInitFunc>(hypertextIfaceInit),
748 atk_hypertext_get_type,
749 cppu::UnoType<accessibility::XAccessibleHypertext>::get
750 },
751 {
752 "Sel", reinterpret_cast<GInterfaceInitFunc>(selectionIfaceInit),
753 atk_selection_get_type,
754 cppu::UnoType<accessibility::XAccessibleSelection>::get
755 }
756 // AtkDocument is a nastily broken interface (so far)
757 // we could impl. get_document_type perhaps though.
758 };
759
760 const int aTypeTableSize = G_N_ELEMENTS( aTypeTable );
761
762 static GType
ensureTypeFor(uno::XInterface * pAccessible)763 ensureTypeFor( uno::XInterface *pAccessible )
764 {
765 int i;
766 bool bTypes[ aTypeTableSize ] = { false, };
767 OStringBuffer aTypeNameBuf( "OOoAtkObj" );
768
769 for( i = 0; i < aTypeTableSize; i++ )
770 {
771 if( isOfType( pAccessible, aTypeTable[i].aGetUnoType() ) )
772 {
773 aTypeNameBuf.append(aTypeTable[i].name);
774 bTypes[i] = true;
775 }
776 }
777
778 OString aTypeName = aTypeNameBuf.makeStringAndClear();
779 GType nType = g_type_from_name( aTypeName.getStr() );
780 if( nType == G_TYPE_INVALID )
781 {
782 GTypeInfo aTypeInfo = {
783 sizeof( AtkObjectWrapperClass ),
784 nullptr, nullptr, nullptr, nullptr, nullptr,
785 sizeof( AtkObjectWrapper ),
786 0, nullptr, nullptr
787 } ;
788 nType = g_type_register_static( ATK_TYPE_OBJECT_WRAPPER,
789 aTypeName.getStr(), &aTypeInfo,
790 GTypeFlags(0) ) ;
791
792 for( int j = 0; j < aTypeTableSize; j++ )
793 if( bTypes[j] )
794 {
795 GInterfaceInfo aIfaceInfo = { nullptr, nullptr, nullptr };
796 aIfaceInfo.interface_init = aTypeTable[j].aInit;
797 g_type_add_interface_static (nType, aTypeTable[j].aGetGIfaceType(),
798 &aIfaceInfo);
799 }
800 }
801 return nType;
802 }
803
804 AtkObject *
atk_object_wrapper_ref(const uno::Reference<accessibility::XAccessible> & rxAccessible,bool create)805 atk_object_wrapper_ref( const uno::Reference< accessibility::XAccessible > &rxAccessible, bool create )
806 {
807 g_return_val_if_fail( bool(rxAccessible), nullptr );
808
809 AtkObject *obj = ooo_wrapper_registry_get(rxAccessible);
810 if( obj )
811 {
812 g_object_ref( obj );
813 return obj;
814 }
815
816 if( create )
817 return atk_object_wrapper_new( rxAccessible );
818
819 return nullptr;
820 }
821
822 AtkObject *
atk_object_wrapper_new(const css::uno::Reference<css::accessibility::XAccessible> & rxAccessible,AtkObject * parent,AtkObject * orig)823 atk_object_wrapper_new( const css::uno::Reference< css::accessibility::XAccessible >& rxAccessible,
824 AtkObject* parent, AtkObject* orig )
825 {
826 g_return_val_if_fail( bool(rxAccessible), nullptr );
827
828 AtkObjectWrapper *pWrap = nullptr;
829
830 try {
831 uno::Reference< accessibility::XAccessibleContext > xContext(rxAccessible->getAccessibleContext());
832
833 g_return_val_if_fail( bool(xContext), nullptr );
834
835 GType nType = ensureTypeFor( xContext.get() );
836 gpointer obj = g_object_new( nType, nullptr);
837
838 pWrap = ATK_OBJECT_WRAPPER( obj );
839 pWrap->mpAccessible = rxAccessible;
840
841 pWrap->index_of_child_about_to_be_removed = -1;
842 pWrap->child_about_to_be_removed = nullptr;
843
844 pWrap->mpContext = xContext;
845 pWrap->mpOrig = orig;
846
847 AtkObject* atk_obj = ATK_OBJECT(pWrap);
848 atk_obj->role = mapToAtkRole( xContext->getAccessibleRole() );
849 atk_obj->accessible_parent = parent;
850
851 ooo_wrapper_registry_add( rxAccessible, atk_obj );
852
853 if( parent )
854 g_object_ref( atk_obj->accessible_parent );
855 else
856 {
857 /* gail_focus_tracker remembers the focused object at the first
858 * parent in the hierarchy that is a Gtk+ widget, but at the time the
859 * event gets processed (at idle), it may be too late to create the
860 * hierarchy, so doing it now ..
861 */
862 uno::Reference< accessibility::XAccessible > xParent( xContext->getAccessibleParent() );
863
864 if( xParent.is() )
865 atk_obj->accessible_parent = atk_object_wrapper_ref( xParent );
866 }
867
868 // Attach a listener to the UNO object if it's not TRANSIENT
869 uno::Reference< accessibility::XAccessibleStateSet > xStateSet( xContext->getAccessibleStateSet() );
870 if( xStateSet.is() && ! xStateSet->contains( accessibility::AccessibleStateType::TRANSIENT ) )
871 {
872 uno::Reference< accessibility::XAccessibleEventBroadcaster > xBroadcaster(xContext, uno::UNO_QUERY);
873 if( xBroadcaster.is() )
874 {
875 uno::Reference<accessibility::XAccessibleEventListener> xListener(new AtkListener(pWrap));
876 xBroadcaster->addAccessibleEventListener(xListener);
877 }
878 else
879 OSL_ASSERT( false );
880 }
881
882 static auto func = reinterpret_cast<void(*)(AtkObject*, const gchar*)>(dlsym(nullptr, "atk_object_set_accessible_id"));
883 if (func)
884 {
885 css::uno::Reference<css::accessibility::XAccessibleContext2> xContext2(xContext, css::uno::UNO_QUERY);
886 if( xContext2.is() )
887 {
888 OString aId = OUStringToOString( xContext2->getAccessibleId(), RTL_TEXTENCODING_UTF8);
889 (*func)(atk_obj, aId.getStr());
890 }
891 }
892
893 // tdf#141197 if we have a sysobj child then include that in the hierarchy
894 if (UnoWrapperBase* pWrapper = UnoWrapperBase::GetUnoWrapper())
895 {
896 css::uno::Reference<css::awt::XWindow> xAWTWindow(rxAccessible, css::uno::UNO_QUERY);
897 VclPtr<vcl::Window> xWindow = pWrapper->GetWindow(xAWTWindow);
898 if (xWindow && xWindow->GetType() == WindowType::SYSTEMCHILDWINDOW)
899 {
900 const SystemEnvData* pEnvData = static_cast<SystemChildWindow*>(xWindow.get())->GetSystemData();
901 if (GtkWidget *pSysObj = pEnvData ? static_cast<GtkWidget*>(pEnvData->pWidget) : nullptr)
902 pWrap->mpSysObjChild = gtk_widget_get_accessible(pSysObj);
903 }
904 }
905
906 return ATK_OBJECT( pWrap );
907 }
908 catch (const uno::Exception &)
909 {
910 if( pWrap )
911 g_object_unref( pWrap );
912
913 return nullptr;
914 }
915 }
916
917 /*****************************************************************************/
918
atk_object_wrapper_add_child(AtkObjectWrapper * wrapper,AtkObject * child,gint index)919 void atk_object_wrapper_add_child(AtkObjectWrapper* wrapper, AtkObject *child, gint index)
920 {
921 AtkObject *atk_obj = ATK_OBJECT( wrapper );
922
923 atk_object_set_parent( child, atk_obj );
924 g_signal_emit_by_name( atk_obj, "children_changed::add", index, child, nullptr );
925 }
926
927 /*****************************************************************************/
928
atk_object_wrapper_remove_child(AtkObjectWrapper * wrapper,AtkObject * child,gint index)929 void atk_object_wrapper_remove_child(AtkObjectWrapper* wrapper, AtkObject *child, gint index)
930 {
931 /*
932 * the atk-bridge GTK+ module gets back to the event source to ref the child just
933 * vanishing, so we keep this reference because the semantic on OOo side is different.
934 */
935 wrapper->child_about_to_be_removed = child;
936 wrapper->index_of_child_about_to_be_removed = index;
937
938 g_signal_emit_by_name( ATK_OBJECT( wrapper ), "children_changed::remove", index, child, nullptr );
939
940 wrapper->index_of_child_about_to_be_removed = -1;
941 wrapper->child_about_to_be_removed = nullptr;
942 }
943
944 /*****************************************************************************/
945
atk_object_wrapper_set_role(AtkObjectWrapper * wrapper,sal_Int16 role)946 void atk_object_wrapper_set_role(AtkObjectWrapper* wrapper, sal_Int16 role)
947 {
948 AtkObject *atk_obj = ATK_OBJECT( wrapper );
949 atk_object_set_role( atk_obj, mapToAtkRole( role ) );
950 }
951
952 /*****************************************************************************/
953
atk_object_wrapper_dispose(AtkObjectWrapper * wrapper)954 void atk_object_wrapper_dispose(AtkObjectWrapper* wrapper)
955 {
956 wrapper->mpContext.clear();
957 wrapper->mpAction.clear();
958 wrapper->mpComponent.clear();
959 wrapper->mpEditableText.clear();
960 wrapper->mpHypertext.clear();
961 wrapper->mpImage.clear();
962 wrapper->mpSelection.clear();
963 wrapper->mpMultiLineText.clear();
964 wrapper->mpTable.clear();
965 wrapper->mpText.clear();
966 wrapper->mpTextMarkup.clear();
967 wrapper->mpTextAttributes.clear();
968 wrapper->mpValue.clear();
969 }
970
971 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
972