Add ManageSieve plugin
[claws.git] / src / plugins / managesieve / sieve_editor.c
1 /*
2  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2004-2015 the Claws Mail team
4  * Copyright (C) 2014-2015 Charles Lehner
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see <http://www.gnu.org/licenses/>.
18  * 
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #include "claws-features.h"
24 #endif
25
26 #include <gtk/gtk.h>
27 #include <glib.h>
28 #include <glib/gi18n.h>
29
30 #include "defs.h"
31 #include "gtk/gtkutils.h"
32 #include "gtk/combobox.h"
33 #include "gtk/manage_window.h"
34 #include "alertpanel.h"
35 #include "undo.h"
36 #include "menu.h"
37 #include "utils.h"
38 #include "prefs.h"
39 #include "prefs_common.h"
40 #include "account.h"
41 #include "mainwindow.h"
42 #include "message_search.h"
43 #include "managesieve.h"
44 #include "sieve_editor.h"
45
46 GSList *editors = NULL;
47
48 static void sieve_editor_destroy(SieveEditorPage *page);
49
50 void sieve_editor_set_position(void *obj, gint pos);
51 gboolean sieve_editor_search_string(void *obj,
52         const gchar *str, gboolean case_sens);
53 gboolean sieve_editor_search_string_backward(void *obj,
54         const gchar *str, gboolean case_sens);
55 static void sieve_editor_save_cb(GtkAction *action, SieveEditorPage *page);
56 static void sieve_editor_check_cb(GtkAction *action, SieveEditorPage *page);
57 static void sieve_editor_revert_cb(GtkAction *action, SieveEditorPage *page);
58 static void sieve_editor_close_cb(GtkAction *action, SieveEditorPage *page);
59 static void sieve_editor_undo_cb(GtkAction *action, SieveEditorPage *page);
60 static void sieve_editor_redo_cb(GtkAction *action, SieveEditorPage *page);
61 static void sieve_editor_cut_cb(GtkAction *action, SieveEditorPage *page);
62 static void sieve_editor_copy_cb(GtkAction *action, SieveEditorPage *page);
63 static void sieve_editor_paste_cb(GtkAction *action, SieveEditorPage *page);
64 static void sieve_editor_allsel_cb(GtkAction *action, SieveEditorPage *page);
65 static void sieve_editor_find_cb(GtkAction *action, SieveEditorPage *page);
66 static void sieve_editor_set_modified(SieveEditorPage *page,
67                 gboolean modified);
68
69 static SearchInterface search_interface = {
70         .set_position = sieve_editor_set_position,
71         .search_string_backward = sieve_editor_search_string_backward,
72         .search_string = sieve_editor_search_string,
73 };
74
75 static GtkActionEntry sieve_editor_entries[] =
76 {
77         {"Menu",                                NULL, "Menu" },
78 /* menus */
79         {"Filter",                      NULL, N_("_Filter") },
80         {"Edit",                        NULL, N_("_Edit") },
81 /* Filter menu */
82
83         {"Filter/Save",         NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(sieve_editor_save_cb) },
84         {"Filter/CheckSyntax",          NULL, N_("Chec_k Syntax"), "<control>K", NULL, G_CALLBACK(sieve_editor_check_cb) },
85         {"Filter/Revert",               NULL, N_("Re_vert"), NULL, NULL, G_CALLBACK(sieve_editor_revert_cb) },
86         {"Filter/Close",                NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(sieve_editor_close_cb) },
87
88 /* Edit menu */
89         {"Edit/Undo",                   NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(sieve_editor_undo_cb) },
90         {"Edit/Redo",                   NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(sieve_editor_redo_cb) },
91         /* {"Edit/---",                 NULL, "---" }, */
92
93         {"Edit/Cut",                    NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(sieve_editor_cut_cb) },
94         {"Edit/Copy",                   NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(sieve_editor_copy_cb) },
95         {"Edit/Paste",                  NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(sieve_editor_paste_cb) },
96
97         {"Edit/SelectAll",              NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(sieve_editor_allsel_cb) },
98
99         {"Edit/---",                    NULL, "---" },
100         {"Edit/Find",           NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(sieve_editor_find_cb) },
101 };
102
103
104 void sieve_editors_close()
105 {
106         if (editors) {
107                 g_slist_free_full(editors, (GDestroyNotify)sieve_editor_close);
108                 editors = NULL;
109         }
110 }
111
112 void sieve_editor_append_text(SieveEditorPage *page, gchar *text, gint len)
113 {
114         GtkTextBuffer *buffer;
115         GtkTextIter iter;
116         gboolean was_modified = page->modified;
117
118         undo_block(page->undostruct);
119         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(page->text));
120         gtk_text_buffer_get_end_iter(buffer, &iter);
121         gtk_text_buffer_insert(buffer, &iter, text, len);
122         undo_unblock(page->undostruct);
123         sieve_editor_set_modified(page, was_modified);
124 }
125
126 static gint sieve_editor_get_text(SieveEditorPage *page, gchar **text)
127 {
128         GtkTextBuffer *buffer;
129         GtkTextIter start, end;
130
131         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(page->text));
132         gtk_text_buffer_get_start_iter(buffer, &start);
133         gtk_text_buffer_get_end_iter(buffer, &end);
134         *text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
135         return gtk_text_iter_get_offset(&end) - gtk_text_iter_get_offset(&start);
136 }
137
138 static void sieve_editor_set_status(SieveEditorPage *page, const gchar *status)
139 {
140         gtk_label_set_text(GTK_LABEL(page->status_text), status);
141 }
142
143 static void sieve_editor_set_status_icon(SieveEditorPage *page, const gchar *img_id)
144 {
145         GtkImage *img = GTK_IMAGE(page->status_icon);
146         if (img_id)
147                 gtk_image_set_from_stock(img, img_id, GTK_ICON_SIZE_BUTTON);
148         else
149                 gtk_image_clear(img);
150 }
151
152 static void sieve_editor_append_status(SieveEditorPage *page,
153                 const gchar *status)
154 {
155         GtkLabel *label = GTK_LABEL(page->status_text);
156         const gchar *prev_status = gtk_label_get_text(label);
157         const gchar *sep = prev_status && prev_status[0] ? "\n" : "";
158         gtk_label_set_text(label, g_strconcat(prev_status, sep, status, NULL));
159 }
160
161 /* Update the status icon and text from a response. */
162 static void sieve_editor_update_status(SieveEditorPage *page,
163                 SieveResult *result)
164 {
165         if (result->has_status) {
166                 /* set status icon */
167                 sieve_editor_set_status_icon(page,
168                         result->success ? GTK_STOCK_DIALOG_INFO : GTK_STOCK_DIALOG_ERROR);
169                 /* clear status text */
170                 sieve_editor_set_status(page, "");
171         }
172         if (result->description) {
173                 /* append to status */
174                 sieve_editor_append_status(page, result->description);
175         }
176 }
177
178 /* Edit Menu */
179
180 static void sieve_editor_undo_cb(GtkAction *action, SieveEditorPage *page)
181 {
182         undo_undo(page->undostruct);
183 }
184
185 static void sieve_editor_redo_cb(GtkAction *action, SieveEditorPage *page)
186 {
187         undo_redo(page->undostruct);
188 }
189
190 static void sieve_editor_cut_cb(GtkAction *action, SieveEditorPage *page)
191 {
192         GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(page->text));
193         GtkClipboard *clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
194         gtk_text_buffer_cut_clipboard(buf, clipboard, TRUE);
195 }
196
197 static void sieve_editor_copy_cb(GtkAction *action, SieveEditorPage *page)
198 {
199         GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(page->text));
200         GtkClipboard *clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
201         gtk_text_buffer_copy_clipboard(buf, clipboard);
202 }
203
204 static void sieve_editor_paste_cb(GtkAction *action, SieveEditorPage *page)
205 {
206         if (!gtk_widget_has_focus(page->text))
207                 return;
208
209         GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(page->text));
210         GtkClipboard *clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
211         gchar *contents = gtk_clipboard_wait_for_text(clipboard);
212         GtkTextMark *start_mark = gtk_text_buffer_get_insert(buf);
213         GtkTextIter start_iter;
214
215         undo_paste_clipboard(GTK_TEXT_VIEW(page->text), page->undostruct);
216         gtk_text_buffer_delete_selection(buf, FALSE, TRUE);
217
218         gtk_text_buffer_get_iter_at_mark(buf, &start_iter, start_mark);
219         gtk_text_buffer_insert(buf, &start_iter, contents, strlen(contents));
220 }
221
222
223 static void sieve_editor_allsel_cb(GtkAction *action, SieveEditorPage *page)
224 {
225         GtkTextIter start, end;
226         GtkTextBuffer *buffer;
227
228         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(page->text));
229         gtk_text_buffer_get_start_iter(buffer, &start);
230         gtk_text_buffer_get_end_iter(buffer, &end);
231         gtk_text_buffer_select_range(buffer, &start, &end);
232 }
233
234 /* Search */
235
236 void sieve_editor_set_position(void *obj, gint pos)
237 {
238         SieveEditorPage *page = (SieveEditorPage *)obj;
239         GtkTextView *text = GTK_TEXT_VIEW(page->text);
240
241         gtkut_text_view_set_position(text, pos);
242 }
243
244 gboolean sieve_editor_search_string(void *obj,
245         const gchar *str, gboolean case_sens)
246 {
247         SieveEditorPage *page = (SieveEditorPage *)obj;
248         GtkTextView *text = GTK_TEXT_VIEW(page->text);
249
250         return gtkut_text_view_search_string(text, str, case_sens);
251 }
252
253 gboolean sieve_editor_search_string_backward(void *obj,
254         const gchar *str, gboolean case_sens)
255 {
256         SieveEditorPage *page = (SieveEditorPage *)obj;
257         GtkTextView *text = GTK_TEXT_VIEW(page->text);
258
259         return gtkut_text_view_search_string_backward(text, str, case_sens);
260 }
261
262 static void sieve_editor_search(SieveEditorPage *page)
263 {
264         message_search_other(&search_interface, page);
265 }
266
267 /* Actions */
268
269 static void got_data_reverting(SieveSession *session, gchar *contents,
270                 SieveEditorPage *page)
271 {
272         if (contents == NULL) {
273                 /* end of data */
274                 undo_unblock(page->undostruct);
275                 gtk_widget_set_sensitive(page->text, TRUE);
276                 sieve_editor_set_status(page, "");
277                 sieve_editor_set_modified(page, FALSE);
278                 return;
279         }
280         if (contents == (void *)-1) {
281                 /* error */
282                 sieve_editor_set_status(page, _("Unable to get script contents"));
283                 sieve_editor_set_status_icon(page, GTK_STOCK_DIALOG_ERROR);
284                 return;
285         }
286
287         if (page->first_line) {
288                 GtkTextIter start, end;
289                 GtkTextBuffer *buffer;
290
291                 page->first_line = FALSE;
292
293                 /* delete previous data */
294                 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(page->text));
295                 gtk_text_buffer_get_start_iter(buffer, &start);
296                 gtk_text_buffer_get_end_iter(buffer, &end);
297                 gtk_text_buffer_delete(buffer, &start, &end);
298
299                 /* append data */
300                 gtk_text_buffer_insert(buffer, &end, contents, strlen(contents));
301         } else {
302                 sieve_editor_append_text(page, "\n", 1);
303                 sieve_editor_append_text(page, contents, strlen(contents));
304         }
305 }
306
307 static void sieve_editor_revert(SieveEditorPage *page)
308 {
309         undo_block(page->undostruct);
310         page->first_line = TRUE;
311         gtk_widget_set_sensitive(page->text, FALSE);
312         sieve_editor_set_status(page, _("Reverting..."));
313         sieve_editor_set_status_icon(page, NULL);
314         sieve_session_get_script(page->session, page->script_name,
315                         (sieve_session_data_cb_fn)got_data_reverting, page);
316 }
317
318 static void sieve_editor_revert_cb(GtkAction *action, SieveEditorPage *page)
319 {
320         if (!page->modified ||
321                         alertpanel(_("Revert script"),
322                                 _("This script has been modified. Revert the unsaved changes?"),
323                                 _("_Revert"), NULL, GTK_STOCK_CANCEL) == G_ALERTDEFAULT)
324                 sieve_editor_revert(page);
325 }
326
327 static void got_data_saved(SieveSession *session, SieveResult *result,
328                 SieveEditorPage *page)
329 {
330         if (result->has_status && result->success) {
331                 sieve_editor_set_modified(page, FALSE);
332                 if (page->closing) {
333                         sieve_editor_close(page);
334                         return;
335                 }
336                 /* use nice status message if there are no warnings */
337                 if (result->code == SIEVE_CODE_NONE) {
338                         result->description = _("Script saved successfully.");
339                 }
340         }
341         sieve_editor_update_status(page, result);
342 }
343
344 static void sieve_editor_save(SieveEditorPage *page)
345 {
346         gchar *text;
347         gint len = sieve_editor_get_text(page, &text);
348         sieve_editor_set_status(page, _("Saving..."));
349         sieve_editor_set_status_icon(page, NULL);
350         sieve_session_put_script(page->session, page->script_name, len, text,
351                         (sieve_session_data_cb_fn)got_data_saved, page);
352         g_free(text);
353 }
354
355 static void sieve_editor_save_cb(GtkAction *action, SieveEditorPage *page)
356 {
357         sieve_editor_save(page);
358 }
359
360 static void sieve_editor_find_cb(GtkAction *action, SieveEditorPage *page)
361 {
362         sieve_editor_search(page);
363 }
364
365 static void got_data_checked(SieveSession *session, SieveResult *result,
366                 SieveEditorPage *page)
367 {
368         sieve_editor_update_status(page, result);
369 }
370
371 static void sieve_editor_check_cb(GtkAction *action, SieveEditorPage *page)
372 {
373         gchar *text;
374         gint len = sieve_editor_get_text(page, &text);
375         sieve_editor_set_status(page, _("Checking syntax..."));
376         sieve_editor_set_status_icon(page, NULL);
377         sieve_session_check_script(page->session, len, text,
378                         (sieve_session_data_cb_fn)got_data_checked, page);
379         g_free(text);
380 }
381
382 static void sieve_editor_changed_cb(GtkTextBuffer *textbuf,
383                 SieveEditorPage *page)
384 {
385         if (!page->modified) {
386                 sieve_editor_set_modified(page, TRUE);
387         }
388 }
389
390 static void sieve_editor_destroy(SieveEditorPage *page)
391 {
392         gtk_widget_destroy(page->window);
393         undo_destroy(page->undostruct);
394         g_free(page);
395 }
396
397 void sieve_editor_close(SieveEditorPage *page)
398 {
399         editors = g_slist_remove(editors, (gconstpointer)page);
400         sieve_editor_destroy(page);
401         sieve_sessions_discard_callbacks(page);
402 }
403
404 static gboolean sieve_editor_confirm_close(SieveEditorPage *page)
405 {
406         if (page->modified) {
407                 switch (alertpanel(_("Save changes"),
408                                 _("This script has been modified. Save the latest changes?"),
409                                 _("_Discard"), _("+_Save"), GTK_STOCK_CANCEL)) {
410                         case G_ALERTDEFAULT:
411                                 return TRUE;
412                         case G_ALERTALTERNATE:
413                                 page->closing = TRUE;
414                                 sieve_editor_save(page);
415                                 return FALSE;
416                         default:
417                                 return FALSE;
418                 }
419         }
420         return TRUE;
421 }
422
423 static void sieve_editor_close_cb(GtkAction *action, SieveEditorPage *page)
424 {
425         if (sieve_editor_confirm_close(page)) {
426                 sieve_editor_close(page);
427         }
428 }
429
430 static gint sieve_editor_delete_cb(GtkWidget *widget, GdkEventAny *event,
431                 SieveEditorPage *page)
432 {
433         sieve_editor_close_cb(NULL, page);
434         return TRUE;
435 }
436
437 /**
438  * sieve_editor_undo_state_changed:
439  *
440  * Change the sensivity of the menuentries undo and redo
441  **/
442 static void sieve_editor_undo_state_changed(UndoMain *undostruct,
443                 gint undo_state, gint redo_state, gpointer data)
444 {
445         SieveEditorPage *page = (SieveEditorPage *)data;
446
447         switch (undo_state) {
448         case UNDO_STATE_TRUE:
449                 if (!undostruct->undo_state) {
450                         undostruct->undo_state = TRUE;
451                         cm_menu_set_sensitive_full(page->ui_manager, "Menu/Edit/Undo", TRUE);
452                 }
453                 break;
454         case UNDO_STATE_FALSE:
455                 if (undostruct->undo_state) {
456                         undostruct->undo_state = FALSE;
457                         cm_menu_set_sensitive_full(page->ui_manager, "Menu/Edit/Undo", FALSE);
458                 }
459                 break;
460         case UNDO_STATE_UNCHANGED:
461                 break;
462         case UNDO_STATE_REFRESH:
463                 cm_menu_set_sensitive_full(page->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
464                 break;
465         default:
466                 g_warning("Undo state not recognized");
467                 break;
468         }
469
470         switch (redo_state) {
471         case UNDO_STATE_TRUE:
472                 if (!undostruct->redo_state) {
473                         undostruct->redo_state = TRUE;
474                         cm_menu_set_sensitive_full(page->ui_manager, "Menu/Edit/Redo", TRUE);
475                 }
476                 break;
477         case UNDO_STATE_FALSE:
478                 if (undostruct->redo_state) {
479                         undostruct->redo_state = FALSE;
480                         cm_menu_set_sensitive_full(page->ui_manager, "Menu/Edit/Redo", FALSE);
481                 }
482                 break;
483         case UNDO_STATE_UNCHANGED:
484                 break;
485         case UNDO_STATE_REFRESH:
486                 cm_menu_set_sensitive_full(page->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
487                 break;
488         default:
489                 g_warning("Redo state not recognized");
490                 break;
491         }
492 }
493
494
495 SieveEditorPage *sieve_editor_new(SieveSession *session, gchar *script_name)
496 {
497         SieveEditorPage *page;
498         GtkUIManager *ui_manager;
499         UndoMain *undostruct;
500         GtkWidget *window;
501         GtkWidget *menubar;
502         GtkWidget *vbox, *hbox, *hbox1;
503         GtkWidget *scrolledwin;
504         GtkWidget *text;
505         GtkTextBuffer *buffer;
506         GtkWidget *check_btn, *save_btn, *close_btn;
507         GtkWidget *status_text;
508         GtkWidget *status_icon;
509
510         page = g_new0(SieveEditorPage, 1);
511
512         /* window */
513         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "sieveeditor");
514         gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
515         MANAGE_WINDOW_SIGNALS_CONNECT (window);
516         g_signal_connect(G_OBJECT(window), "delete_event",
517                          G_CALLBACK(sieve_editor_delete_cb), page);
518
519         vbox = gtk_vbox_new(FALSE, 0);
520         gtk_container_add(GTK_CONTAINER(window), vbox);
521
522         ui_manager = gtk_ui_manager_new();
523         cm_menu_create_action_group_full(ui_manager,
524                         "Menu", sieve_editor_entries, G_N_ELEMENTS(sieve_editor_entries),
525                         page);
526
527         MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
528
529         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu", "Filter", "Filter", GTK_UI_MANAGER_MENU)
530         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
531
532 /* Filter menu */
533         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Filter", "Save", "Filter/Save", GTK_UI_MANAGER_MENUITEM)
534 MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Filter", "CheckSyntax", "Filter/CheckSyntax", GTK_UI_MANAGER_MENUITEM)
535 MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Filter", "Revert", "Filter/Revert", GTK_UI_MANAGER_MENUITEM)
536         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Filter", "Close", "Filter/Close", GTK_UI_MANAGER_MENUITEM)
537
538 /* Edit menu */
539         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
540         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
541         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
542
543         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
544         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
545         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
546
547         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
548
549         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
550
551         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
552
553         menubar = gtk_ui_manager_get_widget(ui_manager, "/Menu");
554
555         gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
556         gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
557
558         cm_menu_set_sensitive_full(ui_manager, "Menu/Edit/Undo", FALSE);
559         cm_menu_set_sensitive_full(ui_manager, "Menu/Edit/Redo", FALSE);
560
561         /* text */
562         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
563         gtk_widget_set_size_request (scrolledwin, 660, 408);
564         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
565                                        GTK_POLICY_AUTOMATIC,
566                                        GTK_POLICY_AUTOMATIC);
567         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
568                                             GTK_SHADOW_IN);
569         gtk_box_pack_start(GTK_BOX(vbox), scrolledwin, TRUE, TRUE, 0);
570
571         text = gtk_text_view_new();
572         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
573         gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
574         gtk_container_add(GTK_CONTAINER(scrolledwin), text);
575
576         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
577         g_signal_connect(G_OBJECT(buffer), "changed",
578                          G_CALLBACK(sieve_editor_changed_cb), page);
579
580         /* set text font */
581         if (prefs_common.textfont) {
582                 PangoFontDescription *font_desc;
583
584                 font_desc = pango_font_description_from_string
585                         (prefs_common.textfont);
586                 if (font_desc) {
587                         gtk_widget_modify_font(text, font_desc);
588                         pango_font_description_free(font_desc);
589                 }
590         }
591
592         hbox = gtk_hbox_new (FALSE, 8);
593         gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
594         gtk_container_set_border_width (GTK_CONTAINER (hbox), 8);
595
596         /* status */
597         status_icon = gtk_image_new ();
598         gtk_box_pack_start (GTK_BOX (hbox), status_icon, FALSE, FALSE, 0);
599         status_text = gtk_label_new ("");
600         gtk_box_pack_start (GTK_BOX (hbox), status_text, FALSE, FALSE, 0);
601         gtk_label_set_justify (GTK_LABEL (status_text), GTK_JUSTIFY_LEFT);
602
603         /* buttons */
604         gtkut_stock_with_text_button_set_create(&hbox1,
605                         &close_btn, GTK_STOCK_CANCEL, _("_Close"),
606                         &check_btn, GTK_STOCK_OK, _("Chec_k Syntax"),
607                         &save_btn, GTK_STOCK_SAVE, _("_Save"));
608         gtk_box_pack_end (GTK_BOX (hbox), hbox1, FALSE, FALSE, 0);
609         gtk_widget_grab_default (save_btn);
610         g_signal_connect (G_OBJECT (close_btn), "clicked",
611                           G_CALLBACK (sieve_editor_close_cb), page);
612         g_signal_connect (G_OBJECT (check_btn), "clicked",
613                           G_CALLBACK (sieve_editor_check_cb), page);
614         g_signal_connect (G_OBJECT (save_btn), "clicked",
615                           G_CALLBACK (sieve_editor_save_cb), page);
616
617         undostruct = undo_init(text);
618         undo_set_change_state_func(undostruct, &sieve_editor_undo_state_changed,
619                         page);
620
621         gtk_widget_show_all(window);
622
623         page->window = window;
624         page->ui_manager = ui_manager;
625         page->text = text;
626         page->undostruct = undostruct;
627         page->session = session;
628         page->script_name = script_name;
629         page->status_text = status_text;
630         page->status_icon = status_icon;
631
632         editors = g_slist_prepend(editors, page);
633
634         sieve_editor_set_modified(page, FALSE);
635
636         return page;
637 }
638
639 SieveEditorPage *sieve_editor_get(SieveSession *session, gchar *script_name)
640 {
641         GSList *item;
642         SieveEditorPage *page;
643         for (item = editors; item; item = item->next) {
644                 page = (SieveEditorPage *)item->data;
645                 if (page->session == session &&
646                                 strcmp(script_name, page->script_name) == 0)
647                         return page;
648         }
649         return NULL;
650 }
651
652 void sieve_editor_present(SieveEditorPage *page)
653 {
654         gtk_window_present(GTK_WINDOW(page->window));
655 }
656
657 static void sieve_editor_set_modified(SieveEditorPage *page,
658                 gboolean modified)
659 {
660         gchar *title;
661
662         page->modified = modified;
663         cm_menu_set_sensitive_full(page->ui_manager, "Menu/Filter/Revert",
664                         modified);
665
666         title = g_strdup_printf(_("%s - Sieve Filter%s"), page->script_name,
667                         modified ? " [Edited]" : "");
668         gtk_window_set_title (GTK_WINDOW (page->window), title);
669         g_free(title);
670
671         if (modified) {
672                 sieve_editor_set_status(page, "");
673                 sieve_editor_set_status_icon(page, NULL);
674         }
675 }