evolution-3.6.4/e-util/e-event.c

No issues found

  1 /*
  2  * This program is free software; you can redistribute it and/or
  3  * modify it under the terms of the GNU Lesser General Public
  4  * License as published by the Free Software Foundation; either
  5  * version 2 of the License, or (at your option) version 3.
  6  *
  7  * This program is distributed in the hope that it will be useful,
  8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 10  * Lesser General Public License for more details.
 11  *
 12  * You should have received a copy of the GNU Lesser General Public
 13  * License along with the program; if not, see <http://www.gnu.org/licenses/>
 14  *
 15  *
 16  * Authors:
 17  *		Michael Zucchi <notzed@ximian.com>
 18  *
 19  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 20  *
 21  */
 22 
 23 #ifdef HAVE_CONFIG_H
 24 #include <config.h>
 25 #endif
 26 
 27 #include <string.h>
 28 #include <stdlib.h>
 29 
 30 #include <gtk/gtk.h>
 31 
 32 #include "e-event.h"
 33 
 34 #include <glib/gi18n.h>
 35 
 36 #define E_EVENT_GET_PRIVATE(obj) \
 37 	(G_TYPE_INSTANCE_GET_PRIVATE \
 38 	((obj), E_TYPE_EVENT, EEventPrivate))
 39 
 40 #define d(x)
 41 
 42 struct _event_node {
 43 	GSList *events;
 44 	gpointer data;
 45 	EEventItemsFunc freefunc;
 46 };
 47 
 48 struct _event_info {
 49 	struct _event_node *parent;
 50 	EEventItem *item;
 51 };
 52 
 53 struct _EEventPrivate {
 54 	GQueue events;
 55 	GSList *sorted;		/* sorted list of struct _event_info's */
 56 };
 57 
 58 G_DEFINE_TYPE (
 59 	EEvent,
 60 	e_event,
 61 	G_TYPE_OBJECT)
 62 
 63 static void
 64 event_finalize (GObject *object)
 65 {
 66 	EEvent *event = (EEvent *) object;
 67 	EEventPrivate *p = event->priv;
 68 
 69 	if (event->target)
 70 		e_event_target_free (event, event->target);
 71 
 72 	g_free (event->id);
 73 
 74 	while (!g_queue_is_empty (&p->events)) {
 75 		struct _event_node *node;
 76 
 77 		node = g_queue_pop_head (&p->events);
 78 
 79 		if (node->freefunc != NULL)
 80 			node->freefunc (event, node->events, node->data);
 81 
 82 		g_free (node);
 83 	}
 84 
 85 	g_slist_foreach (p->sorted, (GFunc) g_free, NULL);
 86 	g_slist_free (p->sorted);
 87 
 88 	/* Chain up to parent's finalize() method. */
 89 	G_OBJECT_CLASS (e_event_parent_class)->finalize (object);
 90 }
 91 
 92 static void
 93 event_target_free (EEvent *event,
 94                    EEventTarget *target)
 95 {
 96 	g_free (target);
 97 	g_object_unref (event);
 98 }
 99 
