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 Library 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
 * Library General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Library 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 "atknoopobjectfactory.h"
23
#include "atkregistry.h"
24

            
25
/**
26
 * AtkRegistry:
27
 *
28
 * An object used to store the GType of the
29
 * factories used to create an accessible object for an object of a
30
 * particular GType.
31
 *
32
 * The AtkRegistry is normally used to create appropriate ATK "peers"
33
 * for user interface components.  Application developers usually need
34
 * only interact with the AtkRegistry by associating appropriate ATK
35
 * implementation classes with GObject classes via the
36
 * atk_registry_set_factory_type call, passing the appropriate GType
37
 * for application custom widget classes.
38
 */
39

            
40
static AtkRegistry *default_registry = NULL;
41

            
42
static void atk_registry_init (AtkRegistry *instance,
43
                               AtkRegistryClass *klass);
44
static void atk_registry_finalize (GObject *instance);
45
static void atk_registry_class_init (AtkRegistryClass *klass);
46
static AtkRegistry *atk_registry_new (void);
47

            
48
static gpointer parent_class = NULL;
49

            
50
GType
51
atk_registry_get_type (void)
52
{
53
  static GType type = 0;
54

            
55
  if (!type)
56
    {
57
      static const GTypeInfo info = {
58
        sizeof (AtkRegistryClass),
59
        (GBaseInitFunc) NULL,                     /* base_init */
60
        (GBaseFinalizeFunc) NULL,                 /* base_finalize */
61
        (GClassInitFunc) atk_registry_class_init, /* class_init */
62
        (GClassFinalizeFunc) NULL,                /* class_finalize */
63
        NULL,                                     /* class_data */
64
        sizeof (AtkRegistry),                     /* instance size */
65
        0,                                        /* n_preallocs */
66
        (GInstanceInitFunc) atk_registry_init,    /* instance init */
67
        NULL                                      /* value table */
68
      };
69

            
70
      type = g_type_register_static (G_TYPE_OBJECT, "AtkRegistry", &info, 0);
71
    }
72

            
73
  return type;
74
}
75

            
76
static void
77
atk_registry_class_init (AtkRegistryClass *klass)
78
{
79
  GObjectClass *object_class = (GObjectClass *) klass;
80

            
81
  parent_class = g_type_class_peek_parent (klass);
82

            
83
  object_class->finalize = atk_registry_finalize;
84
}
85

            
86
static void
87
atk_registry_init (AtkRegistry *instance, AtkRegistryClass *klass)
88
{
89
  instance->factory_type_registry = g_hash_table_new ((GHashFunc) NULL,
90
                                                      (GEqualFunc) NULL);
91
  instance->factory_singleton_cache = g_hash_table_new ((GHashFunc) NULL,
92
                                                        (GEqualFunc) NULL);
93
}
94

            
95
static AtkRegistry *
96
atk_registry_new (void)
97
{
98
  GObject *object;
99

            
100
  object = g_object_new (ATK_TYPE_REGISTRY, NULL);
101

            
102
  g_return_val_if_fail (ATK_IS_REGISTRY (object), NULL);
103

            
104
  return (AtkRegistry *) object;
105
}
106

            
107
static void
108
atk_registry_finalize (GObject *object)
109
{
110
  AtkRegistry *registry = ATK_REGISTRY (object);
111

            
112
  g_hash_table_destroy (registry->factory_type_registry);
113
  g_hash_table_destroy (registry->factory_singleton_cache);
114

            
115
  G_OBJECT_CLASS (parent_class)->finalize (object);
116
}
117

            
118
/**
119
 * atk_registry_set_factory_type:
120
 * @registry: the #AtkRegistry in which to register the type association
121
 * @type: an #AtkObject type
122
 * @factory_type: an #AtkObjectFactory type to associate with @type.  Must
123
 * implement AtkObject appropriate for @type.
124
 *
125
 * Associate an #AtkObjectFactory subclass with a #GType. Note:
126
 * The associated @factory_type will thereafter be responsible for
127
 * the creation of new #AtkObject implementations for instances
128
 * appropriate for @type.
129
 **/
130
void
131
atk_registry_set_factory_type (AtkRegistry *registry,
132
                               GType type,
133
                               GType factory_type)
