1
/* ATK -  Accessibility Toolkit
2
 * Copyright 2001 Sun Microsystems Inc.
3
 *
4
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
6
 * License as published by the Free Software Foundation; either
7
 * version 2 of the License, or (at your option) any later version.
8
 *
9
 * This library is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
 * Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
15
 * License along with this library; if not, write to the
16
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17
 * Boston, MA 02111-1307, USA.
18
 */
19

            
20
#include "config.h"
21

            
22
#include "atkmarshal.h"
23
#include "atkutil.h"
24

            
25
/**
26
 * AtkUtil:
27
 *
28
 * A set of ATK utility functions for event and toolkit support.
29
 *
30
 * A set of ATK utility functions which are used to support event
31
 * registration of various types, and obtaining the 'root' accessible
32
 * of a process and information about the current ATK implementation
33
 * and toolkit version.
34
 */
35

            
36
static void atk_util_class_init (AtkUtilClass *klass);
37

            
38
static AtkObject *previous_focus_object = NULL;
39

            
40
typedef struct _AtkUtilListenerInfo AtkUtilListenerInfo;
41
struct _AtkUtilListenerInfo
42
{
43
  gint key;
44
  guint signal_id;
45
  gulong hook_id;
46
};
47
static GHashTable *listener_list = NULL;
48

            
49
GType
50
12857
atk_util_get_type (void)
51
{
52
  static GType type = 0;
53

            
54
12857
  if (!type)
55
    {
56
      static const GTypeInfo typeInfo = {
57
        sizeof (AtkUtilClass),
58
        (GBaseInitFunc) NULL,
59
        (GBaseFinalizeFunc) NULL,
60
        (GClassInitFunc) atk_util_class_init,
61
        (GClassFinalizeFunc) NULL,
62
        NULL,
63
        sizeof (AtkUtil),
64
        0,
65
        (GInstanceInitFunc) NULL,
66
      };
67
161
      type = g_type_register_static (G_TYPE_OBJECT, "AtkUtil", &typeInfo, 0);
68
    }
69
12857
  return type;
70
}
71

            
72
/*
73
 * This file supports the addition and removal of multiple focus handlers
74
 * as long as they are all called in the same thread.
75
 */
76
static AtkEventListenerInit focus_tracker_init = (AtkEventListenerInit) NULL;
77

            
78
static gboolean init_done = FALSE;
79

            
80
/*
81
 * Array of FocusTracker structs
82
 */
83
static GArray *trackers = NULL;
84
static guint global_index = 0;
85

            
86
typedef struct _FocusTracker FocusTracker;
87

            
88
struct _FocusTracker
89
{
90
  guint index;
91
  AtkEventListener func;
92
};
93

            
94
/**
95
 * atk_focus_tracker_init: (skip)
96
 * @init: Function to be called for focus tracker initialization
97
 *
98
 * Specifies the function to be called for focus tracker initialization.
99
 * This function should be called by an implementation of the
100
 * ATK interface if any specific work needs to be done to enable
101
 * focus tracking.
102
 *
103
 * Deprecated: 2.9.4: Focus tracking has been dropped as a feature
104
 * to be implemented by ATK itself.
105
 *
106
 **/
107
void
108
atk_focus_tracker_init (AtkEventListenerInit init)
109
{
110
  if (!focus_tracker_init)
111
    focus_tracker_init = init;
112
}
113

            
114
/**
115
 * atk_add_focus_tracker: (skip)
116
 * @focus_tracker: Function to be added to the list of functions to be called
117
 * when an object receives focus.
118
 *
119
 * Adds the specified function to the list of functions to be called
120
 * when an object receives focus.
121
 *
122
 * Deprecated: 2.9.4: Focus tracking has been dropped as a feature
123
 * to be implemented by ATK itself. If you need focus tracking on your
124
 * implementation, subscribe to the #AtkObject::state-change "focused" signal.
125
 *
126
 * Returns: added focus tracker id, or 0 on failure.
127
 **/