100 static void
101 e_event_class_init (EEventClass *class)
102 {
103 	GObjectClass *object_class;
104 
105 	g_type_class_add_private (class, sizeof (EEventPrivate));
106 
107 	object_class = G_OBJECT_CLASS (class);
108 	object_class->finalize = event_finalize;
109 
110 	class->target_free = event_target_free;
111 }
112 
113 static void
114 e_event_init (EEvent *event)
115 {
116 	event->priv = E_EVENT_GET_PRIVATE (event);
117 
118 	g_queue_init (&event->priv->events);
119 }
120 
121 /**
122  * e_event_construct:
123  * @ep: An instantiated but uninitialised EEvent.
124  * @id: Event manager id.
125  *
126  * Construct the base event instance with standard parameters.
127  *
128  * Return value: Returns @ep.
129  **/
130 EEvent *e_event_construct (EEvent *ep, const gchar *id)
131 {
132 	ep->id = g_strdup (id);
133 
134 	return ep;
135 }
136 
137 /**
138  * e_event_add_items:
139  * @event: An initialised EEvent structure.
140  * @items: A list of EEventItems event listeners to register on this event manager.
141  * @freefunc: A function called when the @items list is no longer needed.
142  * @data: callback data for @freefunc and for item event handlers.
143  *
144  * Adds @items to the list of events listened to on the event manager @event.
145  *
146  * Return value: An opaque key which can later be passed to remove_items.
147  **/
148 gpointer
149 e_event_add_items (EEvent *event,
150                    GSList *items,
151                    EEventItemsFunc freefunc,
152                    gpointer data)
153 {
154 	struct _event_node *node;
155 
156 	node = g_malloc (sizeof (*node));
157 	node->events = items;
158 	node->freefunc = freefunc;
159 	node->data = data;
160 
161 	g_queue_push_tail (&event->priv->events, node);
162 
163 	if (event->priv->sorted) {
164 		g_slist_foreach (event->priv->sorted, (GFunc) g_free, NULL);
165 		g_slist_free (event->priv->sorted);
166 		event->priv->sorted = NULL;
167 	}
168 
169 	return (gpointer) node;
170 }
171 
172 /**
173  * e_event_remove_items:
174  * @event: an #EEvent
175  * @handle: an opaque key returned by e_event_add_items()
176  *
177  * Remove items previously added.  They MUST have been previously
178  * added, and may only be removed once.
179  **/
180 void
181 e_event_remove_items (EEvent *event,
182                       gpointer handle)
183 {
184 	struct _event_node *node = handle;
185 
186 	g_queue_remove (&event->priv->events, node);
187 
188 	if (node->freefunc)
189 		node->freefunc (event, node->events, node->data);
190 	g_free (node);
191 
192 	if (event->priv->sorted) {
193 		g_slist_foreach (event->priv->sorted, (GFunc) g_free, NULL);
194 		g_slist_free (event->priv->sorted);
195 		event->priv->sorted = NULL;
196 	}
197 }
198 
199 static gint
200 ee_cmp (gconstpointer ap,
201         gconstpointer bp)
202 {
203 	gint a = ((struct _event_info **) ap)[0]->item->priority;
204 	gint b = ((struct _event_info **) bp)[0]->item->priority;
205 
206 	if (a < b)
207 		return 1;
208 	else if (a > b)
209 		return -1;
210 	else
211 		return 0;
212 }
213 
214 /**
215  * e_event_emit:
216  * @ee: An initialised EEvent, potentially with registered event listeners.
217  * @id: Event name.  This will be compared against EEventItem.id.
218  * @target: The target describing the event context.  This will be
219  * implementation defined.
220  *
221  * Emit an event.  @target will automatically be freed once its
222  * emission is complete.
223  **/
224 void
225 e_event_emit (EEvent *event,
226               const gchar *id,
227               EEventTarget *target)
228 {
229 	EEventPrivate *p = event->priv;
230 	GSList *events;
231 
232 	d (printf ("emit event %s\n", id));
233 
234 	if (event->target != NULL) {
235 		g_warning ("Event already in progress.\n");
236 		return;
237 	}
238 
239 	event->target = target;
240 	events = p->sorted;
241 	if (events == NULL) {
242 		GList *link = g_queue_peek_head_link (&p->events);
243 
244 		while (link != NULL) {
245 			struct _event_node *node = link->data;
246 			GSList *l = node->events;
247 
248 			for (; l; l = g_slist_next (l)) {
249 				struct _event_info *info;
250 
251 				info = g_malloc0 (sizeof (*info));
252 				info->parent = node;
253 				info->item = l->data;
254 				events = g_slist_prepend (events, info);
255 			}
256 
257 			link = g_list_next (link);
258 		}
259 
260 		p->sorted = events = g_slist_sort (events, ee_cmp);
261 	}
262 
263 	for (; events; events = g_slist_next (events)) {
264 		struct _event_info *info = events->data;
265 		EEventItem *item = info->item;
266 
267 		if (item->enable & target->mask)
268 			continue;
269 
270 		if (strcmp (item->id, id) == 0) {
271 			item->handle (event, item, info->parent->data);
272 
273 			if (item->type == E_EVENT_SINK)
274 				break;
275 		}
276 	}
277 
278 	e_event_target_free (event, target);
279 	event->target = NULL;
280 }
281 
282 /**
283  * e_event_target_new:
284  * @ep: An initialised EEvent instance.
285  * @type: type, up to implementor
286  * @size: The size of memory to allocate.  This must be >= sizeof(EEventTarget).
287  *
288  * Allocate a new event target suitable for this class.  It is up to
289  * the implementation to define the available target types and their
290  * structure.
291  **/
292 gpointer
293 e_event_target_new (EEvent *event,
294                     gint type,
295                     gsize size)
296 {
297 	EEventTarget *target;
298 
299 	if (size < sizeof (EEventTarget)) {
300 		g_warning ("Size is less than the size of EEventTarget\n");
301 		size = sizeof (EEventTarget);
302 	}
303 
304 	target = g_malloc0 (size);
305 	target->event = g_object_ref (event);
306 	target->type = type;
307 
308 	return target;
309 }
310 
311 /**
312  * e_event_target_free:
313  * @ep: An initialised EEvent instance on which this target was allocated.
314  * @o: The target to free.
315  *
316  * Free a target.  This invokes the virtual free method on the EEventClass.
317  **/
318 void
319 e_event_target_free (EEvent *event,
320                      gpointer object)
321 {
322 	EEventTarget *target = object;
323 
324 	E_EVENT_GET_CLASS (event)->target_free (event, target);
325 }
326 
327 /* ********************************************************************** */
328 
329 /* Event menu plugin handler */
330 
331 /*
332  * <e-plugin
333  *  class="org.gnome.mail.plugin.event:1.0"
334  *  id="org.gnome.mail.plugin.event.item:1.0"
335  *  type="shlib"
336  *  location="/opt/gnome2/lib/camel/1.0/libcamelimap.so"
337  *  name="imap"
338  *  description="IMAP4 and IMAP4v1 mail store">
339  *  <hook class="org.gnome.mail.eventMenu:1.0"
340  *      handler="HandleEvent">
341  *   <menu id="any" target="select">
342  *   <item
343  *    type="item|toggle|radio|image|submenu|bar"
344  *    active
345  *    path="foo/bar"
346  *    label="label"
347  *    icon="foo"
348  *    mask="select_one"
349  *    activate="ep_view_emacs"/>
350  *   </menu>
351  *  </hook>
352  *
353  * <hook class="org.gnome.evolution.mail.events:1.0">
354  * <event id=".folder.changed"
355  *  target=""
356  *  priority="0"
357  *  handle="gotevent"
358  *  enable="new"
359  *  />
360  * <event id=".message.read"
361  *  priority="0"
362  *  handle="gotevent"
363  *  mask="new"
364  *  />
365  * </hook>
366  *
367  */
368 
369 #define emph ((EEventHook *)eph)
370 
371 /* must have 1:1 correspondence with e-event types in order */
372 static const EPluginHookTargetKey emph_item_types[] = {
373 	{ "pass", E_EVENT_PASS },
374 	{ "sink", E_EVENT_SINK },
375 	{ NULL }
376 };
377 
378 G_DEFINE_TYPE (
379 	EEventHook,
380 	e_event_hook,
381 	E_TYPE_PLUGIN_HOOK)
382 
383 static void
384 emph_event_handle (EEvent *ee,
385                    EEventItem *item,
386                    gpointer data)
387 {
388 	EEventHook *hook = data;
389 
390 	/* FIXME We could/should just remove the items
391 	 *       we added to the event handler. */
392 	if (!hook->hook.plugin->enabled)
393 		return;
394 
395 	e_plugin_invoke (
396 		hook->hook.plugin, (gchar *) item->user_data, ee->target);
397 }
398 
399 static void
400 emph_free_item (EEventItem *item)
401 {
402 	g_free ((gchar *) item->id);
403 	g_free (item->user_data);
404 	g_free (item);
405 }
406 
407 static void
408 emph_free_items (EEvent *ee,
409                  GSList *items,
410                  gpointer data)
411 {
412 	/*EPluginHook *eph = data;*/
413 
414 	g_slist_foreach (items, (GFunc) emph_free_item, NULL);
415 	g_slist_free (items);
416 }
417 
418 static EEventItem *
419 emph_construct_item (EPluginHook *eph,
420                      xmlNodePtr root,
421                      EEventHookClass *class)
422 {
423 	EEventItem *item;
424 	EEventHookTargetMap *map;
425 	gchar *tmp;
426 
427 	item = g_malloc0 (sizeof (*item));
428 
429 	tmp = (gchar *) xmlGetProp (root, (const guchar *)"target");
430 	if (tmp == NULL)
431 		goto error;
432 	map = g_hash_table_lookup (class->target_map, tmp);
433 	xmlFree (tmp);
434 	if (map == NULL)
435 		goto error;
436 	item->target_type = map->id;
437 	item->type = e_plugin_hook_id (root, emph_item_types, "type");
438 	if (item->type == -1)
439 		item->type = E_EVENT_PASS;
440 	item->priority = e_plugin_xml_int (root, "priority", 0);
441 	item->id = e_plugin_xml_prop (root, "id");
442 	item->enable = e_plugin_hook_mask (root, map->mask_bits, "enable");
443 	item->user_data = e_plugin_xml_prop (root, "handle");
444 
445 	if (item->user_data == NULL || item->id == NULL)
446 		goto error;
447 
448 	item->handle = emph_event_handle;
449 
450 	return item;
451 error:
452 	emph_free_item (item);
453 	return NULL;
454 }
455 
456 static gint
457 emph_construct (EPluginHook *eph,
458                 EPlugin *ep,
459                 xmlNodePtr root)
460 {
461 	xmlNodePtr node;
462 	EEventHookClass *class;
463 	GSList *items = NULL;
464 
465 	d (printf ("loading event hook\n"));
466 
467 	if (((EPluginHookClass *) e_event_hook_parent_class)->
468 		construct (eph, ep, root) == -1)
469 		return -1;
470 
471 	class = E_EVENT_HOOK_GET_CLASS (eph);
472 	g_return_val_if_fail (class->event != NULL, -1);
473 
474 	node = root->children;
475 	while (node) {
476 		if (strcmp ((gchar *) node->name, "event") == 0) {
477 			EEventItem *item;
478 
479 			item = emph_construct_item (eph, node, class);
480 			if (item)
481 				items = g_slist_prepend (items, item);
482 		}
483 		node = node->next;
484 	}
485 
486 	eph->plugin = ep;
487 
488 	if (items)
489 		e_event_add_items (class->event, items, emph_free_items, eph);
490 
491 	return 0;
492 }
493 
494 static void
495 e_event_hook_class_init (EEventHookClass *class)
496 {
497 	EPluginHookClass *plugin_hook_class;
498 
499 	plugin_hook_class = E_PLUGIN_HOOK_CLASS (class);
500 	plugin_hook_class->id = "org.gnome.evolution.event:1.0";
501 	plugin_hook_class->construct = emph_construct;
502 
503 	class->target_map = g_hash_table_new (g_str_hash, g_str_equal);
504 }
505 
506 static void
507 e_event_hook_init (EEventHook *hook)
508 {
509 }
510 
511 /**
512  * e_event_hook_class_add_target_map:
513  * @class: The derived EEventHook class.
514  * @map: A map used to describe a single EEventTarget type for this
515  * class.
516  *
517  * Add a target map to a concrete derived class of EEvent.  The target
518  * map enumerates a single target type and th eenable mask bit names,
519  * so that the type can be loaded automatically by the base EEvent class.
520  **/
521 void e_event_hook_class_add_target_map (EEventHookClass *class,
522                                         const EEventHookTargetMap *map)
523 {
524 	g_hash_table_insert (
525 		class->target_map, (gpointer) map->type, (gpointer) map);
526 }