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 }