128
guint
129
161
atk_add_focus_tracker (AtkEventListener focus_tracker)
130
{
131
161
  g_return_val_if_fail (focus_tracker, 0);
132

            
133
161
  if (!init_done)
134
    {
135
161
      if (focus_tracker_init)
136
        {
137
          focus_tracker_init ();
138
        }
139
161
      trackers = g_array_sized_new (FALSE, TRUE, sizeof (FocusTracker), 0);
140
161
      init_done = TRUE;
141
    }
142
161
  if (init_done)
143
    {
144
      FocusTracker item;
145

            
146
161
      item.index = ++global_index;
147
161
      item.func = focus_tracker;
148
161
      trackers = g_array_append_val (trackers, item);
149
161
      return global_index;
150
    }
151
  else
152
    {
153
      return 0;
154
    }
155
}
156

            
157
/**
158
 * atk_remove_focus_tracker:
159
 * @tracker_id: the id of the focus tracker to remove
160
 *
161
 * Removes the specified focus tracker from the list of functions
162
 * to be called when any object receives focus.
163
 *
164
 * Deprecated: 2.9.4: Focus tracking has been dropped as a feature
165
 *   to be implemented by ATK itself. If you need focus tracking on your
166
 *   implementation, subscribe to the #AtkObject::state-change "focused"
167
 *   signal.
168
 */
169
void
170
161
atk_remove_focus_tracker (guint tracker_id)
171
{
172
  FocusTracker *item;
173
  guint i;
174

            
175
161
  if (trackers == NULL)
176
    return;
177

            
178
161
  if (tracker_id == 0)
179
    return;
180

            
181
161
  for (i = 0; i < trackers->len; i++)
182
    {
183
161
      item = &g_array_index (trackers, FocusTracker, i);
184
161
      if (item->index == tracker_id)
185
        {
186
161
          trackers = g_array_remove_index (trackers, i);
187
161
          break;
188
        }
189
    }
190
}
191

            
192
/**
193
 * atk_focus_tracker_notify:
194
 * @object: an #AtkObject
195
 *
196
 * Cause the focus tracker functions which have been specified to be
197
 * executed for the object.
198
 *
199
 * Deprecated: 2.9.4: Focus tracking has been dropped as a feature
200
 * to be implemented by ATK itself. As #AtkObject::focus-event was
201
 * deprecated in favor of a #AtkObject::state-change signal, in order
202
 * to notify a focus change on your implementation, you can use
203
 * atk_object_notify_state_change() instead.
204
 **/
205
void
206
atk_focus_tracker_notify (AtkObject *object)
207
{
208
  FocusTracker *item;
209
  guint i;
210

            
211
  if (trackers == NULL)
212
    return;
213

            
214
  if (object == previous_focus_object)
215
    return;
216
  else
217
    {
218
      if (previous_focus_object)
219
        g_object_unref (previous_focus_object);
220

            
221
      previous_focus_object = object;
222
      if (object)
223
        {
224
          g_object_ref (object);
225

            
226
          for (i = 0; i < trackers->len; i++)
227
            {
228
              item = &g_array_index (trackers, FocusTracker, i);
229
              g_return_if_fail (item != NULL);
230
              item->func (object);
231
            }
232
        }
233
    }
234
}
235

            
236
static guint
237
5957
add_listener (GSignalEmissionHook listener,
238
              const gchar *object_type,
239
              const gchar *signal_name,
240
              const gchar *detail_string,
241
              const gchar *hook_data)
