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 * Not Zed <notzed@lostzed.mmc.com.au>
18 * Jeffrey Stedfast <fejj@ximian.com>
19 *
20 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
21 *
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 /* for getenv only, remove when getenv need removed */
29 #include <stdlib.h>
30 #include <string.h>
31
32 #include <glib/gi18n.h>
33
34 #include "libevolution-utils/e-alert-dialog.h"
35 #include "libevolution-utils/evolution-util.h"
36
37 #include "e-rule-editor.h"
38
39 #define E_RULE_EDITOR_GET_PRIVATE(obj) \
40 (G_TYPE_INSTANCE_GET_PRIVATE \
41 ((obj), E_TYPE_RULE_EDITOR, ERuleEditorPrivate))
42
43 static gint enable_undo = 0;
44
45 enum {
46 BUTTON_ADD,
47 BUTTON_EDIT,
48 BUTTON_DELETE,
49 BUTTON_TOP,
50 BUTTON_UP,
51 BUTTON_DOWN,
52 BUTTON_BOTTOM,
53 BUTTON_LAST
54 };
55
56 struct _ERuleEditorPrivate {
57 GtkButton *buttons[BUTTON_LAST];
58 };
59
60 G_DEFINE_TYPE (
61 ERuleEditor,
62 e_rule_editor,
63 GTK_TYPE_DIALOG)
64
65 static void
66 rule_editor_add_undo (ERuleEditor *editor,
67 gint type,
68 EFilterRule *rule,
69 gint rank,
70 gint newrank)
71 {
72 ERuleEditorUndo *undo;
73
74 if (!editor->undo_active && enable_undo) {
75 undo = g_malloc0 (sizeof (*undo));
76 undo->rule = rule;
77 undo->type = type;
78 undo->rank = rank;
79 undo->newrank = newrank;
80
81 undo->next = editor->undo_log;
82 editor->undo_log = undo;
83 } else {
84 g_object_unref (rule);
85 }
86 }
87
88 static void
89 rule_editor_play_undo (ERuleEditor *editor)
90 {
91 ERuleEditorUndo *undo, *next;
92 EFilterRule *rule;
93
94 editor->undo_active = TRUE;
95 undo = editor->undo_log;
96 editor->undo_log = NULL;
97 while (undo) {
98 next = undo->next;
99 switch (undo->type) {
100 case E_RULE_EDITOR_LOG_EDIT:
101 rule = e_rule_context_find_rank_rule (editor->context, undo->rank, undo->rule->source);
102 if (rule) {
103 e_filter_rule_copy (rule, undo->rule);
104 } else {
105 g_warning ("Could not find the right rule to undo against?");
106 }
107 break;
108 case E_RULE_EDITOR_LOG_ADD:
109 rule = e_rule_context_find_rank_rule (editor->context, undo->rank, undo->rule->source);
110 if (rule)
111 e_rule_context_remove_rule (editor->context, rule);
112 break;
113 case E_RULE_EDITOR_LOG_REMOVE:
114 g_object_ref (undo->rule);
115 e_rule_context_add_rule (editor->context, undo->rule);
116 e_rule_context_rank_rule (editor->context, undo->rule, editor->source, undo->rank);
117 break;
118 case E_RULE_EDITOR_LOG_RANK:
119 rule = e_rule_context_find_rank_rule (editor->context, undo->newrank, undo->rule->source);
120 if (rule)
121 e_rule_context_rank_rule (editor->context, rule, editor->source, undo->rank);
122 break;
123 }
124
125 g_object_unref (undo->rule);
126 g_free (undo);
127 undo = next;
128 }
129 editor->undo_active = FALSE;
130 }
131
132 static void
133 dialog_rule_changed (EFilterRule *fr,
134 GtkWidget *dialog)
135 {
136 g_return_if_fail (dialog != NULL);
137
138 gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_OK, fr && fr->parts);
139 }
140
141 static void
142 add_editor_response (GtkWidget *dialog,
143 gint button,
144 ERuleEditor *editor)
145 {
146 GtkTreeSelection *selection;
147 GtkTreePath *path;
148 GtkTreeIter iter;
149
150 if (button == GTK_RESPONSE_OK) {
151 EAlert *alert = NULL;
152 if (!e_filter_rule_validate (editor->edit, &alert)) {
153 e_alert_run_dialog (GTK_WINDOW (dialog), alert);
154 g_object_unref (alert);
155 return;
156 }
157
158 if (e_rule_context_find_rule (editor->context, editor->edit->name, editor->edit->source)) {
159 e_alert_run_dialog_for_args (
160 GTK_WINDOW (dialog),
161 "filter:bad-name-notunique",
162 editor->edit->name, NULL);
163 return;
164 }
165
166 g_object_ref (editor->edit);
167
168 gtk_list_store_append (editor->model, &iter);
169 gtk_list_store_set (
170 editor->model, &iter,
171 0, editor->edit->name,
172 1, editor->edit,
173 2, editor->edit->enabled, -1);
174 selection = gtk_tree_view_get_selection (editor->list);
175 gtk_tree_selection_select_iter (selection, &iter);
176
177 /* scroll to the newly added row */
178 path = gtk_tree_model_get_path (
179 GTK_TREE_MODEL (editor->model), &iter);
180 gtk_tree_view_scroll_to_cell (
181 editor->list, path, NULL, TRUE, 1.0, 0.0);
182 gtk_tree_path_free (path);
183
184 editor->current = editor->edit;
185 e_rule_context_add_rule (editor->context, editor->current);
186
187 g_object_ref (editor->current);
188 rule_editor_add_undo (
189 editor,
190 E_RULE_EDITOR_LOG_ADD,
191 editor->current,
192 e_rule_context_get_rank_rule (
193 editor->context,
194 editor->current,
195 editor->current->source),
196 0);
197 }
198
199 gtk_widget_destroy (dialog);
200 }
201
202 static void
203 editor_destroy (ERuleEditor *editor,
204 GObject *deadbeef)
205 {
206 if (editor->edit) {
207 g_object_unref (editor->edit);
208 editor->edit = NULL;
209 }
210
211 editor->dialog = NULL;
212
213 gtk_widget_set_sensitive (GTK_WIDGET (editor), TRUE);
214 e_rule_editor_set_sensitive (editor);
215 }
216
217 static gboolean
218 update_selected_rule (ERuleEditor *editor)
219 {
220 GtkTreeSelection *selection;
221 GtkTreeModel *model;
222 GtkTreeIter iter;
223
224 selection = gtk_tree_view_get_selection (editor->list);
225 if (selection && gtk_tree_selection_get_selected (selection, &model, &iter)) {
226 gtk_tree_model_get (GTK_TREE_MODEL (editor->model), &iter, 1, &editor->current, -1);
227 return TRUE;
228 }
229
230 return FALSE;
231 }
232
233 static void
234 cursor_changed (GtkTreeView *treeview,
235 ERuleEditor *editor)
236 {
237 if (update_selected_rule (editor)) {
238 g_return_if_fail (editor->current);
239
240 e_rule_editor_set_sensitive (editor);
241 }
242 }
243
244 static void
245 editor_response (GtkWidget *dialog,
246 gint button,
247 ERuleEditor *editor)
248 {
249 if (button == GTK_RESPONSE_CANCEL) {
250 if (enable_undo)
251 rule_editor_play_undo (editor);
252 else {
253 ERuleEditorUndo *undo, *next;
254
255 undo = editor->undo_log;
256 editor->undo_log = NULL;
257 while (undo) {
258 next = undo->next;
259 g_object_unref (undo->rule);
260 g_free (undo);
261 undo = next;
262 }
263 }
264 }
265 }
266
267 static void
268 rule_add (GtkWidget *widget,
269 ERuleEditor *editor)
270 {
271 GtkWidget *rules;
272 GtkWidget *content_area;
273
274 if (editor->edit != NULL)
275 return;
276
277 editor->edit = e_rule_editor_create_rule (editor);
278 e_filter_rule_set_source (editor->edit, editor->source);
279 rules = e_filter_rule_get_widget (editor->edit, editor->context);
280
281 editor->dialog = gtk_dialog_new ();
282 gtk_dialog_add_buttons (
283 GTK_DIALOG (editor->dialog),
284 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
285 GTK_STOCK_OK, GTK_RESPONSE_OK,
286 NULL);
287
288 gtk_window_set_title ((GtkWindow *) editor->dialog, _("Add Rule"));
289 gtk_window_set_default_size (GTK_WINDOW (editor->dialog), 650, 400);
290 gtk_window_set_resizable (GTK_WINDOW (editor->dialog), TRUE);
291 gtk_window_set_transient_for ((GtkWindow *) editor->dialog, (GtkWindow *) editor);
292 gtk_container_set_border_width ((GtkContainer *) editor->dialog, 6);
293
294 content_area = gtk_dialog_get_content_area (GTK_DIALOG (editor->dialog));
295 gtk_box_pack_start (GTK_BOX (content_area), rules, TRUE, TRUE, 3);
296
297 g_signal_connect (
298 editor->dialog, "response",
299 G_CALLBACK (add_editor_response), editor);
300 g_object_weak_ref ((GObject *) editor->dialog, (GWeakNotify) editor_destroy, editor);
301
302 g_signal_connect (
303 editor->edit, "changed",
304 G_CALLBACK (dialog_rule_changed), editor->dialog);
305 dialog_rule_changed (editor->edit, editor->dialog);
306
307 gtk_widget_set_sensitive (GTK_WIDGET (editor), FALSE);
308
309 gtk_widget_show (editor->dialog);
310 }
311
312 static void
313 edit_editor_response (GtkWidget *dialog,
314 gint button,
315 ERuleEditor *editor)
316 {
317 EFilterRule *rule;
318 GtkTreePath *path;
319 GtkTreeIter iter;
320 gint pos;
321
322 if (button == GTK_RESPONSE_OK) {
323 EAlert *alert = NULL;
324 if (!e_filter_rule_validate (editor->edit, &alert)) {
325 e_alert_run_dialog (GTK_WINDOW (dialog), alert);
326 g_object_unref (alert);
327 return;
328 }
329
330 rule = e_rule_context_find_rule (
331 editor->context,
332 editor->edit->name,
333 editor->edit->source);
334
335 if (rule != NULL && rule != editor->current) {
336 e_alert_run_dialog_for_args (
337 GTK_WINDOW (dialog),
338 "filter:bad-name-notunique",
339 rule->name, NULL);
340 return;
341 }
342
343 pos = e_rule_context_get_rank_rule (
344 editor->context,
345 editor->current,
346 editor->source);
347
348 if (pos != -1) {
349 path = gtk_tree_path_new ();
350 gtk_tree_path_append_index (path, pos);
351 gtk_tree_model_get_iter (
352 GTK_TREE_MODEL (editor->model), &iter, path);
353 gtk_tree_path_free (path);
354
355 gtk_list_store_set (
356 editor->model, &iter,
357 0, editor->edit->name, -1);
358
359 rule_editor_add_undo (
360 editor, E_RULE_EDITOR_LOG_EDIT,
361 e_filter_rule_clone (editor->current),
362 pos, 0);
363
364 /* replace the old rule with the new rule */
365 e_filter_rule_copy (editor->current, editor->edit);
366 }
367 }
368
369 gtk_widget_destroy (dialog);
370 }
371
372 static void
373 rule_edit (GtkWidget *widget,
374 ERuleEditor *editor)
375 {
376 GtkWidget *rules;
377 GtkWidget *content_area;
378
379 update_selected_rule (editor);
380
381 if (editor->current == NULL || editor->edit != NULL)
382 return;
383
384 editor->edit = e_filter_rule_clone (editor->current);
385
386 rules = e_filter_rule_get_widget (editor->edit, editor->context);
387
388 editor->dialog = gtk_dialog_new ();
389 gtk_dialog_add_buttons (
390 (GtkDialog *) editor->dialog,
391 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
392 GTK_STOCK_OK, GTK_RESPONSE_OK,
393 NULL);
394
395 gtk_window_set_title ((GtkWindow *) editor->dialog, _("Edit Rule"));
396 gtk_window_set_default_size (GTK_WINDOW (editor->dialog), 650, 400);
397 gtk_window_set_resizable (GTK_WINDOW (editor->dialog), TRUE);
398 gtk_window_set_transient_for (GTK_WINDOW (editor->dialog), GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (editor))));
399 gtk_container_set_border_width ((GtkContainer *) editor->dialog, 6);
400
401 content_area = gtk_dialog_get_content_area (GTK_DIALOG (editor->dialog));
402 gtk_box_pack_start (GTK_BOX (content_area), rules, TRUE, TRUE, 3);
403
404 g_signal_connect (
405 editor->dialog, "response",
406 G_CALLBACK (edit_editor_response), editor);
407 g_object_weak_ref ((GObject *) editor->dialog, (GWeakNotify) editor_destroy, editor);
408
409 g_signal_connect (
410 editor->edit, "changed",
411 G_CALLBACK (dialog_rule_changed), editor->dialog);
412 dialog_rule_changed (editor->edit, editor->dialog);
413
414 gtk_widget_set_sensitive (GTK_WIDGET (editor), FALSE);
415
416 gtk_widget_show (editor->dialog);
417 }
418
419 static void
420 rule_delete (GtkWidget *widget,
421 ERuleEditor *editor)
422 {
423 GtkTreeSelection *selection;
424 GtkTreePath *path;
425 GtkTreeIter iter;
426 gint pos, len;
427
428 update_selected_rule (editor);
429
430 pos = e_rule_context_get_rank_rule (editor->context, editor->current, editor->source);
431 if (pos != -1) {
432 EFilterRule *delete_rule = editor->current;
433
434 editor->current = NULL;
435
436 e_rule_context_remove_rule (editor->context, delete_rule);
437
438 path = gtk_tree_path_new ();
439 gtk_tree_path_append_index (path, pos);
440 gtk_tree_model_get_iter (GTK_TREE_MODEL (editor->model), &iter, path);
441 gtk_list_store_remove (editor->model, &iter);
442 gtk_tree_path_free (path);
443
444 rule_editor_add_undo (
445 editor,
446 E_RULE_EDITOR_LOG_REMOVE,
447 delete_rule,
448 e_rule_context_get_rank_rule (
449 editor->context,
450 delete_rule,
451 delete_rule->source),
452 0);
453 #if 0
454 g_object_unref (delete_rule);
455 #endif
456
457 /* now select the next rule */
458 len = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (editor->model), NULL);
459 pos = pos >= len ? len - 1 : pos;
460
461 if (pos >= 0) {
462 path = gtk_tree_path_new ();
463 gtk_tree_path_append_index (path, pos);
464 gtk_tree_model_get_iter (GTK_TREE_MODEL (editor->model), &iter, path);
465 gtk_tree_path_free (path);
466
467 /* select the new row */
468 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (editor->list));
469 gtk_tree_selection_select_iter (selection, &iter);
470
471 /* scroll to the selected row */
472 path = gtk_tree_model_get_path ((GtkTreeModel *) editor->model, &iter);
473 gtk_tree_view_scroll_to_cell (editor->list, path, NULL, FALSE, 0.0, 0.0);
474 gtk_tree_path_free (path);
475
476 /* update our selection state */
477 cursor_changed (editor->list, editor);
478 return;
479 }
480 }
481
482 e_rule_editor_set_sensitive (editor);
483 }
484
485 static void
486 rule_move (ERuleEditor *editor,
487 gint from,
488 gint to)
489 {
490 GtkTreeSelection *selection;
491 GtkTreePath *path;
492 GtkTreeIter iter;
493 EFilterRule *rule;
494
495 rule_editor_add_undo (
496 editor, E_RULE_EDITOR_LOG_RANK,
497 g_object_ref (editor->current),
498 e_rule_context_get_rank_rule (editor->context,
499 editor->current, editor->source), to);
500
501 e_rule_context_rank_rule (
502 editor->context, editor->current, editor->source, to);
503
504 path = gtk_tree_path_new ();
505 gtk_tree_path_append_index (path, from);
506 gtk_tree_model_get_iter (GTK_TREE_MODEL (editor->model), &iter, path);
507 gtk_tree_path_free (path);
508
509 gtk_tree_model_get (GTK_TREE_MODEL (editor->model), &iter, 1, &rule, -1);
510 g_return_if_fail (rule != NULL);
511
512 /* remove and then re-insert the row at the new location */
513 gtk_list_store_remove (editor->model, &iter);
514 gtk_list_store_insert (editor->model, &iter, to);
515
516 /* set the data on the row */
517 gtk_list_store_set (editor->model, &iter, 0, rule->name, 1, rule, 2, rule->enabled, -1);
518
519 /* select the row */
520 selection = gtk_tree_view_get_selection (editor->list);
521 gtk_tree_selection_select_iter (selection, &iter);
522
523 /* scroll to the selected row */
524 path = gtk_tree_model_get_path ((GtkTreeModel *) editor->model, &iter);
525 gtk_tree_view_scroll_to_cell (editor->list, path, NULL, FALSE, 0.0, 0.0);
526 gtk_tree_path_free (path);
527
528 e_rule_editor_set_sensitive (editor);
529 }
530
531 static void
532 rule_top (GtkWidget *widget,
533 ERuleEditor *editor)
534 {
535 gint pos;
536
537 update_selected_rule (editor);
538
539 pos = e_rule_context_get_rank_rule (
540 editor->context, editor->current, editor->source);
541 if (pos > 0)
542 rule_move (editor, pos, 0);
543 }
544
545 static void
546 rule_up (GtkWidget *widget,
547 ERuleEditor *editor)
548 {
549 gint pos;
550
551 update_selected_rule (editor);
552
553 pos = e_rule_context_get_rank_rule (
554 editor->context, editor->current, editor->source);
555 if (pos > 0)
556 rule_move (editor, pos, pos - 1);
557 }
558
559 static void
560 rule_down (GtkWidget *widget,
561 ERuleEditor *editor)
562 {
563 gint pos;
564
565 update_selected_rule (editor);
566
567 pos = e_rule_context_get_rank_rule (
568 editor->context, editor->current, editor->source);
569 if (pos >= 0)
570 rule_move (editor, pos, pos + 1);
571 }
572
573 static void
574 rule_bottom (GtkWidget *widget,
575 ERuleEditor *editor)
576 {
577 gint pos;
578 gint count = 0;
579 EFilterRule *rule = NULL;
580
581 update_selected_rule (editor);
582
583 pos = e_rule_context_get_rank_rule (
584 editor->context, editor->current, editor->source);
585 /* There's probably a better/faster way to get the count of the list here */
586 while ((rule = e_rule_context_next_rule (editor->context, rule, editor->source)))
587 count++;
588 count--;
589 if (pos >= 0)
590 rule_move (editor, pos, count);
591 }
592
593 static struct {
594 const gchar *name;
595 GCallback func;
596 } edit_buttons[] = {
597 { "rule_add", G_CALLBACK (rule_add) },
598 { "rule_edit", G_CALLBACK (rule_edit) },
599 { "rule_delete", G_CALLBACK (rule_delete) },
600 { "rule_top", G_CALLBACK (rule_top) },
601 { "rule_up", G_CALLBACK (rule_up) },
602 { "rule_down", G_CALLBACK (rule_down) },
603 { "rule_bottom", G_CALLBACK (rule_bottom) },
604 };
605
606 static void
607 rule_editor_finalize (GObject *object)
608 {
609 ERuleEditor *editor = E_RULE_EDITOR (object);
610 ERuleEditorUndo *undo, *next;
611
612 g_object_unref (editor->context);
613
614 undo = editor->undo_log;
615 while (undo) {
616 next = undo->next;
617 g_object_unref (undo->rule);
618 g_free (undo);
619 undo = next;
620 }
621
622 /* Chain up to parent's finalize() method. */
623 G_OBJECT_CLASS (e_rule_editor_parent_class)->finalize (object);
624 }
625
626 static void
627 rule_editor_dispose (GObject *object)
628 {
629 ERuleEditor *editor = E_RULE_EDITOR (object);
630
631 if (editor->dialog != NULL) {
632 gtk_widget_destroy (GTK_WIDGET (editor->dialog));
633 editor->dialog = NULL;
634 }
635
636 /* Chain up to parent's dispose() method. */
637 G_OBJECT_CLASS (e_rule_editor_parent_class)->dispose (object);
638 }
639
640 static void
641 rule_editor_set_source (ERuleEditor *editor,
642 const gchar *source)
643 {
644 EFilterRule *rule = NULL;
645 GtkTreeIter iter;
646
647 gtk_list_store_clear (editor->model);
648
649 while ((rule = e_rule_context_next_rule (editor->context, rule, source)) != NULL) {
650 gtk_list_store_append (editor->model, &iter);
651 gtk_list_store_set (
652 editor->model, &iter,
653 0, rule->name, 1, rule, 2, rule->enabled, -1);
654 }
655
656 g_free (editor->source);
657 editor->source = g_strdup (source);
658 editor->current = NULL;
659 e_rule_editor_set_sensitive (editor);
660 }
661
662 static void
663 rule_editor_set_sensitive (ERuleEditor *editor)
664 {
665 EFilterRule *rule = NULL;
666 gint index = -1, count = 0;
667
668 while ((rule = e_rule_context_next_rule (editor->context, rule, editor->source))) {
669 if (rule == editor->current)
670 index = count;
671 count++;
672 }
673
674 count--;
675
676 gtk_widget_set_sensitive (GTK_WIDGET (editor->priv->buttons[BUTTON_EDIT]), index != -1);
677 gtk_widget_set_sensitive (GTK_WIDGET (editor->priv->buttons[BUTTON_DELETE]), index != -1);
678 gtk_widget_set_sensitive (GTK_WIDGET (editor->priv->buttons[BUTTON_TOP]), index > 0);
679 gtk_widget_set_sensitive (GTK_WIDGET (editor->priv->buttons[BUTTON_UP]), index > 0);
680 gtk_widget_set_sensitive (GTK_WIDGET (editor->priv->buttons[BUTTON_DOWN]), index >= 0 && index < count);
681 gtk_widget_set_sensitive (GTK_WIDGET (editor->priv->buttons[BUTTON_BOTTOM]), index >= 0 && index < count);
682 }
683
684 static EFilterRule *
685 rule_editor_create_rule (ERuleEditor *editor)
686 {
687 EFilterRule *rule;
688 EFilterPart *part;
689
690 /* create a rule with 1 part in it */
691 rule = e_filter_rule_new ();
692 part = e_rule_context_next_part (editor->context, NULL);
693 e_filter_rule_add_part (rule, e_filter_part_clone (part));
694
695 return rule;
696 }
697
698 static void
699 e_rule_editor_class_init (ERuleEditorClass *class)
700 {
701 GObjectClass *object_class;
702
703 g_type_class_add_private (class, sizeof (ERuleEditorPrivate));
704
705 object_class = G_OBJECT_CLASS (class);
706 object_class->finalize = rule_editor_finalize;
707 object_class->dispose = rule_editor_dispose;
708
709 class->set_source = rule_editor_set_source;
710 class->set_sensitive = rule_editor_set_sensitive;
711 class->create_rule = rule_editor_create_rule;
712
713 /* TODO: Remove when it works (or never will) */
714 enable_undo = getenv ("EVOLUTION_RULE_UNDO") != NULL;
715 }
716
717 static void
718 e_rule_editor_init (ERuleEditor *editor)
719 {
720 editor->priv = E_RULE_EDITOR_GET_PRIVATE (editor);
721 }
722
723 /**
724 * rule_editor_new:
725 *
726 * Create a new ERuleEditor object.
727 *
728 * Return value: A new #ERuleEditor object.
729 **/
730 ERuleEditor *
731 e_rule_editor_new (ERuleContext *context,
732 const gchar *source,
733 const gchar *label)
734 {
735 ERuleEditor *editor = (ERuleEditor *) g_object_new (E_TYPE_RULE_EDITOR, NULL);
736 GtkBuilder *builder;
737
738 builder = gtk_builder_new ();
739 e_load_ui_builder_definition (builder, "filter.ui");
740 e_rule_editor_construct (editor, context, builder, source, label);
741 gtk_widget_hide (e_builder_get_widget (builder, "label17"));
742 gtk_widget_hide (e_builder_get_widget (builder, "filter_source_combobox"));
743 g_object_unref (builder);
744
745 return editor;
746 }
747
748 void
749 e_rule_editor_set_sensitive (ERuleEditor *editor)
750 {
751 ERuleEditorClass *class;
752
753 g_return_if_fail (E_IS_RULE_EDITOR (editor));
754
755 class = E_RULE_EDITOR_GET_CLASS (editor);
756 g_return_if_fail (class->set_sensitive != NULL);
757
758 class->set_sensitive (editor);
759 }
760
761 void
762 e_rule_editor_set_source (ERuleEditor *editor,
763 const gchar *source)
764 {
765 ERuleEditorClass *class;
766
767 g_return_if_fail (E_IS_RULE_EDITOR (editor));
768
769 class = E_RULE_EDITOR_GET_CLASS (editor);
770 g_return_if_fail (class->set_source != NULL);
771
772 class->set_source (editor, source);
773 }
774
775 EFilterRule *
776 e_rule_editor_create_rule (ERuleEditor *editor)
777 {
778 ERuleEditorClass *class;
779
780 g_return_val_if_fail (E_IS_RULE_EDITOR (editor), NULL);
781
782 class = E_RULE_EDITOR_GET_CLASS (editor);
783 g_return_val_if_fail (class->create_rule != NULL, NULL);
784
785 return class->create_rule (editor);
786 }
787
788 static void
789 double_click (GtkTreeView *treeview,
790 GtkTreePath *path,
791 GtkTreeViewColumn *column,
792 ERuleEditor *editor)
793 {
794 GtkTreeSelection *selection;
795 GtkTreeModel *model;
796 GtkTreeIter iter;
797
798 selection = gtk_tree_view_get_selection (editor->list);
799 if (gtk_tree_selection_get_selected (selection, &model, &iter))
800 gtk_tree_model_get (GTK_TREE_MODEL (editor->model), &iter, 1, &editor->current, -1);
801
802 if (editor->current)
803 rule_edit ((GtkWidget *) treeview, editor);
804 }
805
806 static void
807 rule_able_toggled (GtkCellRendererToggle *renderer,
808 gchar *path_string,
809 gpointer user_data)
810 {
811 GtkWidget *table = user_data;
812 GtkTreeModel *model;
813 GtkTreePath *path;
814 GtkTreeIter iter;
815
816 path = gtk_tree_path_new_from_string (path_string);
817 model = gtk_tree_view_get_model (GTK_TREE_VIEW (table));
818
819 if (gtk_tree_model_get_iter (model, &iter, path)) {
820 EFilterRule *rule = NULL;
821
822 gtk_tree_model_get (model, &iter, 1, &rule, -1);
823
824 if (rule) {
825 rule->enabled = !rule->enabled;
826 gtk_list_store_set (GTK_LIST_STORE (model), &iter, 2, rule->enabled, -1);
827 }
828 }
829
830 gtk_tree_path_free (path);
831 }
832
833 void
834 e_rule_editor_construct (ERuleEditor *editor,
835 ERuleContext *context,
836 GtkBuilder *builder,
837 const gchar *source,
838 const gchar *label)
839 {
840 GtkWidget *widget;
841 GtkWidget *action_area;
842 GtkWidget *content_area;
843 GtkTreeViewColumn *column;
844 GtkCellRenderer *renderer;
845 GtkTreeSelection *selection;
846 GObject *object;
847 GList *list;
848 gint i;
849
850 g_return_if_fail (E_IS_RULE_EDITOR (editor));
851 g_return_if_fail (E_IS_RULE_CONTEXT (context));
852 g_return_if_fail (GTK_IS_BUILDER (builder));
853
854 editor->context = g_object_ref (context);
855
856 action_area = gtk_dialog_get_action_area (GTK_DIALOG (editor));
857 content_area = gtk_dialog_get_content_area (GTK_DIALOG (editor));
858
859 gtk_window_set_resizable ((GtkWindow *) editor, TRUE);
860 gtk_window_set_default_size ((GtkWindow *) editor, 350, 400);
861 gtk_widget_realize ((GtkWidget *) editor);
862 gtk_container_set_border_width (GTK_CONTAINER (action_area), 12);
863
864 widget = e_builder_get_widget (builder, "rule_editor");
865 gtk_box_pack_start (GTK_BOX (content_area), widget, TRUE, TRUE, 0);
866
867 for (i = 0; i < BUTTON_LAST; i++) {
868 widget = e_builder_get_widget (builder, edit_buttons[i].name);
869 editor->priv->buttons[i] = GTK_BUTTON (widget);
870 g_signal_connect (
871 widget, "clicked",
872 G_CALLBACK (edit_buttons[i].func), editor);
873 }
874
875 object = gtk_builder_get_object (builder, "rule_tree_view");
876 editor->list = GTK_TREE_VIEW (object);
877
878 column = gtk_tree_view_get_column (GTK_TREE_VIEW (object), 0);
879 g_return_if_fail (column != NULL);
880
881 gtk_tree_view_column_set_visible (column, FALSE);
882 list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
883 g_return_if_fail (list != NULL);
884
885 renderer = GTK_CELL_RENDERER (list->data);
886 g_warn_if_fail (GTK_IS_CELL_RENDERER_TOGGLE (renderer));
887
888 g_signal_connect (
889 renderer, "toggled",
890 G_CALLBACK (rule_able_toggled), editor->list);
891
892 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (object));
893 gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
894
895 object = gtk_builder_get_object (builder, "rule_list_store");
896 editor->model = GTK_LIST_STORE (object);
897
898 g_signal_connect (
899 editor->list, "cursor-changed",
900 G_CALLBACK (cursor_changed), editor);
901 g_signal_connect (
902 editor->list, "row-activated",
903 G_CALLBACK (double_click), editor);
904
905 widget = e_builder_get_widget (builder, "rule_label");
906 gtk_label_set_label (GTK_LABEL (widget), label);
907 gtk_label_set_mnemonic_widget (
908 GTK_LABEL (widget), GTK_WIDGET (editor->list));
909
910 g_signal_connect (
911 editor, "response",
912 G_CALLBACK (editor_response), editor);
913 rule_editor_set_source (editor, source);
914
915 gtk_dialog_add_buttons (
916 GTK_DIALOG (editor),
917 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
918 GTK_STOCK_OK, GTK_RESPONSE_OK,
919 NULL);
920 }