No issues found
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2 of the License, or (at your option) version 3.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with the program; if not, see <http://www.gnu.org/licenses/>
14 *
15 *
16 * Authors:
17 * Jeffrey Stedfast <fejj@ximian.com>
18 *
19 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
20 *
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include "em-composer-prefs.h"
28
29 #include <string.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <unistd.h>
33 #include <fcntl.h>
34
35 #include <glib/gi18n.h>
36 #include <glib/gstdio.h>
37
38 #include <gtkhtml/gtkhtml.h>
39 #include <editor/gtkhtml-spell-language.h>
40
41 #include <e-util/e-util.h>
42 #include <e-util/e-util-private.h>
43
44 #include <composer/e-msg-composer.h>
45
46 #include <shell/e-shell-utils.h>
47
48 #include <misc/e-charset-combo-box.h>
49 #include <misc/e-mail-signature-manager.h>
50
51 #include <mail/em-config.h>
52 #include <mail/em-folder-selection-button.h>
53 #include <mail/e-mail-junk-options.h>
54
55 G_DEFINE_TYPE (
56 EMComposerPrefs,
57 em_composer_prefs,
58 GTK_TYPE_VBOX)
59
60 static gboolean
61 transform_old_to_new_reply_style (GBinding *binding,
62 const GValue *source_value,
63 GValue *target_value,
64 gpointer user_data)
65 {
66 gboolean success = TRUE;
67
68 /* XXX This is the kind of legacy crap we wind up
69 * with when we don't migrate things properly. */
70
71 switch (g_value_get_int (source_value)) {
72 case 0: /* Quoted: 0 -> 2 */
73 g_value_set_int (target_value, 2);
74 break;
75
76 case 1: /* Do Not Quote: 1 -> 3 */
77 g_value_set_int (target_value, 3);
78 break;
79
80 case 2: /* Attach: 2 -> 0 */
81 g_value_set_int (target_value, 0);
82 break;
83
84 case 3: /* Outlook: 3 -> 1 */
85 g_value_set_int (target_value, 1);
86 break;
87
88 default:
89 success = FALSE;
90 break;
91 }
92
93 return success;
94 }
95
96 static gboolean
97 transform_new_to_old_reply_style (GBinding *binding,
98 const GValue *source_value,
99 GValue *target_value,
100 gpointer user_data)
101 {
102 gboolean success = TRUE;
103
104 /* XXX This is the kind of legacy crap we wind up
105 * with when we don't migrate things properly. */
106
107 switch (g_value_get_int (source_value)) {
108 case 0: /* Attach: 0 -> 2 */
109 g_value_set_int (target_value, 2);
110 break;
111
112 case 1: /* Outlook: 1 -> 3 */
113 g_value_set_int (target_value, 3);
114 break;
115
116 case 2: /* Quoted: 2 -> 0 */
117 g_value_set_int (target_value, 0);
118 break;
119
120 case 3: /* Do Not Quote: 3 -> 1 */
121 g_value_set_int (target_value, 1);
122 break;
123
124 default:
125 success = FALSE;
126 break;
127 }
128
129 return success;
130 }
131
132 static void
133 composer_prefs_dispose (GObject *object)
134 {
135 EMComposerPrefs *prefs = (EMComposerPrefs *) object;
136
137 if (prefs->builder != NULL) {
138 g_object_unref (prefs->builder);
139 prefs->builder = NULL;
140 }
141
142 /* Chain up to parent's dispose() method. */
143 G_OBJECT_CLASS (em_composer_prefs_parent_class)->dispose (object);
144 }
145
146 static void
147 em_composer_prefs_class_init (EMComposerPrefsClass *class)
148 {
149 GObjectClass *object_class;
150
151 object_class = G_OBJECT_CLASS (class);
152 object_class->dispose = composer_prefs_dispose;
153 }
154
155 static void
156 em_composer_prefs_init (EMComposerPrefs *prefs)
157 {
158 }
159
160 static void
161 spell_language_toggled_cb (GtkCellRendererToggle *renderer,
162 const gchar *path_string,
163 EMComposerPrefs *prefs)
164 {
165 GtkTreeModel *model;
166 GtkTreePath *path;
167 GtkTreeIter iter;
168 gboolean active;
169 gboolean valid;
170
171 model = prefs->language_model;
172
173 /* Convert the path string to a tree iterator. */
174 path = gtk_tree_path_new_from_string (path_string);
175 valid = gtk_tree_model_get_iter (model, &iter, path);
176 gtk_tree_path_free (path);
177 g_return_if_fail (valid);
178
179 /* Toggle the active state. */
180 gtk_tree_model_get (model, &iter, 0, &active, -1);
181 gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, !active, -1);
182 }
183
184 static void
185 spell_language_save (EMComposerPrefs *prefs)
186 {
187 GList *spell_languages = NULL;
188 GtkTreeModel *model;
189 GtkTreeIter iter;
190 gboolean valid;
191
192 model = prefs->language_model;
193
194 /* Build a list of active spell languages. */
195 valid = gtk_tree_model_get_iter_first (model, &iter);
196 while (valid) {
197 const GtkhtmlSpellLanguage *language;
198 gboolean active;
199
200 gtk_tree_model_get (
201 model, &iter, 0, &active, 2, &language, -1);
202
203 if (active)
204 spell_languages = g_list_prepend (
205 spell_languages, (gpointer) language);
206
207 valid = gtk_tree_model_iter_next (model, &iter);
208 }
209 spell_languages = g_list_reverse (spell_languages);
210
211 /* Update the GSettings value. */
212 e_save_spell_languages (spell_languages);
213
214 g_list_free (spell_languages);
215 }
216
217 static void
218 spell_setup (EMComposerPrefs *prefs)
219 {
220 const GList *available_languages;
221 GList *active_languages;
222 GtkListStore *store;
223
224 store = GTK_LIST_STORE (prefs->language_model);
225 available_languages = gtkhtml_spell_language_get_available ();
226
227 active_languages = e_load_spell_languages ();
228
229 /* Populate the GtkListStore. */
230 while (available_languages != NULL) {
231 const GtkhtmlSpellLanguage *language;
232 GtkTreeIter tree_iter;
233 const gchar *name;
234 gboolean active;
235
236 language = available_languages->data;
237 name = gtkhtml_spell_language_get_name (language);
238 active = (g_list_find (active_languages, language) != NULL);
239
240 gtk_list_store_append (store, &tree_iter);
241
242 gtk_list_store_set (
243 store, &tree_iter,
244 0, active, 1, name, 2, language, -1);
245
246 available_languages = available_languages->next;
247 }
248
249 g_list_free (active_languages);
250 }
251
252 static GtkWidget *
253 emcp_widget_glade (EConfig *ec,
254 EConfigItem *item,
255 GtkWidget *parent,
256 GtkWidget *old,
257 gint position,
258 gpointer data)
259 {
260 EMComposerPrefs *prefs = data;
261
262 return e_builder_get_widget (prefs->builder, item->label);
263 }
264
265 /* plugin meta-data */
266 static EMConfigItem emcp_items[] = {
267
268 { E_CONFIG_BOOK,
269 (gchar *) "",
270 (gchar *) "composer_toplevel",
271 emcp_widget_glade },
272
273 { E_CONFIG_PAGE,
274 (gchar *) "00.general",
275 (gchar *) "vboxComposerGeneral",
276 emcp_widget_glade },
277
278 { E_CONFIG_SECTION,
279 (gchar *) "00.general/00.behavior",
280 (gchar *) "default-behavior-vbox",
281 emcp_widget_glade },
282
283 { E_CONFIG_PAGE,
284 (gchar *) "10.signatures",
285 (gchar *) "vboxSignatures",
286 emcp_widget_glade },
287
288 /* signature/signatures and signature/preview parts not usable */
289
290 { E_CONFIG_PAGE,
291 (gchar *) "20.spellcheck",
292 (gchar *) "vboxSpellChecking",
293 emcp_widget_glade }
294 };
295
296 static void
297 emcp_free (EConfig *ec,
298 GSList *items,
299 gpointer data)
300 {
301 /* the prefs data is freed automagically */
302 g_slist_free (items);
303 }
304
305 static void
306 em_composer_prefs_construct (EMComposerPrefs *prefs,
307 EShell *shell)
308 {
309 GtkWidget *toplevel, *widget, *info_pixmap;
310 GtkWidget *container;
311 EShellSettings *shell_settings;
312 ESourceRegistry *registry;
313 GtkTreeView *view;
314 GtkListStore *store;
315 GtkTreeSelection *selection;
316 GtkCellRenderer *renderer;
317 EMConfig *ec;
318 EMConfigTargetPrefs *target;
319 GSList *l;
320 gint i;
321
322 registry = e_shell_get_registry (shell);
323 shell_settings = e_shell_get_shell_settings (shell);
324
325 /* Make sure our custom widget classes are registered with
326 * GType before we load the GtkBuilder definition file. */
327 E_TYPE_MAIL_JUNK_OPTIONS;
328 EM_TYPE_FOLDER_SELECTION_BUTTON;
329
330 prefs->builder = gtk_builder_new ();
331 e_load_ui_builder_definition (prefs->builder, "mail-config.ui");
332
333 /** @HookPoint-EMConfig: Mail Composer Preferences
334 * @Id: org.gnome.evolution.mail.composerPrefs
335 * @Type: E_CONFIG_BOOK
336 * @Class: org.gnome.evolution.mail.config:1.0
337 * @Target: EMConfigTargetPrefs
338 *
339 * The mail composer preferences settings page.
340 */
341 ec = em_config_new (E_CONFIG_BOOK, "org.gnome.evolution.mail.composerPrefs");
342 l = NULL;
343 for (i = 0; i < G_N_ELEMENTS (emcp_items); i++)
344 l = g_slist_prepend (l, &emcp_items[i]);
345 e_config_add_items ((EConfig *) ec, l, emcp_free, prefs);
346
347 /* General tab */
348
349 /* Default Behavior */
350
351 /* Express mode does not honor this setting. */
352 widget = e_builder_get_widget (prefs->builder, "chkSendHTML");
353 if (e_shell_get_express_mode (shell))
354 gtk_widget_hide (widget);
355 else
356 g_object_bind_property (
357 shell_settings, "composer-format-html",
358 widget, "active",
359 G_BINDING_BIDIRECTIONAL |
360 G_BINDING_SYNC_CREATE);
361
362 widget = e_builder_get_widget (prefs->builder, "chkPromptEmptySubject");
363 g_object_bind_property (
364 shell_settings, "composer-prompt-empty-subject",
365 widget, "active",
366 G_BINDING_BIDIRECTIONAL |
367 G_BINDING_SYNC_CREATE);
368
369 widget = e_builder_get_widget (prefs->builder, "chkPromptBccOnly");
370 g_object_bind_property (
371 shell_settings, "composer-prompt-only-bcc",
372 widget, "active",
373 G_BINDING_BIDIRECTIONAL |
374 G_BINDING_SYNC_CREATE);
375
376 widget = e_builder_get_widget (prefs->builder, "chkPromptPrivateListReply");
377 g_object_bind_property (
378 shell_settings, "composer-prompt-private-list-reply",
379 widget, "active",
380 G_BINDING_BIDIRECTIONAL |
381 G_BINDING_SYNC_CREATE);
382
383 widget = e_builder_get_widget (prefs->builder, "chkPromptReplyManyRecips");
384 g_object_bind_property (
385 shell_settings, "composer-prompt-reply-many-recips",
386 widget, "active",
387 G_BINDING_BIDIRECTIONAL |
388 G_BINDING_SYNC_CREATE);
389
390 widget = e_builder_get_widget (prefs->builder, "chkPromptListReplyTo");
391 g_object_bind_property (
392 shell_settings, "composer-prompt-list-reply-to",
393 widget, "active",
394 G_BINDING_BIDIRECTIONAL |
395 G_BINDING_SYNC_CREATE);
396
397 widget = e_builder_get_widget (prefs->builder, "chkPromptSendInvalidRecip");
398 g_object_bind_property (
399 shell_settings, "composer-prompt-send-invalid-recip",
400 widget, "active",
401 G_BINDING_BIDIRECTIONAL |
402 G_BINDING_SYNC_CREATE);
403
404 widget = e_builder_get_widget (prefs->builder, "chkAutoSmileys");
405 g_object_bind_property (
406 shell_settings, "composer-magic-smileys",
407 widget, "active",
408 G_BINDING_BIDIRECTIONAL |
409 G_BINDING_SYNC_CREATE);
410
411 widget = e_builder_get_widget (prefs->builder, "chkRequestReceipt");
412 g_object_bind_property (
413 shell_settings, "composer-request-receipt",
414 widget, "active",
415 G_BINDING_BIDIRECTIONAL |
416 G_BINDING_SYNC_CREATE);
417
418 widget = e_builder_get_widget (prefs->builder, "chkReplyStartBottom");
419 g_object_bind_property (
420 shell_settings, "composer-reply-start-bottom",
421 widget, "active",
422 G_BINDING_BIDIRECTIONAL |
423 G_BINDING_SYNC_CREATE);
424
425 widget = e_builder_get_widget (prefs->builder, "chkOutlookFilenames");
426 g_object_bind_property (
427 shell_settings, "composer-outlook-filenames",
428 widget, "active",
429 G_BINDING_BIDIRECTIONAL |
430 G_BINDING_SYNC_CREATE);
431
432 widget = e_builder_get_widget (prefs->builder, "chkIgnoreListReplyTo");
433 g_object_bind_property (
434 shell_settings, "composer-ignore-list-reply-to",
435 widget, "active",
436 G_BINDING_BIDIRECTIONAL |
437 G_BINDING_SYNC_CREATE);
438
439 widget = e_builder_get_widget (prefs->builder, "chkGroupReplyToList");
440 g_object_bind_property (
441 shell_settings, "composer-group-reply-to-list",
442 widget, "active",
443 G_BINDING_BIDIRECTIONAL |
444 G_BINDING_SYNC_CREATE);
445
446 widget = e_builder_get_widget (prefs->builder, "chkSignReplyIfSigned");
447 g_object_bind_property (
448 shell_settings, "composer-sign-reply-if-signed",
449 widget, "active",
450 G_BINDING_BIDIRECTIONAL |
451 G_BINDING_SYNC_CREATE);
452
453 widget = e_builder_get_widget (prefs->builder, "chkTopSignature");
454 g_object_bind_property (
455 shell_settings, "composer-top-signature",
456 widget, "active",
457 G_BINDING_BIDIRECTIONAL |
458 G_BINDING_SYNC_CREATE);
459
460 widget = e_builder_get_widget (prefs->builder, "chkEnableSpellChecking");
461 g_object_bind_property (
462 shell_settings, "composer-inline-spelling",
463 widget, "active",
464 G_BINDING_BIDIRECTIONAL |
465 G_BINDING_SYNC_CREATE);
466
467 widget = e_charset_combo_box_new ();
468 container = e_builder_get_widget (prefs->builder, "hboxComposerCharset");
469 gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
470 gtk_widget_show (widget);
471 g_object_bind_property (
472 shell_settings, "composer-charset",
473 widget, "charset",
474 G_BINDING_BIDIRECTIONAL |
475 G_BINDING_SYNC_CREATE);
476
477 container = e_builder_get_widget (prefs->builder, "lblCharset");
478 gtk_label_set_mnemonic_widget (GTK_LABEL (container), widget);
479
480 /* Spell Checking */
481 widget = e_builder_get_widget (prefs->builder, "listSpellCheckLanguage");
482 view = GTK_TREE_VIEW (widget);
483 store = gtk_list_store_new (
484 3, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_POINTER);
485 g_signal_connect_swapped (
486 store, "row-changed",
487 G_CALLBACK (spell_language_save), prefs);
488 prefs->language_model = GTK_TREE_MODEL (store);
489 gtk_tree_view_set_model (view, prefs->language_model);
490 renderer = gtk_cell_renderer_toggle_new ();
491 g_signal_connect (
492 renderer, "toggled",
493 G_CALLBACK (spell_language_toggled_cb), prefs);
494 gtk_tree_view_insert_column_with_attributes (
495 view, -1, _("Enabled"),
496 renderer, "active", 0, NULL);
497
498 gtk_tree_view_insert_column_with_attributes (
499 view, -1, _("Language(s)"),
500 gtk_cell_renderer_text_new (),
501 "text", 1, NULL);
502 selection = gtk_tree_view_get_selection (view);
503 gtk_tree_selection_set_mode (selection, GTK_SELECTION_NONE);
504 info_pixmap = e_builder_get_widget (prefs->builder, "pixmapSpellInfo");
505 gtk_image_set_from_stock (
506 GTK_IMAGE (info_pixmap),
507 GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_BUTTON);
508
509 widget = e_builder_get_widget (prefs->builder, "colorButtonSpellCheckColor");
510 g_object_bind_property_full (
511 shell_settings, "composer-spell-color",
512 widget, "color",
513 G_BINDING_BIDIRECTIONAL |
514 G_BINDING_SYNC_CREATE,
515 e_binding_transform_string_to_color,
516 e_binding_transform_color_to_string,
517 NULL, (GDestroyNotify) NULL);
518
519 spell_setup (prefs);
520
521 /* Forwards and Replies */
522 widget = e_builder_get_widget (prefs->builder, "comboboxForwardStyle");
523 g_object_bind_property (
524 shell_settings, "mail-forward-style",
525 widget, "active",
526 G_BINDING_BIDIRECTIONAL |
527 G_BINDING_SYNC_CREATE);
528
529 widget = e_builder_get_widget (prefs->builder, "comboboxReplyStyle");
530 g_object_bind_property_full (
531 shell_settings, "mail-reply-style",
532 widget, "active",
533 G_BINDING_BIDIRECTIONAL |
534 G_BINDING_SYNC_CREATE,
535 transform_old_to_new_reply_style,
536 transform_new_to_old_reply_style,
537 NULL, (GDestroyNotify) NULL);
538
539 /* Signatures */
540 container = e_builder_get_widget (
541 prefs->builder, "signature-alignment");
542 widget = e_mail_signature_manager_new (registry);
543 gtk_container_add (GTK_CONTAINER (container), widget);
544 gtk_widget_show (widget);
545
546 /* The mail shell backend responds to the "window-added" signal
547 * that this triggers and configures it with composer preferences. */
548 g_signal_connect_swapped (
549 widget, "editor-created",
550 G_CALLBACK (gtk_application_add_window), shell);
551
552 /* Express mode does not honor this setting. */
553 if (!e_shell_get_express_mode (shell))
554 g_object_bind_property (
555 shell_settings, "composer-format-html",
556 widget, "prefer-html",
557 G_BINDING_SYNC_CREATE);
558
559 /* Sanitize the dialog for Express mode */
560 e_shell_hide_widgets_for_express_mode (
561 shell, prefs->builder,
562 "chkOutlookFilenames",
563 "chkTopSignature",
564 NULL);
565
566 /* get our toplevel widget */
567 target = em_config_target_new_prefs (ec);
568 e_config_set_target ((EConfig *) ec, (EConfigTarget *) target);
569 toplevel = e_config_create_widget ((EConfig *) ec);
570 gtk_container_add (GTK_CONTAINER (prefs), toplevel);
571 }
572
573 GtkWidget *
574 em_composer_prefs_new (EPreferencesWindow *window)
575 {
576 EShell *shell;
577 EMComposerPrefs *prefs;
578
579 shell = e_preferences_window_get_shell (window);
580 g_return_val_if_fail (E_IS_SHELL (shell), NULL);
581
582 prefs = g_object_new (EM_TYPE_COMPOSER_PREFS, NULL);
583 em_composer_prefs_construct (prefs, shell);
584
585 return GTK_WIDGET (prefs);
586 }