242
{
243
  GType type;
244
  guint signal_id;
245
5957
  gint rc = 0;
246
  static gint listener_idx = 1;
247
5957
  GQuark detail_quark = 0;
248

            
249
5957
  type = g_type_from_name (object_type);
250
5957
  if (type)
251
    {
252
5957
      signal_id = g_signal_lookup (signal_name, type);
253
5957
      detail_quark = g_quark_from_string (detail_string);
254

            
255
5957
      if (signal_id > 0)
256
        {
257
          AtkUtilListenerInfo *listener_info;
258

            
259
5957
          rc = listener_idx;
260

            
261
5957
          listener_info = g_new (AtkUtilListenerInfo, 1);
262
5957
          listener_info->key = listener_idx;
263
5957
          listener_info->hook_id =
264
5957
              g_signal_add_emission_hook (signal_id, detail_quark, listener,
265
5957
                                          g_strdup (hook_data),
266
                                          (GDestroyNotify) g_free);
267
5957
          listener_info->signal_id = signal_id;
268

            
269
5957
          g_hash_table_insert (listener_list, &(listener_info->key), listener_info);
270
5957
          listener_idx++;
271
        }
272
      else
273
        {
274
          g_debug ("Signal type %s not supported\n", signal_name);
275
        }
276
    }
277
  else
278
    {
279
      g_warning ("Invalid object type %s\n", object_type);
280
    }
281
5957
  return rc;
282
}
283

            
284
static guint
285
6118
atk_util_real_add_global_event_listener (GSignalEmissionHook listener,
286
                                         const gchar *event_type)
287
{
288
6118
  guint rc = 0;
289
  gchar **split_string;
290
  guint length;
291

            
292
6118
  split_string = g_strsplit (event_type, ":", 0);
293
6118
  length = g_strv_length (split_string);
294

            
295
6118
  if ((length == 3) || (length == 4))
296
5957
    rc = add_listener (listener, split_string[1], split_string[2],
297
5957
                       split_string[3], event_type);
298

            
299
6118
  g_strfreev (split_string);
300

            
301
6118
  return rc;
302
}
303

            
304
static void
305
5957
atk_util_real_remove_global_event_listener (guint remove_listener)
306
{
307
5957
  if (remove_listener > 0)
308
    {
309
      AtkUtilListenerInfo *listener_info;
310
5957
      gint tmp_idx = remove_listener;
311

            
312
      listener_info = (AtkUtilListenerInfo *)
313
5957
          g_hash_table_lookup (listener_list, &tmp_idx);
314

            
315
5957
      if (listener_info != NULL)
316
        {
317
          /* Hook id of 0 and signal id of 0 are invalid */
318
5957
          if (listener_info->hook_id != 0 && listener_info->signal_id != 0)
319
            {
320
              /* Remove the emission hook */
321
5957
              g_signal_remove_emission_hook (listener_info->signal_id,
322
                                             listener_info->hook_id);
323

            
324
              /* Remove the element from the hash */
325
5957
              g_hash_table_remove (listener_list, &tmp_idx);
326
            }
327
          else
328
            {
329
              g_warning ("Invalid listener hook_id %ld or signal_id %d\n",
330
                         listener_info->hook_id, listener_info->signal_id);
331
            }
332
        }
333
      else
334
        {
335
          g_warning ("No listener with the specified listener id %d",
336
                     remove_listener);
337
        }
338
    }
339
  else
340
    {
341
      g_warning ("Invalid listener_id %d", remove_listener);
342
    }
343
5957
}
344

            
345
/**
346
 * atk_add_global_event_listener: (skip)
347
 * @listener: the listener to notify
348
 * @event_type: the type of event for which notification is requested
349
 *
350
 * Adds the specified function to the list of functions to be called
351
 * when an ATK event of type event_type occurs.
352
 *
353
 * The format of event_type is the following:
354
 *  "ATK:&lt;atk_type&gt;:&lt;atk_event&gt;:&lt;atk_event_detail&gt;
355
 *
356
 * Where "ATK" works as the namespace, &lt;atk_interface&gt; is the name of
357
 * the ATK type (interface or object), &lt;atk_event&gt; is the name of the
358
 * signal defined on that interface and &lt;atk_event_detail&gt; is the
359
 * gsignal detail of that signal. You can find more info about gsignal
360
 * details here:
361
 * http://developer.gnome.org/gobject/stable/gobject-Signals.html
362
 *
363
 * The first three parameters are mandatory. The last one is optional.
364
 *
365
 * For example:
366
 *   ATK:AtkObject:state-change
367
 *   ATK:AtkText:text-selection-changed
368
 *   ATK:AtkText:text-insert:system
369
 *
370
 * Toolkit implementor note: ATK provides a default implementation for
371
 * this virtual method. ATK implementors are discouraged from
372
 * reimplementing this method.
373
 *
374
 * Toolkit implementor note: this method is not intended to be used by
375
 * ATK implementors but by ATK consumers.
376
 *
377
 * ATK consumers note: as this method adds a listener for a given ATK
378
 * type, that type should be already registered on the GType system
379
 * before calling this method. A simple way to do that is creating an
380
 * instance of #AtkNoOpObject. This class implements all ATK
381
 * interfaces, so creating the instance will register all ATK types as
382
 * a collateral effect.
383
 *
384
 * Returns: added event listener id, or 0 on failure.
385
 **/
