2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2016 Hiroyuki Yamamoto and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "claws-features.h"
26 #ifndef PANGO_ENABLE_ENGINE
27 # define PANGO_ENABLE_ENGINE
31 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
35 #include <pango/pango-break.h>
40 #include <sys/types.h>
46 # include <sys/wait.h>
50 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
54 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
61 #include "mainwindow.h"
63 #ifndef USE_ALT_ADDRBOOK
64 #include "addressbook.h"
66 #include "addressbook-dbus.h"
67 #include "addressadd.h"
69 #include "folderview.h"
72 #include "stock_pixmap.h"
73 #include "send_message.h"
76 #include "customheader.h"
77 #include "prefs_common.h"
78 #include "prefs_account.h"
82 #include "procheader.h"
84 #include "statusbar.h"
86 #include "quoted-printable.h"
90 #include "gtkshruler.h"
92 #include "alertpanel.h"
93 #include "manage_window.h"
95 #include "folder_item_prefs.h"
96 #include "addr_compl.h"
97 #include "quote_fmt.h"
99 #include "foldersel.h"
102 #include "message_search.h"
103 #include "combobox.h"
107 #include "autofaces.h"
108 #include "spell_entry.h"
121 #define N_ATTACH_COLS (N_COL_COLUMNS)
125 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
126 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
127 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
128 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
129 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
130 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
131 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
132 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
133 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
134 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
135 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
136 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
137 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
138 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
139 } ComposeCallAdvancedAction;
143 PRIORITY_HIGHEST = 1,
152 COMPOSE_INSERT_SUCCESS,
153 COMPOSE_INSERT_READ_ERROR,
154 COMPOSE_INSERT_INVALID_CHARACTER,
155 COMPOSE_INSERT_NO_FILE
156 } ComposeInsertResult;
160 COMPOSE_WRITE_FOR_SEND,
161 COMPOSE_WRITE_FOR_STORE
166 COMPOSE_QUOTE_FORCED,
173 SUBJECT_FIELD_PRESENT,
178 #define B64_LINE_SIZE 57
179 #define B64_BUFFSIZE 77
181 #define MAX_REFERENCES_LEN 999
183 #define COMPOSE_DRAFT_TIMEOUT_UNSET -1
184 #define COMPOSE_DRAFT_TIMEOUT_FORBIDDEN -2
186 static GdkColor default_to_bgcolor = {
193 static GdkColor default_to_color = {
200 static GList *compose_list = NULL;
201 static GSList *extra_headers = NULL;
203 static Compose *compose_generic_new (PrefsAccount *account,
207 GList *listAddress );
209 static Compose *compose_create (PrefsAccount *account,
214 static void compose_entry_mark_default_to (Compose *compose,
215 const gchar *address);
216 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
217 ComposeQuoteMode quote_mode,
221 static Compose *compose_forward_multiple (PrefsAccount *account,
222 GSList *msginfo_list);
223 static Compose *compose_reply (MsgInfo *msginfo,
224 ComposeQuoteMode quote_mode,
229 static Compose *compose_reply_mode (ComposeMode mode,
230 GSList *msginfo_list,
232 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
233 static void compose_update_privacy_systems_menu(Compose *compose);
235 static GtkWidget *compose_account_option_menu_create
237 static void compose_set_out_encoding (Compose *compose);
238 static void compose_set_template_menu (Compose *compose);
239 static void compose_destroy (Compose *compose);
241 static MailField compose_entries_set (Compose *compose,
243 ComposeEntryType to_type);
244 static gint compose_parse_header (Compose *compose,
246 static gint compose_parse_manual_headers (Compose *compose,
248 HeaderEntry *entries);
249 static gchar *compose_parse_references (const gchar *ref,
252 static gchar *compose_quote_fmt (Compose *compose,
258 gboolean need_unescape,
259 const gchar *err_msg);
261 static void compose_reply_set_entry (Compose *compose,
267 followup_and_reply_to);
268 static void compose_reedit_set_entry (Compose *compose,
271 static void compose_insert_sig (Compose *compose,
273 static ComposeInsertResult compose_insert_file (Compose *compose,
276 static gboolean compose_attach_append (Compose *compose,
279 const gchar *content_type,
280 const gchar *charset);
281 static void compose_attach_parts (Compose *compose,
284 static gboolean compose_beautify_paragraph (Compose *compose,
285 GtkTextIter *par_iter,
287 static void compose_wrap_all (Compose *compose);
288 static void compose_wrap_all_full (Compose *compose,
291 static void compose_set_title (Compose *compose);
292 static void compose_select_account (Compose *compose,
293 PrefsAccount *account,
296 static PrefsAccount *compose_current_mail_account(void);
297 /* static gint compose_send (Compose *compose); */
298 static gboolean compose_check_for_valid_recipient
300 static gboolean compose_check_entries (Compose *compose,
301 gboolean check_everything);
302 static gint compose_write_to_file (Compose *compose,
305 gboolean attach_parts);
306 static gint compose_write_body_to_file (Compose *compose,
308 static gint compose_remove_reedit_target (Compose *compose,
310 static void compose_remove_draft (Compose *compose);
311 static gint compose_queue_sub (Compose *compose,
315 gboolean check_subject,
316 gboolean remove_reedit_target);
317 static int compose_add_attachments (Compose *compose,
319 static gchar *compose_get_header (Compose *compose);
320 static gchar *compose_get_manual_headers_info (Compose *compose);
322 static void compose_convert_header (Compose *compose,
327 gboolean addr_field);
329 static void compose_attach_info_free (AttachInfo *ainfo);
330 static void compose_attach_remove_selected (GtkAction *action,
333 static void compose_template_apply (Compose *compose,
336 static void compose_attach_property (GtkAction *action,
338 static void compose_attach_property_create (gboolean *cancelled);
339 static void attach_property_ok (GtkWidget *widget,
340 gboolean *cancelled);
341 static void attach_property_cancel (GtkWidget *widget,
342 gboolean *cancelled);
343 static gint attach_property_delete_event (GtkWidget *widget,
345 gboolean *cancelled);
346 static gboolean attach_property_key_pressed (GtkWidget *widget,
348 gboolean *cancelled);
350 static void compose_exec_ext_editor (Compose *compose);
352 static gint compose_exec_ext_editor_real (const gchar *file,
353 GdkNativeWindow socket_wid);
354 static gboolean compose_ext_editor_kill (Compose *compose);
355 static gboolean compose_input_cb (GIOChannel *source,
356 GIOCondition condition,
358 static void compose_set_ext_editor_sensitive (Compose *compose,
360 static gboolean compose_get_ext_editor_cmd_valid();
361 static gboolean compose_get_ext_editor_uses_socket();
362 static gboolean compose_ext_editor_plug_removed_cb
365 #endif /* G_OS_UNIX */
367 static void compose_undo_state_changed (UndoMain *undostruct,
372 static void compose_create_header_entry (Compose *compose);
373 static void compose_add_header_entry (Compose *compose, const gchar *header,
374 gchar *text, ComposePrefType pref_type);
375 static void compose_remove_header_entries(Compose *compose);
377 static void compose_update_priority_menu_item(Compose * compose);
379 static void compose_spell_menu_changed (void *data);
380 static void compose_dict_changed (void *data);
382 static void compose_add_field_list ( Compose *compose,
383 GList *listAddress );
385 /* callback functions */
387 static void compose_notebook_size_alloc (GtkNotebook *notebook,
388 GtkAllocation *allocation,
390 static gboolean compose_edit_size_alloc (GtkEditable *widget,
391 GtkAllocation *allocation,
392 GtkSHRuler *shruler);
393 static void account_activated (GtkComboBox *optmenu,
395 static void attach_selected (GtkTreeView *tree_view,
396 GtkTreePath *tree_path,
397 GtkTreeViewColumn *column,
399 static gboolean attach_button_pressed (GtkWidget *widget,
400 GdkEventButton *event,
402 static gboolean attach_key_pressed (GtkWidget *widget,
405 static void compose_send_cb (GtkAction *action, gpointer data);
406 static void compose_send_later_cb (GtkAction *action, gpointer data);
408 static void compose_save_cb (GtkAction *action,
411 static void compose_attach_cb (GtkAction *action,
413 static void compose_insert_file_cb (GtkAction *action,
415 static void compose_insert_sig_cb (GtkAction *action,
417 static void compose_replace_sig_cb (GtkAction *action,
420 static void compose_close_cb (GtkAction *action,
422 static void compose_print_cb (GtkAction *action,
425 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
427 static void compose_address_cb (GtkAction *action,
429 static void about_show_cb (GtkAction *action,
431 static void compose_template_activate_cb(GtkWidget *widget,
434 static void compose_ext_editor_cb (GtkAction *action,
437 static gint compose_delete_cb (GtkWidget *widget,
441 static void compose_undo_cb (GtkAction *action,
443 static void compose_redo_cb (GtkAction *action,
445 static void compose_cut_cb (GtkAction *action,
447 static void compose_copy_cb (GtkAction *action,
449 static void compose_paste_cb (GtkAction *action,
451 static void compose_paste_as_quote_cb (GtkAction *action,
453 static void compose_paste_no_wrap_cb (GtkAction *action,
455 static void compose_paste_wrap_cb (GtkAction *action,
457 static void compose_allsel_cb (GtkAction *action,
460 static void compose_advanced_action_cb (GtkAction *action,
463 static void compose_grab_focus_cb (GtkWidget *widget,
466 static void compose_changed_cb (GtkTextBuffer *textbuf,
469 static void compose_wrap_cb (GtkAction *action,
471 static void compose_wrap_all_cb (GtkAction *action,
473 static void compose_find_cb (GtkAction *action,
475 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
477 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
480 static void compose_toggle_ruler_cb (GtkToggleAction *action,
482 static void compose_toggle_sign_cb (GtkToggleAction *action,
484 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
486 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
487 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
488 static void activate_privacy_system (Compose *compose,
489 PrefsAccount *account,
491 static void compose_use_signing(Compose *compose, gboolean use_signing);
492 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
493 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
495 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
497 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
498 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
499 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
501 static void compose_attach_drag_received_cb (GtkWidget *widget,
502 GdkDragContext *drag_context,
505 GtkSelectionData *data,
509 static void compose_insert_drag_received_cb (GtkWidget *widget,
510 GdkDragContext *drag_context,
513 GtkSelectionData *data,
517 static void compose_header_drag_received_cb (GtkWidget *widget,
518 GdkDragContext *drag_context,
521 GtkSelectionData *data,
526 static gboolean compose_drag_drop (GtkWidget *widget,
527 GdkDragContext *drag_context,
529 guint time, gpointer user_data);
530 static gboolean completion_set_focus_to_subject
535 static void text_inserted (GtkTextBuffer *buffer,
540 static Compose *compose_generic_reply(MsgInfo *msginfo,
541 ComposeQuoteMode quote_mode,
545 gboolean followup_and_reply_to,
548 static void compose_headerentry_changed_cb (GtkWidget *entry,
549 ComposeHeaderEntry *headerentry);
550 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
552 ComposeHeaderEntry *headerentry);
553 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
554 ComposeHeaderEntry *headerentry);
556 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
558 static void compose_allow_user_actions (Compose *compose, gboolean allow);
560 static void compose_nothing_cb (GtkAction *action, gpointer data)
566 static void compose_check_all (GtkAction *action, gpointer data);
567 static void compose_highlight_all (GtkAction *action, gpointer data);
568 static void compose_check_backwards (GtkAction *action, gpointer data);
569 static void compose_check_forwards_go (GtkAction *action, gpointer data);
572 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
574 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
577 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
578 FolderItem *folder_item);
580 static void compose_attach_update_label(Compose *compose);
581 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
582 gboolean respect_default_to);
583 static void compose_subject_entry_activated(GtkWidget *widget, gpointer data);
584 static void from_name_activate_cb(GtkWidget *widget, gpointer data);
586 static GtkActionEntry compose_popup_entries[] =
588 {"Compose", NULL, "Compose" },
589 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
590 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
591 {"Compose/---", NULL, "---", NULL, NULL, NULL },
592 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
595 static GtkActionEntry compose_entries[] =
597 {"Menu", NULL, "Menu" },
599 {"Message", NULL, N_("_Message") },
600 {"Edit", NULL, N_("_Edit") },
602 {"Spelling", NULL, N_("_Spelling") },
604 {"Options", NULL, N_("_Options") },
605 {"Tools", NULL, N_("_Tools") },
606 {"Help", NULL, N_("_Help") },
608 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
609 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
610 {"Message/---", NULL, "---" },
612 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
613 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
614 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
615 {"Message/ReplaceSig", NULL, N_("_Replace signature"), NULL, NULL, G_CALLBACK(compose_replace_sig_cb) },
616 /* {"Message/---", NULL, "---" }, */
617 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
618 /* {"Message/---", NULL, "---" }, */
619 {"Message/Print", NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) },
620 /* {"Message/---", NULL, "---" }, */
621 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
624 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
625 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
626 {"Edit/---", NULL, "---" },
628 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
629 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
630 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
632 {"Edit/SpecialPaste", NULL, N_("_Special paste") },
633 {"Edit/SpecialPaste/AsQuotation", NULL, N_("As _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
634 {"Edit/SpecialPaste/Wrapped", NULL, N_("_Wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
635 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_Unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
637 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
639 {"Edit/Advanced", NULL, N_("A_dvanced") },
640 {"Edit/Advanced/BackChar", NULL, N_("Move a character backward"), "<shift><control>B", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER*/
641 {"Edit/Advanced/ForwChar", NULL, N_("Move a character forward"), "<shift><control>F", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER*/
642 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
643 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
644 {"Edit/Advanced/BegLine", NULL, N_("Move to beginning of line"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE*/
645 {"Edit/Advanced/EndLine", NULL, N_("Move to end of line"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE*/
646 {"Edit/Advanced/PrevLine", NULL, N_("Move to previous line"), "<control>P", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE*/
647 {"Edit/Advanced/NextLine", NULL, N_("Move to next line"), "<control>N", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE*/
648 {"Edit/Advanced/DelBackChar", NULL, N_("Delete a character backward"), "<control>H", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER*/
649 {"Edit/Advanced/DelForwChar", NULL, N_("Delete a character forward"), "<control>D", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER*/
650 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
651 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
652 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
653 {"Edit/Advanced/DelEndLine", NULL, N_("Delete to end of line"), "<control>K", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END*/
655 /* {"Edit/---", NULL, "---" }, */
656 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
658 /* {"Edit/---", NULL, "---" }, */
659 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
660 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
661 /* {"Edit/---", NULL, "---" }, */
662 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
665 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
666 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
667 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
668 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
670 {"Spelling/---", NULL, "---" },
671 {"Spelling/Options", NULL, N_("_Options") },
676 {"Options/ReplyMode", NULL, N_("Reply _mode") },
677 {"Options/---", NULL, "---" },
678 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
679 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
681 /* {"Options/---", NULL, "---" }, */
683 {"Options/Priority", NULL, N_("_Priority") },
685 {"Options/Encoding", NULL, N_("Character _encoding") },
686 {"Options/Encoding/---", NULL, "---" },
687 #define ENC_ACTION(cs_char,c_char,string) \
688 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
690 {"Options/Encoding/Western", NULL, N_("Western European") },
691 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
692 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
693 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
694 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
695 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
696 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
697 {"Options/Encoding/Korean", NULL, N_("Korean") },
698 {"Options/Encoding/Thai", NULL, N_("Thai") },
701 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
703 {"Tools/Template", NULL, N_("_Template") },
704 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
705 {"Tools/Actions", NULL, N_("Actio_ns") },
706 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
709 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
712 static GtkToggleActionEntry compose_toggle_entries[] =
714 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
715 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
716 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
717 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
718 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
719 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
720 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
723 static GtkRadioActionEntry compose_radio_rm_entries[] =
725 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
726 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
727 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
728 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
731 static GtkRadioActionEntry compose_radio_prio_entries[] =
733 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
734 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
735 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
736 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
737 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
740 static GtkRadioActionEntry compose_radio_enc_entries[] =
742 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
743 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
744 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
745 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
746 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
747 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
748 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
749 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
750 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
751 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
752 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
753 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
754 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
755 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
756 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
757 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
758 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
759 ENC_ACTION("Cyrillic/"CS_MACCYR, C_MACCYR, "_Mac-Cyrillic"), /* RADIO compose_set_encoding_cb */
760 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
761 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
762 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
763 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
764 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
765 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
766 ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
767 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
768 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
769 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
770 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
771 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
772 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
773 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
774 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
777 static GtkTargetEntry compose_mime_types[] =
779 {"text/uri-list", 0, 0},
780 {"UTF8_STRING", 0, 0},
784 static gboolean compose_put_existing_to_front(MsgInfo *info)
786 const GList *compose_list = compose_get_compose_list();
787 const GList *elem = NULL;
790 for (elem = compose_list; elem != NULL && elem->data != NULL;
792 Compose *c = (Compose*)elem->data;
794 if (!c->targetinfo || !c->targetinfo->msgid ||
798 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
799 gtkut_window_popup(c->window);
807 static GdkColor quote_color1 =
808 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
809 static GdkColor quote_color2 =
810 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
811 static GdkColor quote_color3 =
812 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
814 static GdkColor quote_bgcolor1 =
815 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
816 static GdkColor quote_bgcolor2 =
817 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
818 static GdkColor quote_bgcolor3 =
819 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
821 static GdkColor signature_color = {
828 static GdkColor uri_color = {
835 static void compose_create_tags(GtkTextView *text, Compose *compose)
837 GtkTextBuffer *buffer;
838 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
839 #if !GTK_CHECK_VERSION(2, 24, 0)
846 buffer = gtk_text_view_get_buffer(text);
848 if (prefs_common.enable_color) {
849 /* grab the quote colors, converting from an int to a GdkColor */
850 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
852 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
854 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
856 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
858 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
860 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
862 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
864 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
867 signature_color = quote_color1 = quote_color2 = quote_color3 =
868 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
871 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
872 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
873 "foreground-gdk", "e_color1,
874 "paragraph-background-gdk", "e_bgcolor1,
876 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
877 "foreground-gdk", "e_color2,
878 "paragraph-background-gdk", "e_bgcolor2,
880 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
881 "foreground-gdk", "e_color3,
882 "paragraph-background-gdk", "e_bgcolor3,
885 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
886 "foreground-gdk", "e_color1,
888 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
889 "foreground-gdk", "e_color2,
891 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
892 "foreground-gdk", "e_color3,
896 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
897 "foreground-gdk", &signature_color,
900 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
901 "foreground-gdk", &uri_color,
903 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
904 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
906 #if !GTK_CHECK_VERSION(2, 24, 0)
907 color[0] = quote_color1;
908 color[1] = quote_color2;
909 color[2] = quote_color3;
910 color[3] = quote_bgcolor1;
911 color[4] = quote_bgcolor2;
912 color[5] = quote_bgcolor3;
913 color[6] = signature_color;
914 color[7] = uri_color;
916 cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
917 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
919 for (i = 0; i < 8; i++) {
920 if (success[i] == FALSE) {
921 g_warning("Compose: color allocation failed.");
922 quote_color1 = quote_color2 = quote_color3 =
923 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
924 signature_color = uri_color = black;
930 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
933 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
936 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
938 return compose_generic_new(account, mailto, item, NULL, NULL);
941 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
943 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
946 #define SCROLL_TO_CURSOR(compose) { \
947 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
948 gtk_text_view_get_buffer( \
949 GTK_TEXT_VIEW(compose->text))); \
950 gtk_text_view_scroll_mark_onscreen( \
951 GTK_TEXT_VIEW(compose->text), \
955 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
958 if (folderidentifier) {
959 #if !GTK_CHECK_VERSION(2, 24, 0)
960 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
962 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
964 prefs_common.compose_save_to_history = add_history(
965 prefs_common.compose_save_to_history, folderidentifier);
966 #if !GTK_CHECK_VERSION(2, 24, 0)
967 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
968 prefs_common.compose_save_to_history);
970 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
971 prefs_common.compose_save_to_history);
975 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
976 if (folderidentifier)
977 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
979 gtk_entry_set_text(GTK_ENTRY(entry), "");
982 static gchar *compose_get_save_to(Compose *compose)
985 gchar *result = NULL;
986 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
987 result = gtk_editable_get_chars(entry, 0, -1);
990 #if !GTK_CHECK_VERSION(2, 24, 0)
991 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
993 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
995 prefs_common.compose_save_to_history = add_history(
996 prefs_common.compose_save_to_history, result);
997 #if !GTK_CHECK_VERSION(2, 24, 0)
998 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
999 prefs_common.compose_save_to_history);
1001 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
1002 prefs_common.compose_save_to_history);
1008 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
1009 GList *attach_files, GList *listAddress )
1012 GtkTextView *textview;
1013 GtkTextBuffer *textbuf;
1015 const gchar *subject_format = NULL;
1016 const gchar *body_format = NULL;
1017 gchar *mailto_from = NULL;
1018 PrefsAccount *mailto_account = NULL;
1019 MsgInfo* dummyinfo = NULL;
1020 gint cursor_pos = -1;
1021 MailField mfield = NO_FIELD_PRESENT;
1025 /* check if mailto defines a from */
1026 if (mailto && *mailto != '\0') {
1027 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
1028 /* mailto defines a from, check if we can get account prefs from it,
1029 if not, the account prefs will be guessed using other ways, but we'll keep
1032 mailto_account = account_find_from_address(mailto_from, TRUE);
1033 if (mailto_account == NULL) {
1035 Xstrdup_a(tmp_from, mailto_from, return NULL);
1036 extract_address(tmp_from);
1037 mailto_account = account_find_from_address(tmp_from, TRUE);
1041 account = mailto_account;
1044 /* if no account prefs set from mailto, set if from folder prefs (if any) */
1045 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
1046 account = account_find_from_id(item->prefs->default_account);
1048 /* if no account prefs set, fallback to the current one */
1049 if (!account) account = cur_account;
1050 cm_return_val_if_fail(account != NULL, NULL);
1052 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
1054 /* override from name if mailto asked for it */
1056 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
1057 g_free(mailto_from);
1059 /* override from name according to folder properties */
1060 if (item && item->prefs &&
1061 item->prefs->compose_with_format &&
1062 item->prefs->compose_override_from_format &&
1063 *item->prefs->compose_override_from_format != '\0') {
1068 dummyinfo = compose_msginfo_new_from_compose(compose);
1070 /* decode \-escape sequences in the internal representation of the quote format */
1071 tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
1072 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1075 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1076 compose->gtkaspell);
1078 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1080 quote_fmt_scan_string(tmp);
1083 buf = quote_fmt_get_buffer();
1085 alertpanel_error(_("New message From format error."));
1087 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1088 quote_fmt_reset_vartable();
1093 compose->replyinfo = NULL;
1094 compose->fwdinfo = NULL;
1096 textview = GTK_TEXT_VIEW(compose->text);
1097 textbuf = gtk_text_view_get_buffer(textview);
1098 compose_create_tags(textview, compose);
1100 undo_block(compose->undostruct);
1102 compose_set_dictionaries_from_folder_prefs(compose, item);
1105 if (account->auto_sig)
1106 compose_insert_sig(compose, FALSE);
1107 gtk_text_buffer_get_start_iter(textbuf, &iter);
1108 gtk_text_buffer_place_cursor(textbuf, &iter);
1110 if (account->protocol != A_NNTP) {
1111 if (mailto && *mailto != '\0') {
1112 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1115 compose_set_folder_prefs(compose, item, TRUE);
1117 if (item && item->ret_rcpt) {
1118 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1121 if (mailto && *mailto != '\0') {
1122 if (!strchr(mailto, '@'))
1123 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1125 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1126 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1127 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1128 mfield = TO_FIELD_PRESENT;
1131 * CLAWS: just don't allow return receipt request, even if the user
1132 * may want to send an email. simple but foolproof.
1134 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1136 compose_add_field_list( compose, listAddress );
1138 if (item && item->prefs && item->prefs->compose_with_format) {
1139 subject_format = item->prefs->compose_subject_format;
1140 body_format = item->prefs->compose_body_format;
1141 } else if (account->compose_with_format) {
1142 subject_format = account->compose_subject_format;
1143 body_format = account->compose_body_format;
1144 } else if (prefs_common.compose_with_format) {
1145 subject_format = prefs_common.compose_subject_format;
1146 body_format = prefs_common.compose_body_format;
1149 if (subject_format || body_format) {
1152 && *subject_format != '\0' )
1154 gchar *subject = NULL;
1159 dummyinfo = compose_msginfo_new_from_compose(compose);
1161 /* decode \-escape sequences in the internal representation of the quote format */
1162 tmp = g_malloc(strlen(subject_format)+1);
1163 pref_get_unescaped_pref(tmp, subject_format);
1165 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1167 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1168 compose->gtkaspell);
1170 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1172 quote_fmt_scan_string(tmp);
1175 buf = quote_fmt_get_buffer();
1177 alertpanel_error(_("New message subject format error."));
1179 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1180 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1181 quote_fmt_reset_vartable();
1185 mfield = SUBJECT_FIELD_PRESENT;
1189 && *body_format != '\0' )
1192 GtkTextBuffer *buffer;
1193 GtkTextIter start, end;
1197 dummyinfo = compose_msginfo_new_from_compose(compose);
1199 text = GTK_TEXT_VIEW(compose->text);
1200 buffer = gtk_text_view_get_buffer(text);
1201 gtk_text_buffer_get_start_iter(buffer, &start);
1202 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1203 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1205 compose_quote_fmt(compose, dummyinfo,
1207 NULL, tmp, FALSE, TRUE,
1208 _("The body of the \"New message\" template has an error at line %d."));
1209 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1210 quote_fmt_reset_vartable();
1214 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1215 gtkaspell_highlight_all(compose->gtkaspell);
1217 mfield = BODY_FIELD_PRESENT;
1221 procmsg_msginfo_free( &dummyinfo );
1227 for (curr = attach_files ; curr != NULL ; curr = curr->next) {
1228 ainfo = (AttachInfo *) curr->data;
1229 compose_attach_append(compose, ainfo->file, ainfo->file,
1230 ainfo->content_type, ainfo->charset);
1234 compose_show_first_last_header(compose, TRUE);
1236 /* Set save folder */
1237 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1238 gchar *folderidentifier;
1240 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1241 folderidentifier = folder_item_get_identifier(item);
1242 compose_set_save_to(compose, folderidentifier);
1243 g_free(folderidentifier);
1246 /* Place cursor according to provided input (mfield) */
1248 case NO_FIELD_PRESENT:
1249 if (compose->header_last)
1250 gtk_widget_grab_focus(compose->header_last->entry);
1252 case TO_FIELD_PRESENT:
1253 buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1255 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1258 gtk_widget_grab_focus(compose->subject_entry);
1260 case SUBJECT_FIELD_PRESENT:
1261 textview = GTK_TEXT_VIEW(compose->text);
1264 textbuf = gtk_text_view_get_buffer(textview);
1267 mark = gtk_text_buffer_get_insert(textbuf);
1268 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1269 gtk_text_buffer_insert(textbuf, &iter, "", -1);
1271 * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1272 * only defers where it comes to the variable body
1273 * is not null. If no body is present compose->text
1274 * will be null in which case you cannot place the
1275 * cursor inside the component so. An empty component
1276 * is therefore created before placing the cursor
1278 case BODY_FIELD_PRESENT:
1279 cursor_pos = quote_fmt_get_cursor_pos();
1280 if (cursor_pos == -1)
1281 gtk_widget_grab_focus(compose->header_last->entry);
1283 gtk_widget_grab_focus(compose->text);
1287 undo_unblock(compose->undostruct);
1289 if (prefs_common.auto_exteditor)
1290 compose_exec_ext_editor(compose);
1292 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET;
1294 SCROLL_TO_CURSOR(compose);
1296 compose->modified = FALSE;
1297 compose_set_title(compose);
1299 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1304 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1305 gboolean override_pref, const gchar *system)
1307 const gchar *privacy = NULL;
1309 cm_return_if_fail(compose != NULL);
1310 cm_return_if_fail(account != NULL);
1312 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1315 if (account->default_privacy_system && strlen(account->default_privacy_system))
1316 privacy = account->default_privacy_system;
1320 GSList *privacy_avail = privacy_get_system_ids();
1321 if (privacy_avail && g_slist_length(privacy_avail)) {
1322 privacy = (gchar *)(privacy_avail->data);
1325 if (privacy != NULL) {
1327 g_free(compose->privacy_system);
1328 compose->privacy_system = NULL;
1329 g_free(compose->encdata);
1330 compose->encdata = NULL;
1332 if (compose->privacy_system == NULL)
1333 compose->privacy_system = g_strdup(privacy);
1334 else if (*(compose->privacy_system) == '\0') {
1335 g_free(compose->privacy_system);
1336 g_free(compose->encdata);
1337 compose->encdata = NULL;
1338 compose->privacy_system = g_strdup(privacy);
1340 compose_update_privacy_system_menu_item(compose, FALSE);
1341 compose_use_encryption(compose, TRUE);
1345 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1347 const gchar *privacy = NULL;
1349 if (account->default_privacy_system && strlen(account->default_privacy_system))
1350 privacy = account->default_privacy_system;
1354 GSList *privacy_avail = privacy_get_system_ids();
1355 if (privacy_avail && g_slist_length(privacy_avail)) {
1356 privacy = (gchar *)(privacy_avail->data);
1360 if (privacy != NULL) {
1362 g_free(compose->privacy_system);
1363 compose->privacy_system = NULL;
1364 g_free(compose->encdata);
1365 compose->encdata = NULL;
1367 if (compose->privacy_system == NULL)
1368 compose->privacy_system = g_strdup(privacy);
1369 compose_update_privacy_system_menu_item(compose, FALSE);
1370 compose_use_signing(compose, TRUE);
1374 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1378 Compose *compose = NULL;
1380 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1382 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1383 cm_return_val_if_fail(msginfo != NULL, NULL);
1385 list_len = g_slist_length(msginfo_list);
1389 case COMPOSE_REPLY_TO_ADDRESS:
1390 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1391 FALSE, prefs_common.default_reply_list, FALSE, body);
1393 case COMPOSE_REPLY_WITH_QUOTE:
1394 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1395 FALSE, prefs_common.default_reply_list, FALSE, body);
1397 case COMPOSE_REPLY_WITHOUT_QUOTE:
1398 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1399 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1401 case COMPOSE_REPLY_TO_SENDER:
1402 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1403 FALSE, FALSE, TRUE, body);
1405 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1406 compose = compose_followup_and_reply_to(msginfo,
1407 COMPOSE_QUOTE_CHECK,
1408 FALSE, FALSE, body);
1410 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1411 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1412 FALSE, FALSE, TRUE, body);
1414 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1415 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1416 FALSE, FALSE, TRUE, NULL);
1418 case COMPOSE_REPLY_TO_ALL:
1419 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1420 TRUE, FALSE, FALSE, body);
1422 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1423 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1424 TRUE, FALSE, FALSE, body);
1426 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1427 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1428 TRUE, FALSE, FALSE, NULL);
1430 case COMPOSE_REPLY_TO_LIST:
1431 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1432 FALSE, TRUE, FALSE, body);
1434 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1435 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1436 FALSE, TRUE, FALSE, body);
1438 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1439 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1440 FALSE, TRUE, FALSE, NULL);
1442 case COMPOSE_FORWARD:
1443 if (prefs_common.forward_as_attachment) {
1444 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1447 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1451 case COMPOSE_FORWARD_INLINE:
1452 /* check if we reply to more than one Message */
1453 if (list_len == 1) {
1454 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1457 /* more messages FALL THROUGH */
1458 case COMPOSE_FORWARD_AS_ATTACH:
1459 compose = compose_forward_multiple(NULL, msginfo_list);
1461 case COMPOSE_REDIRECT:
1462 compose = compose_redirect(NULL, msginfo, FALSE);
1465 g_warning("compose_reply_mode(): invalid Compose Mode: %d", mode);
1468 if (compose == NULL) {
1469 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1473 compose->rmode = mode;
1474 switch (compose->rmode) {
1476 case COMPOSE_REPLY_WITH_QUOTE:
1477 case COMPOSE_REPLY_WITHOUT_QUOTE:
1478 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1479 debug_print("reply mode Normal\n");
1480 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1481 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1483 case COMPOSE_REPLY_TO_SENDER:
1484 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1485 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1486 debug_print("reply mode Sender\n");
1487 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1489 case COMPOSE_REPLY_TO_ALL:
1490 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1491 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1492 debug_print("reply mode All\n");
1493 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1495 case COMPOSE_REPLY_TO_LIST:
1496 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1497 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1498 debug_print("reply mode List\n");
1499 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1501 case COMPOSE_REPLY_TO_ADDRESS:
1502 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1510 static Compose *compose_reply(MsgInfo *msginfo,
1511 ComposeQuoteMode quote_mode,
1517 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1518 to_sender, FALSE, body);
1521 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1522 ComposeQuoteMode quote_mode,
1527 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1528 to_sender, TRUE, body);
1531 static void compose_extract_original_charset(Compose *compose)
1533 MsgInfo *info = NULL;
1534 if (compose->replyinfo) {
1535 info = compose->replyinfo;
1536 } else if (compose->fwdinfo) {
1537 info = compose->fwdinfo;
1538 } else if (compose->targetinfo) {
1539 info = compose->targetinfo;
1542 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1543 MimeInfo *partinfo = mimeinfo;
1544 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1545 partinfo = procmime_mimeinfo_next(partinfo);
1547 compose->orig_charset =
1548 g_strdup(procmime_mimeinfo_get_parameter(
1549 partinfo, "charset"));
1551 procmime_mimeinfo_free_all(&mimeinfo);
1555 #define SIGNAL_BLOCK(buffer) { \
1556 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1557 G_CALLBACK(compose_changed_cb), \
1559 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1560 G_CALLBACK(text_inserted), \
1564 #define SIGNAL_UNBLOCK(buffer) { \
1565 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1566 G_CALLBACK(compose_changed_cb), \
1568 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1569 G_CALLBACK(text_inserted), \
1573 static Compose *compose_generic_reply(MsgInfo *msginfo,
1574 ComposeQuoteMode quote_mode,
1575 gboolean to_all, gboolean to_ml,
1577 gboolean followup_and_reply_to,
1581 PrefsAccount *account = NULL;
1582 GtkTextView *textview;
1583 GtkTextBuffer *textbuf;
1584 gboolean quote = FALSE;
1585 const gchar *qmark = NULL;
1586 const gchar *body_fmt = NULL;
1587 gchar *s_system = NULL;
1589 cm_return_val_if_fail(msginfo != NULL, NULL);
1590 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1592 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1594 cm_return_val_if_fail(account != NULL, NULL);
1596 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1598 compose->updating = TRUE;
1600 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1601 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1603 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1604 if (!compose->replyinfo)
1605 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1607 compose_extract_original_charset(compose);
1609 if (msginfo->folder && msginfo->folder->ret_rcpt)
1610 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1612 /* Set save folder */
1613 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1614 gchar *folderidentifier;
1616 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1617 folderidentifier = folder_item_get_identifier(msginfo->folder);
1618 compose_set_save_to(compose, folderidentifier);
1619 g_free(folderidentifier);
1622 if (compose_parse_header(compose, msginfo) < 0) {
1623 compose->updating = FALSE;
1624 compose_destroy(compose);
1628 /* override from name according to folder properties */
1629 if (msginfo->folder && msginfo->folder->prefs &&
1630 msginfo->folder->prefs->reply_with_format &&
1631 msginfo->folder->prefs->reply_override_from_format &&
1632 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1637 /* decode \-escape sequences in the internal representation of the quote format */
1638 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1639 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1642 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1643 compose->gtkaspell);
1645 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1647 quote_fmt_scan_string(tmp);
1650 buf = quote_fmt_get_buffer();
1652 alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1654 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1655 quote_fmt_reset_vartable();
1660 textview = (GTK_TEXT_VIEW(compose->text));
1661 textbuf = gtk_text_view_get_buffer(textview);
1662 compose_create_tags(textview, compose);
1664 undo_block(compose->undostruct);
1666 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1667 gtkaspell_block_check(compose->gtkaspell);
1670 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1671 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1672 /* use the reply format of folder (if enabled), or the account's one
1673 (if enabled) or fallback to the global reply format, which is always
1674 enabled (even if empty), and use the relevant quotemark */
1676 if (msginfo->folder && msginfo->folder->prefs &&
1677 msginfo->folder->prefs->reply_with_format) {
1678 qmark = msginfo->folder->prefs->reply_quotemark;
1679 body_fmt = msginfo->folder->prefs->reply_body_format;
1681 } else if (account->reply_with_format) {
1682 qmark = account->reply_quotemark;
1683 body_fmt = account->reply_body_format;
1686 qmark = prefs_common.quotemark;
1687 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1688 body_fmt = gettext(prefs_common.quotefmt);
1695 /* empty quotemark is not allowed */
1696 if (qmark == NULL || *qmark == '\0')
1698 compose_quote_fmt(compose, compose->replyinfo,
1699 body_fmt, qmark, body, FALSE, TRUE,
1700 _("The body of the \"Reply\" template has an error at line %d."));
1701 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1702 quote_fmt_reset_vartable();
1705 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1706 compose_force_encryption(compose, account, FALSE, s_system);
1709 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1710 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1711 compose_force_signing(compose, account, s_system);
1715 SIGNAL_BLOCK(textbuf);
1717 if (account->auto_sig)
1718 compose_insert_sig(compose, FALSE);
1720 compose_wrap_all(compose);
1723 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1724 gtkaspell_highlight_all(compose->gtkaspell);
1725 gtkaspell_unblock_check(compose->gtkaspell);
1727 SIGNAL_UNBLOCK(textbuf);
1729 gtk_widget_grab_focus(compose->text);
1731 undo_unblock(compose->undostruct);
1733 if (prefs_common.auto_exteditor)
1734 compose_exec_ext_editor(compose);
1736 compose->modified = FALSE;
1737 compose_set_title(compose);
1739 compose->updating = FALSE;
1740 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
1741 SCROLL_TO_CURSOR(compose);
1743 if (compose->deferred_destroy) {
1744 compose_destroy(compose);
1752 #define INSERT_FW_HEADER(var, hdr) \
1753 if (msginfo->var && *msginfo->var) { \
1754 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1755 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1756 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1759 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1760 gboolean as_attach, const gchar *body,
1761 gboolean no_extedit,
1765 GtkTextView *textview;
1766 GtkTextBuffer *textbuf;
1767 gint cursor_pos = -1;
1770 cm_return_val_if_fail(msginfo != NULL, NULL);
1771 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1774 !(account = compose_guess_forward_account_from_msginfo
1776 account = cur_account;
1778 if (!prefs_common.forward_as_attachment)
1779 mode = COMPOSE_FORWARD_INLINE;
1781 mode = COMPOSE_FORWARD;
1782 compose = compose_create(account, msginfo->folder, mode, batch);
1784 compose->updating = TRUE;
1785 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1786 if (!compose->fwdinfo)
1787 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1789 compose_extract_original_charset(compose);
1791 if (msginfo->subject && *msginfo->subject) {
1792 gchar *buf, *buf2, *p;
1794 buf = p = g_strdup(msginfo->subject);
1795 p += subject_get_prefix_length(p);
1796 memmove(buf, p, strlen(p) + 1);
1798 buf2 = g_strdup_printf("Fw: %s", buf);
1799 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1805 /* override from name according to folder properties */
1806 if (msginfo->folder && msginfo->folder->prefs &&
1807 msginfo->folder->prefs->forward_with_format &&
1808 msginfo->folder->prefs->forward_override_from_format &&
1809 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1813 MsgInfo *full_msginfo = NULL;
1816 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1818 full_msginfo = procmsg_msginfo_copy(msginfo);
1820 /* decode \-escape sequences in the internal representation of the quote format */
1821 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1822 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1825 gtkaspell_block_check(compose->gtkaspell);
1826 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1827 compose->gtkaspell);
1829 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1831 quote_fmt_scan_string(tmp);
1834 buf = quote_fmt_get_buffer();
1836 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1838 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1839 quote_fmt_reset_vartable();
1842 procmsg_msginfo_free(&full_msginfo);
1845 textview = GTK_TEXT_VIEW(compose->text);
1846 textbuf = gtk_text_view_get_buffer(textview);
1847 compose_create_tags(textview, compose);
1849 undo_block(compose->undostruct);
1853 msgfile = procmsg_get_message_file(msginfo);
1854 if (!is_file_exist(msgfile))
1855 g_warning("%s: file does not exist", msgfile);
1857 compose_attach_append(compose, msgfile, msgfile,
1858 "message/rfc822", NULL);
1862 const gchar *qmark = NULL;
1863 const gchar *body_fmt = NULL;
1864 MsgInfo *full_msginfo;
1866 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1868 full_msginfo = procmsg_msginfo_copy(msginfo);
1870 /* use the forward format of folder (if enabled), or the account's one
1871 (if enabled) or fallback to the global forward format, which is always
1872 enabled (even if empty), and use the relevant quotemark */
1873 if (msginfo->folder && msginfo->folder->prefs &&
1874 msginfo->folder->prefs->forward_with_format) {
1875 qmark = msginfo->folder->prefs->forward_quotemark;
1876 body_fmt = msginfo->folder->prefs->forward_body_format;
1878 } else if (account->forward_with_format) {
1879 qmark = account->forward_quotemark;
1880 body_fmt = account->forward_body_format;
1883 qmark = prefs_common.fw_quotemark;
1884 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1885 body_fmt = gettext(prefs_common.fw_quotefmt);
1890 /* empty quotemark is not allowed */
1891 if (qmark == NULL || *qmark == '\0')
1894 compose_quote_fmt(compose, full_msginfo,
1895 body_fmt, qmark, body, FALSE, TRUE,
1896 _("The body of the \"Forward\" template has an error at line %d."));
1897 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1898 quote_fmt_reset_vartable();
1899 compose_attach_parts(compose, msginfo);
1901 procmsg_msginfo_free(&full_msginfo);
1904 SIGNAL_BLOCK(textbuf);
1906 if (account->auto_sig)
1907 compose_insert_sig(compose, FALSE);
1909 compose_wrap_all(compose);
1912 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1913 gtkaspell_highlight_all(compose->gtkaspell);
1914 gtkaspell_unblock_check(compose->gtkaspell);
1916 SIGNAL_UNBLOCK(textbuf);
1918 cursor_pos = quote_fmt_get_cursor_pos();
1919 if (cursor_pos == -1)
1920 gtk_widget_grab_focus(compose->header_last->entry);
1922 gtk_widget_grab_focus(compose->text);
1924 if (!no_extedit && prefs_common.auto_exteditor)
1925 compose_exec_ext_editor(compose);
1928 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1929 gchar *folderidentifier;
1931 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1932 folderidentifier = folder_item_get_identifier(msginfo->folder);
1933 compose_set_save_to(compose, folderidentifier);
1934 g_free(folderidentifier);
1937 undo_unblock(compose->undostruct);
1939 compose->modified = FALSE;
1940 compose_set_title(compose);
1942 compose->updating = FALSE;
1943 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
1944 SCROLL_TO_CURSOR(compose);
1946 if (compose->deferred_destroy) {
1947 compose_destroy(compose);
1951 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1956 #undef INSERT_FW_HEADER
1958 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1961 GtkTextView *textview;
1962 GtkTextBuffer *textbuf;
1966 gboolean single_mail = TRUE;
1968 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1970 if (g_slist_length(msginfo_list) > 1)
1971 single_mail = FALSE;
1973 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1974 if (((MsgInfo *)msginfo->data)->folder == NULL)
1977 /* guess account from first selected message */
1979 !(account = compose_guess_forward_account_from_msginfo
1980 (msginfo_list->data)))
1981 account = cur_account;
1983 cm_return_val_if_fail(account != NULL, NULL);
1985 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1986 if (msginfo->data) {
1987 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1988 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1992 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1993 g_warning("no msginfo_list");
1997 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1999 compose->updating = TRUE;
2001 /* override from name according to folder properties */
2002 if (msginfo_list->data) {
2003 MsgInfo *msginfo = msginfo_list->data;
2005 if (msginfo->folder && msginfo->folder->prefs &&
2006 msginfo->folder->prefs->forward_with_format &&
2007 msginfo->folder->prefs->forward_override_from_format &&
2008 *msginfo->folder->prefs->forward_override_from_format != '\0') {
2013 /* decode \-escape sequences in the internal representation of the quote format */
2014 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
2015 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
2018 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2019 compose->gtkaspell);
2021 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2023 quote_fmt_scan_string(tmp);
2026 buf = quote_fmt_get_buffer();
2028 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
2030 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
2031 quote_fmt_reset_vartable();
2037 textview = GTK_TEXT_VIEW(compose->text);
2038 textbuf = gtk_text_view_get_buffer(textview);
2039 compose_create_tags(textview, compose);
2041 undo_block(compose->undostruct);
2042 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
2043 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
2045 if (!is_file_exist(msgfile))
2046 g_warning("%s: file does not exist", msgfile);
2048 compose_attach_append(compose, msgfile, msgfile,
2049 "message/rfc822", NULL);
2054 MsgInfo *info = (MsgInfo *)msginfo_list->data;
2055 if (info->subject && *info->subject) {
2056 gchar *buf, *buf2, *p;
2058 buf = p = g_strdup(info->subject);
2059 p += subject_get_prefix_length(p);
2060 memmove(buf, p, strlen(p) + 1);
2062 buf2 = g_strdup_printf("Fw: %s", buf);
2063 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2069 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2070 _("Fw: multiple emails"));
2073 SIGNAL_BLOCK(textbuf);
2075 if (account->auto_sig)
2076 compose_insert_sig(compose, FALSE);
2078 compose_wrap_all(compose);
2080 SIGNAL_UNBLOCK(textbuf);
2082 gtk_text_buffer_get_start_iter(textbuf, &iter);
2083 gtk_text_buffer_place_cursor(textbuf, &iter);
2085 if (prefs_common.auto_exteditor)
2086 compose_exec_ext_editor(compose);
2088 gtk_widget_grab_focus(compose->header_last->entry);
2089 undo_unblock(compose->undostruct);
2090 compose->modified = FALSE;
2091 compose_set_title(compose);
2093 compose->updating = FALSE;
2094 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2095 SCROLL_TO_CURSOR(compose);
2097 if (compose->deferred_destroy) {
2098 compose_destroy(compose);
2102 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2107 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
2109 GtkTextIter start = *iter;
2110 GtkTextIter end_iter;
2111 int start_pos = gtk_text_iter_get_offset(&start);
2113 if (!compose->account->sig_sep)
2116 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2117 start_pos+strlen(compose->account->sig_sep));
2119 /* check sig separator */
2120 str = gtk_text_iter_get_text(&start, &end_iter);
2121 if (!strcmp(str, compose->account->sig_sep)) {
2123 /* check end of line (\n) */
2124 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2125 start_pos+strlen(compose->account->sig_sep));
2126 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2127 start_pos+strlen(compose->account->sig_sep)+1);
2128 tmp = gtk_text_iter_get_text(&start, &end_iter);
2129 if (!strcmp(tmp,"\n")) {
2141 static gboolean compose_update_folder_hook(gpointer source, gpointer data)
2143 FolderUpdateData *hookdata = (FolderUpdateData *)source;
2144 Compose *compose = (Compose *)data;
2145 FolderItem *old_item = NULL;
2146 FolderItem *new_item = NULL;
2147 gchar *old_id, *new_id;
2149 if (!(hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM)
2150 && !(hookdata->update_flags & FOLDER_MOVE_FOLDERITEM))
2153 old_item = hookdata->item;
2154 new_item = hookdata->item2;
2156 old_id = folder_item_get_identifier(old_item);
2157 new_id = new_item ? folder_item_get_identifier(new_item) : g_strdup("NULL");
2159 if (compose->targetinfo && compose->targetinfo->folder == old_item) {
2160 debug_print("updating targetinfo folder: %s -> %s\n", old_id, new_id);
2161 compose->targetinfo->folder = new_item;
2164 if (compose->replyinfo && compose->replyinfo->folder == old_item) {
2165 debug_print("updating replyinfo folder: %s -> %s\n", old_id, new_id);
2166 compose->replyinfo->folder = new_item;
2169 if (compose->fwdinfo && compose->fwdinfo->folder == old_item) {
2170 debug_print("updating fwdinfo folder: %s -> %s\n", old_id, new_id);
2171 compose->fwdinfo->folder = new_item;
2179 static void compose_colorize_signature(Compose *compose)
2181 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2183 GtkTextIter end_iter;
2184 gtk_text_buffer_get_start_iter(buffer, &iter);
2185 while (gtk_text_iter_forward_line(&iter))
2186 if (compose_is_sig_separator(compose, buffer, &iter)) {
2187 gtk_text_buffer_get_end_iter(buffer, &end_iter);
2188 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2192 #define BLOCK_WRAP() { \
2193 prev_autowrap = compose->autowrap; \
2194 buffer = gtk_text_view_get_buffer( \
2195 GTK_TEXT_VIEW(compose->text)); \
2196 compose->autowrap = FALSE; \
2198 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2199 G_CALLBACK(compose_changed_cb), \
2201 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2202 G_CALLBACK(text_inserted), \
2205 #define UNBLOCK_WRAP() { \
2206 compose->autowrap = prev_autowrap; \
2207 if (compose->autowrap) { \
2208 gint old = compose->draft_timeout_tag; \
2209 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN; \
2210 compose_wrap_all(compose); \
2211 compose->draft_timeout_tag = old; \
2214 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2215 G_CALLBACK(compose_changed_cb), \
2217 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2218 G_CALLBACK(text_inserted), \
2222 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2224 Compose *compose = NULL;
2225 PrefsAccount *account = NULL;
2226 GtkTextView *textview;
2227 GtkTextBuffer *textbuf;
2231 gchar buf[BUFFSIZE];
2232 gboolean use_signing = FALSE;
2233 gboolean use_encryption = FALSE;
2234 gchar *privacy_system = NULL;
2235 int priority = PRIORITY_NORMAL;
2236 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2237 gboolean autowrap = prefs_common.autowrap;
2238 gboolean autoindent = prefs_common.auto_indent;
2239 HeaderEntry *manual_headers = NULL;
2241 cm_return_val_if_fail(msginfo != NULL, NULL);
2242 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2244 if (compose_put_existing_to_front(msginfo)) {
2248 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2249 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2250 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2251 gchar queueheader_buf[BUFFSIZE];
2254 /* Select Account from queue headers */
2255 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2256 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2257 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2258 account = account_find_from_id(id);
2260 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2261 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2262 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2263 account = account_find_from_id(id);
2265 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2266 sizeof(queueheader_buf), "NAID:")) {
2267 id = atoi(&queueheader_buf[strlen("NAID:")]);
2268 account = account_find_from_id(id);
2270 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2271 sizeof(queueheader_buf), "MAID:")) {
2272 id = atoi(&queueheader_buf[strlen("MAID:")]);
2273 account = account_find_from_id(id);
2275 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2276 sizeof(queueheader_buf), "S:")) {
2277 account = account_find_from_address(queueheader_buf, FALSE);
2279 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2280 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2281 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2282 use_signing = param;
2285 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2286 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2287 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2288 use_signing = param;
2291 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2292 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2293 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2294 use_encryption = param;
2296 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2297 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2298 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2299 use_encryption = param;
2301 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2302 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2303 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2306 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2307 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2308 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2311 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2312 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2313 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2315 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2316 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2317 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2319 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2320 sizeof(queueheader_buf), "X-Priority: ")) {
2321 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2324 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2325 sizeof(queueheader_buf), "RMID:")) {
2326 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2327 if (tokens[0] && tokens[1] && tokens[2]) {
2328 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2329 if (orig_item != NULL) {
2330 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2335 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2336 sizeof(queueheader_buf), "FMID:")) {
2337 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2338 if (tokens[0] && tokens[1] && tokens[2]) {
2339 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2340 if (orig_item != NULL) {
2341 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2346 /* Get manual headers */
2347 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "X-Claws-Manual-Headers:")) {
2348 gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2349 if (*listmh != '\0') {
2350 debug_print("Got manual headers: %s\n", listmh);
2351 manual_headers = procheader_entries_from_str(listmh);
2356 account = msginfo->folder->folder->account;
2359 if (!account && prefs_common.reedit_account_autosel) {
2360 gchar from[BUFFSIZE];
2361 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2362 extract_address(from);
2363 account = account_find_from_address(from, FALSE);
2367 account = cur_account;
2369 cm_return_val_if_fail(account != NULL, NULL);
2371 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2373 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2374 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2375 compose->autowrap = autowrap;
2376 compose->replyinfo = replyinfo;
2377 compose->fwdinfo = fwdinfo;
2379 compose->updating = TRUE;
2380 compose->priority = priority;
2382 if (privacy_system != NULL) {
2383 compose->privacy_system = privacy_system;
2384 compose_use_signing(compose, use_signing);
2385 compose_use_encryption(compose, use_encryption);
2386 compose_update_privacy_system_menu_item(compose, FALSE);
2388 activate_privacy_system(compose, account, FALSE);
2391 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2393 compose_extract_original_charset(compose);
2395 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2396 folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2397 folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2398 gchar queueheader_buf[BUFFSIZE];
2400 /* Set message save folder */
2401 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2402 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2403 compose_set_save_to(compose, &queueheader_buf[4]);
2405 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2406 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2408 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2413 if (compose_parse_header(compose, msginfo) < 0) {
2414 compose->updating = FALSE;
2415 compose_destroy(compose);
2418 compose_reedit_set_entry(compose, msginfo);
2420 textview = GTK_TEXT_VIEW(compose->text);
2421 textbuf = gtk_text_view_get_buffer(textview);
2422 compose_create_tags(textview, compose);
2424 mark = gtk_text_buffer_get_insert(textbuf);
2425 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2427 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2428 G_CALLBACK(compose_changed_cb),
2431 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2432 fp = procmime_get_first_encrypted_text_content(msginfo);
2434 compose_force_encryption(compose, account, TRUE, NULL);
2437 fp = procmime_get_first_text_content(msginfo);
2440 g_warning("Can't get text part");
2444 gboolean prev_autowrap;
2445 GtkTextBuffer *buffer;
2447 while (fgets(buf, sizeof(buf), fp) != NULL) {
2449 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2455 compose_attach_parts(compose, msginfo);
2457 compose_colorize_signature(compose);
2459 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2460 G_CALLBACK(compose_changed_cb),
2463 if (manual_headers != NULL) {
2464 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2465 procheader_entries_free(manual_headers);
2466 compose->updating = FALSE;
2467 compose_destroy(compose);
2470 procheader_entries_free(manual_headers);
2473 gtk_widget_grab_focus(compose->text);
2475 if (prefs_common.auto_exteditor) {
2476 compose_exec_ext_editor(compose);
2478 compose->modified = FALSE;
2479 compose_set_title(compose);
2481 compose->updating = FALSE;
2482 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2483 SCROLL_TO_CURSOR(compose);
2485 if (compose->deferred_destroy) {
2486 compose_destroy(compose);
2490 compose->sig_str = account_get_signature_str(compose->account);
2492 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2497 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2504 cm_return_val_if_fail(msginfo != NULL, NULL);
2507 account = account_get_reply_account(msginfo,
2508 prefs_common.reply_account_autosel);
2509 cm_return_val_if_fail(account != NULL, NULL);
2511 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2513 compose->updating = TRUE;
2515 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2516 compose->replyinfo = NULL;
2517 compose->fwdinfo = NULL;
2519 compose_show_first_last_header(compose, TRUE);
2521 gtk_widget_grab_focus(compose->header_last->entry);
2523 filename = procmsg_get_message_file(msginfo);
2525 if (filename == NULL) {
2526 compose->updating = FALSE;
2527 compose_destroy(compose);
2532 compose->redirect_filename = filename;
2534 /* Set save folder */
2535 item = msginfo->folder;
2536 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2537 gchar *folderidentifier;
2539 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2540 folderidentifier = folder_item_get_identifier(item);
2541 compose_set_save_to(compose, folderidentifier);
2542 g_free(folderidentifier);
2545 compose_attach_parts(compose, msginfo);
2547 if (msginfo->subject)
2548 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2550 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2552 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2553 _("The body of the \"Redirect\" template has an error at line %d."));
2554 quote_fmt_reset_vartable();
2555 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2557 compose_colorize_signature(compose);
2560 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2561 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2562 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2564 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2565 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2566 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2567 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2568 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/ReplaceSig", FALSE);
2569 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2570 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2571 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2572 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2574 if (compose->toolbar->draft_btn)
2575 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2576 if (compose->toolbar->insert_btn)
2577 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2578 if (compose->toolbar->attach_btn)
2579 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2580 if (compose->toolbar->sig_btn)
2581 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2582 if (compose->toolbar->exteditor_btn)
2583 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2584 if (compose->toolbar->linewrap_current_btn)
2585 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2586 if (compose->toolbar->linewrap_all_btn)
2587 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2589 compose->modified = FALSE;
2590 compose_set_title(compose);
2591 compose->updating = FALSE;
2592 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2593 SCROLL_TO_CURSOR(compose);
2595 if (compose->deferred_destroy) {
2596 compose_destroy(compose);
2600 hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2605 const GList *compose_get_compose_list(void)
2607 return compose_list;
2610 void compose_entry_append(Compose *compose, const gchar *address,
2611 ComposeEntryType type, ComposePrefType pref_type)
2613 const gchar *header;
2615 gboolean in_quote = FALSE;
2616 if (!address || *address == '\0') return;
2623 header = N_("Bcc:");
2625 case COMPOSE_REPLYTO:
2626 header = N_("Reply-To:");
2628 case COMPOSE_NEWSGROUPS:
2629 header = N_("Newsgroups:");
2631 case COMPOSE_FOLLOWUPTO:
2632 header = N_( "Followup-To:");
2634 case COMPOSE_INREPLYTO:
2635 header = N_( "In-Reply-To:");
2642 header = prefs_common_translated_header_name(header);
2644 cur = begin = (gchar *)address;
2646 /* we separate the line by commas, but not if we're inside a quoted
2648 while (*cur != '\0') {
2650 in_quote = !in_quote;
2651 if (*cur == ',' && !in_quote) {
2652 gchar *tmp = g_strdup(begin);
2654 tmp[cur-begin]='\0';
2657 while (*tmp == ' ' || *tmp == '\t')
2659 compose_add_header_entry(compose, header, tmp, pref_type);
2666 gchar *tmp = g_strdup(begin);
2668 tmp[cur-begin]='\0';
2669 while (*tmp == ' ' || *tmp == '\t')
2671 compose_add_header_entry(compose, header, tmp, pref_type);
2676 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2681 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2682 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2683 if (gtk_entry_get_text(entry) &&
2684 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2685 gtk_widget_modify_base(
2686 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2687 GTK_STATE_NORMAL, &default_to_bgcolor);
2688 gtk_widget_modify_text(
2689 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2690 GTK_STATE_NORMAL, &default_to_color);
2695 void compose_toolbar_cb(gint action, gpointer data)
2697 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2698 Compose *compose = (Compose*)toolbar_item->parent;
2700 cm_return_if_fail(compose != NULL);
2704 compose_send_cb(NULL, compose);
2707 compose_send_later_cb(NULL, compose);
2710 compose_draft(compose, COMPOSE_QUIT_EDITING);
2713 compose_insert_file_cb(NULL, compose);
2716 compose_attach_cb(NULL, compose);
2719 compose_insert_sig(compose, FALSE);
2722 compose_insert_sig(compose, TRUE);
2725 compose_ext_editor_cb(NULL, compose);
2727 case A_LINEWRAP_CURRENT:
2728 compose_beautify_paragraph(compose, NULL, TRUE);
2730 case A_LINEWRAP_ALL:
2731 compose_wrap_all_full(compose, TRUE);
2734 compose_address_cb(NULL, compose);
2737 case A_CHECK_SPELLING:
2738 compose_check_all(NULL, compose);
2746 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2751 gchar *subject = NULL;
2755 gchar **attach = NULL;
2756 gchar *inreplyto = NULL;
2757 MailField mfield = NO_FIELD_PRESENT;
2759 /* get mailto parts but skip from */
2760 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2763 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2764 mfield = TO_FIELD_PRESENT;
2767 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2769 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2771 if (!g_utf8_validate (subject, -1, NULL)) {
2772 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2773 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2776 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2778 mfield = SUBJECT_FIELD_PRESENT;
2781 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2782 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2785 gboolean prev_autowrap = compose->autowrap;
2787 compose->autowrap = FALSE;
2789 mark = gtk_text_buffer_get_insert(buffer);
2790 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2792 if (!g_utf8_validate (body, -1, NULL)) {
2793 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2794 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2797 gtk_text_buffer_insert(buffer, &iter, body, -1);
2799 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2801 compose->autowrap = prev_autowrap;
2802 if (compose->autowrap)
2803 compose_wrap_all(compose);
2804 mfield = BODY_FIELD_PRESENT;
2808 gint i = 0, att = 0;
2809 gchar *warn_files = NULL;
2810 while (attach[i] != NULL) {
2811 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2812 if (utf8_filename) {
2813 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2814 gchar *tmp = g_strdup_printf("%s%s\n",
2815 warn_files?warn_files:"",
2821 g_free(utf8_filename);
2823 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2828 alertpanel_notice(ngettext(
2829 "The following file has been attached: \n%s",
2830 "The following files have been attached: \n%s", att), warn_files);
2835 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2848 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2850 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2851 {"Cc:", NULL, TRUE},
2852 {"References:", NULL, FALSE},
2853 {"Bcc:", NULL, TRUE},
2854 {"Newsgroups:", NULL, TRUE},
2855 {"Followup-To:", NULL, TRUE},
2856 {"List-Post:", NULL, FALSE},
2857 {"X-Priority:", NULL, FALSE},
2858 {NULL, NULL, FALSE}};
2874 cm_return_val_if_fail(msginfo != NULL, -1);
2876 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2877 procheader_get_header_fields(fp, hentry);
2880 if (hentry[H_REPLY_TO].body != NULL) {
2881 if (hentry[H_REPLY_TO].body[0] != '\0') {
2883 conv_unmime_header(hentry[H_REPLY_TO].body,
2886 g_free(hentry[H_REPLY_TO].body);
2887 hentry[H_REPLY_TO].body = NULL;
2889 if (hentry[H_CC].body != NULL) {
2890 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2891 g_free(hentry[H_CC].body);
2892 hentry[H_CC].body = NULL;
2894 if (hentry[H_REFERENCES].body != NULL) {
2895 if (compose->mode == COMPOSE_REEDIT)
2896 compose->references = hentry[H_REFERENCES].body;
2898 compose->references = compose_parse_references
2899 (hentry[H_REFERENCES].body, msginfo->msgid);
2900 g_free(hentry[H_REFERENCES].body);
2902 hentry[H_REFERENCES].body = NULL;
2904 if (hentry[H_BCC].body != NULL) {
2905 if (compose->mode == COMPOSE_REEDIT)
2907 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2908 g_free(hentry[H_BCC].body);
2909 hentry[H_BCC].body = NULL;
2911 if (hentry[H_NEWSGROUPS].body != NULL) {
2912 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2913 hentry[H_NEWSGROUPS].body = NULL;
2915 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2916 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2917 compose->followup_to =
2918 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2921 g_free(hentry[H_FOLLOWUP_TO].body);
2922 hentry[H_FOLLOWUP_TO].body = NULL;
2924 if (hentry[H_LIST_POST].body != NULL) {
2925 gchar *to = NULL, *start = NULL;
2927 extract_address(hentry[H_LIST_POST].body);
2928 if (hentry[H_LIST_POST].body[0] != '\0') {
2929 start = strstr(hentry[H_LIST_POST].body, "mailto:");
2931 scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2932 NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2935 g_free(compose->ml_post);
2936 compose->ml_post = to;
2939 g_free(hentry[H_LIST_POST].body);
2940 hentry[H_LIST_POST].body = NULL;
2943 /* CLAWS - X-Priority */
2944 if (compose->mode == COMPOSE_REEDIT)
2945 if (hentry[H_X_PRIORITY].body != NULL) {
2948 priority = atoi(hentry[H_X_PRIORITY].body);
2949 g_free(hentry[H_X_PRIORITY].body);
2951 hentry[H_X_PRIORITY].body = NULL;
2953 if (priority < PRIORITY_HIGHEST ||
2954 priority > PRIORITY_LOWEST)
2955 priority = PRIORITY_NORMAL;
2957 compose->priority = priority;
2960 if (compose->mode == COMPOSE_REEDIT) {
2961 if (msginfo->inreplyto && *msginfo->inreplyto)
2962 compose->inreplyto = g_strdup(msginfo->inreplyto);
2964 if (msginfo->msgid && *msginfo->msgid)
2965 compose->msgid = g_strdup(msginfo->msgid);
2967 if (msginfo->msgid && *msginfo->msgid)
2968 compose->inreplyto = g_strdup(msginfo->msgid);
2970 if (!compose->references) {
2971 if (msginfo->msgid && *msginfo->msgid) {
2972 if (msginfo->inreplyto && *msginfo->inreplyto)
2973 compose->references =
2974 g_strdup_printf("<%s>\n\t<%s>",
2978 compose->references =
2979 g_strconcat("<", msginfo->msgid, ">",
2981 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2982 compose->references =
2983 g_strconcat("<", msginfo->inreplyto, ">",
2992 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
2997 cm_return_val_if_fail(msginfo != NULL, -1);
2999 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
3000 procheader_get_header_fields(fp, entries);
3004 while (he != NULL && he->name != NULL) {
3006 GtkListStore *model = NULL;
3008 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
3009 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
3010 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
3011 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
3012 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
3019 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
3021 GSList *ref_id_list, *cur;
3025 ref_id_list = references_list_append(NULL, ref);
3026 if (!ref_id_list) return NULL;
3027 if (msgid && *msgid)
3028 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
3033 for (cur = ref_id_list; cur != NULL; cur = cur->next)
3034 /* "<" + Message-ID + ">" + CR+LF+TAB */
3035 len += strlen((gchar *)cur->data) + 5;
3037 if (len > MAX_REFERENCES_LEN) {
3038 /* remove second message-ID */
3039 if (ref_id_list && ref_id_list->next &&
3040 ref_id_list->next->next) {
3041 g_free(ref_id_list->next->data);
3042 ref_id_list = g_slist_remove
3043 (ref_id_list, ref_id_list->next->data);
3045 slist_free_strings_full(ref_id_list);
3052 new_ref = g_string_new("");
3053 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
3054 if (new_ref->len > 0)
3055 g_string_append(new_ref, "\n\t");
3056 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
3059 slist_free_strings_full(ref_id_list);
3061 new_ref_str = new_ref->str;
3062 g_string_free(new_ref, FALSE);
3067 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
3068 const gchar *fmt, const gchar *qmark,
3069 const gchar *body, gboolean rewrap,
3070 gboolean need_unescape,
3071 const gchar *err_msg)
3073 MsgInfo* dummyinfo = NULL;
3074 gchar *quote_str = NULL;
3076 gboolean prev_autowrap;
3077 const gchar *trimmed_body = body;
3078 gint cursor_pos = -1;
3079 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3080 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3085 SIGNAL_BLOCK(buffer);
3088 dummyinfo = compose_msginfo_new_from_compose(compose);
3089 msginfo = dummyinfo;
3092 if (qmark != NULL) {
3094 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3095 compose->gtkaspell);
3097 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3099 quote_fmt_scan_string(qmark);
3102 buf = quote_fmt_get_buffer();
3104 alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3106 Xstrdup_a(quote_str, buf, goto error)
3109 if (fmt && *fmt != '\0') {
3112 while (*trimmed_body == '\n')
3116 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3117 compose->gtkaspell);
3119 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3121 if (need_unescape) {
3124 /* decode \-escape sequences in the internal representation of the quote format */
3125 tmp = g_malloc(strlen(fmt)+1);
3126 pref_get_unescaped_pref(tmp, fmt);
3127 quote_fmt_scan_string(tmp);
3131 quote_fmt_scan_string(fmt);
3135 buf = quote_fmt_get_buffer();
3137 gint line = quote_fmt_get_line();
3138 alertpanel_error(err_msg, line);
3144 prev_autowrap = compose->autowrap;
3145 compose->autowrap = FALSE;
3147 mark = gtk_text_buffer_get_insert(buffer);
3148 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3149 if (g_utf8_validate(buf, -1, NULL)) {
3150 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3152 gchar *tmpout = NULL;
3153 tmpout = conv_codeset_strdup
3154 (buf, conv_get_locale_charset_str_no_utf8(),
3156 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3158 tmpout = g_malloc(strlen(buf)*2+1);
3159 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3161 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3165 cursor_pos = quote_fmt_get_cursor_pos();
3166 if (cursor_pos == -1)
3167 cursor_pos = gtk_text_iter_get_offset(&iter);
3168 compose->set_cursor_pos = cursor_pos;
3170 gtk_text_buffer_get_start_iter(buffer, &iter);
3171 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3172 gtk_text_buffer_place_cursor(buffer, &iter);
3174 compose->autowrap = prev_autowrap;
3175 if (compose->autowrap && rewrap)