summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukasz Stanislawski <lukasz.stanislawski@gmail.com>2015-10-04 22:30:31 +0200
committerNicolas Aguirre <aguirre.nicolas@gmail.com>2015-11-12 10:15:03 +0100
commit196171e6a29edf9271011b895b142f0634292afb (patch)
tree66fc1860c549ecf42a696d4dbf648dd3282cfd4c
parent8e74dbef59bee696b5fb87fdf0a4d5618b17321d (diff)
atspi: add new relationship append/remove/clear API
Allow to add some extra contextul information about accessibility objects which can be used by Assistive Technology to provide better user experience. API is still marked as beta. @feature
-rw-r--r--src/lib/elm_atspi_bridge.c12
-rw-r--r--src/lib/elm_interface_atspi_accessible.c180
-rw-r--r--src/lib/elm_interface_atspi_accessible.eo38
-rw-r--r--src/lib/elm_interface_atspi_accessible.h39
-rw-r--r--src/lib/elm_widget.c54
-rw-r--r--src/lib/elm_widget.eo1
6 files changed, 270 insertions, 54 deletions
diff --git a/src/lib/elm_atspi_bridge.c b/src/lib/elm_atspi_bridge.c
index 934115350..b4056553a 100644
--- a/src/lib/elm_atspi_bridge.c
+++ b/src/lib/elm_atspi_bridge.c
@@ -786,11 +786,12 @@ _accessible_get_relation_set(const Eldbus_Service_Interface *iface EINA_UNUSED,
786{ 786{
787 const char *obj_path = eldbus_message_path_get(msg); 787 const char *obj_path = eldbus_message_path_get(msg);
788 Eo *bridge = eldbus_service_object_data_get(iface, ELM_ATSPI_BRIDGE_CLASS_NAME); 788 Eo *bridge = eldbus_service_object_data_get(iface, ELM_ATSPI_BRIDGE_CLASS_NAME);
789 Eo *obj = _bridge_object_from_path(bridge, obj_path); 789 Eo *rel_obj, *obj = _bridge_object_from_path(bridge, obj_path);
790 Eldbus_Message *ret = NULL; 790 Eldbus_Message *ret = NULL;
791 Eldbus_Message_Iter *iter = NULL, *iter_array = NULL, *iter_array2 = NULL, *iter_struct; 791 Eldbus_Message_Iter *iter = NULL, *iter_array = NULL, *iter_array2 = NULL, *iter_struct;
792 Elm_Atspi_Relation *rel; 792 Elm_Atspi_Relation *rel;
793 Eina_List *rels; 793 Eina_List *l, *l2;
794 Elm_Atspi_Relation_Set rels;
794 795
795 ELM_ATSPI_OBJ_CHECK_OR_RETURN_DBUS_ERROR(obj, ELM_INTERFACE_ATSPI_ACCESSIBLE_MIXIN, msg); 796 ELM_ATSPI_OBJ_CHECK_OR_RETURN_DBUS_ERROR(obj, ELM_INTERFACE_ATSPI_ACCESSIBLE_MIXIN, msg);
796 797
@@ -803,17 +804,18 @@ _accessible_get_relation_set(const Eldbus_Service_Interface *iface EINA_UNUSED,
803 804
804 eo_do(obj, rels = elm_interface_atspi_accessible_relation_set_get()); 805 eo_do(obj, rels = elm_interface_atspi_accessible_relation_set_get());
805 806
806 EINA_LIST_FREE(rels, rel) 807 EINA_LIST_FOREACH(rels, l, rel)
807 { 808 {
808 iter_struct = eldbus_message_iter_container_new(iter_array, 'r', NULL); 809 iter_struct = eldbus_message_iter_container_new(iter_array, 'r', NULL);
809 eldbus_message_iter_basic_append(iter_struct, 'u', _elm_relation_to_atspi_relation(rel->type)); 810 eldbus_message_iter_basic_append(iter_struct, 'u', _elm_relation_to_atspi_relation(rel->type));
810 iter_array2 = eldbus_message_iter_container_new(iter_struct, 'a', "(so)"); 811 iter_array2 = eldbus_message_iter_container_new(iter_struct, 'a', "(so)");
811 EINA_SAFETY_ON_NULL_GOTO(iter_array2, fail); 812 EINA_SAFETY_ON_NULL_GOTO(iter_array2, fail);
812 _bridge_iter_object_reference_append(bridge, iter_array2, rel->obj); 813 EINA_LIST_FOREACH(rel->objects, l2, rel_obj)
814 _bridge_iter_object_reference_append(bridge, iter_array2, rel_obj);
813 eldbus_message_iter_container_close(iter_struct, iter_array2); 815 eldbus_message_iter_container_close(iter_struct, iter_array2);
814 eldbus_message_iter_container_close(iter_array, iter_struct); 816 eldbus_message_iter_container_close(iter_array, iter_struct);
815 free(rel);
816 } 817 }
818 elm_atspi_relation_set_free(rels);
817 eldbus_message_iter_container_close(iter, iter_array); 819 eldbus_message_iter_container_close(iter, iter_array);
818 820
819 return ret; 821 return ret;
diff --git a/src/lib/elm_interface_atspi_accessible.c b/src/lib/elm_interface_atspi_accessible.c
index 709d142ff..0c0a213fc 100644
--- a/src/lib/elm_interface_atspi_accessible.c
+++ b/src/lib/elm_interface_atspi_accessible.c
@@ -127,6 +127,7 @@ struct _Elm_Interface_Atspi_Accessible_Data
127 const char *name; 127 const char *name;
128 const char *description; 128 const char *description;
129 const char *translation_domain; 129 const char *translation_domain;
130 Elm_Atspi_Relation_Set relations;
130 Elm_Interface_Atspi_Accessible *parent; 131 Elm_Interface_Atspi_Accessible *parent;
131}; 132};
132 133
@@ -290,12 +291,10 @@ _elm_interface_atspi_accessible_state_set_get(Eo *obj EINA_UNUSED, Elm_Interface
290 return 0; 291 return 0;
291} 292}
292 293
293EOLIAN Eina_List* 294EOLIAN Elm_Atspi_Relation_Set
294_elm_interface_atspi_accessible_relation_set_get(Eo *obj EINA_UNUSED, Elm_Interface_Atspi_Accessible_Data *pd EINA_UNUSED) 295_elm_interface_atspi_accessible_relation_set_get(Eo *obj EINA_UNUSED, Elm_Interface_Atspi_Accessible_Data *pd EINA_UNUSED)
295{ 296{
296 WRN("The %s object does not implement the \"accessible_relation_set\" function.", 297 return elm_atspi_relation_set_clone(pd->relations);
297 eo_class_name_get(eo_class_get(obj)));
298 return NULL;
299} 298}
300 299
301EAPI void elm_atspi_attributes_list_free(Eina_List *list) 300EAPI void elm_atspi_attributes_list_free(Eina_List *list)
@@ -369,4 +368,177 @@ _elm_interface_atspi_accessible_translation_domain_get(Eo *obj EINA_UNUSED, Elm_
369 return pd->translation_domain; 368 return pd->translation_domain;
370} 369}
371 370
371EAPI void
372elm_atspi_relation_free(Elm_Atspi_Relation *relation)
373{
374 eina_list_free(relation->objects);
375 free(relation);
376}
377
378EAPI Elm_Atspi_Relation *
379elm_atspi_relation_clone(const Elm_Atspi_Relation *relation)
380{
381 Elm_Atspi_Relation *ret = calloc(sizeof(Elm_Atspi_Relation), 1);
382 if (!ret) return NULL;
383
384 ret->type = relation->type;
385 ret->objects = eina_list_clone(relation->objects);
386 return ret;
387}
388
389static Eina_Bool
390_on_rel_obj_del(void *data, Eo *obj, const Eo_Event_Description *desc EINA_UNUSED, void *event_info EINA_UNUSED)
391{
392 Elm_Atspi_Relation_Set *set = data;
393 Elm_Atspi_Relation *rel;
394 Eina_List *l, *l2, *p, *p2;
395 Eo *rel_obj;
396
397 EINA_LIST_FOREACH_SAFE(*set, l, l2, rel)
398 {
399 EINA_LIST_FOREACH_SAFE(rel->objects, p, p2, rel_obj)
400 {
401 if (rel_obj == obj)
402 rel->objects = eina_list_remove_list(rel->objects, p);
403 }
404 if (!rel->objects)
405 {
406 *set = eina_list_remove_list(*set, l);
407 free(rel);
408 }
409 }
410 return EINA_TRUE;
411}
412
413EAPI Eina_Bool
414elm_atspi_relation_set_relation_append(Elm_Atspi_Relation_Set *set, Elm_Atspi_Relation_Type type, const Eo *rel_obj)
415{
416 Elm_Atspi_Relation *rel;
417 Eina_List *l;
418
419 if (!eo_isa(rel_obj, ELM_INTERFACE_ATSPI_ACCESSIBLE_MIXIN))
420 return EINA_FALSE;
421
422 EINA_LIST_FOREACH(*set, l, rel)
423 {
424 if (rel->type == type)
425 {
426 if (!eina_list_data_find(rel->objects, rel_obj))
427 {
428 rel->objects = eina_list_append(rel->objects, rel_obj);
429 eo_do(rel_obj, eo_event_callback_add(EO_BASE_EVENT_DEL, _on_rel_obj_del, set));
430 }
431 return EINA_TRUE;
432 }
433 }
434
435 rel = calloc(sizeof(Elm_Atspi_Relation), 1);
436 if (!rel) return EINA_FALSE;
437
438 rel->type = type;
439 rel->objects = eina_list_append(rel->objects, rel_obj);
440 *set = eina_list_append(*set, rel);
441
442 eo_do(rel_obj, eo_event_callback_add(EO_BASE_EVENT_DEL, _on_rel_obj_del, set));
443 return EINA_TRUE;
444}
445
446EAPI void
447elm_atspi_relation_set_relation_remove(Elm_Atspi_Relation_Set *set, Elm_Atspi_Relation_Type type, const Eo *rel_obj)
448{
449 Eina_List *l;
450 Elm_Atspi_Relation *rel;
451
452 EINA_LIST_FOREACH(*set, l, rel)
453 {
454 if (rel->type == type)
455 {
456 if (eina_list_data_find(rel->objects, rel_obj))
457 {
458 eo_do(rel_obj, eo_event_callback_del(EO_BASE_EVENT_DEL, _on_rel_obj_del, set));
459 rel->objects = eina_list_remove(rel->objects, rel_obj);
460 }
461 if (!rel->objects)
462 {
463 *set = eina_list_remove(*set, rel);
464 elm_atspi_relation_free(rel);
465 }
466 return;
467 }
468 }
469}
470
471EAPI void
472elm_atspi_relation_set_relation_type_remove(Elm_Atspi_Relation_Set *set, Elm_Atspi_Relation_Type type)
473{
474 Eina_List *l;
475 Elm_Atspi_Relation *rel;
476 Eo *obj;
477
478 EINA_LIST_FOREACH(*set, l, rel)
479 {
480 if (rel->type == type)
481 {
482 EINA_LIST_FOREACH(rel->objects, l, obj)
483 eo_do(obj, eo_event_callback_del(EO_BASE_EVENT_DEL, _on_rel_obj_del, set));
484 *set = eina_list_remove(*set, rel);
485 elm_atspi_relation_free(rel);
486 return;
487 }
488 }
489}
490
491EAPI void
492elm_atspi_relation_set_free(Elm_Atspi_Relation_Set set)
493{
494 Elm_Atspi_Relation *rel;
495 Eina_List *l;
496 Eo *obj;
497
498 EINA_LIST_FREE(set, rel)
499 {
500 EINA_LIST_FOREACH(rel->objects, l, obj)
501 eo_do(obj, eo_event_callback_del(EO_BASE_EVENT_DEL, _on_rel_obj_del, set));
502 elm_atspi_relation_free(rel);
503 }
504}
505
506EAPI Elm_Atspi_Relation_Set
507elm_atspi_relation_set_clone(const Elm_Atspi_Relation_Set set)
508{
509 Elm_Atspi_Relation_Set ret = NULL;
510 Eina_List *l;
511 Elm_Atspi_Relation *rel;
512
513 EINA_LIST_FOREACH(set, l, rel)
514 {
515 Elm_Atspi_Relation *cpy = elm_atspi_relation_clone(rel);
516 ret = eina_list_append(ret, cpy);
517 }
518
519 return ret;
520}
521
522EOLIAN static Eina_Bool
523_elm_interface_atspi_accessible_relationship_append(Eo *obj EINA_UNUSED, Elm_Interface_Atspi_Accessible_Data *sd, Elm_Atspi_Relation_Type type, const Elm_Interface_Atspi_Accessible *relation_obj)
524{
525 return elm_atspi_relation_set_relation_append(&sd->relations, type, relation_obj);
526}
527
528EOLIAN static void
529_elm_interface_atspi_accessible_relationship_remove(Eo *obj EINA_UNUSED, Elm_Interface_Atspi_Accessible_Data *sd, Elm_Atspi_Relation_Type type, const Elm_Interface_Atspi_Accessible *relation_obj)
530{
531 if (relation_obj)
532 elm_atspi_relation_set_relation_remove(&sd->relations, type, relation_obj);
533 else
534 elm_atspi_relation_set_relation_type_remove(&sd->relations, type);
535}
536
537EOLIAN static void
538_elm_interface_atspi_accessible_relationships_clear(Eo *obj EINA_UNUSED, Elm_Interface_Atspi_Accessible_Data *sd)
539{
540 elm_atspi_relation_set_free(sd->relations);
541 sd->relations = NULL;
542}
543
372#include "elm_interface_atspi_accessible.eo.c" 544#include "elm_interface_atspi_accessible.eo.c"
diff --git a/src/lib/elm_interface_atspi_accessible.eo b/src/lib/elm_interface_atspi_accessible.eo
index f6a4ed7e2..65b6b95c9 100644
--- a/src/lib/elm_interface_atspi_accessible.eo
+++ b/src/lib/elm_interface_atspi_accessible.eo
@@ -27,7 +27,7 @@ mixin Elm_Interface_Atspi_Accessible ()
27 [[Gets an string describing ATSPI widget role name. Lists and elements Should be free by a user.]] 27 [[Gets an string describing ATSPI widget role name. Lists and elements Should be free by a user.]]
28 } 28 }
29 values { 29 values {
30 relations: own(list<own(Elm_Atspi_Relation *)> *); 30 relations: Elm_Atspi_Relation_Set;
31 } 31 }
32 } 32 }
33 @property role @protected { 33 @property role @protected {
@@ -147,6 +147,42 @@ mixin Elm_Interface_Atspi_Accessible ()
147 domain: const(char)*; [[ translation domain ]] 147 domain: const(char)*; [[ translation domain ]]
148 } 148 }
149 } 149 }
150 relationship_append @protected {
151 [[Defines the relationship between two accessible objects.
152
153 Adds unique relation between source object and relation_object of a
154 given type.
155
156 Relationships can be queried by Assistive Technology clients to
157 provide customized feedback, improving overall user experience.
158
159 Relationship_append API is asymmetric, which means that
160 appending, for example, relation ELM_ATSPI_RELATION_FLOWS_TO from object A to B,
161 do NOT append relation ELM_ATSPI_RELATION_FLOWS_FROM from object B to
162 object A.
163
164 return: EINA_TRUE is relationship was successfully appended, EINA_FALSE
165 otherwise]]
166 return: bool;
167 params {
168 @in type: Elm_Atspi_Relation_Type;
169 @in relation_object: const(Elm_Interface_Atspi_Accessible)*;
170 }
171 }
172 relationship_remove @protected {
173 [[Removes the relationship between two accessible objects.
174
175 If relation_object is NULL function removes all relations
176 of given type.
177 ]]
178 params {
179 @in type: Elm_Atspi_Relation_Type;
180 @in relation_object: const(Elm_Interface_Atspi_Accessible)*;
181 }
182 }
183 relationships_clear @protected {
184 [[Removes all relationships in accessible object.]]
185 }
150 } 186 }
151 events { 187 events {
152 property,changed: const(char)*; 188 property,changed: const(char)*;
diff --git a/src/lib/elm_interface_atspi_accessible.h b/src/lib/elm_interface_atspi_accessible.h
index 0012caa41..8ebe244a1 100644
--- a/src/lib/elm_interface_atspi_accessible.h
+++ b/src/lib/elm_interface_atspi_accessible.h
@@ -252,7 +252,7 @@ typedef struct _Elm_Atspi_Attribute Elm_Atspi_Attribute;
252struct _Elm_Atspi_Relation 252struct _Elm_Atspi_Relation
253{ 253{
254 Elm_Atspi_Relation_Type type; 254 Elm_Atspi_Relation_Type type;
255 const Eo *obj; 255 Eina_List *objects;
256}; 256};
257 257
258typedef struct _Elm_Atspi_Relation Elm_Atspi_Relation; 258typedef struct _Elm_Atspi_Relation Elm_Atspi_Relation;
@@ -262,6 +262,43 @@ typedef struct _Elm_Atspi_Relation Elm_Atspi_Relation;
262 */ 262 */
263EAPI void elm_atspi_attributes_list_free(Eina_List *list); 263EAPI void elm_atspi_attributes_list_free(Eina_List *list);
264 264
265typedef Eina_List *Elm_Atspi_Relation_Set;
266
267/**
268 * Frees relation.
269 */
270EAPI void elm_atspi_relation_free(Elm_Atspi_Relation *relation);
271
272/**
273 * Clones relation.
274 */
275EAPI Elm_Atspi_Relation * elm_atspi_relation_clone(const Elm_Atspi_Relation *relation);
276
277/**
278 * Appends relation to relation set
279 */
280EAPI Eina_Bool elm_atspi_relation_set_relation_append(Elm_Atspi_Relation_Set *set, Elm_Atspi_Relation_Type type, const Eo *rel_obj);
281
282/**
283 * Removes relation from relation set
284 */
285EAPI void elm_atspi_relation_set_relation_remove(Elm_Atspi_Relation_Set *set, Elm_Atspi_Relation_Type type, const Eo *rel_obj);
286
287/**
288 * Removes all relation from relation set of a given type
289 */
290EAPI void elm_atspi_relation_set_relation_type_remove(Elm_Atspi_Relation_Set *set, Elm_Atspi_Relation_Type type);
291
292/**
293 * Frees Elm_Atspi_Relation_Set
294 */
295EAPI void elm_atspi_relation_set_free(Elm_Atspi_Relation_Set set);
296
297/**
298 * Clones Elm_Atspi_Relation_Set
299 */
300EAPI Elm_Atspi_Relation_Set elm_atspi_relation_set_clone(const Elm_Atspi_Relation_Set set);
301
265#ifdef EFL_EO_API_SUPPORT 302#ifdef EFL_EO_API_SUPPORT
266 303
267/** 304/**
diff --git a/src/lib/elm_widget.c b/src/lib/elm_widget.c
index 73d024a53..4a9022f78 100644
--- a/src/lib/elm_widget.c
+++ b/src/lib/elm_widget.c
@@ -4431,9 +4431,12 @@ _elm_widget_item_eo_base_destructor(Eo *eo_item, Elm_Widget_Item_Data *item)
4431 } 4431 }
4432 eina_hash_free(item->labels); 4432 eina_hash_free(item->labels);
4433 4433
4434 eo_do(eo_item, elm_interface_atspi_accessible_description_set(NULL)); 4434 eo_do(eo_item,
4435 eo_do(eo_item, elm_interface_atspi_accessible_name_set(NULL)); 4435 elm_interface_atspi_accessible_description_set(NULL),
4436 eo_do(eo_item, elm_interface_atspi_accessible_translation_domain_set(NULL)); 4436 elm_interface_atspi_accessible_name_set(NULL),
4437 elm_interface_atspi_accessible_translation_domain_set(NULL),
4438 elm_interface_atspi_accessible_relationships_clear()
4439 );
4437 4440
4438 if (_elm_config->atspi_mode && item->widget) 4441 if (_elm_config->atspi_mode && item->widget)
4439 elm_interface_atspi_accessible_children_changed_del_signal_emit(item->widget, eo_item); 4442 elm_interface_atspi_accessible_children_changed_del_signal_emit(item->widget, eo_item);
@@ -5706,9 +5709,12 @@ _elm_widget_eo_base_constructor(Eo *obj, Elm_Widget_Smart_Data *sd EINA_UNUSED)
5706EOLIAN static void 5709EOLIAN static void
5707_elm_widget_eo_base_destructor(Eo *obj, Elm_Widget_Smart_Data *sd EINA_UNUSED) 5710_elm_widget_eo_base_destructor(Eo *obj, Elm_Widget_Smart_Data *sd EINA_UNUSED)
5708{ 5711{
5709 eo_do(obj, elm_interface_atspi_accessible_description_set(NULL)); 5712 eo_do(obj,
5710 eo_do(obj, elm_interface_atspi_accessible_name_set(NULL)); 5713 elm_interface_atspi_accessible_description_set(NULL),
5711 eo_do(obj, elm_interface_atspi_accessible_translation_domain_set(NULL)); 5714 elm_interface_atspi_accessible_name_set(NULL),
5715 elm_interface_atspi_accessible_translation_domain_set(NULL),
5716 elm_interface_atspi_accessible_relationships_clear()
5717 );
5712 elm_interface_atspi_accessible_removed(obj); 5718 elm_interface_atspi_accessible_removed(obj);
5713 5719
5714 eo_do_super(obj, ELM_WIDGET_CLASS, eo_destructor()); 5720 eo_do_super(obj, ELM_WIDGET_CLASS, eo_destructor());
@@ -5881,42 +5887,6 @@ _elm_widget_elm_interface_atspi_accessible_attributes_get(Eo *obj, Elm_Widget_Sm
5881 return ret; 5887 return ret;
5882} 5888}
5883 5889
5884static Elm_Atspi_Relation*
5885_relation_new(Elm_Atspi_Relation_Type type, Eo *obj)
5886{
5887 Elm_Atspi_Relation *rel = calloc(1, sizeof(Elm_Atspi_Relation));
5888 if (!rel) return NULL;
5889
5890 rel->type = type;
5891 rel->obj = obj;
5892
5893 return rel;
5894}
5895
5896EOLIAN static Eina_List*
5897_elm_widget_elm_interface_atspi_accessible_relation_set_get(Eo *obj, Elm_Widget_Smart_Data *pd EINA_UNUSED)
5898{
5899 Eina_List *list = NULL;
5900 Elm_Atspi_Relation *rel;
5901 Evas_Object *rel_obj;
5902
5903 rel_obj = elm_object_focus_next_object_get(obj, ELM_FOCUS_NEXT);
5904 if (eo_isa(rel_obj, ELM_INTERFACE_ATSPI_ACCESSIBLE_MIXIN))
5905 {
5906 rel = _relation_new(ELM_ATSPI_RELATION_FLOWS_TO, rel_obj);
5907 list = eina_list_append(list, rel);
5908 }
5909
5910 rel_obj = elm_object_focus_next_object_get(obj, ELM_FOCUS_PREVIOUS);
5911 if (eo_isa(rel_obj, ELM_INTERFACE_ATSPI_ACCESSIBLE_MIXIN))
5912 {
5913 rel = _relation_new(ELM_ATSPI_RELATION_FLOWS_FROM, rel_obj);
5914 list = eina_list_append(list, rel);
5915 }
5916
5917 return list;
5918}
5919
5920EOLIAN static void 5890EOLIAN static void
5921_elm_widget_item_elm_interface_atspi_component_extents_get(Eo *obj EINA_UNUSED, Elm_Widget_Item_Data *sd EINA_UNUSED, Eina_Bool screen_coords, int *x, int *y, int *w, int *h) 5891_elm_widget_item_elm_interface_atspi_component_extents_get(Eo *obj EINA_UNUSED, Elm_Widget_Item_Data *sd EINA_UNUSED, Eina_Bool screen_coords, int *x, int *y, int *w, int *h)
5922{ 5892{
diff --git a/src/lib/elm_widget.eo b/src/lib/elm_widget.eo
index f53d2c478..35bed55f1 100644
--- a/src/lib/elm_widget.eo
+++ b/src/lib/elm_widget.eo
@@ -771,7 +771,6 @@ abstract Elm.Widget (Evas.Object_Smart, Elm_Interface_Atspi_Accessible, Elm_Inte
771 Elm_Interface_Atspi_Accessible.children.get; 771 Elm_Interface_Atspi_Accessible.children.get;
772 Elm_Interface_Atspi_Accessible.parent.get; 772 Elm_Interface_Atspi_Accessible.parent.get;
773 Elm_Interface_Atspi_Accessible.attributes.get; 773 Elm_Interface_Atspi_Accessible.attributes.get;
774 Elm_Interface_Atspi_Accessible.relation_set.get;
775 Elm_Interface_Atspi_Component.focus_grab; 774 Elm_Interface_Atspi_Component.focus_grab;
776 } 775 }
777 events { 776 events {