e085dcba8f9e97b6b7bc691a1a9e538f57351e44
[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_manager.h"
45 #include "sieve_editor.h"
46
47 GSList *editors = NULL;
48
49 static void sieve_editor_destroy(SieveEditorPage *page);
50
51 void sieve_editor_set_position(void *obj, gint pos);
52 gboolean sieve_editor_search_string(void *obj,
53         const gchar *str, gboolean case_sens);
54 gboolean sieve_editor_search_string_backward(void *obj,
55         const gchar *str, gboolean case_sens);
56 static void sieve_editor_save_cb(GtkAction *action, SieveEditorPage *page);
57 static void sieve_editor_check_cb(GtkAction *action, SieveEditorPage *page);
58 static void sieve_editor_changed_cb(GtkTextBuffer *, SieveEditorPage *page);
59 static void sieve_editor_revert_cb(GtkAction *action, SieveEditorPage *page);
60 static void sieve_editor_close_cb(GtkAction *action, SieveEditorPage *page);
61 static void sieve_editor_undo_cb(GtkAction *action, SieveEditorPage *page);
62 static void sieve_editor_redo_cb(GtkAction *action, SieveEditorPage *page);
63 static void sieve_editor_cut_cb(GtkAction *action, SieveEditorPage *page);
64 static void sieve_editor_copy_cb(GtkAction *action, SieveEditorPage *page);
65 static void sieve_editor_paste_cb(GtkAction *action, SieveEditorPage *page);
66 static void sieve_editor_allsel_cb(GtkAction *action, SieveEditorPage *page);
67 static void sieve_editor_find_cb(GtkAction *action, SieveEditorPage *page);
68 static void sieve_editor_set_modified(SieveEditorPage *page,
69                 gboolean modified);
70
71 static SearchInterface search_interface = {
72         .set_position = sieve_editor_set_position,
73         .search_string_backward = sieve_editor_search_string_backward,
74         .search_string = sieve_editor_search_string,
75 };
76
77 static GtkActionEntry sieve_editor_entries[] =
78 {
79         {"Menu",                                NULL, "Menu" },
80 /* menus */
81         {"Filter",                      NULL, N_("_Filter") },
82         {"Edit",                        NULL, N_("_Edit") },
83 /* Filter menu */
84
85         {"Filter/Save",         NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(sieve_editor_save_cb) },
86         {"Filter/CheckSyntax",          NULL, N_("Chec_k Syntax"), "<control>K", NULL, G_CALLBACK(sieve_editor_check_cb) },
87         {"Filter/Revert",               NULL, N_("Re_vert"), NULL, NULL, G_CALLBACK(sieve_editor_revert_cb) },
88         {"Filter/Close",                NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(sieve_editor_close_cb) },
89
90 /* Edit menu */
91         {"Edit/Undo",                   NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(sieve_editor_undo_cb) },
92         {"Edit/Redo",                   NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(sieve_editor_redo_cb) },
93         /* {"Edit/---",                 NULL, "---" }, */
94
95         {"Edit/Cut",                    NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(sieve_editor_cut_cb) },
96         {"Edit/Copy",                   NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(sieve_editor_copy_cb) },
97         {"Edit/Paste",                  NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(sieve_editor_paste_cb) },
98
99         {"Edit/SelectAll",              NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(sieve_editor_allsel_cb) },
100
101         {"Edit/---",                    NULL, "---" },
102         {"Edit/Find",           NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(sieve_editor_find_cb) },
103 };
104
105
106 void sieve_editors_close()
107 {
108         if (editors) {
109                 g_slist_free_full(editors, (GDestroyNotify)sieve_editor_close);
110                 editors = NULL;
111         }
112 }
113
114 void sieve_editor_append_text(SieveEditorPage *page, gchar *text, gint len)
115 {
116         GtkTextBuffer *buffer;
117         GtkTextIter iter;
118
119         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(page->text));
120
121         g_signal_handlers_block_by_func(G_OBJECT(buffer),
122                          G_CALLBACK(sieve_editor_changed_cb), page);
123
124         undo_block(page->undostruct);
125         gtk_text_buffer_get_end_iter(buffer, &iter);
126         gtk_text_buffer_insert(buffer, &iter, text, len);
127         undo_unblock(page->undostruct);
128
129         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
130                          G_CALLBACK(sieve_editor_changed_cb), page);
131 }
132
133 static gint sieve_editor_get_text(SieveEditorPage *page, gchar **text)
134 {
135         GtkTextBuffer *buffer;
136         GtkTextIter start, end;
137
138         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(page->text));
139         gtk_text_buffer_get_start_iter(buffer, &start);
140         gtk_text_buffer_get_end_iter(buffer, &end);
141         *text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
142         return gtk_text_iter_get_offset(&end) - gtk_text_iter_get_offset(&start);
143 }
144
145 static void sieve_editor_set_status(SieveEditorPage *page, const gchar *status)
146 {
147         gtk_label_set_text(GTK_LABEL(page->status_text), status);
148 }
149
150 static void sieve_editor_set_status_icon(SieveEditorPage *page, const gchar *img_id)
151 {
152         GtkImage *img = GTK_IMAGE(page->status_icon);
153         if (img_id)
154                 gtk_image_set_from_stock(img, img_id, GTK_ICON_SIZE_BUTTON);
155         else
156                 gtk_image_clear(img);
157 }
158
159 static void sieve_editor_append_status(SieveEditorPage *page,
160                 const gchar *status)
161 {
162         GtkLabel *label = GTK_LABEL(page->status_text);
163         const gchar *prev_status = gtk_label_get_text(label);
164         const gchar *sep = prev_status && prev_status[0] ? "\n" : "";
165         gtk_label_set_text(label, g_strconcat(prev_status, sep, status, NULL));
166 }
167
168 /* Update the status icon and text from a response. */
169 static void sieve_editor_update_status(SieveEditorPage *page,
170                 SieveResult *result)
171 {
172         if (result->has_status) {
173                 /* set status icon */
174                 sieve_editor_set_status_icon(page,
175                         result->success ? GTK_STOCK_DIALOG_INFO : GTK_STOCK_DIALOG_ERROR);
176                 /* clear status text */
177                 sieve_editor_set_status(page, "");
178         }
179         if (result->description) {
180                 /* append to status */
181                 sieve_editor_append_status(page, result->description);
182         }
183 }
184
185 /* Edit Menu */
186
187 static void sieve_editor_undo_cb(GtkAction *action, SieveEditorPage *page)
188 {
189         undo_undo(page->undostruct);
190 }
191
192 static void sieve_editor_redo_cb(GtkAction *action, SieveEditorPage *page)
193 {
194         undo_redo(page->undostruct);
195 }
196
197 static void sieve_editor_cut_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_cut_clipboard(buf, clipboard, TRUE);
202 }
203
204 static void sieve_editor_copy_cb(GtkAction *action, SieveEditorPage *page)
205 {
206         GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(page->text));
207         GtkClipboard *clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
208         gtk_text_buffer_copy_clipboard(buf, clipboard);
209 }
210
211 static void sieve_editor_paste_cb(GtkAction *action, SieveEditorPage *page)
212 {
213         if (!gtk_widget_has_focus(page->text))
214                 return;
215
216         GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(page->text));
217         GtkClipboard *clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
218         gchar *contents = gtk_clipboard_wait_for_text(clipboard);
219         GtkTextMark *start_mark = gtk_text_buffer_get_insert(buf);
220         GtkTextIter start_iter;
221
222         undo_paste_clipboard(GTK_TEXT_VIEW(page->text), page->undostruct);
223         gtk_text_buffer_delete_selection(buf, FALSE, TRUE);
224
225         gtk_text_buffer_get_iter_at_mark(buf, &start_iter, start_mark);
226         gtk_text_buffer_insert(buf, &start_iter, contents, strlen(contents));
227 }
228
229
230 static void sieve_editor_allsel_cb(GtkAction *action, SieveEditorPage *page)
231 {
232         GtkTextIter start, end;
233         GtkTextBuffer *buffer;
234
235         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(page->text));
236         gtk_text_buffer_get_start_iter(buffer, &start);
237         gtk_text_buffer_get_end_iter(buffer, &end);
238         gtk_text_buffer_select_range(buffer, &start, &end);
239 }
240
241 /* Search */
242
243 void sieve_editor_set_position(void *obj, gint pos)
244 {
245         SieveEditorPage *page = (SieveEditorPage *)obj;
246         GtkTextView *text = GTK_TEXT_VIEW(page->text);
247
248         gtkut_text_view_set_position(text, pos);
249 }
250
251 gboolean sieve_editor_search_string(void *obj,
252         const gchar *str, gboolean case_sens)
253 {
254         SieveEditorPage *page = (SieveEditorPage *)obj;
255         GtkTextView *text = GTK_TEXT_VIEW(page->text);
256
257         return gtkut_text_view_search_string(text, str, case_sens);
258 }
259
260 gboolean sieve_editor_search_string_backward(void *obj,
261         const gchar *str, gboolean case_sens)
262 {
263         SieveEditorPage *page = (SieveEditorPage *)obj;
264         GtkTextView *text = GTK_TEXT_VIEW(page->text);
265
266         return gtkut_text_view_search_string_backward(text, str, case_sens);
267 }
268
269 static void sieve_editor_search(SieveEditorPage *page)
270 {
271         message_search_other(&search_interface, page);
272 }
273
274 /* Actions */
275
276 static void got_data_reverting(SieveSession *session, gboolean abort,
277                 gchar *contents,
278                 SieveEditorPage *page)
279 {
280         if (abort)
281                 return;
282         if (contents == NULL) {
283                 /* end of data */
284                 undo_unblock(page->undostruct);
285                 gtk_widget_set_sensitive(page->text, TRUE);
286                 sieve_editor_set_status(page, "");
287                 sieve_editor_set_modified(page, FALSE);
288                 return;
289         }
290         if (contents == (void *)-1) {
291                 /* error */
292                 sieve_editor_set_status(page, _("Unable to get script contents"));
293                 sieve_editor_set_status_icon(page, GTK_STOCK_DIALOG_ERROR);
294                 return;
295         }
296
297         if (page->first_line) {
298                 GtkTextIter start, end;
299                 GtkTextBuffer *buffer;
300
301                 page->first_line = FALSE;
302
303                 /* delete previous data */
304                 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(page->text));
305                 gtk_text_buffer_get_start_iter(buffer, &start);
306                 gtk_text_buffer_get_end_iter(buffer, &end);
307                 gtk_text_buffer_delete(buffer, &start, &end);
308
309                 /* append data */
310                 gtk_text_buffer_insert(buffer, &end, contents, strlen(contents));
311         } else {
312                 sieve_editor_append_text(page, "\n", 1);
313                 sieve_editor_append_text(page, contents, strlen(contents));
314         }
315 }
316
317 static void sieve_editor_revert(SieveEditorPage *page)
318 {
319         undo_block(page->undostruct);
320         page->first_line = TRUE;
321         gtk_widget_set_sensitive(page->text, FALSE);
322         sieve_editor_set_status(page, _("Reverting..."));
323         sieve_editor_set_status_icon(page, NULL);
324         sieve_session_get_script(page->session, page->script_name,
325                         (sieve_session_data_cb_fn)got_data_reverting, page);
326 }
327
328 static void sieve_editor_revert_cb(GtkAction *action, SieveEditorPage *page)
329 {
330         if (!page->modified ||
331                         alertpanel(_("Revert script"),
332                                 _("This script has been modified. Revert the unsaved changes?"),
333                                 _("_Revert"), NULL, GTK_STOCK_CANCEL) == G_ALERTDEFAULT)
334                 sieve_editor_revert(page);
335 }
336
337 static void got_data_saved(SieveSession *session, gboolean abort,
338                 SieveResult *result, SieveEditorPage *page)
339 {
340         if (abort)
341                 return;
342         if (result->has_status && result->success) {
343                 sieve_editor_set_modified(page, FALSE);
344                 if (page->closing) {
345                         sieve_editor_close(page);
346                         return;
347                 }
348                 /* use nice status message if there are no warnings */
349                 if (result->code == SIEVE_CODE_NONE) {
350                         result->description = _("Script saved successfully.");
351                 }
352
353                 if (page->is_new) {
354                         /* notify manager windows of newly created script */
355                         page->is_new = FALSE;
356                         sieve_manager_script_created(session,
357                                         page->script_name);
358                 }
359         }
360         sieve_editor_update_status(page, result);
361 }
362
363 static void sieve_editor_save(SieveEditorPage *page)
364 {
365         gchar *text;
366         gint len = sieve_editor_get_text(page, &text);
367         sieve_editor_set_status(page, _("Saving..."));
368         sieve_editor_set_status_icon(page, NULL);
369         sieve_session_put_script(page->session, page->script_name, len, text,
370                         (sieve_session_data_cb_fn)got_data_saved, page);
371         g_free(text);
372 }
373
374 static void sieve_editor_save_cb(GtkAction *action, SieveEditorPage *page)
375 {
376         sieve_editor_save(page);
377 }
378
379 static void sieve_editor_find_cb(GtkAction *action, SieveEditorPage *page)
380 {
381         sieve_editor_search(page);
382 }
383
384 static void got_data_checked(SieveSession *session, gboolean abort,
385                 SieveResult *result, SieveEditorPage *page)
386 {
387         if (abort)
388                 return;
389         sieve_editor_update_status(page, result);
390 }
391
392 static void sieve_editor_check_cb(GtkAction *action, SieveEditorPage *page)
393 {
394         gchar *text;
395         gint len = sieve_editor_get_text(page, &text);
396         sieve_editor_set_status(page, _("Checking syntax..."));
397         sieve_editor_set_status_icon(page, NULL);
398         sieve_session_check_script(page->session, len, text,
399                         (sieve_session_data_cb_fn)got_data_checked, page);
400         g_free(text);
401 }
402
403 static void sieve_editor_changed_cb(GtkTextBuffer *textbuf,
404                 SieveEditorPage *page)
405 {
406         if (!page->modified) {
407                 sieve_editor_set_modified(page, TRUE);
408         }
409 }
410
411 static void sieve_editor_destroy(SieveEditorPage *page)
412 {
413         gtk_widget_destroy(page->window);
414         undo_destroy(page->undostruct);
415         g_free(page);
416 }
417
418 void sieve_editor_close(SieveEditorPage *page)
419 {
420         editors = g_slist_remove(editors, (gconstpointer)page);
421         sieve_editor_destroy(page);
422         sieve_sessions_discard_callbacks(page);
423 }
424
425 static gboolean sieve_editor_confirm_close(SieveEditorPage *page)
426 {
427         if (page->modified) {
428                 switch (alertpanel(_("Save changes"),
429                                 _("This script has been modified. Save the latest changes?"),
430                                 _("_Discard"), _("+_Save"), GTK_STOCK_CANCEL)) {
431                         case G_ALERTDEFAULT:
432                                 return TRUE;
433                         case G_ALERTALTERNATE:
434                                 page->closing = TRUE;
435                                 sieve_editor_save(page);
436                                 return FALSE;
437                         default:
438                                 return FALSE;
439                 }
440         }
441         return TRUE;
442 }
443
444 static void sieve_editor_close_cb(GtkAction *action, SieveEditorPage *page)
445 {
446         if (sieve_editor_confirm_close(page)) {
447                 sieve_editor_close(page);
448         }
449 }
450
451 static gint sieve_editor_delete_cb(GtkWidget *widget, GdkEventAny *event,
452                 SieveEditorPage *page)
453 {
454         sieve_editor_close_cb(NULL, page);
455         return TRUE;
456 }
457
458 /**
459  * sieve_editor_undo_state_changed:
460  *
461  * Change the sensivity of the menuentries undo and redo
462  **/
463 static void sieve_editor_undo_state_changed(UndoMain *undostruct,
464                 gint undo_state, gint redo_state, gpointer data)
465 {
466         SieveEditorPage *page = (SieveEditorPage *)data;
467
468         switch (undo_state) {
469         case UNDO_STATE_TRUE:
470                 if (!undostruct->undo_state) {
471                         undostruct->undo_state = TRUE;
472                         cm_menu_set_sensitive_full(page->ui_manager, "Menu/Edit/Undo", TRUE);
473                 }
474                 break;
475         case UNDO_STATE_FALSE:
476                 if (undostruct->undo_state) {
477                         undostruct->undo_state = FALSE;
478                         cm_menu_set_sensitive_full(page->ui_manager, "Menu/Edit/Undo", FALSE);
479                 }
480                 break;
481         case UNDO_STATE_UNCHANGED:
482                 break;
483         case UNDO_STATE_REFRESH:
484                 cm_menu_set_sensitive_full(page->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
485                 break;
486         default:
487                 g_warning("Undo state not recognized");
488                 break;
489         }
490
491         switch (redo_state) {
492         case UNDO_STATE_TRUE:
493                 if (!undostruct->redo_state) {
494                         undostruct->redo_state = TRUE;
495                         cm_menu_set_sensitive_full(page->ui_manager, "Menu/Edit/Redo", TRUE);
496                 }
497                 break;
498         case UNDO_STATE_FALSE:
499                 if (undostruct->redo_state) {
500                         undostruct->redo_state = FALSE;
501                         cm_menu_set_sensitive_full(page->ui_manager, "Menu/Edit/Redo", FALSE);
502                 }
503                 break;
504         case UNDO_STATE_UNCHANGED:
505                 break;
506         case UNDO_STATE_REFRESH:
507                 cm_menu_set_sensitive_full(page->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
508                 break;
509         default:
510                 g_warning("Redo state not recognized");
511                 break;
512         }
513 }
514
515
516 SieveEditorPage *sieve_editor_new(SieveSession *session, gchar *script_name)
517 {
518         SieveEditorPage *page;
519         GtkUIManager *ui_manager;
520         UndoMain *undostruct;
521         GtkWidget *window;
522         GtkWidget *menubar;
523         GtkWidget *vbox, *hbox, *hbox1;
524         GtkWidget *scrolledwin;
525         GtkWidget *text;
526         GtkTextBuffer *buffer;
527         GtkWidget *check_btn, *save_btn, *close_btn;
528         GtkWidget *status_text;
529         GtkWidget *status_icon;
530
531         page = g_new0(SieveEditorPage, 1);
532
533         /* window */
534         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "sieveeditor");
535         gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
536         MANAGE_WINDOW_SIGNALS_CONNECT (window);
537         g_signal_connect(G_OBJECT(window), "delete_event",
538                          G_CALLBACK(sieve_editor_delete_cb), page);
539
540         vbox = gtk_vbox_new(FALSE, 0);
541         gtk_container_add(GTK_CONTAINER(window), vbox);
542
543         ui_manager = gtk_ui_manager_new();
544         cm_menu_create_action_group_full(ui_manager,
545                         "Menu", sieve_editor_entries, G_N_ELEMENTS(sieve_editor_entries),
546                         page);
547
548         MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
549
550         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu", "Filter", "Filter", GTK_UI_MANAGER_MENU)
551         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
552
553 /* Filter menu */
554         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Filter", "Save", "Filter/Save", GTK_UI_MANAGER_MENUITEM)
555 MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Filter", "CheckSyntax", "Filter/CheckSyntax", GTK_UI_MANAGER_MENUITEM)
556 MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Filter", "Revert", "Filter/Revert", GTK_UI_MANAGER_MENUITEM)
557         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Filter", "Close", "Filter/Close", GTK_UI_MANAGER_MENUITEM)
558
559 /* Edit menu */
560         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
561         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
562         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
563
564         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
565         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
566         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
567
568         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
569
570         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
571
572         MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
573
574         menubar = gtk_ui_manager_get_widget(ui_manager, "/Menu");
575
576         gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
577         gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
578
579         cm_menu_set_sensitive_full(ui_manager, "Menu/Edit/Undo", FALSE);
580         cm_menu_set_sensitive_full(ui_manager, "Menu/Edit/Redo", FALSE);
581
582         /* text */
583         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
584         gtk_widget_set_size_request (scrolledwin, 660, 408);
585         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
586                                        GTK_POLICY_AUTOMATIC,
587                                        GTK_POLICY_AUTOMATIC);
588         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
589                                             GTK_SHADOW_IN);
590         gtk_box_pack_start(GTK_BOX(vbox), scrolledwin, TRUE, TRUE, 0);
591
592         text = gtk_text_view_new();
593         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
594         gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
595         gtk_container_add(GTK_CONTAINER(scrolledwin), text);
596
597         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
598         g_signal_connect(G_OBJECT(buffer), "changed",
599                          G_CALLBACK(sieve_editor_changed_cb), page);
600
601         /* set text font */
602         if (prefs_common_get_prefs()->textfont) {
603                 PangoFontDescription *font_desc;
604
605                 font_desc = pango_font_description_from_string
606                         (prefs_common_get_prefs()->textfont);
607                 if (font_desc) {
608                         gtk_widget_modify_font(text, font_desc);
609                         pango_font_description_free(font_desc);
610                 }
611         }
612
613         hbox = gtk_hbox_new (FALSE, 8);
614         gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
615         gtk_container_set_border_width (GTK_CONTAINER (hbox), 8);
616
617         /* status */
618         status_icon = gtk_image_new ();
619         gtk_box_pack_start (GTK_BOX (hbox), status_icon, FALSE, FALSE, 0);
620         status_text = gtk_label_new ("");
621         gtk_box_pack_start (GTK_BOX (hbox), status_text, FALSE, FALSE, 0);
622         gtk_label_set_justify (GTK_LABEL (status_text), GTK_JUSTIFY_LEFT);
623
624         /* buttons */
625         gtkut_stock_with_text_button_set_create(&hbox1,
626                         &close_btn, GTK_STOCK_CANCEL, _("_Close"),
627                         &check_btn, GTK_STOCK_OK, _("Chec_k Syntax"),
628                         &save_btn, GTK_STOCK_SAVE, _("_Save"));
629         gtk_box_pack_end (GTK_BOX (hbox), hbox1, FALSE, FALSE, 0);
630         gtk_widget_grab_default (save_btn);
631         g_signal_connect (G_OBJECT (close_btn), "clicked",
632                           G_CALLBACK (sieve_editor_close_cb), page);
633         g_signal_connect (G_OBJECT (check_btn), "clicked",
634                           G_CALLBACK (sieve_editor_check_cb), page);
635         g_signal_connect (G_OBJECT (save_btn), "clicked",
636                           G_CALLBACK (sieve_editor_save_cb), page);
637
638         undostruct = undo_init(text);
639         undo_set_change_state_func(undostruct, &sieve_editor_undo_state_changed,
640                         page);
641
642         page->window = window;
643         page->ui_manager = ui_manager;
644         page->text = text;
645         page->undostruct = undostruct;
646         page->session = session;
647         page->script_name = script_name;
648         page->status_text = status_text;
649         page->status_icon = status_icon;
650
651         editors = g_slist_prepend(editors, page);
652
653         sieve_editor_set_modified(page, FALSE);
654
655         return page;
656 }
657
658 SieveEditorPage *sieve_editor_get(SieveSession *session, gchar *script_name)
659 {
660         GSList *item;
661         SieveEditorPage *page;
662         for (item = editors; item; item = item->next) {
663                 page = (SieveEditorPage *)item->data;
664                 if (page->session == session &&
665                                 strcmp(script_name, page->script_name) == 0)
666                         return page;
667         }
668         return NULL;
669 }
670
671 void sieve_editor_present(SieveEditorPage *page)
672 {
673         gtk_window_present(GTK_WINDOW(page->window));
674 }
675
676 void sieve_editor_show(SieveEditorPage *page)
677 {
678         gtk_widget_show_all(GTK_WIDGET(page->window));
679 }
680
681 static void sieve_editor_set_modified(SieveEditorPage *page,
682                 gboolean modified)
683 {
684         gchar *title;
685
686         page->modified = modified;
687         cm_menu_set_sensitive_full(page->ui_manager, "Menu/Filter/Revert",
688                         modified);
689
690         title = g_strdup_printf(_("%s - Sieve Filter%s"), page->script_name,
691                         modified ? _(" [Edited]") : "");
692         gtk_window_set_title (GTK_WINDOW (page->window), title);
693         g_free(title);
694
695         if (modified) {
696                 sieve_editor_set_status(page, "");
697                 sieve_editor_set_status_icon(page, NULL);
698         }
699 }
700
701 static void got_data_loading(SieveSession *session, gboolean aborted,
702                 gchar *contents, SieveEditorPage *page)
703 {
704         if (aborted)
705                 return;
706         if (contents == NULL) {
707                 /* end of data */
708                 sieve_editor_set_status(page, "");
709                 return;
710         }
711         if (contents == (void *)-1) {
712                 /* error */
713                 if (page->first_line) {
714                         /* no data. show error in manager window */
715                         if (page->on_load_error)
716                                 page->on_load_error(session, page->on_load_error_data);
717                 } else {
718                         /* partial failure. show error in editor window */
719                         sieve_editor_set_status(page, _("Unable to get script contents"));
720                         sieve_editor_set_status_icon(page, GTK_STOCK_DIALOG_ERROR);
721                 }
722                 return;
723         }
724
725         if (page->first_line) {
726                 page->first_line = FALSE;
727                 sieve_editor_show(page);
728         } else {
729                 sieve_editor_append_text(page, "\n", 1);
730         }
731         sieve_editor_append_text(page, contents, strlen(contents));
732 }
733
734 /* load the script for this editor */
735 void sieve_editor_load(SieveEditorPage *page,
736                 sieve_session_cb_fn on_load_error, gpointer load_error_data)
737 {
738         page->first_line = TRUE;
739         page->on_load_error = on_load_error;
740         page->on_load_error_data = load_error_data;
741         sieve_editor_set_status(page, _("Loading..."));
742         sieve_editor_set_status_icon(page, NULL);
743         sieve_session_get_script(page->session, page->script_name,
744                         (sieve_session_data_cb_fn)got_data_loading, page);
745 }