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", NULL, NULL, NULL },
80 /* menus */
81 	{"Filter",			NULL, N_("_Filter"), NULL, NULL, NULL  },
82 	{"Edit",			NULL, N_("_Edit"), NULL, NULL, NULL  },
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, "---", NULL, NULL, 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, "---", NULL, NULL, NULL },
102 	{"Edit/Find",		NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(sieve_editor_find_cb) },
103 };
104 
105 
sieve_editors_close()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 
sieve_editor_append_text(SieveEditorPage * page,gchar * text,gint len)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 
sieve_editor_get_text(SieveEditorPage * page,gchar ** text)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 
sieve_editor_set_status(SieveEditorPage * page,const gchar * status)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 
sieve_editor_set_status_icon(SieveEditorPage * page,const gchar * img_id)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 
sieve_editor_append_status(SieveEditorPage * page,const gchar * new_status)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. */
sieve_editor_update_status(SieveEditorPage * page,SieveResult * result)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 
sieve_editor_undo_cb(GtkAction * action,SieveEditorPage * page)191 static void sieve_editor_undo_cb(GtkAction *action, SieveEditorPage *page)
192 {
193 	undo_undo(page->undostruct);
194 }
195 
sieve_editor_redo_cb(GtkAction * action,SieveEditorPage * page)196 static void sieve_editor_redo_cb(GtkAction *action, SieveEditorPage *page)
197 {
198 	undo_redo(page->undostruct);
199 }
200 
sieve_editor_cut_cb(GtkAction * action,SieveEditorPage * page)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 
sieve_editor_copy_cb(GtkAction * action,SieveEditorPage * page)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 
sieve_editor_paste_cb(GtkAction * action,SieveEditorPage * page)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 
sieve_editor_allsel_cb(GtkAction * action,SieveEditorPage * page)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 
sieve_editor_set_position(void * obj,gint pos)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 
sieve_editor_search_string(void * obj,const gchar * str,gboolean case_sens)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 
sieve_editor_search_string_backward(void * obj,const gchar * str,gboolean case_sens)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 
sieve_editor_search(SieveEditorPage * page)273 static void sieve_editor_search(SieveEditorPage *page)
274 {
275 	message_search_other(&search_interface, page);
276 }
277 
278 /* Actions */
279 
got_data_reverting(SieveSession * session,gboolean abort,gchar * contents,SieveEditorPage * page)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 
sieve_editor_revert(SieveEditorPage * page)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 
sieve_editor_revert_cb(GtkAction * action,SieveEditorPage * page)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 
got_data_saved(SieveSession * session,gboolean abort,SieveResult * result,SieveEditorPage * page)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 
sieve_editor_save(SieveEditorPage * page)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 
sieve_editor_save_cb(GtkAction * action,SieveEditorPage * page)377 static void sieve_editor_save_cb(GtkAction *action, SieveEditorPage *page)
378 {
379 	sieve_editor_save(page);
380 }
381 
sieve_editor_find_cb(GtkAction * action,SieveEditorPage * page)382 static void sieve_editor_find_cb(GtkAction *action, SieveEditorPage *page)
383 {
384 	sieve_editor_search(page);
385 }
386 
got_data_checked(SieveSession * session,gboolean abort,SieveResult * result,SieveEditorPage * page)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 
sieve_editor_check_cb(GtkAction * action,SieveEditorPage * page)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 
sieve_editor_changed_cb(GtkTextBuffer * textbuf,SieveEditorPage * page)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 
sieve_editor_destroy(SieveEditorPage * page)414 static void sieve_editor_destroy(SieveEditorPage *page)
415 {
416 	gtk_widget_destroy(page->window);
417 	undo_destroy(page->undostruct);
418 	g_free(page->script_name);
419 	g_free(page);
420 }
421 
sieve_editor_close(SieveEditorPage * page)422 void sieve_editor_close(SieveEditorPage *page)
423 {
424 	editors = g_slist_remove(editors, (gconstpointer)page);
425 	sieve_editor_destroy(page);
426 	sieve_sessions_discard_callbacks(page);
427 }
428 
sieve_editor_confirm_close(SieveEditorPage * page)429 static gboolean sieve_editor_confirm_close(SieveEditorPage *page)
430 {
431 	if (page->modified) {
432 		switch (alertpanel(_("Save changes"),
433 				_("This script has been modified. Save the latest changes?"),
434 				_("_Discard"), _("_Save"), GTK_STOCK_CANCEL,
435 				ALERTFOCUS_SECOND)) {
436 			case G_ALERTDEFAULT:
437 				return TRUE;
438 			case G_ALERTALTERNATE:
439 				page->closing = TRUE;
440 				sieve_editor_save(page);
441 				return FALSE;
442 			default:
443 				return FALSE;
444 		}
445 	}
446 	return TRUE;
447 }
448 
sieve_editor_close_cb(GtkAction * action,SieveEditorPage * page)449 static void sieve_editor_close_cb(GtkAction *action, SieveEditorPage *page)
450 {
451 	if (sieve_editor_confirm_close(page)) {
452 		sieve_editor_close(page);
453 	}
454 }
455 
sieve_editor_delete_cb(GtkWidget * widget,GdkEventAny * event,SieveEditorPage * page)456 static gint sieve_editor_delete_cb(GtkWidget *widget, GdkEventAny *event,
457 		SieveEditorPage *page)
458 {
459 	sieve_editor_close_cb(NULL, page);
460 	return TRUE;
461 }
462 
463 /**
464  * sieve_editor_undo_state_changed:
465  *
466  * Change the sensivity of the menuentries undo and redo
467  **/
sieve_editor_undo_state_changed(UndoMain * undostruct,gint undo_state,gint redo_state,gpointer data)468 static void sieve_editor_undo_state_changed(UndoMain *undostruct,
469 		gint undo_state, gint redo_state, gpointer data)
470 {
471 	SieveEditorPage *page = (SieveEditorPage *)data;
472 
473 	switch (undo_state) {
474 	case UNDO_STATE_TRUE:
475 		if (!undostruct->undo_state) {
476 			undostruct->undo_state = TRUE;
477 			cm_menu_set_sensitive_full(page->ui_manager, "Menu/Edit/Undo", TRUE);
478 		}
479 		break;
480 	case UNDO_STATE_FALSE:
481 		if (undostruct->undo_state) {
482 			undostruct->undo_state = FALSE;
483 			cm_menu_set_sensitive_full(page->ui_manager, "Menu/Edit/Undo", FALSE);
484 		}
485 		break;
486 	case UNDO_STATE_UNCHANGED:
487 		break;
488 	case UNDO_STATE_REFRESH:
489 		cm_menu_set_sensitive_full(page->ui_manager, "Menu/Edit/Undo", undostruct->undo_state);
490 		break;
491 	default:
492 		g_warning("Undo state not recognized");
493 		break;
494 	}
495 
496 	switch (redo_state) {
497 	case UNDO_STATE_TRUE:
498 		if (!undostruct->redo_state) {
499 			undostruct->redo_state = TRUE;
500 			cm_menu_set_sensitive_full(page->ui_manager, "Menu/Edit/Redo", TRUE);
501 		}
502 		break;
503 	case UNDO_STATE_FALSE:
504 		if (undostruct->redo_state) {
505 			undostruct->redo_state = FALSE;
506 			cm_menu_set_sensitive_full(page->ui_manager, "Menu/Edit/Redo", FALSE);
507 		}
508 		break;
509 	case UNDO_STATE_UNCHANGED:
510 		break;
511 	case UNDO_STATE_REFRESH:
512 		cm_menu_set_sensitive_full(page->ui_manager, "Menu/Edit/Redo", undostruct->redo_state);
513 		break;
514 	default:
515 		g_warning("Redo state not recognized");
516 		break;
517 	}
518 }
519 
520 
sieve_editor_new(SieveSession * session,gchar * script_name)521 SieveEditorPage *sieve_editor_new(SieveSession *session, gchar *script_name)
522 {
523 	SieveEditorPage *page;
524 	GtkUIManager *ui_manager;
525 	UndoMain *undostruct;
526 	GtkWidget *window;
527 	GtkWidget *menubar;
528 	GtkWidget *vbox, *hbox, *hbox1;
529 	GtkWidget *scrolledwin;
530 	GtkWidget *text;
531 	GtkTextBuffer *buffer;
532 	GtkWidget *check_btn, *save_btn, *close_btn;
533 	GtkWidget *status_text;
534 	GtkWidget *status_icon;
535 
536 	page = g_new0(SieveEditorPage, 1);
537 
538 	/* window */
539 	window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "sieveeditor");
540 	gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
541 	MANAGE_WINDOW_SIGNALS_CONNECT (window);
542 	g_signal_connect(G_OBJECT(window), "delete_event",
543 			 G_CALLBACK(sieve_editor_delete_cb), page);
544 
545 	vbox = gtk_vbox_new(FALSE, 0);
546 	gtk_container_add(GTK_CONTAINER(window), vbox);
547 
548 	ui_manager = gtk_ui_manager_new();
549 	cm_menu_create_action_group_full(ui_manager,
550 			"Menu", sieve_editor_entries, G_N_ELEMENTS(sieve_editor_entries),
551 			page);
552 
553 	MENUITEM_ADDUI_MANAGER(ui_manager, "/", "Menu", NULL, GTK_UI_MANAGER_MENUBAR)
554 
555 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu", "Filter", "Filter", GTK_UI_MANAGER_MENU)
556 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu", "Edit", "Edit", GTK_UI_MANAGER_MENU)
557 
558 /* Filter menu */
559 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Filter", "Save", "Filter/Save", GTK_UI_MANAGER_MENUITEM)
560 MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Filter", "CheckSyntax", "Filter/CheckSyntax", GTK_UI_MANAGER_MENUITEM)
561 MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Filter", "Revert", "Filter/Revert", GTK_UI_MANAGER_MENUITEM)
562 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Filter", "Close", "Filter/Close", GTK_UI_MANAGER_MENUITEM)
563 
564 /* Edit menu */
565 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Undo", "Edit/Undo", GTK_UI_MANAGER_MENUITEM)
566 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Redo", "Edit/Redo", GTK_UI_MANAGER_MENUITEM)
567 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Separator1", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
568 
569 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Cut", "Edit/Cut", GTK_UI_MANAGER_MENUITEM)
570 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Copy", "Edit/Copy", GTK_UI_MANAGER_MENUITEM)
571 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Paste", "Edit/Paste", GTK_UI_MANAGER_MENUITEM)
572 
573 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "SelectAll", "Edit/SelectAll", GTK_UI_MANAGER_MENUITEM)
574 
575 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Separator2", "Edit/---", GTK_UI_MANAGER_SEPARATOR)
576 
577 	MENUITEM_ADDUI_MANAGER(ui_manager, "/Menu/Edit", "Find", "Edit/Find", GTK_UI_MANAGER_MENUITEM)
578 
579 	menubar = gtk_ui_manager_get_widget(ui_manager, "/Menu");
580 
581 	gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
582 	gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
583 
584 	cm_menu_set_sensitive_full(ui_manager, "Menu/Edit/Undo", FALSE);
585 	cm_menu_set_sensitive_full(ui_manager, "Menu/Edit/Redo", FALSE);
586 
587 	/* text */
588 	scrolledwin = gtk_scrolled_window_new(NULL, NULL);
589 	gtk_widget_set_size_request (scrolledwin, 660, 408);
590 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
591 				       GTK_POLICY_AUTOMATIC,
592 				       GTK_POLICY_AUTOMATIC);
593 	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
594 					    GTK_SHADOW_IN);
595 	gtk_box_pack_start(GTK_BOX(vbox), scrolledwin, TRUE, TRUE, 0);
596 
597 	text = gtk_text_view_new();
598 	gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
599 	gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
600 	gtk_container_add(GTK_CONTAINER(scrolledwin), text);
601 
602 	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
603 	g_signal_connect(G_OBJECT(buffer), "changed",
604 			 G_CALLBACK(sieve_editor_changed_cb), page);
605 
606 	/* set text font */
607 	if (prefs_common_get_prefs()->textfont) {
608 		PangoFontDescription *font_desc;
609 
610 		font_desc = pango_font_description_from_string
611 			(prefs_common_get_prefs()->textfont);
612 		if (font_desc) {
613 			gtk_widget_modify_font(text, font_desc);
614 			pango_font_description_free(font_desc);
615 		}
616 	}
617 
618 	hbox = gtk_hbox_new (FALSE, 8);
619 	gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
620 	gtk_container_set_border_width (GTK_CONTAINER (hbox), 8);
621 
622 	/* status */
623 	status_icon = gtk_image_new ();
624 	gtk_box_pack_start (GTK_BOX (hbox), status_icon, FALSE, FALSE, 0);
625 	status_text = gtk_label_new ("");
626 	gtk_box_pack_start (GTK_BOX (hbox), status_text, FALSE, FALSE, 0);
627 	gtk_label_set_justify (GTK_LABEL (status_text), GTK_JUSTIFY_LEFT);
628 
629 	/* buttons */
630 	gtkut_stock_with_text_button_set_create(&hbox1,
631 			&close_btn, GTK_STOCK_CANCEL, _("_Close"),
632 			&check_btn, GTK_STOCK_OK, _("Chec_k Syntax"),
633 			&save_btn, GTK_STOCK_SAVE, _("_Save"));
634 	gtk_box_pack_end (GTK_BOX (hbox), hbox1, FALSE, FALSE, 0);
635 	gtk_widget_grab_default (save_btn);
636 	g_signal_connect (G_OBJECT (close_btn), "clicked",
637 			  G_CALLBACK (sieve_editor_close_cb), page);
638 	g_signal_connect (G_OBJECT (check_btn), "clicked",
639 			  G_CALLBACK (sieve_editor_check_cb), page);
640 	g_signal_connect (G_OBJECT (save_btn), "clicked",
641 			  G_CALLBACK (sieve_editor_save_cb), page);
642 
643 	undostruct = undo_init(text);
644 	undo_set_change_state_func(undostruct, &sieve_editor_undo_state_changed,
645 			page);
646 
647 	page->window = window;
648 	page->ui_manager = ui_manager;
649 	page->text = text;
650 	page->undostruct = undostruct;
651 	page->session = session;
652 	page->script_name = script_name;
653 	page->status_text = status_text;
654 	page->status_icon = status_icon;
655 
656 	editors = g_slist_prepend(editors, page);
657 
658 	sieve_editor_set_modified(page, FALSE);
659 
660 	return page;
661 }
662 
sieve_editor_get(SieveSession * session,gchar * script_name)663 SieveEditorPage *sieve_editor_get(SieveSession *session, gchar *script_name)
664 {
665 	GSList *item;
666 	SieveEditorPage *page;
667 	for (item = editors; item; item = item->next) {
668 		page = (SieveEditorPage *)item->data;
669 		if (page->session == session &&
670 				strcmp(script_name, page->script_name) == 0)
671 			return page;
672 	}
673 	return NULL;
674 }
675 
sieve_editor_present(SieveEditorPage * page)676 void sieve_editor_present(SieveEditorPage *page)
677 {
678 	gtk_window_present(GTK_WINDOW(page->window));
679 }
680 
sieve_editor_show(SieveEditorPage * page)681 void sieve_editor_show(SieveEditorPage *page)
682 {
683 	gtk_widget_show_all(GTK_WIDGET(page->window));
684 }
685 
sieve_editor_set_modified(SieveEditorPage * page,gboolean modified)686 static void sieve_editor_set_modified(SieveEditorPage *page,
687 		gboolean modified)
688 {
689 	gchar *title;
690 
691 	page->modified = modified;
692 	cm_menu_set_sensitive_full(page->ui_manager, "Menu/Filter/Revert",
693 			modified);
694 
695 	title = g_strdup_printf(_("%s - Sieve Filter%s"), page->script_name,
696 			modified ? _(" [Edited]") : "");
697 	gtk_window_set_title (GTK_WINDOW (page->window), title);
698 	g_free(title);
699 
700 	if (modified) {
701 		sieve_editor_set_status(page, "");
702 		sieve_editor_set_status_icon(page, NULL);
703 	}
704 }
705 
got_data_loading(SieveSession * session,gboolean aborted,gchar * contents,SieveEditorPage * page)706 static void got_data_loading(SieveSession *session, gboolean aborted,
707 		gchar *contents, SieveEditorPage *page)
708 {
709 	if (aborted)
710 		return;
711 	if (contents == NULL) {
712 		/* end of data */
713 		sieve_editor_set_status(page, "");
714 		return;
715 	}
716 	if (contents == (void *)-1) {
717 		/* error */
718 		if (page->first_line) {
719 			/* no data. show error in manager window */
720 			if (page->on_load_error)
721 				page->on_load_error(session, page->on_load_error_data);
722 		} else {
723 			/* partial failure. show error in editor window */
724 			sieve_editor_set_status(page, _("Unable to get script contents"));
725 			sieve_editor_set_status_icon(page, GTK_STOCK_DIALOG_ERROR);
726 		}
727 		return;
728 	}
729 
730 	if (page->first_line) {
731 		page->first_line = FALSE;
732 		sieve_editor_show(page);
733 	}
734 	sieve_editor_append_text(page, contents, strlen(contents));
735 }
736 
737 /* load the script for this editor */
sieve_editor_load(SieveEditorPage * page,sieve_session_cb_fn on_load_error,gpointer load_error_data)738 void sieve_editor_load(SieveEditorPage *page,
739 		sieve_session_cb_fn on_load_error, gpointer load_error_data)
740 {
741 	page->first_line = TRUE;
742 	page->on_load_error = on_load_error;
743 	page->on_load_error_data = load_error_data;
744 	sieve_editor_set_status(page, _("Loading..."));
745 	sieve_editor_set_status_icon(page, NULL);
746 	sieve_session_get_script(page->session, page->script_name,
747 			(sieve_session_data_cb_fn)got_data_loading, page);
748 }
749