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