386
guint
387
6118
atk_add_global_event_listener (GSignalEmissionHook listener,
388
                               const gchar *event_type)
389
{
390
  guint retval;
391
6118
  AtkUtilClass *klass = g_type_class_ref (ATK_TYPE_UTIL);
392

            
393
6118
  if (klass->add_global_event_listener)
394
    {
395
6118
      retval = klass->add_global_event_listener (listener, event_type);
396
    }
397
  else
398
    {
399
      retval = 0;
400
    }
401
6118
  g_type_class_unref (klass);
402

            
403
6118
  return retval;
404
}
405

            
406
/**
407
 * atk_remove_global_event_listener:
408
 * @listener_id: the id of the event listener to remove
409
 *
410
 * @listener_id is the value returned by #atk_add_global_event_listener
411
 * when you registered that event listener.
412
 *
413
 * Toolkit implementor note: ATK provides a default implementation for
414
 * this virtual method. ATK implementors are discouraged from
415
 * reimplementing this method.
416
 *
417
 * Toolkit implementor note: this method is not intended to be used by
418
 * ATK implementors but by ATK consumers.
419
 *
420
 * Removes the specified event listener
421
 **/
422
void
423
5957
atk_remove_global_event_listener (guint listener_id)
424
{
425
5957
  AtkUtilClass *klass = g_type_class_peek (ATK_TYPE_UTIL);
426

            
427
5957
  if (klass && klass->remove_global_event_listener)
428
5957
    klass->remove_global_event_listener (listener_id);
429
5957
}
430

            
431
/**
432
 * atk_add_key_event_listener: (skip)
433
 * @listener: the listener to notify
434
 * @data: a #gpointer that points to a block of data that should be sent to the registered listeners,
435
 *        along with the event notification, when it occurs.
436
 *
437
 * Adds the specified function to the list of functions to be called
438
 *        when a key event occurs.  The @data element will be passed to the
439
 *        #AtkKeySnoopFunc (@listener) as the @func_data param, on notification.
440
 *
441
 * Returns: added event listener id, or 0 on failure.
442
 **/
443
guint
444
161
atk_add_key_event_listener (AtkKeySnoopFunc listener, gpointer data)
445
{
446
  guint retval;
447
161
  AtkUtilClass *klass = g_type_class_peek (ATK_TYPE_UTIL);
448
161
  if (klass && klass->add_key_event_listener)
449
    {
450
      retval = klass->add_key_event_listener (listener, data);
451
    }
452
  else
453
    {
454
161
      retval = 0;
455
    }
456

            
457
161
  return retval;
458
}
459

            
460
/**
461
 * atk_remove_key_event_listener:
462
 * @listener_id: the id of the event listener to remove
463
 *
464
 * @listener_id is the value returned by #atk_add_key_event_listener
465
 * when you registered that event listener.
466
 *
467
 * Removes the specified event listener.
468
 **/
