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