134
{
135
  GType old_type;
136
  gpointer value;
137
  AtkObjectFactory *old_factory;
138

            
139
  g_return_if_fail (ATK_IS_REGISTRY (registry));
140

            
141
  value = g_hash_table_lookup (registry->factory_type_registry,
142
                               (gpointer) type);
143
  old_type = (GType) value;
144
  if (old_type && old_type != factory_type)
145
    {
146
      g_hash_table_remove (registry->factory_type_registry,
147
                           (gpointer) type);
148
      /*
149
       * If the old factory was created, notify it that it has
150
       * been replaced, then free it.
151
       */
152
      old_factory = g_hash_table_lookup (registry->factory_singleton_cache,
153
                                         (gpointer) old_type);
154
      if (old_factory)
155
        {
156
          atk_object_factory_invalidate (old_factory);
157
          g_type_free_instance ((GTypeInstance *) old_factory);
158
        }
159
    }
160
  g_hash_table_insert (registry->factory_type_registry,
161
                       (gpointer) type,
162
                       (gpointer) factory_type);
163
}
164

            
165
/**
166
 * atk_registry_get_factory_type:
167
 * @registry: an #AtkRegistry
168
 * @type: a #GType with which to look up the associated #AtkObjectFactory
169
 * subclass
170
 *
171
 * Provides a #GType indicating the #AtkObjectFactory subclass
172
 * associated with @type.
173
 *
174
 * Returns: a #GType associated with type @type
175
 **/
176
GType
177
atk_registry_get_factory_type (AtkRegistry *registry,
178
                               GType type)
179
{
180
  GType factory_type;
181
  gpointer value;
182

            
183
  /*
184
   * look up factory type in first hash;
185
   * if there isn't an explicitly registered factory type,
186
   * try inheriting one...
187
   */
188
  do
189
    {
190
      value =
191
          g_hash_table_lookup (registry->factory_type_registry,
192
                               (gpointer) type);
193
      type = g_type_parent (type);
194
      if (type == G_TYPE_INVALID)
195
        {
196
          break;
197
        }
198
    }
199
  while (value == NULL);
200

            
201
  factory_type = (GType) value;
202
  return factory_type;
203
}
204

            
205
/**
206
 * atk_registry_get_factory:
207
 * @registry: an #AtkRegistry
208
 * @type: a #GType with which to look up the associated #AtkObjectFactory
209
 *
210
 * Gets an #AtkObjectFactory appropriate for creating #AtkObjects
211
 * appropriate for @type.
212
 *
213
 * Returns: (transfer none): an #AtkObjectFactory appropriate for creating
214
 * #AtkObjects appropriate for @type.
215
 **/
216
AtkObjectFactory *
217
atk_registry_get_factory (AtkRegistry *registry,
218
                          GType type)
219
{
220
  gpointer factory_pointer = NULL;
221
  GType factory_type;
222

            
223
  factory_type = atk_registry_get_factory_type (registry, type);
224

            
225
  if (factory_type == G_TYPE_INVALID)
226
    {
227
      /* Factory type has not been specified for this object type */
228
      static AtkObjectFactory *default_factory = NULL;
229

            
230
      if (!default_factory)
231
        default_factory = atk_no_op_object_factory_new ();
232

            
233
      return default_factory;
234
    }
235

            
236
  /* ask second hashtable for instance of factory type */
237
  factory_pointer =
238
      g_hash_table_lookup (registry->factory_singleton_cache,
239
                           (gpointer) factory_type);
240

            
241
  /* if there isn't one already, create one and save it */
242
  if (factory_pointer == NULL)
243
    {
244
      factory_pointer = g_type_create_instance (factory_type);
245
      g_hash_table_insert (registry->factory_singleton_cache,
246
                           (gpointer) factory_type,
247
                           factory_pointer);
248
    }
249

            
250
  return ATK_OBJECT_FACTORY (factory_pointer);
251
}
252

            
253
/**
254
 * atk_get_default_registry:
255
 *
256
 * Gets a default implementation of the #AtkObjectFactory/type
257
 * registry.
258
 * Note: For most toolkit maintainers, this will be the correct
259
 * registry for registering new #AtkObject factories. Following
260
 * a call to this function, maintainers may call atk_registry_set_factory_type()
261
 * to associate an #AtkObjectFactory subclass with the GType of objects
262
 * for whom accessibility information will be provided.
263
 *
264
 * Returns: (transfer full): a default implementation of the
265
 * #AtkObjectFactory/type registry
266
 **/
267
AtkRegistry *
268
atk_get_default_registry (void)
269
{
270
  if (!default_registry)
271
    {
272
      default_registry = atk_registry_new ();
273
    }
274
  return default_registry;
275
}