469
void
470
atk_remove_key_event_listener (guint listener_id)
471
{
472
  AtkUtilClass *klass = g_type_class_peek (ATK_TYPE_UTIL);
473

            
474
  if (klass->remove_key_event_listener)
475
    klass->remove_key_event_listener (listener_id);
476
}
477

            
478
/**
479
 * atk_get_root:
480
 *
481
 * Gets the root accessible container for the current application.
482
 *
483
 * Returns: (transfer none): the root accessible container for the current
484
 * application
485
 **/
486
AtkObject *
487
322
atk_get_root (void)
488
{
489
322
  AtkUtilClass *klass = g_type_class_ref (ATK_TYPE_UTIL);
490
  AtkObject *retval;
491

            
492
322
  if (klass->get_root)
493
    {
494
322
      retval = klass->get_root ();
495
    }
496
  else
497
    {
498
      retval = NULL;
499
    }
500
322
  g_type_class_unref (klass);
501

            
502
322
  return retval;
503
}
504

            
505
/**
506
 * atk_get_focus_object:
507
 *
508
 * Gets the currently focused object.
509
 *
510
 * Since: 1.6
511
 *
512
 * Returns: (transfer none): the currently focused object for the current
513
 * application
514
 **/
515
AtkObject *
516
atk_get_focus_object (void)
517
{
518
  return previous_focus_object;
519
}
520

            
521
/**
522
 * atk_get_toolkit_name:
523
 *
524
 * Gets name string for the GUI toolkit implementing ATK for this application.
525
 *
526
 * Returns: name string for the GUI toolkit implementing ATK for this application
527
 **/
528
const gchar *
529
137
atk_get_toolkit_name (void)
530
{
531
  const gchar *retval;
532
137
  AtkUtilClass *klass = g_type_class_ref (ATK_TYPE_UTIL);
533
137
  if (klass->get_toolkit_name)
534
    {
535
137
      retval = klass->get_toolkit_name ();
536
    }
537
  else
538
    {
539
      retval = NULL;
540
    }
541
137
  g_type_class_unref (klass);
542

            
543
137
  return retval;
544
}
545

            
546
/**
547
 * atk_get_toolkit_version:
548
 *
549
 * Gets version string for the GUI toolkit implementing ATK for this application.
550
 *
551
 * Returns: version string for the GUI toolkit implementing ATK for this application
552
 **/
553
const gchar *
554
1
atk_get_toolkit_version (void)
555
{
556
  const gchar *retval;
557
1
  AtkUtilClass *klass = g_type_class_ref (ATK_TYPE_UTIL);
558
1
  if (klass->get_toolkit_version)
559
    {
560
      retval = klass->get_toolkit_version ();
561
    }
562
  else
563
    {
564
1
      retval = NULL;
565
    }
566
1
  g_type_class_unref (klass);
567

            
568
1
  return retval;
569
}
570

            
571
/**
572
 * atk_get_version:
573
 *
574
 * Gets the current version for ATK.
575
 *
576
 * Returns: version string for ATK
577
 *
578
 * Since: 1.20
579
 */
580
const gchar *
581
atk_get_version (void)
582
{
583
  return ATK_VERSION;
584
}
585

            
586
static void
587
161
atk_util_class_init (AtkUtilClass *klass)
588
{
589
161
  klass->add_global_event_listener = atk_util_real_add_global_event_listener;
590
161
  klass->remove_global_event_listener = atk_util_real_remove_global_event_listener;
591
161
  klass->get_root = NULL;
592
161
  klass->get_toolkit_name = NULL;
593
161
  klass->get_toolkit_version = NULL;
594

            
595
161
  listener_list = g_hash_table_new_full (g_int_hash, g_int_equal, NULL,
596
                                         g_free);
597
161
}