No issues found
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * This program is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2 of the License, or (at your option) version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with the program; if not, see <http://www.gnu.org/licenses/>
15 *
16 *
17 * Authors:
18 * Not Zed <notzed@lostzed.mmc.com.au>
19 * Jeffrey Stedfast <fejj@ximian.com>
20 *
21 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
22 *
23 */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <string.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <unistd.h>
33 #include <fcntl.h>
34 #include <errno.h>
35
36 #include <glib/gstdio.h>
37
38 #include <gtk/gtk.h>
39
40 #include <glib/gi18n.h>
41
42 #include <libedataserver/libedataserver.h>
43
44 #include "libevolution-utils/e-alert-dialog.h"
45 #include "libevolution-utils/e-xml-utils.h"
46
47 #include "e-filter-code.h"
48 #include "e-filter-color.h"
49 #include "e-filter-datespec.h"
50 #include "e-filter-file.h"
51 #include "e-filter-input.h"
52 #include "e-filter-int.h"
53 #include "e-filter-option.h"
54 #include "e-filter-rule.h"
55 #include "e-rule-context.h"
56
57 #define E_RULE_CONTEXT_GET_PRIVATE(obj) \
58 (G_TYPE_INSTANCE_GET_PRIVATE \
59 ((obj), E_TYPE_RULE_CONTEXT, ERuleContextPrivate))
60
61 struct _ERuleContextPrivate {
62 gint frozen;
63 };
64
65 enum {
66 RULE_ADDED,
67 RULE_REMOVED,
68 CHANGED,
69 LAST_SIGNAL
70 };
71
72 static guint signals[LAST_SIGNAL];
73
74 struct _revert_data {
75 GHashTable *rules;
76 gint rank;
77 };
78
79 G_DEFINE_TYPE (
80 ERuleContext,
81 e_rule_context,
82 G_TYPE_OBJECT)
83
84 static void
85 rule_context_set_error (ERuleContext *context,
86 gchar *error)
87 {
88 g_free (context->error);
89 context->error = error;
90 }
91
92 static void
93 new_rule_response (GtkWidget *dialog,
94 gint button,
95 ERuleContext *context)
96 {
97 if (button == GTK_RESPONSE_OK) {
98 EFilterRule *rule = g_object_get_data ((GObject *) dialog, "rule");
99 gchar *user = g_object_get_data ((GObject *) dialog, "path");
100 EAlert *alert = NULL;
101
102 if (!e_filter_rule_validate (rule, &alert)) {
103 e_alert_run_dialog (GTK_WINDOW (dialog), alert);
104 g_object_unref (alert);
105 return;
106 }
107
108 if (e_rule_context_find_rule (context, rule->name, rule->source)) {
109 e_alert_run_dialog_for_args ((GtkWindow *) dialog,
110 "filter:bad-name-notunique",
111 rule->name, NULL);
112
113 return;
114 }
115
116 g_object_ref (rule);
117 e_rule_context_add_rule (context, rule);
118 if (user)
119 e_rule_context_save (context, user);
120 }
121
122 gtk_widget_destroy (dialog);
123 }
124
125 static void
126 revert_rule_remove (gpointer key,
127 EFilterRule *rule,
128 ERuleContext *context)
129 {
130 e_rule_context_remove_rule (context, rule);
131 g_object_unref (rule);
132 }
133
134 static void
135 revert_source_remove (gpointer key,
136 struct _revert_data *rest_data,
137 ERuleContext *context)
138 {
139 g_hash_table_foreach (
140 rest_data->rules, (GHFunc) revert_rule_remove, context);
141 g_hash_table_destroy (rest_data->rules);
142 g_free (rest_data);
143 }
144
145 static guint
146 source_hashf (const gchar *a)
147 {
148 return (a != NULL) ? g_str_hash (a) : 0;
149 }
150
151 static gint
152 source_eqf (const gchar *a,
153 const gchar *b)
154 {
155 return (g_strcmp0 (a, b) == 0);
156 }
157
158 static void
159 free_part_set (struct _part_set_map *map)
160 {
161 g_free (map->name);
162 g_free (map);
163 }
164
165 static void
166 free_rule_set (struct _rule_set_map *map)
167 {
168 g_free (map->name);
169 g_free (map);
170 }
171
172 static void
173 rule_context_finalize (GObject *obj)
174 {
175 ERuleContext *context =(ERuleContext *) obj;
176
177 g_list_foreach (context->rule_set_list, (GFunc) free_rule_set, NULL);
178 g_list_free (context->rule_set_list);
179 g_hash_table_destroy (context->rule_set_map);
180
181 g_list_foreach (context->part_set_list, (GFunc) free_part_set, NULL);
182 g_list_free (context->part_set_list);
183 g_hash_table_destroy (context->part_set_map);
184
185 g_free (context->error);
186
187 g_list_foreach (context->parts, (GFunc) g_object_unref, NULL);
188 g_list_free (context->parts);
189
190 g_list_foreach (context->rules, (GFunc) g_object_unref, NULL);
191 g_list_free (context->rules);
192
193 G_OBJECT_CLASS (e_rule_context_parent_class)->finalize (obj);
194 }
195
196 static gint
197 rule_context_load (ERuleContext *context,
198 const gchar *system,
199 const gchar *user)
200 {
201 xmlNodePtr set, rule, root;
202 xmlDocPtr systemdoc, userdoc;
203 struct _part_set_map *part_map;
204 struct _rule_set_map *rule_map;
205
206 rule_context_set_error (context, NULL);
207
208 systemdoc = e_xml_parse_file (system);
209 if (systemdoc == NULL) {
210 gchar * err_msg;
211
212 err_msg = g_strdup_printf (
213 "Unable to load system rules '%s': %s",
214 system, g_strerror (errno));
215 g_warning ("%s: %s", G_STRFUNC, err_msg);
216 rule_context_set_error (context, err_msg);
217 /* no need to free err_msg here */
218 return -1;
219 }
220
221 root = xmlDocGetRootElement (systemdoc);
222 if (root == NULL || strcmp ((gchar *) root->name, "filterdescription")) {
223 gchar * err_msg;
224
225 err_msg = g_strdup_printf (
226 "Unable to load system rules '%s': "
227 "Invalid format", system);
228 g_warning ("%s: %s", G_STRFUNC, err_msg);
229 rule_context_set_error (context, err_msg);
230 /* no need to free err_msg here */
231 xmlFreeDoc (systemdoc);
232 return -1;
233 }
234 /* doesn't matter if this doens't exist */
235 userdoc = NULL;
236 if (g_file_test (user, G_FILE_TEST_IS_REGULAR))
237 userdoc = e_xml_parse_file (user);
238
239 /* now parse structure */
240 /* get rule parts */
241 set = root->children;
242 while (set) {
243 part_map = g_hash_table_lookup (context->part_set_map, set->name);
244 if (part_map) {
245 rule = set->children;
246 while (rule) {
247 if (!strcmp ((gchar *) rule->name, "part")) {
248 EFilterPart *part =
249 E_FILTER_PART (g_object_new (
250 part_map->type, NULL, NULL));
251
252 if (e_filter_part_xml_create (part, rule, context) == 0) {
253 part_map->append (context, part);
254 } else {
255 g_object_unref (part);
256 g_warning ("Cannot load filter part");
257 }
258 }
259 rule = rule->next;
260 }
261 } else if ((rule_map = g_hash_table_lookup (
262 context->rule_set_map, set->name))) {
263 rule = set->children;
264 while (rule) {
265 if (!strcmp ((gchar *) rule->name, "rule")) {
266 EFilterRule *part =
267 E_FILTER_RULE (g_object_new (
268 rule_map->type, NULL, NULL));
269
270 if (e_filter_rule_xml_decode (part, rule, context) == 0) {
271 part->system = TRUE;
272 rule_map->append (context, part);
273 } else {
274 g_object_unref (part);
275 g_warning ("Cannot load filter part");
276 }
277 }
278 rule = rule->next;
279 }
280 }
281 set = set->next;
282 }
283
284 /* now load actual rules */
285 if (userdoc) {
286 root = xmlDocGetRootElement (userdoc);
287 set = root ? root->children : NULL;
288 while (set) {
289 rule_map = g_hash_table_lookup (context->rule_set_map, set->name);
290 if (rule_map) {
291 rule = set->children;
292 while (rule) {
293 if (!strcmp ((gchar *) rule->name, "rule")) {
294 EFilterRule *part =
295 E_FILTER_RULE (g_object_new (
296 rule_map->type, NULL, NULL));
297
298 if (e_filter_rule_xml_decode (part, rule, context) == 0) {
299 rule_map->append (context, part);
300 } else {
301 g_object_unref (part);
302 g_warning ("Cannot load filter part");
303 }
304 }
305 rule = rule->next;
306 }
307 }
308 set = set->next;
309 }
310 }
311
312 xmlFreeDoc (userdoc);
313 xmlFreeDoc (systemdoc);
314
315 return 0;
316 }
317
318 static gint
319 rule_context_save (ERuleContext *context,
320 const gchar *user)
321 {
322 xmlDocPtr doc;
323 xmlNodePtr root, rules, work;
324 GList *l;
325 EFilterRule *rule;
326 struct _rule_set_map *map;
327 gint ret;
328
329 doc = xmlNewDoc ((xmlChar *)"1.0");
330 /* FIXME: set character encoding to UTF-8? */
331 root = xmlNewDocNode (doc, NULL, (xmlChar *)"filteroptions", NULL);
332 xmlDocSetRootElement (doc, root);
333 l = context->rule_set_list;
334 while (l) {
335 map = l->data;
336 rules = xmlNewDocNode (doc, NULL, (xmlChar *) map->name, NULL);
337 xmlAddChild (root, rules);
338 rule = NULL;
339 while ((rule = map->next (context, rule, NULL))) {
340 if (!rule->system) {
341 work = e_filter_rule_xml_encode (rule);
342 xmlAddChild (rules, work);
343 }
344 }
345 l = g_list_next (l);
346 }
347
348 ret = e_xml_save_file (user, doc);
349
350 xmlFreeDoc (doc);
351
352 return ret;
353 }
354
355 static gint
356 rule_context_revert (ERuleContext *context,
357 const gchar *user)
358 {
359 xmlNodePtr set, rule;
360 /*struct _part_set_map *part_map;*/
361 struct _rule_set_map *rule_map;
362 struct _revert_data *rest_data;
363 GHashTable *source_hash;
364 xmlDocPtr userdoc;
365 EFilterRule *frule;
366
367 rule_context_set_error (context, NULL);
368
369 userdoc = e_xml_parse_file (user);
370 if (userdoc == NULL)
371 /* clear out anythign we have? */
372 return 0;
373
374 source_hash = g_hash_table_new (
375 (GHashFunc) source_hashf,
376 (GCompareFunc) source_eqf);
377
378 /* setup stuff we have now */
379 /* Note that we assume there is only 1 set of rules in a given rule context,
380 * although other parts of the code dont assume this */
381 frule = NULL;
382 while ((frule = e_rule_context_next_rule (context, frule, NULL))) {
383 rest_data = g_hash_table_lookup (source_hash, frule->source);
384 if (rest_data == NULL) {
385 rest_data = g_malloc0 (sizeof (*rest_data));
386 rest_data->rules = g_hash_table_new (g_str_hash, g_str_equal);
387 g_hash_table_insert (source_hash, frule->source, rest_data);
388 }
389 g_hash_table_insert (rest_data->rules, frule->name, frule);
390 }
391
392 /* make what we have, match what we load */
393 set = xmlDocGetRootElement (userdoc);
394 set = set ? set->children : NULL;
395 while (set) {
396 rule_map = g_hash_table_lookup (context->rule_set_map, set->name);
397 if (rule_map) {
398 rule = set->children;
399 while (rule) {
400 if (!strcmp ((gchar *) rule->name, "rule")) {
401 EFilterRule *part =
402 E_FILTER_RULE (g_object_new (
403 rule_map->type, NULL, NULL));
404
405 if (e_filter_rule_xml_decode (part, rule, context) == 0) {
406 /* Use the revert data to keep
407 * track of the right rank of
408 * this rule part. */
409 rest_data = g_hash_table_lookup (source_hash, part->source);
410 if (rest_data == NULL) {
411 rest_data = g_malloc0 (sizeof (*rest_data));
412 rest_data->rules = g_hash_table_new (
413 g_str_hash,
414 g_str_equal);
415 g_hash_table_insert (
416 source_hash,
417 part->source,
418 rest_data);
419 }
420 frule = g_hash_table_lookup (
421 rest_data->rules,
422 part->name);
423 if (frule) {
424 if (context->priv->frozen == 0 &&
425 !e_filter_rule_eq (frule, part))
426 e_filter_rule_copy (frule, part);
427
428 g_object_unref (part);
429 e_rule_context_rank_rule (
430 context, frule,
431 frule->source,
432 rest_data->rank);
433 g_hash_table_remove (rest_data->rules, frule->name);
434 } else {
435 e_rule_context_add_rule (context, part);
436 e_rule_context_rank_rule (
437 context,
438 part,
439 part->source,
440 rest_data->rank);
441 }
442 rest_data->rank++;
443 } else {
444 g_object_unref (part);
445 g_warning ("Cannot load filter part");
446 }
447 }
448 rule = rule->next;
449 }
450 }
451 set = set->next;
452 }
453
454 xmlFreeDoc (userdoc);
455
456 /* remove any we still have that weren't in the file */
457 g_hash_table_foreach (source_hash, (GHFunc) revert_source_remove, context);
458 g_hash_table_destroy (source_hash);
459
460 return 0;
461 }
462
463 static EFilterElement *
464 rule_context_new_element (ERuleContext *context,
465 const gchar *type)
466 {
467 if (!strcmp (type, "string")) {
468 return (EFilterElement *) e_filter_input_new ();
469 } else if (!strcmp (type, "address")) {
470 /* FIXME: temporary ... need real address type */
471 return (EFilterElement *) e_filter_input_new_type_name (type);
472 } else if (!strcmp (type, "code")) {
473 return (EFilterElement *) e_filter_code_new (FALSE);
474 } else if (!strcmp (type, "rawcode")) {
475 return (EFilterElement *) e_filter_code_new (TRUE);
476 } else if (!strcmp (type, "colour")) {
477 return (EFilterElement *) e_filter_color_new ();
478 } else if (!strcmp (type, "optionlist")) {
479 return (EFilterElement *) e_filter_option_new ();
480 } else if (!strcmp (type, "datespec")) {
481 return (EFilterElement *) e_filter_datespec_new ();
482 } else if (!strcmp (type, "command")) {
483 return (EFilterElement *) e_filter_file_new_type_name (type);
484 } else if (!strcmp (type, "file")) {
485 return (EFilterElement *) e_filter_file_new_type_name (type);
486 } else if (!strcmp (type, "integer")) {
487 return (EFilterElement *) e_filter_int_new ();
488 } else if (!strcmp (type, "regex")) {
489 return (EFilterElement *) e_filter_input_new_type_name (type);
490 } else if (!strcmp (type, "completedpercent")) {
491 return (EFilterElement *) e_filter_int_new_type (
492 "completedpercent", 0,100);
493 } else {
494 g_warning ("Unknown filter type '%s'", type);
495 return NULL;
496 }
497 }
498
499 static void
500 e_rule_context_class_init (ERuleContextClass *class)
501 {
502 GObjectClass *object_class;
503
504 g_type_class_add_private (class, sizeof (ERuleContextPrivate));
505
506 object_class = G_OBJECT_CLASS (class);
507 object_class->finalize = rule_context_finalize;
508
509 class->load = rule_context_load;
510 class->save = rule_context_save;
511 class->revert = rule_context_revert;
512 class->new_element = rule_context_new_element;
513
514 signals[RULE_ADDED] = g_signal_new (
515 "rule-added",
516 E_TYPE_RULE_CONTEXT,
517 G_SIGNAL_RUN_LAST,
518 G_STRUCT_OFFSET (ERuleContextClass, rule_added),
519 NULL,
520 NULL,
521 g_cclosure_marshal_VOID__POINTER,
522 G_TYPE_NONE, 1,
523 G_TYPE_POINTER);
524
525 signals[RULE_REMOVED] = g_signal_new (
526 "rule-removed",
527 E_TYPE_RULE_CONTEXT,
528 G_SIGNAL_RUN_LAST,
529 G_STRUCT_OFFSET (ERuleContextClass, rule_removed),
530 NULL,
531 NULL,
532 g_cclosure_marshal_VOID__POINTER,
533 G_TYPE_NONE, 1,
534 G_TYPE_POINTER);
535
536 signals[CHANGED] = g_signal_new (
537 "changed",
538 E_TYPE_RULE_CONTEXT,
539 G_SIGNAL_RUN_LAST,
540 G_STRUCT_OFFSET (ERuleContextClass, changed),
541 NULL,
542 NULL,
543 g_cclosure_marshal_VOID__VOID,
544 G_TYPE_NONE, 0);
545 }
546
547 static void
548 e_rule_context_init (ERuleContext *context)
549 {
550 context->priv = E_RULE_CONTEXT_GET_PRIVATE (context);
551
552 context->part_set_map = g_hash_table_new (g_str_hash, g_str_equal);
553 context->rule_set_map = g_hash_table_new (g_str_hash, g_str_equal);
554
555 context->flags = E_RULE_CONTEXT_GROUPING;
556 }
557
558 /**
559 * e_rule_context_new:
560 *
561 * Create a new ERuleContext object.
562 *
563 * Return value: A new #ERuleContext object.
564 **/
565 ERuleContext *
566 e_rule_context_new (void)
567 {
568 return g_object_new (E_TYPE_RULE_CONTEXT, NULL);
569 }
570
571 void
572 e_rule_context_add_part_set (ERuleContext *context,
573 const gchar *setname,
574 GType part_type,
575 ERuleContextPartFunc append,
576 ERuleContextNextPartFunc next)
577 {
578 struct _part_set_map *map;
579
580 g_return_if_fail (E_IS_RULE_CONTEXT (context));
581 g_return_if_fail (setname != NULL);
582 g_return_if_fail (append != NULL);
583 g_return_if_fail (next != NULL);
584
585 map = g_hash_table_lookup (context->part_set_map, setname);
586 if (map != NULL) {
587 g_hash_table_remove (context->part_set_map, setname);
588 context->part_set_list = g_list_remove (context->part_set_list, map);
589 free_part_set (map);
590 map = NULL;
591 }
592
593 map = g_malloc0 (sizeof (*map));
594 map->type = part_type;
595 map->append = append;
596 map->next = next;
597 map->name = g_strdup (setname);
598 g_hash_table_insert (context->part_set_map, map->name, map);
599 context->part_set_list = g_list_append (context->part_set_list, map);
600 }
601
602 void
603 e_rule_context_add_rule_set (ERuleContext *context,
604 const gchar *setname,
605 GType rule_type,
606 ERuleContextRuleFunc append,
607 ERuleContextNextRuleFunc next)
608 {
609 struct _rule_set_map *map;
610
611 g_return_if_fail (E_IS_RULE_CONTEXT (context));
612 g_return_if_fail (setname != NULL);
613 g_return_if_fail (append != NULL);
614 g_return_if_fail (next != NULL);
615
616 map = g_hash_table_lookup (context->rule_set_map, setname);
617 if (map != NULL) {
618 g_hash_table_remove (context->rule_set_map, setname);
619 context->rule_set_list = g_list_remove (context->rule_set_list, map);
620 free_rule_set (map);
621 map = NULL;
622 }
623
624 map = g_malloc0 (sizeof (*map));
625 map->type = rule_type;
626 map->append = append;
627 map->next = next;
628 map->name = g_strdup (setname);
629 g_hash_table_insert (context->rule_set_map, map->name, map);
630 context->rule_set_list = g_list_append (context->rule_set_list, map);
631 }
632
633 /**
634 * e_rule_context_load:
635 * @f:
636 * @system:
637 * @user:
638 *
639 * Load a rule context from a system and user description file.
640 *
641 * Return value:
642 **/
643 gint
644 e_rule_context_load (ERuleContext *context,
645 const gchar *system,
646 const gchar *user)
647 {
648 ERuleContextClass *class;
649 gint result;
650
651 g_return_val_if_fail (E_IS_RULE_CONTEXT (context), -1);
652 g_return_val_if_fail (system != NULL, -1);
653 g_return_val_if_fail (user != NULL, -1);
654
655 class = E_RULE_CONTEXT_GET_CLASS (context);
656 g_return_val_if_fail (class->load != NULL, -1);
657
658 context->priv->frozen++;
659 result = class->load (context, system, user);
660 context->priv->frozen--;
661
662 return result;
663 }
664
665 /**
666 * e_rule_context_save:
667 * @f:
668 * @user:
669 *
670 * Save a rule context to disk.
671 *
672 * Return value:
673 **/
674 gint
675 e_rule_context_save (ERuleContext *context,
676 const gchar *user)
677 {
678 ERuleContextClass *class;
679
680 g_return_val_if_fail (E_IS_RULE_CONTEXT (context), -1);
681 g_return_val_if_fail (user != NULL, -1);
682
683 class = E_RULE_CONTEXT_GET_CLASS (context);
684 g_return_val_if_fail (class->save != NULL, -1);
685
686 return class->save (context, user);
687 }
688
689 /**
690 * e_rule_context_revert:
691 * @f:
692 * @user:
693 *
694 * Reverts a rule context from a user description file. Assumes the
695 * system description file is unchanged from when it was loaded.
696 *
697 * Return value:
698 **/
699 gint
700 e_rule_context_revert (ERuleContext *context,
701 const gchar *user)
702 {
703 ERuleContextClass *class;
704
705 g_return_val_if_fail (E_RULE_CONTEXT (context), 0);
706 g_return_val_if_fail (user != NULL, 0);
707
708 class = E_RULE_CONTEXT_GET_CLASS (context);
709 g_return_val_if_fail (class->revert != NULL, 0);
710
711 return class->revert (context, user);
712 }
713
714 EFilterPart *
715 e_rule_context_find_part (ERuleContext *context,
716 const gchar *name)
717 {
718 g_return_val_if_fail (E_IS_RULE_CONTEXT (context), NULL);
719 g_return_val_if_fail (name != NULL, NULL);
720
721 return e_filter_part_find_list (context->parts, name);
722 }
723
724 EFilterPart *
725 e_rule_context_create_part (ERuleContext *context,
726 const gchar *name)
727 {
728 EFilterPart *part;
729
730 g_return_val_if_fail (E_IS_RULE_CONTEXT (context), NULL);
731 g_return_val_if_fail (name != NULL, NULL);
732
733 part = e_rule_context_find_part (context, name);
734
735 if (part == NULL)
736 return NULL;
737
738 return e_filter_part_clone (part);
739 }
740
741 EFilterPart *
742 e_rule_context_next_part (ERuleContext *context,
743 EFilterPart *last)
744 {
745 g_return_val_if_fail (E_IS_RULE_CONTEXT (context), NULL);
746
747 return e_filter_part_next_list (context->parts, last);
748 }
749
750 EFilterRule *
751 e_rule_context_next_rule (ERuleContext *context,
752 EFilterRule *last,
753 const gchar *source)
754 {
755 g_return_val_if_fail (E_IS_RULE_CONTEXT (context), NULL);
756
757 return e_filter_rule_next_list (context->rules, last, source);
758 }
759
760 EFilterRule *
761 e_rule_context_find_rule (ERuleContext *context,
762 const gchar *name,
763 const gchar *source)
764 {
765 g_return_val_if_fail (E_IS_RULE_CONTEXT (context), NULL);
766 g_return_val_if_fail (name != NULL, NULL);
767
768 return e_filter_rule_find_list (context->rules, name, source);
769 }
770
771 void
772 e_rule_context_add_part (ERuleContext *context,
773 EFilterPart *part)
774 {
775 g_return_if_fail (E_IS_RULE_CONTEXT (context));
776 g_return_if_fail (E_IS_FILTER_PART (part));
777
778 context->parts = g_list_append (context->parts, part);
779 }
780
781 void
782 e_rule_context_add_rule (ERuleContext *context,
783 EFilterRule *rule)
784 {
785 g_return_if_fail (E_IS_RULE_CONTEXT (context));
786 g_return_if_fail (E_IS_FILTER_RULE (rule));
787
788 context->rules = g_list_append (context->rules, rule);
789
790 if (context->priv->frozen == 0) {
791 g_signal_emit (context, signals[RULE_ADDED], 0, rule);
792 g_signal_emit (context, signals[CHANGED], 0);
793 }
794 }
795
796 /* Add a rule, with a gui, asking for confirmation first,
797 * and optionally save to path. */
798 void
799 e_rule_context_add_rule_gui (ERuleContext *context,
800 EFilterRule *rule,
801 const gchar *title,
802 const gchar *path)
803 {
804 GtkDialog *dialog;
805 GtkWidget *widget;
806 GtkWidget *content_area;
807
808 g_return_if_fail (E_IS_RULE_CONTEXT (context));
809 g_return_if_fail (E_IS_FILTER_RULE (rule));
810
811 widget = e_filter_rule_get_widget (rule, context);
812 gtk_widget_show (widget);
813
814 dialog =(GtkDialog *) gtk_dialog_new ();
815 gtk_dialog_add_buttons (
816 dialog,
817 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
818 GTK_STOCK_OK, GTK_RESPONSE_OK,
819 NULL);
820
821 gtk_window_set_title ((GtkWindow *) dialog, title);
822 gtk_window_set_default_size ((GtkWindow *) dialog, 600, 400);
823 gtk_window_set_resizable ((GtkWindow *) dialog, TRUE);
824
825 content_area = gtk_dialog_get_content_area (dialog);
826 gtk_box_pack_start (GTK_BOX (content_area), widget, TRUE, TRUE, 0);
827
828 g_object_set_data_full ((GObject *) dialog, "rule", rule, g_object_unref);
829 if (path)
830 g_object_set_data_full ((GObject *) dialog, "path", g_strdup (path), g_free);
831
832 g_signal_connect (
833 dialog, "response",
834 G_CALLBACK (new_rule_response), context);
835
836 g_object_ref (context);
837
838 g_object_set_data_full ((GObject *) dialog, "context", context, g_object_unref);
839
840 gtk_widget_show ((GtkWidget *) dialog);
841 }
842
843 void
844 e_rule_context_remove_rule (ERuleContext *context,
845 EFilterRule *rule)
846 {
847 g_return_if_fail (E_IS_RULE_CONTEXT (context));
848 g_return_if_fail (E_IS_FILTER_RULE (rule));
849
850 context->rules = g_list_remove (context->rules, rule);
851
852 if (context->priv->frozen == 0) {
853 g_signal_emit (context, signals[RULE_REMOVED], 0, rule);
854 g_signal_emit (context, signals[CHANGED], 0);
855 }
856 }
857
858 void
859 e_rule_context_rank_rule (ERuleContext *context,
860 EFilterRule *rule,
861 const gchar *source,
862 gint rank)
863 {
864 GList *node;
865 gint i = 0, index = 0;
866
867 g_return_if_fail (E_IS_RULE_CONTEXT (context));
868 g_return_if_fail (E_IS_FILTER_RULE (rule));
869
870 if (e_rule_context_get_rank_rule (context, rule, source) == rank)
871 return;
872
873 context->rules = g_list_remove (context->rules, rule);
874 node = context->rules;
875 while (node) {
876 EFilterRule *r = node->data;
877
878 if (i == rank) {
879 context->rules = g_list_insert (context->rules, rule, index);
880 if (context->priv->frozen == 0)
881 g_signal_emit (context, signals[CHANGED], 0);
882
883 return;
884 }
885
886 index++;
887 if (source == NULL || (r->source && strcmp (r->source, source) == 0))
888 i++;
889
890 node = node->next;
891 }
892
893 context->rules = g_list_append (context->rules, rule);
894 if (context->priv->frozen == 0)
895 g_signal_emit (context, signals[CHANGED], 0);
896 }
897
898 gint
899 e_rule_context_get_rank_rule (ERuleContext *context,
900 EFilterRule *rule,
901 const gchar *source)
902 {
903 GList *node;
904 gint i = 0;
905
906 g_return_val_if_fail (E_IS_RULE_CONTEXT (context), -1);
907 g_return_val_if_fail (E_IS_FILTER_RULE (rule), -1);
908
909 node = context->rules;
910 while (node) {
911 EFilterRule *r = node->data;
912
913 if (r == rule)
914 return i;
915
916 if (source == NULL || (r->source && strcmp (r->source, source) == 0))
917 i++;
918
919 node = node->next;
920 }
921
922 return -1;
923 }
924
925 EFilterRule *
926 e_rule_context_find_rank_rule (ERuleContext *context,
927 gint rank,
928 const gchar *source)
929 {
930 GList *node;
931 gint i = 0;
932
933 g_return_val_if_fail (E_IS_RULE_CONTEXT (context), NULL);
934
935 node = context->rules;
936 while (node) {
937 EFilterRule *r = node->data;
938
939 if (source == NULL || (r->source && strcmp (r->source, source) == 0)) {
940 if (rank == i)
941 return r;
942 i++;
943 }
944
945 node = node->next;
946 }
947
948 return NULL;
949 }
950
951 GList *
952 e_rule_context_rename_uri (ERuleContext *context,
953 const gchar *old_uri,
954 const gchar *new_uri,
955 GCompareFunc compare)
956 {
957 ERuleContextClass *class;
958
959 g_return_val_if_fail (E_IS_RULE_CONTEXT (context), NULL);
960 g_return_val_if_fail (old_uri != NULL, NULL);
961 g_return_val_if_fail (new_uri != NULL, NULL);
962 g_return_val_if_fail (compare != NULL, NULL);
963
964 class = E_RULE_CONTEXT_GET_CLASS (context);
965
966 /* This method is optional. */
967 if (class->rename_uri == NULL)
968 return NULL;
969
970 return class->rename_uri (context, old_uri, new_uri, compare);
971 }
972
973 GList *
974 e_rule_context_delete_uri (ERuleContext *context,
975 const gchar *uri,
976 GCompareFunc compare)
977 {
978 ERuleContextClass *class;
979
980 g_return_val_if_fail (E_IS_RULE_CONTEXT (context), NULL);
981 g_return_val_if_fail (uri != NULL, NULL);
982 g_return_val_if_fail (compare != NULL, NULL);
983
984 class = E_RULE_CONTEXT_GET_CLASS (context);
985
986 /* This method is optional. */
987 if (class->delete_uri == NULL)
988 return NULL;
989
990 return class->delete_uri (context, uri, compare);
991 }
992
993 void
994 e_rule_context_free_uri_list (ERuleContext *context,
995 GList *uris)
996 {
997 g_return_if_fail (E_IS_RULE_CONTEXT (context));
998
999 /* TODO: should be virtual */
1000
1001 g_list_foreach (uris, (GFunc) g_free, NULL);
1002 g_list_free (uris);
1003 }
1004
1005 /**
1006 * e_rule_context_new_element:
1007 * @context:
1008 * @name:
1009 *
1010 * create a new filter element based on name.
1011 *
1012 * Return value:
1013 **/
1014 EFilterElement *
1015 e_rule_context_new_element (ERuleContext *context,
1016 const gchar *name)
1017 {
1018 ERuleContextClass *class;
1019
1020 g_return_val_if_fail (E_IS_RULE_CONTEXT (context), NULL);
1021 g_return_val_if_fail (name != NULL, NULL);
1022
1023 class = E_RULE_CONTEXT_GET_CLASS (context);
1024 g_return_val_if_fail (class->new_element != NULL, NULL);
1025
1026 return class->new_element (context, name);
1027 }