source: trunk/third/bonobo/bonobo/bonobo-event-source.c @ 16750

Revision 16750, 10.8 KB checked in by ghudson, 23 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r16749, which included commits to RCS files with non-trunk default branches.
Line 
1/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2/*
3 * bonobo-event-source.c: Generic event emitter.
4 *
5 * Author:
6 *      Alex Graveley (alex@helixcode.com)
7 *      Iain Holmes   (iain@helixcode.com)
8 *      docs, Miguel de Icaza (miguel@helixcode.com)
9 *
10 * Copyright (C) 2000, Helix Code, Inc.
11 */
12#include <config.h>
13#include <gtk/gtksignal.h>
14#include <bonobo/bonobo-listener.h>
15#include <bonobo/bonobo-exception.h>
16#include <bonobo/bonobo-event-source.h>
17#include <bonobo/bonobo-running-context.h>
18#include <time.h>
19
20#define PARENT_TYPE BONOBO_X_OBJECT_TYPE
21
22static GtkObjectClass *bonobo_event_source_parent_class;
23
24struct _BonoboEventSourcePrivate {
25        GSList  *listeners;  /* CONTAINS: ListenerDesc* */
26        gboolean ignore;
27        gint     counter;    /* to create unique listener Ids */
28};
29
30typedef struct {
31        Bonobo_Listener listener;
32        Bonobo_EventSource_ListenerId id;
33        gchar **event_masks; /* send all events if NULL */
34} ListenerDesc;
35
36/*
37 * tries to make a unique connection Id. Adding the time make an
38 * accidental remove of a listener more unlikely.
39 */
40static Bonobo_EventSource_ListenerId
41create_listener_id (BonoboEventSource *source)
42{
43        guint32 id;
44
45        source->priv->counter = source->priv->counter++ & 0x0000ffff;
46
47        if (!source->priv->counter) source->priv->counter++;
48
49        id = source->priv->counter | (time(NULL) << 16);
50
51        return id;
52}
53
54static inline BonoboEventSource *
55bonobo_event_source_from_servant (PortableServer_Servant servant)
56{
57        return BONOBO_EVENT_SOURCE (bonobo_object_from_servant (servant));
58}
59
60static void
61desc_free (ListenerDesc *desc, CORBA_Environment *ev)
62{
63        if (desc) {
64                g_strfreev (desc->event_masks);
65                bonobo_object_release_unref (desc->listener, ev);
66                g_free (desc);
67        }
68}
69
70static Bonobo_EventSource_ListenerId
71impl_Bonobo_EventSource_addListenerWithMask (PortableServer_Servant servant,
72                                             const Bonobo_Listener  l,
73                                             const CORBA_char      *event_mask,
74                                             CORBA_Environment     *ev)
75{
76        BonoboEventSource *event_source;
77        ListenerDesc      *desc;
78
79        g_return_val_if_fail (!CORBA_Object_is_nil (l, ev), 0);
80
81        event_source = bonobo_event_source_from_servant (servant);
82
83        if (event_source->priv->ignore) /* Hook for running context */
84                bonobo_running_context_ignore_object (l);
85
86        desc = g_new0 (ListenerDesc, 1);
87        desc->listener = bonobo_object_dup_ref (l, ev);
88        desc->id = create_listener_id (event_source);
89
90        if (event_mask)
91                desc->event_masks = g_strsplit (event_mask, ",", 0);
92
93        event_source->priv->listeners = g_slist_prepend (
94                event_source->priv->listeners, desc);
95
96        return desc->id;
97}
98
99static Bonobo_EventSource_ListenerId
100impl_Bonobo_EventSource_addListener (PortableServer_Servant servant,
101                                     const Bonobo_Listener  l,
102                                     CORBA_Environment     *ev)
103{
104        return impl_Bonobo_EventSource_addListenerWithMask (
105                servant, l, NULL, ev);
106}
107
108static void
109impl_Bonobo_EventSource_removeListener (PortableServer_Servant servant,
110                                        const Bonobo_EventSource_ListenerId id,
111                                        CORBA_Environment     *ev)
112{
113        GSList                   *l, *next;
114        BonoboEventSourcePrivate *priv;
115
116        priv = bonobo_event_source_from_servant (servant)->priv;
117
118        for (l = priv->listeners; l; l = next) {
119                ListenerDesc *desc = l->data;
120
121                next = l->next;
122
123                if (desc->id == id) {
124                        priv->listeners = g_slist_remove_link (
125                                priv->listeners, l);
126                        g_slist_free_1 (l);
127                        desc_free (desc, ev);
128                        return;
129                }
130        }
131
132        CORBA_exception_set (ev, CORBA_USER_EXCEPTION,
133                             ex_Bonobo_EventSource_UnknownListener,
134                             NULL);
135}
136
137/*
138 * if the mask starts with a '=', we do exact compares - else we only check
139 * if the mask is a prefix of name.
140 */
141static gboolean
142event_match (const char *name, gchar **event_masks)
143{
144        int i = 0, j = 0;
145
146        while (event_masks[j]) {
147                char *mask = event_masks[j];
148               
149                if (mask [0] == '=')
150                        if (!strcmp (name, mask + 1))
151                                return TRUE;
152
153                while (name [i] && mask [i] && name [i] == mask [i])
154                        i++;
155               
156                if (mask [i] == '\0')
157                        return TRUE;
158
159                j++;
160        }
161       
162        return FALSE;
163}
164
165/**
166 * bonobo_event_source_notify_listeners:
167 * @event_source: the Event Source that will emit the event.
168 * @event_name: Name of the event being emitted
169 * @value: A CORBA_any value that contains the data that is passed to interested clients
170 * @opt_ev: A CORBA_Environment where a failure code can be returned, can be NULL.
171 *
172 * This will notify all clients that have registered with this EventSource
173 * (through the addListener or addListenerWithMask methods) of the availability
174 * of the event named @event_name.  The @value CORBA::any value is passed to
175 * all listeners.
176 *
177 * @event_name can not contain comma separators, as commas are used to
178 * separate the various event names.
179 */
180void
181bonobo_event_source_notify_listeners (BonoboEventSource *event_source,
182                                      const char        *event_name,
183                                      const CORBA_any   *value,
184                                      CORBA_Environment *opt_ev)
185{
186        GSList *l, *notify;
187        CORBA_Environment ev, *my_ev;
188
189        if (!opt_ev) {
190                CORBA_exception_init (&ev);
191                my_ev = &ev;
192        } else
193                my_ev = opt_ev;
194
195        notify = NULL;
196
197        for (l = event_source->priv->listeners; l; l = l->next) {
198                ListenerDesc *desc = (ListenerDesc *) l->data;
199
200                if (desc->event_masks == NULL ||
201                    event_match (event_name, desc->event_masks)) {
202                        CORBA_Object_duplicate (desc->listener, my_ev);
203                        notify = g_slist_prepend (notify, desc->listener);
204                }
205        }
206
207        bonobo_object_ref (BONOBO_OBJECT (event_source));
208
209        for (l = notify; l; l = l->next) {
210                Bonobo_Listener_event (l->data, event_name, value, my_ev);
211                CORBA_Object_release (l->data, my_ev);
212        }
213
214        bonobo_object_unref (BONOBO_OBJECT (event_source));
215
216        g_slist_free (notify);
217
218        if (!opt_ev)
219                CORBA_exception_free (&ev);
220}
221
222void
223bonobo_event_source_notify_listeners_full (BonoboEventSource *event_source,
224                                           const char        *path,
225                                           const char        *type,
226                                           const char        *subtype,
227                                           const CORBA_any   *value,                         
228                                           CORBA_Environment *opt_ev)
229{
230        char *event_name;
231
232        event_name = bonobo_event_make_name (path, type, subtype);
233
234        bonobo_event_source_notify_listeners (event_source, event_name,
235                                              value, opt_ev);
236
237        g_free (event_name);
238}
239
240static void
241bonobo_event_source_destroy (GtkObject *object)
242{
243        CORBA_Environment         ev;
244        BonoboEventSourcePrivate *priv;
245       
246        priv = BONOBO_EVENT_SOURCE (object)->priv;
247
248        CORBA_exception_init (&ev);
249       
250        while (priv->listeners) {
251                ListenerDesc *d = priv->listeners->data;
252
253                priv->listeners = g_slist_remove (
254                        priv->listeners, d);
255
256                desc_free (d, &ev);
257        }
258
259        CORBA_exception_free (&ev);
260
261        g_free (priv);
262
263        bonobo_event_source_parent_class->destroy (object);
264}
265
266static void
267bonobo_event_source_class_init (BonoboEventSourceClass *klass)
268{
269        GtkObjectClass *oclass = (GtkObjectClass *) klass;
270        POA_Bonobo_EventSource__epv *epv = &klass->epv;
271
272        bonobo_event_source_parent_class = gtk_type_class (PARENT_TYPE);
273
274        oclass->destroy = bonobo_event_source_destroy;
275
276        epv->addListener         = impl_Bonobo_EventSource_addListener;
277        epv->addListenerWithMask = impl_Bonobo_EventSource_addListenerWithMask;
278        epv->removeListener      = impl_Bonobo_EventSource_removeListener;
279}
280
281static void
282bonobo_event_source_init (GtkObject *object)
283{
284        BonoboEventSource *event_source;
285
286        event_source = BONOBO_EVENT_SOURCE (object);
287        event_source->priv = g_new0 (BonoboEventSourcePrivate, 1);
288        event_source->priv->listeners = NULL;
289}
290
291BONOBO_X_TYPE_FUNC_FULL (BonoboEventSource,
292                           Bonobo_EventSource,
293                           PARENT_TYPE,
294                           bonobo_event_source);
295
296/**
297 * bonobo_event_source_new:
298 *
299 * Creates a new BonoboEventSource object.  Typically this
300 * object will be exposed to clients through CORBA and they
301 * will register and unregister functions to be notified
302 * of events that this EventSource generates.
303 *
304 * To notify clients of an event, use the bonobo_event_source_notify_listeners()
305 * function.
306 *
307 * Returns: A new #BonoboEventSource server object.
308 */
309BonoboEventSource *
310bonobo_event_source_new (void)
311{
312        return gtk_type_new (BONOBO_EVENT_SOURCE_TYPE);
313}
314
315/**
316 * bonobo_event_source_ignore_listeners:
317 * @event_source:
318 *
319 *  Instructs the event source to de-register any listeners
320 * that are added from the global running context.
321 **/
322void
323bonobo_event_source_ignore_listeners (BonoboEventSource *event_source)
324{
325        g_return_if_fail (BONOBO_IS_EVENT_SOURCE (event_source));
326
327        event_source->priv->ignore = TRUE;
328}
329
330void
331bonobo_event_source_client_remove_listener (Bonobo_Unknown  object,
332                                            Bonobo_EventSource_ListenerId id,
333                                            CORBA_Environment *opt_ev)
334{
335        CORBA_Environment ev, *my_ev;
336        Bonobo_Unknown es;
337
338        g_return_if_fail (object != CORBA_OBJECT_NIL);
339        g_return_if_fail (id != 0);
340
341        if (!opt_ev) {
342                CORBA_exception_init (&ev);
343                my_ev = &ev;
344        } else
345                my_ev = opt_ev;
346
347        if (CORBA_Object_is_a (object, "IDL:Bonobo/Property:1.0", my_ev)) {
348
349                Bonobo_Property_removeListener (object, id, my_ev);
350
351        } else {
352
353                es = Bonobo_Unknown_queryInterface (object,
354                       "IDL:Bonobo/EventSource:1.0", my_ev);
355
356                if (BONOBO_EX(my_ev) || !es)
357                        goto remove_listener_end;
358
359                Bonobo_EventSource_removeListener (es, id, my_ev);
360
361                Bonobo_Unknown_unref (es, my_ev);
362        }
363
364 remove_listener_end:
365
366        if (!opt_ev) {
367                if (BONOBO_EX (my_ev))
368                        g_warning ("remove_listener failed '%s'",
369                                   bonobo_exception_get_text (my_ev));
370                CORBA_exception_free (&ev);
371        }
372}
373
374Bonobo_EventSource_ListenerId
375bonobo_event_source_client_add_listener (Bonobo_Unknown object,
376                                         BonoboListenerCallbackFn event_callback,
377                                         const char *opt_mask,
378                                         CORBA_Environment *opt_ev,
379                                         gpointer user_data)
380{
381        CORBA_Environment ev, *my_ev;
382        BonoboListener *listener = NULL;
383        Bonobo_Listener corba_listener;
384        Bonobo_EventSource_ListenerId id = 0;
385        Bonobo_Unknown es;
386
387        g_return_val_if_fail (object != CORBA_OBJECT_NIL, 0);
388        g_return_val_if_fail (event_callback != NULL, 0);
389       
390        if (!opt_ev) {
391                CORBA_exception_init (&ev);
392                my_ev = &ev;
393        } else
394                my_ev = opt_ev;
395
396        if (CORBA_Object_is_a (object, "IDL:Bonobo/Property:1.0", my_ev)) {
397                if (!(listener = bonobo_listener_new (event_callback,
398                                                      user_data)))
399                        goto add_listener_end;
400             
401                corba_listener = BONOBO_OBJREF (listener);
402
403                id = Bonobo_Property_addListener (object, corba_listener,
404                                                  my_ev);
405
406                bonobo_object_unref (BONOBO_OBJECT (listener));
407
408        } else {
409                es = Bonobo_Unknown_queryInterface (object,
410                        "IDL:Bonobo/EventSource:1.0", my_ev);
411
412                if (BONOBO_EX(my_ev) || !es)
413                        goto add_listener_end;
414
415                if (!(listener = bonobo_listener_new (event_callback,
416                                                      user_data)))
417                        goto add_listener_end;
418
419                corba_listener = BONOBO_OBJREF (listener);
420       
421                if (opt_mask)
422                        id = Bonobo_EventSource_addListenerWithMask (es,
423                                corba_listener, opt_mask, my_ev);
424                else
425                        id = Bonobo_EventSource_addListener (es,
426                                corba_listener, my_ev);
427
428                bonobo_object_unref (BONOBO_OBJECT (listener));
429                Bonobo_Unknown_unref (es, my_ev);
430        }
431
432 add_listener_end:
433
434        if (!opt_ev) {
435                if (BONOBO_EX (my_ev))
436                        g_warning ("add_listener failed '%s'",
437                                   bonobo_exception_get_text (my_ev));
438                CORBA_exception_free (&ev);
439        }
440
441        return id;
442}
443
Note: See TracBrowser for help on using the repository browser.