evolution-3.6.4/filter/e-rule-editor.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  *		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 }